Merge commit '00f1a4f432b3d8aad1aa270e91c44c57f03ef407'
[unleashed.git] / usr / src / cmd / plimit / plimit.c
blob935c3be5e772a47fff43353eb207a9ce17070037
1 /*
2 * CDDL HEADER START
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
7 * with the License.
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]
20 * CDDL HEADER END
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 */
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <ctype.h>
37 #include <string.h>
38 #include <signal.h>
39 #include <limits.h>
40 #include <errno.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/mman.h>
44 #include <sys/mkdev.h>
45 #include <libproc.h>
46 #include <priv.h>
48 #define TRUE 1
49 #define FALSE 0
51 static int interrupt;
52 static char *command;
53 static int Fflag;
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 *);
65 static void
66 usage()
68 (void) fprintf(stderr,
69 "usage:\n"
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);
85 exit(2);
88 int
89 main(int argc, char **argv)
91 int retc = 0;
92 int opt;
93 int errflg = 0;
94 int set = FALSE;
95 struct ps_prochandle *Pr;
97 if ((command = strrchr(argv[0], '/')) != NULL)
98 command++;
99 else
100 command = argv[0];
102 while ((opt = getopt(argc, argv, "Fkmc:d:f:n:s:t:v:")) != EOF) {
103 switch (opt) {
104 case 'F': /* force grabbing (no O_EXCL) */
105 Fflag = PGRAB_FORCE;
106 break;
107 case 'k':
108 kbytes = TRUE;
109 mbytes = FALSE;
110 break;
111 case 'm':
112 kbytes = FALSE;
113 mbytes = TRUE;
114 break;
115 case 'c': /* core file size */
116 set = TRUE;
117 errflg += parse_limits(RLIMIT_CORE, optarg);
118 break;
119 case 'd': /* data segment size */
120 set = TRUE;
121 errflg += parse_limits(RLIMIT_DATA, optarg);
122 break;
123 case 'f': /* file size */
124 set = TRUE;
125 errflg += parse_limits(RLIMIT_FSIZE, optarg);
126 break;
127 case 'n': /* file descriptors */
128 set = TRUE;
129 errflg += parse_limits(RLIMIT_NOFILE, optarg);
130 break;
131 case 's': /* stack segment size */
132 set = TRUE;
133 errflg += parse_limits(RLIMIT_STACK, optarg);
134 break;
135 case 't': /* CPU time */
136 set = TRUE;
137 errflg += parse_limits(RLIMIT_CPU, optarg);
138 break;
139 case 'v': /* virtual memory size */
140 set = TRUE;
141 errflg += parse_limits(RLIMIT_VMEM, optarg);
142 break;
143 default:
144 errflg = 1;
145 break;
149 argc -= optind;
150 argv += optind;
152 if (errflg || argc <= 0)
153 usage();
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) {
166 psinfo_t psinfo;
167 char *arg;
168 pid_t pid;
169 int gret;
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));
178 retc = 1;
179 } else if ((Pr = Pgrab(pid, Fflag, &gret)) != NULL) {
180 if (Pcreate_agent(Pr) == 0) {
181 if (set) {
182 if (set_limits(Pr) != 0)
183 retc = 1;
184 } else {
185 proc_unctrl_psinfo(&psinfo);
186 (void) printf("%d:\t%.70s\n",
187 (int)pid, psinfo.pr_psargs);
188 show_limits(Pr);
190 Pdestroy_agent(Pr);
191 } else {
192 (void) fprintf(stderr,
193 "%s: cannot control process %d\n",
194 command, (int)pid);
195 retc = 1;
197 Prelease(Pr, 0);
198 } else {
199 if ((gret == G_SYS || gret == G_SELF) && !set) {
200 proc_unctrl_psinfo(&psinfo);
201 (void) printf("%d:\t%.70s\n", (int)pid,
202 psinfo.pr_psargs);
203 if (gret == G_SYS)
204 (void) printf(" [system process]\n");
205 else
206 show_limits(NULL);
207 } else {
208 (void) fprintf(stderr,
209 "%s: %s: %d\n",
210 command, Pgrab_error(gret), (int)pid);
211 retc = 1;
216 if (interrupt)
217 retc = 1;
218 return (retc);
221 static void
222 intr(int sig)
224 interrupt = sig;
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)
237 static int
238 limit_value(int which, char *arg, rlim64_t *limit)
240 rlim64_t value;
241 rlim64_t unit;
242 char *lastc;
244 if (strcmp(arg, "unlimited") == 0) {
245 *limit = RLIM64_INFINITY;
246 return (0);
249 if (which == RLIMIT_CPU && strchr(arg, ':') != NULL) {
250 char *minutes = strtok_r(arg, " \t:", &lastc);
251 char *seconds = strtok_r(NULL, " \t", &lastc);
252 rlim64_t sec;
254 if (seconds != NULL && strtok_r(NULL, " \t", &lastc) != NULL)
255 return (1);
256 value = strtoull(minutes, &lastc, 10);
257 if (*lastc != '\0' || value > RLIM64_INFINITY / 60)
258 return (1);
259 if (seconds == NULL || *seconds == '\0')
260 sec = 0;
261 else {
262 sec = strtoull(seconds, &lastc, 10);
263 if (*lastc != '\0' || sec > 60)
264 return (1);
266 value = value * 60 + sec;
267 if (value > RLIM64_INFINITY)
268 value = RLIM64_INFINITY;
269 *limit = value;
270 return (0);
273 switch (*(lastc = arg + strlen(arg) - 1)) {
274 case 'k':
275 unit = 1024;
276 *lastc = '\0';
277 break;
278 case 'm':
279 if (which == RLIMIT_CPU)
280 unit = 60;
281 else
282 unit = 1024 * 1024;
283 *lastc = '\0';
284 break;
285 case 'h':
286 if (which == RLIMIT_CPU)
287 unit = 60 * 60;
288 else
289 return (1);
290 *lastc = '\0';
291 break;
292 default:
293 switch (which) {
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;
302 break;
305 value = strtoull(arg, &lastc, 10);
306 if (*lastc != '\0' || value > RLIM64_INFINITY / unit)
307 return (1);
309 value *= unit;
310 if (value > RLIM64_INFINITY)
311 value = RLIM64_INFINITY;
312 *limit = value;
313 return (0);
316 static int
317 parse_limits(int which, char *arg)
319 char *lastc;
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)
325 return (1);
327 if (soft == NULL || *soft == '\0') {
328 rp->rlim_cur = 0;
329 set_current[which] = FALSE;
330 } else {
331 if (limit_value(which, soft, &rp->rlim_cur) != 0)
332 return (1);
333 set_current[which] = TRUE;
336 if (hard == NULL || *hard == '\0') {
337 rp->rlim_max = 0;
338 set_maximum[which] = FALSE;
339 } else {
340 if (limit_value(which, hard, &rp->rlim_max) != 0)
341 return (1);
342 set_maximum[which] = TRUE;
344 if (set_current[which] && set_maximum[which] &&
345 rp->rlim_cur > rp->rlim_max)
346 return (1);
348 return (0);
351 static void
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;
360 static char *
361 limit_values(struct rlimit64 *rp)
363 static char buffer[64];
364 char buf1[32];
365 char buf2[32];
366 char *s1;
367 char *s2;
369 if (rp->rlim_cur == RLIM64_INFINITY)
370 s1 = "unlimited";
371 else {
372 (void) sprintf(s1 = buf1, "%lld", rp->rlim_cur);
373 if (strlen(s1) < 8)
374 (void) strcat(s1, "\t");
377 if (rp->rlim_max == RLIM64_INFINITY)
378 s2 = "unlimited";
379 else {
380 (void) sprintf(s2 = buf2, "%lld", rp->rlim_max);
383 (void) sprintf(buffer, "%s\t%s", s1, s2);
385 return (buffer);
388 static void
389 show_limits(struct ps_prochandle *Pr)
391 struct rlimit64 rlim;
392 int resource;
393 char buf[32];
394 char *s;
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)
400 continue;
402 switch (resource) {
403 case RLIMIT_CPU:
404 s = " time(seconds)\t\t";
405 break;
406 case RLIMIT_FSIZE:
407 if (kbytes) {
408 s = " file(kbytes)\t\t";
409 limit_adjust(&rlim, 1024);
410 } else if (mbytes) {
411 s = " file(mbytes)\t\t";
412 limit_adjust(&rlim, 1024 * 1024);
413 } else {
414 s = " file(blocks)\t\t";
415 limit_adjust(&rlim, 512);
417 break;
418 case RLIMIT_DATA:
419 if (mbytes) {
420 s = " data(mbytes)\t\t";
421 limit_adjust(&rlim, 1024 * 1024);
422 } else {
423 s = " data(kbytes)\t\t";
424 limit_adjust(&rlim, 1024);
426 break;
427 case RLIMIT_STACK:
428 if (mbytes) {
429 s = " stack(mbytes)\t\t";
430 limit_adjust(&rlim, 1024 * 1024);
431 } else {
432 s = " stack(kbytes)\t\t";
433 limit_adjust(&rlim, 1024);
435 break;
436 case RLIMIT_CORE:
437 if (kbytes) {
438 s = " coredump(kbytes)\t";
439 limit_adjust(&rlim, 1024);
440 } else if (mbytes) {
441 s = " coredump(mbytes)\t";
442 limit_adjust(&rlim, 1024 * 1024);
443 } else {
444 s = " coredump(blocks)\t";
445 limit_adjust(&rlim, 512);
447 break;
448 case RLIMIT_NOFILE:
449 s = " nofiles(descriptors)\t";
450 break;
451 case RLIMIT_VMEM:
452 if (mbytes) {
453 s = " vmemory(mbytes)\t";
454 limit_adjust(&rlim, 1024 * 1024);
455 } else {
456 s = " vmemory(kbytes)\t";
457 limit_adjust(&rlim, 1024);
459 break;
460 default:
461 (void) sprintf(buf, " rlimit #%d\t", resource);
462 s = buf;
463 break;
466 (void) printf("%s%s\n", s, limit_values(&rlim));
470 static int
471 set_one_limit(struct ps_prochandle *Pr, int which, rlim64_t cur, rlim64_t max)
473 struct rlimit64 rlim;
474 int be_su = 0;
475 prpriv_t *old_prpriv = NULL, *new_prpriv = NULL;
476 priv_set_t *eset, *pset;
477 int ret = 0;
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));
483 return (1);
486 if (!set_current[which])
487 cur = rlim.rlim_cur;
488 if (!set_maximum[which])
489 max = rlim.rlim_max;
491 if (max < cur)
492 max = cur;
494 if (max > rlim.rlim_max && Pr != NULL)
495 be_su = 1;
496 rlim.rlim_cur = cur;
497 rlim.rlim_max = max;
499 if (be_su) {
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,
505 strerror(errno));
506 return (1);
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));
530 return (1);
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);
545 return (1);
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));
554 ret = 1;
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).
565 Pdestroy_agent(Pr);
566 (void) fprintf(stderr,
567 "%s: cannot relinquish privileges for pid %d."
568 " The process was killed.",
569 command, Pstatus(Pr)->pr_pid);
570 ret = 1;
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);
577 ret = 1;
580 proc_free_priv(old_prpriv);
583 proc_free_priv(new_prpriv);
585 return (ret);
588 static int
589 set_limits(struct ps_prochandle *Pr)
591 int which;
592 int retc = 0;
594 for (which = 0; which < RLIM_NLIMITS; which++) {
595 if (set_current[which] || set_maximum[which]) {
596 if (set_one_limit(Pr, which, rlimit[which].rlim_cur,
597 rlimit[which].rlim_max) != 0)
598 retc = 1;
602 return (retc);