ps.c revision 127542
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1990, 1993, 1994
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * Redistribution and use in source and binary forms, with or without
61556Srgrimes * modification, are permitted provided that the following conditions
71556Srgrimes * are met:
81556Srgrimes * 1. Redistributions of source code must retain the above copyright
91556Srgrimes *    notice, this list of conditions and the following disclaimer.
101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111556Srgrimes *    notice, this list of conditions and the following disclaimer in the
121556Srgrimes *    documentation and/or other materials provided with the distribution.
131556Srgrimes * 3. All advertising materials mentioning features or use of this software
141556Srgrimes *    must display the following acknowledgement:
151556Srgrimes *	This product includes software developed by the University of
161556Srgrimes *	California, Berkeley and its contributors.
171556Srgrimes * 4. Neither the name of the University nor the names of its contributors
181556Srgrimes *    may be used to endorse or promote products derived from this software
191556Srgrimes *    without specific prior written permission.
201556Srgrimes *
211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311556Srgrimes * SUCH DAMAGE.
32127499Sgad * ------+---------+---------+-------- + --------+---------+---------+---------*
33127499Sgad * Copyright (c) 2004  - Garance Alistair Drosehn <gad@FreeBSD.org>.
34127499Sgad * All rights reserved.
35127499Sgad *
36127499Sgad * Significant modifications made to bring `ps' options somewhat closer
37127499Sgad * to the standard for `ps' as described in SingleUnixSpec-v3.
38127499Sgad * ------+---------+---------+-------- + --------+---------+---------+---------*
391556Srgrimes */
401556Srgrimes
411556Srgrimes#ifndef lint
4290143Smarkmstatic const char copyright[] =
431556Srgrimes"@(#) Copyright (c) 1990, 1993, 1994\n\
441556Srgrimes	The Regents of the University of California.  All rights reserved.\n";
451556Srgrimes#endif /* not lint */
461556Srgrimes
4790143Smarkm#if 0
481556Srgrimes#ifndef lint
4936049Scharnierstatic char sccsid[] = "@(#)ps.c	8.4 (Berkeley) 4/2/94";
5090143Smarkm#endif /* not lint */
5136049Scharnier#endif
52110391Scharnier
5399110Sobrien#include <sys/cdefs.h>
5499110Sobrien__FBSDID("$FreeBSD: head/bin/ps/ps.c 127542 2004-03-29 01:15:27Z gad $");
551556Srgrimes
561556Srgrimes#include <sys/param.h>
573296Sdg#include <sys/user.h>
581556Srgrimes#include <sys/stat.h>
591556Srgrimes#include <sys/ioctl.h>
601556Srgrimes#include <sys/sysctl.h>
611556Srgrimes
621556Srgrimes#include <ctype.h>
631556Srgrimes#include <err.h>
64127149Sgad#include <errno.h>
651556Srgrimes#include <fcntl.h>
66127499Sgad#include <grp.h>
671556Srgrimes#include <kvm.h>
6813514Smpp#include <limits.h>
6973367Sache#include <locale.h>
701556Srgrimes#include <paths.h>
7190143Smarkm#include <pwd.h>
72127499Sgad#include <stdint.h>
731556Srgrimes#include <stdio.h>
741556Srgrimes#include <stdlib.h>
751556Srgrimes#include <string.h>
761556Srgrimes#include <unistd.h>
771556Srgrimes
781556Srgrimes#include "ps.h"
791556Srgrimes
80127499Sgad#define	W_SEP	" \t"		/* "Whitespace" list separators */
81127499Sgad#define	T_SEP	","		/* "Terminate-element" list separators */
8266377Sbrian
83127537Sgad#ifdef LAZY_PS
84127537Sgad#define	DEF_UREAD	0;
85127537Sgad#define	OPT_LAZY_f	"f"
86127537Sgad#else
87127537Sgad#define	DEF_UREAD	1;	/* Always do the more-expensive read. */
88127537Sgad#define	OPT_LAZY_f		/* I.e., the `-f' option is not added. */
89127537Sgad#endif
90127537Sgad
91127537Sgadint	 cflag;			/* -c */
92127537Sgadint	 eval;			/* Exit value */
93127537Sgadtime_t	 now;			/* Current time(3) value */
94127537Sgadint	 rawcpu;		/* -C */
95127537Sgadint	 sumrusage;		/* -S */
96127537Sgadint	 termwidth;		/* Width of the screen (0 == infinity). */
97127537Sgadint	 totwidth;		/* Calculated-width of requested variables. */
98127537Sgad
9990143Smarkmstruct varent *vhead;
1001556Srgrimes
101127537Sgadstatic int	 forceuread = DEF_UREAD; /* Do extra work to get u-area. */
102127537Sgadstatic kvm_t	*kd;
103127537Sgadstatic KINFO	*kinfo;
104127537Sgadstatic int	 needcomm;	/* -o "command" */
105127537Sgadstatic int	 needenv;	/* -e */
106127537Sgadstatic int	 needuser;	/* -o "user" */
107127537Sgadstatic int	 optfatal;	/* Fatal error parsing some list-option. */
1081556Srgrimes
109127537Sgadstatic enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
11097966Sjmallett
111127499Sgadstruct listinfo;
112127537Sgadtypedef	int	addelem_rtn(struct listinfo *_inf, const char *_elem);
113127499Sgad
114127499Sgadstruct listinfo {
115127499Sgad	int		 count;
116127499Sgad	int		 maxcount;
117127499Sgad	int		 elemsize;
118127499Sgad	addelem_rtn	*addelem;
119127499Sgad	const char	*lname;
120127499Sgad	union {
121127499Sgad		gid_t	*gids;
122127499Sgad		pid_t	*pids;
123127499Sgad		dev_t	*ttys;
124127499Sgad		uid_t	*uids;
125127499Sgad		void	*ptr;
126127499Sgad	};
127127499Sgad};
128127499Sgad
129127499Sgadstatic int	 addelem_gid(struct listinfo *, const char *);
130127499Sgadstatic int	 addelem_pid(struct listinfo *, const char *);
131127499Sgadstatic int	 addelem_tty(struct listinfo *, const char *);
132127499Sgadstatic int	 addelem_uid(struct listinfo *, const char *);
133127499Sgadstatic void	 add_list(struct listinfo *, const char *);
134127536Sgadstatic void	 dynsizevars(KINFO *);
135127499Sgadstatic void	*expand_list(struct listinfo *);
136127536Sgadstatic const char *fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int),
137127536Sgad		    KINFO *, char *, int);
138127499Sgadstatic void	 free_list(struct listinfo *);
139127499Sgadstatic void	 init_list(struct listinfo *, addelem_rtn, int, const char *);
140127536Sgadstatic char	*kludge_oldps_options(char *);
141127536Sgadstatic int	 pscomp(const void *, const void *);
142127536Sgadstatic void	 saveuser(KINFO *);
143127536Sgadstatic void	 scanvars(void);
144127536Sgadstatic void	 sizevars(void);
145127536Sgadstatic void	 usage(void);
146127499Sgad
14797875Sjmallettstatic char dfmt[] = "pid,tt,state,time,command";
14897875Sjmallettstatic char jfmt[] = "user,pid,ppid,pgid,jobc,state,tt,time,command";
149127538Sgadstatic char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,"
150127538Sgad			"tt,time,command";
15190143Smarkmstatic char   o1[] = "pid";
15297875Sjmallettstatic char   o2[] = "tt,state,time,command";
15397875Sjmallettstatic char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
154127538Sgadstatic char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,"
155127538Sgad			"%cpu,%mem,command";
156105831Srwatsonstatic char Zfmt[] = "label";
1571556Srgrimes
158127507Sgad#define	PS_ARGS	"AaCc" OPT_LAZY_f "G:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ"
15998494Ssobomax
1601556Srgrimesint
16190110Simpmain(int argc, char *argv[])
1621556Srgrimes{
163127499Sgad	struct listinfo gidlist, pgrplist, pidlist;
164127499Sgad	struct listinfo ruidlist, sesslist, ttylist, uidlist;
1651556Srgrimes	struct kinfo_proc *kp;
1661556Srgrimes	struct varent *vent;
1671556Srgrimes	struct winsize ws;
168127539Sgad	const char *cp, *nlistf, *memf;
169127539Sgad	char *cols;
170127499Sgad	int all, ch, dropgid, elem, flag, _fmt, i, lineno;
171127499Sgad	int nentries, nocludge, nkept, nselectors;
172127499Sgad	int prtheader, showthreads, wflag, what, xkeep, xkeep_implied;
17390143Smarkm	char errbuf[_POSIX2_LINE_MAX];
1741556Srgrimes
17511809Sache	(void) setlocale(LC_ALL, "");
176127542Sgad	time(&now);			/* Used by routines in print.c. */
17711809Sache
17897804Stjr	if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0')
17997804Stjr		termwidth = atoi(cols);
18097804Stjr	else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
1811556Srgrimes	     ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
1821556Srgrimes	     ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) == -1) ||
1831556Srgrimes	     ws.ws_col == 0)
1841556Srgrimes		termwidth = 79;
1851556Srgrimes	else
1861556Srgrimes		termwidth = ws.ws_col - 1;
1871556Srgrimes
18898494Ssobomax	/*
18998494Ssobomax	 * Don't apply a kludge if the first argument is an option taking an
19098494Ssobomax	 * argument
19198494Ssobomax	 */
19298494Ssobomax	if (argc > 1) {
19398494Ssobomax		nocludge = 0;
19498494Ssobomax		if (argv[1][0] == '-') {
19598494Ssobomax			for (cp = PS_ARGS; *cp != '\0'; cp++) {
19698494Ssobomax				if (*cp != ':')
19798494Ssobomax					continue;
19898494Ssobomax				if (*(cp - 1) == argv[1][1]) {
19998494Ssobomax					nocludge = 1;
20098494Ssobomax					break;
20198494Ssobomax				}
20298494Ssobomax			}
20398494Ssobomax		}
20498494Ssobomax		if (nocludge == 0)
20598494Ssobomax			argv[1] = kludge_oldps_options(argv[1]);
20698494Ssobomax	}
2071556Srgrimes
208127542Sgad	all = dropgid = _fmt = nselectors = optfatal = 0;
209127542Sgad	prtheader = showthreads = wflag = xkeep_implied = 0;
210127542Sgad	xkeep = -1;			/* Neither -x nor -X. */
211127499Sgad	init_list(&gidlist, addelem_gid, sizeof(gid_t), "group");
212127499Sgad	init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group");
213127499Sgad	init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id");
214127499Sgad	init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser");
215127499Sgad	init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id");
216127499Sgad	init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty");
217127499Sgad	init_list(&uidlist, addelem_uid, sizeof(uid_t), "user");
21889909Sru	memf = nlistf = _PATH_DEVNULL;
21998494Ssobomax	while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
2201556Srgrimes		switch((char)ch) {
221127499Sgad		case 'A':
222127499Sgad			/*
223127499Sgad			 * Exactly the same as `-ax'.   This has been
224127499Sgad			 * added for compatability with SUSv3, but for
225127499Sgad			 * now it will not be described in the man page.
226127499Sgad			 */
227127499Sgad			nselectors++;
228127499Sgad			all = xkeep = 1;
229127499Sgad			break;
2301556Srgrimes		case 'a':
231127499Sgad			nselectors++;
2321556Srgrimes			all = 1;
2331556Srgrimes			break;
23419068Speter		case 'C':
23519068Speter			rawcpu = 1;
23619068Speter			break;
23719068Speter		case 'c':
23819068Speter			cflag = 1;
23919068Speter			break;
2401556Srgrimes		case 'e':			/* XXX set ufmt */
2411556Srgrimes			needenv = 1;
2421556Srgrimes			break;
243127506Sgad#ifdef LAZY_PS
244127506Sgad		case 'f':
245127506Sgad			if (getuid() == 0 || getgid() == 0)
246127542Sgad				forceuread = 1;
247127506Sgad			break;
248127506Sgad#endif
249127499Sgad		case 'G':
250127499Sgad			add_list(&gidlist, optarg);
251127499Sgad			xkeep_implied = 1;
252127499Sgad			nselectors++;
253127499Sgad			break;
254127542Sgad		case 'g':
255127499Sgad#if 0
256127542Sgad			/*
257127542Sgad			 * XXX - This SUSv3 behavior is still under debate
258127542Sgad			 *	since it conflicts with the (undocumented)
259127542Sgad			 *	`-g' option.  So we skip it for now.
260127542Sgad			 */
261127499Sgad			add_list(&pgrplist, optarg);
262127499Sgad			xkeep_implied = 1;
263127499Sgad			nselectors++;
264127499Sgad			break;
265127499Sgad#else
266127542Sgad			/* The historical BSD-ish (from SunOS) behavior. */
2671556Srgrimes			break;			/* no-op */
268127499Sgad#endif
269116265Sscottl		case 'H':
270126127Sdeischen			showthreads = KERN_PROC_INC_THREAD;
271116265Sscottl			break;
2721556Srgrimes		case 'h':
2731556Srgrimes			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
2741556Srgrimes			break;
2751556Srgrimes		case 'j':
276109502Sjmallett			parsefmt(jfmt, 0);
27790143Smarkm			_fmt = 1;
2781556Srgrimes			jfmt[0] = '\0';
2791556Srgrimes			break;
2801556Srgrimes		case 'L':
2811556Srgrimes			showkey();
2821556Srgrimes			exit(0);
2831556Srgrimes		case 'l':
284109502Sjmallett			parsefmt(lfmt, 0);
28590143Smarkm			_fmt = 1;
2861556Srgrimes			lfmt[0] = '\0';
2871556Srgrimes			break;
2881556Srgrimes		case 'M':
2891556Srgrimes			memf = optarg;
29037317Sphk			dropgid = 1;
2911556Srgrimes			break;
2921556Srgrimes		case 'm':
2931556Srgrimes			sortby = SORTMEM;
2941556Srgrimes			break;
2951556Srgrimes		case 'N':
2961556Srgrimes			nlistf = optarg;
29737317Sphk			dropgid = 1;
2981556Srgrimes			break;
2991556Srgrimes		case 'O':
300109502Sjmallett			parsefmt(o1, 1);
301109502Sjmallett			parsefmt(optarg, 1);
302109502Sjmallett			parsefmt(o2, 1);
3031556Srgrimes			o1[0] = o2[0] = '\0';
30490143Smarkm			_fmt = 1;
3051556Srgrimes			break;
3061556Srgrimes		case 'o':
307109502Sjmallett			parsefmt(optarg, 1);
30890143Smarkm			_fmt = 1;
3091556Srgrimes			break;
3101556Srgrimes		case 'p':
311127499Sgad			add_list(&pidlist, optarg);
312127499Sgad			/*
313127499Sgad			 * Note: `-p' does not *set* xkeep, but any values
314127499Sgad			 * from pidlist are checked before xkeep is.  That
315127499Sgad			 * way they are always matched, even if the user
316127499Sgad			 * specifies `-X'.
317127499Sgad			 */
318127499Sgad			nselectors++;
3191556Srgrimes			break;
320127499Sgad#if 0
321127499Sgad		case 'R':
322127542Sgad			/*
323127542Sgad			 * XXX - This un-standard option is still under
324127542Sgad			 *	debate.  This is what SUSv3 defines as
325127542Sgad			 *	the `-U' option, and while it would be
326127542Sgad			 *	nice to have, it could cause even more
327127542Sgad			 *	confusion to implement it as `-R'.
328127542Sgad			 */
329127499Sgad			add_list(&ruidlist, optarg);
330127499Sgad			xkeep_implied = 1;
331127499Sgad			nselectors++;
332127499Sgad			break;
333127499Sgad#endif
3341556Srgrimes		case 'r':
3351556Srgrimes			sortby = SORTCPU;
3361556Srgrimes			break;
3371556Srgrimes		case 'S':
3381556Srgrimes			sumrusage = 1;
3391556Srgrimes			break;
340127499Sgad#if 0
341127499Sgad		case 's':
342127542Sgad			/*
343127542Sgad			 * XXX - This non-standard option is still under
344127542Sgad			 *	debate.  This *is* supported on Solaris,
345127542Sgad			 *	Linux, and IRIX, but conflicts with `-s'
346127542Sgad			 *	on NetBSD and maybe some older BSD's.
347127542Sgad			 */
348127499Sgad			add_list(&sesslist, optarg);
349127499Sgad			xkeep_implied = 1;
350127499Sgad			nselectors++;
351127499Sgad			break;
352127499Sgad#endif
3531556Srgrimes		case 'T':
3541556Srgrimes			if ((optarg = ttyname(STDIN_FILENO)) == NULL)
3551556Srgrimes				errx(1, "stdin: not a terminal");
3561556Srgrimes			/* FALLTHROUGH */
357127499Sgad		case 't':
358127499Sgad			add_list(&ttylist, optarg);
359127499Sgad			xkeep_implied = 1;
360127499Sgad			nselectors++;
3611556Srgrimes			break;
36213020Speter		case 'U':
363127499Sgad			/* This is what SUSv3 defines as the `-u' option. */
364127499Sgad			add_list(&uidlist, optarg);
365127499Sgad			xkeep_implied = 1;
366127499Sgad			nselectors++;
36713020Speter			break;
3681556Srgrimes		case 'u':
369109502Sjmallett			parsefmt(ufmt, 0);
3701556Srgrimes			sortby = SORTCPU;
37190143Smarkm			_fmt = 1;
3721556Srgrimes			ufmt[0] = '\0';
3731556Srgrimes			break;
3741556Srgrimes		case 'v':
375109502Sjmallett			parsefmt(vfmt, 0);
3761556Srgrimes			sortby = SORTMEM;
37790143Smarkm			_fmt = 1;
3781556Srgrimes			vfmt[0] = '\0';
3791556Srgrimes			break;
3801556Srgrimes		case 'w':
3811556Srgrimes			if (wflag)
3821556Srgrimes				termwidth = UNLIMITED;
3831556Srgrimes			else if (termwidth < 131)
3841556Srgrimes				termwidth = 131;
3851556Srgrimes			wflag++;
3861556Srgrimes			break;
387127499Sgad		case 'X':
388127499Sgad			/*
389127499Sgad			 * Note that `-X' and `-x' are not standard "selector"
390127499Sgad			 * options. For most selector-options, we check *all*
391127499Sgad			 * processes to see if any are matched by the given
392127499Sgad			 * value(s).  After we have a set of all the matched
393127499Sgad			 * processes, then `-X' and `-x' govern whether we
394127499Sgad			 * modify that *matched* set for processes which do
395127499Sgad			 * not have a controlling terminal.  `-X' causes
396127499Sgad			 * those processes to be deleted from the matched
397127499Sgad			 * set, while `-x' causes them to be kept.
398127499Sgad			 */
399127499Sgad			xkeep = 0;
400127499Sgad			break;
4011556Srgrimes		case 'x':
402127499Sgad			xkeep = 1;
4031556Srgrimes			break;
40486922Sgreen		case 'Z':
405109502Sjmallett			parsefmt(Zfmt, 0);
40686922Sgreen			Zfmt[0] = '\0';
40786922Sgreen			break;
4081556Srgrimes		case '?':
4091556Srgrimes		default:
4101556Srgrimes			usage();
4111556Srgrimes		}
4121556Srgrimes	argc -= optind;
4131556Srgrimes	argv += optind;
414127499Sgad	if (optfatal)
415127542Sgad		exit(1);		/* Error messages already printed. */
416127542Sgad	if (xkeep < 0)			/* Neither -X nor -x was specified. */
417127499Sgad		xkeep = xkeep_implied;
418127499Sgad
4191556Srgrimes#define	BACKWARD_COMPATIBILITY
4201556Srgrimes#ifdef	BACKWARD_COMPATIBILITY
4211556Srgrimes	if (*argv) {
4221556Srgrimes		nlistf = *argv;
423127542Sgad		if (*++argv)
4241556Srgrimes			memf = *argv;
4251556Srgrimes	}
4261556Srgrimes#endif
4271556Srgrimes	/*
4281556Srgrimes	 * Discard setgid privileges if not the running kernel so that bad
4291556Srgrimes	 * guys can't print interesting stuff from kernel memory.
4301556Srgrimes	 */
43137317Sphk	if (dropgid) {
4321556Srgrimes		setgid(getgid());
43337317Sphk		setuid(getuid());
43437317Sphk	}
4351556Srgrimes
43689909Sru	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
4371556Srgrimes	if (kd == 0)
4381556Srgrimes		errx(1, "%s", errbuf);
4391556Srgrimes
44090143Smarkm	if (!_fmt)
441109502Sjmallett		parsefmt(dfmt, 0);
4421556Srgrimes
443127499Sgad	if (nselectors == 0) {
444127499Sgad		uidlist.ptr = malloc(sizeof(uid_t));
445127499Sgad		if (uidlist.ptr == NULL)
44697877Sjmallett			errx(1, "malloc failed");
447127499Sgad		nselectors = 1;
448127499Sgad		uidlist.count = uidlist.maxcount = 1;
449127499Sgad		*uidlist.uids = getuid();
45066377Sbrian	}
4511556Srgrimes
4521556Srgrimes	/*
4531556Srgrimes	 * scan requested variables, noting what structures are needed,
45453170Skris	 * and adjusting header widths as appropriate.
4551556Srgrimes	 */
4561556Srgrimes	scanvars();
457127499Sgad
4581556Srgrimes	/*
459127499Sgad	 * Get process list.  If the user requested just one selector-
460127499Sgad	 * option, then kvm_getprocs can be asked to return just those
461127499Sgad	 * processes.  Otherwise, have it return all processes, and
462127499Sgad	 * then this routine will search that full list and select the
463127499Sgad	 * processes which match any of the user's selector-options.
4641556Srgrimes	 */
465127499Sgad	what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
466127499Sgad	flag = 0;
467127499Sgad	if (nselectors == 1) {
468127499Sgad		/* XXX - Apparently there's no KERN_PROC_GID flag. */
469127499Sgad		if (pgrplist.count == 1) {
470127499Sgad			what = KERN_PROC_PGRP | showthreads;
471127499Sgad			flag = *pgrplist.pids;
472127499Sgad			nselectors = 0;
473127499Sgad		} else if (pidlist.count == 1) {
474127499Sgad			what = KERN_PROC_PID | showthreads;
475127499Sgad			flag = *pidlist.pids;
476127499Sgad			nselectors = 0;
477127499Sgad		} else if (ruidlist.count == 1) {
478127499Sgad			what = KERN_PROC_RUID | showthreads;
479127499Sgad			flag = *ruidlist.uids;
480127499Sgad			nselectors = 0;
481127542Sgad#if 0
482127542Sgad		/*
483127542Sgad		 * XXX - KERN_PROC_SESSION causes error in kvm_getprocs?
484127542Sgad		 *	For now, always do sid-matching in this routine.
485127542Sgad		 */
486127499Sgad		} else if (sesslist.count == 1) {
487127499Sgad			what = KERN_PROC_SESSION | showthreads;
488127499Sgad			flag = *sesslist.pids;
489127499Sgad			nselectors = 0;
490127499Sgad#endif
491127499Sgad		} else if (ttylist.count == 1) {
492127499Sgad			what = KERN_PROC_TTY | showthreads;
493127499Sgad			flag = *ttylist.ttys;
494127499Sgad			nselectors = 0;
495127499Sgad		} else if (uidlist.count == 1) {
496127499Sgad			what = KERN_PROC_UID | showthreads;
497127499Sgad			flag = *uidlist.uids;
498127499Sgad			nselectors = 0;
499127499Sgad		} else if (all) {
500127499Sgad			/* No need for this routine to select processes. */
501127499Sgad			nselectors = 0;
502127499Sgad		}
5031556Srgrimes	}
504126127Sdeischen
5051556Srgrimes	/*
5061556Srgrimes	 * select procs
5071556Srgrimes	 */
508127499Sgad	nentries = -1;
509127149Sgad	kp = kvm_getprocs(kd, what, flag, &nentries);
510127513Sgad	if ((kp == 0 && nentries > 0) || (kp != 0 && nentries < 0))
5111556Srgrimes		errx(1, "%s", kvm_geterr(kd));
512127499Sgad	nkept = 0;
513127149Sgad	if (nentries > 0) {
514127149Sgad		if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
515127149Sgad			errx(1, "malloc failed");
516127149Sgad		for (i = nentries; --i >= 0; ++kp) {
517127499Sgad			/*
518127499Sgad			 * If the user specified multiple selection-criteria,
519127499Sgad			 * then keep any process matched by the inclusive OR
520127499Sgad			 * of all the selection-criteria given.
521127499Sgad			 */
522127499Sgad			if (pidlist.count > 0) {
523127499Sgad				for (elem = 0; elem < pidlist.count; elem++)
524127499Sgad					if (kp->ki_pid == pidlist.pids[elem])
525127499Sgad						goto keepit;
526127499Sgad			}
527127499Sgad			/*
528127499Sgad			 * Note that we had to process pidlist before
529127499Sgad			 * filtering out processes which do not have
530127499Sgad			 * a controlling terminal.
531127499Sgad			 */
532127499Sgad			if (xkeep == 0) {
533127499Sgad				if ((kp->ki_tdev == NODEV ||
534127499Sgad				    (kp->ki_flag & P_CONTROLT) == 0))
535127499Sgad					continue;
536127499Sgad			}
537127499Sgad			if (nselectors == 0)
538127499Sgad				goto keepit;
539127499Sgad			if (gidlist.count > 0) {
540127499Sgad				for (elem = 0; elem < gidlist.count; elem++)
541127499Sgad					if (kp->ki_rgid == gidlist.gids[elem])
542127499Sgad						goto keepit;
543127499Sgad			}
544127499Sgad			if (pgrplist.count > 0) {
545127499Sgad				for (elem = 0; elem < pgrplist.count; elem++)
546127499Sgad					if (kp->ki_pgid == pgrplist.pids[elem])
547127499Sgad						goto keepit;
548127499Sgad			}
549127499Sgad			if (ruidlist.count > 0) {
550127499Sgad				for (elem = 0; elem < ruidlist.count; elem++)
551127499Sgad					if (kp->ki_ruid == ruidlist.uids[elem])
552127499Sgad						goto keepit;
553127499Sgad			}
554127499Sgad			if (sesslist.count > 0) {
555127499Sgad				for (elem = 0; elem < sesslist.count; elem++)
556127499Sgad					if (kp->ki_sid == sesslist.pids[elem])
557127499Sgad						goto keepit;
558127499Sgad			}
559127499Sgad			if (ttylist.count > 0) {
560127499Sgad				for (elem = 0; elem < ttylist.count; elem++)
561127499Sgad					if (kp->ki_tdev == ttylist.ttys[elem])
562127499Sgad						goto keepit;
563127499Sgad			}
564127499Sgad			if (uidlist.count > 0) {
565127499Sgad				for (elem = 0; elem < uidlist.count; elem++)
566127499Sgad					if (kp->ki_uid == uidlist.uids[elem])
567127499Sgad						goto keepit;
568127499Sgad			}
569127499Sgad			/*
570127499Sgad			 * This process did not match any of the user's
571127499Sgad			 * selector-options, so skip the process.
572127499Sgad			 */
573127499Sgad			continue;
574127499Sgad
575127499Sgad		keepit:
576127499Sgad			kinfo[nkept].ki_p = kp;
577127149Sgad			if (needuser)
578127499Sgad				saveuser(&kinfo[nkept]);
579127499Sgad			dynsizevars(&kinfo[nkept]);
580127499Sgad			nkept++;
581127149Sgad		}
5821556Srgrimes	}
58325271Sjkh
58425271Sjkh	sizevars();
58525271Sjkh
5861556Srgrimes	/*
5871556Srgrimes	 * print header
5881556Srgrimes	 */
5891556Srgrimes	printheader();
590127499Sgad	if (nkept == 0)
59162803Swill		exit(1);
592127499Sgad
5931556Srgrimes	/*
5941556Srgrimes	 * sort proc list
5951556Srgrimes	 */
596127499Sgad	qsort(kinfo, nkept, sizeof(KINFO), pscomp);
5971556Srgrimes	/*
598127499Sgad	 * For each process, call each variable output function.
5991556Srgrimes	 */
600127499Sgad	for (i = lineno = 0; i < nkept; i++) {
6011556Srgrimes		for (vent = vhead; vent; vent = vent->next) {
6021556Srgrimes			(vent->var->oproc)(&kinfo[i], vent);
6031556Srgrimes			if (vent->next != NULL)
6041556Srgrimes				(void)putchar(' ');
6051556Srgrimes		}
6061556Srgrimes		(void)putchar('\n');
6071556Srgrimes		if (prtheader && lineno++ == prtheader - 4) {
6081556Srgrimes			(void)putchar('\n');
6091556Srgrimes			printheader();
6101556Srgrimes			lineno = 0;
6111556Srgrimes		}
6121556Srgrimes	}
613127499Sgad	free_list(&gidlist);
614127499Sgad	free_list(&pidlist);
615127499Sgad	free_list(&pgrplist);
616127499Sgad	free_list(&ruidlist);
617127499Sgad	free_list(&sesslist);
618127499Sgad	free_list(&ttylist);
619127499Sgad	free_list(&uidlist);
62066377Sbrian
6211556Srgrimes	exit(eval);
6221556Srgrimes}
6231556Srgrimes
624127499Sgadstatic int
625127499Sgadaddelem_gid(struct listinfo *inf, const char *elem)
626127499Sgad{
627127499Sgad	struct group *grp;
628127499Sgad	const char *nameorID;
629127499Sgad	char *endp;
630127539Sgad	intmax_t ltemp;
631127499Sgad
632127499Sgad	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
633127499Sgad		if (*elem == '\0')
634127499Sgad			warnx("Invalid (zero-length) %s name", inf->lname);
635127499Sgad		else
636127499Sgad			warnx("%s name too long: %s", inf->lname, elem);
637127499Sgad		optfatal = 1;
638127542Sgad		return (0);		/* Do not add this value. */
639127499Sgad	}
640127499Sgad
641127499Sgad	/*
642127499Sgad	 * SUSv3 states that `ps -G grouplist' should match "real-group
643127499Sgad	 * ID numbers", and does not mention group-names.  I do want to
644127499Sgad	 * also support group-names, so this tries for a group-id first,
645127499Sgad	 * and only tries for a name if that doesn't work.  This is the
646127499Sgad	 * opposite order of what is done in addelem_uid(), but in
647127499Sgad	 * practice the order would only matter for group-names which
648127499Sgad	 * are all-numeric.
649127499Sgad	 */
650127499Sgad	grp = NULL;
651127499Sgad	nameorID = "named";
652127499Sgad	errno = 0;
653127499Sgad	ltemp = strtol(elem, &endp, 10);
654127499Sgad	if (errno == 0 && *endp == '\0' && ltemp >= 0 && ltemp <= GID_MAX) {
655127499Sgad		nameorID = "name or ID matches";
656127499Sgad		grp = getgrgid((gid_t)ltemp);
657127499Sgad	}
658127499Sgad	if (grp == NULL)
659127499Sgad		grp = getgrnam(elem);
660127499Sgad	if (grp == NULL) {
661127499Sgad		warnx("No %s %s '%s'", inf->lname, nameorID, elem);
662127499Sgad		optfatal = 1;
663127542Sgad		return (0);		/* Do not add this value. */
664127499Sgad	}
665127499Sgad
666127499Sgad	if (inf->count >= inf->maxcount)
667127499Sgad		expand_list(inf);
668127499Sgad	inf->gids[(inf->count)++] = grp->gr_gid;
669127499Sgad	return (1);
670127499Sgad}
671127499Sgad
672127542Sgad#define	BSD_PID_MAX	99999	/* Copy of PID_MAX from sys/proc.h. */
673127499Sgadstatic int
674127499Sgadaddelem_pid(struct listinfo *inf, const char *elem)
675127149Sgad{
676127539Sgad	char *endp;
677127149Sgad	long tempid;
678127149Sgad
679127499Sgad	if (*elem == '\0')
680127499Sgad		tempid = 0L;
681127499Sgad	else {
682127499Sgad		errno = 0;
683127499Sgad		tempid = strtol(elem, &endp, 10);
684127499Sgad		if (*endp != '\0' || tempid < 0 || elem == endp) {
685127499Sgad			warnx("Invalid %s: %s", inf->lname, elem);
686127499Sgad			errno = ERANGE;
687127499Sgad		} else if (errno != 0 || tempid > BSD_PID_MAX) {
688127499Sgad			warnx("%s too large: %s", inf->lname, elem);
689127499Sgad			errno = ERANGE;
690127149Sgad		}
691127499Sgad		if (errno == ERANGE) {
692127499Sgad			optfatal = 1;
693127542Sgad			return (0);	/* Do not add this value. */
694127149Sgad		}
695127149Sgad	}
696127149Sgad
697127499Sgad	if (inf->count >= inf->maxcount)
698127499Sgad		expand_list(inf);
699127499Sgad	inf->pids[(inf->count)++] = tempid;
700127499Sgad	return (1);
701127499Sgad}
702127499Sgad#undef	BSD_PID_MAX
703127149Sgad
704127499Sgadstatic int
705127499Sgadaddelem_tty(struct listinfo *inf, const char *elem)
706127499Sgad{
707127539Sgad	const char *ttypath;
708127539Sgad	struct stat sb;
709127499Sgad	char pathbuf[PATH_MAX];
710127499Sgad
711127499Sgad	if (strcmp(elem, "co") == 0)
712127499Sgad		ttypath = strdup(_PATH_CONSOLE);
713127499Sgad	else if (*elem == '/')
714127499Sgad		ttypath = elem;
715127499Sgad	else {
716127499Sgad		strlcpy(pathbuf, _PATH_TTY, sizeof(pathbuf));
717127499Sgad		strlcat(pathbuf, elem, sizeof(pathbuf));
718127499Sgad		ttypath = pathbuf;
719127499Sgad	}
720127499Sgad
721127499Sgad	if (stat(ttypath, &sb) == -1) {
722127499Sgad		warn("%s", ttypath);
723127499Sgad		optfatal = 1;
724127542Sgad		return (0);		/* Do not add this value. */
725127499Sgad	}
726127499Sgad	if (!S_ISCHR(sb.st_mode)) {
727127499Sgad		warn("%s: Not a terminal", ttypath);
728127499Sgad		optfatal = 1;
729127542Sgad		return (0);		/* Do not add this value. */
730127499Sgad	}
731127499Sgad
732127499Sgad	if (inf->count >= inf->maxcount)
733127499Sgad		expand_list(inf);
734127499Sgad	inf->ttys[(inf->count)++] = sb.st_rdev;
735127499Sgad	return (1);
736127149Sgad}
737127149Sgad
738127499Sgadstatic int
739127499Sgadaddelem_uid(struct listinfo *inf, const char *elem)
74066377Sbrian{
74166377Sbrian	struct passwd *pwd;
742127539Sgad	char *endp;
743127499Sgad	intmax_t ltemp;
74466377Sbrian
745127499Sgad	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
746127499Sgad		if (*elem == '\0')
747127499Sgad			warnx("Invalid (zero-length) %s name", inf->lname);
748127499Sgad		else
749127499Sgad			warnx("%s name too long: %s", inf->lname, elem);
750127499Sgad		optfatal = 1;
751127542Sgad		return (0);		/* Do not add this value. */
752127499Sgad	}
75366377Sbrian
754127499Sgad	pwd = getpwnam(elem);
755127499Sgad	if (pwd == NULL) {
756127499Sgad		errno = 0;
757127499Sgad		ltemp = strtol(elem, &endp, 10);
758127499Sgad		if (errno != 0 || *endp != '\0' || ltemp < 0 ||
759127499Sgad		    ltemp > UID_MAX)
760127499Sgad			warnx("No %s named '%s'", inf->lname, elem);
761127499Sgad		else {
762127499Sgad			/* The string is all digits, so it might be a userID. */
763127499Sgad			pwd = getpwuid((uid_t)ltemp);
764127499Sgad			if (pwd == NULL)
765127499Sgad				warnx("No %s name or ID matches '%s'",
766127499Sgad				    inf->lname, elem);
76766377Sbrian		}
768127499Sgad	}
769127499Sgad	if (pwd == NULL) {
770127509Sgad		/*
771127509Sgad		 * These used to be treated as minor warnings (and the
772127509Sgad		 * option was simply ignored), but now they are fatal
773127509Sgad		 * errors (and the command will be aborted).
774127509Sgad		 */
775127509Sgad		optfatal = 1;
776127542Sgad		return (0);		/* Do not add this value. */
777127499Sgad	}
778127499Sgad
779127499Sgad	if (inf->count >= inf->maxcount)
780127499Sgad		expand_list(inf);
781127499Sgad	inf->uids[(inf->count)++] = pwd->pw_uid;
782127499Sgad	return (1);
783127499Sgad}
784127499Sgad
785127499Sgadstatic void
786127499Sgadadd_list(struct listinfo *inf, const char *argp)
787127499Sgad{
788127499Sgad	const char *savep;
789127499Sgad	char *cp, *endp;
790127499Sgad	int toolong;
791127539Sgad	char elemcopy[PATH_MAX];
792127499Sgad
793127499Sgad	while (*argp != '\0') {
794127499Sgad		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
795127499Sgad			argp++;
796127499Sgad		savep = argp;
797127499Sgad		toolong = 0;
798127499Sgad		cp = elemcopy;
799127499Sgad		if (strchr(T_SEP, *argp) == NULL) {
800127499Sgad			endp = elemcopy + sizeof(elemcopy) - 1;
801127499Sgad			while (*argp != '\0' && cp <= endp &&
802127499Sgad			    strchr(W_SEP T_SEP, *argp) == NULL)
803127499Sgad				*cp++ = *argp++;
804127499Sgad			if (cp > endp)
805127499Sgad				toolong = 1;
80666377Sbrian		}
807127499Sgad		if (!toolong) {
808127499Sgad			*cp = '\0';
809127499Sgad#ifndef ADD_PS_LISTRESET
810127542Sgad			/*
811127542Sgad			 * This is how the standard expects lists to
812127542Sgad			 * be handled.
813127542Sgad			 */
814127499Sgad			inf->addelem(inf, elemcopy);
815127499Sgad#else
816127499Sgad			/*
817127542Sgad			 * This would add a simple non-standard-but-convienent
818127542Sgad			 * feature.
819127542Sgad			 *
820127542Sgad			 * XXX - The first time I tried to add this check,
821127542Sgad			 *	it increased the total size of `ps' by 3940
822127542Sgad			 *	bytes on i386!  That's 12% of the entire
823127542Sgad			 *	program!  The `ps.o' file grew by only about
824127542Sgad			 *	40 bytes, but the final (stripped) executable
825127542Sgad			 *	in /bin/ps grew by 12%.  I have not had time
826127542Sgad			 *	to investigate, so skip the feature for now.
827127542Sgad			 */
828127542Sgad			/*
829127499Sgad			 * We now have a single element.  Add it to the
830127499Sgad			 * list, unless the element is ":".  In that case,
831127499Sgad			 * reset the list so previous entries are ignored.
832127499Sgad			 */
833127499Sgad			if (strcmp(elemcopy, ":") == 0)
834127499Sgad				inf->count = 0;
835127499Sgad			else
836127499Sgad				inf->addelem(inf, elemcopy);
837127499Sgad#endif
838127499Sgad		} else {
839127499Sgad			/*
840127499Sgad			 * The string is too long to copy.  Find the end
841127499Sgad			 * of the string to print out the warning message.
842127499Sgad			 */
843127499Sgad			while (*argp != '\0' && strchr(W_SEP T_SEP,
844127499Sgad			    *argp) == NULL)
845127499Sgad				argp++;
846127499Sgad			warnx("Value too long: %.*s", (int)(argp - savep),
847127499Sgad			    savep);
848127499Sgad			optfatal = 1;
84966377Sbrian		}
850127499Sgad		/*
851127499Sgad		 * Skip over any number of trailing whitespace characters,
852127499Sgad		 * but only one (at most) trailing element-terminating
853127499Sgad		 * character.
854127499Sgad		 */
855127499Sgad		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
856127499Sgad			argp++;
857127499Sgad		if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) {
858127499Sgad			argp++;
859127499Sgad			/* Catch case where string ended with a comma. */
860127499Sgad			if (*argp == '\0')
861127499Sgad				inf->addelem(inf, argp);
862127499Sgad		}
86366377Sbrian	}
864127499Sgad}
86566377Sbrian
866127499Sgadstatic void *
867127499Sgadexpand_list(struct listinfo *inf)
868127499Sgad{
869127539Sgad	void *newlist;
870127499Sgad	int newmax;
87166377Sbrian
872127499Sgad	newmax = (inf->maxcount + 1) << 1;
873127499Sgad	newlist = realloc(inf->ptr, newmax * inf->elemsize);
874127499Sgad	if (newlist == NULL) {
875127499Sgad		free(inf->ptr);
876127499Sgad		errx(1, "realloc to %d %ss failed", newmax,
877127499Sgad		    inf->lname);
878127499Sgad	}
879127499Sgad	inf->maxcount = newmax;
880127499Sgad	inf->ptr = newlist;
881127499Sgad
882127499Sgad	return (newlist);
88366377Sbrian}
88466377Sbrian
885127499Sgadstatic void
886127499Sgadfree_list(struct listinfo *inf)
887127499Sgad{
888127499Sgad
889127499Sgad	inf->count = inf->elemsize = inf->maxcount = 0;
890127499Sgad	if (inf->ptr != NULL)
891127499Sgad		free(inf->ptr);
892127499Sgad	inf->addelem = NULL;
893127499Sgad	inf->lname = NULL;
894127499Sgad	inf->ptr = NULL;
895127499Sgad}
896127499Sgad
897127499Sgadstatic void
898127499Sgadinit_list(struct listinfo *inf, addelem_rtn artn, int elemsize,
899127499Sgad    const char *lname)
900127499Sgad{
901127499Sgad
902127499Sgad	inf->count = inf->maxcount = 0;
903127499Sgad	inf->elemsize = elemsize;
904127499Sgad	inf->addelem = artn;
905127499Sgad	inf->lname = lname;
906127499Sgad	inf->ptr = NULL;
907127499Sgad}
908127499Sgad
909109502SjmallettVARENT *
910109502Sjmallettfind_varentry(VAR *v)
911109502Sjmallett{
912109502Sjmallett	struct varent *vent;
913109502Sjmallett
914109502Sjmallett	for (vent = vhead; vent; vent = vent->next) {
915109502Sjmallett		if (strcmp(vent->var->name, v->name) == 0)
916109502Sjmallett			return vent;
917109502Sjmallett	}
918109502Sjmallett	return NULL;
919109502Sjmallett}
920109502Sjmallett
9211556Srgrimesstatic void
92290110Simpscanvars(void)
9231556Srgrimes{
9241556Srgrimes	struct varent *vent;
9251556Srgrimes	VAR *v;
92625271Sjkh
92725271Sjkh	for (vent = vhead; vent; vent = vent->next) {
92825271Sjkh		v = vent->var;
92925271Sjkh		if (v->flag & DSIZ) {
93025271Sjkh			v->dwidth = v->width;
93125271Sjkh			v->width = 0;
93225271Sjkh		}
93325271Sjkh		if (v->flag & USER)
93425271Sjkh			needuser = 1;
93525271Sjkh		if (v->flag & COMM)
93625271Sjkh			needcomm = 1;
93725271Sjkh	}
93825271Sjkh}
93925271Sjkh
94025271Sjkhstatic void
94190110Simpdynsizevars(KINFO *ki)
94225271Sjkh{
94325271Sjkh	struct varent *vent;
94425271Sjkh	VAR *v;
9451556Srgrimes	int i;
9461556Srgrimes
9471556Srgrimes	for (vent = vhead; vent; vent = vent->next) {
9481556Srgrimes		v = vent->var;
94925271Sjkh		if (!(v->flag & DSIZ))
95025271Sjkh			continue;
95125271Sjkh		i = (v->sproc)( ki);
95225271Sjkh		if (v->width < i)
95325271Sjkh			v->width = i;
95425271Sjkh		if (v->width > v->dwidth)
95525271Sjkh			v->width = v->dwidth;
95625271Sjkh	}
95725271Sjkh}
95825271Sjkh
95925271Sjkhstatic void
96090110Simpsizevars(void)
96125271Sjkh{
96225271Sjkh	struct varent *vent;
96325271Sjkh	VAR *v;
96425271Sjkh	int i;
96525271Sjkh
96625271Sjkh	for (vent = vhead; vent; vent = vent->next) {
96725271Sjkh		v = vent->var;
968109504Sjmallett		i = strlen(vent->header);
9691556Srgrimes		if (v->width < i)
9701556Srgrimes			v->width = i;
9711556Srgrimes		totwidth += v->width + 1;	/* +1 for space */
9721556Srgrimes	}
9731556Srgrimes	totwidth--;
9741556Srgrimes}
9751556Srgrimes
97690143Smarkmstatic const char *
97790110Simpfmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki,
978127542Sgad    char *comm, int maxlen)
9791556Srgrimes{
98090143Smarkm	const char *s;
9811556Srgrimes
98290143Smarkm	s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen);
9831556Srgrimes	return (s);
9841556Srgrimes}
9851556Srgrimes
98671578Sjhb#define UREADOK(ki)	(forceuread || (ki->ki_p->ki_sflag & PS_INMEM))
98731552Sdyson
9881556Srgrimesstatic void
98990110Simpsaveuser(KINFO *ki)
9901556Srgrimes{
9911556Srgrimes
99271578Sjhb	if (ki->ki_p->ki_sflag & PS_INMEM) {
9931556Srgrimes		/*
9941556Srgrimes		 * The u-area might be swapped out, and we can't get
9951556Srgrimes		 * at it because we have a crashdump and no swap.
9961556Srgrimes		 * If it's here fill in these fields, otherwise, just
9971556Srgrimes		 * leave them 0.
9981556Srgrimes		 */
99969896Smckusick		ki->ki_valid = 1;
10001556Srgrimes	} else
100169896Smckusick		ki->ki_valid = 0;
10021556Srgrimes	/*
10031556Srgrimes	 * save arguments if needed
10041556Srgrimes	 */
100569896Smckusick	if (needcomm && (UREADOK(ki) || (ki->ki_p->ki_args != NULL))) {
100690143Smarkm		ki->ki_args = strdup(fmt(kvm_getargv, ki, ki->ki_p->ki_comm,
100790143Smarkm		    MAXCOMLEN));
100831552Sdyson	} else if (needcomm) {
100990143Smarkm		asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm);
101053276Speter	} else {
101153276Speter		ki->ki_args = NULL;
101253276Speter	}
101353276Speter	if (needenv && UREADOK(ki)) {
101490143Smarkm		ki->ki_env = strdup(fmt(kvm_getenvv, ki, (char *)NULL, 0));
101553276Speter	} else if (needenv) {
101653276Speter		ki->ki_env = malloc(3);
101753276Speter		strcpy(ki->ki_env, "()");
101853276Speter	} else {
101953276Speter		ki->ki_env = NULL;
102053276Speter	}
10211556Srgrimes}
10221556Srgrimes
10231556Srgrimesstatic int
102490110Simppscomp(const void *a, const void *b)
10251556Srgrimes{
10261556Srgrimes	int i;
102769896Smckusick#define VSIZE(k) ((k)->ki_p->ki_dsize + (k)->ki_p->ki_ssize + \
102869896Smckusick		  (k)->ki_p->ki_tsize)
10291556Srgrimes
10301556Srgrimes	if (sortby == SORTCPU)
103190143Smarkm		return (getpcpu((const KINFO *)b) - getpcpu((const KINFO *)a));
10321556Srgrimes	if (sortby == SORTMEM)
103390143Smarkm		return (VSIZE((const KINFO *)b) - VSIZE((const KINFO *)a));
1034127508Sgad	i =  (int)((const KINFO *)a)->ki_p->ki_tdev -
1035127508Sgad	    (int)((const KINFO *)b)->ki_p->ki_tdev;
10361556Srgrimes	if (i == 0)
1037127508Sgad		i = ((const KINFO *)a)->ki_p->ki_pid -
1038127508Sgad		    ((const KINFO *)b)->ki_p->ki_pid;
10391556Srgrimes	return (i);
10401556Srgrimes}
10411556Srgrimes
10421556Srgrimes/*
10431556Srgrimes * ICK (all for getopt), would rather hide the ugliness
10441556Srgrimes * here than taint the main code.
10451556Srgrimes *
10461556Srgrimes *  ps foo -> ps -foo
10471556Srgrimes *  ps 34 -> ps -p34
10481556Srgrimes *
10491556Srgrimes * The old convention that 't' with no trailing tty arg means the users
10501556Srgrimes * tty, is only supported if argv[1] doesn't begin with a '-'.  This same
10511556Srgrimes * feature is available with the option 'T', which takes no argument.
10521556Srgrimes */
10531556Srgrimesstatic char *
105490110Simpkludge_oldps_options(char *s)
10551556Srgrimes{
1056102886Sjmallett	int have_fmt;
10571556Srgrimes	size_t len;
10581556Srgrimes	char *newopts, *ns, *cp;
10591556Srgrimes
1060102886Sjmallett	/*
1061102886Sjmallett	 * If we have an 'o' option, then note it, since we don't want to do
1062102886Sjmallett	 * some types of munging.
1063102886Sjmallett	 */
1064102886Sjmallett	have_fmt = index(s, 'o') != NULL;
1065102886Sjmallett
10661556Srgrimes	len = strlen(s);
10671556Srgrimes	if ((newopts = ns = malloc(len + 2)) == NULL)
106897877Sjmallett		errx(1, "malloc failed");
10691556Srgrimes	/*
10701556Srgrimes	 * options begin with '-'
10711556Srgrimes	 */
10721556Srgrimes	if (*s != '-')
10731556Srgrimes		*ns++ = '-';	/* add option flag */
10741556Srgrimes	/*
10751556Srgrimes	 * gaze to end of argv[1]
10761556Srgrimes	 */
10771556Srgrimes	cp = s + len - 1;
10781556Srgrimes	/*
10791556Srgrimes	 * if last letter is a 't' flag with no argument (in the context
10801556Srgrimes	 * of the oldps options -- option string NOT starting with a '-' --
10811556Srgrimes	 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
108281743Sbrian	 *
108381743Sbrian	 * However, if a flag accepting a string argument is found in the
108481743Sbrian	 * option string, the remainder of the string is the argument to
108581743Sbrian	 * that flag; do not modify that argument.
10861556Srgrimes	 */
108789909Sru	if (strcspn(s, "MNOoU") == len && *cp == 't' && *s != '-')
10881556Srgrimes		*cp = 'T';
10891556Srgrimes	else {
10901556Srgrimes		/*
10911556Srgrimes		 * otherwise check for trailing number, which *may* be a
10921556Srgrimes		 * pid.
10931556Srgrimes		 */
10941556Srgrimes		while (cp >= s && isdigit(*cp))
10951556Srgrimes			--cp;
10961556Srgrimes	}
10971556Srgrimes	cp++;
10981556Srgrimes	memmove(ns, s, (size_t)(cp - s));	/* copy up to trailing number */
10991556Srgrimes	ns += cp - s;
11001556Srgrimes	/*
11011556Srgrimes	 * if there's a trailing number, and not a preceding 'p' (pid) or
11021556Srgrimes	 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag.
11031556Srgrimes	 */
11048855Srgrimes	if (isdigit(*cp) &&
11057165Sjoerg	    (cp == s || (cp[-1] != 't' && cp[-1] != 'p')) &&
1106102886Sjmallett	    (cp - 1 == s || cp[-2] != 't') && !have_fmt)
11071556Srgrimes		*ns++ = 'p';
11081556Srgrimes	(void)strcpy(ns, cp);		/* and append the number */
11091556Srgrimes
11101556Srgrimes	return (newopts);
11111556Srgrimes}
11121556Srgrimes
11131556Srgrimesstatic void
111490110Simpusage(void)
11151556Srgrimes{
1116127507Sgad#define	SINGLE_OPTS	"[-aC" OPT_LAZY_f "HhjlmrSTuvwXxZ]"
11171556Srgrimes
1118127499Sgad	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
1119127507Sgad	    "usage: ps " SINGLE_OPTS " [-G gid[,gid]] [-O|o fmt]",
1120127499Sgad	    "          [-p pid[,pid]] [-t tty[,tty]] [-U user[,user]]",
1121127499Sgad	    "          [-M core] [-N system]",
112226465Scharnier	    "       ps [-L]");
11231556Srgrimes	exit(1);
11241556Srgrimes}
1125