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