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>
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. */
112244154Spjdstatic int	 pid_max;	/* kern.max_pid */
1131556Srgrimes
114127537Sgadstatic enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
11597966Sjmallett
116127499Sgadstruct listinfo;
117127537Sgadtypedef	int	addelem_rtn(struct listinfo *_inf, const char *_elem);
118127499Sgad
119127499Sgadstruct listinfo {
120127499Sgad	int		 count;
121127499Sgad	int		 maxcount;
122127499Sgad	int		 elemsize;
123127499Sgad	addelem_rtn	*addelem;
124127499Sgad	const char	*lname;
125127499Sgad	union {
126127499Sgad		gid_t	*gids;
127127499Sgad		pid_t	*pids;
128127499Sgad		dev_t	*ttys;
129127499Sgad		uid_t	*uids;
130127499Sgad		void	*ptr;
131127823Sgad	} l;
132127499Sgad};
133127499Sgad
134127499Sgadstatic int	 addelem_gid(struct listinfo *, const char *);
135127499Sgadstatic int	 addelem_pid(struct listinfo *, const char *);
136127499Sgadstatic int	 addelem_tty(struct listinfo *, const char *);
137127499Sgadstatic int	 addelem_uid(struct listinfo *, const char *);
138127499Sgadstatic void	 add_list(struct listinfo *, const char *);
139192239Sbrianstatic void	 descendant_sort(KINFO *, int);
140225868Straszstatic void	 format_output(KINFO *);
141127499Sgadstatic void	*expand_list(struct listinfo *);
142127598Sgadstatic const char *
143127598Sgad		 fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int),
144245610Sjhb		    KINFO *, char *, char *, int);
145127499Sgadstatic void	 free_list(struct listinfo *);
146127499Sgadstatic void	 init_list(struct listinfo *, addelem_rtn, int, const char *);
147129952Sgadstatic char	*kludge_oldps_options(const char *, char *, const char *);
148127536Sgadstatic int	 pscomp(const void *, const void *);
149127536Sgadstatic void	 saveuser(KINFO *);
150127536Sgadstatic void	 scanvars(void);
151127536Sgadstatic void	 sizevars(void);
152244154Spjdstatic void	 pidmax_init(void);
153127536Sgadstatic void	 usage(void);
154127499Sgad
15597875Sjmallettstatic char dfmt[] = "pid,tt,state,time,command";
156129635Sgadstatic char jfmt[] = "user,pid,ppid,pgid,sid,jobc,state,tt,time,command";
157127538Sgadstatic char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,"
158127538Sgad			"tt,time,command";
15990143Smarkmstatic char   o1[] = "pid";
16097875Sjmallettstatic char   o2[] = "tt,state,time,command";
16197875Sjmallettstatic char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
162127538Sgadstatic char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,"
163127538Sgad			"%cpu,%mem,command";
164105831Srwatsonstatic char Zfmt[] = "label";
1651556Srgrimes
166192239Sbrian#define	PS_ARGS	"AaCcde" OPT_LAZY_f "G:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ"
16798494Ssobomax
1681556Srgrimesint
16990110Simpmain(int argc, char *argv[])
1701556Srgrimes{
171127499Sgad	struct listinfo gidlist, pgrplist, pidlist;
172127499Sgad	struct listinfo ruidlist, sesslist, ttylist, uidlist;
1731556Srgrimes	struct kinfo_proc *kp;
174225868Strasz	KINFO *kinfo = NULL, *next_KINFO;
175225868Strasz	KINFO_STR *ks;
1761556Srgrimes	struct varent *vent;
1771556Srgrimes	struct winsize ws;
178225868Strasz	const char *nlistf, *memf, *fmtstr, *str;
179127539Sgad	char *cols;
180225868Strasz	int all, ch, elem, flag, _fmt, i, lineno, linelen, left;
181192239Sbrian	int descendancy, nentries, nkept, nselectors;
182173004Sjulian	int prtheader, wflag, what, xkeep, xkeep_implied;
18390143Smarkm	char errbuf[_POSIX2_LINE_MAX];
1841556Srgrimes
18511809Sache	(void) setlocale(LC_ALL, "");
186127542Sgad	time(&now);			/* Used by routines in print.c. */
18711809Sache
18897804Stjr	if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0')
18997804Stjr		termwidth = atoi(cols);
19097804Stjr	else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
1911556Srgrimes	     ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
1921556Srgrimes	     ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) == -1) ||
1931556Srgrimes	     ws.ws_col == 0)
1941556Srgrimes		termwidth = 79;
1951556Srgrimes	else
1961556Srgrimes		termwidth = ws.ws_col - 1;
1971556Srgrimes
19898494Ssobomax	/*
199129914Sgad	 * Hide a number of option-processing kludges in a separate routine,
200129914Sgad	 * to support some historical BSD behaviors, such as `ps axu'.
20198494Ssobomax	 */
202129914Sgad	if (argc > 1)
203129915Sgad		argv[1] = kludge_oldps_options(PS_ARGS, argv[1], argv[2]);
2041556Srgrimes
205244154Spjd	pidmax_init();
206244154Spjd
207192239Sbrian	all = descendancy = _fmt = nselectors = optfatal = 0;
208127542Sgad	prtheader = showthreads = wflag = xkeep_implied = 0;
209127542Sgad	xkeep = -1;			/* Neither -x nor -X. */
210127499Sgad	init_list(&gidlist, addelem_gid, sizeof(gid_t), "group");
211127499Sgad	init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group");
212127499Sgad	init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id");
213127499Sgad	init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser");
214127499Sgad	init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id");
215127499Sgad	init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty");
216127499Sgad	init_list(&uidlist, addelem_uid, sizeof(uid_t), "user");
217203688Sbrucec	memf = _PATH_DEVNULL;
218203688Sbrucec	nlistf = NULL;
21998494Ssobomax	while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
220180596Skevlo		switch (ch) {
221127499Sgad		case 'A':
222127499Sgad			/*
223127499Sgad			 * Exactly the same as `-ax'.   This has been
224222178Suqs			 * added for compatibility with SUSv3, but for
225127499Sgad			 * now it will not be described in the man page.
226127499Sgad			 */
227127499Sgad			nselectors++;
228127499Sgad			all = xkeep = 1;
229127499Sgad			break;
2301556Srgrimes		case 'a':
231127499Sgad			nselectors++;
2321556Srgrimes			all = 1;
2331556Srgrimes			break;
23419068Speter		case 'C':
23519068Speter			rawcpu = 1;
23619068Speter			break;
23719068Speter		case 'c':
23819068Speter			cflag = 1;
23919068Speter			break;
240192239Sbrian		case 'd':
241192239Sbrian			descendancy = 1;
242192239Sbrian			break;
2431556Srgrimes		case 'e':			/* XXX set ufmt */
2441556Srgrimes			needenv = 1;
2451556Srgrimes			break;
246127506Sgad#ifdef LAZY_PS
247127506Sgad		case 'f':
248127506Sgad			if (getuid() == 0 || getgid() == 0)
249127542Sgad				forceuread = 1;
250127506Sgad			break;
251127506Sgad#endif
252127499Sgad		case 'G':
253127499Sgad			add_list(&gidlist, optarg);
254127499Sgad			xkeep_implied = 1;
255127499Sgad			nselectors++;
256127499Sgad			break;
257127542Sgad		case 'g':
258127499Sgad#if 0
259127597Sgad			/*-
260127542Sgad			 * XXX - This SUSv3 behavior is still under debate
261127542Sgad			 *	since it conflicts with the (undocumented)
262127542Sgad			 *	`-g' option.  So we skip it for now.
263127542Sgad			 */
264127499Sgad			add_list(&pgrplist, optarg);
265127499Sgad			xkeep_implied = 1;
266127499Sgad			nselectors++;
267127499Sgad			break;
268127499Sgad#else
269127542Sgad			/* The historical BSD-ish (from SunOS) behavior. */
2701556Srgrimes			break;			/* no-op */
271127499Sgad#endif
272116265Sscottl		case 'H':
273126127Sdeischen			showthreads = KERN_PROC_INC_THREAD;
274116265Sscottl			break;
2751556Srgrimes		case 'h':
2761556Srgrimes			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
2771556Srgrimes			break;
2781556Srgrimes		case 'j':
279109502Sjmallett			parsefmt(jfmt, 0);
28090143Smarkm			_fmt = 1;
2811556Srgrimes			jfmt[0] = '\0';
2821556Srgrimes			break;
2831556Srgrimes		case 'L':
2841556Srgrimes			showkey();
2851556Srgrimes			exit(0);
2861556Srgrimes		case 'l':
287109502Sjmallett			parsefmt(lfmt, 0);
28890143Smarkm			_fmt = 1;
2891556Srgrimes			lfmt[0] = '\0';
2901556Srgrimes			break;
2911556Srgrimes		case 'M':
2921556Srgrimes			memf = optarg;
2931556Srgrimes			break;
2941556Srgrimes		case 'm':
2951556Srgrimes			sortby = SORTMEM;
2961556Srgrimes			break;
2971556Srgrimes		case 'N':
2981556Srgrimes			nlistf = optarg;
2991556Srgrimes			break;
3001556Srgrimes		case 'O':
301109502Sjmallett			parsefmt(o1, 1);
302109502Sjmallett			parsefmt(optarg, 1);
303109502Sjmallett			parsefmt(o2, 1);
3041556Srgrimes			o1[0] = o2[0] = '\0';
30590143Smarkm			_fmt = 1;
3061556Srgrimes			break;
3071556Srgrimes		case 'o':
308109502Sjmallett			parsefmt(optarg, 1);
30990143Smarkm			_fmt = 1;
3101556Srgrimes			break;
3111556Srgrimes		case 'p':
312127499Sgad			add_list(&pidlist, optarg);
313127499Sgad			/*
314127499Sgad			 * Note: `-p' does not *set* xkeep, but any values
315127499Sgad			 * from pidlist are checked before xkeep is.  That
316127499Sgad			 * way they are always matched, even if the user
317127499Sgad			 * specifies `-X'.
318127499Sgad			 */
319127499Sgad			nselectors++;
3201556Srgrimes			break;
321127499Sgad#if 0
322127499Sgad		case 'R':
323127597Sgad			/*-
324127542Sgad			 * XXX - This un-standard option is still under
325127542Sgad			 *	debate.  This is what SUSv3 defines as
326127542Sgad			 *	the `-U' option, and while it would be
327127542Sgad			 *	nice to have, it could cause even more
328127542Sgad			 *	confusion to implement it as `-R'.
329127542Sgad			 */
330127499Sgad			add_list(&ruidlist, optarg);
331127499Sgad			xkeep_implied = 1;
332127499Sgad			nselectors++;
333127499Sgad			break;
334127499Sgad#endif
3351556Srgrimes		case 'r':
3361556Srgrimes			sortby = SORTCPU;
3371556Srgrimes			break;
3381556Srgrimes		case 'S':
3391556Srgrimes			sumrusage = 1;
3401556Srgrimes			break;
341127499Sgad#if 0
342127499Sgad		case 's':
343127597Sgad			/*-
344127542Sgad			 * XXX - This non-standard option is still under
345127542Sgad			 *	debate.  This *is* supported on Solaris,
346127542Sgad			 *	Linux, and IRIX, but conflicts with `-s'
347127542Sgad			 *	on NetBSD and maybe some older BSD's.
348127542Sgad			 */
349127499Sgad			add_list(&sesslist, optarg);
350127499Sgad			xkeep_implied = 1;
351127499Sgad			nselectors++;
352127499Sgad			break;
353127499Sgad#endif
3541556Srgrimes		case 'T':
3551556Srgrimes			if ((optarg = ttyname(STDIN_FILENO)) == NULL)
3561556Srgrimes				errx(1, "stdin: not a terminal");
3571556Srgrimes			/* FALLTHROUGH */
358127499Sgad		case 't':
359127499Sgad			add_list(&ttylist, optarg);
360127499Sgad			xkeep_implied = 1;
361127499Sgad			nselectors++;
3621556Srgrimes			break;
36313020Speter		case 'U':
364127499Sgad			/* This is what SUSv3 defines as the `-u' option. */
365127499Sgad			add_list(&uidlist, optarg);
366127499Sgad			xkeep_implied = 1;
367127499Sgad			nselectors++;
36813020Speter			break;
3691556Srgrimes		case 'u':
370109502Sjmallett			parsefmt(ufmt, 0);
3711556Srgrimes			sortby = SORTCPU;
37290143Smarkm			_fmt = 1;
3731556Srgrimes			ufmt[0] = '\0';
3741556Srgrimes			break;
3751556Srgrimes		case 'v':
376109502Sjmallett			parsefmt(vfmt, 0);
3771556Srgrimes			sortby = SORTMEM;
37890143Smarkm			_fmt = 1;
3791556Srgrimes			vfmt[0] = '\0';
3801556Srgrimes			break;
3811556Srgrimes		case 'w':
3821556Srgrimes			if (wflag)
3831556Srgrimes				termwidth = UNLIMITED;
3841556Srgrimes			else if (termwidth < 131)
3851556Srgrimes				termwidth = 131;
3861556Srgrimes			wflag++;
3871556Srgrimes			break;
388127499Sgad		case 'X':
389127499Sgad			/*
390127499Sgad			 * Note that `-X' and `-x' are not standard "selector"
391127499Sgad			 * options. For most selector-options, we check *all*
392127499Sgad			 * processes to see if any are matched by the given
393127499Sgad			 * value(s).  After we have a set of all the matched
394127499Sgad			 * processes, then `-X' and `-x' govern whether we
395127499Sgad			 * modify that *matched* set for processes which do
396127499Sgad			 * not have a controlling terminal.  `-X' causes
397127499Sgad			 * those processes to be deleted from the matched
398127499Sgad			 * set, while `-x' causes them to be kept.
399127499Sgad			 */
400127499Sgad			xkeep = 0;
401127499Sgad			break;
4021556Srgrimes		case 'x':
403127499Sgad			xkeep = 1;
4041556Srgrimes			break;
40586922Sgreen		case 'Z':
406109502Sjmallett			parsefmt(Zfmt, 0);
40786922Sgreen			Zfmt[0] = '\0';
40886922Sgreen			break;
4091556Srgrimes		case '?':
4101556Srgrimes		default:
4111556Srgrimes			usage();
4121556Srgrimes		}
4131556Srgrimes	argc -= optind;
4141556Srgrimes	argv += optind;
415129914Sgad
416129914Sgad	/*
417129914Sgad	 * If there arguments after processing all the options, attempt
418129914Sgad	 * to treat them as a list of process ids.
419129914Sgad	 */
420129914Sgad	while (*argv) {
421129914Sgad		if (!isdigitch(**argv))
422129914Sgad			break;
423129914Sgad		add_list(&pidlist, *argv);
424129914Sgad		argv++;
425129914Sgad	}
426129914Sgad	if (*argv) {
427129914Sgad		fprintf(stderr, "%s: illegal argument: %s\n",
428129914Sgad		    getprogname(), *argv);
429129914Sgad		usage();
430129914Sgad	}
431127499Sgad	if (optfatal)
432127542Sgad		exit(1);		/* Error messages already printed. */
433127542Sgad	if (xkeep < 0)			/* Neither -X nor -x was specified. */
434127499Sgad		xkeep = xkeep_implied;
435127499Sgad
43689909Sru	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
4371556Srgrimes	if (kd == 0)
4381556Srgrimes		errx(1, "%s", errbuf);
4391556Srgrimes
44090143Smarkm	if (!_fmt)
441109502Sjmallett		parsefmt(dfmt, 0);
4421556Srgrimes
443127499Sgad	if (nselectors == 0) {
444127823Sgad		uidlist.l.ptr = malloc(sizeof(uid_t));
445127823Sgad		if (uidlist.l.ptr == NULL)
44697877Sjmallett			errx(1, "malloc failed");
447127499Sgad		nselectors = 1;
448127499Sgad		uidlist.count = uidlist.maxcount = 1;
449127823Sgad		*uidlist.l.uids = getuid();
45066377Sbrian	}
4511556Srgrimes
4521556Srgrimes	/*
4531556Srgrimes	 * scan requested variables, noting what structures are needed,
45453170Skris	 * and adjusting header widths as appropriate.
4551556Srgrimes	 */
4561556Srgrimes	scanvars();
457127499Sgad
4581556Srgrimes	/*
459127499Sgad	 * Get process list.  If the user requested just one selector-
460127499Sgad	 * option, then kvm_getprocs can be asked to return just those
461127499Sgad	 * processes.  Otherwise, have it return all processes, and
462127499Sgad	 * then this routine will search that full list and select the
463127499Sgad	 * processes which match any of the user's selector-options.
4641556Srgrimes	 */
465127499Sgad	what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
466127499Sgad	flag = 0;
467127499Sgad	if (nselectors == 1) {
468129600Sgad		if (gidlist.count == 1) {
469129600Sgad			what = KERN_PROC_RGID | showthreads;
470129600Sgad			flag = *gidlist.l.gids;
471129600Sgad			nselectors = 0;
472129600Sgad		} else if (pgrplist.count == 1) {
473127499Sgad			what = KERN_PROC_PGRP | showthreads;
474127823Sgad			flag = *pgrplist.l.pids;
475127499Sgad			nselectors = 0;
476127499Sgad		} else if (pidlist.count == 1) {
477127499Sgad			what = KERN_PROC_PID | showthreads;
478127823Sgad			flag = *pidlist.l.pids;
479127499Sgad			nselectors = 0;
480127499Sgad		} else if (ruidlist.count == 1) {
481127499Sgad			what = KERN_PROC_RUID | showthreads;
482127823Sgad			flag = *ruidlist.l.uids;
483127499Sgad			nselectors = 0;
484127499Sgad		} else if (sesslist.count == 1) {
485127499Sgad			what = KERN_PROC_SESSION | showthreads;
486127823Sgad			flag = *sesslist.l.pids;
487127499Sgad			nselectors = 0;
488127499Sgad		} else if (ttylist.count == 1) {
489127499Sgad			what = KERN_PROC_TTY | showthreads;
490127823Sgad			flag = *ttylist.l.ttys;
491127499Sgad			nselectors = 0;
492127499Sgad		} else if (uidlist.count == 1) {
493127499Sgad			what = KERN_PROC_UID | showthreads;
494127823Sgad			flag = *uidlist.l.uids;
495127499Sgad			nselectors = 0;
496127499Sgad		} else if (all) {
497127499Sgad			/* No need for this routine to select processes. */
498127499Sgad			nselectors = 0;
499127499Sgad		}
5001556Srgrimes	}
501126127Sdeischen
5021556Srgrimes	/*
5031556Srgrimes	 * select procs
5041556Srgrimes	 */
505127499Sgad	nentries = -1;
506127149Sgad	kp = kvm_getprocs(kd, what, flag, &nentries);
507127544Sgad	if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0))
5081556Srgrimes		errx(1, "%s", kvm_geterr(kd));
509127499Sgad	nkept = 0;
510127149Sgad	if (nentries > 0) {
511127149Sgad		if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
512127149Sgad			errx(1, "malloc failed");
513127149Sgad		for (i = nentries; --i >= 0; ++kp) {
514127499Sgad			/*
515127499Sgad			 * If the user specified multiple selection-criteria,
516127499Sgad			 * then keep any process matched by the inclusive OR
517127499Sgad			 * of all the selection-criteria given.
518127499Sgad			 */
519127499Sgad			if (pidlist.count > 0) {
520127499Sgad				for (elem = 0; elem < pidlist.count; elem++)
521127823Sgad					if (kp->ki_pid == pidlist.l.pids[elem])
522127499Sgad						goto keepit;
523127499Sgad			}
524127499Sgad			/*
525127499Sgad			 * Note that we had to process pidlist before
526127499Sgad			 * filtering out processes which do not have
527127499Sgad			 * a controlling terminal.
528127499Sgad			 */
529127499Sgad			if (xkeep == 0) {
530127499Sgad				if ((kp->ki_tdev == NODEV ||
531127499Sgad				    (kp->ki_flag & P_CONTROLT) == 0))
532127499Sgad					continue;
533127499Sgad			}
534127499Sgad			if (nselectors == 0)
535127499Sgad				goto keepit;
536127499Sgad			if (gidlist.count > 0) {
537127499Sgad				for (elem = 0; elem < gidlist.count; elem++)
538127823Sgad					if (kp->ki_rgid == gidlist.l.gids[elem])
539127499Sgad						goto keepit;
540127499Sgad			}
541127499Sgad			if (pgrplist.count > 0) {
542127499Sgad				for (elem = 0; elem < pgrplist.count; elem++)
543127823Sgad					if (kp->ki_pgid ==
544127823Sgad					    pgrplist.l.pids[elem])
545127499Sgad						goto keepit;
546127499Sgad			}
547127499Sgad			if (ruidlist.count > 0) {
548127499Sgad				for (elem = 0; elem < ruidlist.count; elem++)
549127823Sgad					if (kp->ki_ruid ==
550127823Sgad					    ruidlist.l.uids[elem])
551127499Sgad						goto keepit;
552127499Sgad			}
553127499Sgad			if (sesslist.count > 0) {
554127499Sgad				for (elem = 0; elem < sesslist.count; elem++)
555127823Sgad					if (kp->ki_sid == sesslist.l.pids[elem])
556127499Sgad						goto keepit;
557127499Sgad			}
558127499Sgad			if (ttylist.count > 0) {
559127499Sgad				for (elem = 0; elem < ttylist.count; elem++)
560127823Sgad					if (kp->ki_tdev == ttylist.l.ttys[elem])
561127499Sgad						goto keepit;
562127499Sgad			}
563127499Sgad			if (uidlist.count > 0) {
564127499Sgad				for (elem = 0; elem < uidlist.count; elem++)
565127823Sgad					if (kp->ki_uid == uidlist.l.uids[elem])
566127499Sgad						goto keepit;
567127499Sgad			}
568127499Sgad			/*
569127499Sgad			 * This process did not match any of the user's
570127499Sgad			 * selector-options, so skip the process.
571127499Sgad			 */
572127499Sgad			continue;
573127499Sgad
574127499Sgad		keepit:
575130816Sgad			next_KINFO = &kinfo[nkept];
576130816Sgad			next_KINFO->ki_p = kp;
577192239Sbrian			next_KINFO->ki_d.level = 0;
578192239Sbrian			next_KINFO->ki_d.prefix = NULL;
579130816Sgad			next_KINFO->ki_pcpu = getpcpu(next_KINFO);
580130816Sgad			if (sortby == SORTMEM)
581130816Sgad				next_KINFO->ki_memsize = kp->ki_tsize +
582130816Sgad				    kp->ki_dsize + kp->ki_ssize;
583127149Sgad			if (needuser)
584130816Sgad				saveuser(next_KINFO);
585127499Sgad			nkept++;
586127149Sgad		}
5871556Srgrimes	}
58825271Sjkh
58925271Sjkh	sizevars();
59025271Sjkh
591225868Strasz	if (nkept == 0) {
592225868Strasz		printheader();
59362803Swill		exit(1);
594225868Strasz	}
595127499Sgad
5961556Srgrimes	/*
5971556Srgrimes	 * sort proc list
5981556Srgrimes	 */
599127499Sgad	qsort(kinfo, nkept, sizeof(KINFO), pscomp);
600192239Sbrian
6011556Srgrimes	/*
602192239Sbrian	 * We want things in descendant order
603192239Sbrian	 */
604192239Sbrian	if (descendancy)
605192239Sbrian		descendant_sort(kinfo, nkept);
606192239Sbrian
607225868Strasz
608192239Sbrian	/*
609225868Strasz	 * Prepare formatted output.
6101556Srgrimes	 */
611225868Strasz	for (i = 0; i < nkept; i++)
612225868Strasz		format_output(&kinfo[i]);
613225868Strasz
614225868Strasz	/*
615225868Strasz	 * Print header.
616225868Strasz	 */
617225868Strasz	printheader();
618225868Strasz
619225868Strasz	/*
620225868Strasz	 * Output formatted lines.
621225868Strasz	 */
622127499Sgad	for (i = lineno = 0; i < nkept; i++) {
623225868Strasz		linelen = 0;
624130999Sgad		STAILQ_FOREACH(vent, &varlist, next_ve) {
625225868Strasz	        	if (vent->var->flag & LJUST)
626225868Strasz				fmtstr = "%-*s";
627225868Strasz			else
628225868Strasz				fmtstr = "%*s";
629225868Strasz
630225868Strasz			ks = STAILQ_FIRST(&kinfo[i].ki_ks);
631225868Strasz			STAILQ_REMOVE_HEAD(&kinfo[i].ki_ks, ks_next);
632229782Suqs			/* Truncate rightmost column if necessary.  */
633225868Strasz			if (STAILQ_NEXT(vent, next_ve) == NULL &&
634225868Strasz			   termwidth != UNLIMITED && ks->ks_str != NULL) {
635225868Strasz				left = termwidth - linelen;
636225868Strasz				if (left > 0 && left < (int)strlen(ks->ks_str))
637225868Strasz					ks->ks_str[left] = '\0';
638225868Strasz			}
639225868Strasz			str = ks->ks_str;
640225868Strasz			if (str == NULL)
641225868Strasz				str = "-";
642225868Strasz			/* No padding for the last column, if it's LJUST. */
643225868Strasz			if (STAILQ_NEXT(vent, next_ve) == NULL &&
644225868Strasz			    vent->var->flag & LJUST)
645225868Strasz				linelen += printf(fmtstr, 0, str);
646225868Strasz			else
647225868Strasz				linelen += printf(fmtstr, vent->var->width, str);
648225868Strasz
649225868Strasz			if (ks->ks_str != NULL) {
650225868Strasz				free(ks->ks_str);
651225868Strasz				ks->ks_str = NULL;
652225868Strasz			}
653225868Strasz			free(ks);
654225868Strasz			ks = NULL;
655225868Strasz
656225868Strasz			if (STAILQ_NEXT(vent, next_ve) != NULL) {
6571556Srgrimes				(void)putchar(' ');
658225868Strasz				linelen++;
659225868Strasz			}
6601556Srgrimes		}
6611556Srgrimes		(void)putchar('\n');
6621556Srgrimes		if (prtheader && lineno++ == prtheader - 4) {
6631556Srgrimes			(void)putchar('\n');
6641556Srgrimes			printheader();
6651556Srgrimes			lineno = 0;
6661556Srgrimes		}
6671556Srgrimes	}
668127499Sgad	free_list(&gidlist);
669127499Sgad	free_list(&pidlist);
670127499Sgad	free_list(&pgrplist);
671127499Sgad	free_list(&ruidlist);
672127499Sgad	free_list(&sesslist);
673127499Sgad	free_list(&ttylist);
674127499Sgad	free_list(&uidlist);
675192239Sbrian	for (i = 0; i < nkept; i++)
676192239Sbrian		free(kinfo[i].ki_d.prefix);
677192239Sbrian	free(kinfo);
67866377Sbrian
6791556Srgrimes	exit(eval);
6801556Srgrimes}
6811556Srgrimes
682127499Sgadstatic int
683127499Sgadaddelem_gid(struct listinfo *inf, const char *elem)
684127499Sgad{
685127499Sgad	struct group *grp;
686127499Sgad	const char *nameorID;
687127499Sgad	char *endp;
688127602Sgad	u_long bigtemp;
689127499Sgad
690127499Sgad	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
691127499Sgad		if (*elem == '\0')
692127499Sgad			warnx("Invalid (zero-length) %s name", inf->lname);
693127499Sgad		else
694127499Sgad			warnx("%s name too long: %s", inf->lname, elem);
695127499Sgad		optfatal = 1;
696127542Sgad		return (0);		/* Do not add this value. */
697127499Sgad	}
698127499Sgad
699127499Sgad	/*
700127499Sgad	 * SUSv3 states that `ps -G grouplist' should match "real-group
701127499Sgad	 * ID numbers", and does not mention group-names.  I do want to
702127499Sgad	 * also support group-names, so this tries for a group-id first,
703127499Sgad	 * and only tries for a name if that doesn't work.  This is the
704127499Sgad	 * opposite order of what is done in addelem_uid(), but in
705127499Sgad	 * practice the order would only matter for group-names which
706127499Sgad	 * are all-numeric.
707127499Sgad	 */
708127499Sgad	grp = NULL;
709127499Sgad	nameorID = "named";
710127499Sgad	errno = 0;
711127602Sgad	bigtemp = strtoul(elem, &endp, 10);
712127602Sgad	if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) {
713127499Sgad		nameorID = "name or ID matches";
714127602Sgad		grp = getgrgid((gid_t)bigtemp);
715127499Sgad	}
716127499Sgad	if (grp == NULL)
717127499Sgad		grp = getgrnam(elem);
718127499Sgad	if (grp == NULL) {
719127499Sgad		warnx("No %s %s '%s'", inf->lname, nameorID, elem);
720127499Sgad		optfatal = 1;
721129967Sgad		return (0);
722127499Sgad	}
723127499Sgad	if (inf->count >= inf->maxcount)
724127499Sgad		expand_list(inf);
725127823Sgad	inf->l.gids[(inf->count)++] = grp->gr_gid;
726127499Sgad	return (1);
727127499Sgad}
728127499Sgad
729127499Sgadstatic int
730127499Sgadaddelem_pid(struct listinfo *inf, const char *elem)
731127149Sgad{
732127539Sgad	char *endp;
733127149Sgad	long tempid;
734127149Sgad
735129917Sgad	if (*elem == '\0') {
736129917Sgad		warnx("Invalid (zero-length) process id");
737129917Sgad		optfatal = 1;
738129917Sgad		return (0);		/* Do not add this value. */
739127149Sgad	}
740127149Sgad
741129952Sgad	errno = 0;
742129952Sgad	tempid = strtol(elem, &endp, 10);
743129952Sgad	if (*endp != '\0' || tempid < 0 || elem == endp) {
744129952Sgad		warnx("Invalid %s: %s", inf->lname, elem);
745129952Sgad		errno = ERANGE;
746244154Spjd	} else if (errno != 0 || tempid > pid_max) {
747129952Sgad		warnx("%s too large: %s", inf->lname, elem);
748129952Sgad		errno = ERANGE;
749129952Sgad	}
750129952Sgad	if (errno == ERANGE) {
751129952Sgad		optfatal = 1;
752129967Sgad		return (0);
753129952Sgad	}
754127499Sgad	if (inf->count >= inf->maxcount)
755127499Sgad		expand_list(inf);
756127823Sgad	inf->l.pids[(inf->count)++] = tempid;
757127499Sgad	return (1);
758127499Sgad}
759127149Sgad
760131010Sgad/*-
761131010Sgad * The user can specify a device via one of three formats:
762173492Sjhb *     1) fully qualified, e.g.:     /dev/ttyp0 /dev/console	/dev/pts/0
763173492Sjhb *     2) missing "/dev", e.g.:      ttyp0      console		pts/0
764173492Sjhb *     3) two-letters, e.g.:         p0         co		0
765131010Sgad *        (matching letters that would be seen in the "TT" column)
766131010Sgad */
767127499Sgadstatic int
768127499Sgadaddelem_tty(struct listinfo *inf, const char *elem)
769127499Sgad{
770127539Sgad	const char *ttypath;
771127539Sgad	struct stat sb;
772173492Sjhb	char pathbuf[PATH_MAX], pathbuf2[PATH_MAX], pathbuf3[PATH_MAX];
773127499Sgad
774131010Sgad	ttypath = NULL;
775131209Sgad	pathbuf2[0] = '\0';
776173492Sjhb	pathbuf3[0] = '\0';
777131010Sgad	switch (*elem) {
778131010Sgad	case '/':
779127499Sgad		ttypath = elem;
780131010Sgad		break;
781131010Sgad	case 'c':
782131010Sgad		if (strcmp(elem, "co") == 0) {
783131010Sgad			ttypath = _PATH_CONSOLE;
784131010Sgad			break;
785131010Sgad		}
786131010Sgad		/* FALLTHROUGH */
787131010Sgad	default:
788131010Sgad		strlcpy(pathbuf, _PATH_DEV, sizeof(pathbuf));
789131010Sgad		strlcat(pathbuf, elem, sizeof(pathbuf));
790131010Sgad		ttypath = pathbuf;
791131209Sgad		if (strncmp(pathbuf, _PATH_TTY, strlen(_PATH_TTY)) == 0)
792131010Sgad			break;
793173492Sjhb		if (strncmp(pathbuf, _PATH_PTS, strlen(_PATH_PTS)) == 0)
794173492Sjhb			break;
795131010Sgad		if (strcmp(pathbuf, _PATH_CONSOLE) == 0)
796131010Sgad			break;
797131209Sgad		/* Check to see if /dev/tty${elem} exists */
798131209Sgad		strlcpy(pathbuf2, _PATH_TTY, sizeof(pathbuf2));
799131209Sgad		strlcat(pathbuf2, elem, sizeof(pathbuf2));
800131209Sgad		if (stat(pathbuf2, &sb) == 0 && S_ISCHR(sb.st_mode)) {
801131010Sgad			/* No need to repeat stat() && S_ISCHR() checks */
802192280Sbrian			ttypath = NULL;
803131010Sgad			break;
804131010Sgad		}
805173492Sjhb		/* Check to see if /dev/pts/${elem} exists */
806173492Sjhb		strlcpy(pathbuf3, _PATH_PTS, sizeof(pathbuf3));
807173492Sjhb		strlcat(pathbuf3, elem, sizeof(pathbuf3));
808173492Sjhb		if (stat(pathbuf3, &sb) == 0 && S_ISCHR(sb.st_mode)) {
809173492Sjhb			/* No need to repeat stat() && S_ISCHR() checks */
810192280Sbrian			ttypath = NULL;
811173492Sjhb			break;
812173492Sjhb		}
813131010Sgad		break;
814127499Sgad	}
815131010Sgad	if (ttypath) {
816131010Sgad		if (stat(ttypath, &sb) == -1) {
817173492Sjhb			if (pathbuf3[0] != '\0')
818173492Sjhb				warn("%s, %s, and %s", pathbuf3, pathbuf2,
819173492Sjhb				    ttypath);
820131209Sgad			else
821131209Sgad				warn("%s", ttypath);
822131010Sgad			optfatal = 1;
823131010Sgad			return (0);
824131010Sgad		}
825131010Sgad		if (!S_ISCHR(sb.st_mode)) {
826173492Sjhb			if (pathbuf3[0] != '\0')
827173492Sjhb				warnx("%s, %s, and %s: Not a terminal",
828173492Sjhb				    pathbuf3, pathbuf2, ttypath);
829131209Sgad			else
830131209Sgad				warnx("%s: Not a terminal", ttypath);
831131010Sgad			optfatal = 1;
832131010Sgad			return (0);
833131010Sgad		}
834127499Sgad	}
835127499Sgad	if (inf->count >= inf->maxcount)
836127499Sgad		expand_list(inf);
837127823Sgad	inf->l.ttys[(inf->count)++] = sb.st_rdev;
838127499Sgad	return (1);
839127149Sgad}
840127149Sgad
841127499Sgadstatic int
842127499Sgadaddelem_uid(struct listinfo *inf, const char *elem)
84366377Sbrian{
84466377Sbrian	struct passwd *pwd;
845127539Sgad	char *endp;
846127602Sgad	u_long bigtemp;
84766377Sbrian
848127499Sgad	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
849127499Sgad		if (*elem == '\0')
850127499Sgad			warnx("Invalid (zero-length) %s name", inf->lname);
851127499Sgad		else
852127499Sgad			warnx("%s name too long: %s", inf->lname, elem);
853127499Sgad		optfatal = 1;
854127542Sgad		return (0);		/* Do not add this value. */
855127499Sgad	}
85666377Sbrian
857127499Sgad	pwd = getpwnam(elem);
858127499Sgad	if (pwd == NULL) {
859127499Sgad		errno = 0;
860127602Sgad		bigtemp = strtoul(elem, &endp, 10);
861127602Sgad		if (errno != 0 || *endp != '\0' || bigtemp > UID_MAX)
862127499Sgad			warnx("No %s named '%s'", inf->lname, elem);
863127499Sgad		else {
864127499Sgad			/* The string is all digits, so it might be a userID. */
865127602Sgad			pwd = getpwuid((uid_t)bigtemp);
866127499Sgad			if (pwd == NULL)
867127499Sgad				warnx("No %s name or ID matches '%s'",
868127499Sgad				    inf->lname, elem);
86966377Sbrian		}
870127499Sgad	}
871127499Sgad	if (pwd == NULL) {
872127509Sgad		/*
873127509Sgad		 * These used to be treated as minor warnings (and the
874127509Sgad		 * option was simply ignored), but now they are fatal
875127509Sgad		 * errors (and the command will be aborted).
876127509Sgad		 */
877127509Sgad		optfatal = 1;
878129967Sgad		return (0);
879127499Sgad	}
880127499Sgad	if (inf->count >= inf->maxcount)
881127499Sgad		expand_list(inf);
882127823Sgad	inf->l.uids[(inf->count)++] = pwd->pw_uid;
883127499Sgad	return (1);
884127499Sgad}
885127499Sgad
886127499Sgadstatic void
887127499Sgadadd_list(struct listinfo *inf, const char *argp)
888127499Sgad{
889127499Sgad	const char *savep;
890127499Sgad	char *cp, *endp;
891127499Sgad	int toolong;
892127539Sgad	char elemcopy[PATH_MAX];
893127499Sgad
894239883Semaste	if (*argp == '\0')
895239883Semaste		inf->addelem(inf, argp);
896127499Sgad	while (*argp != '\0') {
897127499Sgad		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
898127499Sgad			argp++;
899127499Sgad		savep = argp;
900127499Sgad		toolong = 0;
901127499Sgad		cp = elemcopy;
902127499Sgad		if (strchr(T_SEP, *argp) == NULL) {
903127499Sgad			endp = elemcopy + sizeof(elemcopy) - 1;
904127499Sgad			while (*argp != '\0' && cp <= endp &&
905127499Sgad			    strchr(W_SEP T_SEP, *argp) == NULL)
906127499Sgad				*cp++ = *argp++;
907127499Sgad			if (cp > endp)
908127499Sgad				toolong = 1;
90966377Sbrian		}
910127499Sgad		if (!toolong) {
911127499Sgad			*cp = '\0';
912127542Sgad			/*
913129953Sgad			 * Add this single element to the given list.
914127542Sgad			 */
915127499Sgad			inf->addelem(inf, elemcopy);
916127499Sgad		} else {
917127499Sgad			/*
918127499Sgad			 * The string is too long to copy.  Find the end
919127499Sgad			 * of the string to print out the warning message.
920127499Sgad			 */
921127499Sgad			while (*argp != '\0' && strchr(W_SEP T_SEP,
922127499Sgad			    *argp) == NULL)
923127499Sgad				argp++;
924127499Sgad			warnx("Value too long: %.*s", (int)(argp - savep),
925127499Sgad			    savep);
926127499Sgad			optfatal = 1;
92766377Sbrian		}
928127499Sgad		/*
929127499Sgad		 * Skip over any number of trailing whitespace characters,
930127499Sgad		 * but only one (at most) trailing element-terminating
931127499Sgad		 * character.
932127499Sgad		 */
933127499Sgad		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
934127499Sgad			argp++;
935127499Sgad		if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) {
936127499Sgad			argp++;
937127499Sgad			/* Catch case where string ended with a comma. */
938127499Sgad			if (*argp == '\0')
939127499Sgad				inf->addelem(inf, argp);
940127499Sgad		}
94166377Sbrian	}
942127499Sgad}
94366377Sbrian
944192239Sbrianstatic void
945192239Sbriandescendant_sort(KINFO *ki, int items)
946192239Sbrian{
947192239Sbrian	int dst, lvl, maxlvl, n, ndst, nsrc, siblings, src;
948192239Sbrian	unsigned char *path;
949192239Sbrian	KINFO kn;
950192239Sbrian
951192239Sbrian	/*
952192239Sbrian	 * First, sort the entries by descendancy, tracking the descendancy
953192239Sbrian	 * depth in the ki_d.level field.
954192239Sbrian	 */
955192239Sbrian	src = 0;
956192239Sbrian	maxlvl = 0;
957192239Sbrian	while (src < items) {
958192239Sbrian		if (ki[src].ki_d.level) {
959192239Sbrian			src++;
960192239Sbrian			continue;
961192239Sbrian		}
962192239Sbrian		for (nsrc = 1; src + nsrc < items; nsrc++)
963192239Sbrian			if (!ki[src + nsrc].ki_d.level)
964192239Sbrian				break;
965192239Sbrian
966192239Sbrian		for (dst = 0; dst < items; dst++) {
967192239Sbrian			if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_pid)
968192239Sbrian				continue;
969192239Sbrian			if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_ppid)
970192239Sbrian				break;
971192239Sbrian		}
972192239Sbrian
973192239Sbrian		if (dst == items) {
974192239Sbrian			src += nsrc;
975192239Sbrian			continue;
976192239Sbrian		}
977192239Sbrian
978192239Sbrian		for (ndst = 1; dst + ndst < items; ndst++)
979192239Sbrian			if (ki[dst + ndst].ki_d.level <= ki[dst].ki_d.level)
980192239Sbrian				break;
981192239Sbrian
982192239Sbrian		for (n = src; n < src + nsrc; n++) {
983192239Sbrian			ki[n].ki_d.level += ki[dst].ki_d.level + 1;
984192239Sbrian			if (maxlvl < ki[n].ki_d.level)
985192239Sbrian				maxlvl = ki[n].ki_d.level;
986192239Sbrian		}
987192239Sbrian
988192239Sbrian		while (nsrc) {
989192239Sbrian			if (src < dst) {
990192239Sbrian				kn = ki[src];
991192239Sbrian				memmove(ki + src, ki + src + 1,
992192239Sbrian				    (dst - src + ndst - 1) * sizeof *ki);
993192239Sbrian				ki[dst + ndst - 1] = kn;
994192239Sbrian				nsrc--;
995192239Sbrian				dst--;
996192239Sbrian				ndst++;
997192239Sbrian			} else if (src != dst + ndst) {
998192239Sbrian				kn = ki[src];
999192239Sbrian				memmove(ki + dst + ndst + 1, ki + dst + ndst,
1000192239Sbrian				    (src - dst - ndst) * sizeof *ki);
1001192239Sbrian				ki[dst + ndst] = kn;
1002192239Sbrian				ndst++;
1003192239Sbrian				nsrc--;
1004192239Sbrian				src++;
1005192239Sbrian			} else {
1006192239Sbrian				ndst += nsrc;
1007192239Sbrian				src += nsrc;
1008192239Sbrian				nsrc = 0;
1009192239Sbrian			}
1010192239Sbrian		}
1011192239Sbrian	}
1012192239Sbrian
1013192239Sbrian	/*
1014192239Sbrian	 * Now populate ki_d.prefix (instead of ki_d.level) with the command
1015192239Sbrian	 * prefix used to show descendancies.
1016192239Sbrian	 */
1017192239Sbrian	path = malloc((maxlvl + 7) / 8);
1018192239Sbrian	memset(path, '\0', (maxlvl + 7) / 8);
1019192239Sbrian	for (src = 0; src < items; src++) {
1020192239Sbrian		if ((lvl = ki[src].ki_d.level) == 0) {
1021192239Sbrian			ki[src].ki_d.prefix = NULL;
1022192239Sbrian			continue;
1023192239Sbrian		}
1024192239Sbrian		if ((ki[src].ki_d.prefix = malloc(lvl * 2 + 1)) == NULL)
1025192239Sbrian			errx(1, "malloc failed");
1026192239Sbrian		for (n = 0; n < lvl - 2; n++) {
1027192239Sbrian			ki[src].ki_d.prefix[n * 2] =
1028192239Sbrian			    path[n / 8] & 1 << (n % 8) ? '|' : ' ';
1029192239Sbrian			ki[src].ki_d.prefix[n * 2 + 1] = ' ';
1030192239Sbrian		}
1031192239Sbrian		if (n == lvl - 2) {
1032192239Sbrian			/* Have I any more siblings? */
1033192239Sbrian			for (siblings = 0, dst = src + 1; dst < items; dst++) {
1034192239Sbrian				if (ki[dst].ki_d.level > lvl)
1035192239Sbrian					continue;
1036192239Sbrian				if (ki[dst].ki_d.level == lvl)
1037192239Sbrian					siblings = 1;
1038192239Sbrian				break;
1039192239Sbrian			}
1040192239Sbrian			if (siblings)
1041192239Sbrian				path[n / 8] |= 1 << (n % 8);
1042192239Sbrian			else
1043192239Sbrian				path[n / 8] &= ~(1 << (n % 8));
1044192239Sbrian			ki[src].ki_d.prefix[n * 2] = siblings ? '|' : '`';
1045192239Sbrian			ki[src].ki_d.prefix[n * 2 + 1] = '-';
1046192239Sbrian			n++;
1047192239Sbrian		}
1048192239Sbrian		strcpy(ki[src].ki_d.prefix + n * 2, "- ");
1049192239Sbrian	}
1050192239Sbrian	free(path);
1051192239Sbrian}
1052192239Sbrian
1053127499Sgadstatic void *
1054127499Sgadexpand_list(struct listinfo *inf)
1055127499Sgad{
1056127539Sgad	void *newlist;
1057127499Sgad	int newmax;
105866377Sbrian
1059127499Sgad	newmax = (inf->maxcount + 1) << 1;
1060127823Sgad	newlist = realloc(inf->l.ptr, newmax * inf->elemsize);
1061127499Sgad	if (newlist == NULL) {
1062127823Sgad		free(inf->l.ptr);
1063129967Sgad		errx(1, "realloc to %d %ss failed", newmax, inf->lname);
1064127499Sgad	}
1065127499Sgad	inf->maxcount = newmax;
1066127823Sgad	inf->l.ptr = newlist;
1067127499Sgad
1068127499Sgad	return (newlist);
106966377Sbrian}
107066377Sbrian
1071127499Sgadstatic void
1072127499Sgadfree_list(struct listinfo *inf)
1073127499Sgad{
1074127499Sgad
1075127499Sgad	inf->count = inf->elemsize = inf->maxcount = 0;
1076127823Sgad	if (inf->l.ptr != NULL)
1077127823Sgad		free(inf->l.ptr);
1078127499Sgad	inf->addelem = NULL;
1079127499Sgad	inf->lname = NULL;
1080127823Sgad	inf->l.ptr = NULL;
1081127499Sgad}
1082127499Sgad
1083127499Sgadstatic void
1084127499Sgadinit_list(struct listinfo *inf, addelem_rtn artn, int elemsize,
1085127499Sgad    const char *lname)
1086127499Sgad{
1087127499Sgad
1088127499Sgad	inf->count = inf->maxcount = 0;
1089127499Sgad	inf->elemsize = elemsize;
1090127499Sgad	inf->addelem = artn;
1091127499Sgad	inf->lname = lname;
1092127823Sgad	inf->l.ptr = NULL;
1093127499Sgad}
1094127499Sgad
1095109502SjmallettVARENT *
1096109502Sjmallettfind_varentry(VAR *v)
1097109502Sjmallett{
1098109502Sjmallett	struct varent *vent;
1099109502Sjmallett
1100130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
1101109502Sjmallett		if (strcmp(vent->var->name, v->name) == 0)
1102109502Sjmallett			return vent;
1103109502Sjmallett	}
1104109502Sjmallett	return NULL;
1105109502Sjmallett}
1106109502Sjmallett
11071556Srgrimesstatic void
110890110Simpscanvars(void)
11091556Srgrimes{
11101556Srgrimes	struct varent *vent;
11111556Srgrimes	VAR *v;
111225271Sjkh
1113130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
111425271Sjkh		v = vent->var;
111525271Sjkh		if (v->flag & USER)
111625271Sjkh			needuser = 1;
111725271Sjkh		if (v->flag & COMM)
111825271Sjkh			needcomm = 1;
111925271Sjkh	}
112025271Sjkh}
112125271Sjkh
112225271Sjkhstatic void
1123225868Straszformat_output(KINFO *ki)
112425271Sjkh{
112525271Sjkh	struct varent *vent;
112625271Sjkh	VAR *v;
1127225868Strasz	KINFO_STR *ks;
1128225868Strasz	char *str;
1129225868Strasz	int len;
11301556Srgrimes
1131225868Strasz	STAILQ_INIT(&ki->ki_ks);
1132130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
11331556Srgrimes		v = vent->var;
1134225868Strasz		str = (v->oproc)(ki, vent);
1135225868Strasz		ks = malloc(sizeof(*ks));
1136225868Strasz		if (ks == NULL)
1137225868Strasz			errx(1, "malloc failed");
1138225868Strasz		ks->ks_str = str;
1139225868Strasz		STAILQ_INSERT_TAIL(&ki->ki_ks, ks, ks_next);
1140225868Strasz		if (str != NULL) {
1141225868Strasz			len = strlen(str);
1142225868Strasz		} else
1143225868Strasz			len = 1; /* "-" */
1144225868Strasz		if (v->width < len)
1145225868Strasz			v->width = len;
114625271Sjkh	}
114725271Sjkh}
114825271Sjkh
114925271Sjkhstatic void
115090110Simpsizevars(void)
115125271Sjkh{
115225271Sjkh	struct varent *vent;
115325271Sjkh	VAR *v;
115425271Sjkh	int i;
115525271Sjkh
1156130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
115725271Sjkh		v = vent->var;
1158109504Sjmallett		i = strlen(vent->header);
11591556Srgrimes		if (v->width < i)
11601556Srgrimes			v->width = i;
11611556Srgrimes	}
11621556Srgrimes}
11631556Srgrimes
116490143Smarkmstatic const char *
116590110Simpfmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki,
1166245610Sjhb    char *comm, char *thread, int maxlen)
11671556Srgrimes{
116890143Smarkm	const char *s;
11691556Srgrimes
1170245610Sjhb	s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm,
1171245635Sjhb	    showthreads && ki->ki_p->ki_numthreads > 1 ? thread : NULL, maxlen);
11721556Srgrimes	return (s);
11731556Srgrimes}
11741556Srgrimes
1175172207Sjeff#define UREADOK(ki)	(forceuread || (ki->ki_p->ki_flag & P_INMEM))
117631552Sdyson
11771556Srgrimesstatic void
117890110Simpsaveuser(KINFO *ki)
11791556Srgrimes{
11801556Srgrimes
1181172207Sjeff	if (ki->ki_p->ki_flag & P_INMEM) {
11821556Srgrimes		/*
11831556Srgrimes		 * The u-area might be swapped out, and we can't get
11841556Srgrimes		 * at it because we have a crashdump and no swap.
11851556Srgrimes		 * If it's here fill in these fields, otherwise, just
11861556Srgrimes		 * leave them 0.
11871556Srgrimes		 */
118869896Smckusick		ki->ki_valid = 1;
11891556Srgrimes	} else
119069896Smckusick		ki->ki_valid = 0;
11911556Srgrimes	/*
11921556Srgrimes	 * save arguments if needed
11931556Srgrimes	 */
1194130991Sgad	if (needcomm) {
1195130991Sgad		if (ki->ki_p->ki_stat == SZOMB)
1196130991Sgad			ki->ki_args = strdup("<defunct>");
1197130991Sgad		else if (UREADOK(ki) || (ki->ki_p->ki_args != NULL))
1198130991Sgad			ki->ki_args = strdup(fmt(kvm_getargv, ki,
1199245610Sjhb			    ki->ki_p->ki_comm, ki->ki_p->ki_tdname, MAXCOMLEN));
1200130991Sgad		else
1201130991Sgad			asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm);
1202131024Sgad		if (ki->ki_args == NULL)
1203130991Sgad			errx(1, "malloc failed");
120453276Speter	} else {
120553276Speter		ki->ki_args = NULL;
120653276Speter	}
1207130991Sgad	if (needenv) {
1208130991Sgad		if (UREADOK(ki))
1209130991Sgad			ki->ki_env = strdup(fmt(kvm_getenvv, ki,
1210245610Sjhb			    (char *)NULL, (char *)NULL, 0));
1211130991Sgad		else
1212130991Sgad			ki->ki_env = strdup("()");
1213130991Sgad		if (ki->ki_env == NULL)
1214130991Sgad			errx(1, "malloc failed");
121553276Speter	} else {
121653276Speter		ki->ki_env = NULL;
121753276Speter	}
12181556Srgrimes}
12191556Srgrimes
1220130816Sgad/* A macro used to improve the readability of pscomp(). */
1221130816Sgad#define	DIFF_RETURN(a, b, field) do {	\
1222130816Sgad	if ((a)->field != (b)->field)	\
1223130816Sgad		return (((a)->field < (b)->field) ? -1 : 1); 	\
1224130816Sgad} while (0)
1225130816Sgad
12261556Srgrimesstatic int
122790110Simppscomp(const void *a, const void *b)
12281556Srgrimes{
1229127596Sgad	const KINFO *ka, *kb;
12301556Srgrimes
1231127596Sgad	ka = a;
1232127596Sgad	kb = b;
1233127596Sgad	/* SORTCPU and SORTMEM are sorted in descending order. */
1234130816Sgad	if (sortby == SORTCPU)
1235130816Sgad		DIFF_RETURN(kb, ka, ki_pcpu);
1236130816Sgad	if (sortby == SORTMEM)
1237130816Sgad		DIFF_RETURN(kb, ka, ki_memsize);
1238127596Sgad	/*
1239127596Sgad	 * TTY's are sorted in ascending order, except that all NODEV
1240127596Sgad	 * processes come before all processes with a device.
1241127596Sgad	 */
1242130816Sgad	if (ka->ki_p->ki_tdev != kb->ki_p->ki_tdev) {
1243130816Sgad		if (ka->ki_p->ki_tdev == NODEV)
1244130816Sgad			return (-1);
1245130816Sgad		if (kb->ki_p->ki_tdev == NODEV)
1246130816Sgad			return (1);
1247130816Sgad		DIFF_RETURN(ka, kb, ki_p->ki_tdev);
1248130816Sgad	}
1249130816Sgad
1250130972Sgad	/* PID's and TID's (threads) are sorted in ascending order. */
1251130816Sgad	DIFF_RETURN(ka, kb, ki_p->ki_pid);
1252130972Sgad	DIFF_RETURN(ka, kb, ki_p->ki_tid);
1253127596Sgad	return (0);
12541556Srgrimes}
1255130816Sgad#undef DIFF_RETURN
12561556Srgrimes
12571556Srgrimes/*
12581556Srgrimes * ICK (all for getopt), would rather hide the ugliness
12591556Srgrimes * here than taint the main code.
12601556Srgrimes *
12611556Srgrimes *  ps foo -> ps -foo
12621556Srgrimes *  ps 34 -> ps -p34
12631556Srgrimes *
12641556Srgrimes * The old convention that 't' with no trailing tty arg means the users
12651556Srgrimes * tty, is only supported if argv[1] doesn't begin with a '-'.  This same
12661556Srgrimes * feature is available with the option 'T', which takes no argument.
12671556Srgrimes */
12681556Srgrimesstatic char *
1269129915Sgadkludge_oldps_options(const char *optlist, char *origval, const char *nextarg)
12701556Srgrimes{
12711556Srgrimes	size_t len;
1272129914Sgad	char *argp, *cp, *newopts, *ns, *optp, *pidp;
12731556Srgrimes
1274102886Sjmallett	/*
1275129914Sgad	 * See if the original value includes any option which takes an
1276129914Sgad	 * argument (and will thus use up the remainder of the string).
1277102886Sjmallett	 */
1278129914Sgad	argp = NULL;
1279129914Sgad	if (optlist != NULL) {
1280129914Sgad		for (cp = origval; *cp != '\0'; cp++) {
1281129914Sgad			optp = strchr(optlist, *cp);
1282129914Sgad			if ((optp != NULL) && *(optp + 1) == ':') {
1283129914Sgad				argp = cp;
1284129914Sgad				break;
1285129914Sgad			}
1286129914Sgad		}
1287129914Sgad	}
1288129914Sgad	if (argp != NULL && *origval == '-')
1289129914Sgad		return (origval);
1290102886Sjmallett
12911556Srgrimes	/*
12921556Srgrimes	 * if last letter is a 't' flag with no argument (in the context
12931556Srgrimes	 * of the oldps options -- option string NOT starting with a '-' --
12941556Srgrimes	 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
129581743Sbrian	 *
1296129634Sgad	 * However, if a flag accepting a string argument is found earlier
1297129634Sgad	 * in the option string (including a possible `t' flag), then the
1298129634Sgad	 * remainder of the string must be the argument to that flag; so
1299129914Sgad	 * do not modify that argument.  Note that a trailing `t' would
1300129914Sgad	 * cause argp to be set, if argp was not already set by some
1301129914Sgad	 * earlier option.
13021556Srgrimes	 */
1303129914Sgad	len = strlen(origval);
1304129914Sgad	cp = origval + len - 1;
1305129914Sgad	pidp = NULL;
1306129915Sgad	if (*cp == 't' && *origval != '-' && cp == argp) {
1307129915Sgad		if (nextarg == NULL || *nextarg == '-' || isdigitch(*nextarg))
1308129915Sgad			*cp = 'T';
1309129915Sgad	} else if (argp == NULL) {
13101556Srgrimes		/*
1311129914Sgad		 * The original value did not include any option which takes
1312129914Sgad		 * an argument (and that would include `p' and `t'), so check
1313129914Sgad		 * the value for trailing number, or comma-separated list of
1314129914Sgad		 * numbers, which we will treat as a pid request.
13151556Srgrimes		 */
1316129914Sgad		if (isdigitch(*cp)) {
1317129914Sgad			while (cp >= origval && (*cp == ',' || isdigitch(*cp)))
1318129914Sgad				--cp;
1319129914Sgad			pidp = cp + 1;
1320129914Sgad		}
13211556Srgrimes	}
1322129914Sgad
13231556Srgrimes	/*
1324129914Sgad	 * If nothing needs to be added to the string, then return
1325129914Sgad	 * the "original" (although possibly modified) value.
13261556Srgrimes	 */
1327129914Sgad	if (*origval == '-' && pidp == NULL)
1328129914Sgad		return (origval);
1329129914Sgad
1330129914Sgad	/*
1331129914Sgad	 * Create a copy of the string to add '-' and/or 'p' to the
1332129914Sgad	 * original value.
1333129914Sgad	 */
1334129914Sgad	if ((newopts = ns = malloc(len + 3)) == NULL)
1335129914Sgad		errx(1, "malloc failed");
1336129914Sgad
1337129914Sgad	if (*origval != '-')
1338129914Sgad		*ns++ = '-';	/* add option flag */
1339129914Sgad
1340129914Sgad	if (pidp == NULL)
1341129914Sgad		strcpy(ns, origval);
1342129914Sgad	else {
1343129914Sgad		/*
1344129914Sgad		 * Copy everything before the pid string, add the `p',
1345129914Sgad		 * and then copy the pid string.
1346129914Sgad		 */
1347129914Sgad		len = pidp - origval;
1348129914Sgad		memcpy(ns, origval, len);
1349129914Sgad		ns += len;
13501556Srgrimes		*ns++ = 'p';
1351129914Sgad		strcpy(ns, pidp);
1352129914Sgad	}
13531556Srgrimes
13541556Srgrimes	return (newopts);
13551556Srgrimes}
13561556Srgrimes
13571556Srgrimesstatic void
1358244154Spjdpidmax_init(void)
1359244154Spjd{
1360244154Spjd	size_t intsize;
1361244154Spjd
1362244154Spjd	intsize = sizeof(pid_max);
1363244154Spjd	if (sysctlbyname("kern.pid_max", &pid_max, &intsize, NULL, 0) < 0) {
1364244154Spjd		warn("unable to read kern.pid_max");
1365244154Spjd		pid_max = 99999;
1366244154Spjd	}
1367244154Spjd}
1368244154Spjd
1369244154Spjdstatic void
137090110Simpusage(void)
13711556Srgrimes{
1372195830Sbrian#define	SINGLE_OPTS	"[-aCcde" OPT_LAZY_f "HhjlmrSTuvwXxZ]"
13731556Srgrimes
1374127499Sgad	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
1375141578Sru	    "usage: ps " SINGLE_OPTS " [-O fmt | -o fmt] [-G gid[,gid...]]",
1376127499Sgad	    "          [-M core] [-N system]",
1377141578Sru	    "          [-p pid[,pid...]] [-t tty[,tty...]] [-U user[,user...]]",
137826465Scharnier	    "       ps [-L]");
13791556Srgrimes	exit(1);
13801556Srgrimes}
1381