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