1 /* $NetBSD: ps.c,v 1.97 2021/09/14 22:01:17 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2000-2008 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Simon Burge. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c) 1990, 1993, 1994 34 * The Regents of the University of California. All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 3. Neither the name of the University nor the names of its contributors 45 * may be used to endorse or promote products derived from this software 46 * without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58 * SUCH DAMAGE. 59 */ 60 61 #include <sys/cdefs.h> 62 #ifndef lint 63 __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\ 64 The Regents of the University of California. All rights reserved."); 65 #endif /* not lint */ 66 67 #ifndef lint 68 #if 0 69 static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94"; 70 #else 71 __RCSID("$NetBSD: ps.c,v 1.97 2021/09/14 22:01:17 christos Exp $"); 72 #endif 73 #endif /* not lint */ 74 75 #include <sys/param.h> 76 #include <sys/time.h> 77 #include <sys/resource.h> 78 #include <sys/lwp.h> 79 #include <sys/proc.h> 80 #include <sys/stat.h> 81 #include <sys/ioctl.h> 82 #include <sys/sysctl.h> 83 84 #include <stddef.h> 85 #include <ctype.h> 86 #include <err.h> 87 #include <errno.h> 88 #include <fcntl.h> 89 #include <grp.h> 90 #include <kvm.h> 91 #include <limits.h> 92 #include <locale.h> 93 #include <math.h> 94 #include <nlist.h> 95 #include <paths.h> 96 #include <pwd.h> 97 #include <stdio.h> 98 #include <stdlib.h> 99 #include <string.h> 100 #include <unistd.h> 101 #include <util.h> 102 103 #include "ps.h" 104 105 /* 106 * ARGOPTS must contain all option characters that take arguments 107 * (except for 't'!) - it is used in kludge_oldps_options() 108 */ 109 #define GETOPTSTR "aAcCdegG:hjk:LlM:mN:O:o:p:rSsTt:U:uvW:wx" 110 #define ARGOPTS "GkMNOopUW" 111 112 struct varlist displaylist = SIMPLEQ_HEAD_INITIALIZER(displaylist); 113 struct varlist sortlist = SIMPLEQ_HEAD_INITIALIZER(sortlist); 114 115 int eval; /* exit value */ 116 int sumrusage; /* -S */ 117 int termwidth; /* width of screen (0 == infinity) */ 118 int totwidth; /* calculated width of requested variables */ 119 120 int needcomm, needenv, commandonly; 121 uid_t myuid; 122 123 static struct kinfo_lwp 124 *pick_representative_lwp(struct kinfo_proc2 *, 125 struct kinfo_lwp *, int); 126 static struct kinfo_proc2 127 *getkinfo_kvm(kvm_t *, int, int, int *); 128 static struct pinfo 129 *setpinfo(struct kinfo_proc2 *, int, int, int); 130 static char *kludge_oldps_options(char *); 131 static int pscomp(const void *, const void *); 132 static void scanvars(void); 133 __dead static void usage(void); 134 static int parsenum(const char *, const char *); 135 static void descendant_sort(struct pinfo *, int); 136 137 char dfmt[] = "pid tt state time command"; 138 char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; 139 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; 140 char sfmt[] = "uid pid ppid cpu lid nlwp pri nice vsz rss wchan lstate tt " 141 "ltime command"; 142 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; 143 char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command"; 144 145 const char *default_fmt = dfmt; 146 147 struct varent *Opos = NULL; /* -O flag inserts after this point */ 148 149 kvm_t *kd; 150 151 static long long 152 ttyname2dev(const char *ttname, int *xflg, int *what) 153 { 154 struct stat sb; 155 const char *ttypath; 156 char pathbuf[MAXPATHLEN]; 157 158 ttypath = NULL; 159 if (strcmp(ttname, "?") == 0) { 160 *xflg = 1; 161 return KERN_PROC_TTY_NODEV; 162 } 163 if (strcmp(ttname, "-") == 0) 164 return KERN_PROC_TTY_REVOKE; 165 166 if (strcmp(ttname, "co") == 0) 167 ttypath = _PATH_CONSOLE; 168 else if (strncmp(ttname, "pts/", 4) == 0 || 169 strncmp(ttname, "tty", 3) == 0) { 170 (void)snprintf(pathbuf, 171 sizeof(pathbuf), "%s%s", _PATH_DEV, ttname); 172 ttypath = pathbuf; 173 } else if (*ttname != '/') { 174 (void)snprintf(pathbuf, 175 sizeof(pathbuf), "%s%s", _PATH_TTY, ttname); 176 ttypath = pathbuf; 177 } else 178 ttypath = ttname; 179 *what = KERN_PROC_TTY; 180 if (stat(ttypath, &sb) == -1) { 181 devmajor_t pts; 182 int serrno; 183 184 serrno = errno; 185 pts = getdevmajor("pts", S_IFCHR); 186 if (pts != NODEVMAJOR && strncmp(ttname, "pts/", 4) == 0) { 187 int ptsminor = atoi(ttname + 4); 188 189 snprintf(pathbuf, sizeof(pathbuf), "pts/%d", ptsminor); 190 if (strcmp(pathbuf, ttname) == 0 && ptsminor >= 0) 191 return makedev(pts, ptsminor); 192 } 193 errno = serrno; 194 err(EXIT_FAILURE, "%s", ttypath); 195 } 196 if (!S_ISCHR(sb.st_mode)) 197 errx(EXIT_FAILURE, "%s: not a terminal", ttypath); 198 return sb.st_rdev; 199 } 200 201 int 202 main(int argc, char *argv[]) 203 { 204 struct kinfo_proc2 *kinfo; 205 struct pinfo *pinfo; 206 struct varent *vent; 207 struct winsize ws; 208 struct kinfo_lwp *kl, *l; 209 int ch, i, j, fmt, lineno, descendancy, nentries, nlwps; 210 long long flag; 211 int calc_pcpu, prtheader, wflag, what, xflg, rawcpu, showlwps; 212 char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX]; 213 char *ttname; 214 215 setprogname(argv[0]); 216 (void)setlocale(LC_ALL, ""); 217 218 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 219 ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 220 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 221 ws.ws_col == 0) 222 termwidth = 79; 223 else 224 termwidth = ws.ws_col - 1; 225 226 if (argc > 1) 227 argv[1] = kludge_oldps_options(argv[1]); 228 229 descendancy = fmt = prtheader = wflag = xflg = rawcpu = showlwps = 0; 230 what = KERN_PROC_UID; 231 flag = myuid = getuid(); 232 memf = nlistf = swapf = NULL; 233 234 while ((ch = getopt(argc, argv, GETOPTSTR)) != -1) 235 switch((char)ch) { 236 case 'A': 237 /* "-A" shows all processes, like "-ax" */ 238 xflg = 1; 239 /*FALLTHROUGH*/ 240 case 'a': 241 what = KERN_PROC_ALL; 242 flag = 0; 243 break; 244 case 'c': 245 commandonly = 1; 246 break; 247 case 'd': 248 descendancy = 1; 249 break; 250 case 'e': /* XXX set ufmt */ 251 needenv = 1; 252 break; 253 case 'C': 254 rawcpu = 1; 255 break; 256 case 'g': 257 break; /* no-op */ 258 case 'G': 259 if (*optarg != '\0') { 260 struct group *gr; 261 262 what = KERN_PROC_GID; 263 gr = getgrnam(optarg); 264 if (gr == NULL) { 265 flag = parsenum(optarg, "group id"); 266 } else 267 flag = gr->gr_gid; 268 } 269 break; 270 271 case 'h': 272 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 273 break; 274 case 'j': 275 parsefmt(jfmt); 276 fmt = 1; 277 jfmt[0] = '\0'; 278 break; 279 case 'k': 280 parsesort(optarg); 281 break; 282 case 'K': 283 break; /* no-op - was dontuseprocfs */ 284 case 'L': 285 showkey(); 286 return 0; 287 case 'l': 288 parsefmt(lfmt); 289 fmt = 1; 290 lfmt[0] = '\0'; 291 break; 292 case 'M': 293 memf = optarg; 294 break; 295 case 'm': 296 parsesort("vsz"); 297 break; 298 case 'N': 299 nlistf = optarg; 300 break; 301 case 'O': 302 /* 303 * If this is not the first -O option, insert 304 * just after the previous one. 305 * 306 * If there is no format yet, start with the default 307 * format, and insert after the pid column. 308 * 309 * If there is already a format, insert after 310 * the pid column, or at the end if there's no 311 * pid column. 312 */ 313 if (!Opos) { 314 if (!fmt) 315 parsefmt(default_fmt); 316 Opos = varlist_find(&displaylist, "pid"); 317 } 318 parsefmt_insert(optarg, &Opos); 319 fmt = 1; 320 break; 321 case 'o': 322 parsefmt(optarg); 323 fmt = 1; 324 break; 325 case 'p': 326 what = KERN_PROC_PID; 327 flag = parsenum(optarg, "process id"); 328 xflg = 1; 329 break; 330 case 'r': 331 parsesort("%cpu"); 332 break; 333 case 'S': 334 sumrusage = 1; 335 break; 336 case 's': 337 /* -L was already taken... */ 338 showlwps = 1; 339 default_fmt = sfmt; 340 break; 341 case 'T': 342 if ((ttname = ttyname(STDIN_FILENO)) == NULL) 343 errx(EXIT_FAILURE, "stdin: not a terminal"); 344 flag = ttyname2dev(ttname, &xflg, &what); 345 break; 346 case 't': 347 flag = ttyname2dev(optarg, &xflg, &what); 348 break; 349 case 'U': 350 if (*optarg != '\0') { 351 struct passwd *pw; 352 353 what = KERN_PROC_UID; 354 pw = getpwnam(optarg); 355 if (pw == NULL) { 356 flag = parsenum(optarg, "user id"); 357 } else 358 flag = pw->pw_uid; 359 } 360 break; 361 case 'u': 362 parsefmt(ufmt); 363 parsesort("%cpu"); 364 fmt = 1; 365 ufmt[0] = '\0'; 366 break; 367 case 'v': 368 parsefmt(vfmt); 369 parsesort("vsz"); 370 fmt = 1; 371 vfmt[0] = '\0'; 372 break; 373 case 'W': 374 swapf = optarg; 375 break; 376 case 'w': 377 if (wflag) 378 termwidth = UNLIMITED; 379 else if (termwidth < 131) 380 termwidth = 131; 381 wflag++; 382 break; 383 case 'x': 384 xflg = 1; 385 break; 386 case '?': 387 default: 388 usage(); 389 } 390 argc -= optind; 391 argv += optind; 392 393 #define BACKWARD_COMPATIBILITY 394 #ifdef BACKWARD_COMPATIBILITY 395 if (*argv) { 396 nlistf = *argv; 397 if (*++argv) { 398 memf = *argv; 399 if (*++argv) 400 swapf = *argv; 401 } 402 } 403 #endif 404 405 if (memf == NULL) { 406 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); 407 donlist_sysctl(); 408 } else 409 kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf); 410 411 if (kd == NULL) 412 errx(EXIT_FAILURE, "%s", errbuf); 413 414 if (!fmt) 415 parsefmt(default_fmt); 416 417 /* Add default sort criteria */ 418 parsesort("tdev,pid"); 419 calc_pcpu = 0; 420 SIMPLEQ_FOREACH(vent, &sortlist, next) { 421 if (vent->var->flag & LWP || vent->var->type == UNSPECIFIED) 422 warnx("Cannot sort on %s, sort key ignored", 423 vent->var->name); 424 if (vent->var->type == PCPU) 425 calc_pcpu = 1; 426 } 427 if (!calc_pcpu) 428 SIMPLEQ_FOREACH(vent, &displaylist, next) 429 if (vent->var->type == PCPU) { 430 calc_pcpu = 1; 431 break; 432 } 433 434 /* 435 * scan requested variables, noting what structures are needed. 436 */ 437 scanvars(); 438 439 /* 440 * select procs 441 */ 442 if (!(kinfo = getkinfo_kvm(kd, what, flag, &nentries))) 443 errx(EXIT_FAILURE, "%s", kvm_geterr(kd)); 444 if (nentries == 0) { 445 printheader(); 446 return 1; 447 } 448 pinfo = setpinfo(kinfo, nentries, calc_pcpu, rawcpu); 449 450 /* 451 * sort proc list 452 */ 453 qsort(pinfo, nentries, sizeof(*pinfo), pscomp); 454 455 /* 456 * We want things in descendant order 457 */ 458 if (descendancy) 459 descendant_sort(pinfo, nentries); 460 461 /* 462 * For each proc, call each variable output function in 463 * "setwidth" mode to determine the widest element of 464 * the column. 465 */ 466 467 for (i = 0; i < nentries; i++) { 468 struct pinfo *pi = &pinfo[i]; 469 struct kinfo_proc2 *ki = pi->ki; 470 471 if (xflg == 0 && (ki->p_tdev == (uint32_t)NODEV || 472 (ki->p_flag & P_CONTROLT) == 0)) 473 continue; 474 475 kl = kvm_getlwps(kd, ki->p_pid, ki->p_paddr, 476 sizeof(*kl), &nlwps); 477 if (kl == 0) 478 nlwps = 0; 479 if (showlwps == 0) { 480 l = pick_representative_lwp(ki, kl, nlwps); 481 SIMPLEQ_FOREACH(vent, &displaylist, next) 482 OUTPUT(vent, l, pi, ki, WIDTHMODE); 483 } else { 484 /* The printing is done with the loops 485 * reversed, but here we don't need that, 486 * and this improves the code locality a bit. 487 */ 488 SIMPLEQ_FOREACH(vent, &displaylist, next) 489 for (j = 0; j < nlwps; j++) 490 OUTPUT(vent, &kl[j], pi, ki, WIDTHMODE); 491 } 492 } 493 /* 494 * Print header - AFTER determining process field widths. 495 * printheader() also adds up the total width of all 496 * fields the first time it's called. 497 */ 498 printheader(); 499 /* 500 * For each proc, call each variable output function in 501 * print mode. 502 */ 503 for (i = lineno = 0; i < nentries; i++) { 504 struct pinfo *pi = &pinfo[i]; 505 struct kinfo_proc2 *ki = pi->ki; 506 507 if (xflg == 0 && (ki->p_tdev == (uint32_t)NODEV || 508 (ki->p_flag & P_CONTROLT ) == 0)) 509 continue; 510 kl = kvm_getlwps(kd, ki->p_pid, (u_long)ki->p_paddr, 511 sizeof(*kl), &nlwps); 512 if (kl == 0) 513 nlwps = 0; 514 if (showlwps == 0) { 515 l = pick_representative_lwp(ki, kl, nlwps); 516 SIMPLEQ_FOREACH(vent, &displaylist, next) { 517 OUTPUT(vent, l, pi, ki, PRINTMODE); 518 if (SIMPLEQ_NEXT(vent, next) != NULL) 519 (void)putchar(' '); 520 } 521 (void)putchar('\n'); 522 if (prtheader && lineno++ == prtheader - 4) { 523 (void)putchar('\n'); 524 printheader(); 525 lineno = 0; 526 } 527 } else { 528 for (j = 0; j < nlwps; j++) { 529 SIMPLEQ_FOREACH(vent, &displaylist, next) { 530 OUTPUT(vent, &kl[j], pi, ki, PRINTMODE); 531 if (SIMPLEQ_NEXT(vent, next) != NULL) 532 (void)putchar(' '); 533 } 534 (void)putchar('\n'); 535 if (prtheader && lineno++ == prtheader - 4) { 536 (void)putchar('\n'); 537 printheader(); 538 lineno = 0; 539 } 540 } 541 } 542 } 543 544 #ifdef __NO_LEAKS 545 free(pinfo); 546 #endif 547 548 return eval; 549 } 550 551 static struct kinfo_lwp * 552 pick_representative_lwp(struct kinfo_proc2 *ki, struct kinfo_lwp *kl, int nlwps) 553 { 554 int i, onproc, running, sleeping, stopped, suspended; 555 static struct kinfo_lwp zero_lwp; 556 557 if (kl == 0) 558 return &zero_lwp; 559 560 /* Trivial case: only one LWP */ 561 if (nlwps == 1) 562 return kl; 563 564 switch (ki->p_realstat) { 565 case SSTOP: 566 case SACTIVE: 567 /* Pick the most live LWP */ 568 onproc = running = sleeping = stopped = suspended = -1; 569 for (i = 0; i < nlwps; i++) { 570 switch (kl[i].l_stat) { 571 case LSONPROC: 572 onproc = i; 573 break; 574 case LSRUN: 575 running = i; 576 break; 577 case LSSLEEP: 578 sleeping = i; 579 break; 580 case LSSTOP: 581 stopped = i; 582 break; 583 case LSSUSPENDED: 584 suspended = i; 585 break; 586 } 587 } 588 if (onproc != -1) 589 return &kl[onproc]; 590 if (running != -1) 591 return &kl[running]; 592 if (sleeping != -1) 593 return &kl[sleeping]; 594 if (stopped != -1) 595 return &kl[stopped]; 596 if (suspended != -1) 597 return &kl[suspended]; 598 break; 599 case SZOMB: 600 /* First will do */ 601 return kl; 602 break; 603 } 604 /* Error condition! */ 605 warnx("Inconsistent LWP state for process %d", ki->p_pid); 606 return kl; 607 } 608 609 static struct kinfo_proc2 * 610 getkinfo_kvm(kvm_t *kdp, int what, int flag, int *nentriesp) 611 { 612 613 return kvm_getproc2(kdp, what, flag, sizeof(struct kinfo_proc2), 614 nentriesp); 615 } 616 617 static struct pinfo * 618 setpinfo(struct kinfo_proc2 *ki, int nentries, int calc_pcpu, int rawcpu) 619 { 620 struct pinfo *pi; 621 int i; 622 623 pi = ecalloc(nentries, sizeof(*pi)); 624 if (calc_pcpu && !nlistread) 625 donlist(); 626 627 for (i = 0; i < nentries; i++) { 628 pi[i].ki = &ki[i]; 629 if (!calc_pcpu) 630 continue; 631 if (ki[i].p_swtime == 0 || ki[i].p_realstat == SZOMB) { 632 pi[i].pcpu = 0.0; 633 continue; 634 } 635 pi[i].pcpu = 100.0 * (double)ki[i].p_pctcpu / fscale; 636 if (!rawcpu) 637 pi[i].pcpu /= 1.0 - exp(ki[i].p_swtime * log_ccpu); 638 } 639 640 return pi; 641 } 642 643 static void 644 scanvars(void) 645 { 646 struct varent *vent; 647 VAR *v; 648 649 SIMPLEQ_FOREACH(vent, &displaylist, next) { 650 v = vent->var; 651 if (v->flag & COMM) { 652 needcomm = 1; 653 break; 654 } 655 } 656 } 657 658 static int 659 pscomp(const void *a, const void *b) 660 { 661 const struct pinfo *pa = (const struct pinfo *)a; 662 const struct pinfo *pb = (const struct pinfo *)b; 663 const struct kinfo_proc2 *ka = pa->ki; 664 const struct kinfo_proc2 *kb = pb->ki; 665 666 int i; 667 int64_t i64; 668 VAR *v; 669 struct varent *ve; 670 const sigset_t *sa, *sb; 671 672 #define V_SIZE(k) ((k)->p_vm_msize) 673 #define RDIFF_N(t, n) \ 674 if (((const t *)((const char *)ka + v->off))[n] > ((const t *)((const char *)kb + v->off))[n]) \ 675 return 1; \ 676 if (((const t *)((const char *)ka + v->off))[n] < ((const t *)((const char *)kb + v->off))[n]) \ 677 return -1; 678 679 #define RDIFF(type) RDIFF_N(type, 0); continue 680 681 SIMPLEQ_FOREACH(ve, &sortlist, next) { 682 v = ve->var; 683 if (v->flag & LWP) 684 /* LWP structure not available (yet) */ 685 continue; 686 /* Sort on pvar() fields, + a few others */ 687 switch (v->type) { 688 case CHAR: 689 RDIFF(char); 690 case UCHAR: 691 RDIFF(u_char); 692 case SHORT: 693 RDIFF(short); 694 case USHORT: 695 RDIFF(ushort); 696 case INT: 697 RDIFF(int); 698 case UINT: 699 RDIFF(uint); 700 case LONG: 701 RDIFF(long); 702 case ULONG: 703 RDIFF(ulong); 704 case INT32: 705 RDIFF(int32_t); 706 case UINT32: 707 RDIFF(uint32_t); 708 case SIGLIST: 709 sa = (const void *)((const char *)ka + v->off); 710 sb = (const void *)((const char *)kb + v->off); 711 i = 0; 712 do { 713 if (sa->__bits[i] > sb->__bits[i]) 714 return 1; 715 if (sa->__bits[i] < sb->__bits[i]) 716 return -1; 717 i++; 718 } while (i < (int)__arraycount(sa->__bits)); 719 continue; 720 case INT64: 721 RDIFF(int64_t); 722 case KPTR: 723 case KPTR24: 724 case UINT64: 725 RDIFF(uint64_t); 726 case TIMEVAL: 727 /* compare xxx_sec then xxx_usec */ 728 RDIFF_N(uint32_t, 0); 729 RDIFF_N(uint32_t, 1); 730 continue; 731 case CPUTIME: 732 i64 = ka->p_rtime_sec * 1000000 + ka->p_rtime_usec; 733 i64 -= kb->p_rtime_sec * 1000000 + kb->p_rtime_usec; 734 if (sumrusage) { 735 i64 += ka->p_uctime_sec * 1000000 736 + ka->p_uctime_usec; 737 i64 -= kb->p_uctime_sec * 1000000 738 + kb->p_uctime_usec; 739 } 740 if (i64 != 0) 741 return i64 > 0 ? 1 : -1; 742 continue; 743 case PCPU: 744 i = pb->pcpu - pa->pcpu; 745 if (i != 0) 746 return i; 747 continue; 748 case VSIZE: 749 i = V_SIZE(kb) - V_SIZE(ka); 750 if (i != 0) 751 return i; 752 continue; 753 754 default: 755 /* Ignore everything else */ 756 break; 757 } 758 } 759 return 0; 760 761 #undef VSIZE 762 } 763 764 /* 765 * ICK (all for getopt), would rather hide the ugliness 766 * here than taint the main code. 767 * 768 * ps foo -> ps -foo 769 * ps 34 -> ps -p34 770 * 771 * The old convention that 't' with no trailing tty arg means the user's 772 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 773 * feature is available with the option 'T', which takes no argument. 774 */ 775 static char * 776 kludge_oldps_options(char *s) 777 { 778 size_t len; 779 char *newopts, *ns, *cp; 780 781 len = strlen(s); 782 newopts = ns = emalloc(len + 3); 783 /* 784 * options begin with '-' 785 */ 786 if (*s != '-') 787 *ns++ = '-'; /* add option flag */ 788 /* 789 * gaze to end of argv[1] 790 */ 791 cp = s + len - 1; 792 /* 793 * if the last letter is a 't' flag and there are no other option 794 * characters that take arguments (eg U, p, o) in the option 795 * string and the option string doesn't start with a '-' then 796 * convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 797 */ 798 if (*cp == 't' && *s != '-' && strpbrk(s, ARGOPTS) == NULL) 799 *cp = 'T'; 800 else { 801 /* 802 * otherwise check for trailing number, which *may* be a 803 * pid. 804 */ 805 while (cp >= s && isdigit((unsigned char)*cp)) 806 --cp; 807 } 808 cp++; 809 memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */ 810 ns += cp - s; 811 /* 812 * if there's a trailing number, and not a preceding 'p' (pid) or 813 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 814 */ 815 if (isdigit((unsigned char)*cp) && 816 (cp == s || (cp[-1] != 'U' && cp[-1] != 't' && cp[-1] != 'p' && 817 cp[-1] != '/' && (cp - 1 == s || cp[-2] != 't')))) 818 *ns++ = 'p'; 819 /* and append the number */ 820 (void)strcpy(ns, cp); /* XXX strcpy is safe here */ 821 822 return (newopts); 823 } 824 825 static int 826 parsenum(const char *str, const char *msg) 827 { 828 char *ep; 829 unsigned long ul; 830 831 ul = strtoul(str, &ep, 0); 832 833 if (*str == '\0' || *ep != '\0') 834 errx(EXIT_FAILURE, "Invalid %s: `%s'", msg, str); 835 836 if (ul > INT_MAX) 837 errx(EXIT_FAILURE, "Out of range %s: `%s'", msg, str); 838 839 return (int)ul; 840 } 841 842 static void 843 descendant_sort(struct pinfo *ki, int items) 844 { 845 int dst, lvl, maxlvl, n, ndst, nsrc, siblings, src; 846 unsigned char *path; 847 struct pinfo kn; 848 849 /* 850 * First, sort the entries by descendancy, tracking the descendancy 851 * depth in the level field. 852 */ 853 src = 0; 854 maxlvl = 0; 855 while (src < items) { 856 if (ki[src].level) { 857 src++; 858 continue; 859 } 860 for (nsrc = 1; src + nsrc < items; nsrc++) 861 if (!ki[src + nsrc].level) 862 break; 863 864 for (dst = 0; dst < items; dst++) { 865 if (ki[dst].ki->p_pid == ki[src].ki->p_pid) 866 continue; 867 if (ki[dst].ki->p_pid == ki[src].ki->p_ppid) 868 break; 869 } 870 871 if (dst == items) { 872 src += nsrc; 873 continue; 874 } 875 876 for (ndst = 1; dst + ndst < items; ndst++) 877 if (ki[dst + ndst].level <= ki[dst].level) 878 break; 879 880 for (n = src; n < src + nsrc; n++) { 881 ki[n].level += ki[dst].level + 1; 882 if (maxlvl < ki[n].level) 883 maxlvl = ki[n].level; 884 } 885 886 while (nsrc) { 887 if (src < dst) { 888 kn = ki[src]; 889 memmove(ki + src, ki + src + 1, 890 (dst - src + ndst - 1) * sizeof(*ki)); 891 ki[dst + ndst - 1] = kn; 892 nsrc--; 893 dst--; 894 ndst++; 895 } else if (src != dst + ndst) { 896 kn = ki[src]; 897 memmove(ki + dst + ndst + 1, ki + dst + ndst, 898 (src - dst - ndst) * sizeof(*ki)); 899 ki[dst + ndst] = kn; 900 ndst++; 901 nsrc--; 902 src++; 903 } else { 904 ndst += nsrc; 905 src += nsrc; 906 nsrc = 0; 907 } 908 } 909 } 910 911 /* 912 * Now populate prefix (instead of level) with the command 913 * prefix used to show descendancies. 914 */ 915 path = ecalloc((maxlvl + 7) / 8, 1); 916 for (src = 0; src < items; src++) { 917 if ((lvl = ki[src].level) == 0) { 918 ki[src].prefix = NULL; 919 continue; 920 } 921 ki[src].prefix = emalloc(lvl * 2 + 1); 922 for (n = 0; n < lvl - 2; n++) { 923 ki[src].prefix[n * 2] = 924 path[n / 8] & 1 << (n % 8) ? '|' : ' '; 925 ki[src].prefix[n * 2 + 1] = ' '; 926 927 } 928 if (n == lvl - 2) { 929 /* Have I any more siblings? */ 930 for (siblings = 0, dst = src + 1; dst < items; dst++) { 931 if (ki[dst].level > lvl) 932 continue; 933 if (ki[dst].level == lvl) 934 siblings = 1; 935 break; 936 } 937 if (siblings) 938 path[n / 8] |= 1 << (n % 8); 939 else 940 path[n / 8] &= ~(1 << (n % 8)); 941 ki[src].prefix[n * 2] = siblings ? '|' : '`'; 942 ki[src].prefix[n * 2 + 1] = '-'; 943 n++; 944 } 945 strcpy(ki[src].prefix + n * 2, "- "); 946 } 947 free(path); 948 } 949 950 static void 951 usage(void) 952 { 953 954 (void)fprintf(stderr, 955 "usage:\t%s\n\t %s\n\t%s\n", 956 "ps [-AaCcdehjlmrSsTuvwx] [-G group] [-k key] [-M core] [-N system]", 957 "[-O fmt] [-o fmt] [-p pid] [-t tty] [-U user] [-W swap]", 958 "ps -L"); 959 exit(1); 960 /* NOTREACHED */ 961 } 962