1 /*
2 * CDDL HEADER START
3 *
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.
8 *
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.
13 *
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]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.5 */
28
29 #define __EXTENSIONS__ /* For strtok_r */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <ctype.h>
36 #include <string.h>
37 #include <signal.h>
38 #include <limits.h>
39 #include <errno.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/mman.h>
43 #include <sys/mkdev.h>
44 #include <libproc.h>
45 #include <priv.h>
46
47 #define TRUE 1
48 #define FALSE 0
49
50 static int interrupt;
51 static char *command;
52 static int Fflag;
53 static int kbytes = FALSE;
54 static int mbytes = FALSE;
55 static char set_current[RLIM_NLIMITS];
56 static char set_maximum[RLIM_NLIMITS];
57 static struct rlimit64 rlimit[RLIM_NLIMITS];
58
59 static void intr(int);
60 static int parse_limits(int, char *);
61 static void show_limits(struct ps_prochandle *);
62 static int set_limits(struct ps_prochandle *);
63
64 static void
usage()65 usage()
66 {
67 (void) fprintf(stderr,
68 "usage:\n"
69 " For each process, report all resource limits:\n"
70 "\t%s [-km] pid ...\n"
71 "\t-k\treport file sizes in kilobytes\n"
72 "\t-m\treport file/memory sizes in megabytes\n"
73 " For each process, set specified resource limits:\n"
74 "\t%s -{cdfnstv} soft,hard ... pid ...\n"
75 "\t-c soft,hard\tset core file size limits\n"
76 "\t-d soft,hard\tset data segment (heap) size limits\n"
77 "\t-f soft,hard\tset file size limits\n"
78 "\t-n soft,hard\tset file descriptor limits\n"
79 "\t-s soft,hard\tset stack segment size limits\n"
80 "\t-t soft,hard\tset CPU time limits\n"
81 "\t-v soft,hard\tset virtual memory size limits\n"
82 "\t(default units are as shown by the output of '%s pid')\n",
83 command, command, command);
84 exit(2);
85 }
86
87 int
main(int argc,char ** argv)88 main(int argc, char **argv)
89 {
90 int retc = 0;
91 int opt;
92 int errflg = 0;
93 int set = FALSE;
94 struct ps_prochandle *Pr;
95
96 if ((command = strrchr(argv[0], '/')) != NULL)
97 command++;
98 else
99 command = argv[0];
100
101 while ((opt = getopt(argc, argv, "Fkmc:d:f:n:s:t:v:")) != EOF) {
102 switch (opt) {
103 case 'F': /* force grabbing (no O_EXCL) */
104 Fflag = PGRAB_FORCE;
105 break;
106 case 'k':
107 kbytes = TRUE;
108 mbytes = FALSE;
109 break;
110 case 'm':
111 kbytes = FALSE;
112 mbytes = TRUE;
113 break;
114 case 'c': /* core file size */
115 set = TRUE;
116 errflg += parse_limits(RLIMIT_CORE, optarg);
117 break;
118 case 'd': /* data segment size */
119 set = TRUE;
120 errflg += parse_limits(RLIMIT_DATA, optarg);
121 break;
122 case 'f': /* file size */
123 set = TRUE;
124 errflg += parse_limits(RLIMIT_FSIZE, optarg);
125 break;
126 case 'n': /* file descriptors */
127 set = TRUE;
128 errflg += parse_limits(RLIMIT_NOFILE, optarg);
129 break;
130 case 's': /* stack segment size */
131 set = TRUE;
132 errflg += parse_limits(RLIMIT_STACK, optarg);
133 break;
134 case 't': /* CPU time */
135 set = TRUE;
136 errflg += parse_limits(RLIMIT_CPU, optarg);
137 break;
138 case 'v': /* virtual memory size */
139 set = TRUE;
140 errflg += parse_limits(RLIMIT_VMEM, optarg);
141 break;
142 default:
143 errflg = 1;
144 break;
145 }
146 }
147
148 argc -= optind;
149 argv += optind;
150
151 if (errflg || argc <= 0)
152 usage();
153
154 /* catch signals from terminal */
155 if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
156 (void) sigset(SIGHUP, intr);
157 if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
158 (void) sigset(SIGINT, intr);
159 if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
160 (void) sigset(SIGQUIT, intr);
161 (void) sigset(SIGPIPE, intr);
162 (void) sigset(SIGTERM, intr);
163
164 while (--argc >= 0 && !interrupt) {
165 psinfo_t psinfo;
166 char *arg;
167 pid_t pid;
168 int gret;
169
170 (void) fflush(stdout); /* process-at-a-time */
171
172 /* get the specified pid and the psinfo struct */
173 if ((pid = proc_arg_psinfo(arg = *argv++, PR_ARG_PIDS,
174 &psinfo, &gret)) == -1) {
175 (void) fprintf(stderr, "%s: cannot examine %s: %s\n",
176 command, arg, Pgrab_error(gret));
177 retc = 1;
178 } else if ((Pr = Pgrab(pid, Fflag, &gret)) != NULL) {
179 if (Pcreate_agent(Pr) == 0) {
180 if (set) {
181 if (set_limits(Pr) != 0)
182 retc = 1;
183 } else {
184 proc_unctrl_psinfo(&psinfo);
185 (void) printf("%d:\t%.70s\n",
186 (int)pid, psinfo.pr_psargs);
187 show_limits(Pr);
188 }
189 Pdestroy_agent(Pr);
190 } else {
191 (void) fprintf(stderr,
192 "%s: cannot control process %d\n",
193 command, (int)pid);
194 retc = 1;
195 }
196 Prelease(Pr, 0);
197 } else {
198 if ((gret == G_SYS || gret == G_SELF) && !set) {
199 proc_unctrl_psinfo(&psinfo);
200 (void) printf("%d:\t%.70s\n", (int)pid,
201 psinfo.pr_psargs);
202 if (gret == G_SYS)
203 (void) printf(" [system process]\n");
204 else
205 show_limits(NULL);
206 } else {
207 (void) fprintf(stderr,
208 "%s: %s: %d\n",
209 command, Pgrab_error(gret), (int)pid);
210 retc = 1;
211 }
212 }
213 }
214
215 if (interrupt)
216 retc = 1;
217 return (retc);
218 }
219
220 static void
intr(int sig)221 intr(int sig)
222 {
223 interrupt = sig;
224 }
225
226 /* ------ begin specific code ------ */
227
228 /*
229 * Compute a limit, given a string:
230 * unlimited unlimited
231 * nnn k nnn kilobytes
232 * nnn m nnn megabytes (minutes for CPU time)
233 * nnn h nnn hours (for CPU time only)
234 * mm : ss minutes and seconds (for CPU time only)
235 */
236 static int
limit_value(int which,char * arg,rlim64_t * limit)237 limit_value(int which, char *arg, rlim64_t *limit)
238 {
239 rlim64_t value;
240 rlim64_t unit;
241 char *lastc;
242
243 if (strcmp(arg, "unlimited") == 0) {
244 *limit = RLIM64_INFINITY;
245 return (0);
246 }
247
248 if (which == RLIMIT_CPU && strchr(arg, ':') != NULL) {
249 char *minutes = strtok_r(arg, " \t:", &lastc);
250 char *seconds = strtok_r(NULL, " \t", &lastc);
251 rlim64_t sec;
252
253 if (seconds != NULL && strtok_r(NULL, " \t", &lastc) != NULL)
254 return (1);
255 value = strtoull(minutes, &lastc, 10);
256 if (*lastc != '\0' || value > RLIM64_INFINITY / 60)
257 return (1);
258 if (seconds == NULL || *seconds == '\0')
259 sec = 0;
260 else {
261 sec = strtoull(seconds, &lastc, 10);
262 if (*lastc != '\0' || sec > 60)
263 return (1);
264 }
265 value = value * 60 + sec;
266 if (value > RLIM64_INFINITY)
267 value = RLIM64_INFINITY;
268 *limit = value;
269 return (0);
270 }
271
272 switch (*(lastc = arg + strlen(arg) - 1)) {
273 case 'k':
274 unit = 1024;
275 *lastc = '\0';
276 break;
277 case 'm':
278 if (which == RLIMIT_CPU)
279 unit = 60;
280 else
281 unit = 1024 * 1024;
282 *lastc = '\0';
283 break;
284 case 'h':
285 if (which == RLIMIT_CPU)
286 unit = 60 * 60;
287 else
288 return (1);
289 *lastc = '\0';
290 break;
291 default:
292 switch (which) {
293 case RLIMIT_CPU: unit = 1; break;
294 case RLIMIT_FSIZE: unit = 512; break;
295 case RLIMIT_DATA: unit = 1024; break;
296 case RLIMIT_STACK: unit = 1024; break;
297 case RLIMIT_CORE: unit = 512; break;
298 case RLIMIT_NOFILE: unit = 1; break;
299 case RLIMIT_VMEM: unit = 1024; break;
300 }
301 break;
302 }
303
304 value = strtoull(arg, &lastc, 10);
305 if (*lastc != '\0' || value > RLIM64_INFINITY / unit)
306 return (1);
307
308 value *= unit;
309 if (value > RLIM64_INFINITY)
310 value = RLIM64_INFINITY;
311 *limit = value;
312 return (0);
313 }
314
315 static int
parse_limits(int which,char * arg)316 parse_limits(int which, char *arg)
317 {
318 char *lastc;
319 char *soft = strtok_r(arg, " \t,", &lastc);
320 char *hard = strtok_r(NULL, " \t", &lastc);
321 struct rlimit64 *rp = &rlimit[which];
322
323 if (hard != NULL && strtok_r(NULL, " \t", &lastc) != NULL)
324 return (1);
325
326 if (soft == NULL || *soft == '\0') {
327 rp->rlim_cur = 0;
328 set_current[which] = FALSE;
329 } else {
330 if (limit_value(which, soft, &rp->rlim_cur) != 0)
331 return (1);
332 set_current[which] = TRUE;
333 }
334
335 if (hard == NULL || *hard == '\0') {
336 rp->rlim_max = 0;
337 set_maximum[which] = FALSE;
338 } else {
339 if (limit_value(which, hard, &rp->rlim_max) != 0)
340 return (1);
341 set_maximum[which] = TRUE;
342 }
343 if (set_current[which] && set_maximum[which] &&
344 rp->rlim_cur > rp->rlim_max)
345 return (1);
346
347 return (0);
348 }
349
350 static void
limit_adjust(struct rlimit64 * rp,int units)351 limit_adjust(struct rlimit64 *rp, int units)
352 {
353 if (rp->rlim_cur != RLIM64_INFINITY)
354 rp->rlim_cur /= units;
355 if (rp->rlim_max != RLIM64_INFINITY)
356 rp->rlim_max /= units;
357 }
358
359 static char *
limit_values(struct rlimit64 * rp)360 limit_values(struct rlimit64 *rp)
361 {
362 static char buffer[64];
363 char buf1[32];
364 char buf2[32];
365 char *s1;
366 char *s2;
367
368 if (rp->rlim_cur == RLIM64_INFINITY)
369 s1 = "unlimited";
370 else {
371 (void) sprintf(s1 = buf1, "%lld", rp->rlim_cur);
372 if (strlen(s1) < 8)
373 (void) strcat(s1, "\t");
374 }
375
376 if (rp->rlim_max == RLIM64_INFINITY)
377 s2 = "unlimited";
378 else {
379 (void) sprintf(s2 = buf2, "%lld", rp->rlim_max);
380 }
381
382 (void) sprintf(buffer, "%s\t%s", s1, s2);
383
384 return (buffer);
385 }
386
387 static void
show_limits(struct ps_prochandle * Pr)388 show_limits(struct ps_prochandle *Pr)
389 {
390 struct rlimit64 rlim;
391 int resource;
392 char buf[32];
393 char *s;
394
395 (void) printf(" resource\t\t current\t maximum\n");
396
397 for (resource = 0; resource < RLIM_NLIMITS; resource++) {
398 if (pr_getrlimit64(Pr, resource, &rlim) != 0)
399 continue;
400
401 switch (resource) {
402 case RLIMIT_CPU:
403 s = " time(seconds)\t\t";
404 break;
405 case RLIMIT_FSIZE:
406 if (kbytes) {
407 s = " file(kbytes)\t\t";
408 limit_adjust(&rlim, 1024);
409 } else if (mbytes) {
410 s = " file(mbytes)\t\t";
411 limit_adjust(&rlim, 1024 * 1024);
412 } else {
413 s = " file(blocks)\t\t";
414 limit_adjust(&rlim, 512);
415 }
416 break;
417 case RLIMIT_DATA:
418 if (mbytes) {
419 s = " data(mbytes)\t\t";
420 limit_adjust(&rlim, 1024 * 1024);
421 } else {
422 s = " data(kbytes)\t\t";
423 limit_adjust(&rlim, 1024);
424 }
425 break;
426 case RLIMIT_STACK:
427 if (mbytes) {
428 s = " stack(mbytes)\t\t";
429 limit_adjust(&rlim, 1024 * 1024);
430 } else {
431 s = " stack(kbytes)\t\t";
432 limit_adjust(&rlim, 1024);
433 }
434 break;
435 case RLIMIT_CORE:
436 if (kbytes) {
437 s = " coredump(kbytes)\t";
438 limit_adjust(&rlim, 1024);
439 } else if (mbytes) {
440 s = " coredump(mbytes)\t";
441 limit_adjust(&rlim, 1024 * 1024);
442 } else {
443 s = " coredump(blocks)\t";
444 limit_adjust(&rlim, 512);
445 }
446 break;
447 case RLIMIT_NOFILE:
448 s = " nofiles(descriptors)\t";
449 break;
450 case RLIMIT_VMEM:
451 if (mbytes) {
452 s = " vmemory(mbytes)\t";
453 limit_adjust(&rlim, 1024 * 1024);
454 } else {
455 s = " vmemory(kbytes)\t";
456 limit_adjust(&rlim, 1024);
457 }
458 break;
459 default:
460 (void) sprintf(buf, " rlimit #%d\t", resource);
461 s = buf;
462 break;
463 }
464
465 (void) printf("%s%s\n", s, limit_values(&rlim));
466 }
467 }
468
469 static int
set_one_limit(struct ps_prochandle * Pr,int which,rlim64_t cur,rlim64_t max)470 set_one_limit(struct ps_prochandle *Pr, int which, rlim64_t cur, rlim64_t max)
471 {
472 struct rlimit64 rlim;
473 int be_su = 0;
474 prpriv_t *old_prpriv = NULL, *new_prpriv = NULL;
475 priv_set_t *eset, *pset;
476 int ret = 0;
477
478 if (pr_getrlimit64(Pr, which, &rlim) != 0) {
479 (void) fprintf(stderr,
480 "%s: unable to get process limit for pid %d: %s\n",
481 command, Pstatus(Pr)->pr_pid, strerror(errno));
482 return (1);
483 }
484
485 if (!set_current[which])
486 cur = rlim.rlim_cur;
487 if (!set_maximum[which])
488 max = rlim.rlim_max;
489
490 if (max < cur)
491 max = cur;
492
493 if (max > rlim.rlim_max && Pr != NULL)
494 be_su = 1;
495 rlim.rlim_cur = cur;
496 rlim.rlim_max = max;
497
498 if (be_su) {
499 new_prpriv = proc_get_priv(Pstatus(Pr)->pr_pid);
500 if (new_prpriv == NULL) {
501 (void) fprintf(stderr,
502 "%s: unable to get process privileges for pid"
503 " %d: %s\n", command, Pstatus(Pr)->pr_pid,
504 strerror(errno));
505 return (1);
506 }
507
508 /*
509 * We only have to change the process privileges if it doesn't
510 * already have PRIV_SYS_RESOURCE. In addition, we want to make
511 * sure that we don't leave a process with elevated privileges,
512 * so we make sure the process dies if we exit unexpectedly.
513 */
514 eset = (priv_set_t *)
515 &new_prpriv->pr_sets[new_prpriv->pr_setsize *
516 priv_getsetbyname(PRIV_EFFECTIVE)];
517 pset = (priv_set_t *)
518 &new_prpriv->pr_sets[new_prpriv->pr_setsize *
519 priv_getsetbyname(PRIV_PERMITTED)];
520 if (!priv_ismember(eset, PRIV_SYS_RESOURCE)) {
521 /* Keep track of original privileges */
522 old_prpriv = proc_get_priv(Pstatus(Pr)->pr_pid);
523 if (old_prpriv == NULL) {
524 free(new_prpriv);
525 (void) fprintf(stderr,
526 "%s: unable to get process privileges "
527 "for pid %d: %s\n", command,
528 Pstatus(Pr)->pr_pid, strerror(errno));
529 return (1);
530 }
531
532 (void) priv_addset(eset, PRIV_SYS_RESOURCE);
533 (void) priv_addset(pset, PRIV_SYS_RESOURCE);
534
535 if (Psetflags(Pr, PR_KLC) != 0 ||
536 Psetpriv(Pr, new_prpriv) != 0) {
537 (void) fprintf(stderr,
538 "%s: unable to set process privileges for"
539 " pid %d: %s\n", command,
540 Pstatus(Pr)->pr_pid, strerror(errno));
541 (void) Punsetflags(Pr, PR_KLC);
542 free(new_prpriv);
543 free(old_prpriv);
544 return (1);
545 }
546 }
547 }
548
549 if (pr_setrlimit64(Pr, which, &rlim) != 0) {
550 (void) fprintf(stderr,
551 "%s: cannot set resource limit for pid %d: %s\n",
552 command, Pstatus(Pr)->pr_pid, strerror(errno));
553 ret = 1;
554 }
555
556 if (old_prpriv != NULL) {
557 if (Psetpriv(Pr, old_prpriv) != 0) {
558 /*
559 * If this fails, we can't leave a process hanging
560 * around with elevated privileges, so we'll have to
561 * release the process from libproc, knowing that it
562 * will be killed (since we set PR_KLC).
563 */
564 Pdestroy_agent(Pr);
565 (void) fprintf(stderr,
566 "%s: cannot relinquish privileges for pid %d."
567 " The process was killed.",
568 command, Pstatus(Pr)->pr_pid);
569 ret = 1;
570 }
571 if (Punsetflags(Pr, PR_KLC) != 0) {
572 (void) fprintf(stderr,
573 "%s: cannot relinquish privileges for pid %d."
574 " The process was killed.",
575 command, Pstatus(Pr)->pr_pid);
576 ret = 1;
577 }
578
579 free(old_prpriv);
580 }
581
582 if (new_prpriv != NULL)
583 free(new_prpriv);
584
585 return (ret);
586 }
587
588 static int
set_limits(struct ps_prochandle * Pr)589 set_limits(struct ps_prochandle *Pr)
590 {
591 int which;
592 int retc = 0;
593
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;
599 }
600 }
601
602 return (retc);
603 }
604