1 /*- 2 * Copyright (c) 1990, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 static char const copyright[] = 36 "@(#) Copyright (c) 1990, 1993, 1994\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94"; 43 #endif 44 static const char rcsid[] = 45 "$FreeBSD: src/bin/ps/ps.c,v 1.30.2.6 2002/07/04 08:30:37 sobomax Exp $"; 46 #endif /* not lint */ 47 48 #include <sys/param.h> 49 #include <sys/user.h> 50 #include <sys/time.h> 51 #include <sys/resource.h> 52 #include <sys/stat.h> 53 #include <sys/ioctl.h> 54 #include <sys/sysctl.h> 55 56 #include <ctype.h> 57 #include <err.h> 58 #include <errno.h> 59 #include <fcntl.h> 60 #include <kvm.h> 61 #include <limits.h> 62 #include <locale.h> 63 #include <nlist.h> 64 #include <paths.h> 65 #include <stdio.h> 66 #include <stdlib.h> 67 #include <string.h> 68 #include <unistd.h> 69 #include <pwd.h> 70 #include <utmp.h> 71 72 #include "ps.h" 73 74 #define SEP ", \t" /* username separators */ 75 76 KINFO *kinfo; 77 struct varent *vhead, *vtail; 78 79 int eval; /* exit value */ 80 int cflag; /* -c */ 81 int rawcpu; /* -C */ 82 int sumrusage; /* -S */ 83 int termwidth; /* width of screen (0 == infinity) */ 84 int totwidth; /* calculated width of requested variables */ 85 86 static int needuser, needcomm, needenv; 87 #if defined(LAZY_PS) 88 static int forceuread=0; 89 #define PS_ARGS "aCcefghjLlM:mN:O:o:p:rSTt:U:uvwx" 90 #else 91 static int forceuread=1; 92 #define PS_ARGS "aCceghjLlM:mN:O:o:p:rSTt:U:uvwx" 93 #endif 94 95 enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 96 97 static char *fmt __P((char **(*)(kvm_t *, const struct kinfo_proc *, int), 98 KINFO *, char *, int)); 99 static char *kludge_oldps_options __P((char *)); 100 static int pscomp __P((const void *, const void *)); 101 static void saveuser __P((KINFO *)); 102 static void scanvars __P((void)); 103 static void dynsizevars __P((KINFO *)); 104 static void sizevars __P((void)); 105 static void usage __P((void)); 106 static uid_t *getuids(const char *, int *); 107 108 char dfmt[] = "pid tt state time command"; 109 char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; 110 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; 111 char o1[] = "pid"; 112 char o2[] = "tt state time command"; 113 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; 114 char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command"; 115 116 kvm_t *kd; 117 118 int 119 main(argc, argv) 120 int argc; 121 char *argv[]; 122 { 123 struct kinfo_proc *kp; 124 struct varent *vent; 125 struct winsize ws; 126 dev_t ttydev; 127 pid_t pid; 128 uid_t *uids; 129 int all, ch, flag, i, fmt, lineno, nentries, nocludge, dropgid; 130 int prtheader, wflag, what, xflg, uid, nuids; 131 char *cp, *nlistf, *memf, errbuf[_POSIX2_LINE_MAX]; 132 133 (void) setlocale(LC_ALL, ""); 134 135 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 136 ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 137 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 138 ws.ws_col == 0) 139 termwidth = 79; 140 else 141 termwidth = ws.ws_col - 1; 142 143 /* 144 * Don't apply a kludge if the first argument is an option taking an 145 * argument 146 */ 147 if (argc > 1) { 148 nocludge = 0; 149 if (argv[1][0] == '-') { 150 for (cp = PS_ARGS; *cp != '\0'; cp++) { 151 if (*cp != ':') 152 continue; 153 if (*(cp - 1) == argv[1][1]) { 154 nocludge = 1; 155 break; 156 } 157 } 158 } 159 if (nocludge == 0) 160 argv[1] = kludge_oldps_options(argv[1]); 161 } 162 163 all = fmt = prtheader = wflag = xflg = 0; 164 pid = -1; 165 nuids = 0; 166 uids = NULL; 167 ttydev = NODEV; 168 dropgid = 0; 169 memf = nlistf = _PATH_DEVNULL; 170 while ((ch = getopt(argc, argv, PS_ARGS)) != -1) 171 switch((char)ch) { 172 case 'a': 173 all = 1; 174 break; 175 case 'C': 176 rawcpu = 1; 177 break; 178 case 'c': 179 cflag = 1; 180 break; 181 case 'e': /* XXX set ufmt */ 182 needenv = 1; 183 break; 184 case 'g': 185 break; /* no-op */ 186 case 'h': 187 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 188 break; 189 case 'j': 190 parsefmt(jfmt); 191 fmt = 1; 192 jfmt[0] = '\0'; 193 break; 194 case 'L': 195 showkey(); 196 exit(0); 197 case 'l': 198 parsefmt(lfmt); 199 fmt = 1; 200 lfmt[0] = '\0'; 201 break; 202 case 'M': 203 memf = optarg; 204 dropgid = 1; 205 break; 206 case 'm': 207 sortby = SORTMEM; 208 break; 209 case 'N': 210 nlistf = optarg; 211 dropgid = 1; 212 break; 213 case 'O': 214 parsefmt(o1); 215 parsefmt(optarg); 216 parsefmt(o2); 217 o1[0] = o2[0] = '\0'; 218 fmt = 1; 219 break; 220 case 'o': 221 parsefmt(optarg); 222 fmt = 1; 223 break; 224 #if defined(LAZY_PS) 225 case 'f': 226 if (getuid() == 0 || getgid() == 0) 227 forceuread = 1; 228 break; 229 #endif 230 case 'p': 231 pid = atol(optarg); 232 xflg = 1; 233 break; 234 case 'r': 235 sortby = SORTCPU; 236 break; 237 case 'S': 238 sumrusage = 1; 239 break; 240 case 'T': 241 if ((optarg = ttyname(STDIN_FILENO)) == NULL) 242 errx(1, "stdin: not a terminal"); 243 /* FALLTHROUGH */ 244 case 't': { 245 struct stat sb; 246 char *ttypath, pathbuf[PATH_MAX]; 247 248 if (strcmp(optarg, "co") == 0) 249 ttypath = _PATH_CONSOLE; 250 else if (*optarg != '/') 251 (void)snprintf(ttypath = pathbuf, 252 sizeof(pathbuf), "%s%s", _PATH_TTY, optarg); 253 else 254 ttypath = optarg; 255 if (stat(ttypath, &sb) == -1) 256 err(1, "%s", ttypath); 257 if (!S_ISCHR(sb.st_mode)) 258 errx(1, "%s: not a terminal", ttypath); 259 ttydev = sb.st_rdev; 260 break; 261 } 262 case 'U': 263 uids = getuids(optarg, &nuids); 264 xflg++; /* XXX: intuitive? */ 265 break; 266 case 'u': 267 parsefmt(ufmt); 268 sortby = SORTCPU; 269 fmt = 1; 270 ufmt[0] = '\0'; 271 break; 272 case 'v': 273 parsefmt(vfmt); 274 sortby = SORTMEM; 275 fmt = 1; 276 vfmt[0] = '\0'; 277 break; 278 case 'w': 279 if (wflag) 280 termwidth = UNLIMITED; 281 else if (termwidth < 131) 282 termwidth = 131; 283 wflag++; 284 break; 285 case 'x': 286 xflg = 1; 287 break; 288 case '?': 289 default: 290 usage(); 291 } 292 argc -= optind; 293 argv += optind; 294 295 #define BACKWARD_COMPATIBILITY 296 #ifdef BACKWARD_COMPATIBILITY 297 if (*argv) { 298 nlistf = *argv; 299 if (*++argv) { 300 memf = *argv; 301 } 302 } 303 #endif 304 /* 305 * Discard setgid privileges if not the running kernel so that bad 306 * guys can't print interesting stuff from kernel memory. 307 */ 308 if (dropgid) { 309 setgid(getgid()); 310 setuid(getuid()); 311 } 312 313 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 314 if (kd == 0) 315 errx(1, "%s", errbuf); 316 317 if (!fmt) 318 parsefmt(dfmt); 319 320 /* XXX - should be cleaner */ 321 if (!all && ttydev == NODEV && pid == -1 && !nuids) { 322 if ((uids = malloc(sizeof (*uids))) == NULL) 323 errx(1, "malloc: %s", strerror(errno)); 324 nuids = 1; 325 *uids = getuid(); 326 } 327 328 /* 329 * scan requested variables, noting what structures are needed, 330 * and adjusting header widths as appropriate. 331 */ 332 scanvars(); 333 /* 334 * get proc list 335 */ 336 if (nuids == 1) { 337 what = KERN_PROC_UID; 338 flag = *uids; 339 } else if (ttydev != NODEV) { 340 what = KERN_PROC_TTY; 341 flag = ttydev; 342 } else if (pid != -1) { 343 what = KERN_PROC_PID; 344 flag = pid; 345 } else { 346 what = KERN_PROC_ALL; 347 flag = 0; 348 } 349 /* 350 * select procs 351 */ 352 if ((kp = kvm_getprocs(kd, what, flag, &nentries)) == 0) 353 errx(1, "%s", kvm_geterr(kd)); 354 if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL) 355 err(1, NULL); 356 for (i = nentries; --i >= 0; ++kp) { 357 kinfo[i].ki_p = kp; 358 if (needuser) 359 saveuser(&kinfo[i]); 360 dynsizevars(&kinfo[i]); 361 } 362 363 sizevars(); 364 365 /* 366 * print header 367 */ 368 printheader(); 369 if (nentries == 0) 370 exit(1); 371 /* 372 * sort proc list 373 */ 374 qsort(kinfo, nentries, sizeof(KINFO), pscomp); 375 /* 376 * for each proc, call each variable output function. 377 */ 378 for (i = lineno = 0; i < nentries; i++) { 379 if (xflg == 0 && (KI_EPROC(&kinfo[i])->e_tdev == NODEV || 380 (KI_PROC(&kinfo[i])->p_flag & P_CONTROLT ) == 0)) 381 continue; 382 if (nuids > 1) { 383 for (uid = 0; uid < nuids; uid++) 384 if (KI_EPROC(&kinfo[i])->e_ucred.cr_uid == 385 uids[uid]) 386 break; 387 if (uid == nuids) 388 continue; 389 } 390 for (vent = vhead; vent; vent = vent->next) { 391 (vent->var->oproc)(&kinfo[i], vent); 392 if (vent->next != NULL) 393 (void)putchar(' '); 394 } 395 (void)putchar('\n'); 396 if (prtheader && lineno++ == prtheader - 4) { 397 (void)putchar('\n'); 398 printheader(); 399 lineno = 0; 400 } 401 } 402 free(uids); 403 404 exit(eval); 405 } 406 407 uid_t * 408 getuids(const char *arg, int *nuids) 409 { 410 char name[UT_NAMESIZE + 1]; 411 struct passwd *pwd; 412 uid_t *uids, *moreuids; 413 int l, alloc; 414 415 416 alloc = 0; 417 *nuids = 0; 418 uids = NULL; 419 for (; (l = strcspn(arg, SEP)) > 0; arg += l + strspn(arg + l, SEP)) { 420 if (l >= sizeof name) { 421 warnx("%.*s: name too long", l, arg); 422 continue; 423 } 424 strncpy(name, arg, l); 425 name[l] = '\0'; 426 if ((pwd = getpwnam(name)) == NULL) { 427 warnx("%s: no such user", name); 428 continue; 429 } 430 if (*nuids >= alloc) { 431 alloc = (alloc + 1) << 1; 432 moreuids = realloc(uids, alloc * sizeof (*uids)); 433 if (moreuids == NULL) { 434 free(uids); 435 errx(1, "realloc: %s", strerror(errno)); 436 } 437 uids = moreuids; 438 } 439 uids[(*nuids)++] = pwd->pw_uid; 440 } 441 endpwent(); 442 443 if (!*nuids) 444 errx(1, "No users specified"); 445 446 return uids; 447 } 448 449 static void 450 scanvars() 451 { 452 struct varent *vent; 453 VAR *v; 454 455 for (vent = vhead; vent; vent = vent->next) { 456 v = vent->var; 457 if (v->flag & DSIZ) { 458 v->dwidth = v->width; 459 v->width = 0; 460 } 461 if (v->flag & USER) 462 needuser = 1; 463 if (v->flag & COMM) 464 needcomm = 1; 465 } 466 } 467 468 static void 469 dynsizevars(ki) 470 KINFO *ki; 471 { 472 struct varent *vent; 473 VAR *v; 474 int i; 475 476 for (vent = vhead; vent; vent = vent->next) { 477 v = vent->var; 478 if (!(v->flag & DSIZ)) 479 continue; 480 i = (v->sproc)( ki); 481 if (v->width < i) 482 v->width = i; 483 if (v->width > v->dwidth) 484 v->width = v->dwidth; 485 } 486 } 487 488 static void 489 sizevars() 490 { 491 struct varent *vent; 492 VAR *v; 493 int i; 494 495 for (vent = vhead; vent; vent = vent->next) { 496 v = vent->var; 497 i = strlen(v->header); 498 if (v->width < i) 499 v->width = i; 500 totwidth += v->width + 1; /* +1 for space */ 501 } 502 totwidth--; 503 } 504 505 static char * 506 fmt(fn, ki, comm, maxlen) 507 char **(*fn) __P((kvm_t *, const struct kinfo_proc *, int)); 508 KINFO *ki; 509 char *comm; 510 int maxlen; 511 { 512 char *s; 513 514 if ((s = 515 fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen)) == NULL) 516 err(1, NULL); 517 return (s); 518 } 519 520 #define UREADOK(ki) (forceuread || (KI_PROC(ki)->p_flag & P_INMEM)) 521 522 static void 523 saveuser(ki) 524 KINFO *ki; 525 { 526 struct usave *usp; 527 528 usp = &ki->ki_u; 529 530 if (KI_PROC(ki)->p_flag & P_INMEM) { 531 /* 532 * The u-area might be swapped out, and we can't get 533 * at it because we have a crashdump and no swap. 534 * If it's here fill in these fields, otherwise, just 535 * leave them 0. 536 */ 537 usp->u_start = KI_EPROC(ki)->e_stats.p_start; 538 usp->u_ru = KI_EPROC(ki)->e_stats.p_ru; 539 usp->u_cru = KI_EPROC(ki)->e_stats.p_cru; 540 usp->u_valid = 1; 541 } else 542 usp->u_valid = 0; 543 /* 544 * save arguments if needed 545 */ 546 if (needcomm && (UREADOK(ki) || (KI_PROC(ki)->p_args != NULL))) { 547 ki->ki_args = fmt(kvm_getargv, ki, KI_PROC(ki)->p_comm, 548 MAXCOMLEN); 549 } else if (needcomm) { 550 ki->ki_args = malloc(strlen(KI_PROC(ki)->p_comm) + 3); 551 sprintf(ki->ki_args, "(%s)", KI_PROC(ki)->p_comm); 552 } else { 553 ki->ki_args = NULL; 554 } 555 if (needenv && UREADOK(ki)) { 556 ki->ki_env = fmt(kvm_getenvv, ki, (char *)NULL, 0); 557 } else if (needenv) { 558 ki->ki_env = malloc(3); 559 strcpy(ki->ki_env, "()"); 560 } else { 561 ki->ki_env = NULL; 562 } 563 } 564 565 static int 566 pscomp(a, b) 567 const void *a, *b; 568 { 569 int i; 570 #define VSIZE(k) (KI_EPROC(k)->e_vm.vm_dsize + KI_EPROC(k)->e_vm.vm_ssize + \ 571 KI_EPROC(k)->e_vm.vm_tsize) 572 573 if (sortby == SORTCPU) 574 return (getpcpu((KINFO *)b) - getpcpu((KINFO *)a)); 575 if (sortby == SORTMEM) 576 return (VSIZE((KINFO *)b) - VSIZE((KINFO *)a)); 577 i = KI_EPROC((KINFO *)a)->e_tdev - KI_EPROC((KINFO *)b)->e_tdev; 578 if (i == 0) 579 i = KI_PROC((KINFO *)a)->p_pid - KI_PROC((KINFO *)b)->p_pid; 580 return (i); 581 } 582 583 /* 584 * ICK (all for getopt), would rather hide the ugliness 585 * here than taint the main code. 586 * 587 * ps foo -> ps -foo 588 * ps 34 -> ps -p34 589 * 590 * The old convention that 't' with no trailing tty arg means the users 591 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 592 * feature is available with the option 'T', which takes no argument. 593 */ 594 static char * 595 kludge_oldps_options(s) 596 char *s; 597 { 598 size_t len; 599 char *newopts, *ns, *cp; 600 601 len = strlen(s); 602 if ((newopts = ns = malloc(len + 2)) == NULL) 603 err(1, NULL); 604 /* 605 * options begin with '-' 606 */ 607 if (*s != '-') 608 *ns++ = '-'; /* add option flag */ 609 /* 610 * gaze to end of argv[1] 611 */ 612 cp = s + len - 1; 613 /* 614 * if last letter is a 't' flag with no argument (in the context 615 * of the oldps options -- option string NOT starting with a '-' -- 616 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 617 * 618 * However, if a flag accepting a string argument is found in the 619 * option string, the remainder of the string is the argument to 620 * that flag; do not modify that argument. 621 */ 622 if (strcspn(s, "MNOoU") == len && *cp == 't' && *s != '-') 623 *cp = 'T'; 624 else { 625 /* 626 * otherwise check for trailing number, which *may* be a 627 * pid. 628 */ 629 while (cp >= s && isdigit(*cp)) 630 --cp; 631 } 632 cp++; 633 memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */ 634 ns += cp - s; 635 /* 636 * if there's a trailing number, and not a preceding 'p' (pid) or 637 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 638 */ 639 if (isdigit(*cp) && 640 (cp == s || (cp[-1] != 't' && cp[-1] != 'p')) && 641 (cp - 1 == s || cp[-2] != 't')) 642 *ns++ = 'p'; 643 (void)strcpy(ns, cp); /* and append the number */ 644 645 return (newopts); 646 } 647 648 static void 649 usage() 650 { 651 652 (void)fprintf(stderr, "%s\n%s\n%s\n", 653 "usage: ps [-aChjlmrSTuvwx] [-O|o fmt] [-p pid] [-t tty] [-U user]", 654 " [-M core] [-N system]", 655 " ps [-L]"); 656 exit(1); 657 } 658