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 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * ------+---------+---------+-------- + --------+---------+---------+---------* 29 * Copyright (c) 2004 - Garance Alistair Drosehn <gad@FreeBSD.org>. 30 * All rights reserved. 31 * 32 * Significant modifications made to bring `ps' options somewhat closer 33 * to the standard for `ps' as described in SingleUnixSpec-v3. 34 * ------+---------+---------+-------- + --------+---------+---------+---------* 35 */ 36 37#ifndef lint 38static const char copyright[] = 39"@(#) Copyright (c) 1990, 1993, 1994\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41#endif /* not lint */ 42 43#if 0 44#ifndef lint 45static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94"; 46#endif /* not lint */ 47#endif 48 49#include <sys/cdefs.h> 50__FBSDID("$FreeBSD: src/bin/ps/ps.c,v 1.110 2005/02/09 17:37:38 ru Exp $"); 51 52#include <sys/param.h> 53#ifdef __APPLE__ 54#include <sys/time.h> 55#endif /* __APPLE__ */ 56#include <sys/proc.h> 57#include <sys/user.h> 58#include <sys/stat.h> 59#include <sys/ioctl.h> 60#include <sys/sysctl.h> 61#include <sys/mount.h> 62#include <sys/resourcevar.h> 63 64#include <ctype.h> 65#include <err.h> 66#include <errno.h> 67#include <fcntl.h> 68#include <grp.h> 69#ifndef __APPLE__ 70#include <kvm.h> 71#endif /* !__APPLE__ */ 72#include <limits.h> 73#include <locale.h> 74#include <paths.h> 75#include <pwd.h> 76#include <stdio.h> 77#include <stdlib.h> 78#include <string.h> 79#include <unistd.h> 80 81#include "ps.h" 82 83#ifdef __APPLE__ 84#include <get_compat.h> 85#else /* !__APPLE__ */ 86#define COMPAT_MODE(func, mode) (1) 87#endif /* __APPLE__ */ 88 89#define W_SEP " \t" /* "Whitespace" list separators */ 90#define T_SEP "," /* "Terminate-element" list separators */ 91 92#ifdef LAZY_PS 93#define DEF_UREAD 0 94#define OPT_LAZY_f "f" 95#else 96#define DEF_UREAD 1 /* Always do the more-expensive read. */ 97#define OPT_LAZY_f /* I.e., the `-f' option is not added. */ 98#endif 99 100/* 101 * isdigit takes an `int', but expects values in the range of unsigned char. 102 * This wrapper ensures that values from a 'char' end up in the correct range. 103 */ 104#define isdigitch(Anychar) isdigit((u_char)(Anychar)) 105 106int cflag; /* -c */ 107int eval; /* Exit value */ 108time_t now; /* Current time(3) value */ 109int rawcpu; /* -C */ 110int sumrusage; /* -S */ 111int termwidth; /* Width of the screen (0 == infinity). */ 112int totwidth; /* Calculated-width of requested variables. */ 113 114struct velisthead varlist = STAILQ_HEAD_INITIALIZER(varlist); 115 116#ifndef __APPLE__ 117static int forceuread = DEF_UREAD; /* Do extra work to get u-area. */ 118static kvm_t *kd; 119#endif /* !__APPLE__ */ 120static KINFO *kinfo; 121static int needcomm; /* -o "command" */ 122static int needenv; /* -e */ 123static int needuser; /* -o "user" */ 124static int optfatal; /* Fatal error parsing some list-option. */ 125 126static enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 127 128struct listinfo; 129typedef int addelem_rtn(struct listinfo *_inf, const char *_elem); 130 131struct listinfo { 132 int count; 133 int maxcount; 134 int elemsize; 135 addelem_rtn *addelem; 136 const char *lname; 137 union { 138 gid_t *gids; 139 pid_t *pids; 140 dev_t *ttys; 141 uid_t *uids; 142 void *ptr; 143 } l; 144}; 145 146#ifndef __APPLE__ 147static int check_procfs(void); 148#endif /* !__APPLE__ */ 149static int addelem_gid(struct listinfo *, const char *); 150static int addelem_pid(struct listinfo *, const char *); 151static int addelem_tty(struct listinfo *, const char *); 152static int addelem_uid(struct listinfo *, const char *); 153static void add_list(struct listinfo *, const char *); 154static void dynsizevars(KINFO *); 155static void *expand_list(struct listinfo *); 156#ifndef __APPLE__ 157static const char * 158 fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int), 159 KINFO *, char *, int); 160#endif /* !__APPLE__ */ 161static void free_list(struct listinfo *); 162static void init_list(struct listinfo *, addelem_rtn, int, const char *); 163static char *kludge_oldps_options(const char *, char *, const char *, int *); 164static int pscomp(const void *, const void *); 165static void saveuser(KINFO *); 166static void scanvars(void); 167static void sizevars(void); 168static void usage(int); 169 170/* 5842004: Fix -f option. */ 171VAR *findvar(char *, int, char **); 172 173/* p_ == POSIX/SUSv3/UNIX2003 format */ 174static char dfmt[] = "pid,tt,state,time,command"; 175static char jfmt[] = "user,pid,ppid,pgid,sess,jobc,state,tt,time,command"; 176static char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,wchan,state," 177 "tt,time,command"; 178static char o1[] = "pid"; 179static char o2[] = "tt,state,time,command"; 180static char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command"; 181static char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz," 182 "%cpu,%mem,command"; 183#ifndef __APPLE__ 184static char Zfmt[] = "label"; 185#endif /* !__APPLE__ */ 186char p_dfmt[] = "pid tty time command=CMD"; 187char p_ffmt[] = "uid pid ppid cpu=C start=STIME tty time command=CMD"; 188char p_uffmt[] = "user pid ppid cpu=C start=STIME tty time command=CMD"; 189char p_lfmt[] = "uid pid ppid flags cpu pri nice vsz=SZ rss wchan state=S paddr=ADDR tty time command=CMD"; 190char mfmt[] = "user pid tt %cpu state pri stime utime command"; 191 192int eflg = 0; 193int mflg = 0; /* if -M option to display all mach threads */ 194int print_thread_num = 0; 195int print_all_thread = 0; 196 197#define PS_ARGS (u03 ? "aACcdeEfg:G:hjLlMmO:o:p:rSTt:U:u:vwx" : \ 198 "aACcdeEgG:hjLlMmO:o:p:rSTt:U:uvwx") 199 200int 201main(int argc, char *argv[]) 202{ 203 struct listinfo gidlist, pgrplist, pidlist; 204 struct listinfo ruidlist, sesslist, ttylist, uidlist; 205 struct kinfo_proc *kp; 206 KINFO *next_KINFO; 207 struct varent *vent; 208 struct winsize ws; 209#ifndef __APPLE__ 210 const char *nlistf, *memf; 211#endif /* !__APPLE__ */ 212 char *cols; 213 int all, ch, elem, flag, _fmt, i, lineno; 214 int nentries, nkept, nselectors; 215 int prtheader, showthreads, wflag, what, xkeep, xkeep_implied; 216#ifndef __APPLE__ 217 char errbuf[_POSIX2_LINE_MAX]; 218#endif /* !__APPLE__ */ 219 struct kinfo_proc *kprocbuf; 220 int j; 221 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; 222 size_t bufSize = 0; 223 size_t orig_bufSize = 0; 224 int local_error=0; 225 int retry_count = 0; 226 int u03 = COMPAT_MODE("bin/ps", "unix2003"); 227#ifdef __APPLE__ 228 int dflag = 0; 229#endif /* __APPLE__ */ 230 231 (void) setlocale(LC_ALL, ""); 232 time(&now); /* Used by routines in print.c. */ 233 234 if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') 235 termwidth = atoi(cols); 236 else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 237 ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 238 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 239 ws.ws_col == 0) 240 termwidth = 79; 241 else 242 termwidth = ws.ws_col - 1; 243 244 /* 245 * Hide a number of option-processing kludges in a separate routine, 246 * to support some historical BSD behaviors, such as `ps axu'. 247 */ 248 if (argc > 1) 249 argv[1] = kludge_oldps_options(PS_ARGS, argv[1], argv[2], &u03); 250 251 all = _fmt = nselectors = optfatal = 0; 252 prtheader = showthreads = wflag = xkeep_implied = 0; 253 xkeep = -1; /* Neither -x nor -X. */ 254 init_list(&gidlist, addelem_gid, sizeof(gid_t), "group"); 255 init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group"); 256 init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id"); 257 init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser"); 258 init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id"); 259 init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty"); 260 init_list(&uidlist, addelem_uid, sizeof(uid_t), "user"); 261#ifndef __APPLE__ 262 memf = nlistf = _PATH_DEVNULL; 263#endif /* !__APPLE__ */ 264 while ((ch = getopt(argc, argv, PS_ARGS)) != -1) 265 switch ((char)ch) { 266#ifdef __APPLE__ 267 case 'd': 268 dflag = 1; 269#endif /* __APPLE__ */ 270 case 'A': 271 /* 272 * Exactly the same as `-ax'. This has been 273 * added for compatability with SUSv3, but for 274 * now it will not be described in the man page. 275 */ 276 nselectors++; 277 all = xkeep = 1; 278 break; 279 case 'a': 280 nselectors++; 281 all = 1; 282 break; 283 case 'C': 284 rawcpu = 1; 285 break; 286 case 'c': 287 cflag = 1; 288 break; 289 case 'e': /* XXX set ufmt */ 290 if (u03) { 291 nselectors++; 292 all = xkeep = 1; 293 break; 294 } 295 case 'E': 296 needenv = 1; 297 eflg = 1; 298 break; 299#ifdef LAZY_PS 300 case 'f': 301 if (getuid() == 0 || getgid() == 0) 302 forceuread = 1; 303 break; 304#endif 305 case 'f': 306 termwidth = UNLIMITED; /* 4990408 */ 307 if (u03 && uidlist.count == 0) { 308 parsefmt(p_ffmt, 0); 309 /* This is a unplesent little trick that makes 310 ./ps -f -p PID -o pid,comm,args 311 print out the whole command even if they slap 312 more fields on after it and gobble up too much 313 space */ 314 VAR *v = findvar("command", 0, NULL); 315 if (v) { 316 v->width = 64; 317 } 318 } else { 319 parsefmt(p_uffmt, 0); 320 } 321 _fmt = 1; 322 break; 323 case 'G': 324 add_list(&gidlist, optarg); 325 xkeep_implied = 1; 326 nselectors++; 327 break; 328 case 'g': 329 /* The historical BSD-ish (from SunOS) behavior. */ 330 if (!u03) break; 331 332 add_list(&pgrplist, optarg); 333 xkeep_implied = 1; 334 nselectors++; 335 break; 336#ifndef __APPLE__ 337 case 'H': 338 showthreads = KERN_PROC_INC_THREAD; 339 break; 340#endif /* !__APPLE__ */ 341 case 'h': 342 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 343 break; 344 case 'j': 345 parsefmt(jfmt, 0); 346 _fmt = 1; 347 jfmt[0] = '\0'; 348 break; 349 case 'L': 350 showkey(); 351 exit(0); 352 case 'l': 353 parsefmt(u03 ? p_lfmt : lfmt, 0); 354 _fmt = 1; 355 lfmt[0] = '\0'; 356 break; 357 case 'M': 358#ifndef __APPLE__ 359 memf = optarg; 360#else 361 parsefmt(mfmt, 0); 362 _fmt = 1; 363 mfmt[0] = '\0'; 364 mflg = 1; 365#endif /* 0 */ 366 break; 367 case 'm': 368 sortby = SORTMEM; 369 break; 370#ifndef __APPLE__ 371 case 'N': 372 nlistf = optarg; 373 break; 374#endif /* !__APPLE__ */ 375 case 'O': 376 parsefmt(o1, 1); 377 parsefmt(optarg, 1); 378 parsefmt(o2, 1); 379 o1[0] = o2[0] = '\0'; 380 _fmt = 1; 381 break; 382 case 'o': 383 parsefmt(optarg, 1); 384 _fmt = 1; 385 break; 386 case 'p': 387 add_list(&pidlist, optarg); 388 /* 389 * Note: `-p' does not *set* xkeep, but any values 390 * from pidlist are checked before xkeep is. That 391 * way they are always matched, even if the user 392 * specifies `-X'. 393 */ 394 nselectors++; 395 break; 396 case 'r': 397 sortby = SORTCPU; 398 break; 399 case 'S': 400 sumrusage = 1; 401 break; 402 case 'T': 403 if ((optarg = ttyname(STDIN_FILENO)) == NULL) 404 errx(1, "stdin: not a terminal"); 405 /* FALLTHROUGH */ 406 case 't': 407 add_list(&ttylist, optarg); 408 xkeep_implied = 1; 409 nselectors++; 410 break; 411 case 'U': 412 add_list(&ruidlist, optarg); 413 xkeep_implied = 1; 414 nselectors++; 415 break; 416 case 'u': 417 if (u03) { 418 /* This is what SUSv3 defines as the `-u' option. */ 419 add_list(&uidlist, optarg); 420 xkeep_implied = 1; 421 nselectors++; 422 break; 423 } 424 parsefmt(ufmt, 0); 425 sortby = SORTCPU; 426 _fmt = 1; 427 ufmt[0] = '\0'; 428 break; 429 case 'v': 430 parsefmt(vfmt, 0); 431 sortby = SORTMEM; 432 _fmt = 1; 433 vfmt[0] = '\0'; 434 break; 435 case 'w': 436 if (wflag) 437 termwidth = UNLIMITED; 438 else if (termwidth < 131) 439 termwidth = 131; 440 wflag++; 441 break; 442 case 'X': 443 /* 444 * Note that `-X' and `-x' are not standard "selector" 445 * options. For most selector-options, we check *all* 446 * processes to see if any are matched by the given 447 * value(s). After we have a set of all the matched 448 * processes, then `-X' and `-x' govern whether we 449 * modify that *matched* set for processes which do 450 * not have a controlling terminal. `-X' causes 451 * those processes to be deleted from the matched 452 * set, while `-x' causes them to be kept. 453 */ 454 xkeep = 0; 455 break; 456 case 'x': 457 xkeep = 1; 458 break; 459 case '?': 460 default: 461 usage(u03); 462 } 463 argc -= optind; 464 argv += optind; 465 466#ifdef __APPLE__ 467 /* 3862041 */ 468 if (!isatty(STDOUT_FILENO)) 469 termwidth = UNLIMITED; 470#endif /* __APPLE__ */ 471 472 /* 473 * If the user specified ps -e then they want a copy of the process 474 * environment kvm_getenvv(3) attempts to open /proc/<pid>/mem. 475 * Check to make sure that procfs is mounted on /proc, otherwise 476 * print a warning informing the user that output will be incomplete. 477 */ 478#ifndef __APPLE__ 479 if (needenv == 1 && check_procfs() == 0) 480 warnx("Process environment requires procfs(5)"); 481#endif /* !__APPLE__ */ 482 /* 483 * If there arguments after processing all the options, attempt 484 * to treat them as a list of process ids. 485 */ 486 while (*argv) { 487 if (!isdigitch(**argv)) 488 break; 489 add_list(&pidlist, *argv); 490 argv++; 491 } 492 if (*argv) { 493 fprintf(stderr, "%s: illegal argument: %s\n", 494 getprogname(), *argv); 495 usage(u03); 496 } 497 if (optfatal) 498 exit(1); /* Error messages already printed. */ 499 if (xkeep < 0) /* Neither -X nor -x was specified. */ 500 xkeep = xkeep_implied; 501 502#if FIXME 503 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 504 if (kd == 0) 505 errx(1, "%s", errbuf); 506#endif /* FIXME */ 507 508 if (!_fmt) { 509 if (u03 && uidlist.count != 0) { 510 parsefmt("uid", 0); 511 } 512 parsefmt(u03 ? p_dfmt : dfmt, 0); 513 } 514 515 if (nselectors == 0) { 516 uidlist.l.ptr = malloc(sizeof(uid_t)); 517 if (uidlist.l.ptr == NULL) 518 errx(1, "malloc failed"); 519 nselectors = 1; 520 uidlist.count = uidlist.maxcount = 1; 521 *uidlist.l.uids = getuid(); 522 } 523 524 /* 525 * scan requested variables, noting what structures are needed, 526 * and adjusting header widths as appropriate. 527 */ 528 scanvars(); 529 530 /* 531 * Get process list. If the user requested just one selector- 532 * option, then kvm_getprocs can be asked to return just those 533 * processes. Otherwise, have it return all processes, and 534 * then this routine will search that full list and select the 535 * processes which match any of the user's selector-options. 536 */ 537 what = KERN_PROC_ALL; 538 flag = 0; 539 if (nselectors == 1) { 540 if (gidlist.count == 1) { 541#if 0 542 what = KERN_PROC_RGID | showthreads; 543 flag = *gidlist.l.gids; 544 nselectors = 0; 545#endif /* 0 */ 546 } else if (pgrplist.count == 1) { 547 what = KERN_PROC_PGRP | showthreads; 548 flag = *pgrplist.l.pids; 549 nselectors = 0; 550 } else if (pidlist.count == 1) { 551 what = KERN_PROC_PID | showthreads; 552 flag = *pidlist.l.pids; 553 nselectors = 0; 554 } else if (ruidlist.count == 1) { 555 what = KERN_PROC_RUID | showthreads; 556 flag = *ruidlist.l.uids; 557 nselectors = 0; 558 } else if (sesslist.count == 1) { 559 what = KERN_PROC_SESSION | showthreads; 560 flag = *sesslist.l.pids; 561 nselectors = 0; 562 } else if (ttylist.count == 1) { 563 what = KERN_PROC_TTY | showthreads; 564 flag = *ttylist.l.ttys; 565 nselectors = 0; 566 } else if (uidlist.count == 1) { 567 what = (xkeep ? KERN_PROC_RUID : KERN_PROC_UID) | showthreads; 568 flag = *uidlist.l.uids; 569 nselectors = 0; 570 } 571 } 572 573 /* 574 * select procs 575 */ 576 nentries = -1; 577#if FIXME 578 kp = kvm_getprocs(kd, what, flag, &nentries); 579 if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0)) 580 errx(1, "%s", kvm_geterr(kd)); 581#else /* FIXME */ 582 mib[0] = CTL_KERN; 583 mib[1] = KERN_PROC; 584 mib[2] = what; 585 mib[3] = flag; 586 587 if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0) { 588 perror("Failure calling sysctl"); 589 return 0; 590 } 591 592 kprocbuf= kp = (struct kinfo_proc *)malloc(bufSize); 593 594 retry_count = 0; 595 orig_bufSize = bufSize; 596 for(retry_count=0; ; retry_count++) { 597 /* retry for transient errors due to load in the system */ 598 local_error = 0; 599 bufSize = orig_bufSize; 600 if ((local_error = sysctl(mib, 4, kp, &bufSize, NULL, 0)) < 0) { 601 if (retry_count < 1000) { 602 /* 1 sec back off */ 603 sleep(1); 604 continue; 605 } 606 perror("Failure calling sysctl"); 607 return 0; 608 } else if (local_error == 0) { 609 break; 610 } 611 /* 1 sec back off */ 612 sleep(1); 613 } 614 615 /* This has to be after the second sysctl since the bufSize 616 may have changed. */ 617 nentries = bufSize / sizeof(struct kinfo_proc); 618#endif /* FIXME */ 619 nkept = 0; 620 if (nentries > 0) { 621 if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL) 622 errx(1, "malloc failed"); 623 for (i = nentries; --i >= 0; ++kp) { 624#ifdef __APPLE__ 625 if (kp->kp_proc.p_pid == 0) { 626 continue; 627 } 628#endif /* __APPLE__ */ 629 630#ifdef __APPLE__ 631 if (dflag && (kp->kp_proc.p_pid == kp->kp_eproc.e_pgid)) 632 continue; 633#endif /* __APPLE__ */ 634 635 /* 636 * If the user specified multiple selection-criteria, 637 * then keep any process matched by the inclusive OR 638 * of all the selection-criteria given. 639 */ 640 if (pidlist.count > 0) { 641 for (elem = 0; elem < pidlist.count; elem++) 642 if (kp->kp_proc.p_pid == pidlist.l.pids[elem]) 643 goto keepit; 644 } 645 /* 646 * Note that we had to process pidlist before 647 * filtering out processes which do not have 648 * a controlling terminal. 649 */ 650 if (xkeep == 0) { 651 if ((kp->kp_eproc.e_tdev == NODEV || 652 (kp->kp_proc.p_flag & P_CONTROLT) == 0)) 653 continue; 654 } 655 if (all || nselectors == 0) 656 goto keepit; 657 if (gidlist.count > 0) { 658 for (elem = 0; elem < gidlist.count; elem++) 659 if (kp->kp_eproc.e_pcred.p_rgid == gidlist.l.gids[elem]) 660 goto keepit; 661 } 662 if (pgrplist.count > 0) { 663 for (elem = 0; elem < pgrplist.count; elem++) 664 if (kp->kp_eproc.e_pgid == 665 pgrplist.l.pids[elem]) 666 goto keepit; 667 } 668 if (ruidlist.count > 0) { 669 for (elem = 0; elem < ruidlist.count; elem++) 670 if (kp->kp_eproc.e_pcred.p_ruid == 671 ruidlist.l.uids[elem]) 672 goto keepit; 673 } 674#if 0 675 if (sesslist.count > 0) { 676 for (elem = 0; elem < sesslist.count; elem++) 677 if (kp->ki_sid == sesslist.l.pids[elem]) 678 goto keepit; 679 } 680#endif 681 if (ttylist.count > 0) { 682 for (elem = 0; elem < ttylist.count; elem++) 683 if (kp->kp_eproc.e_tdev == ttylist.l.ttys[elem]) 684 goto keepit; 685 } 686 if (uidlist.count > 0) { 687 for (elem = 0; elem < uidlist.count; elem++) 688 if (kp->kp_eproc.e_ucred.cr_uid == uidlist.l.uids[elem]) 689 goto keepit; 690 } 691 /* 692 * This process did not match any of the user's 693 * selector-options, so skip the process. 694 */ 695 continue; 696 697 keepit: 698 next_KINFO = &kinfo[nkept]; 699 next_KINFO->ki_p = kp; 700 get_task_info(next_KINFO); 701#ifndef __APPLE__ 702 next_KINFO->ki_pcpu = getpcpu(next_KINFO); 703 if (sortby == SORTMEM) 704 next_KINFO->ki_memsize = kp->ki_tsize + 705 kp->ki_dsize + kp->ki_ssize; 706#endif /* !__APPLE__ */ 707 if (needuser) 708 saveuser(next_KINFO); 709 dynsizevars(next_KINFO); 710 nkept++; 711 } 712 } 713 714 sizevars(); 715 716 /* 717 * print header 718 */ 719 printheader(); 720 if (nkept == 0) 721 exit(1); 722 723 /* 724 * sort proc list 725 */ 726 qsort(kinfo, nkept, sizeof(KINFO), pscomp); 727 /* 728 * For each process, call each variable output function. 729 */ 730 for (i = lineno = 0; i < nkept; i++) { 731 if(mflg) { 732 print_all_thread = 1; 733 for(j=0; j < kinfo[i].thread_count; j++) { 734 print_thread_num = j; 735 STAILQ_FOREACH(vent, &varlist, next_ve) { 736 (vent->var->oproc)(&kinfo[i], vent); 737 if (STAILQ_NEXT(vent, next_ve) != NULL) 738 (void)putchar(' '); 739 } 740 741 (void)putchar('\n'); 742 if (prtheader && lineno++ == prtheader - 4) { 743 (void)putchar('\n'); 744 printheader(); 745 lineno = 0; 746 } 747 } 748 print_all_thread = 0; 749 } else { 750 STAILQ_FOREACH(vent, &varlist, next_ve) { 751 (vent->var->oproc)(&kinfo[i], vent); 752 if (STAILQ_NEXT(vent, next_ve) != NULL) 753 (void)putchar(' '); 754 } 755 756 (void)putchar('\n'); 757 if (prtheader && lineno++ == prtheader - 4) { 758 (void)putchar('\n'); 759 printheader(); 760 lineno = 0; 761 } 762 } 763 } 764 for (i = 0; i < nkept; i++) { 765 if (kinfo[i].invalid_tinfo == 0 && kinfo[i].thread_count) 766 free(kinfo[i].thval); 767 } 768 free(kprocbuf); 769 free(kinfo); 770 free_list(&gidlist); 771 free_list(&pidlist); 772 free_list(&pgrplist); 773 free_list(&ruidlist); 774 free_list(&sesslist); 775 free_list(&ttylist); 776 free_list(&uidlist); 777 778 exit(eval); 779} 780 781static int 782addelem_gid(struct listinfo *inf, const char *elem) 783{ 784 struct group *grp; 785 const char *nameorID; 786 char *endp; 787 u_long bigtemp; 788 789 if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) { 790 if (*elem == '\0') 791 warnx("Invalid (zero-length) %s name", inf->lname); 792 else 793 warnx("%s name too long: %s", inf->lname, elem); 794 optfatal = 1; 795 return (0); /* Do not add this value. */ 796 } 797 798 /* 799 * SUSv3 states that `ps -G grouplist' should match "real-group 800 * ID numbers", and does not mention group-names. I do want to 801 * also support group-names, so this tries for a group-id first, 802 * and only tries for a name if that doesn't work. This is the 803 * opposite order of what is done in addelem_uid(), but in 804 * practice the order would only matter for group-names which 805 * are all-numeric. 806 */ 807 grp = NULL; 808 nameorID = "named"; 809 errno = 0; 810 bigtemp = strtoul(elem, &endp, 10); 811 if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) { 812 nameorID = "name or ID matches"; 813 grp = getgrgid((gid_t)bigtemp); 814 } 815 if (grp == NULL) 816 grp = getgrnam(elem); 817 if (grp == NULL) { 818 warnx("No %s %s '%s'", inf->lname, nameorID, elem); 819 optfatal = 1; 820 return (0); 821 } 822 if (inf->count >= inf->maxcount) 823 expand_list(inf); 824 inf->l.gids[(inf->count)++] = grp->gr_gid; 825 return (1); 826} 827 828#define BSD_PID_MAX 99999 /* Copy of PID_MAX from sys/proc.h. */ 829static int 830addelem_pid(struct listinfo *inf, const char *elem) 831{ 832 char *endp; 833 long tempid; 834 835 if (*elem == '\0') { 836 warnx("Invalid (zero-length) process id"); 837 optfatal = 1; 838 return (0); /* Do not add this value. */ 839 } 840 841 errno = 0; 842 tempid = strtol(elem, &endp, 10); 843 if (*endp != '\0' || tempid < 0 || elem == endp) { 844 warnx("Invalid %s: %s", inf->lname, elem); 845 errno = ERANGE; 846 } else if (errno != 0 || tempid > BSD_PID_MAX) { 847 warnx("%s too large: %s", inf->lname, elem); 848 errno = ERANGE; 849 } 850 if (errno == ERANGE) { 851 optfatal = 1; 852 return (0); 853 } 854 if (inf->count >= inf->maxcount) 855 expand_list(inf); 856 inf->l.pids[(inf->count)++] = tempid; 857 return (1); 858} 859#undef BSD_PID_MAX 860 861/*- 862 * The user can specify a device via one of three formats: 863 * 1) fully qualified, e.g.: /dev/ttyp0 /dev/console 864 * 2) missing "/dev", e.g.: ttyp0 console 865 * 3) two-letters, e.g.: p0 co 866 * (matching letters that would be seen in the "TT" column) 867 */ 868static int 869addelem_tty(struct listinfo *inf, const char *elem) 870{ 871 const char *ttypath; 872 struct stat sb; 873 char pathbuf[PATH_MAX], pathbuf2[PATH_MAX]; 874 875 ttypath = NULL; 876 pathbuf2[0] = '\0'; 877 switch (*elem) { 878 case '/': 879 ttypath = elem; 880 break; 881 case 'c': 882 if (strcmp(elem, "co") == 0) { 883 ttypath = _PATH_CONSOLE; 884 break; 885 } 886 /* FALLTHROUGH */ 887 default: 888 strlcpy(pathbuf, _PATH_DEV, sizeof(pathbuf)); 889 strlcat(pathbuf, elem, sizeof(pathbuf)); 890 ttypath = pathbuf; 891 if (strncmp(pathbuf, _PATH_TTY, strlen(_PATH_TTY)) == 0) 892 break; 893 if (strcmp(pathbuf, _PATH_CONSOLE) == 0) 894 break; 895 /* Check to see if /dev/tty${elem} exists */ 896 strlcpy(pathbuf2, _PATH_TTY, sizeof(pathbuf2)); 897 strlcat(pathbuf2, elem, sizeof(pathbuf2)); 898 if (stat(pathbuf2, &sb) == 0 && S_ISCHR(sb.st_mode)) { 899 /* No need to repeat stat() && S_ISCHR() checks */ 900 ttypath = NULL; 901 break; 902 } 903 break; 904 } 905 if (ttypath) { 906#ifdef __APPLE__ 907 if (access(ttypath, F_OK) == -1 || stat(ttypath, &sb) == -1) { 908#else 909 if (stat(ttypath, &sb) == -1) { 910#endif 911 if (pathbuf2[0] != '\0') 912 warn("%s and %s", pathbuf2, ttypath); 913 else 914 warn("%s", ttypath); 915 optfatal = 1; 916 return (0); 917 } 918 if (!S_ISCHR(sb.st_mode)) { 919 if (pathbuf2[0] != '\0') 920 warnx("%s and %s: Not a terminal", pathbuf2, 921 ttypath); 922 else 923 warnx("%s: Not a terminal", ttypath); 924 optfatal = 1; 925 return (0); 926 } 927 } 928 if (inf->count >= inf->maxcount) 929 expand_list(inf); 930 inf->l.ttys[(inf->count)++] = sb.st_rdev; 931 return (1); 932} 933 934static int 935addelem_uid(struct listinfo *inf, const char *elem) 936{ 937 struct passwd *pwd; 938 char *endp; 939 u_long bigtemp; 940 941 if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) { 942 if (*elem == '\0') 943 warnx("Invalid (zero-length) %s name", inf->lname); 944 else 945 warnx("%s name too long: %s", inf->lname, elem); 946 optfatal = 1; 947 return (0); /* Do not add this value. */ 948 } 949 950 pwd = getpwnam(elem); 951 if (pwd == NULL) { 952 errno = 0; 953 bigtemp = strtoul(elem, &endp, 10); 954 if (errno != 0 || *endp != '\0' || bigtemp > UID_MAX) 955 warnx("No %s named '%s'", inf->lname, elem); 956 else { 957 /* The string is all digits, so it might be a userID. */ 958 pwd = getpwuid((uid_t)bigtemp); 959 if (pwd == NULL) 960 warnx("No %s name or ID matches '%s'", 961 inf->lname, elem); 962 } 963 } 964 if (pwd == NULL) { 965 /* 966 * These used to be treated as minor warnings (and the 967 * option was simply ignored), but now they are fatal 968 * errors (and the command will be aborted). 969 */ 970 optfatal = 1; 971 return (0); 972 } 973 if (inf->count >= inf->maxcount) 974 expand_list(inf); 975 inf->l.uids[(inf->count)++] = pwd->pw_uid; 976 return (1); 977} 978 979static void 980add_list(struct listinfo *inf, const char *argp) 981{ 982 const char *savep; 983 char *cp, *endp; 984 int toolong; 985 char elemcopy[PATH_MAX]; 986 987 if (*argp == 0) 988 inf->addelem(inf, elemcopy); 989 while (*argp != '\0') { 990 while (*argp != '\0' && strchr(W_SEP, *argp) != NULL) 991 argp++; 992 savep = argp; 993 toolong = 0; 994 cp = elemcopy; 995 if (strchr(T_SEP, *argp) == NULL) { 996 endp = elemcopy + sizeof(elemcopy) - 1; 997 while (*argp != '\0' && cp <= endp && 998 strchr(W_SEP T_SEP, *argp) == NULL) 999 *cp++ = *argp++; 1000 if (cp > endp) 1001 toolong = 1; 1002 } 1003 if (!toolong) { 1004 *cp = '\0'; 1005 /* 1006 * Add this single element to the given list. 1007 */ 1008 inf->addelem(inf, elemcopy); 1009 } else { 1010 /* 1011 * The string is too long to copy. Find the end 1012 * of the string to print out the warning message. 1013 */ 1014 while (*argp != '\0' && strchr(W_SEP T_SEP, 1015 *argp) == NULL) 1016 argp++; 1017 warnx("Value too long: %.*s", (int)(argp - savep), 1018 savep); 1019 optfatal = 1; 1020 } 1021 /* 1022 * Skip over any number of trailing whitespace characters, 1023 * but only one (at most) trailing element-terminating 1024 * character. 1025 */ 1026 while (*argp != '\0' && strchr(W_SEP, *argp) != NULL) 1027 argp++; 1028 if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) { 1029 argp++; 1030#if 0 1031 /* Catch case where string ended with a comma. */ 1032 if (*argp == '\0') 1033 inf->addelem(inf, argp); 1034#endif /* 0 */ 1035 } 1036 } 1037} 1038 1039static void * 1040expand_list(struct listinfo *inf) 1041{ 1042 void *newlist; 1043 int newmax; 1044 1045 newmax = (inf->maxcount + 1) << 1; 1046 newlist = realloc(inf->l.ptr, newmax * inf->elemsize); 1047 if (newlist == NULL) { 1048 free(inf->l.ptr); 1049 errx(1, "realloc to %d %ss failed", newmax, inf->lname); 1050 } 1051 inf->maxcount = newmax; 1052 inf->l.ptr = newlist; 1053 1054 return (newlist); 1055} 1056 1057static void 1058free_list(struct listinfo *inf) 1059{ 1060 1061 inf->count = inf->elemsize = inf->maxcount = 0; 1062 if (inf->l.ptr != NULL) 1063 free(inf->l.ptr); 1064 inf->addelem = NULL; 1065 inf->lname = NULL; 1066 inf->l.ptr = NULL; 1067} 1068 1069static void 1070init_list(struct listinfo *inf, addelem_rtn artn, int elemsize, 1071 const char *lname) 1072{ 1073 1074 inf->count = inf->maxcount = 0; 1075 inf->elemsize = elemsize; 1076 inf->addelem = artn; 1077 inf->lname = lname; 1078 inf->l.ptr = NULL; 1079} 1080 1081VARENT * 1082find_varentry(VAR *v) 1083{ 1084 struct varent *vent; 1085 1086 STAILQ_FOREACH(vent, &varlist, next_ve) { 1087 if (strcmp(vent->var->name, v->name) == 0) 1088 return vent; 1089 } 1090 return NULL; 1091} 1092 1093static void 1094scanvars(void) 1095{ 1096 struct varent *vent; 1097 VAR *v; 1098 1099 STAILQ_FOREACH(vent, &varlist, next_ve) { 1100 v = vent->var; 1101 if (v->flag & DSIZ) { 1102 v->dwidth = v->width; 1103 v->width = 0; 1104 } 1105 if (v->flag & USER) 1106 needuser = 1; 1107 if (v->flag & COMM) 1108 needcomm = 1; 1109 } 1110} 1111 1112static void 1113dynsizevars(KINFO *ki) 1114{ 1115 struct varent *vent; 1116 VAR *v; 1117 int i; 1118 1119 STAILQ_FOREACH(vent, &varlist, next_ve) { 1120 v = vent->var; 1121 if (!(v->flag & DSIZ)) 1122 continue; 1123 i = (v->sproc)( ki); 1124 if (v->width < i) 1125 v->width = i; 1126 if (v->width > v->dwidth) 1127 v->width = v->dwidth; 1128 } 1129} 1130 1131static void 1132sizevars(void) 1133{ 1134 struct varent *vent; 1135 VAR *v; 1136 int i; 1137 1138 STAILQ_FOREACH(vent, &varlist, next_ve) { 1139 v = vent->var; 1140 i = strlen(vent->header); 1141 if (v->width < i) 1142 v->width = i; 1143 totwidth += v->width + 1; /* +1 for space */ 1144 } 1145 totwidth--; 1146} 1147 1148#ifndef __APPLE__ 1149static const char * 1150fmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki, 1151 char *comm, int maxlen) 1152{ 1153 const char *s; 1154 1155 s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen); 1156 return (s); 1157} 1158#endif /* !__APPLE__ */ 1159 1160#define UREADOK(ki) (forceuread || (KI_PROC(ki)->p_flag & P_INMEM)) 1161 1162static void 1163saveuser(KINFO *ki) 1164{ 1165 struct usave *usp; 1166#if FIXME 1167 struct user *u_addr = (struct user *)USRSTACK; 1168#endif /* FIXME */ 1169 1170 usp = &ki->ki_u; 1171#if FIXME 1172 if (UREADOK(ki) && kvm_uread(kd, KI_PROC(ki), (unsigned long)&u_addr->u_stats, 1173 (char *)&pstats, sizeof(pstats)) == sizeof(pstats)) 1174 { 1175 /* 1176 * The u-area might be swapped out, and we can't get 1177 * at it because we have a crashdump and no swap. 1178 * If it's here fill in these fields, otherwise, just 1179 * leave them 0. 1180 */ 1181 usp->u_start = pstats.p_start; 1182 usp->u_ru = pstats.p_ru; 1183 usp->u_cru = pstats.p_cru; 1184 usp->u_valid = 1; 1185 } else 1186 usp->u_valid = 0; 1187#else /* FIXME */ 1188 usp->u_valid = 0; 1189#endif /* FIXME */ 1190 /* 1191 * save arguments if needed 1192 */ 1193#if FIXME 1194 if (needcomm && UREADOK(ki)) { 1195 ki->ki_args = fmt(kvm_getargv, ki, KI_PROC(ki)->p_comm, 1196 MAXCOMLEN); 1197 } else if (needcomm) { 1198 ki->ki_args = malloc(strlen(KI_PROC(ki)->p_comm) + 3); 1199 sprintf(ki->ki_args, "(%s)", KI_PROC(ki)->p_comm); 1200 } else { 1201 ki->ki_args = NULL; 1202 } 1203#else /* FIXME */ 1204 ki->ki_args = malloc(strlen(KI_PROC(ki)->p_comm) + 3); 1205 sprintf(ki->ki_args, "%s", KI_PROC(ki)->p_comm); 1206 //ki->ki_args = malloc(10); 1207 //strcpy(ki->ki_args, "()"); 1208#endif /* FIXME */ 1209#if FIXME 1210 if (needenv && UREADOK(ki)) { 1211 ki->ki_env = fmt(kvm_getenvv, ki, (char *)NULL, 0); 1212 } else if (needenv) { 1213 ki->ki_env = malloc(3); 1214 strcpy(ki->ki_env, "()"); 1215 } else { 1216 ki->ki_env = NULL; 1217 } 1218#else /* FIXME */ 1219 ki->ki_env = malloc(10); 1220 strcpy(ki->ki_env, ""); 1221#endif /* FIXME */ 1222} 1223 1224static int 1225pscomp(const void *a, const void *b) 1226{ 1227 int i; 1228#if FIXME 1229#define VSIZE(k) (KI_EPROC(k)->e_vm.vm_dsize + KI_EPROC(k)->e_vm.vm_ssize + \ 1230 KI_EPROC(k)->e_vm.vm_tsize) 1231#else /* FIXME */ 1232#define VSIZE(k) ((k)->tasks_info.resident_size) 1233 1234#endif /* FIXME */ 1235 1236 if (sortby == SORTCPU) 1237 return (getpcpu((KINFO *)b) - getpcpu((KINFO *)a)); 1238 if (sortby == SORTMEM) 1239 return (VSIZE((KINFO *)b) - VSIZE((KINFO *)a)); 1240 i = KI_EPROC((KINFO *)a)->e_tdev - KI_EPROC((KINFO *)b)->e_tdev; 1241 if (i == 0) 1242 i = KI_PROC((KINFO *)a)->p_pid - KI_PROC((KINFO *)b)->p_pid; 1243 return (i); 1244} 1245 1246/* 1247 * ICK (all for getopt), would rather hide the ugliness 1248 * here than taint the main code. 1249 * 1250 * ps foo -> ps -foo 1251 * ps 34 -> ps -p34 1252 * 1253 * The old convention that 't' with no trailing tty arg means the users 1254 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 1255 * feature is available with the option 'T', which takes no argument. 1256 */ 1257static char * 1258kludge_oldps_options(const char *optlist, char *origval, const char *nextarg, int *u03) 1259{ 1260 size_t len; 1261 char *argp, *cp, *newopts, *ns, *optp, *pidp; 1262 1263 /* 1264 * See if the original value includes any option which takes an 1265 * argument (and will thus use up the remainder of the string). 1266 */ 1267 argp = NULL; 1268 if (optlist != NULL) { 1269 for (cp = origval; *cp != '\0'; cp++) { 1270 optp = strchr(optlist, *cp); 1271 if ((optp != NULL) && *(optp + 1) == ':') { 1272 argp = cp; 1273 break; 1274 } 1275 } 1276 } 1277 if (argp != NULL && *origval == '-') 1278 return (origval); 1279 1280 /* 1281 * if last letter is a 't' flag with no argument (in the context 1282 * of the oldps options -- option string NOT starting with a '-' -- 1283 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 1284 * 1285 * However, if a flag accepting a string argument is found earlier 1286 * in the option string (including a possible `t' flag), then the 1287 * remainder of the string must be the argument to that flag; so 1288 * do not modify that argument. Note that a trailing `t' would 1289 * cause argp to be set, if argp was not already set by some 1290 * earlier option. 1291 */ 1292 len = strlen(origval); 1293 cp = origval + len - 1; 1294 pidp = NULL; 1295 if (*cp == 't' && *origval != '-' && cp == argp) { 1296 if (nextarg == NULL || *nextarg == '-' || isdigitch(*nextarg)) 1297 *cp = 'T'; 1298 } else if (argp == NULL) { 1299 /* 1300 * The original value did not include any option which takes 1301 * an argument (and that would include `p' and `t'), so check 1302 * the value for trailing number, or comma-separated list of 1303 * numbers, which we will treat as a pid request. 1304 */ 1305 if (isdigitch(*cp)) { 1306 while (cp >= origval && (*cp == ',' || isdigitch(*cp))) 1307 --cp; 1308 pidp = cp + 1; 1309 } 1310 } 1311 1312 /* 1313 * If nothing needs to be added to the string, then return 1314 * the "original" (although possibly modified) value. 1315 */ 1316 if (*origval == '-' && pidp == NULL) 1317 return (origval); 1318 1319 /* 1320 * Create a copy of the string to add '-' and/or 'p' to the 1321 * original value. 1322 */ 1323 if ((newopts = ns = malloc(len + 3)) == NULL) 1324 errx(1, "malloc failed"); 1325 1326 if (*origval != '-') { 1327 *ns++ = '-'; /* add option flag */ 1328 *u03 = 0; 1329 } 1330 1331 if (pidp == NULL) 1332 strcpy(ns, origval); 1333 else { 1334 /* 1335 * Copy everything before the pid string, add the `p', 1336 * and then copy the pid string. 1337 */ 1338 len = pidp - origval; 1339 memcpy(ns, origval, len); 1340 ns += len; 1341 *ns++ = 'p'; 1342 strcpy(ns, pidp); 1343 } 1344 1345 return (newopts); 1346} 1347 1348#ifndef __APPLE__ 1349static int 1350check_procfs(void) 1351{ 1352 struct statfs mnt; 1353 1354 if (statfs("/proc", &mnt) < 0) 1355 return (0); 1356 if (strcmp(mnt.f_fstypename, "procfs") != 0) 1357 return (0); 1358 return (1); 1359} 1360#endif /* !__APPLE__ */ 1361 1362static void 1363usage(int u03) 1364{ 1365#define SINGLE_OPTS "[-AaCcEefhjlMmrSTvwXx]" 1366 1367 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1368 "usage: ps " SINGLE_OPTS " [-O fmt | -o fmt] [-G gid[,gid...]]", 1369 (u03 ? " [-g grp[,grp...]] [-u [uid,uid...]]" : " [-u]"), 1370 " [-p pid[,pid...]] [-t tty[,tty...]] [-U user[,user...]]", 1371 " ps [-L]"); 1372 exit(1); 1373} 1374