ps.c revision 239883
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: head/bin/ps/ps.c 239883 2012-08-29 21:38:34Z emaste $");
511556Srgrimes
521556Srgrimes#include <sys/param.h>
53127546Sgad#include <sys/proc.h>
543296Sdg#include <sys/user.h>
551556Srgrimes#include <sys/stat.h>
561556Srgrimes#include <sys/ioctl.h>
571556Srgrimes#include <sys/sysctl.h>
58137696Scsjp#include <sys/mount.h>
591556Srgrimes
601556Srgrimes#include <ctype.h>
611556Srgrimes#include <err.h>
62127149Sgad#include <errno.h>
631556Srgrimes#include <fcntl.h>
64127499Sgad#include <grp.h>
651556Srgrimes#include <kvm.h>
6613514Smpp#include <limits.h>
6773367Sache#include <locale.h>
681556Srgrimes#include <paths.h>
6990143Smarkm#include <pwd.h>
701556Srgrimes#include <stdio.h>
711556Srgrimes#include <stdlib.h>
721556Srgrimes#include <string.h>
731556Srgrimes#include <unistd.h>
741556Srgrimes
751556Srgrimes#include "ps.h"
761556Srgrimes
77173492Sjhb#define	_PATH_PTS	"/dev/pts/"
78173492Sjhb
79127499Sgad#define	W_SEP	" \t"		/* "Whitespace" list separators */
80127499Sgad#define	T_SEP	","		/* "Terminate-element" list separators */
8166377Sbrian
82127537Sgad#ifdef LAZY_PS
83127555Sgad#define	DEF_UREAD	0
84127537Sgad#define	OPT_LAZY_f	"f"
85127537Sgad#else
86127555Sgad#define	DEF_UREAD	1	/* Always do the more-expensive read. */
87127537Sgad#define	OPT_LAZY_f		/* I.e., the `-f' option is not added. */
88127537Sgad#endif
89127537Sgad
90129914Sgad/*
91129971Sgad * isdigit takes an `int', but expects values in the range of unsigned char.
92129971Sgad * This wrapper ensures that values from a 'char' end up in the correct range.
93129914Sgad */
94129971Sgad#define	isdigitch(Anychar) isdigit((u_char)(Anychar))
95129914Sgad
96127537Sgadint	 cflag;			/* -c */
97127537Sgadint	 eval;			/* Exit value */
98127537Sgadtime_t	 now;			/* Current time(3) value */
99127537Sgadint	 rawcpu;		/* -C */
100127537Sgadint	 sumrusage;		/* -S */
101127537Sgadint	 termwidth;		/* Width of the screen (0 == infinity). */
102173004Sjulianint	 showthreads;		/* will threads be shown? */
103127537Sgad
104130999Sgadstruct velisthead varlist = STAILQ_HEAD_INITIALIZER(varlist);
1051556Srgrimes
106127537Sgadstatic int	 forceuread = DEF_UREAD; /* Do extra work to get u-area. */
107127537Sgadstatic kvm_t	*kd;
108127537Sgadstatic int	 needcomm;	/* -o "command" */
109127537Sgadstatic int	 needenv;	/* -e */
110127537Sgadstatic int	 needuser;	/* -o "user" */
111127537Sgadstatic int	 optfatal;	/* Fatal error parsing some list-option. */
1121556Srgrimes
113127537Sgadstatic enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
11497966Sjmallett
115127499Sgadstruct listinfo;
116127537Sgadtypedef	int	addelem_rtn(struct listinfo *_inf, const char *_elem);
117127499Sgad
118127499Sgadstruct listinfo {
119127499Sgad	int		 count;
120127499Sgad	int		 maxcount;
121127499Sgad	int		 elemsize;
122127499Sgad	addelem_rtn	*addelem;
123127499Sgad	const char	*lname;
124127499Sgad	union {
125127499Sgad		gid_t	*gids;
126127499Sgad		pid_t	*pids;
127127499Sgad		dev_t	*ttys;
128127499Sgad		uid_t	*uids;
129127499Sgad		void	*ptr;
130127823Sgad	} l;
131127499Sgad};
132127499Sgad
133127499Sgadstatic int	 addelem_gid(struct listinfo *, const char *);
134127499Sgadstatic int	 addelem_pid(struct listinfo *, const char *);
135127499Sgadstatic int	 addelem_tty(struct listinfo *, const char *);
136127499Sgadstatic int	 addelem_uid(struct listinfo *, const char *);
137127499Sgadstatic void	 add_list(struct listinfo *, const char *);
138192239Sbrianstatic void	 descendant_sort(KINFO *, int);
139225868Straszstatic void	 format_output(KINFO *);
140127499Sgadstatic void	*expand_list(struct listinfo *);
141127598Sgadstatic const char *
142127598Sgad		 fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int),
143127536Sgad		    KINFO *, char *, int);
144127499Sgadstatic void	 free_list(struct listinfo *);
145127499Sgadstatic void	 init_list(struct listinfo *, addelem_rtn, int, const char *);
146129952Sgadstatic char	*kludge_oldps_options(const char *, char *, const char *);
147127536Sgadstatic int	 pscomp(const void *, const void *);
148127536Sgadstatic void	 saveuser(KINFO *);
149127536Sgadstatic void	 scanvars(void);
150127536Sgadstatic void	 sizevars(void);
151127536Sgadstatic void	 usage(void);
152127499Sgad
15397875Sjmallettstatic char dfmt[] = "pid,tt,state,time,command";
154129635Sgadstatic char jfmt[] = "user,pid,ppid,pgid,sid,jobc,state,tt,time,command";
155127538Sgadstatic char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,"
156127538Sgad			"tt,time,command";
15790143Smarkmstatic char   o1[] = "pid";
15897875Sjmallettstatic char   o2[] = "tt,state,time,command";
15997875Sjmallettstatic char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
160127538Sgadstatic char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,"
161127538Sgad			"%cpu,%mem,command";
162105831Srwatsonstatic char Zfmt[] = "label";
1631556Srgrimes
164192239Sbrian#define	PS_ARGS	"AaCcde" OPT_LAZY_f "G:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ"
16598494Ssobomax
1661556Srgrimesint
16790110Simpmain(int argc, char *argv[])
1681556Srgrimes{
169127499Sgad	struct listinfo gidlist, pgrplist, pidlist;
170127499Sgad	struct listinfo ruidlist, sesslist, ttylist, uidlist;
1711556Srgrimes	struct kinfo_proc *kp;
172225868Strasz	KINFO *kinfo = NULL, *next_KINFO;
173225868Strasz	KINFO_STR *ks;
1741556Srgrimes	struct varent *vent;
1751556Srgrimes	struct winsize ws;
176225868Strasz	const char *nlistf, *memf, *fmtstr, *str;
177127539Sgad	char *cols;
178225868Strasz	int all, ch, elem, flag, _fmt, i, lineno, linelen, left;
179192239Sbrian	int descendancy, nentries, nkept, nselectors;
180173004Sjulian	int prtheader, wflag, what, xkeep, xkeep_implied;
18190143Smarkm	char errbuf[_POSIX2_LINE_MAX];
1821556Srgrimes
18311809Sache	(void) setlocale(LC_ALL, "");
184127542Sgad	time(&now);			/* Used by routines in print.c. */
18511809Sache
18697804Stjr	if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0')
18797804Stjr		termwidth = atoi(cols);
18897804Stjr	else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
1891556Srgrimes	     ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
1901556Srgrimes	     ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) == -1) ||
1911556Srgrimes	     ws.ws_col == 0)
1921556Srgrimes		termwidth = 79;
1931556Srgrimes	else
1941556Srgrimes		termwidth = ws.ws_col - 1;
1951556Srgrimes
19698494Ssobomax	/*
197129914Sgad	 * Hide a number of option-processing kludges in a separate routine,
198129914Sgad	 * to support some historical BSD behaviors, such as `ps axu'.
19998494Ssobomax	 */
200129914Sgad	if (argc > 1)
201129915Sgad		argv[1] = kludge_oldps_options(PS_ARGS, argv[1], argv[2]);
2021556Srgrimes
203192239Sbrian	all = descendancy = _fmt = nselectors = optfatal = 0;
204127542Sgad	prtheader = showthreads = wflag = xkeep_implied = 0;
205127542Sgad	xkeep = -1;			/* Neither -x nor -X. */
206127499Sgad	init_list(&gidlist, addelem_gid, sizeof(gid_t), "group");
207127499Sgad	init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group");
208127499Sgad	init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id");
209127499Sgad	init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser");
210127499Sgad	init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id");
211127499Sgad	init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty");
212127499Sgad	init_list(&uidlist, addelem_uid, sizeof(uid_t), "user");
213203688Sbrucec	memf = _PATH_DEVNULL;
214203688Sbrucec	nlistf = NULL;
21598494Ssobomax	while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
216180596Skevlo		switch (ch) {
217127499Sgad		case 'A':
218127499Sgad			/*
219127499Sgad			 * Exactly the same as `-ax'.   This has been
220222178Suqs			 * added for compatibility with SUSv3, but for
221127499Sgad			 * now it will not be described in the man page.
222127499Sgad			 */
223127499Sgad			nselectors++;
224127499Sgad			all = xkeep = 1;
225127499Sgad			break;
2261556Srgrimes		case 'a':
227127499Sgad			nselectors++;
2281556Srgrimes			all = 1;
2291556Srgrimes			break;
23019068Speter		case 'C':
23119068Speter			rawcpu = 1;
23219068Speter			break;
23319068Speter		case 'c':
23419068Speter			cflag = 1;
23519068Speter			break;
236192239Sbrian		case 'd':
237192239Sbrian			descendancy = 1;
238192239Sbrian			break;
2391556Srgrimes		case 'e':			/* XXX set ufmt */
2401556Srgrimes			needenv = 1;
2411556Srgrimes			break;
242127506Sgad#ifdef LAZY_PS
243127506Sgad		case 'f':
244127506Sgad			if (getuid() == 0 || getgid() == 0)
245127542Sgad				forceuread = 1;
246127506Sgad			break;
247127506Sgad#endif
248127499Sgad		case 'G':
249127499Sgad			add_list(&gidlist, optarg);
250127499Sgad			xkeep_implied = 1;
251127499Sgad			nselectors++;
252127499Sgad			break;
253127542Sgad		case 'g':
254127499Sgad#if 0
255127597Sgad			/*-
256127542Sgad			 * XXX - This SUSv3 behavior is still under debate
257127542Sgad			 *	since it conflicts with the (undocumented)
258127542Sgad			 *	`-g' option.  So we skip it for now.
259127542Sgad			 */
260127499Sgad			add_list(&pgrplist, optarg);
261127499Sgad			xkeep_implied = 1;
262127499Sgad			nselectors++;
263127499Sgad			break;
264127499Sgad#else
265127542Sgad			/* The historical BSD-ish (from SunOS) behavior. */
2661556Srgrimes			break;			/* no-op */
267127499Sgad#endif
268116265Sscottl		case 'H':
269126127Sdeischen			showthreads = KERN_PROC_INC_THREAD;
270116265Sscottl			break;
2711556Srgrimes		case 'h':
2721556Srgrimes			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
2731556Srgrimes			break;
2741556Srgrimes		case 'j':
275109502Sjmallett			parsefmt(jfmt, 0);
27690143Smarkm			_fmt = 1;
2771556Srgrimes			jfmt[0] = '\0';
2781556Srgrimes			break;
2791556Srgrimes		case 'L':
2801556Srgrimes			showkey();
2811556Srgrimes			exit(0);
2821556Srgrimes		case 'l':
283109502Sjmallett			parsefmt(lfmt, 0);
28490143Smarkm			_fmt = 1;
2851556Srgrimes			lfmt[0] = '\0';
2861556Srgrimes			break;
2871556Srgrimes		case 'M':
2881556Srgrimes			memf = optarg;
2891556Srgrimes			break;
2901556Srgrimes		case 'm':
2911556Srgrimes			sortby = SORTMEM;
2921556Srgrimes			break;
2931556Srgrimes		case 'N':
2941556Srgrimes			nlistf = optarg;
2951556Srgrimes			break;
2961556Srgrimes		case 'O':
297109502Sjmallett			parsefmt(o1, 1);
298109502Sjmallett			parsefmt(optarg, 1);
299109502Sjmallett			parsefmt(o2, 1);
3001556Srgrimes			o1[0] = o2[0] = '\0';
30190143Smarkm			_fmt = 1;
3021556Srgrimes			break;
3031556Srgrimes		case 'o':
304109502Sjmallett			parsefmt(optarg, 1);
30590143Smarkm			_fmt = 1;
3061556Srgrimes			break;
3071556Srgrimes		case 'p':
308127499Sgad			add_list(&pidlist, optarg);
309127499Sgad			/*
310127499Sgad			 * Note: `-p' does not *set* xkeep, but any values
311127499Sgad			 * from pidlist are checked before xkeep is.  That
312127499Sgad			 * way they are always matched, even if the user
313127499Sgad			 * specifies `-X'.
314127499Sgad			 */
315127499Sgad			nselectors++;
3161556Srgrimes			break;
317127499Sgad#if 0
318127499Sgad		case 'R':
319127597Sgad			/*-
320127542Sgad			 * XXX - This un-standard option is still under
321127542Sgad			 *	debate.  This is what SUSv3 defines as
322127542Sgad			 *	the `-U' option, and while it would be
323127542Sgad			 *	nice to have, it could cause even more
324127542Sgad			 *	confusion to implement it as `-R'.
325127542Sgad			 */
326127499Sgad			add_list(&ruidlist, optarg);
327127499Sgad			xkeep_implied = 1;
328127499Sgad			nselectors++;
329127499Sgad			break;
330127499Sgad#endif
3311556Srgrimes		case 'r':
3321556Srgrimes			sortby = SORTCPU;
3331556Srgrimes			break;
3341556Srgrimes		case 'S':
3351556Srgrimes			sumrusage = 1;
3361556Srgrimes			break;
337127499Sgad#if 0
338127499Sgad		case 's':
339127597Sgad			/*-
340127542Sgad			 * XXX - This non-standard option is still under
341127542Sgad			 *	debate.  This *is* supported on Solaris,
342127542Sgad			 *	Linux, and IRIX, but conflicts with `-s'
343127542Sgad			 *	on NetBSD and maybe some older BSD's.
344127542Sgad			 */
345127499Sgad			add_list(&sesslist, optarg);
346127499Sgad			xkeep_implied = 1;
347127499Sgad			nselectors++;
348127499Sgad			break;
349127499Sgad#endif
3501556Srgrimes		case 'T':
3511556Srgrimes			if ((optarg = ttyname(STDIN_FILENO)) == NULL)
3521556Srgrimes				errx(1, "stdin: not a terminal");
3531556Srgrimes			/* FALLTHROUGH */
354127499Sgad		case 't':
355127499Sgad			add_list(&ttylist, optarg);
356127499Sgad			xkeep_implied = 1;
357127499Sgad			nselectors++;
3581556Srgrimes			break;
35913020Speter		case 'U':
360127499Sgad			/* This is what SUSv3 defines as the `-u' option. */
361127499Sgad			add_list(&uidlist, optarg);
362127499Sgad			xkeep_implied = 1;
363127499Sgad			nselectors++;
36413020Speter			break;
3651556Srgrimes		case 'u':
366109502Sjmallett			parsefmt(ufmt, 0);
3671556Srgrimes			sortby = SORTCPU;
36890143Smarkm			_fmt = 1;
3691556Srgrimes			ufmt[0] = '\0';
3701556Srgrimes			break;
3711556Srgrimes		case 'v':
372109502Sjmallett			parsefmt(vfmt, 0);
3731556Srgrimes			sortby = SORTMEM;
37490143Smarkm			_fmt = 1;
3751556Srgrimes			vfmt[0] = '\0';
3761556Srgrimes			break;
3771556Srgrimes		case 'w':
3781556Srgrimes			if (wflag)
3791556Srgrimes				termwidth = UNLIMITED;
3801556Srgrimes			else if (termwidth < 131)
3811556Srgrimes				termwidth = 131;
3821556Srgrimes			wflag++;
3831556Srgrimes			break;
384127499Sgad		case 'X':
385127499Sgad			/*
386127499Sgad			 * Note that `-X' and `-x' are not standard "selector"
387127499Sgad			 * options. For most selector-options, we check *all*
388127499Sgad			 * processes to see if any are matched by the given
389127499Sgad			 * value(s).  After we have a set of all the matched
390127499Sgad			 * processes, then `-X' and `-x' govern whether we
391127499Sgad			 * modify that *matched* set for processes which do
392127499Sgad			 * not have a controlling terminal.  `-X' causes
393127499Sgad			 * those processes to be deleted from the matched
394127499Sgad			 * set, while `-x' causes them to be kept.
395127499Sgad			 */
396127499Sgad			xkeep = 0;
397127499Sgad			break;
3981556Srgrimes		case 'x':
399127499Sgad			xkeep = 1;
4001556Srgrimes			break;
40186922Sgreen		case 'Z':
402109502Sjmallett			parsefmt(Zfmt, 0);
40386922Sgreen			Zfmt[0] = '\0';
40486922Sgreen			break;
4051556Srgrimes		case '?':
4061556Srgrimes		default:
4071556Srgrimes			usage();
4081556Srgrimes		}
4091556Srgrimes	argc -= optind;
4101556Srgrimes	argv += optind;
411129914Sgad
412129914Sgad	/*
413129914Sgad	 * If there arguments after processing all the options, attempt
414129914Sgad	 * to treat them as a list of process ids.
415129914Sgad	 */
416129914Sgad	while (*argv) {
417129914Sgad		if (!isdigitch(**argv))
418129914Sgad			break;
419129914Sgad		add_list(&pidlist, *argv);
420129914Sgad		argv++;
421129914Sgad	}
422129914Sgad	if (*argv) {
423129914Sgad		fprintf(stderr, "%s: illegal argument: %s\n",
424129914Sgad		    getprogname(), *argv);
425129914Sgad		usage();
426129914Sgad	}
427127499Sgad	if (optfatal)
428127542Sgad		exit(1);		/* Error messages already printed. */
429127542Sgad	if (xkeep < 0)			/* Neither -X nor -x was specified. */
430127499Sgad		xkeep = xkeep_implied;
431127499Sgad
43289909Sru	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
4331556Srgrimes	if (kd == 0)
4341556Srgrimes		errx(1, "%s", errbuf);
4351556Srgrimes
43690143Smarkm	if (!_fmt)
437109502Sjmallett		parsefmt(dfmt, 0);
4381556Srgrimes
439127499Sgad	if (nselectors == 0) {
440127823Sgad		uidlist.l.ptr = malloc(sizeof(uid_t));
441127823Sgad		if (uidlist.l.ptr == NULL)
44297877Sjmallett			errx(1, "malloc failed");
443127499Sgad		nselectors = 1;
444127499Sgad		uidlist.count = uidlist.maxcount = 1;
445127823Sgad		*uidlist.l.uids = getuid();
44666377Sbrian	}
4471556Srgrimes
4481556Srgrimes	/*
4491556Srgrimes	 * scan requested variables, noting what structures are needed,
45053170Skris	 * and adjusting header widths as appropriate.
4511556Srgrimes	 */
4521556Srgrimes	scanvars();
453127499Sgad
4541556Srgrimes	/*
455127499Sgad	 * Get process list.  If the user requested just one selector-
456127499Sgad	 * option, then kvm_getprocs can be asked to return just those
457127499Sgad	 * processes.  Otherwise, have it return all processes, and
458127499Sgad	 * then this routine will search that full list and select the
459127499Sgad	 * processes which match any of the user's selector-options.
4601556Srgrimes	 */
461127499Sgad	what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
462127499Sgad	flag = 0;
463127499Sgad	if (nselectors == 1) {
464129600Sgad		if (gidlist.count == 1) {
465129600Sgad			what = KERN_PROC_RGID | showthreads;
466129600Sgad			flag = *gidlist.l.gids;
467129600Sgad			nselectors = 0;
468129600Sgad		} else if (pgrplist.count == 1) {
469127499Sgad			what = KERN_PROC_PGRP | showthreads;
470127823Sgad			flag = *pgrplist.l.pids;
471127499Sgad			nselectors = 0;
472127499Sgad		} else if (pidlist.count == 1) {
473127499Sgad			what = KERN_PROC_PID | showthreads;
474127823Sgad			flag = *pidlist.l.pids;
475127499Sgad			nselectors = 0;
476127499Sgad		} else if (ruidlist.count == 1) {
477127499Sgad			what = KERN_PROC_RUID | showthreads;
478127823Sgad			flag = *ruidlist.l.uids;
479127499Sgad			nselectors = 0;
480127499Sgad		} else if (sesslist.count == 1) {
481127499Sgad			what = KERN_PROC_SESSION | showthreads;
482127823Sgad			flag = *sesslist.l.pids;
483127499Sgad			nselectors = 0;
484127499Sgad		} else if (ttylist.count == 1) {
485127499Sgad			what = KERN_PROC_TTY | showthreads;
486127823Sgad			flag = *ttylist.l.ttys;
487127499Sgad			nselectors = 0;
488127499Sgad		} else if (uidlist.count == 1) {
489127499Sgad			what = KERN_PROC_UID | showthreads;
490127823Sgad			flag = *uidlist.l.uids;
491127499Sgad			nselectors = 0;
492127499Sgad		} else if (all) {
493127499Sgad			/* No need for this routine to select processes. */
494127499Sgad			nselectors = 0;
495127499Sgad		}
4961556Srgrimes	}
497126127Sdeischen
4981556Srgrimes	/*
4991556Srgrimes	 * select procs
5001556Srgrimes	 */
501127499Sgad	nentries = -1;
502127149Sgad	kp = kvm_getprocs(kd, what, flag, &nentries);
503127544Sgad	if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0))
5041556Srgrimes		errx(1, "%s", kvm_geterr(kd));
505127499Sgad	nkept = 0;
506127149Sgad	if (nentries > 0) {
507127149Sgad		if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
508127149Sgad			errx(1, "malloc failed");
509127149Sgad		for (i = nentries; --i >= 0; ++kp) {
510127499Sgad			/*
511127499Sgad			 * If the user specified multiple selection-criteria,
512127499Sgad			 * then keep any process matched by the inclusive OR
513127499Sgad			 * of all the selection-criteria given.
514127499Sgad			 */
515127499Sgad			if (pidlist.count > 0) {
516127499Sgad				for (elem = 0; elem < pidlist.count; elem++)
517127823Sgad					if (kp->ki_pid == pidlist.l.pids[elem])
518127499Sgad						goto keepit;
519127499Sgad			}
520127499Sgad			/*
521127499Sgad			 * Note that we had to process pidlist before
522127499Sgad			 * filtering out processes which do not have
523127499Sgad			 * a controlling terminal.
524127499Sgad			 */
525127499Sgad			if (xkeep == 0) {
526127499Sgad				if ((kp->ki_tdev == NODEV ||
527127499Sgad				    (kp->ki_flag & P_CONTROLT) == 0))
528127499Sgad					continue;
529127499Sgad			}
530127499Sgad			if (nselectors == 0)
531127499Sgad				goto keepit;
532127499Sgad			if (gidlist.count > 0) {
533127499Sgad				for (elem = 0; elem < gidlist.count; elem++)
534127823Sgad					if (kp->ki_rgid == gidlist.l.gids[elem])
535127499Sgad						goto keepit;
536127499Sgad			}
537127499Sgad			if (pgrplist.count > 0) {
538127499Sgad				for (elem = 0; elem < pgrplist.count; elem++)
539127823Sgad					if (kp->ki_pgid ==
540127823Sgad					    pgrplist.l.pids[elem])
541127499Sgad						goto keepit;
542127499Sgad			}
543127499Sgad			if (ruidlist.count > 0) {
544127499Sgad				for (elem = 0; elem < ruidlist.count; elem++)
545127823Sgad					if (kp->ki_ruid ==
546127823Sgad					    ruidlist.l.uids[elem])
547127499Sgad						goto keepit;
548127499Sgad			}
549127499Sgad			if (sesslist.count > 0) {
550127499Sgad				for (elem = 0; elem < sesslist.count; elem++)
551127823Sgad					if (kp->ki_sid == sesslist.l.pids[elem])
552127499Sgad						goto keepit;
553127499Sgad			}
554127499Sgad			if (ttylist.count > 0) {
555127499Sgad				for (elem = 0; elem < ttylist.count; elem++)
556127823Sgad					if (kp->ki_tdev == ttylist.l.ttys[elem])
557127499Sgad						goto keepit;
558127499Sgad			}
559127499Sgad			if (uidlist.count > 0) {
560127499Sgad				for (elem = 0; elem < uidlist.count; elem++)
561127823Sgad					if (kp->ki_uid == uidlist.l.uids[elem])
562127499Sgad						goto keepit;
563127499Sgad			}
564127499Sgad			/*
565127499Sgad			 * This process did not match any of the user's
566127499Sgad			 * selector-options, so skip the process.
567127499Sgad			 */
568127499Sgad			continue;
569127499Sgad
570127499Sgad		keepit:
571130816Sgad			next_KINFO = &kinfo[nkept];
572130816Sgad			next_KINFO->ki_p = kp;
573192239Sbrian			next_KINFO->ki_d.level = 0;
574192239Sbrian			next_KINFO->ki_d.prefix = NULL;
575130816Sgad			next_KINFO->ki_pcpu = getpcpu(next_KINFO);
576130816Sgad			if (sortby == SORTMEM)
577130816Sgad				next_KINFO->ki_memsize = kp->ki_tsize +
578130816Sgad				    kp->ki_dsize + kp->ki_ssize;
579127149Sgad			if (needuser)
580130816Sgad				saveuser(next_KINFO);
581127499Sgad			nkept++;
582127149Sgad		}
5831556Srgrimes	}
58425271Sjkh
58525271Sjkh	sizevars();
58625271Sjkh
587225868Strasz	if (nkept == 0) {
588225868Strasz		printheader();
58962803Swill		exit(1);
590225868Strasz	}
591127499Sgad
5921556Srgrimes	/*
5931556Srgrimes	 * sort proc list
5941556Srgrimes	 */
595127499Sgad	qsort(kinfo, nkept, sizeof(KINFO), pscomp);
596192239Sbrian
5971556Srgrimes	/*
598192239Sbrian	 * We want things in descendant order
599192239Sbrian	 */
600192239Sbrian	if (descendancy)
601192239Sbrian		descendant_sort(kinfo, nkept);
602192239Sbrian
603225868Strasz
604192239Sbrian	/*
605225868Strasz	 * Prepare formatted output.
6061556Srgrimes	 */
607225868Strasz	for (i = 0; i < nkept; i++)
608225868Strasz		format_output(&kinfo[i]);
609225868Strasz
610225868Strasz	/*
611225868Strasz	 * Print header.
612225868Strasz	 */
613225868Strasz	printheader();
614225868Strasz
615225868Strasz	/*
616225868Strasz	 * Output formatted lines.
617225868Strasz	 */
618127499Sgad	for (i = lineno = 0; i < nkept; i++) {
619225868Strasz		linelen = 0;
620130999Sgad		STAILQ_FOREACH(vent, &varlist, next_ve) {
621225868Strasz	        	if (vent->var->flag & LJUST)
622225868Strasz				fmtstr = "%-*s";
623225868Strasz			else
624225868Strasz				fmtstr = "%*s";
625225868Strasz
626225868Strasz			ks = STAILQ_FIRST(&kinfo[i].ki_ks);
627225868Strasz			STAILQ_REMOVE_HEAD(&kinfo[i].ki_ks, ks_next);
628229782Suqs			/* Truncate rightmost column if necessary.  */
629225868Strasz			if (STAILQ_NEXT(vent, next_ve) == NULL &&
630225868Strasz			   termwidth != UNLIMITED && ks->ks_str != NULL) {
631225868Strasz				left = termwidth - linelen;
632225868Strasz				if (left > 0 && left < (int)strlen(ks->ks_str))
633225868Strasz					ks->ks_str[left] = '\0';
634225868Strasz			}
635225868Strasz			str = ks->ks_str;
636225868Strasz			if (str == NULL)
637225868Strasz				str = "-";
638225868Strasz			/* No padding for the last column, if it's LJUST. */
639225868Strasz			if (STAILQ_NEXT(vent, next_ve) == NULL &&
640225868Strasz			    vent->var->flag & LJUST)
641225868Strasz				linelen += printf(fmtstr, 0, str);
642225868Strasz			else
643225868Strasz				linelen += printf(fmtstr, vent->var->width, str);
644225868Strasz
645225868Strasz			if (ks->ks_str != NULL) {
646225868Strasz				free(ks->ks_str);
647225868Strasz				ks->ks_str = NULL;
648225868Strasz			}
649225868Strasz			free(ks);
650225868Strasz			ks = NULL;
651225868Strasz
652225868Strasz			if (STAILQ_NEXT(vent, next_ve) != NULL) {
6531556Srgrimes				(void)putchar(' ');
654225868Strasz				linelen++;
655225868Strasz			}
6561556Srgrimes		}
6571556Srgrimes		(void)putchar('\n');
6581556Srgrimes		if (prtheader && lineno++ == prtheader - 4) {
6591556Srgrimes			(void)putchar('\n');
6601556Srgrimes			printheader();
6611556Srgrimes			lineno = 0;
6621556Srgrimes		}
6631556Srgrimes	}
664127499Sgad	free_list(&gidlist);
665127499Sgad	free_list(&pidlist);
666127499Sgad	free_list(&pgrplist);
667127499Sgad	free_list(&ruidlist);
668127499Sgad	free_list(&sesslist);
669127499Sgad	free_list(&ttylist);
670127499Sgad	free_list(&uidlist);
671192239Sbrian	for (i = 0; i < nkept; i++)
672192239Sbrian		free(kinfo[i].ki_d.prefix);
673192239Sbrian	free(kinfo);
67466377Sbrian
6751556Srgrimes	exit(eval);
6761556Srgrimes}
6771556Srgrimes
678127499Sgadstatic int
679127499Sgadaddelem_gid(struct listinfo *inf, const char *elem)
680127499Sgad{
681127499Sgad	struct group *grp;
682127499Sgad	const char *nameorID;
683127499Sgad	char *endp;
684127602Sgad	u_long bigtemp;
685127499Sgad
686127499Sgad	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
687127499Sgad		if (*elem == '\0')
688127499Sgad			warnx("Invalid (zero-length) %s name", inf->lname);
689127499Sgad		else
690127499Sgad			warnx("%s name too long: %s", inf->lname, elem);
691127499Sgad		optfatal = 1;
692127542Sgad		return (0);		/* Do not add this value. */
693127499Sgad	}
694127499Sgad
695127499Sgad	/*
696127499Sgad	 * SUSv3 states that `ps -G grouplist' should match "real-group
697127499Sgad	 * ID numbers", and does not mention group-names.  I do want to
698127499Sgad	 * also support group-names, so this tries for a group-id first,
699127499Sgad	 * and only tries for a name if that doesn't work.  This is the
700127499Sgad	 * opposite order of what is done in addelem_uid(), but in
701127499Sgad	 * practice the order would only matter for group-names which
702127499Sgad	 * are all-numeric.
703127499Sgad	 */
704127499Sgad	grp = NULL;
705127499Sgad	nameorID = "named";
706127499Sgad	errno = 0;
707127602Sgad	bigtemp = strtoul(elem, &endp, 10);
708127602Sgad	if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) {
709127499Sgad		nameorID = "name or ID matches";
710127602Sgad		grp = getgrgid((gid_t)bigtemp);
711127499Sgad	}
712127499Sgad	if (grp == NULL)
713127499Sgad		grp = getgrnam(elem);
714127499Sgad	if (grp == NULL) {
715127499Sgad		warnx("No %s %s '%s'", inf->lname, nameorID, elem);
716127499Sgad		optfatal = 1;
717129967Sgad		return (0);
718127499Sgad	}
719127499Sgad	if (inf->count >= inf->maxcount)
720127499Sgad		expand_list(inf);
721127823Sgad	inf->l.gids[(inf->count)++] = grp->gr_gid;
722127499Sgad	return (1);
723127499Sgad}
724127499Sgad
725127597Sgad#define	BSD_PID_MAX	99999		/* Copy of PID_MAX from sys/proc.h. */
726127499Sgadstatic int
727127499Sgadaddelem_pid(struct listinfo *inf, const char *elem)
728127149Sgad{
729127539Sgad	char *endp;
730127149Sgad	long tempid;
731127149Sgad
732129917Sgad	if (*elem == '\0') {
733129917Sgad		warnx("Invalid (zero-length) process id");
734129917Sgad		optfatal = 1;
735129917Sgad		return (0);		/* Do not add this value. */
736127149Sgad	}
737127149Sgad
738129952Sgad	errno = 0;
739129952Sgad	tempid = strtol(elem, &endp, 10);
740129952Sgad	if (*endp != '\0' || tempid < 0 || elem == endp) {
741129952Sgad		warnx("Invalid %s: %s", inf->lname, elem);
742129952Sgad		errno = ERANGE;
743129952Sgad	} else if (errno != 0 || tempid > BSD_PID_MAX) {
744129952Sgad		warnx("%s too large: %s", inf->lname, elem);
745129952Sgad		errno = ERANGE;
746129952Sgad	}
747129952Sgad	if (errno == ERANGE) {
748129952Sgad		optfatal = 1;
749129967Sgad		return (0);
750129952Sgad	}
751127499Sgad	if (inf->count >= inf->maxcount)
752127499Sgad		expand_list(inf);
753127823Sgad	inf->l.pids[(inf->count)++] = tempid;
754127499Sgad	return (1);
755127499Sgad}
756127499Sgad#undef	BSD_PID_MAX
757127149Sgad
758131010Sgad/*-
759131010Sgad * The user can specify a device via one of three formats:
760173492Sjhb *     1) fully qualified, e.g.:     /dev/ttyp0 /dev/console	/dev/pts/0
761173492Sjhb *     2) missing "/dev", e.g.:      ttyp0      console		pts/0
762173492Sjhb *     3) two-letters, e.g.:         p0         co		0
763131010Sgad *        (matching letters that would be seen in the "TT" column)
764131010Sgad */
765127499Sgadstatic int
766127499Sgadaddelem_tty(struct listinfo *inf, const char *elem)
767127499Sgad{
768127539Sgad	const char *ttypath;
769127539Sgad	struct stat sb;
770173492Sjhb	char pathbuf[PATH_MAX], pathbuf2[PATH_MAX], pathbuf3[PATH_MAX];
771127499Sgad
772131010Sgad	ttypath = NULL;
773131209Sgad	pathbuf2[0] = '\0';
774173492Sjhb	pathbuf3[0] = '\0';
775131010Sgad	switch (*elem) {
776131010Sgad	case '/':
777127499Sgad		ttypath = elem;
778131010Sgad		break;
779131010Sgad	case 'c':
780131010Sgad		if (strcmp(elem, "co") == 0) {
781131010Sgad			ttypath = _PATH_CONSOLE;
782131010Sgad			break;
783131010Sgad		}
784131010Sgad		/* FALLTHROUGH */
785131010Sgad	default:
786131010Sgad		strlcpy(pathbuf, _PATH_DEV, sizeof(pathbuf));
787131010Sgad		strlcat(pathbuf, elem, sizeof(pathbuf));
788131010Sgad		ttypath = pathbuf;
789131209Sgad		if (strncmp(pathbuf, _PATH_TTY, strlen(_PATH_TTY)) == 0)
790131010Sgad			break;
791173492Sjhb		if (strncmp(pathbuf, _PATH_PTS, strlen(_PATH_PTS)) == 0)
792173492Sjhb			break;
793131010Sgad		if (strcmp(pathbuf, _PATH_CONSOLE) == 0)
794131010Sgad			break;
795131209Sgad		/* Check to see if /dev/tty${elem} exists */
796131209Sgad		strlcpy(pathbuf2, _PATH_TTY, sizeof(pathbuf2));
797131209Sgad		strlcat(pathbuf2, elem, sizeof(pathbuf2));
798131209Sgad		if (stat(pathbuf2, &sb) == 0 && S_ISCHR(sb.st_mode)) {
799131010Sgad			/* No need to repeat stat() && S_ISCHR() checks */
800192280Sbrian			ttypath = NULL;
801131010Sgad			break;
802131010Sgad		}
803173492Sjhb		/* Check to see if /dev/pts/${elem} exists */
804173492Sjhb		strlcpy(pathbuf3, _PATH_PTS, sizeof(pathbuf3));
805173492Sjhb		strlcat(pathbuf3, elem, sizeof(pathbuf3));
806173492Sjhb		if (stat(pathbuf3, &sb) == 0 && S_ISCHR(sb.st_mode)) {
807173492Sjhb			/* No need to repeat stat() && S_ISCHR() checks */
808192280Sbrian			ttypath = NULL;
809173492Sjhb			break;
810173492Sjhb		}
811131010Sgad		break;
812127499Sgad	}
813131010Sgad	if (ttypath) {
814131010Sgad		if (stat(ttypath, &sb) == -1) {
815173492Sjhb			if (pathbuf3[0] != '\0')
816173492Sjhb				warn("%s, %s, and %s", pathbuf3, pathbuf2,
817173492Sjhb				    ttypath);
818131209Sgad			else
819131209Sgad				warn("%s", ttypath);
820131010Sgad			optfatal = 1;
821131010Sgad			return (0);
822131010Sgad		}
823131010Sgad		if (!S_ISCHR(sb.st_mode)) {
824173492Sjhb			if (pathbuf3[0] != '\0')
825173492Sjhb				warnx("%s, %s, and %s: Not a terminal",
826173492Sjhb				    pathbuf3, pathbuf2, ttypath);
827131209Sgad			else
828131209Sgad				warnx("%s: Not a terminal", ttypath);
829131010Sgad			optfatal = 1;
830131010Sgad			return (0);
831131010Sgad		}
832127499Sgad	}
833127499Sgad	if (inf->count >= inf->maxcount)
834127499Sgad		expand_list(inf);
835127823Sgad	inf->l.ttys[(inf->count)++] = sb.st_rdev;
836127499Sgad	return (1);
837127149Sgad}
838127149Sgad
839127499Sgadstatic int
840127499Sgadaddelem_uid(struct listinfo *inf, const char *elem)
84166377Sbrian{
84266377Sbrian	struct passwd *pwd;
843127539Sgad	char *endp;
844127602Sgad	u_long bigtemp;
84566377Sbrian
846127499Sgad	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
847127499Sgad		if (*elem == '\0')
848127499Sgad			warnx("Invalid (zero-length) %s name", inf->lname);
849127499Sgad		else
850127499Sgad			warnx("%s name too long: %s", inf->lname, elem);
851127499Sgad		optfatal = 1;
852127542Sgad		return (0);		/* Do not add this value. */
853127499Sgad	}
85466377Sbrian
855127499Sgad	pwd = getpwnam(elem);
856127499Sgad	if (pwd == NULL) {
857127499Sgad		errno = 0;
858127602Sgad		bigtemp = strtoul(elem, &endp, 10);
859127602Sgad		if (errno != 0 || *endp != '\0' || bigtemp > UID_MAX)
860127499Sgad			warnx("No %s named '%s'", inf->lname, elem);
861127499Sgad		else {
862127499Sgad			/* The string is all digits, so it might be a userID. */
863127602Sgad			pwd = getpwuid((uid_t)bigtemp);
864127499Sgad			if (pwd == NULL)
865127499Sgad				warnx("No %s name or ID matches '%s'",
866127499Sgad				    inf->lname, elem);
86766377Sbrian		}
868127499Sgad	}
869127499Sgad	if (pwd == NULL) {
870127509Sgad		/*
871127509Sgad		 * These used to be treated as minor warnings (and the
872127509Sgad		 * option was simply ignored), but now they are fatal
873127509Sgad		 * errors (and the command will be aborted).
874127509Sgad		 */
875127509Sgad		optfatal = 1;
876129967Sgad		return (0);
877127499Sgad	}
878127499Sgad	if (inf->count >= inf->maxcount)
879127499Sgad		expand_list(inf);
880127823Sgad	inf->l.uids[(inf->count)++] = pwd->pw_uid;
881127499Sgad	return (1);
882127499Sgad}
883127499Sgad
884127499Sgadstatic void
885127499Sgadadd_list(struct listinfo *inf, const char *argp)
886127499Sgad{
887127499Sgad	const char *savep;
888127499Sgad	char *cp, *endp;
889127499Sgad	int toolong;
890127539Sgad	char elemcopy[PATH_MAX];
891127499Sgad
892239883Semaste	if (*argp == '\0')
893239883Semaste		inf->addelem(inf, argp);
894127499Sgad	while (*argp != '\0') {
895127499Sgad		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
896127499Sgad			argp++;
897127499Sgad		savep = argp;
898127499Sgad		toolong = 0;
899127499Sgad		cp = elemcopy;
900127499Sgad		if (strchr(T_SEP, *argp) == NULL) {
901127499Sgad			endp = elemcopy + sizeof(elemcopy) - 1;
902127499Sgad			while (*argp != '\0' && cp <= endp &&
903127499Sgad			    strchr(W_SEP T_SEP, *argp) == NULL)
904127499Sgad				*cp++ = *argp++;
905127499Sgad			if (cp > endp)
906127499Sgad				toolong = 1;
90766377Sbrian		}
908127499Sgad		if (!toolong) {
909127499Sgad			*cp = '\0';
910127542Sgad			/*
911129953Sgad			 * Add this single element to the given list.
912127542Sgad			 */
913127499Sgad			inf->addelem(inf, elemcopy);
914127499Sgad		} else {
915127499Sgad			/*
916127499Sgad			 * The string is too long to copy.  Find the end
917127499Sgad			 * of the string to print out the warning message.
918127499Sgad			 */
919127499Sgad			while (*argp != '\0' && strchr(W_SEP T_SEP,
920127499Sgad			    *argp) == NULL)
921127499Sgad				argp++;
922127499Sgad			warnx("Value too long: %.*s", (int)(argp - savep),
923127499Sgad			    savep);
924127499Sgad			optfatal = 1;
92566377Sbrian		}
926127499Sgad		/*
927127499Sgad		 * Skip over any number of trailing whitespace characters,
928127499Sgad		 * but only one (at most) trailing element-terminating
929127499Sgad		 * character.
930127499Sgad		 */
931127499Sgad		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
932127499Sgad			argp++;
933127499Sgad		if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) {
934127499Sgad			argp++;
935127499Sgad			/* Catch case where string ended with a comma. */
936127499Sgad			if (*argp == '\0')
937127499Sgad				inf->addelem(inf, argp);
938127499Sgad		}
93966377Sbrian	}
940127499Sgad}
94166377Sbrian
942192239Sbrianstatic void
943192239Sbriandescendant_sort(KINFO *ki, int items)
944192239Sbrian{
945192239Sbrian	int dst, lvl, maxlvl, n, ndst, nsrc, siblings, src;
946192239Sbrian	unsigned char *path;
947192239Sbrian	KINFO kn;
948192239Sbrian
949192239Sbrian	/*
950192239Sbrian	 * First, sort the entries by descendancy, tracking the descendancy
951192239Sbrian	 * depth in the ki_d.level field.
952192239Sbrian	 */
953192239Sbrian	src = 0;
954192239Sbrian	maxlvl = 0;
955192239Sbrian	while (src < items) {
956192239Sbrian		if (ki[src].ki_d.level) {
957192239Sbrian			src++;
958192239Sbrian			continue;
959192239Sbrian		}
960192239Sbrian		for (nsrc = 1; src + nsrc < items; nsrc++)
961192239Sbrian			if (!ki[src + nsrc].ki_d.level)
962192239Sbrian				break;
963192239Sbrian
964192239Sbrian		for (dst = 0; dst < items; dst++) {
965192239Sbrian			if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_pid)
966192239Sbrian				continue;
967192239Sbrian			if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_ppid)
968192239Sbrian				break;
969192239Sbrian		}
970192239Sbrian
971192239Sbrian		if (dst == items) {
972192239Sbrian			src += nsrc;
973192239Sbrian			continue;
974192239Sbrian		}
975192239Sbrian
976192239Sbrian		for (ndst = 1; dst + ndst < items; ndst++)
977192239Sbrian			if (ki[dst + ndst].ki_d.level <= ki[dst].ki_d.level)
978192239Sbrian				break;
979192239Sbrian
980192239Sbrian		for (n = src; n < src + nsrc; n++) {
981192239Sbrian			ki[n].ki_d.level += ki[dst].ki_d.level + 1;
982192239Sbrian			if (maxlvl < ki[n].ki_d.level)
983192239Sbrian				maxlvl = ki[n].ki_d.level;
984192239Sbrian		}
985192239Sbrian
986192239Sbrian		while (nsrc) {
987192239Sbrian			if (src < dst) {
988192239Sbrian				kn = ki[src];
989192239Sbrian				memmove(ki + src, ki + src + 1,
990192239Sbrian				    (dst - src + ndst - 1) * sizeof *ki);
991192239Sbrian				ki[dst + ndst - 1] = kn;
992192239Sbrian				nsrc--;
993192239Sbrian				dst--;
994192239Sbrian				ndst++;
995192239Sbrian			} else if (src != dst + ndst) {
996192239Sbrian				kn = ki[src];
997192239Sbrian				memmove(ki + dst + ndst + 1, ki + dst + ndst,
998192239Sbrian				    (src - dst - ndst) * sizeof *ki);
999192239Sbrian				ki[dst + ndst] = kn;
1000192239Sbrian				ndst++;
1001192239Sbrian				nsrc--;
1002192239Sbrian				src++;
1003192239Sbrian			} else {
1004192239Sbrian				ndst += nsrc;
1005192239Sbrian				src += nsrc;
1006192239Sbrian				nsrc = 0;
1007192239Sbrian			}
1008192239Sbrian		}
1009192239Sbrian	}
1010192239Sbrian
1011192239Sbrian	/*
1012192239Sbrian	 * Now populate ki_d.prefix (instead of ki_d.level) with the command
1013192239Sbrian	 * prefix used to show descendancies.
1014192239Sbrian	 */
1015192239Sbrian	path = malloc((maxlvl + 7) / 8);
1016192239Sbrian	memset(path, '\0', (maxlvl + 7) / 8);
1017192239Sbrian	for (src = 0; src < items; src++) {
1018192239Sbrian		if ((lvl = ki[src].ki_d.level) == 0) {
1019192239Sbrian			ki[src].ki_d.prefix = NULL;
1020192239Sbrian			continue;
1021192239Sbrian		}
1022192239Sbrian		if ((ki[src].ki_d.prefix = malloc(lvl * 2 + 1)) == NULL)
1023192239Sbrian			errx(1, "malloc failed");
1024192239Sbrian		for (n = 0; n < lvl - 2; n++) {
1025192239Sbrian			ki[src].ki_d.prefix[n * 2] =
1026192239Sbrian			    path[n / 8] & 1 << (n % 8) ? '|' : ' ';
1027192239Sbrian			ki[src].ki_d.prefix[n * 2 + 1] = ' ';
1028192239Sbrian		}
1029192239Sbrian		if (n == lvl - 2) {
1030192239Sbrian			/* Have I any more siblings? */
1031192239Sbrian			for (siblings = 0, dst = src + 1; dst < items; dst++) {
1032192239Sbrian				if (ki[dst].ki_d.level > lvl)
1033192239Sbrian					continue;
1034192239Sbrian				if (ki[dst].ki_d.level == lvl)
1035192239Sbrian					siblings = 1;
1036192239Sbrian				break;
1037192239Sbrian			}
1038192239Sbrian			if (siblings)
1039192239Sbrian				path[n / 8] |= 1 << (n % 8);
1040192239Sbrian			else
1041192239Sbrian				path[n / 8] &= ~(1 << (n % 8));
1042192239Sbrian			ki[src].ki_d.prefix[n * 2] = siblings ? '|' : '`';
1043192239Sbrian			ki[src].ki_d.prefix[n * 2 + 1] = '-';
1044192239Sbrian			n++;
1045192239Sbrian		}
1046192239Sbrian		strcpy(ki[src].ki_d.prefix + n * 2, "- ");
1047192239Sbrian	}
1048192239Sbrian	free(path);
1049192239Sbrian}
1050192239Sbrian
1051127499Sgadstatic void *
1052127499Sgadexpand_list(struct listinfo *inf)
1053127499Sgad{
1054127539Sgad	void *newlist;
1055127499Sgad	int newmax;
105666377Sbrian
1057127499Sgad	newmax = (inf->maxcount + 1) << 1;
1058127823Sgad	newlist = realloc(inf->l.ptr, newmax * inf->elemsize);
1059127499Sgad	if (newlist == NULL) {
1060127823Sgad		free(inf->l.ptr);
1061129967Sgad		errx(1, "realloc to %d %ss failed", newmax, inf->lname);
1062127499Sgad	}
1063127499Sgad	inf->maxcount = newmax;
1064127823Sgad	inf->l.ptr = newlist;
1065127499Sgad
1066127499Sgad	return (newlist);
106766377Sbrian}
106866377Sbrian
1069127499Sgadstatic void
1070127499Sgadfree_list(struct listinfo *inf)
1071127499Sgad{
1072127499Sgad
1073127499Sgad	inf->count = inf->elemsize = inf->maxcount = 0;
1074127823Sgad	if (inf->l.ptr != NULL)
1075127823Sgad		free(inf->l.ptr);
1076127499Sgad	inf->addelem = NULL;
1077127499Sgad	inf->lname = NULL;
1078127823Sgad	inf->l.ptr = NULL;
1079127499Sgad}
1080127499Sgad
1081127499Sgadstatic void
1082127499Sgadinit_list(struct listinfo *inf, addelem_rtn artn, int elemsize,
1083127499Sgad    const char *lname)
1084127499Sgad{
1085127499Sgad
1086127499Sgad	inf->count = inf->maxcount = 0;
1087127499Sgad	inf->elemsize = elemsize;
1088127499Sgad	inf->addelem = artn;
1089127499Sgad	inf->lname = lname;
1090127823Sgad	inf->l.ptr = NULL;
1091127499Sgad}
1092127499Sgad
1093109502SjmallettVARENT *
1094109502Sjmallettfind_varentry(VAR *v)
1095109502Sjmallett{
1096109502Sjmallett	struct varent *vent;
1097109502Sjmallett
1098130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
1099109502Sjmallett		if (strcmp(vent->var->name, v->name) == 0)
1100109502Sjmallett			return vent;
1101109502Sjmallett	}
1102109502Sjmallett	return NULL;
1103109502Sjmallett}
1104109502Sjmallett
11051556Srgrimesstatic void
110690110Simpscanvars(void)
11071556Srgrimes{
11081556Srgrimes	struct varent *vent;
11091556Srgrimes	VAR *v;
111025271Sjkh
1111130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
111225271Sjkh		v = vent->var;
111325271Sjkh		if (v->flag & USER)
111425271Sjkh			needuser = 1;
111525271Sjkh		if (v->flag & COMM)
111625271Sjkh			needcomm = 1;
111725271Sjkh	}
111825271Sjkh}
111925271Sjkh
112025271Sjkhstatic void
1121225868Straszformat_output(KINFO *ki)
112225271Sjkh{
112325271Sjkh	struct varent *vent;
112425271Sjkh	VAR *v;
1125225868Strasz	KINFO_STR *ks;
1126225868Strasz	char *str;
1127225868Strasz	int len;
11281556Srgrimes
1129225868Strasz	STAILQ_INIT(&ki->ki_ks);
1130130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
11311556Srgrimes		v = vent->var;
1132225868Strasz		str = (v->oproc)(ki, vent);
1133225868Strasz		ks = malloc(sizeof(*ks));
1134225868Strasz		if (ks == NULL)
1135225868Strasz			errx(1, "malloc failed");
1136225868Strasz		ks->ks_str = str;
1137225868Strasz		STAILQ_INSERT_TAIL(&ki->ki_ks, ks, ks_next);
1138225868Strasz		if (str != NULL) {
1139225868Strasz			len = strlen(str);
1140225868Strasz		} else
1141225868Strasz			len = 1; /* "-" */
1142225868Strasz		if (v->width < len)
1143225868Strasz			v->width = len;
114425271Sjkh	}
114525271Sjkh}
114625271Sjkh
114725271Sjkhstatic void
114890110Simpsizevars(void)
114925271Sjkh{
115025271Sjkh	struct varent *vent;
115125271Sjkh	VAR *v;
115225271Sjkh	int i;
115325271Sjkh
1154130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
115525271Sjkh		v = vent->var;
1156109504Sjmallett		i = strlen(vent->header);
11571556Srgrimes		if (v->width < i)
11581556Srgrimes			v->width = i;
11591556Srgrimes	}
11601556Srgrimes}
11611556Srgrimes
116290143Smarkmstatic const char *
116390110Simpfmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki,
1164127542Sgad    char *comm, int maxlen)
11651556Srgrimes{
116690143Smarkm	const char *s;
11671556Srgrimes
116890143Smarkm	s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen);
11691556Srgrimes	return (s);
11701556Srgrimes}
11711556Srgrimes
1172172207Sjeff#define UREADOK(ki)	(forceuread || (ki->ki_p->ki_flag & P_INMEM))
117331552Sdyson
11741556Srgrimesstatic void
117590110Simpsaveuser(KINFO *ki)
11761556Srgrimes{
11771556Srgrimes
1178172207Sjeff	if (ki->ki_p->ki_flag & P_INMEM) {
11791556Srgrimes		/*
11801556Srgrimes		 * The u-area might be swapped out, and we can't get
11811556Srgrimes		 * at it because we have a crashdump and no swap.
11821556Srgrimes		 * If it's here fill in these fields, otherwise, just
11831556Srgrimes		 * leave them 0.
11841556Srgrimes		 */
118569896Smckusick		ki->ki_valid = 1;
11861556Srgrimes	} else
118769896Smckusick		ki->ki_valid = 0;
11881556Srgrimes	/*
11891556Srgrimes	 * save arguments if needed
11901556Srgrimes	 */
1191130991Sgad	if (needcomm) {
1192130991Sgad		if (ki->ki_p->ki_stat == SZOMB)
1193130991Sgad			ki->ki_args = strdup("<defunct>");
1194130991Sgad		else if (UREADOK(ki) || (ki->ki_p->ki_args != NULL))
1195130991Sgad			ki->ki_args = strdup(fmt(kvm_getargv, ki,
1196130991Sgad			    ki->ki_p->ki_comm, MAXCOMLEN));
1197130991Sgad		else
1198130991Sgad			asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm);
1199131024Sgad		if (ki->ki_args == NULL)
1200130991Sgad			errx(1, "malloc failed");
120153276Speter	} else {
120253276Speter		ki->ki_args = NULL;
120353276Speter	}
1204130991Sgad	if (needenv) {
1205130991Sgad		if (UREADOK(ki))
1206130991Sgad			ki->ki_env = strdup(fmt(kvm_getenvv, ki,
1207130991Sgad			    (char *)NULL, 0));
1208130991Sgad		else
1209130991Sgad			ki->ki_env = strdup("()");
1210130991Sgad		if (ki->ki_env == NULL)
1211130991Sgad			errx(1, "malloc failed");
121253276Speter	} else {
121353276Speter		ki->ki_env = NULL;
121453276Speter	}
12151556Srgrimes}
12161556Srgrimes
1217130816Sgad/* A macro used to improve the readability of pscomp(). */
1218130816Sgad#define	DIFF_RETURN(a, b, field) do {	\
1219130816Sgad	if ((a)->field != (b)->field)	\
1220130816Sgad		return (((a)->field < (b)->field) ? -1 : 1); 	\
1221130816Sgad} while (0)
1222130816Sgad
12231556Srgrimesstatic int
122490110Simppscomp(const void *a, const void *b)
12251556Srgrimes{
1226127596Sgad	const KINFO *ka, *kb;
12271556Srgrimes
1228127596Sgad	ka = a;
1229127596Sgad	kb = b;
1230127596Sgad	/* SORTCPU and SORTMEM are sorted in descending order. */
1231130816Sgad	if (sortby == SORTCPU)
1232130816Sgad		DIFF_RETURN(kb, ka, ki_pcpu);
1233130816Sgad	if (sortby == SORTMEM)
1234130816Sgad		DIFF_RETURN(kb, ka, ki_memsize);
1235127596Sgad	/*
1236127596Sgad	 * TTY's are sorted in ascending order, except that all NODEV
1237127596Sgad	 * processes come before all processes with a device.
1238127596Sgad	 */
1239130816Sgad	if (ka->ki_p->ki_tdev != kb->ki_p->ki_tdev) {
1240130816Sgad		if (ka->ki_p->ki_tdev == NODEV)
1241130816Sgad			return (-1);
1242130816Sgad		if (kb->ki_p->ki_tdev == NODEV)
1243130816Sgad			return (1);
1244130816Sgad		DIFF_RETURN(ka, kb, ki_p->ki_tdev);
1245130816Sgad	}
1246130816Sgad
1247130972Sgad	/* PID's and TID's (threads) are sorted in ascending order. */
1248130816Sgad	DIFF_RETURN(ka, kb, ki_p->ki_pid);
1249130972Sgad	DIFF_RETURN(ka, kb, ki_p->ki_tid);
1250127596Sgad	return (0);
12511556Srgrimes}
1252130816Sgad#undef DIFF_RETURN
12531556Srgrimes
12541556Srgrimes/*
12551556Srgrimes * ICK (all for getopt), would rather hide the ugliness
12561556Srgrimes * here than taint the main code.
12571556Srgrimes *
12581556Srgrimes *  ps foo -> ps -foo
12591556Srgrimes *  ps 34 -> ps -p34
12601556Srgrimes *
12611556Srgrimes * The old convention that 't' with no trailing tty arg means the users
12621556Srgrimes * tty, is only supported if argv[1] doesn't begin with a '-'.  This same
12631556Srgrimes * feature is available with the option 'T', which takes no argument.
12641556Srgrimes */
12651556Srgrimesstatic char *
1266129915Sgadkludge_oldps_options(const char *optlist, char *origval, const char *nextarg)
12671556Srgrimes{
12681556Srgrimes	size_t len;
1269129914Sgad	char *argp, *cp, *newopts, *ns, *optp, *pidp;
12701556Srgrimes
1271102886Sjmallett	/*
1272129914Sgad	 * See if the original value includes any option which takes an
1273129914Sgad	 * argument (and will thus use up the remainder of the string).
1274102886Sjmallett	 */
1275129914Sgad	argp = NULL;
1276129914Sgad	if (optlist != NULL) {
1277129914Sgad		for (cp = origval; *cp != '\0'; cp++) {
1278129914Sgad			optp = strchr(optlist, *cp);
1279129914Sgad			if ((optp != NULL) && *(optp + 1) == ':') {
1280129914Sgad				argp = cp;
1281129914Sgad				break;
1282129914Sgad			}
1283129914Sgad		}
1284129914Sgad	}
1285129914Sgad	if (argp != NULL && *origval == '-')
1286129914Sgad		return (origval);
1287102886Sjmallett
12881556Srgrimes	/*
12891556Srgrimes	 * if last letter is a 't' flag with no argument (in the context
12901556Srgrimes	 * of the oldps options -- option string NOT starting with a '-' --
12911556Srgrimes	 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
129281743Sbrian	 *
1293129634Sgad	 * However, if a flag accepting a string argument is found earlier
1294129634Sgad	 * in the option string (including a possible `t' flag), then the
1295129634Sgad	 * remainder of the string must be the argument to that flag; so
1296129914Sgad	 * do not modify that argument.  Note that a trailing `t' would
1297129914Sgad	 * cause argp to be set, if argp was not already set by some
1298129914Sgad	 * earlier option.
12991556Srgrimes	 */
1300129914Sgad	len = strlen(origval);
1301129914Sgad	cp = origval + len - 1;
1302129914Sgad	pidp = NULL;
1303129915Sgad	if (*cp == 't' && *origval != '-' && cp == argp) {
1304129915Sgad		if (nextarg == NULL || *nextarg == '-' || isdigitch(*nextarg))
1305129915Sgad			*cp = 'T';
1306129915Sgad	} else if (argp == NULL) {
13071556Srgrimes		/*
1308129914Sgad		 * The original value did not include any option which takes
1309129914Sgad		 * an argument (and that would include `p' and `t'), so check
1310129914Sgad		 * the value for trailing number, or comma-separated list of
1311129914Sgad		 * numbers, which we will treat as a pid request.
13121556Srgrimes		 */
1313129914Sgad		if (isdigitch(*cp)) {
1314129914Sgad			while (cp >= origval && (*cp == ',' || isdigitch(*cp)))
1315129914Sgad				--cp;
1316129914Sgad			pidp = cp + 1;
1317129914Sgad		}
13181556Srgrimes	}
1319129914Sgad
13201556Srgrimes	/*
1321129914Sgad	 * If nothing needs to be added to the string, then return
1322129914Sgad	 * the "original" (although possibly modified) value.
13231556Srgrimes	 */
1324129914Sgad	if (*origval == '-' && pidp == NULL)
1325129914Sgad		return (origval);
1326129914Sgad
1327129914Sgad	/*
1328129914Sgad	 * Create a copy of the string to add '-' and/or 'p' to the
1329129914Sgad	 * original value.
1330129914Sgad	 */
1331129914Sgad	if ((newopts = ns = malloc(len + 3)) == NULL)
1332129914Sgad		errx(1, "malloc failed");
1333129914Sgad
1334129914Sgad	if (*origval != '-')
1335129914Sgad		*ns++ = '-';	/* add option flag */
1336129914Sgad
1337129914Sgad	if (pidp == NULL)
1338129914Sgad		strcpy(ns, origval);
1339129914Sgad	else {
1340129914Sgad		/*
1341129914Sgad		 * Copy everything before the pid string, add the `p',
1342129914Sgad		 * and then copy the pid string.
1343129914Sgad		 */
1344129914Sgad		len = pidp - origval;
1345129914Sgad		memcpy(ns, origval, len);
1346129914Sgad		ns += len;
13471556Srgrimes		*ns++ = 'p';
1348129914Sgad		strcpy(ns, pidp);
1349129914Sgad	}
13501556Srgrimes
13511556Srgrimes	return (newopts);
13521556Srgrimes}
13531556Srgrimes
13541556Srgrimesstatic void
135590110Simpusage(void)
13561556Srgrimes{
1357195830Sbrian#define	SINGLE_OPTS	"[-aCcde" OPT_LAZY_f "HhjlmrSTuvwXxZ]"
13581556Srgrimes
1359127499Sgad	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
1360141578Sru	    "usage: ps " SINGLE_OPTS " [-O fmt | -o fmt] [-G gid[,gid...]]",
1361127499Sgad	    "          [-M core] [-N system]",
1362141578Sru	    "          [-p pid[,pid...]] [-t tty[,tty...]] [-U user[,user...]]",
136326465Scharnier	    "       ps [-L]");
13641556Srgrimes	exit(1);
13651556Srgrimes}
1366