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