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