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