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