ps.c revision 127598
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 127598 2004-03-30 02:02:40Z gad $");
551556Srgrimes
561556Srgrimes#include <sys/param.h>
57127546Sgad#include <sys/proc.h>
583296Sdg#include <sys/user.h>
591556Srgrimes#include <sys/stat.h>
601556Srgrimes#include <sys/ioctl.h>
611556Srgrimes#include <sys/sysctl.h>
621556Srgrimes
631556Srgrimes#include <ctype.h>
641556Srgrimes#include <err.h>
65127149Sgad#include <errno.h>
661556Srgrimes#include <fcntl.h>
67127499Sgad#include <grp.h>
681556Srgrimes#include <kvm.h>
6913514Smpp#include <limits.h>
7073367Sache#include <locale.h>
711556Srgrimes#include <paths.h>
7290143Smarkm#include <pwd.h>
73127499Sgad#include <stdint.h>
741556Srgrimes#include <stdio.h>
751556Srgrimes#include <stdlib.h>
761556Srgrimes#include <string.h>
771556Srgrimes#include <unistd.h>
781556Srgrimes
791556Srgrimes#include "ps.h"
801556Srgrimes
81127499Sgad#define	W_SEP	" \t"		/* "Whitespace" list separators */
82127499Sgad#define	T_SEP	","		/* "Terminate-element" list separators */
8366377Sbrian
84127537Sgad#ifdef LAZY_PS
85127555Sgad#define	DEF_UREAD	0
86127537Sgad#define	OPT_LAZY_f	"f"
87127537Sgad#else
88127555Sgad#define	DEF_UREAD	1	/* Always do the more-expensive read. */
89127537Sgad#define	OPT_LAZY_f		/* I.e., the `-f' option is not added. */
90127537Sgad#endif
91127537Sgad
92127537Sgadint	 cflag;			/* -c */
93127537Sgadint	 eval;			/* Exit value */
94127537Sgadtime_t	 now;			/* Current time(3) value */
95127537Sgadint	 rawcpu;		/* -C */
96127537Sgadint	 sumrusage;		/* -S */
97127537Sgadint	 termwidth;		/* Width of the screen (0 == infinity). */
98127537Sgadint	 totwidth;		/* Calculated-width of requested variables. */
99127537Sgad
10090143Smarkmstruct varent *vhead;
1011556Srgrimes
102127537Sgadstatic int	 forceuread = DEF_UREAD; /* Do extra work to get u-area. */
103127537Sgadstatic kvm_t	*kd;
104127537Sgadstatic KINFO	*kinfo;
105127537Sgadstatic int	 needcomm;	/* -o "command" */
106127537Sgadstatic int	 needenv;	/* -e */
107127537Sgadstatic int	 needuser;	/* -o "user" */
108127537Sgadstatic int	 optfatal;	/* Fatal error parsing some list-option. */
1091556Srgrimes
110127537Sgadstatic enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
11197966Sjmallett
112127499Sgadstruct listinfo;
113127537Sgadtypedef	int	addelem_rtn(struct listinfo *_inf, const char *_elem);
114127499Sgad
115127499Sgadstruct listinfo {
116127499Sgad	int		 count;
117127499Sgad	int		 maxcount;
118127499Sgad	int		 elemsize;
119127499Sgad	addelem_rtn	*addelem;
120127499Sgad	const char	*lname;
121127499Sgad	union {
122127499Sgad		gid_t	*gids;
123127499Sgad		pid_t	*pids;
124127499Sgad		dev_t	*ttys;
125127499Sgad		uid_t	*uids;
126127499Sgad		void	*ptr;
127127499Sgad	};
128127499Sgad};
129127499Sgad
130127499Sgadstatic int	 addelem_gid(struct listinfo *, const char *);
131127499Sgadstatic int	 addelem_pid(struct listinfo *, const char *);
132127499Sgadstatic int	 addelem_tty(struct listinfo *, const char *);
133127499Sgadstatic int	 addelem_uid(struct listinfo *, const char *);
134127499Sgadstatic void	 add_list(struct listinfo *, const char *);
135127536Sgadstatic void	 dynsizevars(KINFO *);
136127499Sgadstatic void	*expand_list(struct listinfo *);
137127598Sgadstatic const char *
138127598Sgad		 fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int),
139127536Sgad		    KINFO *, char *, int);
140127499Sgadstatic void	 free_list(struct listinfo *);
141127499Sgadstatic void	 init_list(struct listinfo *, addelem_rtn, int, const char *);
142127536Sgadstatic char	*kludge_oldps_options(char *);
143127536Sgadstatic int	 pscomp(const void *, const void *);
144127536Sgadstatic void	 saveuser(KINFO *);
145127536Sgadstatic void	 scanvars(void);
146127536Sgadstatic void	 sizevars(void);
147127536Sgadstatic void	 usage(void);
148127499Sgad
14997875Sjmallettstatic char dfmt[] = "pid,tt,state,time,command";
15097875Sjmallettstatic char jfmt[] = "user,pid,ppid,pgid,jobc,state,tt,time,command";
151127538Sgadstatic char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,"
152127538Sgad			"tt,time,command";
15390143Smarkmstatic char   o1[] = "pid";
15497875Sjmallettstatic char   o2[] = "tt,state,time,command";
15597875Sjmallettstatic char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
156127538Sgadstatic char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,"
157127538Sgad			"%cpu,%mem,command";
158105831Srwatsonstatic char Zfmt[] = "label";
1591556Srgrimes
160127507Sgad#define	PS_ARGS	"AaCc" OPT_LAZY_f "G:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ"
16198494Ssobomax
1621556Srgrimesint
16390110Simpmain(int argc, char *argv[])
1641556Srgrimes{
165127499Sgad	struct listinfo gidlist, pgrplist, pidlist;
166127499Sgad	struct listinfo ruidlist, sesslist, ttylist, uidlist;
1671556Srgrimes	struct kinfo_proc *kp;
1681556Srgrimes	struct varent *vent;
1691556Srgrimes	struct winsize ws;
170127539Sgad	const char *cp, *nlistf, *memf;
171127539Sgad	char *cols;
172127499Sgad	int all, ch, dropgid, elem, flag, _fmt, i, lineno;
173127499Sgad	int nentries, nocludge, nkept, nselectors;
174127499Sgad	int prtheader, showthreads, wflag, what, xkeep, xkeep_implied;
17590143Smarkm	char errbuf[_POSIX2_LINE_MAX];
1761556Srgrimes
17711809Sache	(void) setlocale(LC_ALL, "");
178127542Sgad	time(&now);			/* Used by routines in print.c. */
17911809Sache
18097804Stjr	if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0')
18197804Stjr		termwidth = atoi(cols);
18297804Stjr	else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
1831556Srgrimes	     ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
1841556Srgrimes	     ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) == -1) ||
1851556Srgrimes	     ws.ws_col == 0)
1861556Srgrimes		termwidth = 79;
1871556Srgrimes	else
1881556Srgrimes		termwidth = ws.ws_col - 1;
1891556Srgrimes
19098494Ssobomax	/*
19198494Ssobomax	 * Don't apply a kludge if the first argument is an option taking an
19298494Ssobomax	 * argument
19398494Ssobomax	 */
19498494Ssobomax	if (argc > 1) {
19598494Ssobomax		nocludge = 0;
19698494Ssobomax		if (argv[1][0] == '-') {
19798494Ssobomax			for (cp = PS_ARGS; *cp != '\0'; cp++) {
19898494Ssobomax				if (*cp != ':')
19998494Ssobomax					continue;
20098494Ssobomax				if (*(cp - 1) == argv[1][1]) {
20198494Ssobomax					nocludge = 1;
20298494Ssobomax					break;
20398494Ssobomax				}
20498494Ssobomax			}
20598494Ssobomax		}
20698494Ssobomax		if (nocludge == 0)
20798494Ssobomax			argv[1] = kludge_oldps_options(argv[1]);
20898494Ssobomax	}
2091556Srgrimes
210127542Sgad	all = dropgid = _fmt = nselectors = optfatal = 0;
211127542Sgad	prtheader = showthreads = wflag = xkeep_implied = 0;
212127542Sgad	xkeep = -1;			/* Neither -x nor -X. */
213127499Sgad	init_list(&gidlist, addelem_gid, sizeof(gid_t), "group");
214127499Sgad	init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group");
215127499Sgad	init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id");
216127499Sgad	init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser");
217127499Sgad	init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id");
218127499Sgad	init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty");
219127499Sgad	init_list(&uidlist, addelem_uid, sizeof(uid_t), "user");
22089909Sru	memf = nlistf = _PATH_DEVNULL;
22198494Ssobomax	while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
2221556Srgrimes		switch((char)ch) {
223127499Sgad		case 'A':
224127499Sgad			/*
225127499Sgad			 * Exactly the same as `-ax'.   This has been
226127499Sgad			 * added for compatability with SUSv3, but for
227127499Sgad			 * now it will not be described in the man page.
228127499Sgad			 */
229127499Sgad			nselectors++;
230127499Sgad			all = xkeep = 1;
231127499Sgad			break;
2321556Srgrimes		case 'a':
233127499Sgad			nselectors++;
2341556Srgrimes			all = 1;
2351556Srgrimes			break;
23619068Speter		case 'C':
23719068Speter			rawcpu = 1;
23819068Speter			break;
23919068Speter		case 'c':
24019068Speter			cflag = 1;
24119068Speter			break;
2421556Srgrimes		case 'e':			/* XXX set ufmt */
2431556Srgrimes			needenv = 1;
2441556Srgrimes			break;
245127506Sgad#ifdef LAZY_PS
246127506Sgad		case 'f':
247127506Sgad			if (getuid() == 0 || getgid() == 0)
248127542Sgad				forceuread = 1;
249127506Sgad			break;
250127506Sgad#endif
251127499Sgad		case 'G':
252127499Sgad			add_list(&gidlist, optarg);
253127499Sgad			xkeep_implied = 1;
254127499Sgad			nselectors++;
255127499Sgad			break;
256127542Sgad		case 'g':
257127499Sgad#if 0
258127597Sgad			/*-
259127542Sgad			 * XXX - This SUSv3 behavior is still under debate
260127542Sgad			 *	since it conflicts with the (undocumented)
261127542Sgad			 *	`-g' option.  So we skip it for now.
262127542Sgad			 */
263127499Sgad			add_list(&pgrplist, optarg);
264127499Sgad			xkeep_implied = 1;
265127499Sgad			nselectors++;
266127499Sgad			break;
267127499Sgad#else
268127542Sgad			/* The historical BSD-ish (from SunOS) behavior. */
2691556Srgrimes			break;			/* no-op */
270127499Sgad#endif
271116265Sscottl		case 'H':
272126127Sdeischen			showthreads = KERN_PROC_INC_THREAD;
273116265Sscottl			break;
2741556Srgrimes		case 'h':
2751556Srgrimes			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
2761556Srgrimes			break;
2771556Srgrimes		case 'j':
278109502Sjmallett			parsefmt(jfmt, 0);
27990143Smarkm			_fmt = 1;
2801556Srgrimes			jfmt[0] = '\0';
2811556Srgrimes			break;
2821556Srgrimes		case 'L':
2831556Srgrimes			showkey();
2841556Srgrimes			exit(0);
2851556Srgrimes		case 'l':
286109502Sjmallett			parsefmt(lfmt, 0);
28790143Smarkm			_fmt = 1;
2881556Srgrimes			lfmt[0] = '\0';
2891556Srgrimes			break;
2901556Srgrimes		case 'M':
2911556Srgrimes			memf = optarg;
29237317Sphk			dropgid = 1;
2931556Srgrimes			break;
2941556Srgrimes		case 'm':
2951556Srgrimes			sortby = SORTMEM;
2961556Srgrimes			break;
2971556Srgrimes		case 'N':
2981556Srgrimes			nlistf = optarg;
29937317Sphk			dropgid = 1;
3001556Srgrimes			break;
3011556Srgrimes		case 'O':
302109502Sjmallett			parsefmt(o1, 1);
303109502Sjmallett			parsefmt(optarg, 1);
304109502Sjmallett			parsefmt(o2, 1);
3051556Srgrimes			o1[0] = o2[0] = '\0';
30690143Smarkm			_fmt = 1;
3071556Srgrimes			break;
3081556Srgrimes		case 'o':
309109502Sjmallett			parsefmt(optarg, 1);
31090143Smarkm			_fmt = 1;
3111556Srgrimes			break;
3121556Srgrimes		case 'p':
313127499Sgad			add_list(&pidlist, optarg);
314127499Sgad			/*
315127499Sgad			 * Note: `-p' does not *set* xkeep, but any values
316127499Sgad			 * from pidlist are checked before xkeep is.  That
317127499Sgad			 * way they are always matched, even if the user
318127499Sgad			 * specifies `-X'.
319127499Sgad			 */
320127499Sgad			nselectors++;
3211556Srgrimes			break;
322127499Sgad#if 0
323127499Sgad		case 'R':
324127597Sgad			/*-
325127542Sgad			 * XXX - This un-standard option is still under
326127542Sgad			 *	debate.  This is what SUSv3 defines as
327127542Sgad			 *	the `-U' option, and while it would be
328127542Sgad			 *	nice to have, it could cause even more
329127542Sgad			 *	confusion to implement it as `-R'.
330127542Sgad			 */
331127499Sgad			add_list(&ruidlist, optarg);
332127499Sgad			xkeep_implied = 1;
333127499Sgad			nselectors++;
334127499Sgad			break;
335127499Sgad#endif
3361556Srgrimes		case 'r':
3371556Srgrimes			sortby = SORTCPU;
3381556Srgrimes			break;
3391556Srgrimes		case 'S':
3401556Srgrimes			sumrusage = 1;
3411556Srgrimes			break;
342127499Sgad#if 0
343127499Sgad		case 's':
344127597Sgad			/*-
345127542Sgad			 * XXX - This non-standard option is still under
346127542Sgad			 *	debate.  This *is* supported on Solaris,
347127542Sgad			 *	Linux, and IRIX, but conflicts with `-s'
348127542Sgad			 *	on NetBSD and maybe some older BSD's.
349127542Sgad			 */
350127499Sgad			add_list(&sesslist, optarg);
351127499Sgad			xkeep_implied = 1;
352127499Sgad			nselectors++;
353127499Sgad			break;
354127499Sgad#endif
3551556Srgrimes		case 'T':
3561556Srgrimes			if ((optarg = ttyname(STDIN_FILENO)) == NULL)
3571556Srgrimes				errx(1, "stdin: not a terminal");
3581556Srgrimes			/* FALLTHROUGH */
359127499Sgad		case 't':
360127499Sgad			add_list(&ttylist, optarg);
361127499Sgad			xkeep_implied = 1;
362127499Sgad			nselectors++;
3631556Srgrimes			break;
36413020Speter		case 'U':
365127499Sgad			/* This is what SUSv3 defines as the `-u' option. */
366127499Sgad			add_list(&uidlist, optarg);
367127499Sgad			xkeep_implied = 1;
368127499Sgad			nselectors++;
36913020Speter			break;
3701556Srgrimes		case 'u':
371109502Sjmallett			parsefmt(ufmt, 0);
3721556Srgrimes			sortby = SORTCPU;
37390143Smarkm			_fmt = 1;
3741556Srgrimes			ufmt[0] = '\0';
3751556Srgrimes			break;
3761556Srgrimes		case 'v':
377109502Sjmallett			parsefmt(vfmt, 0);
3781556Srgrimes			sortby = SORTMEM;
37990143Smarkm			_fmt = 1;
3801556Srgrimes			vfmt[0] = '\0';
3811556Srgrimes			break;
3821556Srgrimes		case 'w':
3831556Srgrimes			if (wflag)
3841556Srgrimes				termwidth = UNLIMITED;
3851556Srgrimes			else if (termwidth < 131)
3861556Srgrimes				termwidth = 131;
3871556Srgrimes			wflag++;
3881556Srgrimes			break;
389127499Sgad		case 'X':
390127499Sgad			/*
391127499Sgad			 * Note that `-X' and `-x' are not standard "selector"
392127499Sgad			 * options. For most selector-options, we check *all*
393127499Sgad			 * processes to see if any are matched by the given
394127499Sgad			 * value(s).  After we have a set of all the matched
395127499Sgad			 * processes, then `-X' and `-x' govern whether we
396127499Sgad			 * modify that *matched* set for processes which do
397127499Sgad			 * not have a controlling terminal.  `-X' causes
398127499Sgad			 * those processes to be deleted from the matched
399127499Sgad			 * set, while `-x' causes them to be kept.
400127499Sgad			 */
401127499Sgad			xkeep = 0;
402127499Sgad			break;
4031556Srgrimes		case 'x':
404127499Sgad			xkeep = 1;
4051556Srgrimes			break;
40686922Sgreen		case 'Z':
407109502Sjmallett			parsefmt(Zfmt, 0);
40886922Sgreen			Zfmt[0] = '\0';
40986922Sgreen			break;
4101556Srgrimes		case '?':
4111556Srgrimes		default:
4121556Srgrimes			usage();
4131556Srgrimes		}
4141556Srgrimes	argc -= optind;
4151556Srgrimes	argv += optind;
416127499Sgad	if (optfatal)
417127542Sgad		exit(1);		/* Error messages already printed. */
418127542Sgad	if (xkeep < 0)			/* Neither -X nor -x was specified. */
419127499Sgad		xkeep = xkeep_implied;
420127499Sgad
4211556Srgrimes#define	BACKWARD_COMPATIBILITY
4221556Srgrimes#ifdef	BACKWARD_COMPATIBILITY
4231556Srgrimes	if (*argv) {
4241556Srgrimes		nlistf = *argv;
425127542Sgad		if (*++argv)
4261556Srgrimes			memf = *argv;
4271556Srgrimes	}
4281556Srgrimes#endif
4291556Srgrimes	/*
4301556Srgrimes	 * Discard setgid privileges if not the running kernel so that bad
4311556Srgrimes	 * guys can't print interesting stuff from kernel memory.
4321556Srgrimes	 */
43337317Sphk	if (dropgid) {
4341556Srgrimes		setgid(getgid());
43537317Sphk		setuid(getuid());
43637317Sphk	}
4371556Srgrimes
43889909Sru	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
4391556Srgrimes	if (kd == 0)
4401556Srgrimes		errx(1, "%s", errbuf);
4411556Srgrimes
44290143Smarkm	if (!_fmt)
443109502Sjmallett		parsefmt(dfmt, 0);
4441556Srgrimes
445127499Sgad	if (nselectors == 0) {
446127499Sgad		uidlist.ptr = malloc(sizeof(uid_t));
447127499Sgad		if (uidlist.ptr == NULL)
44897877Sjmallett			errx(1, "malloc failed");
449127499Sgad		nselectors = 1;
450127499Sgad		uidlist.count = uidlist.maxcount = 1;
451127499Sgad		*uidlist.uids = getuid();
45266377Sbrian	}
4531556Srgrimes
4541556Srgrimes	/*
4551556Srgrimes	 * scan requested variables, noting what structures are needed,
45653170Skris	 * and adjusting header widths as appropriate.
4571556Srgrimes	 */
4581556Srgrimes	scanvars();
459127499Sgad
4601556Srgrimes	/*
461127499Sgad	 * Get process list.  If the user requested just one selector-
462127499Sgad	 * option, then kvm_getprocs can be asked to return just those
463127499Sgad	 * processes.  Otherwise, have it return all processes, and
464127499Sgad	 * then this routine will search that full list and select the
465127499Sgad	 * processes which match any of the user's selector-options.
4661556Srgrimes	 */
467127499Sgad	what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
468127499Sgad	flag = 0;
469127499Sgad	if (nselectors == 1) {
470127499Sgad		/* XXX - Apparently there's no KERN_PROC_GID flag. */
471127499Sgad		if (pgrplist.count == 1) {
472127499Sgad			what = KERN_PROC_PGRP | showthreads;
473127499Sgad			flag = *pgrplist.pids;
474127499Sgad			nselectors = 0;
475127499Sgad		} else if (pidlist.count == 1) {
476127499Sgad			what = KERN_PROC_PID | showthreads;
477127499Sgad			flag = *pidlist.pids;
478127499Sgad			nselectors = 0;
479127499Sgad		} else if (ruidlist.count == 1) {
480127499Sgad			what = KERN_PROC_RUID | showthreads;
481127499Sgad			flag = *ruidlist.uids;
482127499Sgad			nselectors = 0;
483127542Sgad#if 0
484127597Sgad		/*-
485127542Sgad		 * XXX - KERN_PROC_SESSION causes error in kvm_getprocs?
486127542Sgad		 *	For now, always do sid-matching in this routine.
487127542Sgad		 */
488127499Sgad		} else if (sesslist.count == 1) {
489127499Sgad			what = KERN_PROC_SESSION | showthreads;
490127499Sgad			flag = *sesslist.pids;
491127499Sgad			nselectors = 0;
492127499Sgad#endif
493127499Sgad		} else if (ttylist.count == 1) {
494127499Sgad			what = KERN_PROC_TTY | showthreads;
495127499Sgad			flag = *ttylist.ttys;
496127499Sgad			nselectors = 0;
497127499Sgad		} else if (uidlist.count == 1) {
498127499Sgad			what = KERN_PROC_UID | showthreads;
499127499Sgad			flag = *uidlist.uids;
500127499Sgad			nselectors = 0;
501127499Sgad		} else if (all) {
502127499Sgad			/* No need for this routine to select processes. */
503127499Sgad			nselectors = 0;
504127499Sgad		}
5051556Srgrimes	}
506126127Sdeischen
5071556Srgrimes	/*
5081556Srgrimes	 * select procs
5091556Srgrimes	 */
510127499Sgad	nentries = -1;
511127149Sgad	kp = kvm_getprocs(kd, what, flag, &nentries);
512127544Sgad	if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0))
5131556Srgrimes		errx(1, "%s", kvm_geterr(kd));
514127499Sgad	nkept = 0;
515127149Sgad	if (nentries > 0) {
516127149Sgad		if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
517127149Sgad			errx(1, "malloc failed");
518127149Sgad		for (i = nentries; --i >= 0; ++kp) {
519127499Sgad			/*
520127499Sgad			 * If the user specified multiple selection-criteria,
521127499Sgad			 * then keep any process matched by the inclusive OR
522127499Sgad			 * of all the selection-criteria given.
523127499Sgad			 */
524127499Sgad			if (pidlist.count > 0) {
525127499Sgad				for (elem = 0; elem < pidlist.count; elem++)
526127499Sgad					if (kp->ki_pid == pidlist.pids[elem])
527127499Sgad						goto keepit;
528127499Sgad			}
529127499Sgad			/*
530127499Sgad			 * Note that we had to process pidlist before
531127499Sgad			 * filtering out processes which do not have
532127499Sgad			 * a controlling terminal.
533127499Sgad			 */
534127499Sgad			if (xkeep == 0) {
535127499Sgad				if ((kp->ki_tdev == NODEV ||
536127499Sgad				    (kp->ki_flag & P_CONTROLT) == 0))
537127499Sgad					continue;
538127499Sgad			}
539127499Sgad			if (nselectors == 0)
540127499Sgad				goto keepit;
541127499Sgad			if (gidlist.count > 0) {
542127499Sgad				for (elem = 0; elem < gidlist.count; elem++)
543127499Sgad					if (kp->ki_rgid == gidlist.gids[elem])
544127499Sgad						goto keepit;
545127499Sgad			}
546127499Sgad			if (pgrplist.count > 0) {
547127499Sgad				for (elem = 0; elem < pgrplist.count; elem++)
548127499Sgad					if (kp->ki_pgid == pgrplist.pids[elem])
549127499Sgad						goto keepit;
550127499Sgad			}
551127499Sgad			if (ruidlist.count > 0) {
552127499Sgad				for (elem = 0; elem < ruidlist.count; elem++)
553127499Sgad					if (kp->ki_ruid == ruidlist.uids[elem])
554127499Sgad						goto keepit;
555127499Sgad			}
556127499Sgad			if (sesslist.count > 0) {
557127499Sgad				for (elem = 0; elem < sesslist.count; elem++)
558127499Sgad					if (kp->ki_sid == sesslist.pids[elem])
559127499Sgad						goto keepit;
560127499Sgad			}
561127499Sgad			if (ttylist.count > 0) {
562127499Sgad				for (elem = 0; elem < ttylist.count; elem++)
563127499Sgad					if (kp->ki_tdev == ttylist.ttys[elem])
564127499Sgad						goto keepit;
565127499Sgad			}
566127499Sgad			if (uidlist.count > 0) {
567127499Sgad				for (elem = 0; elem < uidlist.count; elem++)
568127499Sgad					if (kp->ki_uid == uidlist.uids[elem])
569127499Sgad						goto keepit;
570127499Sgad			}
571127499Sgad			/*
572127499Sgad			 * This process did not match any of the user's
573127499Sgad			 * selector-options, so skip the process.
574127499Sgad			 */
575127499Sgad			continue;
576127499Sgad
577127499Sgad		keepit:
578127499Sgad			kinfo[nkept].ki_p = kp;
579127149Sgad			if (needuser)
580127499Sgad				saveuser(&kinfo[nkept]);
581127499Sgad			dynsizevars(&kinfo[nkept]);
582127499Sgad			nkept++;
583127149Sgad		}
5841556Srgrimes	}
58525271Sjkh
58625271Sjkh	sizevars();
58725271Sjkh
5881556Srgrimes	/*
5891556Srgrimes	 * print header
5901556Srgrimes	 */
5911556Srgrimes	printheader();
592127499Sgad	if (nkept == 0)
59362803Swill		exit(1);
594127499Sgad
5951556Srgrimes	/*
5961556Srgrimes	 * sort proc list
5971556Srgrimes	 */
598127499Sgad	qsort(kinfo, nkept, sizeof(KINFO), pscomp);
5991556Srgrimes	/*
600127499Sgad	 * For each process, call each variable output function.
6011556Srgrimes	 */
602127499Sgad	for (i = lineno = 0; i < nkept; i++) {
6031556Srgrimes		for (vent = vhead; vent; vent = vent->next) {
6041556Srgrimes			(vent->var->oproc)(&kinfo[i], vent);
6051556Srgrimes			if (vent->next != NULL)
6061556Srgrimes				(void)putchar(' ');
6071556Srgrimes		}
6081556Srgrimes		(void)putchar('\n');
6091556Srgrimes		if (prtheader && lineno++ == prtheader - 4) {
6101556Srgrimes			(void)putchar('\n');
6111556Srgrimes			printheader();
6121556Srgrimes			lineno = 0;
6131556Srgrimes		}
6141556Srgrimes	}
615127499Sgad	free_list(&gidlist);
616127499Sgad	free_list(&pidlist);
617127499Sgad	free_list(&pgrplist);
618127499Sgad	free_list(&ruidlist);
619127499Sgad	free_list(&sesslist);
620127499Sgad	free_list(&ttylist);
621127499Sgad	free_list(&uidlist);
62266377Sbrian
6231556Srgrimes	exit(eval);
6241556Srgrimes}
6251556Srgrimes
626127499Sgadstatic int
627127499Sgadaddelem_gid(struct listinfo *inf, const char *elem)
628127499Sgad{
629127499Sgad	struct group *grp;
630127499Sgad	const char *nameorID;
631127499Sgad	char *endp;
632127539Sgad	intmax_t ltemp;
633127499Sgad
634127499Sgad	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
635127499Sgad		if (*elem == '\0')
636127499Sgad			warnx("Invalid (zero-length) %s name", inf->lname);
637127499Sgad		else
638127499Sgad			warnx("%s name too long: %s", inf->lname, elem);
639127499Sgad		optfatal = 1;
640127542Sgad		return (0);		/* Do not add this value. */
641127499Sgad	}
642127499Sgad
643127499Sgad	/*
644127499Sgad	 * SUSv3 states that `ps -G grouplist' should match "real-group
645127499Sgad	 * ID numbers", and does not mention group-names.  I do want to
646127499Sgad	 * also support group-names, so this tries for a group-id first,
647127499Sgad	 * and only tries for a name if that doesn't work.  This is the
648127499Sgad	 * opposite order of what is done in addelem_uid(), but in
649127499Sgad	 * practice the order would only matter for group-names which
650127499Sgad	 * are all-numeric.
651127499Sgad	 */
652127499Sgad	grp = NULL;
653127499Sgad	nameorID = "named";
654127499Sgad	errno = 0;
655127499Sgad	ltemp = strtol(elem, &endp, 10);
656127499Sgad	if (errno == 0 && *endp == '\0' && ltemp >= 0 && ltemp <= GID_MAX) {
657127499Sgad		nameorID = "name or ID matches";
658127499Sgad		grp = getgrgid((gid_t)ltemp);
659127499Sgad	}
660127499Sgad	if (grp == NULL)
661127499Sgad		grp = getgrnam(elem);
662127499Sgad	if (grp == NULL) {
663127499Sgad		warnx("No %s %s '%s'", inf->lname, nameorID, elem);
664127499Sgad		optfatal = 1;
665127542Sgad		return (0);		/* Do not add this value. */
666127499Sgad	}
667127499Sgad
668127499Sgad	if (inf->count >= inf->maxcount)
669127499Sgad		expand_list(inf);
670127499Sgad	inf->gids[(inf->count)++] = grp->gr_gid;
671127499Sgad	return (1);
672127499Sgad}
673127499Sgad
674127597Sgad#define	BSD_PID_MAX	99999		/* Copy of PID_MAX from sys/proc.h. */
675127499Sgadstatic int
676127499Sgadaddelem_pid(struct listinfo *inf, const char *elem)
677127149Sgad{
678127539Sgad	char *endp;
679127149Sgad	long tempid;
680127149Sgad
681127499Sgad	if (*elem == '\0')
682127499Sgad		tempid = 0L;
683127499Sgad	else {
684127499Sgad		errno = 0;
685127499Sgad		tempid = strtol(elem, &endp, 10);
686127499Sgad		if (*endp != '\0' || tempid < 0 || elem == endp) {
687127499Sgad			warnx("Invalid %s: %s", inf->lname, elem);
688127499Sgad			errno = ERANGE;
689127499Sgad		} else if (errno != 0 || tempid > BSD_PID_MAX) {
690127499Sgad			warnx("%s too large: %s", inf->lname, elem);
691127499Sgad			errno = ERANGE;
692127149Sgad		}
693127499Sgad		if (errno == ERANGE) {
694127499Sgad			optfatal = 1;
695127542Sgad			return (0);	/* Do not add this value. */
696127149Sgad		}
697127149Sgad	}
698127149Sgad
699127499Sgad	if (inf->count >= inf->maxcount)
700127499Sgad		expand_list(inf);
701127499Sgad	inf->pids[(inf->count)++] = tempid;
702127499Sgad	return (1);
703127499Sgad}
704127499Sgad#undef	BSD_PID_MAX
705127149Sgad
706127499Sgadstatic int
707127499Sgadaddelem_tty(struct listinfo *inf, const char *elem)
708127499Sgad{
709127539Sgad	const char *ttypath;
710127539Sgad	struct stat sb;
711127499Sgad	char pathbuf[PATH_MAX];
712127499Sgad
713127499Sgad	if (strcmp(elem, "co") == 0)
714127499Sgad		ttypath = strdup(_PATH_CONSOLE);
715127499Sgad	else if (*elem == '/')
716127499Sgad		ttypath = elem;
717127499Sgad	else {
718127499Sgad		strlcpy(pathbuf, _PATH_TTY, sizeof(pathbuf));
719127499Sgad		strlcat(pathbuf, elem, sizeof(pathbuf));
720127499Sgad		ttypath = pathbuf;
721127499Sgad	}
722127499Sgad
723127499Sgad	if (stat(ttypath, &sb) == -1) {
724127499Sgad		warn("%s", ttypath);
725127499Sgad		optfatal = 1;
726127542Sgad		return (0);		/* Do not add this value. */
727127499Sgad	}
728127499Sgad	if (!S_ISCHR(sb.st_mode)) {
729127499Sgad		warn("%s: Not a terminal", ttypath);
730127499Sgad		optfatal = 1;
731127542Sgad		return (0);		/* Do not add this value. */
732127499Sgad	}
733127499Sgad
734127499Sgad	if (inf->count >= inf->maxcount)
735127499Sgad		expand_list(inf);
736127499Sgad	inf->ttys[(inf->count)++] = sb.st_rdev;
737127499Sgad	return (1);
738127149Sgad}
739127149Sgad
740127499Sgadstatic int
741127499Sgadaddelem_uid(struct listinfo *inf, const char *elem)
74266377Sbrian{
74366377Sbrian	struct passwd *pwd;
744127539Sgad	char *endp;
745127499Sgad	intmax_t ltemp;
74666377Sbrian
747127499Sgad	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
748127499Sgad		if (*elem == '\0')
749127499Sgad			warnx("Invalid (zero-length) %s name", inf->lname);
750127499Sgad		else
751127499Sgad			warnx("%s name too long: %s", inf->lname, elem);
752127499Sgad		optfatal = 1;
753127542Sgad		return (0);		/* Do not add this value. */
754127499Sgad	}
75566377Sbrian
756127499Sgad	pwd = getpwnam(elem);
757127499Sgad	if (pwd == NULL) {
758127499Sgad		errno = 0;
759127499Sgad		ltemp = strtol(elem, &endp, 10);
760127499Sgad		if (errno != 0 || *endp != '\0' || ltemp < 0 ||
761127499Sgad		    ltemp > UID_MAX)
762127499Sgad			warnx("No %s named '%s'", inf->lname, elem);
763127499Sgad		else {
764127499Sgad			/* The string is all digits, so it might be a userID. */
765127499Sgad			pwd = getpwuid((uid_t)ltemp);
766127499Sgad			if (pwd == NULL)
767127499Sgad				warnx("No %s name or ID matches '%s'",
768127499Sgad				    inf->lname, elem);
76966377Sbrian		}
770127499Sgad	}
771127499Sgad	if (pwd == NULL) {
772127509Sgad		/*
773127509Sgad		 * These used to be treated as minor warnings (and the
774127509Sgad		 * option was simply ignored), but now they are fatal
775127509Sgad		 * errors (and the command will be aborted).
776127509Sgad		 */
777127509Sgad		optfatal = 1;
778127542Sgad		return (0);		/* Do not add this value. */
779127499Sgad	}
780127499Sgad
781127499Sgad	if (inf->count >= inf->maxcount)
782127499Sgad		expand_list(inf);
783127499Sgad	inf->uids[(inf->count)++] = pwd->pw_uid;
784127499Sgad	return (1);
785127499Sgad}
786127499Sgad
787127499Sgadstatic void
788127499Sgadadd_list(struct listinfo *inf, const char *argp)
789127499Sgad{
790127499Sgad	const char *savep;
791127499Sgad	char *cp, *endp;
792127499Sgad	int toolong;
793127539Sgad	char elemcopy[PATH_MAX];
794127499Sgad
795127499Sgad	while (*argp != '\0') {
796127499Sgad		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
797127499Sgad			argp++;
798127499Sgad		savep = argp;
799127499Sgad		toolong = 0;
800127499Sgad		cp = elemcopy;
801127499Sgad		if (strchr(T_SEP, *argp) == NULL) {
802127499Sgad			endp = elemcopy + sizeof(elemcopy) - 1;
803127499Sgad			while (*argp != '\0' && cp <= endp &&
804127499Sgad			    strchr(W_SEP T_SEP, *argp) == NULL)
805127499Sgad				*cp++ = *argp++;
806127499Sgad			if (cp > endp)
807127499Sgad				toolong = 1;
80866377Sbrian		}
809127499Sgad		if (!toolong) {
810127499Sgad			*cp = '\0';
811127499Sgad#ifndef ADD_PS_LISTRESET
812127542Sgad			/*
813127542Sgad			 * This is how the standard expects lists to
814127542Sgad			 * be handled.
815127542Sgad			 */
816127499Sgad			inf->addelem(inf, elemcopy);
817127499Sgad#else
818127597Sgad			/*-
819127542Sgad			 * This would add a simple non-standard-but-convienent
820127542Sgad			 * feature.
821127542Sgad			 *
822127542Sgad			 * XXX - The first time I tried to add this check,
823127542Sgad			 *	it increased the total size of `ps' by 3940
824127542Sgad			 *	bytes on i386!  That's 12% of the entire
825127542Sgad			 *	program!  The `ps.o' file grew by only about
826127542Sgad			 *	40 bytes, but the final (stripped) executable
827127542Sgad			 *	in /bin/ps grew by 12%.  I have not had time
828127542Sgad			 *	to investigate, so skip the feature for now.
829127542Sgad			 */
830127542Sgad			/*
831127499Sgad			 * We now have a single element.  Add it to the
832127499Sgad			 * list, unless the element is ":".  In that case,
833127499Sgad			 * reset the list so previous entries are ignored.
834127499Sgad			 */
835127499Sgad			if (strcmp(elemcopy, ":") == 0)
836127499Sgad				inf->count = 0;
837127499Sgad			else
838127499Sgad				inf->addelem(inf, elemcopy);
839127499Sgad#endif
840127499Sgad		} else {
841127499Sgad			/*
842127499Sgad			 * The string is too long to copy.  Find the end
843127499Sgad			 * of the string to print out the warning message.
844127499Sgad			 */
845127499Sgad			while (*argp != '\0' && strchr(W_SEP T_SEP,
846127499Sgad			    *argp) == NULL)
847127499Sgad				argp++;
848127499Sgad			warnx("Value too long: %.*s", (int)(argp - savep),
849127499Sgad			    savep);
850127499Sgad			optfatal = 1;
85166377Sbrian		}
852127499Sgad		/*
853127499Sgad		 * Skip over any number of trailing whitespace characters,
854127499Sgad		 * but only one (at most) trailing element-terminating
855127499Sgad		 * character.
856127499Sgad		 */
857127499Sgad		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
858127499Sgad			argp++;
859127499Sgad		if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) {
860127499Sgad			argp++;
861127499Sgad			/* Catch case where string ended with a comma. */
862127499Sgad			if (*argp == '\0')
863127499Sgad				inf->addelem(inf, argp);
864127499Sgad		}
86566377Sbrian	}
866127499Sgad}
86766377Sbrian
868127499Sgadstatic void *
869127499Sgadexpand_list(struct listinfo *inf)
870127499Sgad{
871127539Sgad	void *newlist;
872127499Sgad	int newmax;
87366377Sbrian
874127499Sgad	newmax = (inf->maxcount + 1) << 1;
875127499Sgad	newlist = realloc(inf->ptr, newmax * inf->elemsize);
876127499Sgad	if (newlist == NULL) {
877127499Sgad		free(inf->ptr);
878127499Sgad		errx(1, "realloc to %d %ss failed", newmax,
879127499Sgad		    inf->lname);
880127499Sgad	}
881127499Sgad	inf->maxcount = newmax;
882127499Sgad	inf->ptr = newlist;
883127499Sgad
884127499Sgad	return (newlist);
88566377Sbrian}
88666377Sbrian
887127499Sgadstatic void
888127499Sgadfree_list(struct listinfo *inf)
889127499Sgad{
890127499Sgad
891127499Sgad	inf->count = inf->elemsize = inf->maxcount = 0;
892127499Sgad	if (inf->ptr != NULL)
893127499Sgad		free(inf->ptr);
894127499Sgad	inf->addelem = NULL;
895127499Sgad	inf->lname = NULL;
896127499Sgad	inf->ptr = NULL;
897127499Sgad}
898127499Sgad
899127499Sgadstatic void
900127499Sgadinit_list(struct listinfo *inf, addelem_rtn artn, int elemsize,
901127499Sgad    const char *lname)
902127499Sgad{
903127499Sgad
904127499Sgad	inf->count = inf->maxcount = 0;
905127499Sgad	inf->elemsize = elemsize;
906127499Sgad	inf->addelem = artn;
907127499Sgad	inf->lname = lname;
908127499Sgad	inf->ptr = NULL;
909127499Sgad}
910127499Sgad
911109502SjmallettVARENT *
912109502Sjmallettfind_varentry(VAR *v)
913109502Sjmallett{
914109502Sjmallett	struct varent *vent;
915109502Sjmallett
916109502Sjmallett	for (vent = vhead; vent; vent = vent->next) {
917109502Sjmallett		if (strcmp(vent->var->name, v->name) == 0)
918109502Sjmallett			return vent;
919109502Sjmallett	}
920109502Sjmallett	return NULL;
921109502Sjmallett}
922109502Sjmallett
9231556Srgrimesstatic void
92490110Simpscanvars(void)
9251556Srgrimes{
9261556Srgrimes	struct varent *vent;
9271556Srgrimes	VAR *v;
92825271Sjkh
92925271Sjkh	for (vent = vhead; vent; vent = vent->next) {
93025271Sjkh		v = vent->var;
93125271Sjkh		if (v->flag & DSIZ) {
93225271Sjkh			v->dwidth = v->width;
93325271Sjkh			v->width = 0;
93425271Sjkh		}
93525271Sjkh		if (v->flag & USER)
93625271Sjkh			needuser = 1;
93725271Sjkh		if (v->flag & COMM)
93825271Sjkh			needcomm = 1;
93925271Sjkh	}
94025271Sjkh}
94125271Sjkh
94225271Sjkhstatic void
94390110Simpdynsizevars(KINFO *ki)
94425271Sjkh{
94525271Sjkh	struct varent *vent;
94625271Sjkh	VAR *v;
9471556Srgrimes	int i;
9481556Srgrimes
9491556Srgrimes	for (vent = vhead; vent; vent = vent->next) {
9501556Srgrimes		v = vent->var;
95125271Sjkh		if (!(v->flag & DSIZ))
95225271Sjkh			continue;
95325271Sjkh		i = (v->sproc)( ki);
95425271Sjkh		if (v->width < i)
95525271Sjkh			v->width = i;
95625271Sjkh		if (v->width > v->dwidth)
95725271Sjkh			v->width = v->dwidth;
95825271Sjkh	}
95925271Sjkh}
96025271Sjkh
96125271Sjkhstatic void
96290110Simpsizevars(void)
96325271Sjkh{
96425271Sjkh	struct varent *vent;
96525271Sjkh	VAR *v;
96625271Sjkh	int i;
96725271Sjkh
96825271Sjkh	for (vent = vhead; vent; vent = vent->next) {
96925271Sjkh		v = vent->var;
970109504Sjmallett		i = strlen(vent->header);
9711556Srgrimes		if (v->width < i)
9721556Srgrimes			v->width = i;
9731556Srgrimes		totwidth += v->width + 1;	/* +1 for space */
9741556Srgrimes	}
9751556Srgrimes	totwidth--;
9761556Srgrimes}
9771556Srgrimes
97890143Smarkmstatic const char *
97990110Simpfmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki,
980127542Sgad    char *comm, int maxlen)
9811556Srgrimes{
98290143Smarkm	const char *s;
9831556Srgrimes
98490143Smarkm	s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen);
9851556Srgrimes	return (s);
9861556Srgrimes}
9871556Srgrimes
98871578Sjhb#define UREADOK(ki)	(forceuread || (ki->ki_p->ki_sflag & PS_INMEM))
98931552Sdyson
9901556Srgrimesstatic void
99190110Simpsaveuser(KINFO *ki)
9921556Srgrimes{
9931556Srgrimes
99471578Sjhb	if (ki->ki_p->ki_sflag & PS_INMEM) {
9951556Srgrimes		/*
9961556Srgrimes		 * The u-area might be swapped out, and we can't get
9971556Srgrimes		 * at it because we have a crashdump and no swap.
9981556Srgrimes		 * If it's here fill in these fields, otherwise, just
9991556Srgrimes		 * leave them 0.
10001556Srgrimes		 */
100169896Smckusick		ki->ki_valid = 1;
10021556Srgrimes	} else
100369896Smckusick		ki->ki_valid = 0;
10041556Srgrimes	/*
10051556Srgrimes	 * save arguments if needed
10061556Srgrimes	 */
100769896Smckusick	if (needcomm && (UREADOK(ki) || (ki->ki_p->ki_args != NULL))) {
100890143Smarkm		ki->ki_args = strdup(fmt(kvm_getargv, ki, ki->ki_p->ki_comm,
100990143Smarkm		    MAXCOMLEN));
101031552Sdyson	} else if (needcomm) {
101190143Smarkm		asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm);
101253276Speter	} else {
101353276Speter		ki->ki_args = NULL;
101453276Speter	}
101553276Speter	if (needenv && UREADOK(ki)) {
101690143Smarkm		ki->ki_env = strdup(fmt(kvm_getenvv, ki, (char *)NULL, 0));
101753276Speter	} else if (needenv) {
101853276Speter		ki->ki_env = malloc(3);
101953276Speter		strcpy(ki->ki_env, "()");
102053276Speter	} else {
102153276Speter		ki->ki_env = NULL;
102253276Speter	}
10231556Srgrimes}
10241556Srgrimes
10251556Srgrimesstatic int
102690110Simppscomp(const void *a, const void *b)
10271556Srgrimes{
1028127596Sgad	const KINFO *ka, *kb;
1029127596Sgad	double cpua, cpub;
1030127596Sgad	segsz_t sizea, sizeb;
10311556Srgrimes
1032127596Sgad	ka = a;
1033127596Sgad	kb = b;
1034127596Sgad	/* SORTCPU and SORTMEM are sorted in descending order. */
1035127596Sgad	if (sortby == SORTCPU) {
1036127596Sgad		cpua = getpcpu(ka);
1037127596Sgad		cpub = getpcpu(kb);
1038127596Sgad		if (cpua < cpub)
1039127596Sgad			return (1);
1040127596Sgad		if (cpua > cpub)
1041127596Sgad			return (-1);
1042127596Sgad	}
1043127596Sgad	if (sortby == SORTMEM) {
1044127596Sgad		sizea = ka->ki_p->ki_tsize + ka->ki_p->ki_dsize +
1045127596Sgad		    ka->ki_p->ki_ssize;
1046127596Sgad		sizeb = kb->ki_p->ki_tsize + kb->ki_p->ki_dsize +
1047127596Sgad		    kb->ki_p->ki_ssize;
1048127596Sgad		if (sizea < sizeb)
1049127596Sgad			return (1);
1050127596Sgad		if (sizea > sizeb)
1051127596Sgad			return (-1);
1052127596Sgad	}
1053127596Sgad	/*
1054127596Sgad	 * TTY's are sorted in ascending order, except that all NODEV
1055127596Sgad	 * processes come before all processes with a device.
1056127596Sgad	 */
1057127596Sgad	if (ka->ki_p->ki_tdev == NODEV && kb->ki_p->ki_tdev != NODEV)
1058127596Sgad		return (-1);
1059127596Sgad	if (ka->ki_p->ki_tdev != NODEV && kb->ki_p->ki_tdev == NODEV)
1060127596Sgad		return (1);
1061127596Sgad	if (ka->ki_p->ki_tdev < kb->ki_p->ki_tdev)
1062127596Sgad		return (-1);
1063127596Sgad	if (ka->ki_p->ki_tdev > kb->ki_p->ki_tdev)
1064127596Sgad		return (1);
1065127596Sgad	/* PID's are sorted in ascending order. */
1066127596Sgad	if (ka->ki_p->ki_pid < kb->ki_p->ki_pid)
1067127596Sgad		return (-1);
1068127596Sgad	if (ka->ki_p->ki_pid > kb->ki_p->ki_pid)
1069127596Sgad		return (1);
1070127596Sgad	return (0);
10711556Srgrimes}
10721556Srgrimes
10731556Srgrimes/*
10741556Srgrimes * ICK (all for getopt), would rather hide the ugliness
10751556Srgrimes * here than taint the main code.
10761556Srgrimes *
10771556Srgrimes *  ps foo -> ps -foo
10781556Srgrimes *  ps 34 -> ps -p34
10791556Srgrimes *
10801556Srgrimes * The old convention that 't' with no trailing tty arg means the users
10811556Srgrimes * tty, is only supported if argv[1] doesn't begin with a '-'.  This same
10821556Srgrimes * feature is available with the option 'T', which takes no argument.
10831556Srgrimes */
10841556Srgrimesstatic char *
108590110Simpkludge_oldps_options(char *s)
10861556Srgrimes{
1087102886Sjmallett	int have_fmt;
10881556Srgrimes	size_t len;
10891556Srgrimes	char *newopts, *ns, *cp;
10901556Srgrimes
1091102886Sjmallett	/*
1092102886Sjmallett	 * If we have an 'o' option, then note it, since we don't want to do
1093102886Sjmallett	 * some types of munging.
1094102886Sjmallett	 */
1095102886Sjmallett	have_fmt = index(s, 'o') != NULL;
1096102886Sjmallett
10971556Srgrimes	len = strlen(s);
10981556Srgrimes	if ((newopts = ns = malloc(len + 2)) == NULL)
109997877Sjmallett		errx(1, "malloc failed");
11001556Srgrimes	/*
11011556Srgrimes	 * options begin with '-'
11021556Srgrimes	 */
11031556Srgrimes	if (*s != '-')
11041556Srgrimes		*ns++ = '-';	/* add option flag */
11051556Srgrimes	/*
11061556Srgrimes	 * gaze to end of argv[1]
11071556Srgrimes	 */
11081556Srgrimes	cp = s + len - 1;
11091556Srgrimes	/*
11101556Srgrimes	 * if last letter is a 't' flag with no argument (in the context
11111556Srgrimes	 * of the oldps options -- option string NOT starting with a '-' --
11121556Srgrimes	 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
111381743Sbrian	 *
111481743Sbrian	 * However, if a flag accepting a string argument is found in the
111581743Sbrian	 * option string, the remainder of the string is the argument to
111681743Sbrian	 * that flag; do not modify that argument.
11171556Srgrimes	 */
111889909Sru	if (strcspn(s, "MNOoU") == len && *cp == 't' && *s != '-')
11191556Srgrimes		*cp = 'T';
11201556Srgrimes	else {
11211556Srgrimes		/*
11221556Srgrimes		 * otherwise check for trailing number, which *may* be a
11231556Srgrimes		 * pid.
11241556Srgrimes		 */
11251556Srgrimes		while (cp >= s && isdigit(*cp))
11261556Srgrimes			--cp;
11271556Srgrimes	}
11281556Srgrimes	cp++;
11291556Srgrimes	memmove(ns, s, (size_t)(cp - s));	/* copy up to trailing number */
11301556Srgrimes	ns += cp - s;
11311556Srgrimes	/*
11321556Srgrimes	 * if there's a trailing number, and not a preceding 'p' (pid) or
11331556Srgrimes	 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag.
11341556Srgrimes	 */
11358855Srgrimes	if (isdigit(*cp) &&
11367165Sjoerg	    (cp == s || (cp[-1] != 't' && cp[-1] != 'p')) &&
1137102886Sjmallett	    (cp - 1 == s || cp[-2] != 't') && !have_fmt)
11381556Srgrimes		*ns++ = 'p';
11391556Srgrimes	(void)strcpy(ns, cp);		/* and append the number */
11401556Srgrimes
11411556Srgrimes	return (newopts);
11421556Srgrimes}
11431556Srgrimes
11441556Srgrimesstatic void
114590110Simpusage(void)
11461556Srgrimes{
1147127507Sgad#define	SINGLE_OPTS	"[-aC" OPT_LAZY_f "HhjlmrSTuvwXxZ]"
11481556Srgrimes
1149127499Sgad	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
1150127507Sgad	    "usage: ps " SINGLE_OPTS " [-G gid[,gid]] [-O|o fmt]",
1151127499Sgad	    "          [-p pid[,pid]] [-t tty[,tty]] [-U user[,user]]",
1152127499Sgad	    "          [-M core] [-N system]",
115326465Scharnier	    "       ps [-L]");
11541556Srgrimes	exit(1);
11551556Srgrimes}
1156