1 /* $NetBSD: ps.c,v 1.67 2008/04/28 20:22:51 martin Exp $ */ 2 3 /* 4 * Copyright (c) 2000 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\n\ 64 The Regents of the University of California. All rights reserved.\n"); 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.67 2008/04/28 20:22:51 martin Exp $"); 72 #endif 73 #endif /* not lint */ 74 75 #include <sys/param.h> 76 #include <sys/user.h> 77 #include <sys/time.h> 78 #include <sys/resource.h> 79 #include <sys/lwp.h> 80 #include <sys/proc.h> 81 #include <sys/stat.h> 82 #include <sys/ioctl.h> 83 #include <sys/sysctl.h> 84 85 #include <stddef.h> 86 #include <ctype.h> 87 #include <err.h> 88 #include <errno.h> 89 #include <fcntl.h> 90 #include <kvm.h> 91 #include <limits.h> 92 #include <nlist.h> 93 #include <paths.h> 94 #include <pwd.h> 95 #include <stdio.h> 96 #include <stdlib.h> 97 #include <string.h> 98 #include <unistd.h> 99 100 #include "ps.h" 101 102 /* 103 * ARGOPTS must contain all option characters that take arguments 104 * (except for 't'!) - it is used in kludge_oldps_options() 105 */ 106 #define GETOPTSTR "acCeghjk:LlM:mN:O:o:p:rSsTt:U:uvW:wx" 107 #define ARGOPTS "kMNOopUW" 108 109 struct kinfo_proc2 *kinfo; 110 struct varlist displaylist = SIMPLEQ_HEAD_INITIALIZER(displaylist); 111 struct varlist sortlist = SIMPLEQ_HEAD_INITIALIZER(sortlist); 112 113 int eval; /* exit value */ 114 int rawcpu; /* -C */ 115 int sumrusage; /* -S */ 116 int termwidth; /* width of screen (0 == infinity) */ 117 int totwidth; /* calculated width of requested variables */ 118 119 int needcomm, needenv, commandonly; 120 uid_t myuid; 121 122 static struct kinfo_lwp 123 *pick_representative_lwp(struct kinfo_proc2 *, 124 struct kinfo_lwp *, int); 125 static struct kinfo_proc2 126 *getkinfo_kvm(kvm_t *, int, int, int *); 127 static char *kludge_oldps_options(char *); 128 static int pscomp(const void *, const void *); 129 static void scanvars(void); 130 static void usage(void); 131 static int parsenum(const char *, const char *); 132 int main(int, char *[]); 133 134 char dfmt[] = "pid tt state time command"; 135 char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; 136 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; 137 char sfmt[] = "uid pid ppid cpu lid nlwp pri nice vsz rss wchan lstate tt " 138 "time command"; 139 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; 140 char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command"; 141 142 const char *default_fmt = dfmt; 143 144 struct varent *Opos = NULL; /* -O flag inserts after this point */ 145 146 kvm_t *kd; 147 148 int 149 main(int argc, char *argv[]) 150 { 151 struct varent *vent; 152 struct winsize ws; 153 struct kinfo_lwp *kl, *l; 154 int ch, flag, i, j, fmt, lineno, nentries, nlwps; 155 int prtheader, wflag, what, xflg, mode, showlwps; 156 char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX]; 157 char *ttname; 158 159 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 160 ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 161 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 162 ws.ws_col == 0) 163 termwidth = 79; 164 else 165 termwidth = ws.ws_col - 1; 166 167 setncpu(); 168 169 if (argc > 1) 170 argv[1] = kludge_oldps_options(argv[1]); 171 172 fmt = prtheader = wflag = xflg = showlwps = 0; 173 what = KERN_PROC_UID; 174 flag = myuid = getuid(); 175 memf = nlistf = swapf = NULL; 176 mode = PRINTMODE; 177 while ((ch = getopt(argc, argv, GETOPTSTR)) != -1) 178 switch((char)ch) { 179 case 'a': 180 what = KERN_PROC_ALL; 181 flag = 0; 182 break; 183 case 'c': 184 commandonly = 1; 185 break; 186 case 'e': /* XXX set ufmt */ 187 needenv = 1; 188 break; 189 case 'C': 190 rawcpu = 1; 191 break; 192 case 'g': 193 break; /* no-op */ 194 case 'h': 195 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 196 break; 197 case 'j': 198 parsefmt(jfmt); 199 fmt = 1; 200 jfmt[0] = '\0'; 201 break; 202 case 'k': 203 parsesort(optarg); 204 break; 205 case 'K': 206 break; /* no-op - was dontuseprocfs */ 207 case 'L': 208 showkey(); 209 exit(0); 210 /* NOTREACHED */ 211 case 'l': 212 parsefmt(lfmt); 213 fmt = 1; 214 lfmt[0] = '\0'; 215 break; 216 case 'M': 217 memf = optarg; 218 break; 219 case 'm': 220 parsesort("vsz"); 221 break; 222 case 'N': 223 nlistf = optarg; 224 break; 225 case 'O': 226 /* 227 * If this is not the first -O option, insert 228 * just after the previous one. 229 * 230 * If there is no format yet, start with the default 231 * format, and insert after the pid column. 232 * 233 * If there is already a format, insert after 234 * the pid column, or at the end if there's no 235 * pid column. 236 */ 237 if (!Opos) { 238 if (!fmt) 239 parsefmt(default_fmt); 240 Opos = varlist_find(&displaylist, "pid"); 241 } 242 parsefmt_insert(optarg, &Opos); 243 fmt = 1; 244 break; 245 case 'o': 246 parsefmt(optarg); 247 fmt = 1; 248 break; 249 case 'p': 250 what = KERN_PROC_PID; 251 flag = parsenum(optarg, "process id"); 252 xflg = 1; 253 break; 254 case 'r': 255 parsesort("%cpu"); 256 break; 257 case 'S': 258 sumrusage = 1; 259 break; 260 case 's': 261 /* -L was already taken... */ 262 showlwps = 1; 263 default_fmt = sfmt; 264 break; 265 case 'T': 266 if ((ttname = ttyname(STDIN_FILENO)) == NULL) 267 errx(1, "stdin: not a terminal"); 268 goto tty; 269 case 't': 270 ttname = optarg; 271 tty: { 272 struct stat sb; 273 const char *ttypath; 274 char pathbuf[MAXPATHLEN]; 275 276 flag = 0; 277 ttypath = NULL; 278 if (strcmp(ttname, "?") == 0) { 279 flag = KERN_PROC_TTY_NODEV; 280 xflg = 1; 281 } else if (strcmp(ttname, "-") == 0) 282 flag = KERN_PROC_TTY_REVOKE; 283 else if (strcmp(ttname, "co") == 0) 284 ttypath = _PATH_CONSOLE; 285 else if (strncmp(ttname, "pts/", 4) == 0 || 286 strncmp(ttname, "tty", 3) == 0) { 287 (void)snprintf(pathbuf, 288 sizeof(pathbuf), "%s%s", _PATH_DEV, ttname); 289 ttypath = pathbuf; 290 } else if (*ttname != '/') { 291 (void)snprintf(pathbuf, 292 sizeof(pathbuf), "%s%s", _PATH_TTY, ttname); 293 ttypath = pathbuf; 294 } else 295 ttypath = ttname; 296 what = KERN_PROC_TTY; 297 if (flag == 0) { 298 if (stat(ttypath, &sb) == -1) 299 err(1, "%s", ttypath); 300 if (!S_ISCHR(sb.st_mode)) 301 errx(1, "%s: not a terminal", ttypath); 302 flag = sb.st_rdev; 303 } 304 break; 305 } 306 case 'U': 307 if (*optarg != '\0') { 308 struct passwd *pw; 309 310 what = KERN_PROC_UID; 311 pw = getpwnam(optarg); 312 if (pw == NULL) { 313 flag = parsenum(optarg, "user name"); 314 } else 315 flag = pw->pw_uid; 316 } 317 break; 318 case 'u': 319 parsefmt(ufmt); 320 parsesort("%cpu"); 321 fmt = 1; 322 ufmt[0] = '\0'; 323 break; 324 case 'v': 325 parsefmt(vfmt); 326 parsesort("vsz"); 327 fmt = 1; 328 vfmt[0] = '\0'; 329 break; 330 case 'W': 331 swapf = optarg; 332 break; 333 case 'w': 334 if (wflag) 335 termwidth = UNLIMITED; 336 else if (termwidth < 131) 337 termwidth = 131; 338 wflag++; 339 break; 340 case 'x': 341 xflg = 1; 342 break; 343 case '?': 344 default: 345 usage(); 346 } 347 argc -= optind; 348 argv += optind; 349 350 #define BACKWARD_COMPATIBILITY 351 #ifdef BACKWARD_COMPATIBILITY 352 if (*argv) { 353 nlistf = *argv; 354 if (*++argv) { 355 memf = *argv; 356 if (*++argv) 357 swapf = *argv; 358 } 359 } 360 #endif 361 362 if (memf == NULL) { 363 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); 364 donlist_sysctl(); 365 } else 366 kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf); 367 368 if (kd == 0) 369 errx(1, "%s", errbuf); 370 371 if (!fmt) 372 parsefmt(default_fmt); 373 374 /* Add default sort criteria */ 375 parsesort("tdev,pid"); 376 SIMPLEQ_FOREACH(vent, &sortlist, next) { 377 if (vent->var->flag & LWP || vent->var->type == UNSPECIFIED) 378 warnx("Cannot sort on %s, sort key ignored\n", 379 vent->var->name); 380 } 381 382 /* 383 * scan requested variables, noting what structures are needed. 384 */ 385 scanvars(); 386 387 /* 388 * select procs 389 */ 390 if (!(kinfo = getkinfo_kvm(kd, what, flag, &nentries))) 391 err(1, "%s", kvm_geterr(kd)); 392 if (nentries == 0) { 393 printheader(); 394 exit(1); 395 } 396 /* 397 * sort proc list 398 */ 399 qsort(kinfo, nentries, sizeof(struct kinfo_proc2), pscomp); 400 /* 401 * For each proc, call each variable output function in 402 * "setwidth" mode to determine the widest element of 403 * the column. 404 */ 405 if (mode == PRINTMODE) 406 for (i = 0; i < nentries; i++) { 407 struct kinfo_proc2 *ki = &kinfo[i]; 408 409 if (xflg == 0 && (ki->p_tdev == NODEV || 410 (ki->p_flag & P_CONTROLT) == 0)) 411 continue; 412 413 kl = kvm_getlwps(kd, ki->p_pid, ki->p_paddr, 414 sizeof(struct kinfo_lwp), &nlwps); 415 if (kl == 0) 416 nlwps = 0; 417 if (showlwps == 0) { 418 l = pick_representative_lwp(ki, kl, nlwps); 419 SIMPLEQ_FOREACH(vent, &displaylist, next) 420 OUTPUT(vent, ki, l, WIDTHMODE); 421 } else { 422 /* The printing is done with the loops 423 * reversed, but here we don't need that, 424 * and this improves the code locality a bit. 425 */ 426 SIMPLEQ_FOREACH(vent, &displaylist, next) 427 for (j = 0; j < nlwps; j++) 428 OUTPUT(vent, ki, &kl[j], 429 WIDTHMODE); 430 } 431 } 432 /* 433 * Print header - AFTER determining process field widths. 434 * printheader() also adds up the total width of all 435 * fields the first time it's called. 436 */ 437 printheader(); 438 /* 439 * For each proc, call each variable output function in 440 * print mode. 441 */ 442 for (i = lineno = 0; i < nentries; i++) { 443 struct kinfo_proc2 *ki = &kinfo[i]; 444 445 if (xflg == 0 && (ki->p_tdev == NODEV || 446 (ki->p_flag & P_CONTROLT ) == 0)) 447 continue; 448 kl = kvm_getlwps(kd, ki->p_pid, (u_long)ki->p_paddr, 449 sizeof(struct kinfo_lwp), &nlwps); 450 if (kl == 0) 451 nlwps = 0; 452 if (showlwps == 0) { 453 l = pick_representative_lwp(ki, kl, nlwps); 454 SIMPLEQ_FOREACH(vent, &displaylist, next) { 455 OUTPUT(vent, ki, l, mode); 456 if (SIMPLEQ_NEXT(vent, next) != NULL) 457 (void)putchar(' '); 458 } 459 (void)putchar('\n'); 460 if (prtheader && lineno++ == prtheader - 4) { 461 (void)putchar('\n'); 462 printheader(); 463 lineno = 0; 464 } 465 } else { 466 for (j = 0; j < nlwps; j++) { 467 SIMPLEQ_FOREACH(vent, &displaylist, next) { 468 OUTPUT(vent, ki, &kl[j], mode); 469 if (SIMPLEQ_NEXT(vent, next) != NULL) 470 (void)putchar(' '); 471 } 472 (void)putchar('\n'); 473 if (prtheader && lineno++ == prtheader - 4) { 474 (void)putchar('\n'); 475 printheader(); 476 lineno = 0; 477 } 478 } 479 } 480 } 481 exit(eval); 482 /* NOTREACHED */ 483 } 484 485 static struct kinfo_lwp * 486 pick_representative_lwp(struct kinfo_proc2 *ki, struct kinfo_lwp *kl, int nlwps) 487 { 488 int i, onproc, running, sleeping, stopped, suspended; 489 static struct kinfo_lwp zero_lwp; 490 491 if (kl == 0) 492 return &zero_lwp; 493 494 /* Trivial case: only one LWP */ 495 if (nlwps == 1) 496 return kl; 497 498 switch (ki->p_realstat) { 499 case SSTOP: 500 case SACTIVE: 501 /* Pick the most live LWP */ 502 onproc = running = sleeping = stopped = suspended = -1; 503 for (i = 0; i < nlwps; i++) { 504 switch (kl[i].l_stat) { 505 case LSONPROC: 506 onproc = i; 507 break; 508 case LSRUN: 509 running = i; 510 break; 511 case LSSLEEP: 512 sleeping = i; 513 break; 514 case LSSTOP: 515 stopped = i; 516 break; 517 case LSSUSPENDED: 518 suspended = i; 519 break; 520 } 521 } 522 if (onproc != -1) 523 return &kl[onproc]; 524 if (running != -1) 525 return &kl[running]; 526 if (sleeping != -1) 527 return &kl[sleeping]; 528 if (stopped != -1) 529 return &kl[stopped]; 530 if (suspended != -1) 531 return &kl[suspended]; 532 break; 533 case SZOMB: 534 /* First will do */ 535 return kl; 536 break; 537 } 538 /* Error condition! */ 539 warnx("Inconsistent LWP state for process %d\n", ki->p_pid); 540 return kl; 541 } 542 543 544 static struct kinfo_proc2 * 545 getkinfo_kvm(kvm_t *kdp, int what, int flag, int *nentriesp) 546 { 547 548 return (kvm_getproc2(kdp, what, flag, sizeof(struct kinfo_proc2), 549 nentriesp)); 550 } 551 552 static void 553 scanvars(void) 554 { 555 struct varent *vent; 556 VAR *v; 557 558 SIMPLEQ_FOREACH(vent, &displaylist, next) { 559 v = vent->var; 560 if (v->flag & COMM) { 561 needcomm = 1; 562 break; 563 } 564 } 565 } 566 567 static int 568 pscomp(const void *a, const void *b) 569 { 570 const struct kinfo_proc2 *ka = (const struct kinfo_proc2 *)a; 571 const struct kinfo_proc2 *kb = (const struct kinfo_proc2 *)b; 572 573 int i; 574 int64_t i64; 575 VAR *v; 576 struct varent *ve; 577 const sigset_t *sa, *sb; 578 579 #define V_SIZE(k) (k->p_vm_dsize + k->p_vm_ssize + k->p_vm_tsize) 580 #define RDIFF_N(t, n) \ 581 if (((const t *)((const char *)ka + v->off))[n] > ((const t *)((const char *)kb + v->off))[n]) \ 582 return 1; \ 583 if (((const t *)((const char *)ka + v->off))[n] < ((const t *)((const char *)kb + v->off))[n]) \ 584 return -1; 585 586 #define RDIFF(type) RDIFF_N(type, 0); continue 587 588 SIMPLEQ_FOREACH(ve, &sortlist, next) { 589 v = ve->var; 590 if (v->flag & LWP) 591 /* LWP structure not available (yet) */ 592 continue; 593 /* Sort on pvar() fields, + a few others */ 594 switch (v->type) { 595 case CHAR: 596 RDIFF(char); 597 case UCHAR: 598 RDIFF(u_char); 599 case SHORT: 600 RDIFF(short); 601 case USHORT: 602 RDIFF(ushort); 603 case INT: 604 RDIFF(int); 605 case UINT: 606 RDIFF(uint); 607 case LONG: 608 RDIFF(long); 609 case ULONG: 610 RDIFF(ulong); 611 case INT32: 612 RDIFF(int32_t); 613 case UINT32: 614 RDIFF(uint32_t); 615 case SIGLIST: 616 sa = (const void *)((const char *)a + v->off); 617 sb = (const void *)((const char *)b + v->off); 618 i = 0; 619 do { 620 if (sa->__bits[i] > sb->__bits[i]) 621 return 1; 622 if (sa->__bits[i] < sb->__bits[i]) 623 return -1; 624 i++; 625 } while (i < sizeof sa->__bits / sizeof sa->__bits[0]); 626 continue; 627 case INT64: 628 RDIFF(int64_t); 629 case KPTR: 630 case KPTR24: 631 case UINT64: 632 RDIFF(uint64_t); 633 case TIMEVAL: 634 /* compare xxx_sec then xxx_usec */ 635 RDIFF_N(uint32_t, 0); 636 RDIFF_N(uint32_t, 1); 637 continue; 638 case CPUTIME: 639 i64 = ka->p_rtime_sec * 1000000 + ka->p_rtime_usec; 640 i64 -= kb->p_rtime_sec * 1000000 + kb->p_rtime_usec; 641 if (sumrusage) { 642 i64 += ka->p_uctime_sec * 1000000 643 + ka->p_uctime_usec; 644 i64 -= kb->p_uctime_sec * 1000000 645 + kb->p_uctime_usec; 646 } 647 if (i64 != 0) 648 return i64 > 0 ? 1 : -1; 649 continue; 650 case PCPU: 651 i = getpcpu(kb) - getpcpu(ka); 652 if (i != 0) 653 return i; 654 continue; 655 case VSIZE: 656 i = V_SIZE(kb) - V_SIZE(ka); 657 if (i != 0) 658 return i; 659 continue; 660 661 default: 662 /* Ignore everything else */ 663 break; 664 } 665 } 666 return 0; 667 668 #undef VSIZE 669 } 670 671 /* 672 * ICK (all for getopt), would rather hide the ugliness 673 * here than taint the main code. 674 * 675 * ps foo -> ps -foo 676 * ps 34 -> ps -p34 677 * 678 * The old convention that 't' with no trailing tty arg means the user's 679 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 680 * feature is available with the option 'T', which takes no argument. 681 */ 682 static char * 683 kludge_oldps_options(char *s) 684 { 685 size_t len; 686 char *newopts, *ns, *cp; 687 688 len = strlen(s); 689 if ((newopts = ns = malloc(len + 3)) == NULL) 690 err(1, NULL); 691 /* 692 * options begin with '-' 693 */ 694 if (*s != '-') 695 *ns++ = '-'; /* add option flag */ 696 /* 697 * gaze to end of argv[1] 698 */ 699 cp = s + len - 1; 700 /* 701 * if the last letter is a 't' flag and there are no other option 702 * characters that take arguments (eg U, p, o) in the option 703 * string and the option string doesn't start with a '-' then 704 * convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 705 */ 706 if (*cp == 't' && *s != '-' && strpbrk(s, ARGOPTS) == NULL) 707 *cp = 'T'; 708 else { 709 /* 710 * otherwise check for trailing number, which *may* be a 711 * pid. 712 */ 713 while (cp >= s && isdigit((unsigned char)*cp)) 714 --cp; 715 } 716 cp++; 717 memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */ 718 ns += cp - s; 719 /* 720 * if there's a trailing number, and not a preceding 'p' (pid) or 721 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 722 */ 723 if (isdigit((unsigned char)*cp) && 724 (cp == s || (cp[-1] != 'U' && cp[-1] != 't' && cp[-1] != 'p' && 725 cp[-1] != '/' && (cp - 1 == s || cp[-2] != 't')))) 726 *ns++ = 'p'; 727 /* and append the number */ 728 (void)strcpy(ns, cp); /* XXX strcpy is safe here */ 729 730 return (newopts); 731 } 732 733 static int 734 parsenum(const char *str, const char *msg) 735 { 736 char *ep; 737 unsigned long ul; 738 739 ul = strtoul(str, &ep, 0); 740 741 if (*str == '\0' || *ep != '\0') 742 errx(1, "Invalid %s: `%s'", msg, str); 743 744 if (ul > INT_MAX) 745 errx(1, "Out of range %s: `%s'", msg, str); 746 747 return (int)ul; 748 } 749 750 static void 751 usage(void) 752 { 753 754 (void)fprintf(stderr, 755 "usage:\t%s\n\t %s\n\t%s\n", 756 "ps [-acCehjlmrsSTuvwx] [-k key] [-O|o fmt] [-p pid] [-t tty]", 757 "[-M core] [-N system] [-W swap] [-U username]", 758 "ps [-L]"); 759 exit(1); 760 /* NOTREACHED */ 761 } 762