1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1990, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * Copyright (c) 2025 The FreeBSD Foundation 7 * 8 * Portions of this software were developed by Olivier Certner 9 * <olce@FreeBSD.org> at Kumacom SARL under sponsorship from the FreeBSD 10 * Foundation. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * ------+---------+---------+-------- + --------+---------+---------+---------* 36 * Copyright (c) 2004 - Garance Alistair Drosehn <gad@FreeBSD.org>. 37 * All rights reserved. 38 * 39 * Significant modifications made to bring `ps' options somewhat closer 40 * to the standard for `ps' as described in SingleUnixSpec-v3. 41 * ------+---------+---------+-------- + --------+---------+---------+---------* 42 */ 43 44 #include <sys/param.h> 45 #include <sys/jail.h> 46 #include <sys/proc.h> 47 #include <sys/user.h> 48 #include <sys/stat.h> 49 #include <sys/ioctl.h> 50 #include <sys/sysctl.h> 51 #include <sys/mount.h> 52 53 #include <ctype.h> 54 #include <errno.h> 55 #include <fcntl.h> 56 #include <grp.h> 57 #include <jail.h> 58 #include <kvm.h> 59 #include <limits.h> 60 #include <locale.h> 61 #include <paths.h> 62 #include <pwd.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <unistd.h> 67 #include <libxo/xo.h> 68 69 #include "ps.h" 70 71 #define _PATH_PTS "/dev/pts/" 72 73 #define W_SEP " \t" /* "Whitespace" list separators */ 74 #define T_SEP "," /* "Terminate-element" list separators */ 75 76 /* 77 * isdigit takes an `int', but expects values in the range of unsigned char. 78 * This wrapper ensures that values from a 'char' end up in the correct range. 79 */ 80 #define isdigitch(Anychar) isdigit((u_char)(Anychar)) 81 82 int cflag; /* -c */ 83 int eval; /* Exit value */ 84 time_t now; /* Current time(3) value */ 85 int rawcpu; /* -C */ 86 int sumrusage; /* -S */ 87 int termwidth; /* Width of the screen (0 == infinity). */ 88 int showthreads; /* will threads be shown? */ 89 90 struct keyword_info { 91 /* 92 * Whether there is (at least) one column referencing this keyword that 93 * must be kept. 94 */ 95 #define KWI_HAS_MUST_KEEP_COLUMN (1 << 0) 96 /* 97 * Whether a column with such a keyword has been seen. 98 */ 99 #define KWI_SEEN (1 << 1) 100 u_int flags; 101 }; 102 103 struct velisthead varlist = STAILQ_HEAD_INITIALIZER(varlist); 104 static struct velisthead Ovarlist = STAILQ_HEAD_INITIALIZER(Ovarlist); 105 106 static kvm_t *kd; 107 static int needcomm; /* -o "command" */ 108 static int needenv; /* -e */ 109 static int needuser; /* -o "user" */ 110 static int optfatal; /* Fatal error parsing some list-option. */ 111 static int pid_max; /* kern.pid_max */ 112 113 static enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 114 115 struct listinfo; 116 typedef int addelem_rtn(struct listinfo *_inf, const char *_elem); 117 118 struct listinfo { 119 int count; 120 int maxcount; 121 int elemsize; 122 addelem_rtn *addelem; 123 const char *lname; 124 union { 125 gid_t *gids; 126 int *jids; 127 pid_t *pids; 128 dev_t *ttys; 129 uid_t *uids; 130 void *ptr; 131 } l; 132 }; 133 134 static int addelem_gid(struct listinfo *, const char *); 135 static int addelem_jid(struct listinfo *, const char *); 136 static int addelem_pid(struct listinfo *, const char *); 137 static int addelem_tty(struct listinfo *, const char *); 138 static int addelem_uid(struct listinfo *, const char *); 139 static void add_list(struct listinfo *, const char *); 140 static void descendant_sort(KINFO *, int); 141 static void format_output(KINFO *); 142 static void *expand_list(struct listinfo *); 143 static const char * 144 fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int), 145 KINFO *, char *, char *, int); 146 static void free_list(struct listinfo *); 147 static void init_list(struct listinfo *, addelem_rtn, int, const char *); 148 static char *kludge_oldps_options(const char *, char *, const char *); 149 static int pscomp(const void *, const void *); 150 static void saveuser(KINFO *); 151 static void scan_vars(struct keyword_info *); 152 static void remove_redundant_columns(struct keyword_info *); 153 static void pidmax_init(void); 154 static void usage(void); 155 156 static const char dfmt[] = "pid,tt,state,time,command"; 157 static const char jfmt[] = "user,pid,ppid,pgid,sid,jobc,state,tt,time,command"; 158 static const char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state," 159 "tt,time,command"; 160 static const char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command"; 161 static const char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz," 162 "%cpu,%mem,command"; 163 static const char Zfmt[] = "label"; 164 165 #define PS_ARGS "AaCcD:defG:gHhjJ:LlM:mN:O:o:p:rSTt:U:uvwXxZ" 166 167 int 168 main(int argc, char *argv[]) 169 { 170 struct listinfo gidlist, jidlist, pgrplist, pidlist; 171 struct listinfo ruidlist, sesslist, ttylist, uidlist; 172 struct kinfo_proc *kp; 173 KINFO *kinfo = NULL, *next_KINFO; 174 KINFO_STR *ks; 175 struct varent *vent; 176 struct winsize ws = { .ws_row = 0 }; 177 const char *nlistf, *memf, *str; 178 char *cols; 179 int all, ch, elem, flag, _fmt, i, lineno, linelen, left; 180 int descendancy, nentries, nkept, nselectors; 181 int prtheader, wflag, what, xkeep, xkeep_implied; 182 int fwidthmin, fwidthmax; 183 char errbuf[_POSIX2_LINE_MAX]; 184 char fmtbuf[_POSIX2_LINE_MAX]; 185 enum { NONE = 0, UP = 1, DOWN = 2, BOTH = 1 | 2 } directions = NONE; 186 struct { int traversed; int initial; } pid_count; 187 struct keyword_info *keywords_info; 188 189 (void) setlocale(LC_ALL, ""); 190 time(&now); /* Used by routines in print.c. */ 191 192 /* 193 * Compute default output line length before processing options. 194 * If COLUMNS is set, use it. Otherwise, if this is part of an 195 * interactive job (i.e. one associated with a terminal), use 196 * the terminal width. "Interactive" is determined by whether 197 * any of stdout, stderr, or stdin is a terminal. The intent 198 * is that "ps", "ps | more", and "ps | grep" all use the same 199 * default line length unless -w is specified. 200 * 201 * If not interactive, the default length was traditionally 79. 202 * It has been changed to unlimited. This is mostly for the 203 * benefit of non-interactive scripts, which arguably should 204 * use -ww, but is compatible with Linux. 205 */ 206 if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') 207 termwidth = atoi(cols); 208 else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 209 ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 210 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 211 ws.ws_col == 0) 212 termwidth = UNLIMITED; 213 else 214 termwidth = ws.ws_col - 1; 215 216 /* 217 * Hide a number of option-processing kludges in a separate routine, 218 * to support some historical BSD behaviors, such as `ps axu'. 219 */ 220 if (argc > 1) 221 argv[1] = kludge_oldps_options(PS_ARGS, argv[1], argv[2]); 222 223 pidmax_init(); 224 225 #ifdef PS_CHECK_KEYWORDS 226 /* Check for obvious problems in the keywords array. */ 227 check_keywords(); 228 /* Resolve all aliases at start to spot errors. */ 229 resolve_aliases(); 230 #endif 231 232 all = descendancy = _fmt = nselectors = optfatal = 0; 233 prtheader = showthreads = wflag = xkeep_implied = 0; 234 xkeep = -1; /* Neither -x nor -X. */ 235 init_list(&gidlist, addelem_gid, sizeof(gid_t), "group"); 236 init_list(&jidlist, addelem_jid, sizeof(int), "jail id"); 237 init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group"); 238 init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id"); 239 init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser"); 240 init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id"); 241 init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty"); 242 init_list(&uidlist, addelem_uid, sizeof(uid_t), "user"); 243 memf = _PATH_DEVNULL; 244 nlistf = NULL; 245 246 argc = xo_parse_args(argc, argv); 247 if (argc < 0) 248 exit(1); 249 250 while ((ch = getopt(argc, argv, PS_ARGS)) != -1) 251 switch (ch) { 252 case 'A': 253 all = xkeep = 1; 254 break; 255 case 'a': 256 all = 1; 257 break; 258 case 'C': 259 rawcpu = 1; 260 break; 261 case 'c': 262 cflag = 1; 263 break; 264 case 'D': { 265 size_t len = strlen(optarg); 266 267 if (len <= 2 && 268 strncasecmp(optarg, "up", len) == 0) 269 directions |= UP; 270 else if (len <= 4 && 271 strncasecmp(optarg, "down", len) == 0) 272 directions |= DOWN; 273 else if (len <= 4 && 274 strncasecmp(optarg, "both", len) == 0) 275 directions |= BOTH; 276 else 277 usage(); 278 break; 279 } 280 case 'd': 281 descendancy = 1; 282 break; 283 case 'e': /* XXX set ufmt */ 284 needenv = 1; 285 break; 286 case 'f': 287 /* compat */ 288 break; 289 case 'G': 290 add_list(&gidlist, optarg); 291 xkeep_implied = 1; 292 nselectors++; 293 break; 294 case 'g': 295 #if 0 296 /* 297 * XXX - This behavior is still under debate since it 298 * conflicts with the (undocumented) `-g' option 299 * and is non-standard. However, it is the 300 * behavior of most UNIX systems except 301 * SunOS/Solaris/illumos (see next comment; see 302 * also comment for '-s' below). 303 */ 304 add_list(&pgrplist, optarg); 305 xkeep_implied = 1; 306 nselectors++; 307 break; 308 #else 309 /* 310 * The historical BSD-ish (from SunOS) behavior: Also 311 * display process group leaders (but we do not filter 312 * them out). 313 */ 314 break; /* no-op */ 315 #endif 316 case 'H': 317 showthreads = KERN_PROC_INC_THREAD; 318 break; 319 case 'h': 320 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 321 break; 322 case 'J': 323 add_list(&jidlist, optarg); 324 xkeep_implied = 1; 325 nselectors++; 326 break; 327 case 'j': 328 parsefmt(jfmt, &varlist, 0); 329 _fmt = 1; 330 break; 331 case 'L': 332 showkey(); 333 exit(0); 334 case 'l': 335 parsefmt(lfmt, &varlist, 0); 336 _fmt = 1; 337 break; 338 case 'M': 339 memf = optarg; 340 break; 341 case 'm': 342 sortby = SORTMEM; 343 break; 344 case 'N': 345 nlistf = optarg; 346 break; 347 case 'O': 348 parsefmt(optarg, &Ovarlist, 1); 349 break; 350 case 'o': 351 parsefmt(optarg, &varlist, 1); 352 _fmt = 1; 353 break; 354 case 'p': 355 add_list(&pidlist, optarg); 356 /* 357 * Note: `-p' does not *set* xkeep, but any values 358 * from pidlist are checked before xkeep is. That 359 * way they are always matched, even if the user 360 * specifies `-X'. 361 */ 362 nselectors++; 363 break; 364 case 'r': 365 sortby = SORTCPU; 366 break; 367 case 'S': 368 sumrusage = 1; 369 break; 370 #if 0 371 case 's': 372 /* 373 * XXX - This non-standard option is still under debate. 374 * It is supported on Solaris, Linux, IRIX, and 375 * OpenBSD but conflicts with '-s' on NetBSD. This 376 * is the same functionality as POSIX option '-g', 377 * but the cited systems do not provide it under 378 * '-g', only under '-s'. 379 */ 380 add_list(&sesslist, optarg); 381 xkeep_implied = 1; 382 nselectors++; 383 break; 384 #endif 385 case 'T': 386 if ((optarg = ttyname(STDIN_FILENO)) == NULL) 387 xo_errx(1, "stdin: not a terminal"); 388 /* FALLTHROUGH */ 389 case 't': 390 add_list(&ttylist, optarg); 391 xkeep_implied = 1; 392 nselectors++; 393 break; 394 case 'U': 395 add_list(&ruidlist, optarg); 396 xkeep_implied = 1; 397 nselectors++; 398 break; 399 case 'u': 400 #if 0 401 /* 402 * POSIX's '-u' behavior. 403 * 404 * This has not been activated because: 405 * 1. Option '-U' is a substitute for most users, and 406 * those that care seem more likely to want to match 407 * on the real user ID to display all processes 408 * launched by some users. 409 * 2. '-u' has been a canned display on the BSDs for 410 * a very long time (POLA). 411 */ 412 add_list(&uidlist, optarg); 413 xkeep_implied = 1; 414 nselectors++; 415 break; 416 #else 417 /* Historical BSD's '-u'. */ 418 parsefmt(ufmt, &varlist, 0); 419 sortby = SORTCPU; 420 _fmt = 1; 421 break; 422 #endif 423 case 'v': 424 parsefmt(vfmt, &varlist, 0); 425 sortby = SORTMEM; 426 _fmt = 1; 427 break; 428 case 'w': 429 if (wflag) 430 termwidth = UNLIMITED; 431 else if (termwidth < 131 && termwidth != UNLIMITED) 432 termwidth = 131; 433 wflag++; 434 break; 435 case 'X': 436 /* 437 * Note that `-X' and `-x' are not standard "selector" 438 * options. For most selector-options, we check *all* 439 * processes to see if any are matched by the given 440 * value(s). After we have a set of all the matched 441 * processes, then `-X' and `-x' govern whether we 442 * modify that *matched* set for processes which do 443 * not have a controlling terminal. `-X' causes 444 * those processes to be deleted from the matched 445 * set, while `-x' causes them to be kept. 446 */ 447 xkeep = 0; 448 break; 449 case 'x': 450 xkeep = 1; 451 break; 452 case 'Z': 453 parsefmt(Zfmt, &varlist, 0); 454 break; 455 case '?': 456 default: 457 usage(); 458 } 459 argc -= optind; 460 argv += optind; 461 462 /* 463 * If there arguments after processing all the options, attempt 464 * to treat them as a list of process ids. 465 */ 466 while (*argv) { 467 if (!isdigitch(**argv)) 468 break; 469 add_list(&pidlist, *argv); 470 argv++; 471 } 472 if (*argv) { 473 xo_warnx("illegal argument: %s\n", *argv); 474 usage(); 475 } 476 if (optfatal) 477 exit(1); /* Error messages already printed. */ 478 if (xkeep < 0) /* Neither -X nor -x was specified. */ 479 xkeep = xkeep_implied; 480 481 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 482 if (kd == NULL) 483 xo_errx(1, "%s", errbuf); 484 485 if (!_fmt) 486 parsefmt(dfmt, &varlist, 0); 487 488 if (!STAILQ_EMPTY(&Ovarlist)) { 489 VARENT *const pid_entry = find_varentry("pid"); 490 491 /* 492 * We insert the keywords passed by '-O' after the process ID if 493 * specified, else at start. 494 */ 495 if (pid_entry != NULL) { 496 struct velisthead rest; 497 498 STAILQ_SPLIT_AFTER(&varlist, pid_entry, &rest, next_ve); 499 STAILQ_CONCAT(&varlist, &Ovarlist); 500 STAILQ_CONCAT(&varlist, &rest); 501 } 502 else { 503 STAILQ_SWAP(&varlist, &Ovarlist, varent); 504 STAILQ_CONCAT(&varlist, &Ovarlist); 505 } 506 } 507 508 keywords_info = calloc(known_keywords_nb, sizeof(struct keyword_info)); 509 if (keywords_info == NULL) 510 xo_errx(1, "malloc failed"); 511 /* 512 * Scan requested variables, noting which structures are needed and 513 * which keywords are specified. 514 */ 515 scan_vars(keywords_info); 516 /* 517 * Remove redundant columns from "canned" displays (see the callee's 518 * herald comment for more details). 519 */ 520 remove_redundant_columns(keywords_info); 521 free(keywords_info); 522 keywords_info = NULL; 523 524 if (all) 525 /* 526 * We have to display all processes, regardless of other 527 * options. 528 */ 529 nselectors = 0; 530 else if (nselectors == 0) { 531 /* 532 * Default is to request our processes only. As per POSIX, we 533 * match processes by their effective user IDs and we use our 534 * effective user ID as our own identity. 535 */ 536 expand_list(&uidlist); 537 uidlist.l.uids[uidlist.count++] = geteuid(); 538 nselectors = 1; 539 } 540 541 /* 542 * Get process list. If the user requested just one selector- 543 * option, then kvm_getprocs can be asked to return just those 544 * processes. Otherwise, have it return all processes, and 545 * then this routine will search that full list and select the 546 * processes which match any of the user's selector-options. 547 */ 548 what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC; 549 flag = 0; 550 if (nselectors == 1) { 551 if (gidlist.count == 1) { 552 what = KERN_PROC_RGID | showthreads; 553 flag = *gidlist.l.gids; 554 nselectors = 0; 555 } else if (pgrplist.count == 1) { 556 what = KERN_PROC_PGRP | showthreads; 557 flag = *pgrplist.l.pids; 558 nselectors = 0; 559 } else if (pidlist.count == 1 && directions == NONE) { 560 what = KERN_PROC_PID | showthreads; 561 flag = *pidlist.l.pids; 562 nselectors = 0; 563 } else if (ruidlist.count == 1) { 564 what = KERN_PROC_RUID | showthreads; 565 flag = *ruidlist.l.uids; 566 nselectors = 0; 567 } else if (sesslist.count == 1) { 568 what = KERN_PROC_SESSION | showthreads; 569 flag = *sesslist.l.pids; 570 nselectors = 0; 571 } else if (ttylist.count == 1) { 572 what = KERN_PROC_TTY | showthreads; 573 flag = *ttylist.l.ttys; 574 nselectors = 0; 575 } else if (uidlist.count == 1) { 576 what = KERN_PROC_UID | showthreads; 577 flag = *uidlist.l.uids; 578 nselectors = 0; 579 } 580 } 581 582 /* 583 * select procs 584 */ 585 nentries = -1; 586 kp = kvm_getprocs(kd, what, flag, &nentries); 587 /* 588 * Ignore ESRCH to preserve behaviour of "ps -p nonexistent-pid" 589 * not reporting an error. 590 */ 591 if ((kp == NULL && errno != ESRCH) || (kp != NULL && nentries < 0)) 592 xo_errx(1, "%s", kvm_geterr(kd)); 593 nkept = 0; 594 pid_count.initial = pidlist.count; 595 if (directions & DOWN) 596 for (elem = 0; elem < pidlist.count; elem++) 597 for (i = 0; i < nentries; i++) { 598 if (kp[i].ki_ppid == kp[i].ki_pid) 599 continue; 600 if (kp[i].ki_ppid == pidlist.l.pids[elem]) { 601 if (pidlist.count >= pidlist.maxcount) 602 expand_list(&pidlist); 603 pidlist.l.pids[pidlist.count++] = kp[i].ki_pid; 604 } 605 } 606 pid_count.traversed = pidlist.count; 607 if (directions & UP) 608 for (elem = 0; elem < pidlist.count; elem++) { 609 if (elem >= pid_count.initial && elem < pid_count.traversed) 610 continue; 611 for (i = 0; i < nentries; i++) { 612 if (kp[i].ki_ppid == kp[i].ki_pid) 613 continue; 614 if (kp[i].ki_pid == pidlist.l.pids[elem]) { 615 if (pidlist.count >= pidlist.maxcount) 616 expand_list(&pidlist); 617 pidlist.l.pids[pidlist.count++] = kp[i].ki_ppid; 618 } 619 } 620 } 621 if (nentries > 0) { 622 if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL) 623 xo_errx(1, "malloc failed"); 624 for (i = nentries; --i >= 0; ++kp) { 625 /* 626 * If the user specified multiple selection-criteria, 627 * then keep any process matched by the inclusive OR 628 * of all the selection-criteria given. 629 */ 630 if (pidlist.count > 0) { 631 for (elem = 0; elem < pidlist.count; elem++) 632 if (kp->ki_pid == pidlist.l.pids[elem]) 633 goto keepit; 634 } 635 /* 636 * Note that we had to process pidlist before 637 * filtering out processes which do not have 638 * a controlling terminal. 639 */ 640 if (xkeep == 0) { 641 if ((kp->ki_tdev == NODEV || 642 (kp->ki_flag & P_CONTROLT) == 0)) 643 continue; 644 } 645 if (nselectors == 0) 646 goto keepit; 647 if (gidlist.count > 0) { 648 for (elem = 0; elem < gidlist.count; elem++) 649 if (kp->ki_rgid == gidlist.l.gids[elem]) 650 goto keepit; 651 } 652 if (jidlist.count > 0) { 653 for (elem = 0; elem < jidlist.count; elem++) 654 if (kp->ki_jid == jidlist.l.jids[elem]) 655 goto keepit; 656 } 657 if (pgrplist.count > 0) { 658 for (elem = 0; elem < pgrplist.count; elem++) 659 if (kp->ki_pgid == 660 pgrplist.l.pids[elem]) 661 goto keepit; 662 } 663 if (ruidlist.count > 0) { 664 for (elem = 0; elem < ruidlist.count; elem++) 665 if (kp->ki_ruid == 666 ruidlist.l.uids[elem]) 667 goto keepit; 668 } 669 if (sesslist.count > 0) { 670 for (elem = 0; elem < sesslist.count; elem++) 671 if (kp->ki_sid == sesslist.l.pids[elem]) 672 goto keepit; 673 } 674 if (ttylist.count > 0) { 675 for (elem = 0; elem < ttylist.count; elem++) 676 if (kp->ki_tdev == ttylist.l.ttys[elem]) 677 goto keepit; 678 } 679 if (uidlist.count > 0) { 680 for (elem = 0; elem < uidlist.count; elem++) 681 if (kp->ki_uid == uidlist.l.uids[elem]) 682 goto keepit; 683 } 684 /* 685 * This process did not match any of the user's 686 * selector-options, so skip the process. 687 */ 688 continue; 689 690 keepit: 691 next_KINFO = &kinfo[nkept]; 692 next_KINFO->ki_p = kp; 693 next_KINFO->ki_d.level = 0; 694 next_KINFO->ki_d.prefix = NULL; 695 next_KINFO->ki_pcpu = getpcpu(next_KINFO); 696 if (sortby == SORTMEM) 697 next_KINFO->ki_memsize = kp->ki_tsize + 698 kp->ki_dsize + kp->ki_ssize; 699 if (needuser) 700 saveuser(next_KINFO); 701 nkept++; 702 } 703 } 704 705 if (nkept == 0) { 706 printheader(); 707 if (xo_finish() < 0) 708 xo_err(1, "stdout"); 709 exit(1); 710 } 711 712 /* 713 * sort proc list 714 */ 715 qsort(kinfo, nkept, sizeof(KINFO), pscomp); 716 717 /* 718 * We want things in descendant order 719 */ 720 if (descendancy) 721 descendant_sort(kinfo, nkept); 722 723 724 /* 725 * Prepare formatted output. 726 */ 727 for (i = 0; i < nkept; i++) 728 format_output(&kinfo[i]); 729 730 /* 731 * Print header. 732 */ 733 xo_open_container("process-information"); 734 printheader(); 735 if (xo_get_style(NULL) != XO_STYLE_TEXT) 736 termwidth = UNLIMITED; 737 738 /* 739 * Output formatted lines. 740 */ 741 xo_open_list("process"); 742 for (i = lineno = 0; i < nkept; i++) { 743 linelen = 0; 744 xo_open_instance("process"); 745 STAILQ_FOREACH(vent, &varlist, next_ve) { 746 ks = STAILQ_FIRST(&kinfo[i].ki_ks); 747 STAILQ_REMOVE_HEAD(&kinfo[i].ki_ks, ks_next); 748 /* Truncate rightmost column if necessary. */ 749 fwidthmax = _POSIX2_LINE_MAX; 750 if (STAILQ_NEXT(vent, next_ve) == NULL && 751 termwidth != UNLIMITED && ks->ks_str != NULL) { 752 left = termwidth - linelen; 753 if (left > 0 && left < (int)strlen(ks->ks_str)) 754 fwidthmax = left; 755 } 756 757 str = ks->ks_str; 758 if (str == NULL) 759 str = "-"; 760 /* No padding for the last column, if it's LJUST. */ 761 fwidthmin = (xo_get_style(NULL) != XO_STYLE_TEXT || 762 (STAILQ_NEXT(vent, next_ve) == NULL && 763 (vent->var->flag & LJUST))) ? 0 : vent->width; 764 snprintf(fmtbuf, sizeof(fmtbuf), "{:%s/%%%s%d..%dhs}", 765 vent->var->field ? vent->var->field : vent->var->name, 766 (vent->var->flag & LJUST) ? "-" : "", 767 fwidthmin, fwidthmax); 768 xo_emit(fmtbuf, str); 769 linelen += fwidthmin; 770 771 if (ks->ks_str != NULL) { 772 free(ks->ks_str); 773 ks->ks_str = NULL; 774 } 775 free(ks); 776 ks = NULL; 777 778 if (STAILQ_NEXT(vent, next_ve) != NULL) { 779 xo_emit("{P: }"); 780 linelen++; 781 } 782 } 783 xo_emit("\n"); 784 xo_close_instance("process"); 785 if (prtheader && lineno++ == prtheader - 4) { 786 xo_emit("\n"); 787 printheader(); 788 lineno = 0; 789 } 790 } 791 xo_close_list("process"); 792 xo_close_container("process-information"); 793 if (xo_finish() < 0) 794 xo_err(1, "stdout"); 795 796 free_list(&gidlist); 797 free_list(&jidlist); 798 free_list(&pidlist); 799 free_list(&pgrplist); 800 free_list(&ruidlist); 801 free_list(&sesslist); 802 free_list(&ttylist); 803 free_list(&uidlist); 804 for (i = 0; i < nkept; i++) 805 free(kinfo[i].ki_d.prefix); 806 free(kinfo); 807 808 exit(eval); 809 } 810 811 static int 812 addelem_gid(struct listinfo *inf, const char *elem) 813 { 814 struct group *grp; 815 const char *nameorID; 816 char *endp; 817 u_long bigtemp; 818 819 if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) { 820 if (*elem == '\0') 821 xo_warnx("Invalid (zero-length) %s name", inf->lname); 822 else 823 xo_warnx("%s name too long: %s", inf->lname, elem); 824 optfatal = 1; 825 return (0); /* Do not add this value. */ 826 } 827 828 /* 829 * SUSv3 states that `ps -G grouplist' should match "real-group 830 * ID numbers", and does not mention group-names. I do want to 831 * also support group-names, so this tries for a group-id first, 832 * and only tries for a name if that doesn't work. This is the 833 * opposite order of what is done in addelem_uid(), but in 834 * practice the order would only matter for group-names which 835 * are all-numeric. 836 */ 837 grp = NULL; 838 nameorID = "named"; 839 errno = 0; 840 bigtemp = strtoul(elem, &endp, 10); 841 if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) { 842 nameorID = "name or ID matches"; 843 grp = getgrgid((gid_t)bigtemp); 844 } 845 if (grp == NULL) 846 grp = getgrnam(elem); 847 if (grp == NULL) { 848 xo_warnx("No %s %s '%s'", inf->lname, nameorID, elem); 849 optfatal = 1; 850 return (0); 851 } 852 if (inf->count >= inf->maxcount) 853 expand_list(inf); 854 inf->l.gids[(inf->count)++] = grp->gr_gid; 855 return (1); 856 } 857 858 static int 859 addelem_jid(struct listinfo *inf, const char *elem) 860 { 861 int tempid; 862 863 if (*elem == '\0') { 864 xo_warnx("Invalid (zero-length) jail id"); 865 optfatal = 1; 866 return (0); /* Do not add this value. */ 867 } 868 869 tempid = jail_getid(elem); 870 if (tempid < 0) { 871 xo_warnx("Invalid %s: %s", inf->lname, elem); 872 optfatal = 1; 873 return (0); 874 } 875 876 if (inf->count >= inf->maxcount) 877 expand_list(inf); 878 inf->l.jids[(inf->count)++] = tempid; 879 return (1); 880 } 881 882 static int 883 addelem_pid(struct listinfo *inf, const char *elem) 884 { 885 char *endp; 886 long tempid; 887 888 if (*elem == '\0') { 889 xo_warnx("Invalid (zero-length) process id"); 890 optfatal = 1; 891 return (0); /* Do not add this value. */ 892 } 893 894 errno = 0; 895 tempid = strtol(elem, &endp, 10); 896 if (*endp != '\0' || tempid < 0 || elem == endp) { 897 xo_warnx("Invalid %s: %s", inf->lname, elem); 898 errno = ERANGE; 899 } else if (errno != 0 || tempid > pid_max) { 900 xo_warnx("%s too large: %s", inf->lname, elem); 901 errno = ERANGE; 902 } 903 if (errno == ERANGE) { 904 optfatal = 1; 905 return (0); 906 } 907 if (inf->count >= inf->maxcount) 908 expand_list(inf); 909 inf->l.pids[(inf->count)++] = tempid; 910 return (1); 911 } 912 913 /* 914 * The user can specify a device via one of three formats: 915 * 1) fully qualified, e.g.: /dev/ttyp0 /dev/console /dev/pts/0 916 * 2) missing "/dev", e.g.: ttyp0 console pts/0 917 * 3) two-letters, e.g.: p0 co 0 918 * (matching letters that would be seen in the "TT" column) 919 */ 920 static int 921 addelem_tty(struct listinfo *inf, const char *elem) 922 { 923 const char *ttypath; 924 struct stat sb; 925 char pathbuf[PATH_MAX], pathbuf2[PATH_MAX], pathbuf3[PATH_MAX]; 926 927 ttypath = NULL; 928 pathbuf2[0] = '\0'; 929 pathbuf3[0] = '\0'; 930 switch (*elem) { 931 case '/': 932 ttypath = elem; 933 break; 934 case 'c': 935 if (strcmp(elem, "co") == 0) { 936 ttypath = _PATH_CONSOLE; 937 break; 938 } 939 /* FALLTHROUGH */ 940 default: 941 strlcpy(pathbuf, _PATH_DEV, sizeof(pathbuf)); 942 strlcat(pathbuf, elem, sizeof(pathbuf)); 943 ttypath = pathbuf; 944 if (strncmp(pathbuf, _PATH_TTY, strlen(_PATH_TTY)) == 0) 945 break; 946 if (strncmp(pathbuf, _PATH_PTS, strlen(_PATH_PTS)) == 0) 947 break; 948 if (strcmp(pathbuf, _PATH_CONSOLE) == 0) 949 break; 950 /* Check to see if /dev/tty${elem} exists */ 951 strlcpy(pathbuf2, _PATH_TTY, sizeof(pathbuf2)); 952 strlcat(pathbuf2, elem, sizeof(pathbuf2)); 953 if (stat(pathbuf2, &sb) == 0 && S_ISCHR(sb.st_mode)) { 954 /* No need to repeat stat() && S_ISCHR() checks */ 955 ttypath = NULL; 956 break; 957 } 958 /* Check to see if /dev/pts/${elem} exists */ 959 strlcpy(pathbuf3, _PATH_PTS, sizeof(pathbuf3)); 960 strlcat(pathbuf3, elem, sizeof(pathbuf3)); 961 if (stat(pathbuf3, &sb) == 0 && S_ISCHR(sb.st_mode)) { 962 /* No need to repeat stat() && S_ISCHR() checks */ 963 ttypath = NULL; 964 break; 965 } 966 break; 967 } 968 if (ttypath) { 969 if (stat(ttypath, &sb) == -1) { 970 if (pathbuf3[0] != '\0') 971 xo_warn("%s, %s, and %s", pathbuf3, pathbuf2, 972 ttypath); 973 else 974 xo_warn("%s", ttypath); 975 optfatal = 1; 976 return (0); 977 } 978 if (!S_ISCHR(sb.st_mode)) { 979 if (pathbuf3[0] != '\0') 980 xo_warnx("%s, %s, and %s: Not a terminal", 981 pathbuf3, pathbuf2, ttypath); 982 else 983 xo_warnx("%s: Not a terminal", ttypath); 984 optfatal = 1; 985 return (0); 986 } 987 } 988 if (inf->count >= inf->maxcount) 989 expand_list(inf); 990 inf->l.ttys[(inf->count)++] = sb.st_rdev; 991 return (1); 992 } 993 994 static int 995 addelem_uid(struct listinfo *inf, const char *elem) 996 { 997 struct passwd *pwd; 998 char *endp; 999 u_long bigtemp; 1000 1001 if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) { 1002 if (*elem == '\0') 1003 xo_warnx("Invalid (zero-length) %s name", inf->lname); 1004 else 1005 xo_warnx("%s name too long: %s", inf->lname, elem); 1006 optfatal = 1; 1007 return (0); /* Do not add this value. */ 1008 } 1009 1010 pwd = getpwnam(elem); 1011 if (pwd == NULL) { 1012 errno = 0; 1013 bigtemp = strtoul(elem, &endp, 10); 1014 if (errno != 0 || *endp != '\0' || bigtemp > UID_MAX) 1015 xo_warnx("No %s named '%s'", inf->lname, elem); 1016 else { 1017 /* The string is all digits, so it might be a userID. */ 1018 pwd = getpwuid((uid_t)bigtemp); 1019 if (pwd == NULL) 1020 xo_warnx("No %s name or ID matches '%s'", 1021 inf->lname, elem); 1022 } 1023 } 1024 if (pwd == NULL) { 1025 /* 1026 * These used to be treated as minor warnings (and the 1027 * option was simply ignored), but now they are fatal 1028 * errors (and the command will be aborted). 1029 */ 1030 optfatal = 1; 1031 return (0); 1032 } 1033 if (inf->count >= inf->maxcount) 1034 expand_list(inf); 1035 inf->l.uids[(inf->count)++] = pwd->pw_uid; 1036 return (1); 1037 } 1038 1039 static void 1040 add_list(struct listinfo *inf, const char *argp) 1041 { 1042 const char *savep; 1043 char *cp, *endp; 1044 int toolong; 1045 char elemcopy[PATH_MAX]; 1046 1047 if (*argp == '\0') 1048 inf->addelem(inf, argp); 1049 while (*argp != '\0') { 1050 while (*argp != '\0' && strchr(W_SEP, *argp) != NULL) 1051 argp++; 1052 savep = argp; 1053 toolong = 0; 1054 cp = elemcopy; 1055 if (strchr(T_SEP, *argp) == NULL) { 1056 endp = elemcopy + sizeof(elemcopy) - 1; 1057 while (*argp != '\0' && cp <= endp && 1058 strchr(W_SEP T_SEP, *argp) == NULL) 1059 *cp++ = *argp++; 1060 if (cp > endp) 1061 toolong = 1; 1062 } 1063 if (!toolong) { 1064 *cp = '\0'; 1065 /* 1066 * Add this single element to the given list. 1067 */ 1068 inf->addelem(inf, elemcopy); 1069 } else { 1070 /* 1071 * The string is too long to copy. Find the end 1072 * of the string to print out the warning message. 1073 */ 1074 while (*argp != '\0' && strchr(W_SEP T_SEP, 1075 *argp) == NULL) 1076 argp++; 1077 xo_warnx("Value too long: %.*s", (int)(argp - savep), 1078 savep); 1079 optfatal = 1; 1080 } 1081 /* 1082 * Skip over any number of trailing whitespace characters, 1083 * but only one (at most) trailing element-terminating 1084 * character. 1085 */ 1086 while (*argp != '\0' && strchr(W_SEP, *argp) != NULL) 1087 argp++; 1088 if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) { 1089 argp++; 1090 /* Catch case where string ended with a comma. */ 1091 if (*argp == '\0') 1092 inf->addelem(inf, argp); 1093 } 1094 } 1095 } 1096 1097 static void 1098 descendant_sort(KINFO *ki, int items) 1099 { 1100 int dst, lvl, maxlvl, n, ndst, nsrc, siblings, src; 1101 unsigned char *path; 1102 KINFO kn; 1103 1104 /* 1105 * First, sort the entries by descendancy, tracking the descendancy 1106 * depth in the ki_d.level field. 1107 */ 1108 src = 0; 1109 maxlvl = 0; 1110 while (src < items) { 1111 if (ki[src].ki_d.level) { 1112 src++; 1113 continue; 1114 } 1115 for (nsrc = 1; src + nsrc < items; nsrc++) 1116 if (!ki[src + nsrc].ki_d.level) 1117 break; 1118 1119 for (dst = 0; dst < items; dst++) { 1120 if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_pid) 1121 continue; 1122 if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_ppid) 1123 break; 1124 } 1125 1126 if (dst == items) { 1127 src += nsrc; 1128 continue; 1129 } 1130 1131 for (ndst = 1; dst + ndst < items; ndst++) 1132 if (ki[dst + ndst].ki_d.level <= ki[dst].ki_d.level) 1133 break; 1134 1135 for (n = src; n < src + nsrc; n++) { 1136 ki[n].ki_d.level += ki[dst].ki_d.level + 1; 1137 if (maxlvl < ki[n].ki_d.level) 1138 maxlvl = ki[n].ki_d.level; 1139 } 1140 1141 while (nsrc) { 1142 if (src < dst) { 1143 kn = ki[src]; 1144 memmove(ki + src, ki + src + 1, 1145 (dst - src + ndst - 1) * sizeof *ki); 1146 ki[dst + ndst - 1] = kn; 1147 nsrc--; 1148 dst--; 1149 ndst++; 1150 } else if (src != dst + ndst) { 1151 kn = ki[src]; 1152 memmove(ki + dst + ndst + 1, ki + dst + ndst, 1153 (src - dst - ndst) * sizeof *ki); 1154 ki[dst + ndst] = kn; 1155 ndst++; 1156 nsrc--; 1157 src++; 1158 } else { 1159 ndst += nsrc; 1160 src += nsrc; 1161 nsrc = 0; 1162 } 1163 } 1164 } 1165 1166 /* 1167 * Now populate ki_d.prefix (instead of ki_d.level) with the command 1168 * prefix used to show descendancies. 1169 */ 1170 path = calloc((maxlvl + 7) / 8, sizeof(unsigned char)); 1171 for (src = 0; src < items; src++) { 1172 if ((lvl = ki[src].ki_d.level) == 0) { 1173 ki[src].ki_d.prefix = NULL; 1174 continue; 1175 } 1176 if ((ki[src].ki_d.prefix = malloc(lvl * 2 + 1)) == NULL) 1177 xo_errx(1, "malloc failed"); 1178 for (n = 0; n < lvl - 2; n++) { 1179 ki[src].ki_d.prefix[n * 2] = 1180 path[n / 8] & 1 << (n % 8) ? '|' : ' '; 1181 ki[src].ki_d.prefix[n * 2 + 1] = ' '; 1182 } 1183 if (n == lvl - 2) { 1184 /* Have I any more siblings? */ 1185 for (siblings = 0, dst = src + 1; dst < items; dst++) { 1186 if (ki[dst].ki_d.level > lvl) 1187 continue; 1188 if (ki[dst].ki_d.level == lvl) 1189 siblings = 1; 1190 break; 1191 } 1192 if (siblings) 1193 path[n / 8] |= 1 << (n % 8); 1194 else 1195 path[n / 8] &= ~(1 << (n % 8)); 1196 ki[src].ki_d.prefix[n * 2] = siblings ? '|' : '`'; 1197 ki[src].ki_d.prefix[n * 2 + 1] = '-'; 1198 n++; 1199 } 1200 strcpy(ki[src].ki_d.prefix + n * 2, "- "); 1201 } 1202 free(path); 1203 } 1204 1205 static void * 1206 expand_list(struct listinfo *inf) 1207 { 1208 void *newlist; 1209 int newmax; 1210 1211 newmax = (inf->maxcount + 1) << 1; 1212 newlist = realloc(inf->l.ptr, newmax * inf->elemsize); 1213 if (newlist == NULL) { 1214 free(inf->l.ptr); 1215 xo_errx(1, "realloc to %d %ss failed", newmax, inf->lname); 1216 } 1217 inf->maxcount = newmax; 1218 inf->l.ptr = newlist; 1219 1220 return (newlist); 1221 } 1222 1223 static void 1224 free_list(struct listinfo *inf) 1225 { 1226 1227 inf->count = inf->elemsize = inf->maxcount = 0; 1228 if (inf->l.ptr != NULL) 1229 free(inf->l.ptr); 1230 inf->addelem = NULL; 1231 inf->lname = NULL; 1232 inf->l.ptr = NULL; 1233 } 1234 1235 static void 1236 init_list(struct listinfo *inf, addelem_rtn artn, int elemsize, 1237 const char *lname) 1238 { 1239 1240 inf->count = inf->maxcount = 0; 1241 inf->elemsize = elemsize; 1242 inf->addelem = artn; 1243 inf->lname = lname; 1244 inf->l.ptr = NULL; 1245 } 1246 1247 VARENT * 1248 find_varentry(const char *name) 1249 { 1250 struct varent *vent; 1251 1252 STAILQ_FOREACH(vent, &varlist, next_ve) { 1253 if (strcmp(vent->var->name, name) == 0) 1254 return vent; 1255 } 1256 return NULL; 1257 } 1258 1259 static void 1260 scan_vars(struct keyword_info *const keywords_info) 1261 { 1262 struct varent *vent; 1263 const VAR *v; 1264 1265 STAILQ_FOREACH(vent, &varlist, next_ve) { 1266 v = vent->var; 1267 if (v->flag & USER) 1268 needuser = 1; 1269 if (v->flag & COMM) 1270 needcomm = 1; 1271 if ((vent->flags & VE_KEEP) != 0) 1272 keywords_info[aliased_keyword_index(v)].flags |= 1273 KWI_HAS_MUST_KEEP_COLUMN; 1274 } 1275 } 1276 1277 /* 1278 * For each explicitly requested keyword, remove all the same keywords 1279 * from "canned" displays. If the same keyword appears multiple times 1280 * only in "canned displays", then keep the first (leftmost) occurence 1281 * only (with the reasoning that columns requested first are the most 1282 * important as their positions catch the eye more). 1283 */ 1284 static void 1285 remove_redundant_columns(struct keyword_info *const keywords_info) 1286 { 1287 struct varent *prev_vent, *vent, *next_vent; 1288 1289 prev_vent = NULL; 1290 STAILQ_FOREACH_SAFE(vent, &varlist, next_ve, next_vent) { 1291 const VAR *const v = vent->var; 1292 struct keyword_info *const kwi = 1293 &keywords_info[aliased_keyword_index(v)]; 1294 1295 /* 1296 * If the current column is not marked as to absolutely keep, 1297 * and we have either already output one with the same keyword 1298 * or know we will output one later, remove it. 1299 */ 1300 if ((vent->flags & VE_KEEP) == 0 && 1301 (kwi->flags & (KWI_HAS_MUST_KEEP_COLUMN | KWI_SEEN)) != 0) { 1302 if (prev_vent == NULL) 1303 STAILQ_REMOVE_HEAD(&varlist, next_ve); 1304 else 1305 STAILQ_REMOVE_AFTER(&varlist, prev_vent, 1306 next_ve); 1307 } else 1308 prev_vent = vent; 1309 1310 1311 kwi->flags |= KWI_SEEN; 1312 } 1313 } 1314 1315 static void 1316 format_output(KINFO *ki) 1317 { 1318 struct varent *vent; 1319 const VAR *v; 1320 KINFO_STR *ks; 1321 char *str; 1322 u_int len; 1323 1324 STAILQ_INIT(&ki->ki_ks); 1325 STAILQ_FOREACH(vent, &varlist, next_ve) { 1326 v = vent->var; 1327 str = (v->oproc)(ki, vent); 1328 ks = malloc(sizeof(*ks)); 1329 if (ks == NULL) 1330 xo_errx(1, "malloc failed"); 1331 ks->ks_str = str; 1332 STAILQ_INSERT_TAIL(&ki->ki_ks, ks, ks_next); 1333 if (str != NULL) { 1334 len = strlen(str); 1335 } else 1336 len = 1; /* "-" */ 1337 if (vent->width < len) 1338 vent->width = len; 1339 } 1340 } 1341 1342 static const char * 1343 fmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki, 1344 char *comm, char *thread, int maxlen) 1345 { 1346 const char *s; 1347 1348 s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, 1349 showthreads && ki->ki_p->ki_numthreads > 1 ? thread : NULL, maxlen); 1350 return (s); 1351 } 1352 1353 static void 1354 saveuser(KINFO *ki) 1355 { 1356 char tdname[COMMLEN + 1]; 1357 1358 ki->ki_valid = 1; 1359 1360 /* 1361 * save arguments if needed 1362 */ 1363 if (needcomm) { 1364 if (ki->ki_p->ki_stat == SZOMB) { 1365 ki->ki_args = strdup("<defunct>"); 1366 } else { 1367 (void)snprintf(tdname, sizeof(tdname), "%s%s", 1368 ki->ki_p->ki_tdname, ki->ki_p->ki_moretdname); 1369 ki->ki_args = fmt(kvm_getargv, ki, 1370 ki->ki_p->ki_comm, tdname, COMMLEN * 2 + 1); 1371 } 1372 if (ki->ki_args == NULL) 1373 xo_errx(1, "malloc failed"); 1374 } else { 1375 ki->ki_args = NULL; 1376 } 1377 if (needenv) { 1378 ki->ki_env = fmt(kvm_getenvv, ki, (char *)NULL, 1379 (char *)NULL, 0); 1380 if (ki->ki_env == NULL) 1381 xo_errx(1, "malloc failed"); 1382 } else { 1383 ki->ki_env = NULL; 1384 } 1385 } 1386 1387 /* A macro used to improve the readability of pscomp(). */ 1388 #define DIFF_RETURN(a, b, field) do { \ 1389 if ((a)->field != (b)->field) \ 1390 return (((a)->field < (b)->field) ? -1 : 1); \ 1391 } while (0) 1392 1393 static int 1394 pscomp(const void *a, const void *b) 1395 { 1396 const KINFO *ka, *kb; 1397 1398 ka = a; 1399 kb = b; 1400 /* SORTCPU and SORTMEM are sorted in descending order. */ 1401 if (sortby == SORTCPU) 1402 DIFF_RETURN(kb, ka, ki_pcpu); 1403 if (sortby == SORTMEM) 1404 DIFF_RETURN(kb, ka, ki_memsize); 1405 /* 1406 * TTY's are sorted in ascending order, except that all NODEV 1407 * processes come before all processes with a device. 1408 */ 1409 if (ka->ki_p->ki_tdev != kb->ki_p->ki_tdev) { 1410 if (ka->ki_p->ki_tdev == NODEV) 1411 return (-1); 1412 if (kb->ki_p->ki_tdev == NODEV) 1413 return (1); 1414 DIFF_RETURN(ka, kb, ki_p->ki_tdev); 1415 } 1416 1417 /* PID's and TID's (threads) are sorted in ascending order. */ 1418 DIFF_RETURN(ka, kb, ki_p->ki_pid); 1419 DIFF_RETURN(ka, kb, ki_p->ki_tid); 1420 return (0); 1421 } 1422 #undef DIFF_RETURN 1423 1424 /* 1425 * ICK (all for getopt), would rather hide the ugliness 1426 * here than taint the main code. 1427 * 1428 * ps foo -> ps -foo 1429 * ps 34 -> ps -p34 1430 * 1431 * The old convention that 't' with no trailing tty arg means the users 1432 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 1433 * feature is available with the option 'T', which takes no argument. 1434 */ 1435 static char * 1436 kludge_oldps_options(const char *optlist, char *origval, const char *nextarg) 1437 { 1438 size_t len; 1439 char *argp, *cp, *newopts, *ns, *optp, *pidp; 1440 1441 /* 1442 * See if the original value includes any option which takes an 1443 * argument (and will thus use up the remainder of the string). 1444 */ 1445 argp = NULL; 1446 if (optlist != NULL) { 1447 for (cp = origval; *cp != '\0'; cp++) { 1448 optp = strchr(optlist, *cp); 1449 if ((optp != NULL) && *(optp + 1) == ':') { 1450 argp = cp; 1451 break; 1452 } 1453 } 1454 } 1455 if (argp != NULL && *origval == '-') 1456 return (origval); 1457 1458 /* 1459 * if last letter is a 't' flag with no argument (in the context 1460 * of the oldps options -- option string NOT starting with a '-' -- 1461 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 1462 * 1463 * However, if a flag accepting a string argument is found earlier 1464 * in the option string (including a possible `t' flag), then the 1465 * remainder of the string must be the argument to that flag; so 1466 * do not modify that argument. Note that a trailing `t' would 1467 * cause argp to be set, if argp was not already set by some 1468 * earlier option. 1469 */ 1470 len = strlen(origval); 1471 cp = origval + len - 1; 1472 pidp = NULL; 1473 if (*cp == 't' && *origval != '-' && cp == argp) { 1474 if (nextarg == NULL || *nextarg == '-' || isdigitch(*nextarg)) 1475 *cp = 'T'; 1476 } else if (argp == NULL) { 1477 /* 1478 * The original value did not include any option which takes 1479 * an argument (and that would include `p' and `t'), so check 1480 * the value for trailing number, or comma-separated list of 1481 * numbers, which we will treat as a pid request. 1482 */ 1483 if (isdigitch(*cp)) { 1484 while (cp >= origval && (*cp == ',' || isdigitch(*cp))) 1485 --cp; 1486 pidp = cp + 1; 1487 } 1488 } 1489 1490 /* 1491 * If nothing needs to be added to the string, then return 1492 * the "original" (although possibly modified) value. 1493 */ 1494 if (*origval == '-' && pidp == NULL) 1495 return (origval); 1496 1497 /* 1498 * Create a copy of the string to add '-' and/or 'p' to the 1499 * original value. 1500 */ 1501 if ((newopts = ns = malloc(len + 3)) == NULL) 1502 xo_errx(1, "malloc failed"); 1503 1504 if (*origval != '-') 1505 *ns++ = '-'; /* add option flag */ 1506 1507 if (pidp == NULL) 1508 strcpy(ns, origval); 1509 else { 1510 /* 1511 * Copy everything before the pid string, add the `p', 1512 * and then copy the pid string. 1513 */ 1514 len = pidp - origval; 1515 memcpy(ns, origval, len); 1516 ns += len; 1517 *ns++ = 'p'; 1518 strcpy(ns, pidp); 1519 } 1520 1521 return (newopts); 1522 } 1523 1524 static void 1525 pidmax_init(void) 1526 { 1527 size_t intsize; 1528 1529 intsize = sizeof(pid_max); 1530 if (sysctlbyname("kern.pid_max", &pid_max, &intsize, NULL, 0) < 0) { 1531 xo_warn("unable to read kern.pid_max"); 1532 pid_max = 99999; 1533 } 1534 } 1535 1536 static void __dead2 1537 usage(void) 1538 { 1539 #define SINGLE_OPTS "[-aCcdeHhjlmrSTuvwXxZ]" 1540 1541 xo_error("%s\n%s\n%s\n%s\n%s\n", 1542 "usage: ps [--libxo] " SINGLE_OPTS " [-O fmt | -o fmt]", 1543 " [-G gid[,gid...]] [-J jid[,jid...]] [-M core] [-N system]", 1544 " [-p pid[,pid...]] [-t tty[,tty...]] [-U user[,user...]]", 1545 " [-D up | down | both]", 1546 " ps [--libxo] -L"); 1547 exit(1); 1548 } 1549