1 /*- 2 * Copyright (c) 1990, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#) Copyright (c) 1990, 1993, 1994 The Regents of the University of California. All rights reserved. 30 * @(#)ps.c 8.4 (Berkeley) 4/2/94 31 * $FreeBSD: src/bin/ps/ps.c,v 1.30.2.6 2002/07/04 08:30:37 sobomax Exp $ 32 */ 33 34 #include <sys/user.h> 35 #include <sys/param.h> 36 #include <sys/time.h> 37 #include <sys/queue.h> 38 #include <sys/resource.h> 39 #include <sys/stat.h> 40 #include <sys/ioctl.h> 41 #include <sys/sysctl.h> 42 #include <sys/mount.h> 43 44 #include <ctype.h> 45 #include <err.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <kvm.h> 49 #include <limits.h> 50 #include <locale.h> 51 #include <nlist.h> 52 #include <paths.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <unistd.h> 57 #include <pwd.h> 58 59 #include "ps.h" 60 61 #define SEP ", \t" /* username separators */ 62 63 #define KHSIZE 2048 64 #define KHMASK (KHSIZE - 1) 65 66 KINFO *KInfo; 67 KINFO **KSort; 68 struct varent *vhead, *vtail; 69 70 int eval; /* exit value */ 71 int cflag; /* -c */ 72 int rawcpu; /* -C */ 73 int sumrusage; /* -S */ 74 int termwidth; /* width of screen (0 == infinity) */ 75 int totwidth; /* calculated width of requested variables */ 76 int numcpus; /* hw.ncpu */ 77 78 static int needuser, needcomm, needenv; 79 #if defined(LAZY_PS) 80 static int forceuread=0; 81 #define PS_ARGS "aCcefgHhjLlM:mN:O:o:p:rRSTt:U:uvwx" 82 #else 83 static int forceuread=1; 84 #define PS_ARGS "aCcegHhjLlM:mN:O:o:p:rRSTt:U:uvwx" 85 #endif 86 87 enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 88 89 static const char *getfmt (char **(*)(kvm_t *, const struct kinfo_proc *, int), 90 KINFO *, char *, int); 91 static char *kludge_oldps_options (char *); 92 static int pscomp (const void *, const void *); 93 static void dochain (KINFO **ksort, int *nentriesp, pid_t pid); 94 static void saveuser (KINFO *); 95 static void scanvars (void); 96 static void dynsizevars (KINFO *); 97 static void sizevars (void); 98 static int check_procfs(void); 99 static void usage (void); 100 static uid_t *getuids(const char *, int *); 101 102 struct timeval btime; 103 104 static char dfmt[] = "pid tt state time command"; 105 static char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; 106 static char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; 107 static char o1[] = "pid"; 108 static char o2[] = "tt state time command"; 109 static char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; 110 static char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command"; 111 112 kvm_t *kd; 113 114 int 115 main(int argc, char **argv) 116 { 117 struct kinfo_proc *kp; 118 struct varent *vent; 119 struct winsize ws; 120 dev_t ttydev; 121 pid_t pid; 122 uid_t *uids; 123 int all, ch, flag, i, fmt, ofmt, lineno, nentries, nocludge, dropgid; 124 int prtheader, wflag, what, xflg, uid, nuids, showtid; 125 int chainflg; 126 char errbuf[_POSIX2_LINE_MAX]; 127 const char *cp, *nlistf, *memf; 128 size_t btime_size = sizeof(struct timeval); 129 size_t numcpus_size = sizeof(numcpus); 130 131 setlocale(LC_ALL, ""); 132 133 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 134 ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 135 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 136 ws.ws_col == 0) 137 termwidth = 79; 138 else 139 termwidth = ws.ws_col - 1; 140 141 /* 142 * Don't apply a kludge if the first argument is an option taking an 143 * argument 144 */ 145 if (argc > 1) { 146 nocludge = 0; 147 if (argv[1][0] == '-') { 148 for (cp = PS_ARGS; *cp != '\0'; cp++) { 149 if (*cp != ':') 150 continue; 151 if (*(cp - 1) == argv[1][1]) { 152 nocludge = 1; 153 break; 154 } 155 } 156 } 157 if (nocludge == 0) 158 argv[1] = kludge_oldps_options(argv[1]); 159 } 160 161 all = fmt = ofmt = prtheader = wflag = xflg = showtid = 0; 162 chainflg = 0; 163 pid = -1; 164 nuids = 0; 165 uids = NULL; 166 ttydev = NODEV; 167 dropgid = 0; 168 memf = nlistf = _PATH_DEVNULL; 169 170 while ((ch = getopt(argc, argv, PS_ARGS)) != -1) { 171 switch((char)ch) { 172 case 'a': 173 all = 1; 174 break; 175 case 'C': 176 rawcpu = 1; 177 break; 178 case 'c': 179 cflag = 1; 180 break; 181 case 'e': /* XXX set ufmt */ 182 needenv = 1; 183 break; 184 case 'g': 185 break; /* no-op */ 186 case 'H': 187 showtid = KERN_PROC_FLAG_LWP; 188 break; 189 case 'h': 190 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 191 break; 192 case 'j': 193 parsefmt(jfmt); 194 fmt = 1; 195 jfmt[0] = '\0'; 196 break; 197 case 'L': 198 showkey(); 199 exit(0); 200 case 'l': 201 parsefmt(lfmt); 202 fmt = 1; 203 lfmt[0] = '\0'; 204 break; 205 case 'M': 206 memf = optarg; 207 dropgid = 1; 208 break; 209 case 'm': 210 sortby = SORTMEM; 211 break; 212 case 'N': 213 nlistf = optarg; 214 dropgid = 1; 215 break; 216 case 'O': 217 parsefmt(o1); 218 parsefmt(optarg); 219 parsefmt(o2); 220 o1[0] = o2[0] = '\0'; 221 fmt = 1; 222 break; 223 case 'o': 224 parsefmt(optarg); 225 fmt = ofmt = 1; 226 break; 227 #if defined(LAZY_PS) 228 case 'f': 229 if (getuid() == 0 || getgid() == 0) 230 forceuread = 1; 231 break; 232 #endif 233 case 'p': 234 pid = atol(optarg); 235 xflg = 1; 236 break; 237 case 'r': 238 sortby = SORTCPU; 239 break; 240 case 'R': 241 chainflg = 1; 242 break; 243 case 'S': 244 sumrusage = 1; 245 break; 246 case 'T': 247 if ((optarg = ttyname(STDIN_FILENO)) == NULL) 248 errx(1, "stdin: not a terminal"); 249 /* FALLTHROUGH */ 250 case 't': { 251 struct stat sb; 252 char pathbuf[PATH_MAX]; 253 const char *ttypath; 254 255 if (strcmp(optarg, "co") == 0) { 256 ttypath = _PATH_CONSOLE; 257 } else if (*optarg >= '0' && *optarg <= '9') { 258 snprintf(pathbuf, sizeof(pathbuf), 259 "%spts/%s", _PATH_DEV, optarg); 260 ttypath = pathbuf; 261 } else if (*optarg != '/') { 262 snprintf(pathbuf, sizeof(pathbuf), 263 "%s%s", _PATH_TTY, optarg); 264 ttypath = pathbuf; 265 } else { 266 ttypath = optarg; 267 } 268 if (stat(ttypath, &sb) == -1) 269 err(1, "%s", ttypath); 270 if (!S_ISCHR(sb.st_mode)) 271 errx(1, "%s: not a terminal", ttypath); 272 ttydev = sb.st_rdev; 273 break; 274 } 275 case 'U': 276 uids = getuids(optarg, &nuids); 277 xflg++; /* XXX: intuitive? */ 278 break; 279 case 'u': 280 parsefmt(ufmt); 281 sortby = SORTCPU; 282 fmt = 1; 283 ufmt[0] = '\0'; 284 break; 285 case 'v': 286 parsefmt(vfmt); 287 sortby = SORTMEM; 288 fmt = 1; 289 vfmt[0] = '\0'; 290 break; 291 case 'w': 292 if (wflag) 293 termwidth = UNLIMITED; 294 else if (termwidth < 131) 295 termwidth = 131; 296 wflag++; 297 break; 298 case 'x': 299 xflg = 1; 300 break; 301 case '?': 302 default: 303 usage(); 304 } 305 } 306 argc -= optind; 307 argv += optind; 308 309 /* 310 * If the user specified ps -e then they want a copy of the process 311 * environment kvm_getenvv(3) attempts to open /proc/<pid>/mem. 312 * Check to make sure that procfs is mounted on /proc, otherwise 313 * print a warning informing the user that output will be incomplete. 314 */ 315 if (needenv == 1 && check_procfs() == 0) 316 warnx("Process environment requires procfs(5)"); 317 318 #define BACKWARD_COMPATIBILITY 319 #ifdef BACKWARD_COMPATIBILITY 320 if (*argv) { 321 nlistf = *argv; 322 if (*++argv) { 323 memf = *argv; 324 } 325 } 326 #endif 327 /* 328 * Discard setgid privileges if not the running kernel so that bad 329 * guys can't print interesting stuff from kernel memory. 330 */ 331 if (dropgid) { 332 setgid(getgid()); 333 setuid(getuid()); 334 } 335 336 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 337 if (kd == NULL) 338 errx(1, "%s", errbuf); 339 340 if (!fmt) 341 parsefmt(dfmt); 342 343 /* 344 * Add TID to output format if requested unless user-specific format 345 * selected. 346 */ 347 if (showtid && !ofmt) 348 insert_tid_in_fmt(); 349 350 /* XXX - should be cleaner */ 351 if (!all && ttydev == NODEV && pid == -1 && !nuids) { 352 if ((uids = malloc(sizeof (*uids))) == NULL) 353 errx(1, "malloc: %s", strerror(errno)); 354 nuids = 1; 355 *uids = getuid(); 356 } 357 358 /* 359 * scan requested variables, noting what structures are needed, 360 * and adjusting header widths as appropriate. 361 */ 362 scanvars(); 363 364 /* 365 * Get boot time 366 */ 367 if (sysctlbyname("kern.boottime", &btime, &btime_size, NULL, 0) < 0) { 368 perror("sysctl: kern.boottime"); 369 exit(EXIT_FAILURE); 370 } 371 372 /* 373 * Get number of cpus 374 */ 375 if (sysctlbyname("hw.ncpu", &numcpus, &numcpus_size, NULL, 0) < 0) 376 numcpus = 1; 377 378 /* 379 * get proc list 380 */ 381 if (nuids == 1) { 382 what = KERN_PROC_UID; 383 flag = *uids; 384 } else if (ttydev != NODEV) { 385 what = KERN_PROC_TTY; 386 flag = ttydev; 387 } else if (pid != -1 && chainflg == 0) { 388 what = KERN_PROC_PID; 389 flag = pid; 390 } else { 391 what = KERN_PROC_ALL; 392 flag = 0; 393 } 394 what |= showtid; 395 396 /* 397 * select procs 398 */ 399 if ((kp = kvm_getprocs(kd, what, flag, &nentries)) == NULL) 400 errx(1, "%s", kvm_geterr(kd)); 401 if ((KInfo = calloc(nentries, sizeof(KINFO))) == NULL) 402 err(1, NULL); 403 if ((KSort = malloc(nentries * sizeof(KINFO *))) == NULL) 404 err(1, NULL); 405 406 for (i = 0; i < nentries; ++i) { 407 KInfo[i].ki_proc = &kp[i]; 408 KInfo[i].ki_indent = -1; 409 KInfo[i].ki_ctailp = &KInfo[i].ki_cbase; 410 if (needuser) 411 saveuser(&KInfo[i]); 412 dynsizevars(&KInfo[i]); 413 KSort[i] = &KInfo[i]; 414 } 415 416 sizevars(); 417 418 /* 419 * print header 420 */ 421 printheader(); 422 if (nentries == 0) 423 exit(1); 424 /* 425 * sort proc list 426 */ 427 qsort(KSort, nentries, sizeof(KINFO *), pscomp); 428 429 /* 430 * rejigger and indent children, parent(s) and children 431 * at each level retain the above sort characteristics. 432 */ 433 if (chainflg) 434 dochain(KSort, &nentries, pid); 435 436 /* 437 * for each proc, call each variable output function. 438 */ 439 for (i = lineno = 0; i < nentries; i++) { 440 if (xflg == 0 && (KI_PROC(KSort[i], tdev) == NODEV || 441 (KI_PROC(KSort[i], flags) & P_CONTROLT ) == 0)) 442 continue; 443 if (nuids > 1) { 444 for (uid = 0; uid < nuids; uid++) 445 if (KI_PROC(KSort[i], uid) == 446 uids[uid]) 447 break; 448 if (uid == nuids) 449 continue; 450 } 451 STAILQ_FOREACH(vent, &var_head, link) { 452 (vent->var->oproc)(KSort[i], vent); 453 if (STAILQ_NEXT(vent, link) != NULL) 454 putchar(' '); 455 } 456 putchar('\n'); 457 if (prtheader && lineno++ == prtheader - 4) { 458 putchar('\n'); 459 printheader(); 460 lineno = 0; 461 } 462 } 463 free(uids); 464 465 exit(eval); 466 } 467 468 uid_t * 469 getuids(const char *arg, int *nuids) 470 { 471 char name[MAXLOGNAME]; 472 struct passwd *pwd; 473 uid_t *uids, *moreuids; 474 size_t l; 475 int alloc; 476 477 478 alloc = 0; 479 *nuids = 0; 480 uids = NULL; 481 for (; (l = strcspn(arg, SEP)) > 0; arg += l + strspn(arg + l, SEP)) { 482 if (l >= sizeof name) { 483 warnx("%.*s: name too long", (int)l, arg); 484 continue; 485 } 486 strncpy(name, arg, l); 487 name[l] = '\0'; 488 if ((pwd = getpwnam(name)) == NULL) { 489 warnx("%s: no such user", name); 490 continue; 491 } 492 if (*nuids >= alloc) { 493 alloc = (alloc + 1) << 1; 494 moreuids = realloc(uids, alloc * sizeof (*uids)); 495 if (moreuids == NULL) { 496 free(uids); 497 errx(1, "realloc: %s", strerror(errno)); 498 } 499 uids = moreuids; 500 } 501 uids[(*nuids)++] = pwd->pw_uid; 502 } 503 endpwent(); 504 505 if (!*nuids) 506 errx(1, "No users specified"); 507 508 return uids; 509 } 510 511 static void 512 scanvars(void) 513 { 514 struct varent *vent; 515 const VAR *v; 516 517 STAILQ_FOREACH(vent, &var_head, link) { 518 v = vent->var; 519 if (v->flag & DSIZ) { 520 vent->dwidth = vent->width; 521 vent->width = 0; 522 } 523 if (v->flag & USER) 524 needuser = 1; 525 if (v->flag & COMM) 526 needcomm = 1; 527 } 528 } 529 530 static void 531 dynsizevars(KINFO *ki) 532 { 533 struct varent *vent; 534 const VAR *v; 535 int i; 536 537 STAILQ_FOREACH(vent, &var_head, link) { 538 v = vent->var; 539 if (!(v->flag & DSIZ)) 540 continue; 541 i = (v->sproc)( ki); 542 if (vent->width < i) 543 vent->width = i; 544 if (vent->width > vent->dwidth) 545 vent->width = vent->dwidth; 546 } 547 } 548 549 static void 550 sizevars(void) 551 { 552 struct varent *vent; 553 int i; 554 555 STAILQ_FOREACH(vent, &var_head, link) { 556 i = strlen(vent->header); 557 if (vent->width < i) 558 vent->width = i; 559 totwidth += vent->width + 1; /* +1 for space */ 560 } 561 totwidth--; 562 } 563 564 static const char * 565 getfmt(char **(*fn) (kvm_t *, const struct kinfo_proc *, int), KINFO *ki, char 566 *comm, int maxlen) 567 { 568 const char *s; 569 570 if ((s = 571 fmt_argv((*fn)(kd, ki->ki_proc, termwidth), comm, maxlen)) == NULL) 572 err(1, NULL); 573 return (s); 574 } 575 576 static void 577 saveuser(KINFO *ki) 578 { 579 /* 580 * save arguments if needed 581 */ 582 if (needcomm) { 583 ki->ki_args = getfmt(kvm_getargv, ki, KI_PROC(ki, comm), 584 MAXCOMLEN); 585 } else { 586 ki->ki_args = NULL; 587 } 588 if (needenv) { 589 ki->ki_env = getfmt(kvm_getenvv, ki, NULL, 0); 590 } else { 591 ki->ki_env = NULL; 592 } 593 } 594 595 static int 596 pscomp(const void *arg_a, const void *arg_b) 597 { 598 const KINFO *a = *(KINFO * const *)arg_a; 599 const KINFO *b = *(KINFO * const *)arg_b; 600 double di; 601 segsz_t si; 602 int i; 603 604 #define VSIZE(k) (KI_PROC(k, vm_dsize) + KI_PROC(k, vm_ssize) + \ 605 KI_PROC(k, vm_tsize)) 606 607 #if 0 608 if (sortby == SORTIAC) 609 return (KI_PROC(a)->p_usdata.bsd4.interactive - KI_PROC(b)->p_usdata.bsd4.interactive); 610 #endif 611 if (sortby == SORTCPU) { 612 di = getpcpu(b) - getpcpu(a); 613 if (di < 0.0) 614 return(-1); 615 if (di > 0.0) 616 return(+1); 617 /* fall through */ 618 } 619 if (sortby == SORTMEM) { 620 si = VSIZE(b) - VSIZE(a); 621 if (si < 0) 622 return(-1); 623 if (si > 0) 624 return(+1); 625 /* fall through */ 626 } 627 i = KI_PROC(a, tdev) - KI_PROC(b, tdev); 628 if (i == 0) 629 i = KI_PROC(a, pid) - KI_PROC(b, pid); 630 return (i); 631 } 632 633 /* 634 * Chain (-R) option. Sub-sorts by parent/child association. 635 */ 636 static int dochain_final(KINFO **kfinal, KINFO *base, int j); 637 638 static void 639 dochain (KINFO **ksort, int *nentriesp, pid_t pid) 640 { 641 int nentries = *nentriesp; 642 int i; 643 int j; 644 int hi; 645 pid_t ppid; 646 KINFO *scan; 647 KINFO **khash; 648 KINFO **kfinal; 649 650 /* 651 * First hash all the pids so we can quickly locate the 652 * parent pids. 653 */ 654 khash = calloc(KHSIZE, sizeof(KINFO *)); 655 kfinal = calloc(nentries, sizeof(KINFO *)); 656 657 for (i = 0; i < nentries; ++i) { 658 hi = KI_PROC(ksort[i], pid) & KHMASK; 659 660 ksort[i]->ki_hnext = khash[hi]; 661 khash[hi] = ksort[i]; 662 } 663 664 /* 665 * Now run through the list and create the parent associations. 666 */ 667 for (i = 0; i < nentries; ++i) { 668 ppid = KI_PROC(ksort[i], ppid); 669 670 if (ppid <= 0 || KI_PROC(ksort[i], pid) <= 0) { 671 scan = NULL; 672 } else { 673 for (scan = khash[ppid & KHMASK]; 674 scan; 675 scan = scan->ki_hnext) { 676 if (ppid == KI_PROC(scan, pid)) 677 break; 678 } 679 } 680 if (scan) { 681 ksort[i]->ki_parent = scan; 682 *scan->ki_ctailp = ksort[i]; 683 scan->ki_ctailp = &ksort[i]->ki_cnext; 684 } 685 } 686 687 /* 688 * Now regenerate the list starting at root entries, then copyback. 689 */ 690 for (i = j = 0; i < nentries; ++i) { 691 if (pid != (pid_t)-1) { 692 if (KI_PROC(ksort[i], pid) == pid) { 693 ksort[i]->ki_indent = 0; 694 j = dochain_final(kfinal, ksort[i], j); 695 } 696 } else if (ksort[i]->ki_parent == NULL) { 697 ksort[i]->ki_indent = 0; 698 j = dochain_final(kfinal, ksort[i], j); 699 } 700 } 701 702 /* 703 * There might be fewer entries if we restricted the chain to a 704 * pid. 705 */ 706 nentries = j; 707 *nentriesp = nentries; 708 709 if (nentries) 710 bcopy(kfinal, ksort, nentries * sizeof(kfinal[0])); 711 free(kfinal); 712 free(khash); 713 } 714 715 static int 716 dochain_final(KINFO **kfinal, KINFO *base, int j) 717 { 718 KINFO *scan; 719 720 kfinal[j++] = base; 721 for (scan = base->ki_cbase; scan; scan = scan->ki_cnext) { 722 /* 723 * Don't let us loop 724 */ 725 if (scan->ki_indent >= 0) 726 continue; 727 scan->ki_indent = base->ki_indent + 1; 728 j = dochain_final(kfinal, scan, j); 729 } 730 return (j); 731 } 732 733 /* 734 * ICK (all for getopt), would rather hide the ugliness 735 * here than taint the main code. 736 * 737 * ps foo -> ps -foo 738 * ps 34 -> ps -p34 739 * 740 * The old convention that 't' with no trailing tty arg means the users 741 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 742 * feature is available with the option 'T', which takes no argument. 743 */ 744 static char * 745 kludge_oldps_options(char *s) 746 { 747 size_t len; 748 char *newopts, *ns, *cp; 749 750 len = strlen(s); 751 if ((newopts = ns = malloc(len + 2)) == NULL) 752 err(1, NULL); 753 /* 754 * options begin with '-' 755 */ 756 if (*s != '-') 757 *ns++ = '-'; /* add option flag */ 758 759 /* 760 * gaze to end of argv[1] 761 */ 762 cp = s + len - 1; 763 764 /* 765 * if last letter is a 't' flag with no argument (in the context 766 * of the oldps options -- option string NOT starting with a '-' -- 767 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 768 * 769 * However, if a flag accepting a string argument is found in the 770 * option string, the remainder of the string is the argument to 771 * that flag; do not modify that argument. 772 */ 773 if (strcspn(s, "MNOoU") == len && *cp == 't' && *s != '-') { 774 *cp = 'T'; 775 } else { 776 /* 777 * otherwise check for trailing number, which *may* be a 778 * pid. 779 */ 780 while (cp >= s && isdigit(*cp)) 781 --cp; 782 } 783 cp++; 784 memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */ 785 ns += cp - s; 786 /* 787 * if there's a trailing number, and not a preceding 'p' (pid) or 788 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 789 */ 790 if (isdigit(*cp) && 791 (cp == s || (cp[-1] != 't' && cp[-1] != 'p')) && 792 (cp - 1 == s || cp[-2] != 't')) { 793 *ns++ = 'p'; 794 } 795 strcpy(ns, cp); /* and append the number */ 796 797 return (newopts); 798 } 799 800 static int 801 check_procfs(void) 802 { 803 struct statfs mnt; 804 805 if (statfs("/proc", &mnt) < 0) 806 return (0); 807 if (strcmp(mnt.f_fstypename, "procfs") != 0) 808 return (0); 809 return (1); 810 } 811 812 static void 813 usage(void) 814 { 815 816 fprintf(stderr, "%s\n%s\n%s\n", 817 "usage: ps [-aCcefhHjlmrSTuvwx] [-M core] [-N system] [-O fmt] [-o fmt] [-p pid]", 818 " [-t tty] [-U username]", 819 " ps [-L]"); 820 exit(1); 821 } 822