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 * 4. Neither the name of the University nor the names of its contributors
141556Srgrimes *    may be used to endorse or promote products derived from this software
151556Srgrimes *    without specific prior written permission.
161556Srgrimes *
171556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271556Srgrimes * SUCH DAMAGE.
28127499Sgad * ------+---------+---------+-------- + --------+---------+---------+---------*
29127499Sgad * Copyright (c) 2004  - Garance Alistair Drosehn <gad@FreeBSD.org>.
30127499Sgad * All rights reserved.
31127499Sgad *
32127499Sgad * Significant modifications made to bring `ps' options somewhat closer
33127499Sgad * to the standard for `ps' as described in SingleUnixSpec-v3.
34127499Sgad * ------+---------+---------+-------- + --------+---------+---------+---------*
351556Srgrimes */
361556Srgrimes
371556Srgrimes#ifndef lint
3890143Smarkmstatic const char copyright[] =
391556Srgrimes"@(#) Copyright (c) 1990, 1993, 1994\n\
401556Srgrimes	The Regents of the University of California.  All rights reserved.\n";
411556Srgrimes#endif /* not lint */
421556Srgrimes
4390143Smarkm#if 0
441556Srgrimes#ifndef lint
4536049Scharnierstatic char sccsid[] = "@(#)ps.c	8.4 (Berkeley) 4/2/94";
4690143Smarkm#endif /* not lint */
4736049Scharnier#endif
48110391Scharnier
4999110Sobrien#include <sys/cdefs.h>
5099110Sobrien__FBSDID("$FreeBSD$");
511556Srgrimes
521556Srgrimes#include <sys/param.h>
53266279Sbdrewery#include <sys/jail.h>
54127546Sgad#include <sys/proc.h>
553296Sdg#include <sys/user.h>
561556Srgrimes#include <sys/stat.h>
571556Srgrimes#include <sys/ioctl.h>
581556Srgrimes#include <sys/sysctl.h>
59137696Scsjp#include <sys/mount.h>
601556Srgrimes
611556Srgrimes#include <ctype.h>
621556Srgrimes#include <err.h>
63127149Sgad#include <errno.h>
641556Srgrimes#include <fcntl.h>
65127499Sgad#include <grp.h>
66266279Sbdrewery#include <jail.h>
671556Srgrimes#include <kvm.h>
6813514Smpp#include <limits.h>
6973367Sache#include <locale.h>
701556Srgrimes#include <paths.h>
7190143Smarkm#include <pwd.h>
721556Srgrimes#include <stdio.h>
731556Srgrimes#include <stdlib.h>
741556Srgrimes#include <string.h>
751556Srgrimes#include <unistd.h>
761556Srgrimes
771556Srgrimes#include "ps.h"
781556Srgrimes
79173492Sjhb#define	_PATH_PTS	"/dev/pts/"
80173492Sjhb
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
92129914Sgad/*
93129971Sgad * isdigit takes an `int', but expects values in the range of unsigned char.
94129971Sgad * This wrapper ensures that values from a 'char' end up in the correct range.
95129914Sgad */
96129971Sgad#define	isdigitch(Anychar) isdigit((u_char)(Anychar))
97129914Sgad
98127537Sgadint	 cflag;			/* -c */
99127537Sgadint	 eval;			/* Exit value */
100127537Sgadtime_t	 now;			/* Current time(3) value */
101127537Sgadint	 rawcpu;		/* -C */
102127537Sgadint	 sumrusage;		/* -S */
103127537Sgadint	 termwidth;		/* Width of the screen (0 == infinity). */
104173004Sjulianint	 showthreads;		/* will threads be shown? */
105127537Sgad
106130999Sgadstruct velisthead varlist = STAILQ_HEAD_INITIALIZER(varlist);
1071556Srgrimes
108127537Sgadstatic int	 forceuread = DEF_UREAD; /* Do extra work to get u-area. */
109127537Sgadstatic kvm_t	*kd;
110127537Sgadstatic int	 needcomm;	/* -o "command" */
111127537Sgadstatic int	 needenv;	/* -e */
112127537Sgadstatic int	 needuser;	/* -o "user" */
113127537Sgadstatic int	 optfatal;	/* Fatal error parsing some list-option. */
114244154Spjdstatic int	 pid_max;	/* kern.max_pid */
1151556Srgrimes
116127537Sgadstatic enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
11797966Sjmallett
118127499Sgadstruct listinfo;
119127537Sgadtypedef	int	addelem_rtn(struct listinfo *_inf, const char *_elem);
120127499Sgad
121127499Sgadstruct listinfo {
122127499Sgad	int		 count;
123127499Sgad	int		 maxcount;
124127499Sgad	int		 elemsize;
125127499Sgad	addelem_rtn	*addelem;
126127499Sgad	const char	*lname;
127127499Sgad	union {
128127499Sgad		gid_t	*gids;
129266279Sbdrewery		int	*jids;
130127499Sgad		pid_t	*pids;
131127499Sgad		dev_t	*ttys;
132127499Sgad		uid_t	*uids;
133127499Sgad		void	*ptr;
134127823Sgad	} l;
135127499Sgad};
136127499Sgad
137127499Sgadstatic int	 addelem_gid(struct listinfo *, const char *);
138266279Sbdrewerystatic int	 addelem_jid(struct listinfo *, const char *);
139127499Sgadstatic int	 addelem_pid(struct listinfo *, const char *);
140127499Sgadstatic int	 addelem_tty(struct listinfo *, const char *);
141127499Sgadstatic int	 addelem_uid(struct listinfo *, const char *);
142127499Sgadstatic void	 add_list(struct listinfo *, const char *);
143192239Sbrianstatic void	 descendant_sort(KINFO *, int);
144225868Straszstatic void	 format_output(KINFO *);
145127499Sgadstatic void	*expand_list(struct listinfo *);
146127598Sgadstatic const char *
147127598Sgad		 fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int),
148245610Sjhb		    KINFO *, char *, char *, int);
149127499Sgadstatic void	 free_list(struct listinfo *);
150127499Sgadstatic void	 init_list(struct listinfo *, addelem_rtn, int, const char *);
151129952Sgadstatic char	*kludge_oldps_options(const char *, char *, const char *);
152127536Sgadstatic int	 pscomp(const void *, const void *);
153127536Sgadstatic void	 saveuser(KINFO *);
154127536Sgadstatic void	 scanvars(void);
155127536Sgadstatic void	 sizevars(void);
156244154Spjdstatic void	 pidmax_init(void);
157127536Sgadstatic void	 usage(void);
158127499Sgad
15997875Sjmallettstatic char dfmt[] = "pid,tt,state,time,command";
160129635Sgadstatic char jfmt[] = "user,pid,ppid,pgid,sid,jobc,state,tt,time,command";
161127538Sgadstatic char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,"
162127538Sgad			"tt,time,command";
16390143Smarkmstatic char   o1[] = "pid";
16497875Sjmallettstatic char   o2[] = "tt,state,time,command";
16597875Sjmallettstatic char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
166127538Sgadstatic char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,"
167127538Sgad			"%cpu,%mem,command";
168105831Srwatsonstatic char Zfmt[] = "label";
1691556Srgrimes
170266279Sbdrewery#define	PS_ARGS	"AaCcde" OPT_LAZY_f "G:gHhjJ:LlM:mN:O:o:p:rSTt:U:uvwXxZ"
17198494Ssobomax
1721556Srgrimesint
17390110Simpmain(int argc, char *argv[])
1741556Srgrimes{
175266279Sbdrewery	struct listinfo gidlist, jidlist, pgrplist, pidlist;
176127499Sgad	struct listinfo ruidlist, sesslist, ttylist, uidlist;
1771556Srgrimes	struct kinfo_proc *kp;
178225868Strasz	KINFO *kinfo = NULL, *next_KINFO;
179225868Strasz	KINFO_STR *ks;
1801556Srgrimes	struct varent *vent;
1811556Srgrimes	struct winsize ws;
182225868Strasz	const char *nlistf, *memf, *fmtstr, *str;
183127539Sgad	char *cols;
184225868Strasz	int all, ch, elem, flag, _fmt, i, lineno, linelen, left;
185192239Sbrian	int descendancy, nentries, nkept, nselectors;
186173004Sjulian	int prtheader, wflag, what, xkeep, xkeep_implied;
18790143Smarkm	char errbuf[_POSIX2_LINE_MAX];
1881556Srgrimes
18911809Sache	(void) setlocale(LC_ALL, "");
190127542Sgad	time(&now);			/* Used by routines in print.c. */
19111809Sache
19297804Stjr	if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0')
19397804Stjr		termwidth = atoi(cols);
19497804Stjr	else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
1951556Srgrimes	     ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
1961556Srgrimes	     ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) == -1) ||
1971556Srgrimes	     ws.ws_col == 0)
1981556Srgrimes		termwidth = 79;
1991556Srgrimes	else
2001556Srgrimes		termwidth = ws.ws_col - 1;
2011556Srgrimes
20298494Ssobomax	/*
203129914Sgad	 * Hide a number of option-processing kludges in a separate routine,
204129914Sgad	 * to support some historical BSD behaviors, such as `ps axu'.
20598494Ssobomax	 */
206129914Sgad	if (argc > 1)
207129915Sgad		argv[1] = kludge_oldps_options(PS_ARGS, argv[1], argv[2]);
2081556Srgrimes
209244154Spjd	pidmax_init();
210244154Spjd
211192239Sbrian	all = descendancy = _fmt = nselectors = optfatal = 0;
212127542Sgad	prtheader = showthreads = wflag = xkeep_implied = 0;
213127542Sgad	xkeep = -1;			/* Neither -x nor -X. */
214127499Sgad	init_list(&gidlist, addelem_gid, sizeof(gid_t), "group");
215266279Sbdrewery	init_list(&jidlist, addelem_jid, sizeof(int), "jail id");
216127499Sgad	init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group");
217127499Sgad	init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id");
218127499Sgad	init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser");
219127499Sgad	init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id");
220127499Sgad	init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty");
221127499Sgad	init_list(&uidlist, addelem_uid, sizeof(uid_t), "user");
222203688Sbrucec	memf = _PATH_DEVNULL;
223203688Sbrucec	nlistf = NULL;
22498494Ssobomax	while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
225180596Skevlo		switch (ch) {
226127499Sgad		case 'A':
227127499Sgad			/*
228127499Sgad			 * Exactly the same as `-ax'.   This has been
229222178Suqs			 * added for compatibility with SUSv3, but for
230127499Sgad			 * now it will not be described in the man page.
231127499Sgad			 */
232127499Sgad			nselectors++;
233127499Sgad			all = xkeep = 1;
234127499Sgad			break;
2351556Srgrimes		case 'a':
236127499Sgad			nselectors++;
2371556Srgrimes			all = 1;
2381556Srgrimes			break;
23919068Speter		case 'C':
24019068Speter			rawcpu = 1;
24119068Speter			break;
24219068Speter		case 'c':
24319068Speter			cflag = 1;
24419068Speter			break;
245192239Sbrian		case 'd':
246192239Sbrian			descendancy = 1;
247192239Sbrian			break;
2481556Srgrimes		case 'e':			/* XXX set ufmt */
2491556Srgrimes			needenv = 1;
2501556Srgrimes			break;
251127506Sgad#ifdef LAZY_PS
252127506Sgad		case 'f':
253127506Sgad			if (getuid() == 0 || getgid() == 0)
254127542Sgad				forceuread = 1;
255127506Sgad			break;
256127506Sgad#endif
257127499Sgad		case 'G':
258127499Sgad			add_list(&gidlist, optarg);
259127499Sgad			xkeep_implied = 1;
260127499Sgad			nselectors++;
261127499Sgad			break;
262127542Sgad		case 'g':
263127499Sgad#if 0
264127597Sgad			/*-
265127542Sgad			 * XXX - This SUSv3 behavior is still under debate
266127542Sgad			 *	since it conflicts with the (undocumented)
267127542Sgad			 *	`-g' option.  So we skip it for now.
268127542Sgad			 */
269127499Sgad			add_list(&pgrplist, optarg);
270127499Sgad			xkeep_implied = 1;
271127499Sgad			nselectors++;
272127499Sgad			break;
273127499Sgad#else
274127542Sgad			/* The historical BSD-ish (from SunOS) behavior. */
2751556Srgrimes			break;			/* no-op */
276127499Sgad#endif
277116265Sscottl		case 'H':
278126127Sdeischen			showthreads = KERN_PROC_INC_THREAD;
279116265Sscottl			break;
2801556Srgrimes		case 'h':
2811556Srgrimes			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
2821556Srgrimes			break;
283266279Sbdrewery		case 'J':
284266279Sbdrewery			add_list(&jidlist, optarg);
285266279Sbdrewery			xkeep_implied = 1;
286266279Sbdrewery			nselectors++;
287266279Sbdrewery			break;
2881556Srgrimes		case 'j':
289109502Sjmallett			parsefmt(jfmt, 0);
29090143Smarkm			_fmt = 1;
2911556Srgrimes			jfmt[0] = '\0';
2921556Srgrimes			break;
2931556Srgrimes		case 'L':
2941556Srgrimes			showkey();
2951556Srgrimes			exit(0);
2961556Srgrimes		case 'l':
297109502Sjmallett			parsefmt(lfmt, 0);
29890143Smarkm			_fmt = 1;
2991556Srgrimes			lfmt[0] = '\0';
3001556Srgrimes			break;
3011556Srgrimes		case 'M':
3021556Srgrimes			memf = optarg;
3031556Srgrimes			break;
3041556Srgrimes		case 'm':
3051556Srgrimes			sortby = SORTMEM;
3061556Srgrimes			break;
3071556Srgrimes		case 'N':
3081556Srgrimes			nlistf = optarg;
3091556Srgrimes			break;
3101556Srgrimes		case 'O':
311109502Sjmallett			parsefmt(o1, 1);
312109502Sjmallett			parsefmt(optarg, 1);
313109502Sjmallett			parsefmt(o2, 1);
3141556Srgrimes			o1[0] = o2[0] = '\0';
31590143Smarkm			_fmt = 1;
3161556Srgrimes			break;
3171556Srgrimes		case 'o':
318109502Sjmallett			parsefmt(optarg, 1);
31990143Smarkm			_fmt = 1;
3201556Srgrimes			break;
3211556Srgrimes		case 'p':
322127499Sgad			add_list(&pidlist, optarg);
323127499Sgad			/*
324127499Sgad			 * Note: `-p' does not *set* xkeep, but any values
325127499Sgad			 * from pidlist are checked before xkeep is.  That
326127499Sgad			 * way they are always matched, even if the user
327127499Sgad			 * specifies `-X'.
328127499Sgad			 */
329127499Sgad			nselectors++;
3301556Srgrimes			break;
331127499Sgad#if 0
332127499Sgad		case 'R':
333127597Sgad			/*-
334127542Sgad			 * XXX - This un-standard option is still under
335127542Sgad			 *	debate.  This is what SUSv3 defines as
336127542Sgad			 *	the `-U' option, and while it would be
337127542Sgad			 *	nice to have, it could cause even more
338127542Sgad			 *	confusion to implement it as `-R'.
339127542Sgad			 */
340127499Sgad			add_list(&ruidlist, optarg);
341127499Sgad			xkeep_implied = 1;
342127499Sgad			nselectors++;
343127499Sgad			break;
344127499Sgad#endif
3451556Srgrimes		case 'r':
3461556Srgrimes			sortby = SORTCPU;
3471556Srgrimes			break;
3481556Srgrimes		case 'S':
3491556Srgrimes			sumrusage = 1;
3501556Srgrimes			break;
351127499Sgad#if 0
352127499Sgad		case 's':
353127597Sgad			/*-
354127542Sgad			 * XXX - This non-standard option is still under
355127542Sgad			 *	debate.  This *is* supported on Solaris,
356127542Sgad			 *	Linux, and IRIX, but conflicts with `-s'
357127542Sgad			 *	on NetBSD and maybe some older BSD's.
358127542Sgad			 */
359127499Sgad			add_list(&sesslist, optarg);
360127499Sgad			xkeep_implied = 1;
361127499Sgad			nselectors++;
362127499Sgad			break;
363127499Sgad#endif
3641556Srgrimes		case 'T':
3651556Srgrimes			if ((optarg = ttyname(STDIN_FILENO)) == NULL)
3661556Srgrimes				errx(1, "stdin: not a terminal");
3671556Srgrimes			/* FALLTHROUGH */
368127499Sgad		case 't':
369127499Sgad			add_list(&ttylist, optarg);
370127499Sgad			xkeep_implied = 1;
371127499Sgad			nselectors++;
3721556Srgrimes			break;
37313020Speter		case 'U':
374127499Sgad			/* This is what SUSv3 defines as the `-u' option. */
375127499Sgad			add_list(&uidlist, optarg);
376127499Sgad			xkeep_implied = 1;
377127499Sgad			nselectors++;
37813020Speter			break;
3791556Srgrimes		case 'u':
380109502Sjmallett			parsefmt(ufmt, 0);
3811556Srgrimes			sortby = SORTCPU;
38290143Smarkm			_fmt = 1;
3831556Srgrimes			ufmt[0] = '\0';
3841556Srgrimes			break;
3851556Srgrimes		case 'v':
386109502Sjmallett			parsefmt(vfmt, 0);
3871556Srgrimes			sortby = SORTMEM;
38890143Smarkm			_fmt = 1;
3891556Srgrimes			vfmt[0] = '\0';
3901556Srgrimes			break;
3911556Srgrimes		case 'w':
3921556Srgrimes			if (wflag)
3931556Srgrimes				termwidth = UNLIMITED;
3941556Srgrimes			else if (termwidth < 131)
3951556Srgrimes				termwidth = 131;
3961556Srgrimes			wflag++;
3971556Srgrimes			break;
398127499Sgad		case 'X':
399127499Sgad			/*
400127499Sgad			 * Note that `-X' and `-x' are not standard "selector"
401127499Sgad			 * options. For most selector-options, we check *all*
402127499Sgad			 * processes to see if any are matched by the given
403127499Sgad			 * value(s).  After we have a set of all the matched
404127499Sgad			 * processes, then `-X' and `-x' govern whether we
405127499Sgad			 * modify that *matched* set for processes which do
406127499Sgad			 * not have a controlling terminal.  `-X' causes
407127499Sgad			 * those processes to be deleted from the matched
408127499Sgad			 * set, while `-x' causes them to be kept.
409127499Sgad			 */
410127499Sgad			xkeep = 0;
411127499Sgad			break;
4121556Srgrimes		case 'x':
413127499Sgad			xkeep = 1;
4141556Srgrimes			break;
41586922Sgreen		case 'Z':
416109502Sjmallett			parsefmt(Zfmt, 0);
41786922Sgreen			Zfmt[0] = '\0';
41886922Sgreen			break;
4191556Srgrimes		case '?':
4201556Srgrimes		default:
4211556Srgrimes			usage();
4221556Srgrimes		}
4231556Srgrimes	argc -= optind;
4241556Srgrimes	argv += optind;
425129914Sgad
426129914Sgad	/*
427129914Sgad	 * If there arguments after processing all the options, attempt
428129914Sgad	 * to treat them as a list of process ids.
429129914Sgad	 */
430129914Sgad	while (*argv) {
431129914Sgad		if (!isdigitch(**argv))
432129914Sgad			break;
433129914Sgad		add_list(&pidlist, *argv);
434129914Sgad		argv++;
435129914Sgad	}
436129914Sgad	if (*argv) {
437129914Sgad		fprintf(stderr, "%s: illegal argument: %s\n",
438129914Sgad		    getprogname(), *argv);
439129914Sgad		usage();
440129914Sgad	}
441127499Sgad	if (optfatal)
442127542Sgad		exit(1);		/* Error messages already printed. */
443127542Sgad	if (xkeep < 0)			/* Neither -X nor -x was specified. */
444127499Sgad		xkeep = xkeep_implied;
445127499Sgad
44689909Sru	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
4471556Srgrimes	if (kd == 0)
4481556Srgrimes		errx(1, "%s", errbuf);
4491556Srgrimes
45090143Smarkm	if (!_fmt)
451109502Sjmallett		parsefmt(dfmt, 0);
4521556Srgrimes
453127499Sgad	if (nselectors == 0) {
454127823Sgad		uidlist.l.ptr = malloc(sizeof(uid_t));
455127823Sgad		if (uidlist.l.ptr == NULL)
45697877Sjmallett			errx(1, "malloc failed");
457127499Sgad		nselectors = 1;
458127499Sgad		uidlist.count = uidlist.maxcount = 1;
459127823Sgad		*uidlist.l.uids = getuid();
46066377Sbrian	}
4611556Srgrimes
4621556Srgrimes	/*
4631556Srgrimes	 * scan requested variables, noting what structures are needed,
46453170Skris	 * and adjusting header widths as appropriate.
4651556Srgrimes	 */
4661556Srgrimes	scanvars();
467127499Sgad
4681556Srgrimes	/*
469127499Sgad	 * Get process list.  If the user requested just one selector-
470127499Sgad	 * option, then kvm_getprocs can be asked to return just those
471127499Sgad	 * processes.  Otherwise, have it return all processes, and
472127499Sgad	 * then this routine will search that full list and select the
473127499Sgad	 * processes which match any of the user's selector-options.
4741556Srgrimes	 */
475127499Sgad	what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
476127499Sgad	flag = 0;
477127499Sgad	if (nselectors == 1) {
478129600Sgad		if (gidlist.count == 1) {
479129600Sgad			what = KERN_PROC_RGID | showthreads;
480129600Sgad			flag = *gidlist.l.gids;
481129600Sgad			nselectors = 0;
482129600Sgad		} else if (pgrplist.count == 1) {
483127499Sgad			what = KERN_PROC_PGRP | showthreads;
484127823Sgad			flag = *pgrplist.l.pids;
485127499Sgad			nselectors = 0;
486127499Sgad		} else if (pidlist.count == 1) {
487127499Sgad			what = KERN_PROC_PID | showthreads;
488127823Sgad			flag = *pidlist.l.pids;
489127499Sgad			nselectors = 0;
490127499Sgad		} else if (ruidlist.count == 1) {
491127499Sgad			what = KERN_PROC_RUID | showthreads;
492127823Sgad			flag = *ruidlist.l.uids;
493127499Sgad			nselectors = 0;
494127499Sgad		} else if (sesslist.count == 1) {
495127499Sgad			what = KERN_PROC_SESSION | showthreads;
496127823Sgad			flag = *sesslist.l.pids;
497127499Sgad			nselectors = 0;
498127499Sgad		} else if (ttylist.count == 1) {
499127499Sgad			what = KERN_PROC_TTY | showthreads;
500127823Sgad			flag = *ttylist.l.ttys;
501127499Sgad			nselectors = 0;
502127499Sgad		} else if (uidlist.count == 1) {
503127499Sgad			what = KERN_PROC_UID | showthreads;
504127823Sgad			flag = *uidlist.l.uids;
505127499Sgad			nselectors = 0;
506127499Sgad		} else if (all) {
507127499Sgad			/* No need for this routine to select processes. */
508127499Sgad			nselectors = 0;
509127499Sgad		}
5101556Srgrimes	}
511126127Sdeischen
5121556Srgrimes	/*
5131556Srgrimes	 * select procs
5141556Srgrimes	 */
515127499Sgad	nentries = -1;
516127149Sgad	kp = kvm_getprocs(kd, what, flag, &nentries);
517127544Sgad	if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0))
5181556Srgrimes		errx(1, "%s", kvm_geterr(kd));
519127499Sgad	nkept = 0;
520127149Sgad	if (nentries > 0) {
521127149Sgad		if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
522127149Sgad			errx(1, "malloc failed");
523127149Sgad		for (i = nentries; --i >= 0; ++kp) {
524127499Sgad			/*
525127499Sgad			 * If the user specified multiple selection-criteria,
526127499Sgad			 * then keep any process matched by the inclusive OR
527127499Sgad			 * of all the selection-criteria given.
528127499Sgad			 */
529127499Sgad			if (pidlist.count > 0) {
530127499Sgad				for (elem = 0; elem < pidlist.count; elem++)
531127823Sgad					if (kp->ki_pid == pidlist.l.pids[elem])
532127499Sgad						goto keepit;
533127499Sgad			}
534127499Sgad			/*
535127499Sgad			 * Note that we had to process pidlist before
536127499Sgad			 * filtering out processes which do not have
537127499Sgad			 * a controlling terminal.
538127499Sgad			 */
539127499Sgad			if (xkeep == 0) {
540127499Sgad				if ((kp->ki_tdev == NODEV ||
541127499Sgad				    (kp->ki_flag & P_CONTROLT) == 0))
542127499Sgad					continue;
543127499Sgad			}
544127499Sgad			if (nselectors == 0)
545127499Sgad				goto keepit;
546127499Sgad			if (gidlist.count > 0) {
547127499Sgad				for (elem = 0; elem < gidlist.count; elem++)
548127823Sgad					if (kp->ki_rgid == gidlist.l.gids[elem])
549127499Sgad						goto keepit;
550127499Sgad			}
551266279Sbdrewery			if (jidlist.count > 0) {
552266279Sbdrewery				for (elem = 0; elem < jidlist.count; elem++)
553266279Sbdrewery					if (kp->ki_jid == jidlist.l.jids[elem])
554266279Sbdrewery						goto keepit;
555266279Sbdrewery			}
556127499Sgad			if (pgrplist.count > 0) {
557127499Sgad				for (elem = 0; elem < pgrplist.count; elem++)
558127823Sgad					if (kp->ki_pgid ==
559127823Sgad					    pgrplist.l.pids[elem])
560127499Sgad						goto keepit;
561127499Sgad			}
562127499Sgad			if (ruidlist.count > 0) {
563127499Sgad				for (elem = 0; elem < ruidlist.count; elem++)
564127823Sgad					if (kp->ki_ruid ==
565127823Sgad					    ruidlist.l.uids[elem])
566127499Sgad						goto keepit;
567127499Sgad			}
568127499Sgad			if (sesslist.count > 0) {
569127499Sgad				for (elem = 0; elem < sesslist.count; elem++)
570127823Sgad					if (kp->ki_sid == sesslist.l.pids[elem])
571127499Sgad						goto keepit;
572127499Sgad			}
573127499Sgad			if (ttylist.count > 0) {
574127499Sgad				for (elem = 0; elem < ttylist.count; elem++)
575127823Sgad					if (kp->ki_tdev == ttylist.l.ttys[elem])
576127499Sgad						goto keepit;
577127499Sgad			}
578127499Sgad			if (uidlist.count > 0) {
579127499Sgad				for (elem = 0; elem < uidlist.count; elem++)
580127823Sgad					if (kp->ki_uid == uidlist.l.uids[elem])
581127499Sgad						goto keepit;
582127499Sgad			}
583127499Sgad			/*
584127499Sgad			 * This process did not match any of the user's
585127499Sgad			 * selector-options, so skip the process.
586127499Sgad			 */
587127499Sgad			continue;
588127499Sgad
589127499Sgad		keepit:
590130816Sgad			next_KINFO = &kinfo[nkept];
591130816Sgad			next_KINFO->ki_p = kp;
592192239Sbrian			next_KINFO->ki_d.level = 0;
593192239Sbrian			next_KINFO->ki_d.prefix = NULL;
594130816Sgad			next_KINFO->ki_pcpu = getpcpu(next_KINFO);
595130816Sgad			if (sortby == SORTMEM)
596130816Sgad				next_KINFO->ki_memsize = kp->ki_tsize +
597130816Sgad				    kp->ki_dsize + kp->ki_ssize;
598127149Sgad			if (needuser)
599130816Sgad				saveuser(next_KINFO);
600127499Sgad			nkept++;
601127149Sgad		}
6021556Srgrimes	}
60325271Sjkh
60425271Sjkh	sizevars();
60525271Sjkh
606225868Strasz	if (nkept == 0) {
607225868Strasz		printheader();
60862803Swill		exit(1);
609225868Strasz	}
610127499Sgad
6111556Srgrimes	/*
6121556Srgrimes	 * sort proc list
6131556Srgrimes	 */
614127499Sgad	qsort(kinfo, nkept, sizeof(KINFO), pscomp);
615192239Sbrian
6161556Srgrimes	/*
617192239Sbrian	 * We want things in descendant order
618192239Sbrian	 */
619192239Sbrian	if (descendancy)
620192239Sbrian		descendant_sort(kinfo, nkept);
621192239Sbrian
622225868Strasz
623192239Sbrian	/*
624225868Strasz	 * Prepare formatted output.
6251556Srgrimes	 */
626225868Strasz	for (i = 0; i < nkept; i++)
627225868Strasz		format_output(&kinfo[i]);
628225868Strasz
629225868Strasz	/*
630225868Strasz	 * Print header.
631225868Strasz	 */
632225868Strasz	printheader();
633225868Strasz
634225868Strasz	/*
635225868Strasz	 * Output formatted lines.
636225868Strasz	 */
637127499Sgad	for (i = lineno = 0; i < nkept; i++) {
638225868Strasz		linelen = 0;
639130999Sgad		STAILQ_FOREACH(vent, &varlist, next_ve) {
640225868Strasz	        	if (vent->var->flag & LJUST)
641225868Strasz				fmtstr = "%-*s";
642225868Strasz			else
643225868Strasz				fmtstr = "%*s";
644225868Strasz
645225868Strasz			ks = STAILQ_FIRST(&kinfo[i].ki_ks);
646225868Strasz			STAILQ_REMOVE_HEAD(&kinfo[i].ki_ks, ks_next);
647229782Suqs			/* Truncate rightmost column if necessary.  */
648225868Strasz			if (STAILQ_NEXT(vent, next_ve) == NULL &&
649225868Strasz			   termwidth != UNLIMITED && ks->ks_str != NULL) {
650225868Strasz				left = termwidth - linelen;
651225868Strasz				if (left > 0 && left < (int)strlen(ks->ks_str))
652225868Strasz					ks->ks_str[left] = '\0';
653225868Strasz			}
654225868Strasz			str = ks->ks_str;
655225868Strasz			if (str == NULL)
656225868Strasz				str = "-";
657225868Strasz			/* No padding for the last column, if it's LJUST. */
658225868Strasz			if (STAILQ_NEXT(vent, next_ve) == NULL &&
659225868Strasz			    vent->var->flag & LJUST)
660225868Strasz				linelen += printf(fmtstr, 0, str);
661225868Strasz			else
662225868Strasz				linelen += printf(fmtstr, vent->var->width, str);
663225868Strasz
664225868Strasz			if (ks->ks_str != NULL) {
665225868Strasz				free(ks->ks_str);
666225868Strasz				ks->ks_str = NULL;
667225868Strasz			}
668225868Strasz			free(ks);
669225868Strasz			ks = NULL;
670225868Strasz
671225868Strasz			if (STAILQ_NEXT(vent, next_ve) != NULL) {
6721556Srgrimes				(void)putchar(' ');
673225868Strasz				linelen++;
674225868Strasz			}
6751556Srgrimes		}
6761556Srgrimes		(void)putchar('\n');
6771556Srgrimes		if (prtheader && lineno++ == prtheader - 4) {
6781556Srgrimes			(void)putchar('\n');
6791556Srgrimes			printheader();
6801556Srgrimes			lineno = 0;
6811556Srgrimes		}
6821556Srgrimes	}
683127499Sgad	free_list(&gidlist);
684266279Sbdrewery	free_list(&jidlist);
685127499Sgad	free_list(&pidlist);
686127499Sgad	free_list(&pgrplist);
687127499Sgad	free_list(&ruidlist);
688127499Sgad	free_list(&sesslist);
689127499Sgad	free_list(&ttylist);
690127499Sgad	free_list(&uidlist);
691192239Sbrian	for (i = 0; i < nkept; i++)
692192239Sbrian		free(kinfo[i].ki_d.prefix);
693192239Sbrian	free(kinfo);
69466377Sbrian
6951556Srgrimes	exit(eval);
6961556Srgrimes}
6971556Srgrimes
698127499Sgadstatic int
699127499Sgadaddelem_gid(struct listinfo *inf, const char *elem)
700127499Sgad{
701127499Sgad	struct group *grp;
702127499Sgad	const char *nameorID;
703127499Sgad	char *endp;
704127602Sgad	u_long bigtemp;
705127499Sgad
706127499Sgad	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
707127499Sgad		if (*elem == '\0')
708127499Sgad			warnx("Invalid (zero-length) %s name", inf->lname);
709127499Sgad		else
710127499Sgad			warnx("%s name too long: %s", inf->lname, elem);
711127499Sgad		optfatal = 1;
712127542Sgad		return (0);		/* Do not add this value. */
713127499Sgad	}
714127499Sgad
715127499Sgad	/*
716127499Sgad	 * SUSv3 states that `ps -G grouplist' should match "real-group
717127499Sgad	 * ID numbers", and does not mention group-names.  I do want to
718127499Sgad	 * also support group-names, so this tries for a group-id first,
719127499Sgad	 * and only tries for a name if that doesn't work.  This is the
720127499Sgad	 * opposite order of what is done in addelem_uid(), but in
721127499Sgad	 * practice the order would only matter for group-names which
722127499Sgad	 * are all-numeric.
723127499Sgad	 */
724127499Sgad	grp = NULL;
725127499Sgad	nameorID = "named";
726127499Sgad	errno = 0;
727127602Sgad	bigtemp = strtoul(elem, &endp, 10);
728127602Sgad	if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) {
729127499Sgad		nameorID = "name or ID matches";
730127602Sgad		grp = getgrgid((gid_t)bigtemp);
731127499Sgad	}
732127499Sgad	if (grp == NULL)
733127499Sgad		grp = getgrnam(elem);
734127499Sgad	if (grp == NULL) {
735127499Sgad		warnx("No %s %s '%s'", inf->lname, nameorID, elem);
736127499Sgad		optfatal = 1;
737129967Sgad		return (0);
738127499Sgad	}
739127499Sgad	if (inf->count >= inf->maxcount)
740127499Sgad		expand_list(inf);
741127823Sgad	inf->l.gids[(inf->count)++] = grp->gr_gid;
742127499Sgad	return (1);
743127499Sgad}
744127499Sgad
745127499Sgadstatic int
746266279Sbdreweryaddelem_jid(struct listinfo *inf, const char *elem)
747266279Sbdrewery{
748266279Sbdrewery	int tempid;
749266279Sbdrewery
750266279Sbdrewery	if (*elem == '\0') {
751266279Sbdrewery		warnx("Invalid (zero-length) jail id");
752266279Sbdrewery		optfatal = 1;
753266279Sbdrewery		return (0);		/* Do not add this value. */
754266279Sbdrewery	}
755266279Sbdrewery
756266279Sbdrewery	tempid = jail_getid(elem);
757266279Sbdrewery	if (tempid < 0) {
758266279Sbdrewery		warnx("Invalid %s: %s", inf->lname, elem);
759266279Sbdrewery		optfatal = 1;
760266279Sbdrewery		return (0);
761266279Sbdrewery	}
762266279Sbdrewery
763266279Sbdrewery	if (inf->count >= inf->maxcount)
764266279Sbdrewery		expand_list(inf);
765266279Sbdrewery	inf->l.jids[(inf->count)++] = tempid;
766266279Sbdrewery	return (1);
767266279Sbdrewery}
768266279Sbdrewery
769266279Sbdrewerystatic int
770127499Sgadaddelem_pid(struct listinfo *inf, const char *elem)
771127149Sgad{
772127539Sgad	char *endp;
773127149Sgad	long tempid;
774127149Sgad
775129917Sgad	if (*elem == '\0') {
776129917Sgad		warnx("Invalid (zero-length) process id");
777129917Sgad		optfatal = 1;
778129917Sgad		return (0);		/* Do not add this value. */
779127149Sgad	}
780127149Sgad
781129952Sgad	errno = 0;
782129952Sgad	tempid = strtol(elem, &endp, 10);
783129952Sgad	if (*endp != '\0' || tempid < 0 || elem == endp) {
784129952Sgad		warnx("Invalid %s: %s", inf->lname, elem);
785129952Sgad		errno = ERANGE;
786244154Spjd	} else if (errno != 0 || tempid > pid_max) {
787129952Sgad		warnx("%s too large: %s", inf->lname, elem);
788129952Sgad		errno = ERANGE;
789129952Sgad	}
790129952Sgad	if (errno == ERANGE) {
791129952Sgad		optfatal = 1;
792129967Sgad		return (0);
793129952Sgad	}
794127499Sgad	if (inf->count >= inf->maxcount)
795127499Sgad		expand_list(inf);
796127823Sgad	inf->l.pids[(inf->count)++] = tempid;
797127499Sgad	return (1);
798127499Sgad}
799127149Sgad
800131010Sgad/*-
801131010Sgad * The user can specify a device via one of three formats:
802173492Sjhb *     1) fully qualified, e.g.:     /dev/ttyp0 /dev/console	/dev/pts/0
803173492Sjhb *     2) missing "/dev", e.g.:      ttyp0      console		pts/0
804173492Sjhb *     3) two-letters, e.g.:         p0         co		0
805131010Sgad *        (matching letters that would be seen in the "TT" column)
806131010Sgad */
807127499Sgadstatic int
808127499Sgadaddelem_tty(struct listinfo *inf, const char *elem)
809127499Sgad{
810127539Sgad	const char *ttypath;
811127539Sgad	struct stat sb;
812173492Sjhb	char pathbuf[PATH_MAX], pathbuf2[PATH_MAX], pathbuf3[PATH_MAX];
813127499Sgad
814131010Sgad	ttypath = NULL;
815131209Sgad	pathbuf2[0] = '\0';
816173492Sjhb	pathbuf3[0] = '\0';
817131010Sgad	switch (*elem) {
818131010Sgad	case '/':
819127499Sgad		ttypath = elem;
820131010Sgad		break;
821131010Sgad	case 'c':
822131010Sgad		if (strcmp(elem, "co") == 0) {
823131010Sgad			ttypath = _PATH_CONSOLE;
824131010Sgad			break;
825131010Sgad		}
826131010Sgad		/* FALLTHROUGH */
827131010Sgad	default:
828131010Sgad		strlcpy(pathbuf, _PATH_DEV, sizeof(pathbuf));
829131010Sgad		strlcat(pathbuf, elem, sizeof(pathbuf));
830131010Sgad		ttypath = pathbuf;
831131209Sgad		if (strncmp(pathbuf, _PATH_TTY, strlen(_PATH_TTY)) == 0)
832131010Sgad			break;
833173492Sjhb		if (strncmp(pathbuf, _PATH_PTS, strlen(_PATH_PTS)) == 0)
834173492Sjhb			break;
835131010Sgad		if (strcmp(pathbuf, _PATH_CONSOLE) == 0)
836131010Sgad			break;
837131209Sgad		/* Check to see if /dev/tty${elem} exists */
838131209Sgad		strlcpy(pathbuf2, _PATH_TTY, sizeof(pathbuf2));
839131209Sgad		strlcat(pathbuf2, elem, sizeof(pathbuf2));
840131209Sgad		if (stat(pathbuf2, &sb) == 0 && S_ISCHR(sb.st_mode)) {
841131010Sgad			/* No need to repeat stat() && S_ISCHR() checks */
842192280Sbrian			ttypath = NULL;
843131010Sgad			break;
844131010Sgad		}
845173492Sjhb		/* Check to see if /dev/pts/${elem} exists */
846173492Sjhb		strlcpy(pathbuf3, _PATH_PTS, sizeof(pathbuf3));
847173492Sjhb		strlcat(pathbuf3, elem, sizeof(pathbuf3));
848173492Sjhb		if (stat(pathbuf3, &sb) == 0 && S_ISCHR(sb.st_mode)) {
849173492Sjhb			/* No need to repeat stat() && S_ISCHR() checks */
850192280Sbrian			ttypath = NULL;
851173492Sjhb			break;
852173492Sjhb		}
853131010Sgad		break;
854127499Sgad	}
855131010Sgad	if (ttypath) {
856131010Sgad		if (stat(ttypath, &sb) == -1) {
857173492Sjhb			if (pathbuf3[0] != '\0')
858173492Sjhb				warn("%s, %s, and %s", pathbuf3, pathbuf2,
859173492Sjhb				    ttypath);
860131209Sgad			else
861131209Sgad				warn("%s", ttypath);
862131010Sgad			optfatal = 1;
863131010Sgad			return (0);
864131010Sgad		}
865131010Sgad		if (!S_ISCHR(sb.st_mode)) {
866173492Sjhb			if (pathbuf3[0] != '\0')
867173492Sjhb				warnx("%s, %s, and %s: Not a terminal",
868173492Sjhb				    pathbuf3, pathbuf2, ttypath);
869131209Sgad			else
870131209Sgad				warnx("%s: Not a terminal", ttypath);
871131010Sgad			optfatal = 1;
872131010Sgad			return (0);
873131010Sgad		}
874127499Sgad	}
875127499Sgad	if (inf->count >= inf->maxcount)
876127499Sgad		expand_list(inf);
877127823Sgad	inf->l.ttys[(inf->count)++] = sb.st_rdev;
878127499Sgad	return (1);
879127149Sgad}
880127149Sgad
881127499Sgadstatic int
882127499Sgadaddelem_uid(struct listinfo *inf, const char *elem)
88366377Sbrian{
88466377Sbrian	struct passwd *pwd;
885127539Sgad	char *endp;
886127602Sgad	u_long bigtemp;
88766377Sbrian
888127499Sgad	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
889127499Sgad		if (*elem == '\0')
890127499Sgad			warnx("Invalid (zero-length) %s name", inf->lname);
891127499Sgad		else
892127499Sgad			warnx("%s name too long: %s", inf->lname, elem);
893127499Sgad		optfatal = 1;
894127542Sgad		return (0);		/* Do not add this value. */
895127499Sgad	}
89666377Sbrian
897127499Sgad	pwd = getpwnam(elem);
898127499Sgad	if (pwd == NULL) {
899127499Sgad		errno = 0;
900127602Sgad		bigtemp = strtoul(elem, &endp, 10);
901127602Sgad		if (errno != 0 || *endp != '\0' || bigtemp > UID_MAX)
902127499Sgad			warnx("No %s named '%s'", inf->lname, elem);
903127499Sgad		else {
904127499Sgad			/* The string is all digits, so it might be a userID. */
905127602Sgad			pwd = getpwuid((uid_t)bigtemp);
906127499Sgad			if (pwd == NULL)
907127499Sgad				warnx("No %s name or ID matches '%s'",
908127499Sgad				    inf->lname, elem);
90966377Sbrian		}
910127499Sgad	}
911127499Sgad	if (pwd == NULL) {
912127509Sgad		/*
913127509Sgad		 * These used to be treated as minor warnings (and the
914127509Sgad		 * option was simply ignored), but now they are fatal
915127509Sgad		 * errors (and the command will be aborted).
916127509Sgad		 */
917127509Sgad		optfatal = 1;
918129967Sgad		return (0);
919127499Sgad	}
920127499Sgad	if (inf->count >= inf->maxcount)
921127499Sgad		expand_list(inf);
922127823Sgad	inf->l.uids[(inf->count)++] = pwd->pw_uid;
923127499Sgad	return (1);
924127499Sgad}
925127499Sgad
926127499Sgadstatic void
927127499Sgadadd_list(struct listinfo *inf, const char *argp)
928127499Sgad{
929127499Sgad	const char *savep;
930127499Sgad	char *cp, *endp;
931127499Sgad	int toolong;
932127539Sgad	char elemcopy[PATH_MAX];
933127499Sgad
934239883Semaste	if (*argp == '\0')
935239883Semaste		inf->addelem(inf, argp);
936127499Sgad	while (*argp != '\0') {
937127499Sgad		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
938127499Sgad			argp++;
939127499Sgad		savep = argp;
940127499Sgad		toolong = 0;
941127499Sgad		cp = elemcopy;
942127499Sgad		if (strchr(T_SEP, *argp) == NULL) {
943127499Sgad			endp = elemcopy + sizeof(elemcopy) - 1;
944127499Sgad			while (*argp != '\0' && cp <= endp &&
945127499Sgad			    strchr(W_SEP T_SEP, *argp) == NULL)
946127499Sgad				*cp++ = *argp++;
947127499Sgad			if (cp > endp)
948127499Sgad				toolong = 1;
94966377Sbrian		}
950127499Sgad		if (!toolong) {
951127499Sgad			*cp = '\0';
952127542Sgad			/*
953129953Sgad			 * Add this single element to the given list.
954127542Sgad			 */
955127499Sgad			inf->addelem(inf, elemcopy);
956127499Sgad		} else {
957127499Sgad			/*
958127499Sgad			 * The string is too long to copy.  Find the end
959127499Sgad			 * of the string to print out the warning message.
960127499Sgad			 */
961127499Sgad			while (*argp != '\0' && strchr(W_SEP T_SEP,
962127499Sgad			    *argp) == NULL)
963127499Sgad				argp++;
964127499Sgad			warnx("Value too long: %.*s", (int)(argp - savep),
965127499Sgad			    savep);
966127499Sgad			optfatal = 1;
96766377Sbrian		}
968127499Sgad		/*
969127499Sgad		 * Skip over any number of trailing whitespace characters,
970127499Sgad		 * but only one (at most) trailing element-terminating
971127499Sgad		 * character.
972127499Sgad		 */
973127499Sgad		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
974127499Sgad			argp++;
975127499Sgad		if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) {
976127499Sgad			argp++;
977127499Sgad			/* Catch case where string ended with a comma. */
978127499Sgad			if (*argp == '\0')
979127499Sgad				inf->addelem(inf, argp);
980127499Sgad		}
98166377Sbrian	}
982127499Sgad}
98366377Sbrian
984192239Sbrianstatic void
985192239Sbriandescendant_sort(KINFO *ki, int items)
986192239Sbrian{
987192239Sbrian	int dst, lvl, maxlvl, n, ndst, nsrc, siblings, src;
988192239Sbrian	unsigned char *path;
989192239Sbrian	KINFO kn;
990192239Sbrian
991192239Sbrian	/*
992192239Sbrian	 * First, sort the entries by descendancy, tracking the descendancy
993192239Sbrian	 * depth in the ki_d.level field.
994192239Sbrian	 */
995192239Sbrian	src = 0;
996192239Sbrian	maxlvl = 0;
997192239Sbrian	while (src < items) {
998192239Sbrian		if (ki[src].ki_d.level) {
999192239Sbrian			src++;
1000192239Sbrian			continue;
1001192239Sbrian		}
1002192239Sbrian		for (nsrc = 1; src + nsrc < items; nsrc++)
1003192239Sbrian			if (!ki[src + nsrc].ki_d.level)
1004192239Sbrian				break;
1005192239Sbrian
1006192239Sbrian		for (dst = 0; dst < items; dst++) {
1007192239Sbrian			if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_pid)
1008192239Sbrian				continue;
1009192239Sbrian			if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_ppid)
1010192239Sbrian				break;
1011192239Sbrian		}
1012192239Sbrian
1013192239Sbrian		if (dst == items) {
1014192239Sbrian			src += nsrc;
1015192239Sbrian			continue;
1016192239Sbrian		}
1017192239Sbrian
1018192239Sbrian		for (ndst = 1; dst + ndst < items; ndst++)
1019192239Sbrian			if (ki[dst + ndst].ki_d.level <= ki[dst].ki_d.level)
1020192239Sbrian				break;
1021192239Sbrian
1022192239Sbrian		for (n = src; n < src + nsrc; n++) {
1023192239Sbrian			ki[n].ki_d.level += ki[dst].ki_d.level + 1;
1024192239Sbrian			if (maxlvl < ki[n].ki_d.level)
1025192239Sbrian				maxlvl = ki[n].ki_d.level;
1026192239Sbrian		}
1027192239Sbrian
1028192239Sbrian		while (nsrc) {
1029192239Sbrian			if (src < dst) {
1030192239Sbrian				kn = ki[src];
1031192239Sbrian				memmove(ki + src, ki + src + 1,
1032192239Sbrian				    (dst - src + ndst - 1) * sizeof *ki);
1033192239Sbrian				ki[dst + ndst - 1] = kn;
1034192239Sbrian				nsrc--;
1035192239Sbrian				dst--;
1036192239Sbrian				ndst++;
1037192239Sbrian			} else if (src != dst + ndst) {
1038192239Sbrian				kn = ki[src];
1039192239Sbrian				memmove(ki + dst + ndst + 1, ki + dst + ndst,
1040192239Sbrian				    (src - dst - ndst) * sizeof *ki);
1041192239Sbrian				ki[dst + ndst] = kn;
1042192239Sbrian				ndst++;
1043192239Sbrian				nsrc--;
1044192239Sbrian				src++;
1045192239Sbrian			} else {
1046192239Sbrian				ndst += nsrc;
1047192239Sbrian				src += nsrc;
1048192239Sbrian				nsrc = 0;
1049192239Sbrian			}
1050192239Sbrian		}
1051192239Sbrian	}
1052192239Sbrian
1053192239Sbrian	/*
1054192239Sbrian	 * Now populate ki_d.prefix (instead of ki_d.level) with the command
1055192239Sbrian	 * prefix used to show descendancies.
1056192239Sbrian	 */
1057192239Sbrian	path = malloc((maxlvl + 7) / 8);
1058192239Sbrian	memset(path, '\0', (maxlvl + 7) / 8);
1059192239Sbrian	for (src = 0; src < items; src++) {
1060192239Sbrian		if ((lvl = ki[src].ki_d.level) == 0) {
1061192239Sbrian			ki[src].ki_d.prefix = NULL;
1062192239Sbrian			continue;
1063192239Sbrian		}
1064192239Sbrian		if ((ki[src].ki_d.prefix = malloc(lvl * 2 + 1)) == NULL)
1065192239Sbrian			errx(1, "malloc failed");
1066192239Sbrian		for (n = 0; n < lvl - 2; n++) {
1067192239Sbrian			ki[src].ki_d.prefix[n * 2] =
1068192239Sbrian			    path[n / 8] & 1 << (n % 8) ? '|' : ' ';
1069192239Sbrian			ki[src].ki_d.prefix[n * 2 + 1] = ' ';
1070192239Sbrian		}
1071192239Sbrian		if (n == lvl - 2) {
1072192239Sbrian			/* Have I any more siblings? */
1073192239Sbrian			for (siblings = 0, dst = src + 1; dst < items; dst++) {
1074192239Sbrian				if (ki[dst].ki_d.level > lvl)
1075192239Sbrian					continue;
1076192239Sbrian				if (ki[dst].ki_d.level == lvl)
1077192239Sbrian					siblings = 1;
1078192239Sbrian				break;
1079192239Sbrian			}
1080192239Sbrian			if (siblings)
1081192239Sbrian				path[n / 8] |= 1 << (n % 8);
1082192239Sbrian			else
1083192239Sbrian				path[n / 8] &= ~(1 << (n % 8));
1084192239Sbrian			ki[src].ki_d.prefix[n * 2] = siblings ? '|' : '`';
1085192239Sbrian			ki[src].ki_d.prefix[n * 2 + 1] = '-';
1086192239Sbrian			n++;
1087192239Sbrian		}
1088192239Sbrian		strcpy(ki[src].ki_d.prefix + n * 2, "- ");
1089192239Sbrian	}
1090192239Sbrian	free(path);
1091192239Sbrian}
1092192239Sbrian
1093127499Sgadstatic void *
1094127499Sgadexpand_list(struct listinfo *inf)
1095127499Sgad{
1096127539Sgad	void *newlist;
1097127499Sgad	int newmax;
109866377Sbrian
1099127499Sgad	newmax = (inf->maxcount + 1) << 1;
1100127823Sgad	newlist = realloc(inf->l.ptr, newmax * inf->elemsize);
1101127499Sgad	if (newlist == NULL) {
1102127823Sgad		free(inf->l.ptr);
1103129967Sgad		errx(1, "realloc to %d %ss failed", newmax, inf->lname);
1104127499Sgad	}
1105127499Sgad	inf->maxcount = newmax;
1106127823Sgad	inf->l.ptr = newlist;
1107127499Sgad
1108127499Sgad	return (newlist);
110966377Sbrian}
111066377Sbrian
1111127499Sgadstatic void
1112127499Sgadfree_list(struct listinfo *inf)
1113127499Sgad{
1114127499Sgad
1115127499Sgad	inf->count = inf->elemsize = inf->maxcount = 0;
1116127823Sgad	if (inf->l.ptr != NULL)
1117127823Sgad		free(inf->l.ptr);
1118127499Sgad	inf->addelem = NULL;
1119127499Sgad	inf->lname = NULL;
1120127823Sgad	inf->l.ptr = NULL;
1121127499Sgad}
1122127499Sgad
1123127499Sgadstatic void
1124127499Sgadinit_list(struct listinfo *inf, addelem_rtn artn, int elemsize,
1125127499Sgad    const char *lname)
1126127499Sgad{
1127127499Sgad
1128127499Sgad	inf->count = inf->maxcount = 0;
1129127499Sgad	inf->elemsize = elemsize;
1130127499Sgad	inf->addelem = artn;
1131127499Sgad	inf->lname = lname;
1132127823Sgad	inf->l.ptr = NULL;
1133127499Sgad}
1134127499Sgad
1135109502SjmallettVARENT *
1136109502Sjmallettfind_varentry(VAR *v)
1137109502Sjmallett{
1138109502Sjmallett	struct varent *vent;
1139109502Sjmallett
1140130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
1141109502Sjmallett		if (strcmp(vent->var->name, v->name) == 0)
1142109502Sjmallett			return vent;
1143109502Sjmallett	}
1144109502Sjmallett	return NULL;
1145109502Sjmallett}
1146109502Sjmallett
11471556Srgrimesstatic void
114890110Simpscanvars(void)
11491556Srgrimes{
11501556Srgrimes	struct varent *vent;
11511556Srgrimes	VAR *v;
115225271Sjkh
1153130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
115425271Sjkh		v = vent->var;
115525271Sjkh		if (v->flag & USER)
115625271Sjkh			needuser = 1;
115725271Sjkh		if (v->flag & COMM)
115825271Sjkh			needcomm = 1;
115925271Sjkh	}
116025271Sjkh}
116125271Sjkh
116225271Sjkhstatic void
1163225868Straszformat_output(KINFO *ki)
116425271Sjkh{
116525271Sjkh	struct varent *vent;
116625271Sjkh	VAR *v;
1167225868Strasz	KINFO_STR *ks;
1168225868Strasz	char *str;
1169225868Strasz	int len;
11701556Srgrimes
1171225868Strasz	STAILQ_INIT(&ki->ki_ks);
1172130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
11731556Srgrimes		v = vent->var;
1174225868Strasz		str = (v->oproc)(ki, vent);
1175225868Strasz		ks = malloc(sizeof(*ks));
1176225868Strasz		if (ks == NULL)
1177225868Strasz			errx(1, "malloc failed");
1178225868Strasz		ks->ks_str = str;
1179225868Strasz		STAILQ_INSERT_TAIL(&ki->ki_ks, ks, ks_next);
1180225868Strasz		if (str != NULL) {
1181225868Strasz			len = strlen(str);
1182225868Strasz		} else
1183225868Strasz			len = 1; /* "-" */
1184225868Strasz		if (v->width < len)
1185225868Strasz			v->width = len;
118625271Sjkh	}
118725271Sjkh}
118825271Sjkh
118925271Sjkhstatic void
119090110Simpsizevars(void)
119125271Sjkh{
119225271Sjkh	struct varent *vent;
119325271Sjkh	VAR *v;
119425271Sjkh	int i;
119525271Sjkh
1196130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
119725271Sjkh		v = vent->var;
1198109504Sjmallett		i = strlen(vent->header);
11991556Srgrimes		if (v->width < i)
12001556Srgrimes			v->width = i;
12011556Srgrimes	}
12021556Srgrimes}
12031556Srgrimes
120490143Smarkmstatic const char *
120590110Simpfmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki,
1206245610Sjhb    char *comm, char *thread, int maxlen)
12071556Srgrimes{
120890143Smarkm	const char *s;
12091556Srgrimes
1210245610Sjhb	s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm,
1211245635Sjhb	    showthreads && ki->ki_p->ki_numthreads > 1 ? thread : NULL, maxlen);
12121556Srgrimes	return (s);
12131556Srgrimes}
12141556Srgrimes
1215172207Sjeff#define UREADOK(ki)	(forceuread || (ki->ki_p->ki_flag & P_INMEM))
121631552Sdyson
12171556Srgrimesstatic void
121890110Simpsaveuser(KINFO *ki)
12191556Srgrimes{
12201556Srgrimes
1221172207Sjeff	if (ki->ki_p->ki_flag & P_INMEM) {
12221556Srgrimes		/*
12231556Srgrimes		 * The u-area might be swapped out, and we can't get
12241556Srgrimes		 * at it because we have a crashdump and no swap.
12251556Srgrimes		 * If it's here fill in these fields, otherwise, just
12261556Srgrimes		 * leave them 0.
12271556Srgrimes		 */
122869896Smckusick		ki->ki_valid = 1;
12291556Srgrimes	} else
123069896Smckusick		ki->ki_valid = 0;
12311556Srgrimes	/*
12321556Srgrimes	 * save arguments if needed
12331556Srgrimes	 */
1234130991Sgad	if (needcomm) {
1235130991Sgad		if (ki->ki_p->ki_stat == SZOMB)
1236130991Sgad			ki->ki_args = strdup("<defunct>");
1237130991Sgad		else if (UREADOK(ki) || (ki->ki_p->ki_args != NULL))
1238130991Sgad			ki->ki_args = strdup(fmt(kvm_getargv, ki,
1239245610Sjhb			    ki->ki_p->ki_comm, ki->ki_p->ki_tdname, MAXCOMLEN));
1240130991Sgad		else
1241130991Sgad			asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm);
1242131024Sgad		if (ki->ki_args == NULL)
1243130991Sgad			errx(1, "malloc failed");
124453276Speter	} else {
124553276Speter		ki->ki_args = NULL;
124653276Speter	}
1247130991Sgad	if (needenv) {
1248130991Sgad		if (UREADOK(ki))
1249130991Sgad			ki->ki_env = strdup(fmt(kvm_getenvv, ki,
1250245610Sjhb			    (char *)NULL, (char *)NULL, 0));
1251130991Sgad		else
1252130991Sgad			ki->ki_env = strdup("()");
1253130991Sgad		if (ki->ki_env == NULL)
1254130991Sgad			errx(1, "malloc failed");
125553276Speter	} else {
125653276Speter		ki->ki_env = NULL;
125753276Speter	}
12581556Srgrimes}
12591556Srgrimes
1260130816Sgad/* A macro used to improve the readability of pscomp(). */
1261130816Sgad#define	DIFF_RETURN(a, b, field) do {	\
1262130816Sgad	if ((a)->field != (b)->field)	\
1263130816Sgad		return (((a)->field < (b)->field) ? -1 : 1); 	\
1264130816Sgad} while (0)
1265130816Sgad
12661556Srgrimesstatic int
126790110Simppscomp(const void *a, const void *b)
12681556Srgrimes{
1269127596Sgad	const KINFO *ka, *kb;
12701556Srgrimes
1271127596Sgad	ka = a;
1272127596Sgad	kb = b;
1273127596Sgad	/* SORTCPU and SORTMEM are sorted in descending order. */
1274130816Sgad	if (sortby == SORTCPU)
1275130816Sgad		DIFF_RETURN(kb, ka, ki_pcpu);
1276130816Sgad	if (sortby == SORTMEM)
1277130816Sgad		DIFF_RETURN(kb, ka, ki_memsize);
1278127596Sgad	/*
1279127596Sgad	 * TTY's are sorted in ascending order, except that all NODEV
1280127596Sgad	 * processes come before all processes with a device.
1281127596Sgad	 */
1282130816Sgad	if (ka->ki_p->ki_tdev != kb->ki_p->ki_tdev) {
1283130816Sgad		if (ka->ki_p->ki_tdev == NODEV)
1284130816Sgad			return (-1);
1285130816Sgad		if (kb->ki_p->ki_tdev == NODEV)
1286130816Sgad			return (1);
1287130816Sgad		DIFF_RETURN(ka, kb, ki_p->ki_tdev);
1288130816Sgad	}
1289130816Sgad
1290130972Sgad	/* PID's and TID's (threads) are sorted in ascending order. */
1291130816Sgad	DIFF_RETURN(ka, kb, ki_p->ki_pid);
1292130972Sgad	DIFF_RETURN(ka, kb, ki_p->ki_tid);
1293127596Sgad	return (0);
12941556Srgrimes}
1295130816Sgad#undef DIFF_RETURN
12961556Srgrimes
12971556Srgrimes/*
12981556Srgrimes * ICK (all for getopt), would rather hide the ugliness
12991556Srgrimes * here than taint the main code.
13001556Srgrimes *
13011556Srgrimes *  ps foo -> ps -foo
13021556Srgrimes *  ps 34 -> ps -p34
13031556Srgrimes *
13041556Srgrimes * The old convention that 't' with no trailing tty arg means the users
13051556Srgrimes * tty, is only supported if argv[1] doesn't begin with a '-'.  This same
13061556Srgrimes * feature is available with the option 'T', which takes no argument.
13071556Srgrimes */
13081556Srgrimesstatic char *
1309129915Sgadkludge_oldps_options(const char *optlist, char *origval, const char *nextarg)
13101556Srgrimes{
13111556Srgrimes	size_t len;
1312129914Sgad	char *argp, *cp, *newopts, *ns, *optp, *pidp;
13131556Srgrimes
1314102886Sjmallett	/*
1315129914Sgad	 * See if the original value includes any option which takes an
1316129914Sgad	 * argument (and will thus use up the remainder of the string).
1317102886Sjmallett	 */
1318129914Sgad	argp = NULL;
1319129914Sgad	if (optlist != NULL) {
1320129914Sgad		for (cp = origval; *cp != '\0'; cp++) {
1321129914Sgad			optp = strchr(optlist, *cp);
1322129914Sgad			if ((optp != NULL) && *(optp + 1) == ':') {
1323129914Sgad				argp = cp;
1324129914Sgad				break;
1325129914Sgad			}
1326129914Sgad		}
1327129914Sgad	}
1328129914Sgad	if (argp != NULL && *origval == '-')
1329129914Sgad		return (origval);
1330102886Sjmallett
13311556Srgrimes	/*
13321556Srgrimes	 * if last letter is a 't' flag with no argument (in the context
13331556Srgrimes	 * of the oldps options -- option string NOT starting with a '-' --
13341556Srgrimes	 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
133581743Sbrian	 *
1336129634Sgad	 * However, if a flag accepting a string argument is found earlier
1337129634Sgad	 * in the option string (including a possible `t' flag), then the
1338129634Sgad	 * remainder of the string must be the argument to that flag; so
1339129914Sgad	 * do not modify that argument.  Note that a trailing `t' would
1340129914Sgad	 * cause argp to be set, if argp was not already set by some
1341129914Sgad	 * earlier option.
13421556Srgrimes	 */
1343129914Sgad	len = strlen(origval);
1344129914Sgad	cp = origval + len - 1;
1345129914Sgad	pidp = NULL;
1346129915Sgad	if (*cp == 't' && *origval != '-' && cp == argp) {
1347129915Sgad		if (nextarg == NULL || *nextarg == '-' || isdigitch(*nextarg))
1348129915Sgad			*cp = 'T';
1349129915Sgad	} else if (argp == NULL) {
13501556Srgrimes		/*
1351129914Sgad		 * The original value did not include any option which takes
1352129914Sgad		 * an argument (and that would include `p' and `t'), so check
1353129914Sgad		 * the value for trailing number, or comma-separated list of
1354129914Sgad		 * numbers, which we will treat as a pid request.
13551556Srgrimes		 */
1356129914Sgad		if (isdigitch(*cp)) {
1357129914Sgad			while (cp >= origval && (*cp == ',' || isdigitch(*cp)))
1358129914Sgad				--cp;
1359129914Sgad			pidp = cp + 1;
1360129914Sgad		}
13611556Srgrimes	}
1362129914Sgad
13631556Srgrimes	/*
1364129914Sgad	 * If nothing needs to be added to the string, then return
1365129914Sgad	 * the "original" (although possibly modified) value.
13661556Srgrimes	 */
1367129914Sgad	if (*origval == '-' && pidp == NULL)
1368129914Sgad		return (origval);
1369129914Sgad
1370129914Sgad	/*
1371129914Sgad	 * Create a copy of the string to add '-' and/or 'p' to the
1372129914Sgad	 * original value.
1373129914Sgad	 */
1374129914Sgad	if ((newopts = ns = malloc(len + 3)) == NULL)
1375129914Sgad		errx(1, "malloc failed");
1376129914Sgad
1377129914Sgad	if (*origval != '-')
1378129914Sgad		*ns++ = '-';	/* add option flag */
1379129914Sgad
1380129914Sgad	if (pidp == NULL)
1381129914Sgad		strcpy(ns, origval);
1382129914Sgad	else {
1383129914Sgad		/*
1384129914Sgad		 * Copy everything before the pid string, add the `p',
1385129914Sgad		 * and then copy the pid string.
1386129914Sgad		 */
1387129914Sgad		len = pidp - origval;
1388129914Sgad		memcpy(ns, origval, len);
1389129914Sgad		ns += len;
13901556Srgrimes		*ns++ = 'p';
1391129914Sgad		strcpy(ns, pidp);
1392129914Sgad	}
13931556Srgrimes
13941556Srgrimes	return (newopts);
13951556Srgrimes}
13961556Srgrimes
13971556Srgrimesstatic void
1398244154Spjdpidmax_init(void)
1399244154Spjd{
1400244154Spjd	size_t intsize;
1401244154Spjd
1402244154Spjd	intsize = sizeof(pid_max);
1403244154Spjd	if (sysctlbyname("kern.pid_max", &pid_max, &intsize, NULL, 0) < 0) {
1404244154Spjd		warn("unable to read kern.pid_max");
1405244154Spjd		pid_max = 99999;
1406244154Spjd	}
1407244154Spjd}
1408244154Spjd
1409244154Spjdstatic void
141090110Simpusage(void)
14111556Srgrimes{
1412195830Sbrian#define	SINGLE_OPTS	"[-aCcde" OPT_LAZY_f "HhjlmrSTuvwXxZ]"
14131556Srgrimes
1414127499Sgad	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
1415141578Sru	    "usage: ps " SINGLE_OPTS " [-O fmt | -o fmt] [-G gid[,gid...]]",
1416266279Sbdrewery	    "          [-J jid[,jid...]] [-M core] [-N system]",
1417141578Sru	    "          [-p pid[,pid...]] [-t tty[,tty...]] [-U user[,user...]]",
141826465Scharnier	    "       ps [-L]");
14191556Srgrimes	exit(1);
14201556Srgrimes}
1421