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