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