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