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