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