xref: /illumos-gate/usr/src/cmd/plimit/plimit.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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
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
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
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
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
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
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 *
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
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
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
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