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