4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 * Copyright 2015, Joyent, Inc.
30 #define __EXTENSIONS__ /* For strtok_r */
41 #include <sys/types.h>
44 #include <sys/mkdev.h>
54 static int kbytes
= FALSE
;
55 static int mbytes
= FALSE
;
56 static char set_current
[RLIM_NLIMITS
];
57 static char set_maximum
[RLIM_NLIMITS
];
58 static struct rlimit64 rlimit
[RLIM_NLIMITS
];
60 static void intr(int);
61 static int parse_limits(int, char *);
62 static void show_limits(struct ps_prochandle
*);
63 static int set_limits(struct ps_prochandle
*);
68 (void) fprintf(stderr
,
70 " For each process, report all resource limits:\n"
71 "\t%s [-km] pid ...\n"
72 "\t-k\treport file sizes in kilobytes\n"
73 "\t-m\treport file/memory sizes in megabytes\n"
74 " For each process, set specified resource limits:\n"
75 "\t%s -{cdfnstv} soft,hard ... pid ...\n"
76 "\t-c soft,hard\tset core file size limits\n"
77 "\t-d soft,hard\tset data segment (heap) size limits\n"
78 "\t-f soft,hard\tset file size limits\n"
79 "\t-n soft,hard\tset file descriptor limits\n"
80 "\t-s soft,hard\tset stack segment size limits\n"
81 "\t-t soft,hard\tset CPU time limits\n"
82 "\t-v soft,hard\tset virtual memory size limits\n"
83 "\t(default units are as shown by the output of '%s pid')\n",
84 command
, command
, command
);
89 main(int argc
, char **argv
)
95 struct ps_prochandle
*Pr
;
97 if ((command
= strrchr(argv
[0], '/')) != NULL
)
102 while ((opt
= getopt(argc
, argv
, "Fkmc:d:f:n:s:t:v:")) != EOF
) {
104 case 'F': /* force grabbing (no O_EXCL) */
115 case 'c': /* core file size */
117 errflg
+= parse_limits(RLIMIT_CORE
, optarg
);
119 case 'd': /* data segment size */
121 errflg
+= parse_limits(RLIMIT_DATA
, optarg
);
123 case 'f': /* file size */
125 errflg
+= parse_limits(RLIMIT_FSIZE
, optarg
);
127 case 'n': /* file descriptors */
129 errflg
+= parse_limits(RLIMIT_NOFILE
, optarg
);
131 case 's': /* stack segment size */
133 errflg
+= parse_limits(RLIMIT_STACK
, optarg
);
135 case 't': /* CPU time */
137 errflg
+= parse_limits(RLIMIT_CPU
, optarg
);
139 case 'v': /* virtual memory size */
141 errflg
+= parse_limits(RLIMIT_VMEM
, optarg
);
152 if (errflg
|| argc
<= 0)
155 /* catch signals from terminal */
156 if (sigset(SIGHUP
, SIG_IGN
) == SIG_DFL
)
157 (void) sigset(SIGHUP
, intr
);
158 if (sigset(SIGINT
, SIG_IGN
) == SIG_DFL
)
159 (void) sigset(SIGINT
, intr
);
160 if (sigset(SIGQUIT
, SIG_IGN
) == SIG_DFL
)
161 (void) sigset(SIGQUIT
, intr
);
162 (void) sigset(SIGPIPE
, intr
);
163 (void) sigset(SIGTERM
, intr
);
165 while (--argc
>= 0 && !interrupt
) {
171 (void) fflush(stdout
); /* process-at-a-time */
173 /* get the specified pid and the psinfo struct */
174 if ((pid
= proc_arg_psinfo(arg
= *argv
++, PR_ARG_PIDS
,
175 &psinfo
, &gret
)) == -1) {
176 (void) fprintf(stderr
, "%s: cannot examine %s: %s\n",
177 command
, arg
, Pgrab_error(gret
));
179 } else if ((Pr
= Pgrab(pid
, Fflag
, &gret
)) != NULL
) {
180 if (Pcreate_agent(Pr
) == 0) {
182 if (set_limits(Pr
) != 0)
185 proc_unctrl_psinfo(&psinfo
);
186 (void) printf("%d:\t%.70s\n",
187 (int)pid
, psinfo
.pr_psargs
);
192 (void) fprintf(stderr
,
193 "%s: cannot control process %d\n",
199 if ((gret
== G_SYS
|| gret
== G_SELF
) && !set
) {
200 proc_unctrl_psinfo(&psinfo
);
201 (void) printf("%d:\t%.70s\n", (int)pid
,
204 (void) printf(" [system process]\n");
208 (void) fprintf(stderr
,
210 command
, Pgrab_error(gret
), (int)pid
);
227 /* ------ begin specific code ------ */
230 * Compute a limit, given a string:
231 * unlimited unlimited
232 * nnn k nnn kilobytes
233 * nnn m nnn megabytes (minutes for CPU time)
234 * nnn h nnn hours (for CPU time only)
235 * mm : ss minutes and seconds (for CPU time only)
238 limit_value(int which
, char *arg
, rlim64_t
*limit
)
244 if (strcmp(arg
, "unlimited") == 0) {
245 *limit
= RLIM64_INFINITY
;
249 if (which
== RLIMIT_CPU
&& strchr(arg
, ':') != NULL
) {
250 char *minutes
= strtok_r(arg
, " \t:", &lastc
);
251 char *seconds
= strtok_r(NULL
, " \t", &lastc
);
254 if (seconds
!= NULL
&& strtok_r(NULL
, " \t", &lastc
) != NULL
)
256 value
= strtoull(minutes
, &lastc
, 10);
257 if (*lastc
!= '\0' || value
> RLIM64_INFINITY
/ 60)
259 if (seconds
== NULL
|| *seconds
== '\0')
262 sec
= strtoull(seconds
, &lastc
, 10);
263 if (*lastc
!= '\0' || sec
> 60)
266 value
= value
* 60 + sec
;
267 if (value
> RLIM64_INFINITY
)
268 value
= RLIM64_INFINITY
;
273 switch (*(lastc
= arg
+ strlen(arg
) - 1)) {
279 if (which
== RLIMIT_CPU
)
286 if (which
== RLIMIT_CPU
)
294 case RLIMIT_CPU
: unit
= 1; break;
295 case RLIMIT_FSIZE
: unit
= 512; break;
296 case RLIMIT_DATA
: unit
= 1024; break;
297 case RLIMIT_STACK
: unit
= 1024; break;
298 case RLIMIT_CORE
: unit
= 512; break;
299 case RLIMIT_NOFILE
: unit
= 1; break;
300 case RLIMIT_VMEM
: unit
= 1024; break;
305 value
= strtoull(arg
, &lastc
, 10);
306 if (*lastc
!= '\0' || value
> RLIM64_INFINITY
/ unit
)
310 if (value
> RLIM64_INFINITY
)
311 value
= RLIM64_INFINITY
;
317 parse_limits(int which
, char *arg
)
320 char *soft
= strtok_r(arg
, " \t,", &lastc
);
321 char *hard
= strtok_r(NULL
, " \t", &lastc
);
322 struct rlimit64
*rp
= &rlimit
[which
];
324 if (hard
!= NULL
&& strtok_r(NULL
, " \t", &lastc
) != NULL
)
327 if (soft
== NULL
|| *soft
== '\0') {
329 set_current
[which
] = FALSE
;
331 if (limit_value(which
, soft
, &rp
->rlim_cur
) != 0)
333 set_current
[which
] = TRUE
;
336 if (hard
== NULL
|| *hard
== '\0') {
338 set_maximum
[which
] = FALSE
;
340 if (limit_value(which
, hard
, &rp
->rlim_max
) != 0)
342 set_maximum
[which
] = TRUE
;
344 if (set_current
[which
] && set_maximum
[which
] &&
345 rp
->rlim_cur
> rp
->rlim_max
)
352 limit_adjust(struct rlimit64
*rp
, int units
)
354 if (rp
->rlim_cur
!= RLIM64_INFINITY
)
355 rp
->rlim_cur
/= units
;
356 if (rp
->rlim_max
!= RLIM64_INFINITY
)
357 rp
->rlim_max
/= units
;
361 limit_values(struct rlimit64
*rp
)
363 static char buffer
[64];
369 if (rp
->rlim_cur
== RLIM64_INFINITY
)
372 (void) sprintf(s1
= buf1
, "%lld", rp
->rlim_cur
);
374 (void) strcat(s1
, "\t");
377 if (rp
->rlim_max
== RLIM64_INFINITY
)
380 (void) sprintf(s2
= buf2
, "%lld", rp
->rlim_max
);
383 (void) sprintf(buffer
, "%s\t%s", s1
, s2
);
389 show_limits(struct ps_prochandle
*Pr
)
391 struct rlimit64 rlim
;
396 (void) printf(" resource\t\t current\t maximum\n");
398 for (resource
= 0; resource
< RLIM_NLIMITS
; resource
++) {
399 if (pr_getrlimit64(Pr
, resource
, &rlim
) != 0)
404 s
= " time(seconds)\t\t";
408 s
= " file(kbytes)\t\t";
409 limit_adjust(&rlim
, 1024);
411 s
= " file(mbytes)\t\t";
412 limit_adjust(&rlim
, 1024 * 1024);
414 s
= " file(blocks)\t\t";
415 limit_adjust(&rlim
, 512);
420 s
= " data(mbytes)\t\t";
421 limit_adjust(&rlim
, 1024 * 1024);
423 s
= " data(kbytes)\t\t";
424 limit_adjust(&rlim
, 1024);
429 s
= " stack(mbytes)\t\t";
430 limit_adjust(&rlim
, 1024 * 1024);
432 s
= " stack(kbytes)\t\t";
433 limit_adjust(&rlim
, 1024);
438 s
= " coredump(kbytes)\t";
439 limit_adjust(&rlim
, 1024);
441 s
= " coredump(mbytes)\t";
442 limit_adjust(&rlim
, 1024 * 1024);
444 s
= " coredump(blocks)\t";
445 limit_adjust(&rlim
, 512);
449 s
= " nofiles(descriptors)\t";
453 s
= " vmemory(mbytes)\t";
454 limit_adjust(&rlim
, 1024 * 1024);
456 s
= " vmemory(kbytes)\t";
457 limit_adjust(&rlim
, 1024);
461 (void) sprintf(buf
, " rlimit #%d\t", resource
);
466 (void) printf("%s%s\n", s
, limit_values(&rlim
));
471 set_one_limit(struct ps_prochandle
*Pr
, int which
, rlim64_t cur
, rlim64_t max
)
473 struct rlimit64 rlim
;
475 prpriv_t
*old_prpriv
= NULL
, *new_prpriv
= NULL
;
476 priv_set_t
*eset
, *pset
;
479 if (pr_getrlimit64(Pr
, which
, &rlim
) != 0) {
480 (void) fprintf(stderr
,
481 "%s: unable to get process limit for pid %d: %s\n",
482 command
, Pstatus(Pr
)->pr_pid
, strerror(errno
));
486 if (!set_current
[which
])
488 if (!set_maximum
[which
])
494 if (max
> rlim
.rlim_max
&& Pr
!= NULL
)
500 new_prpriv
= proc_get_priv(Pstatus(Pr
)->pr_pid
);
501 if (new_prpriv
== NULL
) {
502 (void) fprintf(stderr
,
503 "%s: unable to get process privileges for pid"
504 " %d: %s\n", command
, Pstatus(Pr
)->pr_pid
,
510 * We only have to change the process privileges if it doesn't
511 * already have PRIV_SYS_RESOURCE. In addition, we want to make
512 * sure that we don't leave a process with elevated privileges,
513 * so we make sure the process dies if we exit unexpectedly.
515 eset
= (priv_set_t
*)
516 &new_prpriv
->pr_sets
[new_prpriv
->pr_setsize
*
517 priv_getsetbyname(PRIV_EFFECTIVE
)];
518 pset
= (priv_set_t
*)
519 &new_prpriv
->pr_sets
[new_prpriv
->pr_setsize
*
520 priv_getsetbyname(PRIV_PERMITTED
)];
521 if (!priv_ismember(eset
, PRIV_SYS_RESOURCE
)) {
522 /* Keep track of original privileges */
523 old_prpriv
= proc_get_priv(Pstatus(Pr
)->pr_pid
);
524 if (old_prpriv
== NULL
) {
525 proc_free_priv(new_prpriv
);
526 (void) fprintf(stderr
,
527 "%s: unable to get process privileges "
528 "for pid %d: %s\n", command
,
529 Pstatus(Pr
)->pr_pid
, strerror(errno
));
533 (void) priv_addset(eset
, PRIV_SYS_RESOURCE
);
534 (void) priv_addset(pset
, PRIV_SYS_RESOURCE
);
536 if (Psetflags(Pr
, PR_KLC
) != 0 ||
537 Psetpriv(Pr
, new_prpriv
) != 0) {
538 (void) fprintf(stderr
,
539 "%s: unable to set process privileges for"
540 " pid %d: %s\n", command
,
541 Pstatus(Pr
)->pr_pid
, strerror(errno
));
542 (void) Punsetflags(Pr
, PR_KLC
);
543 proc_free_priv(new_prpriv
);
544 proc_free_priv(old_prpriv
);
550 if (pr_setrlimit64(Pr
, which
, &rlim
) != 0) {
551 (void) fprintf(stderr
,
552 "%s: cannot set resource limit for pid %d: %s\n",
553 command
, Pstatus(Pr
)->pr_pid
, strerror(errno
));
557 if (old_prpriv
!= NULL
) {
558 if (Psetpriv(Pr
, old_prpriv
) != 0) {
560 * If this fails, we can't leave a process hanging
561 * around with elevated privileges, so we'll have to
562 * release the process from libproc, knowing that it
563 * will be killed (since we set PR_KLC).
566 (void) fprintf(stderr
,
567 "%s: cannot relinquish privileges for pid %d."
568 " The process was killed.",
569 command
, Pstatus(Pr
)->pr_pid
);
572 if (Punsetflags(Pr
, PR_KLC
) != 0) {
573 (void) fprintf(stderr
,
574 "%s: cannot relinquish privileges for pid %d."
575 " The process was killed.",
576 command
, Pstatus(Pr
)->pr_pid
);
580 proc_free_priv(old_prpriv
);
583 if (new_prpriv
!= NULL
)
584 proc_free_priv(new_prpriv
);
590 set_limits(struct ps_prochandle
*Pr
)
595 for (which
= 0; which
< RLIM_NLIMITS
; which
++) {
596 if (set_current
[which
] || set_maximum
[which
]) {
597 if (set_one_limit(Pr
, which
, rlimit
[which
].rlim_cur
,
598 rlimit
[which
].rlim_max
) != 0)