ps.c revision 127555
11573Srgrimes/*- 21573Srgrimes * Copyright (c) 1990, 1993, 1994 31573Srgrimes * The Regents of the University of California. All rights reserved. 41573Srgrimes * 51573Srgrimes * Redistribution and use in source and binary forms, with or without 61573Srgrimes * modification, are permitted provided that the following conditions 71573Srgrimes * are met: 81573Srgrimes * 1. Redistributions of source code must retain the above copyright 91573Srgrimes * notice, this list of conditions and the following disclaimer. 101573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111573Srgrimes * notice, this list of conditions and the following disclaimer in the 121573Srgrimes * documentation and/or other materials provided with the distribution. 131573Srgrimes * 3. All advertising materials mentioning features or use of this software 141573Srgrimes * must display the following acknowledgement: 151573Srgrimes * This product includes software developed by the University of 161573Srgrimes * California, Berkeley and its contributors. 171573Srgrimes * 4. Neither the name of the University nor the names of its contributors 181573Srgrimes * may be used to endorse or promote products derived from this software 191573Srgrimes * without specific prior written permission. 201573Srgrimes * 211573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2950476Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31250888Sed * SUCH DAMAGE. 321573Srgrimes * ------+---------+---------+-------- + --------+---------+---------+---------* 331573Srgrimes * Copyright (c) 2004 - Garance Alistair Drosehn <gad@FreeBSD.org>. 341573Srgrimes * All rights reserved. 351573Srgrimes * 361573Srgrimes * Significant modifications made to bring `ps' options somewhat closer 3759460Sphantom * to the standard for `ps' as described in SingleUnixSpec-v3. 3859460Sphantom * ------+---------+---------+-------- + --------+---------+---------+---------* 391573Srgrimes */ 4083722Sru 411573Srgrimes#ifndef lint 42115641Sjmallettstatic const char copyright[] = 431573Srgrimes"@(#) Copyright (c) 1990, 1993, 1994\n\ 441573Srgrimes The Regents of the University of California. All rights reserved.\n"; 451573Srgrimes#endif /* not lint */ 461573Srgrimes 471573Srgrimes#if 0 4823555Smpp#ifndef lint 49103726Swollmanstatic char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94"; 50103726Swollman#endif /* not lint */ 51103726Swollman#endif 52103726Swollman 53103726Swollman#include <sys/cdefs.h> 54103726Swollman__FBSDID("$FreeBSD: head/bin/ps/ps.c 127555 2004-03-29 03:03:28Z gad $"); 551573Srgrimes 561573Srgrimes#include <sys/param.h> 571573Srgrimes#include <sys/proc.h> 581573Srgrimes#include <sys/user.h> 5968945Sru#include <sys/stat.h> 601573Srgrimes#include <sys/ioctl.h> 61107387Sru#include <sys/sysctl.h> 621573Srgrimes 631573Srgrimes#include <ctype.h> 641573Srgrimes#include <err.h> 6552671Sphantom#include <errno.h> 6652671Sphantom#include <fcntl.h> 6752671Sphantom#include <grp.h> 681573Srgrimes#include <kvm.h> 6968945Sru#include <limits.h> 701573Srgrimes#include <locale.h> 711573Srgrimes#include <paths.h> 721573Srgrimes#include <pwd.h> 731573Srgrimes#include <stdint.h> 741573Srgrimes#include <stdio.h> 751573Srgrimes#include <stdlib.h> 761573Srgrimes#include <string.h> 771573Srgrimes#include <unistd.h> 781573Srgrimes 791573Srgrimes#include "ps.h" 801573Srgrimes 811573Srgrimes#define W_SEP " \t" /* "Whitespace" list separators */ 821573Srgrimes#define T_SEP "," /* "Terminate-element" list separators */ 8352671Sphantom 8452671Sphantom#ifdef LAZY_PS 8552671Sphantom#define DEF_UREAD 0 861573Srgrimes#define OPT_LAZY_f "f" 871573Srgrimes#else 881573Srgrimes#define DEF_UREAD 1 /* Always do the more-expensive read. */ 891573Srgrimes#define OPT_LAZY_f /* I.e., the `-f' option is not added. */ 90119893Sru#endif 911573Srgrimes 92108087Sruint cflag; /* -c */ 931573Srgrimesint eval; /* Exit value */ 941573Srgrimestime_t now; /* Current time(3) value */ 95108087Sruint rawcpu; /* -C */ 961573Srgrimesint sumrusage; /* -S */ 971573Srgrimesint termwidth; /* Width of the screen (0 == infinity). */ 981573Srgrimesint totwidth; /* Calculated-width of requested variables. */ 99108087Sru 1001573Srgrimesstruct varent *vhead; 1011573Srgrimes 10252671Sphantomstatic int forceuread = DEF_UREAD; /* Do extra work to get u-area. */ 10352671Sphantomstatic kvm_t *kd; 10452671Sphantomstatic KINFO *kinfo; 105108087Srustatic int needcomm; /* -o "command" */ 1061573Srgrimesstatic int needenv; /* -e */ 1071573Srgrimesstatic int needuser; /* -o "user" */ 108103726Swollmanstatic int optfatal; /* Fatal error parsing some list-option. */ 1091573Srgrimes 110108087Srustatic enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 111103726Swollman 112103726Swollmanstruct listinfo; 113103726Swollmantypedef int addelem_rtn(struct listinfo *_inf, const char *_elem); 114103726Swollman 115103726Swollmanstruct listinfo { 116103726Swollman int count; 117103726Swollman int maxcount; 118103726Swollman int elemsize; 119103726Swollman addelem_rtn *addelem; 120108087Sru const char *lname; 121103726Swollman union { 122103726Swollman gid_t *gids; 123103726Swollman pid_t *pids; 124103726Swollman dev_t *ttys; 125103726Swollman uid_t *uids; 126103726Swollman void *ptr; 127103726Swollman }; 128103726Swollman}; 129108087Sru 1301573Srgrimesstatic int addelem_gid(struct listinfo *, const char *); 1311573Srgrimesstatic int addelem_pid(struct listinfo *, const char *); 1321573Srgrimesstatic int addelem_tty(struct listinfo *, const char *); 1331573Srgrimesstatic int addelem_uid(struct listinfo *, const char *); 134175688Syarstatic void add_list(struct listinfo *, const char *); 1351573Srgrimesstatic void dynsizevars(KINFO *); 1361573Srgrimesstatic void *expand_list(struct listinfo *); 137175688Syarstatic const char *fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int), 1381573Srgrimes KINFO *, char *, int); 139175688Syarstatic void free_list(struct listinfo *); 140175688Syarstatic void init_list(struct listinfo *, addelem_rtn, int, const char *); 1411573Srgrimesstatic char *kludge_oldps_options(char *); 142175688Syarstatic int pscomp(const void *, const void *); 1431573Srgrimesstatic void saveuser(KINFO *); 1441573Srgrimesstatic void scanvars(void); 1451573Srgrimesstatic void sizevars(void); 1461573Srgrimesstatic void usage(void); 1471573Srgrimes 1481573Srgrimesstatic char dfmt[] = "pid,tt,state,time,command"; 1491573Srgrimesstatic char jfmt[] = "user,pid,ppid,pgid,jobc,state,tt,time,command"; 1501573Srgrimesstatic char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state," 1511573Srgrimes "tt,time,command"; 1521573Srgrimesstatic char o1[] = "pid"; 1531573Srgrimesstatic char o2[] = "tt,state,time,command"; 15414103Sjdpstatic char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command"; 155108087Srustatic char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz," 1561573Srgrimes "%cpu,%mem,command"; 1571573Srgrimesstatic char Zfmt[] = "label"; 1581573Srgrimes 1591573Srgrimes#define PS_ARGS "AaCc" OPT_LAZY_f "G:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ" 1601573Srgrimes 1611573Srgrimesint 1621573Srgrimesmain(int argc, char *argv[]) 1631573Srgrimes{ 1641573Srgrimes struct listinfo gidlist, pgrplist, pidlist; 1651573Srgrimes struct listinfo ruidlist, sesslist, ttylist, uidlist; 1661573Srgrimes struct kinfo_proc *kp; 1671573Srgrimes struct varent *vent; 1681573Srgrimes struct winsize ws; 1691573Srgrimes const char *cp, *nlistf, *memf; 1701573Srgrimes char *cols; 171108087Sru int all, ch, dropgid, elem, flag, _fmt, i, lineno; 1721573Srgrimes int nentries, nocludge, nkept, nselectors; 1731573Srgrimes int prtheader, showthreads, wflag, what, xkeep, xkeep_implied; 1741573Srgrimes char errbuf[_POSIX2_LINE_MAX]; 175108087Sru 1761573Srgrimes (void) setlocale(LC_ALL, ""); 1771573Srgrimes time(&now); /* Used by routines in print.c. */ 1781573Srgrimes 1791573Srgrimes if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') 1801573Srgrimes termwidth = atoi(cols); 1811573Srgrimes else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 1821573Srgrimes ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 1831573Srgrimes ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 1841573Srgrimes ws.ws_col == 0) 1851573Srgrimes termwidth = 79; 1861573Srgrimes else 18770481Sru termwidth = ws.ws_col - 1; 1881573Srgrimes 18970481Sru /* 1901573Srgrimes * Don't apply a kludge if the first argument is an option taking an 1911573Srgrimes * argument 1921573Srgrimes */ 1931573Srgrimes if (argc > 1) { 1941573Srgrimes nocludge = 0; 1951573Srgrimes if (argv[1][0] == '-') { 1961573Srgrimes for (cp = PS_ARGS; *cp != '\0'; cp++) { 197108087Sru if (*cp != ':') 1981573Srgrimes continue; 199199843Sjh if (*(cp - 1) == argv[1][1]) { 2001573Srgrimes nocludge = 1; 201199843Sjh break; 2021573Srgrimes } 2031573Srgrimes } 2041573Srgrimes } 2051573Srgrimes if (nocludge == 0) 2061573Srgrimes argv[1] = kludge_oldps_options(argv[1]); 2071573Srgrimes } 2081573Srgrimes 2091573Srgrimes all = dropgid = _fmt = nselectors = optfatal = 0; 2101573Srgrimes prtheader = showthreads = wflag = xkeep_implied = 0; 2111573Srgrimes xkeep = -1; /* Neither -x nor -X. */ 2121573Srgrimes init_list(&gidlist, addelem_gid, sizeof(gid_t), "group"); 2131573Srgrimes init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group"); 2141573Srgrimes init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id"); 2151573Srgrimes init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser"); 2161573Srgrimes init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id"); 2171573Srgrimes init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty"); 2181573Srgrimes init_list(&uidlist, addelem_uid, sizeof(uid_t), "user"); 2191573Srgrimes memf = nlistf = _PATH_DEVNULL; 2201573Srgrimes while ((ch = getopt(argc, argv, PS_ARGS)) != -1) 2211573Srgrimes switch((char)ch) { 2221573Srgrimes case 'A': 2231573Srgrimes /* 2241573Srgrimes * Exactly the same as `-ax'. This has been 2251573Srgrimes * added for compatability with SUSv3, but for 2261573Srgrimes * now it will not be described in the man page. 2271573Srgrimes */ 2281573Srgrimes nselectors++; 2291573Srgrimes all = xkeep = 1; 2301573Srgrimes break; 2311573Srgrimes case 'a': 2321573Srgrimes nselectors++; 2331573Srgrimes all = 1; 2341573Srgrimes break; 2351573Srgrimes case 'C': 2361573Srgrimes rawcpu = 1; 2371573Srgrimes break; 2381573Srgrimes case 'c': 2391573Srgrimes cflag = 1; 2401573Srgrimes break; 2411573Srgrimes case 'e': /* XXX set ufmt */ 2421573Srgrimes needenv = 1; 2431573Srgrimes break; 2441573Srgrimes#ifdef LAZY_PS 2451573Srgrimes case 'f': 2461573Srgrimes if (getuid() == 0 || getgid() == 0) 2471573Srgrimes forceuread = 1; 2481573Srgrimes break; 2491573Srgrimes#endif 2501573Srgrimes case 'G': 2511573Srgrimes add_list(&gidlist, optarg); 2521573Srgrimes xkeep_implied = 1; 253108087Sru nselectors++; 2541573Srgrimes break; 25583722Sru case 'g': 25683722Sru#if 0 25783722Sru /* 258108087Sru * XXX - This SUSv3 behavior is still under debate 2591573Srgrimes * since it conflicts with the (undocumented) 26083722Sru * `-g' option. So we skip it for now. 26183722Sru */ 26283722Sru add_list(&pgrplist, optarg); 2631573Srgrimes xkeep_implied = 1; 2641573Srgrimes nselectors++; 265108087Sru break; 2661573Srgrimes#else 2671573Srgrimes /* The historical BSD-ish (from SunOS) behavior. */ 2681573Srgrimes break; /* no-op */ 2691573Srgrimes#endif 2701573Srgrimes case 'H': 2711573Srgrimes showthreads = KERN_PROC_INC_THREAD; 27267967Sasmodai break; 2731573Srgrimes case 'h': 2741573Srgrimes prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 2751573Srgrimes break; 2761573Srgrimes case 'j': 2771573Srgrimes parsefmt(jfmt, 0); 2781573Srgrimes _fmt = 1; 2791573Srgrimes jfmt[0] = '\0'; 2801573Srgrimes break; 2811573Srgrimes case 'L': 2821573Srgrimes showkey(); 2831573Srgrimes exit(0); 2841573Srgrimes case 'l': 2851573Srgrimes parsefmt(lfmt, 0); 2861573Srgrimes _fmt = 1; 2871573Srgrimes lfmt[0] = '\0'; 28868945Sru break; 2891573Srgrimes case 'M': 2901573Srgrimes memf = optarg; 2911573Srgrimes dropgid = 1; 2921573Srgrimes break; 2931573Srgrimes case 'm': 29468945Sru sortby = SORTMEM; 2951573Srgrimes break; 2961573Srgrimes case 'N': 2971573Srgrimes nlistf = optarg; 2981573Srgrimes dropgid = 1; 2991573Srgrimes break; 300108087Sru case 'O': 3011573Srgrimes parsefmt(o1, 1); 302131504Sru parsefmt(optarg, 1); 3031573Srgrimes parsefmt(o2, 1); 3041573Srgrimes o1[0] = o2[0] = '\0'; 3051573Srgrimes _fmt = 1; 3061573Srgrimes break; 307139838Spjd case 'o': 3081573Srgrimes parsefmt(optarg, 1); 3091573Srgrimes _fmt = 1; 3101573Srgrimes break; 3111573Srgrimes case 'p': 3121573Srgrimes add_list(&pidlist, optarg); 3131573Srgrimes /* 3141573Srgrimes * Note: `-p' does not *set* xkeep, but any values 3151573Srgrimes * from pidlist are checked before xkeep is. That 3161573Srgrimes * way they are always matched, even if the user 3171573Srgrimes * specifies `-X'. 3181573Srgrimes */ 3191573Srgrimes nselectors++; 3201573Srgrimes break; 3211573Srgrimes#if 0 3221573Srgrimes case 'R': 3231573Srgrimes /* 3241573Srgrimes * XXX - This un-standard option is still under 3251573Srgrimes * debate. This is what SUSv3 defines as 3261573Srgrimes * the `-U' option, and while it would be 3271573Srgrimes * nice to have, it could cause even more 3281573Srgrimes * confusion to implement it as `-R'. 3291573Srgrimes */ 330108087Sru add_list(&ruidlist, optarg); 3311573Srgrimes xkeep_implied = 1; 332108087Sru nselectors++; 3331573Srgrimes break; 3341573Srgrimes#endif 3351573Srgrimes case 'r': 3361573Srgrimes sortby = SORTCPU; 3371573Srgrimes break; 3381573Srgrimes case 'S': 3391573Srgrimes sumrusage = 1; 3401573Srgrimes break; 3411573Srgrimes#if 0 3421573Srgrimes case 's': 3431573Srgrimes /* 3441573Srgrimes * XXX - This non-standard option is still under 3451573Srgrimes * debate. This *is* supported on Solaris, 3461573Srgrimes * Linux, and IRIX, but conflicts with `-s' 3471573Srgrimes * on NetBSD and maybe some older BSD's. 3481573Srgrimes */ 3491573Srgrimes add_list(&sesslist, optarg); 35033926Sjraynard xkeep_implied = 1; 3511573Srgrimes nselectors++; 3521573Srgrimes break; 3531573Srgrimes#endif 3541573Srgrimes case 'T': 355108087Sru if ((optarg = ttyname(STDIN_FILENO)) == NULL) 3561573Srgrimes errx(1, "stdin: not a terminal"); 3571573Srgrimes /* FALLTHROUGH */ 358108087Sru case 't': 3591573Srgrimes add_list(&ttylist, optarg); 3601573Srgrimes xkeep_implied = 1; 3611573Srgrimes nselectors++; 3621573Srgrimes break; 3631573Srgrimes case 'U': 3641573Srgrimes /* This is what SUSv3 defines as the `-u' option. */ 3651573Srgrimes add_list(&uidlist, optarg); 3661573Srgrimes xkeep_implied = 1; 3671573Srgrimes nselectors++; 36871895Sru break; 369139838Spjd case 'u': 370139838Spjd parsefmt(ufmt, 0); 371139838Spjd sortby = SORTCPU; 372139838Spjd _fmt = 1; 373139838Spjd ufmt[0] = '\0'; 374139838Spjd break; 375139838Spjd case 'v': 3761573Srgrimes parsefmt(vfmt, 0); 3771573Srgrimes sortby = SORTMEM; 3781573Srgrimes _fmt = 1; 3791573Srgrimes vfmt[0] = '\0'; 3801573Srgrimes break; 3811573Srgrimes case 'w': 3821573Srgrimes if (wflag) 3831573Srgrimes termwidth = UNLIMITED; 3841573Srgrimes else if (termwidth < 131) 3851573Srgrimes termwidth = 131; 3861573Srgrimes wflag++; 3871573Srgrimes break; 3881573Srgrimes case 'X': 3891573Srgrimes /* 3901573Srgrimes * Note that `-X' and `-x' are not standard "selector" 3911573Srgrimes * options. For most selector-options, we check *all* 3921573Srgrimes * processes to see if any are matched by the given 3931573Srgrimes * value(s). After we have a set of all the matched 3941573Srgrimes * processes, then `-X' and `-x' govern whether we 3951573Srgrimes * modify that *matched* set for processes which do 3961573Srgrimes * not have a controlling terminal. `-X' causes 3971573Srgrimes * those processes to be deleted from the matched 3981573Srgrimes * set, while `-x' causes them to be kept. 3991573Srgrimes */ 4001573Srgrimes xkeep = 0; 4011573Srgrimes break; 40268945Sru case 'x': 4031573Srgrimes xkeep = 1; 404108087Sru break; 4051573Srgrimes case 'Z': 4061573Srgrimes parsefmt(Zfmt, 0); 4071573Srgrimes Zfmt[0] = '\0'; 408108087Sru break; 4091573Srgrimes case '?': 4101573Srgrimes default: 4111573Srgrimes usage(); 4121573Srgrimes } 4131573Srgrimes argc -= optind; 4141573Srgrimes argv += optind; 4151573Srgrimes if (optfatal) 4161573Srgrimes exit(1); /* Error messages already printed. */ 4171573Srgrimes if (xkeep < 0) /* Neither -X nor -x was specified. */ 4181573Srgrimes xkeep = xkeep_implied; 4191573Srgrimes 420233132Sjilles#define BACKWARD_COMPATIBILITY 421233132Sjilles#ifdef BACKWARD_COMPATIBILITY 422233132Sjilles if (*argv) { 423233132Sjilles nlistf = *argv; 42468945Sru if (*++argv) 4251573Srgrimes memf = *argv; 4261573Srgrimes } 4271573Srgrimes#endif 4281573Srgrimes /* 4291573Srgrimes * Discard setgid privileges if not the running kernel so that bad 430233132Sjilles * guys can't print interesting stuff from kernel memory. 43168945Sru */ 4321573Srgrimes if (dropgid) { 4331573Srgrimes setgid(getgid()); 4341573Srgrimes setuid(getuid()); 4351573Srgrimes } 4361573Srgrimes 4371573Srgrimes kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 4381573Srgrimes if (kd == 0) 4391573Srgrimes errx(1, "%s", errbuf); 4401573Srgrimes 441108087Sru if (!_fmt) 4421573Srgrimes parsefmt(dfmt, 0); 4431573Srgrimes 4441573Srgrimes if (nselectors == 0) { 4451573Srgrimes uidlist.ptr = malloc(sizeof(uid_t)); 4461573Srgrimes if (uidlist.ptr == NULL) 44768945Sru errx(1, "malloc failed"); 4481573Srgrimes nselectors = 1; 4491573Srgrimes uidlist.count = uidlist.maxcount = 1; 4501573Srgrimes *uidlist.uids = getuid(); 4511573Srgrimes } 4521573Srgrimes 4531573Srgrimes /* 4541573Srgrimes * scan requested variables, noting what structures are needed, 4551573Srgrimes * and adjusting header widths as appropriate. 4561573Srgrimes */ 45768945Sru scanvars(); 4581573Srgrimes 459108087Sru /* 4601573Srgrimes * Get process list. If the user requested just one selector- 4611573Srgrimes * option, then kvm_getprocs can be asked to return just those 4621573Srgrimes * processes. Otherwise, have it return all processes, and 463108087Sru * then this routine will search that full list and select the 4641573Srgrimes * processes which match any of the user's selector-options. 4651573Srgrimes */ 4661573Srgrimes what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC; 4671573Srgrimes flag = 0; 4681573Srgrimes if (nselectors == 1) { 4691573Srgrimes /* XXX - Apparently there's no KERN_PROC_GID flag. */ 4701573Srgrimes if (pgrplist.count == 1) { 4711573Srgrimes what = KERN_PROC_PGRP | showthreads; 4721573Srgrimes flag = *pgrplist.pids; 4731573Srgrimes nselectors = 0; 4741573Srgrimes } else if (pidlist.count == 1) { 4751573Srgrimes what = KERN_PROC_PID | showthreads; 4761573Srgrimes flag = *pidlist.pids; 4771573Srgrimes nselectors = 0; 47870481Sru } else if (ruidlist.count == 1) { 4791573Srgrimes what = KERN_PROC_RUID | showthreads; 48070481Sru flag = *ruidlist.uids; 4811573Srgrimes nselectors = 0; 4821573Srgrimes#if 0 48368945Sru /* 4841573Srgrimes * XXX - KERN_PROC_SESSION causes error in kvm_getprocs? 485108087Sru * For now, always do sid-matching in this routine. 4861573Srgrimes */ 4871573Srgrimes } else if (sesslist.count == 1) { 4881573Srgrimes what = KERN_PROC_SESSION | showthreads; 48968945Sru flag = *sesslist.pids; 4901573Srgrimes nselectors = 0; 4911573Srgrimes#endif 4921573Srgrimes } else if (ttylist.count == 1) { 4931573Srgrimes what = KERN_PROC_TTY | showthreads; 4941573Srgrimes flag = *ttylist.ttys; 4951573Srgrimes nselectors = 0; 4961573Srgrimes } else if (uidlist.count == 1) { 4971573Srgrimes what = KERN_PROC_UID | showthreads; 4981573Srgrimes flag = *uidlist.uids; 4991573Srgrimes nselectors = 0; 500108087Sru } else if (all) { 5011573Srgrimes /* No need for this routine to select processes. */ 5021573Srgrimes nselectors = 0; 5031573Srgrimes } 5041573Srgrimes } 5051573Srgrimes 5061573Srgrimes /* 5071573Srgrimes * select procs 5081573Srgrimes */ 5091573Srgrimes nentries = -1; 5101573Srgrimes kp = kvm_getprocs(kd, what, flag, &nentries); 511108087Sru if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0)) 5121573Srgrimes errx(1, "%s", kvm_geterr(kd)); 5131573Srgrimes nkept = 0; 5141573Srgrimes if (nentries > 0) { 51567967Sasmodai if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL) 5161573Srgrimes errx(1, "malloc failed"); 5171573Srgrimes for (i = nentries; --i >= 0; ++kp) { 5181573Srgrimes /* 5191573Srgrimes * If the user specified multiple selection-criteria, 5201573Srgrimes * then keep any process matched by the inclusive OR 5211573Srgrimes * of all the selection-criteria given. 5221573Srgrimes */ 5231573Srgrimes if (pidlist.count > 0) { 5241573Srgrimes for (elem = 0; elem < pidlist.count; elem++) 5251573Srgrimes if (kp->ki_pid == pidlist.pids[elem]) 5261573Srgrimes goto keepit; 5271573Srgrimes } 5281573Srgrimes /* 5291573Srgrimes * Note that we had to process pidlist before 5301573Srgrimes * filtering out processes which do not have 5311573Srgrimes * a controlling terminal. 5321573Srgrimes */ 5331573Srgrimes if (xkeep == 0) { 5341573Srgrimes if ((kp->ki_tdev == NODEV || 5351573Srgrimes (kp->ki_flag & P_CONTROLT) == 0)) 536108087Sru continue; 5371573Srgrimes } 5381573Srgrimes if (nselectors == 0) 5391573Srgrimes goto keepit; 5401573Srgrimes if (gidlist.count > 0) { 5411573Srgrimes for (elem = 0; elem < gidlist.count; elem++) 5421573Srgrimes if (kp->ki_rgid == gidlist.gids[elem]) 5431573Srgrimes goto keepit; 5441573Srgrimes } 5451573Srgrimes if (pgrplist.count > 0) { 5461573Srgrimes for (elem = 0; elem < pgrplist.count; elem++) 5471573Srgrimes if (kp->ki_pgid == pgrplist.pids[elem]) 5481573Srgrimes goto keepit; 5491573Srgrimes } 5501573Srgrimes if (ruidlist.count > 0) { 5511573Srgrimes for (elem = 0; elem < ruidlist.count; elem++) 5521573Srgrimes if (kp->ki_ruid == ruidlist.uids[elem]) 5531573Srgrimes goto keepit; 5541573Srgrimes } 5551573Srgrimes if (sesslist.count > 0) { 5561573Srgrimes for (elem = 0; elem < sesslist.count; elem++) 5571573Srgrimes if (kp->ki_sid == sesslist.pids[elem]) 5581573Srgrimes goto keepit; 5591573Srgrimes } 5601573Srgrimes if (ttylist.count > 0) { 561108087Sru for (elem = 0; elem < ttylist.count; elem++) 5621573Srgrimes if (kp->ki_tdev == ttylist.ttys[elem]) 5631573Srgrimes goto keepit; 5641573Srgrimes } 5651573Srgrimes if (uidlist.count > 0) { 5661573Srgrimes for (elem = 0; elem < uidlist.count; elem++) 5671573Srgrimes if (kp->ki_uid == uidlist.uids[elem]) 568108087Sru goto keepit; 5691573Srgrimes } 5701573Srgrimes /* 5711573Srgrimes * This process did not match any of the user's 5721573Srgrimes * selector-options, so skip the process. 5731573Srgrimes */ 5741573Srgrimes continue; 5751573Srgrimes 5761573Srgrimes keepit: 5771573Srgrimes kinfo[nkept].ki_p = kp; 5781573Srgrimes if (needuser) 579108087Sru saveuser(&kinfo[nkept]); 5801573Srgrimes dynsizevars(&kinfo[nkept]); 5811573Srgrimes nkept++; 5821573Srgrimes } 5831573Srgrimes } 5841573Srgrimes 5851573Srgrimes sizevars(); 5861573Srgrimes 587108087Sru /* 5881573Srgrimes * print header 5891573Srgrimes */ 590108087Sru printheader(); 5911573Srgrimes if (nkept == 0) 5921573Srgrimes exit(1); 5931573Srgrimes 5941573Srgrimes /* 5951573Srgrimes * sort proc list 596108087Sru */ 5971573Srgrimes qsort(kinfo, nkept, sizeof(KINFO), pscomp); 5981573Srgrimes /* 5991573Srgrimes * For each process, call each variable output function. 6001573Srgrimes */ 6011573Srgrimes for (i = lineno = 0; i < nkept; i++) { 6021573Srgrimes for (vent = vhead; vent; vent = vent->next) { 6031573Srgrimes (vent->var->oproc)(&kinfo[i], vent); 6041573Srgrimes if (vent->next != NULL) 6051573Srgrimes (void)putchar(' '); 6061573Srgrimes } 6071573Srgrimes (void)putchar('\n'); 608131504Sru if (prtheader && lineno++ == prtheader - 4) { 6091573Srgrimes (void)putchar('\n'); 6101573Srgrimes printheader(); 611108087Sru lineno = 0; 6121573Srgrimes } 6131573Srgrimes } 6141573Srgrimes free_list(&gidlist); 6151573Srgrimes free_list(&pidlist); 6161573Srgrimes free_list(&pgrplist); 6171573Srgrimes free_list(&ruidlist); 6181573Srgrimes free_list(&sesslist); 6191573Srgrimes free_list(&ttylist); 6201573Srgrimes free_list(&uidlist); 6211573Srgrimes 6221573Srgrimes exit(eval); 6231573Srgrimes} 6241573Srgrimes 6251573Srgrimesstatic int 6261573Srgrimesaddelem_gid(struct listinfo *inf, const char *elem) 6271573Srgrimes{ 6281573Srgrimes struct group *grp; 6291573Srgrimes const char *nameorID; 6301573Srgrimes char *endp; 631108087Sru intmax_t ltemp; 6321573Srgrimes 6331573Srgrimes if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) { 6341573Srgrimes if (*elem == '\0') 6351573Srgrimes warnx("Invalid (zero-length) %s name", inf->lname); 6361573Srgrimes else 6371573Srgrimes warnx("%s name too long: %s", inf->lname, elem); 6381573Srgrimes optfatal = 1; 6391573Srgrimes return (0); /* Do not add this value. */ 6401573Srgrimes } 6411573Srgrimes 6421573Srgrimes /* 6431573Srgrimes * SUSv3 states that `ps -G grouplist' should match "real-group 6441573Srgrimes * ID numbers", and does not mention group-names. I do want to 6451573Srgrimes * also support group-names, so this tries for a group-id first, 6461573Srgrimes * and only tries for a name if that doesn't work. This is the 6471573Srgrimes * opposite order of what is done in addelem_uid(), but in 6481573Srgrimes * practice the order would only matter for group-names which 6491573Srgrimes * are all-numeric. 6501573Srgrimes */ 6511573Srgrimes grp = NULL; 6521573Srgrimes nameorID = "named"; 6531573Srgrimes errno = 0; 6541573Srgrimes ltemp = strtol(elem, &endp, 10); 6551573Srgrimes if (errno == 0 && *endp == '\0' && ltemp >= 0 && ltemp <= GID_MAX) { 6561573Srgrimes nameorID = "name or ID matches"; 6571573Srgrimes grp = getgrgid((gid_t)ltemp); 6581573Srgrimes } 6591573Srgrimes if (grp == NULL) 6601573Srgrimes grp = getgrnam(elem); 6611573Srgrimes if (grp == NULL) { 6621573Srgrimes warnx("No %s %s '%s'", inf->lname, nameorID, elem); 6631573Srgrimes optfatal = 1; 6641573Srgrimes return (0); /* Do not add this value. */ 6651573Srgrimes } 6661573Srgrimes 6671573Srgrimes if (inf->count >= inf->maxcount) 6681573Srgrimes expand_list(inf); 6691573Srgrimes inf->gids[(inf->count)++] = grp->gr_gid; 6701573Srgrimes return (1); 6711573Srgrimes} 6721573Srgrimes 6731573Srgrimes#define BSD_PID_MAX 99999 /* Copy of PID_MAX from sys/proc.h. */ 6741573Srgrimesstatic int 6751573Srgrimesaddelem_pid(struct listinfo *inf, const char *elem) 6761573Srgrimes{ 6771573Srgrimes char *endp; 6781573Srgrimes long tempid; 6791573Srgrimes 6801573Srgrimes if (*elem == '\0') 6811573Srgrimes tempid = 0L; 6821573Srgrimes else { 6831573Srgrimes errno = 0; 6841573Srgrimes tempid = strtol(elem, &endp, 10); 6851573Srgrimes if (*endp != '\0' || tempid < 0 || elem == endp) { 6861573Srgrimes warnx("Invalid %s: %s", inf->lname, elem); 6871573Srgrimes errno = ERANGE; 6881573Srgrimes } else if (errno != 0 || tempid > BSD_PID_MAX) { 6891573Srgrimes warnx("%s too large: %s", inf->lname, elem); 6901573Srgrimes errno = ERANGE; 6911573Srgrimes } 6921573Srgrimes if (errno == ERANGE) { 6931573Srgrimes optfatal = 1; 6941573Srgrimes return (0); /* Do not add this value. */ 6951573Srgrimes } 6961573Srgrimes } 6971573Srgrimes 6981573Srgrimes if (inf->count >= inf->maxcount) 6991573Srgrimes expand_list(inf); 7001573Srgrimes inf->pids[(inf->count)++] = tempid; 7011573Srgrimes return (1); 7021573Srgrimes} 7031573Srgrimes#undef BSD_PID_MAX 7041573Srgrimes 7051573Srgrimesstatic int 7061573Srgrimesaddelem_tty(struct listinfo *inf, const char *elem) 7071573Srgrimes{ 7081573Srgrimes const char *ttypath; 7091573Srgrimes struct stat sb; 7101573Srgrimes char pathbuf[PATH_MAX]; 7111573Srgrimes 7121573Srgrimes if (strcmp(elem, "co") == 0) 7131573Srgrimes ttypath = strdup(_PATH_CONSOLE); 7141573Srgrimes else if (*elem == '/') 7151573Srgrimes ttypath = elem; 7161573Srgrimes else { 7171573Srgrimes strlcpy(pathbuf, _PATH_TTY, sizeof(pathbuf)); 7181573Srgrimes strlcat(pathbuf, elem, sizeof(pathbuf)); 7191573Srgrimes ttypath = pathbuf; 7201573Srgrimes } 7211573Srgrimes 7221573Srgrimes if (stat(ttypath, &sb) == -1) { 7231573Srgrimes warn("%s", ttypath); 7241573Srgrimes optfatal = 1; 7251573Srgrimes return (0); /* Do not add this value. */ 7261573Srgrimes } 7271573Srgrimes if (!S_ISCHR(sb.st_mode)) { 7281573Srgrimes warn("%s: Not a terminal", ttypath); 7291573Srgrimes optfatal = 1; 7301573Srgrimes return (0); /* Do not add this value. */ 7311573Srgrimes } 7321573Srgrimes 7331573Srgrimes if (inf->count >= inf->maxcount) 7341573Srgrimes expand_list(inf); 7351573Srgrimes inf->ttys[(inf->count)++] = sb.st_rdev; 7361573Srgrimes return (1); 7371573Srgrimes} 7381573Srgrimes 7391573Srgrimesstatic int 7401573Srgrimesaddelem_uid(struct listinfo *inf, const char *elem) 7411573Srgrimes{ 7421573Srgrimes struct passwd *pwd; 7431573Srgrimes char *endp; 7441573Srgrimes intmax_t ltemp; 7451573Srgrimes 7461573Srgrimes if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) { 7471573Srgrimes if (*elem == '\0') 7481573Srgrimes warnx("Invalid (zero-length) %s name", inf->lname); 7491573Srgrimes else 7501573Srgrimes warnx("%s name too long: %s", inf->lname, elem); 7511573Srgrimes optfatal = 1; 7521573Srgrimes return (0); /* Do not add this value. */ 7531573Srgrimes } 7541573Srgrimes 7551573Srgrimes pwd = getpwnam(elem); 7561573Srgrimes if (pwd == NULL) { 7571573Srgrimes errno = 0; 7581573Srgrimes ltemp = strtol(elem, &endp, 10); 7591573Srgrimes if (errno != 0 || *endp != '\0' || ltemp < 0 || 7601573Srgrimes ltemp > UID_MAX) 7611573Srgrimes warnx("No %s named '%s'", inf->lname, elem); 7621573Srgrimes else { 7631573Srgrimes /* The string is all digits, so it might be a userID. */ 7641573Srgrimes pwd = getpwuid((uid_t)ltemp); 7651573Srgrimes if (pwd == NULL) 7661573Srgrimes warnx("No %s name or ID matches '%s'", 7671573Srgrimes inf->lname, elem); 7681573Srgrimes } 7691573Srgrimes } 7701573Srgrimes if (pwd == NULL) { 7711573Srgrimes /* 7721573Srgrimes * These used to be treated as minor warnings (and the 7731573Srgrimes * option was simply ignored), but now they are fatal 7741573Srgrimes * errors (and the command will be aborted). 7751573Srgrimes */ 7761573Srgrimes optfatal = 1; 7771573Srgrimes return (0); /* Do not add this value. */ 7781573Srgrimes } 779197793Sdelphij 7801573Srgrimes if (inf->count >= inf->maxcount) 7811573Srgrimes expand_list(inf); 7821573Srgrimes inf->uids[(inf->count)++] = pwd->pw_uid; 7831573Srgrimes return (1); 7841573Srgrimes} 785134473Stjr 7861573Srgrimesstatic void 787103726Swollmanadd_list(struct listinfo *inf, const char *argp) 7881573Srgrimes{ 78968945Sru const char *savep; 790103726Swollman char *cp, *endp; 791103726Swollman int toolong; 792103726Swollman char elemcopy[PATH_MAX]; 793103726Swollman 794103726Swollman while (*argp != '\0') { 795103726Swollman while (*argp != '\0' && strchr(W_SEP, *argp) != NULL) 796103726Swollman argp++; 797103726Swollman savep = argp; 798103726Swollman toolong = 0; 799103726Swollman cp = elemcopy; 800103726Swollman if (strchr(T_SEP, *argp) == NULL) { 801103726Swollman endp = elemcopy + sizeof(elemcopy) - 1; 802233130Sjilles while (*argp != '\0' && cp <= endp && 803233648Seadler strchr(W_SEP T_SEP, *argp) == NULL) 804233130Sjilles *cp++ = *argp++; 805233130Sjilles if (cp > endp) 806233130Sjilles toolong = 1; 807233648Seadler } 808233130Sjilles if (!toolong) { 809233648Seadler *cp = '\0'; 810233130Sjilles#ifndef ADD_PS_LISTRESET 811233130Sjilles /* 812 * This is how the standard expects lists to 813 * be handled. 814 */ 815 inf->addelem(inf, elemcopy); 816#else 817 /* 818 * This would add a simple non-standard-but-convienent 819 * feature. 820 * 821 * XXX - The first time I tried to add this check, 822 * it increased the total size of `ps' by 3940 823 * bytes on i386! That's 12% of the entire 824 * program! The `ps.o' file grew by only about 825 * 40 bytes, but the final (stripped) executable 826 * in /bin/ps grew by 12%. I have not had time 827 * to investigate, so skip the feature for now. 828 */ 829 /* 830 * We now have a single element. Add it to the 831 * list, unless the element is ":". In that case, 832 * reset the list so previous entries are ignored. 833 */ 834 if (strcmp(elemcopy, ":") == 0) 835 inf->count = 0; 836 else 837 inf->addelem(inf, elemcopy); 838#endif 839 } else { 840 /* 841 * The string is too long to copy. Find the end 842 * of the string to print out the warning message. 843 */ 844 while (*argp != '\0' && strchr(W_SEP T_SEP, 845 *argp) == NULL) 846 argp++; 847 warnx("Value too long: %.*s", (int)(argp - savep), 848 savep); 849 optfatal = 1; 850 } 851 /* 852 * Skip over any number of trailing whitespace characters, 853 * but only one (at most) trailing element-terminating 854 * character. 855 */ 856 while (*argp != '\0' && strchr(W_SEP, *argp) != NULL) 857 argp++; 858 if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) { 859 argp++; 860 /* Catch case where string ended with a comma. */ 861 if (*argp == '\0') 862 inf->addelem(inf, argp); 863 } 864 } 865} 866 867static void * 868expand_list(struct listinfo *inf) 869{ 870 void *newlist; 871 int newmax; 872 873 newmax = (inf->maxcount + 1) << 1; 874 newlist = realloc(inf->ptr, newmax * inf->elemsize); 875 if (newlist == NULL) { 876 free(inf->ptr); 877 errx(1, "realloc to %d %ss failed", newmax, 878 inf->lname); 879 } 880 inf->maxcount = newmax; 881 inf->ptr = newlist; 882 883 return (newlist); 884} 885 886static void 887free_list(struct listinfo *inf) 888{ 889 890 inf->count = inf->elemsize = inf->maxcount = 0; 891 if (inf->ptr != NULL) 892 free(inf->ptr); 893 inf->addelem = NULL; 894 inf->lname = NULL; 895 inf->ptr = NULL; 896} 897 898static void 899init_list(struct listinfo *inf, addelem_rtn artn, int elemsize, 900 const char *lname) 901{ 902 903 inf->count = inf->maxcount = 0; 904 inf->elemsize = elemsize; 905 inf->addelem = artn; 906 inf->lname = lname; 907 inf->ptr = NULL; 908} 909 910VARENT * 911find_varentry(VAR *v) 912{ 913 struct varent *vent; 914 915 for (vent = vhead; vent; vent = vent->next) { 916 if (strcmp(vent->var->name, v->name) == 0) 917 return vent; 918 } 919 return NULL; 920} 921 922static void 923scanvars(void) 924{ 925 struct varent *vent; 926 VAR *v; 927 928 for (vent = vhead; vent; vent = vent->next) { 929 v = vent->var; 930 if (v->flag & DSIZ) { 931 v->dwidth = v->width; 932 v->width = 0; 933 } 934 if (v->flag & USER) 935 needuser = 1; 936 if (v->flag & COMM) 937 needcomm = 1; 938 } 939} 940 941static void 942dynsizevars(KINFO *ki) 943{ 944 struct varent *vent; 945 VAR *v; 946 int i; 947 948 for (vent = vhead; vent; vent = vent->next) { 949 v = vent->var; 950 if (!(v->flag & DSIZ)) 951 continue; 952 i = (v->sproc)( ki); 953 if (v->width < i) 954 v->width = i; 955 if (v->width > v->dwidth) 956 v->width = v->dwidth; 957 } 958} 959 960static void 961sizevars(void) 962{ 963 struct varent *vent; 964 VAR *v; 965 int i; 966 967 for (vent = vhead; vent; vent = vent->next) { 968 v = vent->var; 969 i = strlen(vent->header); 970 if (v->width < i) 971 v->width = i; 972 totwidth += v->width + 1; /* +1 for space */ 973 } 974 totwidth--; 975} 976 977static const char * 978fmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki, 979 char *comm, int maxlen) 980{ 981 const char *s; 982 983 s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen); 984 return (s); 985} 986 987#define UREADOK(ki) (forceuread || (ki->ki_p->ki_sflag & PS_INMEM)) 988 989static void 990saveuser(KINFO *ki) 991{ 992 993 if (ki->ki_p->ki_sflag & PS_INMEM) { 994 /* 995 * The u-area might be swapped out, and we can't get 996 * at it because we have a crashdump and no swap. 997 * If it's here fill in these fields, otherwise, just 998 * leave them 0. 999 */ 1000 ki->ki_valid = 1; 1001 } else 1002 ki->ki_valid = 0; 1003 /* 1004 * save arguments if needed 1005 */ 1006 if (needcomm && (UREADOK(ki) || (ki->ki_p->ki_args != NULL))) { 1007 ki->ki_args = strdup(fmt(kvm_getargv, ki, ki->ki_p->ki_comm, 1008 MAXCOMLEN)); 1009 } else if (needcomm) { 1010 asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm); 1011 } else { 1012 ki->ki_args = NULL; 1013 } 1014 if (needenv && UREADOK(ki)) { 1015 ki->ki_env = strdup(fmt(kvm_getenvv, ki, (char *)NULL, 0)); 1016 } else if (needenv) { 1017 ki->ki_env = malloc(3); 1018 strcpy(ki->ki_env, "()"); 1019 } else { 1020 ki->ki_env = NULL; 1021 } 1022} 1023 1024static int 1025pscomp(const void *a, const void *b) 1026{ 1027 int i; 1028#define VSIZE(k) ((k)->ki_p->ki_dsize + (k)->ki_p->ki_ssize + \ 1029 (k)->ki_p->ki_tsize) 1030 1031 if (sortby == SORTCPU) 1032 return (getpcpu((const KINFO *)b) - getpcpu((const KINFO *)a)); 1033 if (sortby == SORTMEM) 1034 return (VSIZE((const KINFO *)b) - VSIZE((const KINFO *)a)); 1035 i = (int)((const KINFO *)a)->ki_p->ki_tdev - 1036 (int)((const KINFO *)b)->ki_p->ki_tdev; 1037 if (i == 0) 1038 i = ((const KINFO *)a)->ki_p->ki_pid - 1039 ((const KINFO *)b)->ki_p->ki_pid; 1040 return (i); 1041} 1042 1043/* 1044 * ICK (all for getopt), would rather hide the ugliness 1045 * here than taint the main code. 1046 * 1047 * ps foo -> ps -foo 1048 * ps 34 -> ps -p34 1049 * 1050 * The old convention that 't' with no trailing tty arg means the users 1051 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 1052 * feature is available with the option 'T', which takes no argument. 1053 */ 1054static char * 1055kludge_oldps_options(char *s) 1056{ 1057 int have_fmt; 1058 size_t len; 1059 char *newopts, *ns, *cp; 1060 1061 /* 1062 * If we have an 'o' option, then note it, since we don't want to do 1063 * some types of munging. 1064 */ 1065 have_fmt = index(s, 'o') != NULL; 1066 1067 len = strlen(s); 1068 if ((newopts = ns = malloc(len + 2)) == NULL) 1069 errx(1, "malloc failed"); 1070 /* 1071 * options begin with '-' 1072 */ 1073 if (*s != '-') 1074 *ns++ = '-'; /* add option flag */ 1075 /* 1076 * gaze to end of argv[1] 1077 */ 1078 cp = s + len - 1; 1079 /* 1080 * if last letter is a 't' flag with no argument (in the context 1081 * of the oldps options -- option string NOT starting with a '-' -- 1082 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 1083 * 1084 * However, if a flag accepting a string argument is found in the 1085 * option string, the remainder of the string is the argument to 1086 * that flag; do not modify that argument. 1087 */ 1088 if (strcspn(s, "MNOoU") == len && *cp == 't' && *s != '-') 1089 *cp = 'T'; 1090 else { 1091 /* 1092 * otherwise check for trailing number, which *may* be a 1093 * pid. 1094 */ 1095 while (cp >= s && isdigit(*cp)) 1096 --cp; 1097 } 1098 cp++; 1099 memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */ 1100 ns += cp - s; 1101 /* 1102 * if there's a trailing number, and not a preceding 'p' (pid) or 1103 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 1104 */ 1105 if (isdigit(*cp) && 1106 (cp == s || (cp[-1] != 't' && cp[-1] != 'p')) && 1107 (cp - 1 == s || cp[-2] != 't') && !have_fmt) 1108 *ns++ = 'p'; 1109 (void)strcpy(ns, cp); /* and append the number */ 1110 1111 return (newopts); 1112} 1113 1114static void 1115usage(void) 1116{ 1117#define SINGLE_OPTS "[-aC" OPT_LAZY_f "HhjlmrSTuvwXxZ]" 1118 1119 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1120 "usage: ps " SINGLE_OPTS " [-G gid[,gid]] [-O|o fmt]", 1121 " [-p pid[,pid]] [-t tty[,tty]] [-U user[,user]]", 1122 " [-M core] [-N system]", 1123 " ps [-L]"); 1124 exit(1); 1125} 1126