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