ps.c revision 137670
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 137670 2004-11-13 17:12:22Z ru $");
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>
581556Srgrimes
591556Srgrimes#include <ctype.h>
601556Srgrimes#include <err.h>
61127149Sgad#include <errno.h>
621556Srgrimes#include <fcntl.h>
63127499Sgad#include <grp.h>
641556Srgrimes#include <kvm.h>
6513514Smpp#include <limits.h>
6673367Sache#include <locale.h>
671556Srgrimes#include <paths.h>
6890143Smarkm#include <pwd.h>
691556Srgrimes#include <stdio.h>
701556Srgrimes#include <stdlib.h>
711556Srgrimes#include <string.h>
721556Srgrimes#include <unistd.h>
731556Srgrimes
741556Srgrimes#include "ps.h"
751556Srgrimes
76127499Sgad#define	W_SEP	" \t"		/* "Whitespace" list separators */
77127499Sgad#define	T_SEP	","		/* "Terminate-element" list separators */
7866377Sbrian
79127537Sgad#ifdef LAZY_PS
80127555Sgad#define	DEF_UREAD	0
81127537Sgad#define	OPT_LAZY_f	"f"
82127537Sgad#else
83127555Sgad#define	DEF_UREAD	1	/* Always do the more-expensive read. */
84127537Sgad#define	OPT_LAZY_f		/* I.e., the `-f' option is not added. */
85127537Sgad#endif
86127537Sgad
87129914Sgad/*
88129971Sgad * isdigit takes an `int', but expects values in the range of unsigned char.
89129971Sgad * This wrapper ensures that values from a 'char' end up in the correct range.
90129914Sgad */
91129971Sgad#define	isdigitch(Anychar) isdigit((u_char)(Anychar))
92129914Sgad
93127537Sgadint	 cflag;			/* -c */
94127537Sgadint	 eval;			/* Exit value */
95127537Sgadtime_t	 now;			/* Current time(3) value */
96127537Sgadint	 rawcpu;		/* -C */
97127537Sgadint	 sumrusage;		/* -S */
98127537Sgadint	 termwidth;		/* Width of the screen (0 == infinity). */
99127537Sgadint	 totwidth;		/* Calculated-width of requested variables. */
100127537Sgad
101130999Sgadstruct velisthead varlist = STAILQ_HEAD_INITIALIZER(varlist);
1021556Srgrimes
103127537Sgadstatic int	 forceuread = DEF_UREAD; /* Do extra work to get u-area. */
104127537Sgadstatic kvm_t	*kd;
105127537Sgadstatic KINFO	*kinfo;
106127537Sgadstatic int	 needcomm;	/* -o "command" */
107127537Sgadstatic int	 needenv;	/* -e */
108127537Sgadstatic int	 needuser;	/* -o "user" */
109127537Sgadstatic int	 optfatal;	/* Fatal error parsing some list-option. */
1101556Srgrimes
111127537Sgadstatic enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
11297966Sjmallett
113127499Sgadstruct listinfo;
114127537Sgadtypedef	int	addelem_rtn(struct listinfo *_inf, const char *_elem);
115127499Sgad
116127499Sgadstruct listinfo {
117127499Sgad	int		 count;
118127499Sgad	int		 maxcount;
119127499Sgad	int		 elemsize;
120127499Sgad	addelem_rtn	*addelem;
121127499Sgad	const char	*lname;
122127499Sgad	union {
123127499Sgad		gid_t	*gids;
124127499Sgad		pid_t	*pids;
125127499Sgad		dev_t	*ttys;
126127499Sgad		uid_t	*uids;
127127499Sgad		void	*ptr;
128127823Sgad	} l;
129127499Sgad};
130127499Sgad
131127499Sgadstatic int	 addelem_gid(struct listinfo *, const char *);
132127499Sgadstatic int	 addelem_pid(struct listinfo *, const char *);
133127499Sgadstatic int	 addelem_tty(struct listinfo *, const char *);
134127499Sgadstatic int	 addelem_uid(struct listinfo *, const char *);
135127499Sgadstatic void	 add_list(struct listinfo *, const char *);
136127536Sgadstatic void	 dynsizevars(KINFO *);
137127499Sgadstatic void	*expand_list(struct listinfo *);
138127598Sgadstatic const char *
139127598Sgad		 fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int),
140127536Sgad		    KINFO *, char *, int);
141127499Sgadstatic void	 free_list(struct listinfo *);
142127499Sgadstatic void	 init_list(struct listinfo *, addelem_rtn, int, const char *);
143129952Sgadstatic char	*kludge_oldps_options(const char *, char *, const char *);
144127536Sgadstatic int	 pscomp(const void *, const void *);
145127536Sgadstatic void	 saveuser(KINFO *);
146127536Sgadstatic void	 scanvars(void);
147127536Sgadstatic void	 sizevars(void);
148127536Sgadstatic void	 usage(void);
149127499Sgad
15097875Sjmallettstatic char dfmt[] = "pid,tt,state,time,command";
151129635Sgadstatic char jfmt[] = "user,pid,ppid,pgid,sid,jobc,state,tt,time,command";
152127538Sgadstatic char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,"
153127538Sgad			"tt,time,command";
15490143Smarkmstatic char   o1[] = "pid";
15597875Sjmallettstatic char   o2[] = "tt,state,time,command";
15697875Sjmallettstatic char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
157127538Sgadstatic char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,"
158127538Sgad			"%cpu,%mem,command";
159105831Srwatsonstatic char Zfmt[] = "label";
1601556Srgrimes
161127843Sgad#define	PS_ARGS	"AaCce" OPT_LAZY_f "G:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ"
16298494Ssobomax
1631556Srgrimesint
16490110Simpmain(int argc, char *argv[])
1651556Srgrimes{
166127499Sgad	struct listinfo gidlist, pgrplist, pidlist;
167127499Sgad	struct listinfo ruidlist, sesslist, ttylist, uidlist;
1681556Srgrimes	struct kinfo_proc *kp;
169130816Sgad	KINFO *next_KINFO;
1701556Srgrimes	struct varent *vent;
1711556Srgrimes	struct winsize ws;
172129914Sgad	const char *nlistf, *memf;
173127539Sgad	char *cols;
174137670Sru	int all, ch, elem, flag, _fmt, i, lineno;
175129914Sgad	int nentries, nkept, nselectors;
176127499Sgad	int prtheader, showthreads, wflag, what, xkeep, xkeep_implied;
17790143Smarkm	char errbuf[_POSIX2_LINE_MAX];
1781556Srgrimes
17911809Sache	(void) setlocale(LC_ALL, "");
180127542Sgad	time(&now);			/* Used by routines in print.c. */
18111809Sache
18297804Stjr	if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0')
18397804Stjr		termwidth = atoi(cols);
18497804Stjr	else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
1851556Srgrimes	     ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
1861556Srgrimes	     ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) == -1) ||
1871556Srgrimes	     ws.ws_col == 0)
1881556Srgrimes		termwidth = 79;
1891556Srgrimes	else
1901556Srgrimes		termwidth = ws.ws_col - 1;
1911556Srgrimes
19298494Ssobomax	/*
193129914Sgad	 * Hide a number of option-processing kludges in a separate routine,
194129914Sgad	 * to support some historical BSD behaviors, such as `ps axu'.
19598494Ssobomax	 */
196129914Sgad	if (argc > 1)
197129915Sgad		argv[1] = kludge_oldps_options(PS_ARGS, argv[1], argv[2]);
1981556Srgrimes
199137670Sru	all = _fmt = nselectors = optfatal = 0;
200127542Sgad	prtheader = showthreads = wflag = xkeep_implied = 0;
201127542Sgad	xkeep = -1;			/* Neither -x nor -X. */
202127499Sgad	init_list(&gidlist, addelem_gid, sizeof(gid_t), "group");
203127499Sgad	init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group");
204127499Sgad	init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id");
205127499Sgad	init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser");
206127499Sgad	init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id");
207127499Sgad	init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty");
208127499Sgad	init_list(&uidlist, addelem_uid, sizeof(uid_t), "user");
20989909Sru	memf = nlistf = _PATH_DEVNULL;
21098494Ssobomax	while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
211129967Sgad		switch ((char)ch) {
212127499Sgad		case 'A':
213127499Sgad			/*
214127499Sgad			 * Exactly the same as `-ax'.   This has been
215127499Sgad			 * added for compatability with SUSv3, but for
216127499Sgad			 * now it will not be described in the man page.
217127499Sgad			 */
218127499Sgad			nselectors++;
219127499Sgad			all = xkeep = 1;
220127499Sgad			break;
2211556Srgrimes		case 'a':
222127499Sgad			nselectors++;
2231556Srgrimes			all = 1;
2241556Srgrimes			break;
22519068Speter		case 'C':
22619068Speter			rawcpu = 1;
22719068Speter			break;
22819068Speter		case 'c':
22919068Speter			cflag = 1;
23019068Speter			break;
2311556Srgrimes		case 'e':			/* XXX set ufmt */
2321556Srgrimes			needenv = 1;
2331556Srgrimes			break;
234127506Sgad#ifdef LAZY_PS
235127506Sgad		case 'f':
236127506Sgad			if (getuid() == 0 || getgid() == 0)
237127542Sgad				forceuread = 1;
238127506Sgad			break;
239127506Sgad#endif
240127499Sgad		case 'G':
241127499Sgad			add_list(&gidlist, optarg);
242127499Sgad			xkeep_implied = 1;
243127499Sgad			nselectors++;
244127499Sgad			break;
245127542Sgad		case 'g':
246127499Sgad#if 0
247127597Sgad			/*-
248127542Sgad			 * XXX - This SUSv3 behavior is still under debate
249127542Sgad			 *	since it conflicts with the (undocumented)
250127542Sgad			 *	`-g' option.  So we skip it for now.
251127542Sgad			 */
252127499Sgad			add_list(&pgrplist, optarg);
253127499Sgad			xkeep_implied = 1;
254127499Sgad			nselectors++;
255127499Sgad			break;
256127499Sgad#else
257127542Sgad			/* The historical BSD-ish (from SunOS) behavior. */
2581556Srgrimes			break;			/* no-op */
259127499Sgad#endif
260116265Sscottl		case 'H':
261126127Sdeischen			showthreads = KERN_PROC_INC_THREAD;
262116265Sscottl			break;
2631556Srgrimes		case 'h':
2641556Srgrimes			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
2651556Srgrimes			break;
2661556Srgrimes		case 'j':
267109502Sjmallett			parsefmt(jfmt, 0);
26890143Smarkm			_fmt = 1;
2691556Srgrimes			jfmt[0] = '\0';
2701556Srgrimes			break;
2711556Srgrimes		case 'L':
2721556Srgrimes			showkey();
2731556Srgrimes			exit(0);
2741556Srgrimes		case 'l':
275109502Sjmallett			parsefmt(lfmt, 0);
27690143Smarkm			_fmt = 1;
2771556Srgrimes			lfmt[0] = '\0';
2781556Srgrimes			break;
2791556Srgrimes		case 'M':
2801556Srgrimes			memf = optarg;
2811556Srgrimes			break;
2821556Srgrimes		case 'm':
2831556Srgrimes			sortby = SORTMEM;
2841556Srgrimes			break;
2851556Srgrimes		case 'N':
2861556Srgrimes			nlistf = optarg;
2871556Srgrimes			break;
2881556Srgrimes		case 'O':
289109502Sjmallett			parsefmt(o1, 1);
290109502Sjmallett			parsefmt(optarg, 1);
291109502Sjmallett			parsefmt(o2, 1);
2921556Srgrimes			o1[0] = o2[0] = '\0';
29390143Smarkm			_fmt = 1;
2941556Srgrimes			break;
2951556Srgrimes		case 'o':
296109502Sjmallett			parsefmt(optarg, 1);
29790143Smarkm			_fmt = 1;
2981556Srgrimes			break;
2991556Srgrimes		case 'p':
300127499Sgad			add_list(&pidlist, optarg);
301127499Sgad			/*
302127499Sgad			 * Note: `-p' does not *set* xkeep, but any values
303127499Sgad			 * from pidlist are checked before xkeep is.  That
304127499Sgad			 * way they are always matched, even if the user
305127499Sgad			 * specifies `-X'.
306127499Sgad			 */
307127499Sgad			nselectors++;
3081556Srgrimes			break;
309127499Sgad#if 0
310127499Sgad		case 'R':
311127597Sgad			/*-
312127542Sgad			 * XXX - This un-standard option is still under
313127542Sgad			 *	debate.  This is what SUSv3 defines as
314127542Sgad			 *	the `-U' option, and while it would be
315127542Sgad			 *	nice to have, it could cause even more
316127542Sgad			 *	confusion to implement it as `-R'.
317127542Sgad			 */
318127499Sgad			add_list(&ruidlist, optarg);
319127499Sgad			xkeep_implied = 1;
320127499Sgad			nselectors++;
321127499Sgad			break;
322127499Sgad#endif
3231556Srgrimes		case 'r':
3241556Srgrimes			sortby = SORTCPU;
3251556Srgrimes			break;
3261556Srgrimes		case 'S':
3271556Srgrimes			sumrusage = 1;
3281556Srgrimes			break;
329127499Sgad#if 0
330127499Sgad		case 's':
331127597Sgad			/*-
332127542Sgad			 * XXX - This non-standard option is still under
333127542Sgad			 *	debate.  This *is* supported on Solaris,
334127542Sgad			 *	Linux, and IRIX, but conflicts with `-s'
335127542Sgad			 *	on NetBSD and maybe some older BSD's.
336127542Sgad			 */
337127499Sgad			add_list(&sesslist, optarg);
338127499Sgad			xkeep_implied = 1;
339127499Sgad			nselectors++;
340127499Sgad			break;
341127499Sgad#endif
3421556Srgrimes		case 'T':
3431556Srgrimes			if ((optarg = ttyname(STDIN_FILENO)) == NULL)
3441556Srgrimes				errx(1, "stdin: not a terminal");
3451556Srgrimes			/* FALLTHROUGH */
346127499Sgad		case 't':
347127499Sgad			add_list(&ttylist, optarg);
348127499Sgad			xkeep_implied = 1;
349127499Sgad			nselectors++;
3501556Srgrimes			break;
35113020Speter		case 'U':
352127499Sgad			/* This is what SUSv3 defines as the `-u' option. */
353127499Sgad			add_list(&uidlist, optarg);
354127499Sgad			xkeep_implied = 1;
355127499Sgad			nselectors++;
35613020Speter			break;
3571556Srgrimes		case 'u':
358109502Sjmallett			parsefmt(ufmt, 0);
3591556Srgrimes			sortby = SORTCPU;
36090143Smarkm			_fmt = 1;
3611556Srgrimes			ufmt[0] = '\0';
3621556Srgrimes			break;
3631556Srgrimes		case 'v':
364109502Sjmallett			parsefmt(vfmt, 0);
3651556Srgrimes			sortby = SORTMEM;
36690143Smarkm			_fmt = 1;
3671556Srgrimes			vfmt[0] = '\0';
3681556Srgrimes			break;
3691556Srgrimes		case 'w':
3701556Srgrimes			if (wflag)
3711556Srgrimes				termwidth = UNLIMITED;
3721556Srgrimes			else if (termwidth < 131)
3731556Srgrimes				termwidth = 131;
3741556Srgrimes			wflag++;
3751556Srgrimes			break;
376127499Sgad		case 'X':
377127499Sgad			/*
378127499Sgad			 * Note that `-X' and `-x' are not standard "selector"
379127499Sgad			 * options. For most selector-options, we check *all*
380127499Sgad			 * processes to see if any are matched by the given
381127499Sgad			 * value(s).  After we have a set of all the matched
382127499Sgad			 * processes, then `-X' and `-x' govern whether we
383127499Sgad			 * modify that *matched* set for processes which do
384127499Sgad			 * not have a controlling terminal.  `-X' causes
385127499Sgad			 * those processes to be deleted from the matched
386127499Sgad			 * set, while `-x' causes them to be kept.
387127499Sgad			 */
388127499Sgad			xkeep = 0;
389127499Sgad			break;
3901556Srgrimes		case 'x':
391127499Sgad			xkeep = 1;
3921556Srgrimes			break;
39386922Sgreen		case 'Z':
394109502Sjmallett			parsefmt(Zfmt, 0);
39586922Sgreen			Zfmt[0] = '\0';
39686922Sgreen			break;
3971556Srgrimes		case '?':
3981556Srgrimes		default:
3991556Srgrimes			usage();
4001556Srgrimes		}
4011556Srgrimes	argc -= optind;
4021556Srgrimes	argv += optind;
403129914Sgad
404129914Sgad	/*
405129914Sgad	 * If there arguments after processing all the options, attempt
406129914Sgad	 * to treat them as a list of process ids.
407129914Sgad	 */
408129914Sgad	while (*argv) {
409129914Sgad		if (!isdigitch(**argv))
410129914Sgad			break;
411129914Sgad		add_list(&pidlist, *argv);
412129914Sgad		argv++;
413129914Sgad	}
414129914Sgad	if (*argv) {
415129914Sgad		fprintf(stderr, "%s: illegal argument: %s\n",
416129914Sgad		    getprogname(), *argv);
417129914Sgad		usage();
418129914Sgad	}
419127499Sgad	if (optfatal)
420127542Sgad		exit(1);		/* Error messages already printed. */
421127542Sgad	if (xkeep < 0)			/* Neither -X nor -x was specified. */
422127499Sgad		xkeep = xkeep_implied;
423127499Sgad
42489909Sru	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
4251556Srgrimes	if (kd == 0)
4261556Srgrimes		errx(1, "%s", errbuf);
4271556Srgrimes
42890143Smarkm	if (!_fmt)
429109502Sjmallett		parsefmt(dfmt, 0);
4301556Srgrimes
431127499Sgad	if (nselectors == 0) {
432127823Sgad		uidlist.l.ptr = malloc(sizeof(uid_t));
433127823Sgad		if (uidlist.l.ptr == NULL)
43497877Sjmallett			errx(1, "malloc failed");
435127499Sgad		nselectors = 1;
436127499Sgad		uidlist.count = uidlist.maxcount = 1;
437127823Sgad		*uidlist.l.uids = getuid();
43866377Sbrian	}
4391556Srgrimes
4401556Srgrimes	/*
4411556Srgrimes	 * scan requested variables, noting what structures are needed,
44253170Skris	 * and adjusting header widths as appropriate.
4431556Srgrimes	 */
4441556Srgrimes	scanvars();
445127499Sgad
4461556Srgrimes	/*
447127499Sgad	 * Get process list.  If the user requested just one selector-
448127499Sgad	 * option, then kvm_getprocs can be asked to return just those
449127499Sgad	 * processes.  Otherwise, have it return all processes, and
450127499Sgad	 * then this routine will search that full list and select the
451127499Sgad	 * processes which match any of the user's selector-options.
4521556Srgrimes	 */
453127499Sgad	what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
454127499Sgad	flag = 0;
455127499Sgad	if (nselectors == 1) {
456129600Sgad		if (gidlist.count == 1) {
457129600Sgad			what = KERN_PROC_RGID | showthreads;
458129600Sgad			flag = *gidlist.l.gids;
459129600Sgad			nselectors = 0;
460129600Sgad		} else if (pgrplist.count == 1) {
461127499Sgad			what = KERN_PROC_PGRP | showthreads;
462127823Sgad			flag = *pgrplist.l.pids;
463127499Sgad			nselectors = 0;
464127499Sgad		} else if (pidlist.count == 1) {
465127499Sgad			what = KERN_PROC_PID | showthreads;
466127823Sgad			flag = *pidlist.l.pids;
467127499Sgad			nselectors = 0;
468127499Sgad		} else if (ruidlist.count == 1) {
469127499Sgad			what = KERN_PROC_RUID | showthreads;
470127823Sgad			flag = *ruidlist.l.uids;
471127499Sgad			nselectors = 0;
472127499Sgad		} else if (sesslist.count == 1) {
473127499Sgad			what = KERN_PROC_SESSION | showthreads;
474127823Sgad			flag = *sesslist.l.pids;
475127499Sgad			nselectors = 0;
476127499Sgad		} else if (ttylist.count == 1) {
477127499Sgad			what = KERN_PROC_TTY | showthreads;
478127823Sgad			flag = *ttylist.l.ttys;
479127499Sgad			nselectors = 0;
480127499Sgad		} else if (uidlist.count == 1) {
481127499Sgad			what = KERN_PROC_UID | showthreads;
482127823Sgad			flag = *uidlist.l.uids;
483127499Sgad			nselectors = 0;
484127499Sgad		} else if (all) {
485127499Sgad			/* No need for this routine to select processes. */
486127499Sgad			nselectors = 0;
487127499Sgad		}
4881556Srgrimes	}
489126127Sdeischen
4901556Srgrimes	/*
4911556Srgrimes	 * select procs
4921556Srgrimes	 */
493127499Sgad	nentries = -1;
494127149Sgad	kp = kvm_getprocs(kd, what, flag, &nentries);
495127544Sgad	if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0))
4961556Srgrimes		errx(1, "%s", kvm_geterr(kd));
497127499Sgad	nkept = 0;
498127149Sgad	if (nentries > 0) {
499127149Sgad		if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
500127149Sgad			errx(1, "malloc failed");
501127149Sgad		for (i = nentries; --i >= 0; ++kp) {
502127499Sgad			/*
503127499Sgad			 * If the user specified multiple selection-criteria,
504127499Sgad			 * then keep any process matched by the inclusive OR
505127499Sgad			 * of all the selection-criteria given.
506127499Sgad			 */
507127499Sgad			if (pidlist.count > 0) {
508127499Sgad				for (elem = 0; elem < pidlist.count; elem++)
509127823Sgad					if (kp->ki_pid == pidlist.l.pids[elem])
510127499Sgad						goto keepit;
511127499Sgad			}
512127499Sgad			/*
513127499Sgad			 * Note that we had to process pidlist before
514127499Sgad			 * filtering out processes which do not have
515127499Sgad			 * a controlling terminal.
516127499Sgad			 */
517127499Sgad			if (xkeep == 0) {
518127499Sgad				if ((kp->ki_tdev == NODEV ||
519127499Sgad				    (kp->ki_flag & P_CONTROLT) == 0))
520127499Sgad					continue;
521127499Sgad			}
522127499Sgad			if (nselectors == 0)
523127499Sgad				goto keepit;
524127499Sgad			if (gidlist.count > 0) {
525127499Sgad				for (elem = 0; elem < gidlist.count; elem++)
526127823Sgad					if (kp->ki_rgid == gidlist.l.gids[elem])
527127499Sgad						goto keepit;
528127499Sgad			}
529127499Sgad			if (pgrplist.count > 0) {
530127499Sgad				for (elem = 0; elem < pgrplist.count; elem++)
531127823Sgad					if (kp->ki_pgid ==
532127823Sgad					    pgrplist.l.pids[elem])
533127499Sgad						goto keepit;
534127499Sgad			}
535127499Sgad			if (ruidlist.count > 0) {
536127499Sgad				for (elem = 0; elem < ruidlist.count; elem++)
537127823Sgad					if (kp->ki_ruid ==
538127823Sgad					    ruidlist.l.uids[elem])
539127499Sgad						goto keepit;
540127499Sgad			}
541127499Sgad			if (sesslist.count > 0) {
542127499Sgad				for (elem = 0; elem < sesslist.count; elem++)
543127823Sgad					if (kp->ki_sid == sesslist.l.pids[elem])
544127499Sgad						goto keepit;
545127499Sgad			}
546127499Sgad			if (ttylist.count > 0) {
547127499Sgad				for (elem = 0; elem < ttylist.count; elem++)
548127823Sgad					if (kp->ki_tdev == ttylist.l.ttys[elem])
549127499Sgad						goto keepit;
550127499Sgad			}
551127499Sgad			if (uidlist.count > 0) {
552127499Sgad				for (elem = 0; elem < uidlist.count; elem++)
553127823Sgad					if (kp->ki_uid == uidlist.l.uids[elem])
554127499Sgad						goto keepit;
555127499Sgad			}
556127499Sgad			/*
557127499Sgad			 * This process did not match any of the user's
558127499Sgad			 * selector-options, so skip the process.
559127499Sgad			 */
560127499Sgad			continue;
561127499Sgad
562127499Sgad		keepit:
563130816Sgad			next_KINFO = &kinfo[nkept];
564130816Sgad			next_KINFO->ki_p = kp;
565130816Sgad			next_KINFO->ki_pcpu = getpcpu(next_KINFO);
566130816Sgad			if (sortby == SORTMEM)
567130816Sgad				next_KINFO->ki_memsize = kp->ki_tsize +
568130816Sgad				    kp->ki_dsize + kp->ki_ssize;
569127149Sgad			if (needuser)
570130816Sgad				saveuser(next_KINFO);
571130816Sgad			dynsizevars(next_KINFO);
572127499Sgad			nkept++;
573127149Sgad		}
5741556Srgrimes	}
57525271Sjkh
57625271Sjkh	sizevars();
57725271Sjkh
5781556Srgrimes	/*
5791556Srgrimes	 * print header
5801556Srgrimes	 */
5811556Srgrimes	printheader();
582127499Sgad	if (nkept == 0)
58362803Swill		exit(1);
584127499Sgad
5851556Srgrimes	/*
5861556Srgrimes	 * sort proc list
5871556Srgrimes	 */
588127499Sgad	qsort(kinfo, nkept, sizeof(KINFO), pscomp);
5891556Srgrimes	/*
590127499Sgad	 * For each process, call each variable output function.
5911556Srgrimes	 */
592127499Sgad	for (i = lineno = 0; i < nkept; i++) {
593130999Sgad		STAILQ_FOREACH(vent, &varlist, next_ve) {
5941556Srgrimes			(vent->var->oproc)(&kinfo[i], vent);
595130999Sgad			if (STAILQ_NEXT(vent, next_ve) != NULL)
5961556Srgrimes				(void)putchar(' ');
5971556Srgrimes		}
5981556Srgrimes		(void)putchar('\n');
5991556Srgrimes		if (prtheader && lineno++ == prtheader - 4) {
6001556Srgrimes			(void)putchar('\n');
6011556Srgrimes			printheader();
6021556Srgrimes			lineno = 0;
6031556Srgrimes		}
6041556Srgrimes	}
605127499Sgad	free_list(&gidlist);
606127499Sgad	free_list(&pidlist);
607127499Sgad	free_list(&pgrplist);
608127499Sgad	free_list(&ruidlist);
609127499Sgad	free_list(&sesslist);
610127499Sgad	free_list(&ttylist);
611127499Sgad	free_list(&uidlist);
61266377Sbrian
6131556Srgrimes	exit(eval);
6141556Srgrimes}
6151556Srgrimes
616127499Sgadstatic int
617127499Sgadaddelem_gid(struct listinfo *inf, const char *elem)
618127499Sgad{
619127499Sgad	struct group *grp;
620127499Sgad	const char *nameorID;
621127499Sgad	char *endp;
622127602Sgad	u_long bigtemp;
623127499Sgad
624127499Sgad	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
625127499Sgad		if (*elem == '\0')
626127499Sgad			warnx("Invalid (zero-length) %s name", inf->lname);
627127499Sgad		else
628127499Sgad			warnx("%s name too long: %s", inf->lname, elem);
629127499Sgad		optfatal = 1;
630127542Sgad		return (0);		/* Do not add this value. */
631127499Sgad	}
632127499Sgad
633127499Sgad	/*
634127499Sgad	 * SUSv3 states that `ps -G grouplist' should match "real-group
635127499Sgad	 * ID numbers", and does not mention group-names.  I do want to
636127499Sgad	 * also support group-names, so this tries for a group-id first,
637127499Sgad	 * and only tries for a name if that doesn't work.  This is the
638127499Sgad	 * opposite order of what is done in addelem_uid(), but in
639127499Sgad	 * practice the order would only matter for group-names which
640127499Sgad	 * are all-numeric.
641127499Sgad	 */
642127499Sgad	grp = NULL;
643127499Sgad	nameorID = "named";
644127499Sgad	errno = 0;
645127602Sgad	bigtemp = strtoul(elem, &endp, 10);
646127602Sgad	if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) {
647127499Sgad		nameorID = "name or ID matches";
648127602Sgad		grp = getgrgid((gid_t)bigtemp);
649127499Sgad	}
650127499Sgad	if (grp == NULL)
651127499Sgad		grp = getgrnam(elem);
652127499Sgad	if (grp == NULL) {
653127499Sgad		warnx("No %s %s '%s'", inf->lname, nameorID, elem);
654127499Sgad		optfatal = 1;
655129967Sgad		return (0);
656127499Sgad	}
657127499Sgad	if (inf->count >= inf->maxcount)
658127499Sgad		expand_list(inf);
659127823Sgad	inf->l.gids[(inf->count)++] = grp->gr_gid;
660127499Sgad	return (1);
661127499Sgad}
662127499Sgad
663127597Sgad#define	BSD_PID_MAX	99999		/* Copy of PID_MAX from sys/proc.h. */
664127499Sgadstatic int
665127499Sgadaddelem_pid(struct listinfo *inf, const char *elem)
666127149Sgad{
667127539Sgad	char *endp;
668127149Sgad	long tempid;
669127149Sgad
670129917Sgad	if (*elem == '\0') {
671129917Sgad		warnx("Invalid (zero-length) process id");
672129917Sgad		optfatal = 1;
673129917Sgad		return (0);		/* Do not add this value. */
674127149Sgad	}
675127149Sgad
676129952Sgad	errno = 0;
677129952Sgad	tempid = strtol(elem, &endp, 10);
678129952Sgad	if (*endp != '\0' || tempid < 0 || elem == endp) {
679129952Sgad		warnx("Invalid %s: %s", inf->lname, elem);
680129952Sgad		errno = ERANGE;
681129952Sgad	} else if (errno != 0 || tempid > BSD_PID_MAX) {
682129952Sgad		warnx("%s too large: %s", inf->lname, elem);
683129952Sgad		errno = ERANGE;
684129952Sgad	}
685129952Sgad	if (errno == ERANGE) {
686129952Sgad		optfatal = 1;
687129967Sgad		return (0);
688129952Sgad	}
689127499Sgad	if (inf->count >= inf->maxcount)
690127499Sgad		expand_list(inf);
691127823Sgad	inf->l.pids[(inf->count)++] = tempid;
692127499Sgad	return (1);
693127499Sgad}
694127499Sgad#undef	BSD_PID_MAX
695127149Sgad
696131010Sgad/*-
697131010Sgad * The user can specify a device via one of three formats:
698131010Sgad *     1) fully qualified, e.g.:     /dev/ttyp0 /dev/console
699131010Sgad *     2) missing "/dev", e.g.:      ttyp0      console
700131010Sgad *     3) two-letters, e.g.:         p0         co
701131010Sgad *        (matching letters that would be seen in the "TT" column)
702131010Sgad */
703127499Sgadstatic int
704127499Sgadaddelem_tty(struct listinfo *inf, const char *elem)
705127499Sgad{
706127539Sgad	const char *ttypath;
707127539Sgad	struct stat sb;
708131209Sgad	char pathbuf[PATH_MAX], pathbuf2[PATH_MAX];
709127499Sgad
710131010Sgad	ttypath = NULL;
711131209Sgad	pathbuf2[0] = '\0';
712131010Sgad	switch (*elem) {
713131010Sgad	case '/':
714127499Sgad		ttypath = elem;
715131010Sgad		break;
716131010Sgad	case 'c':
717131010Sgad		if (strcmp(elem, "co") == 0) {
718131010Sgad			ttypath = _PATH_CONSOLE;
719131010Sgad			break;
720131010Sgad		}
721131010Sgad		/* FALLTHROUGH */
722131010Sgad	default:
723131010Sgad		strlcpy(pathbuf, _PATH_DEV, sizeof(pathbuf));
724131010Sgad		strlcat(pathbuf, elem, sizeof(pathbuf));
725131010Sgad		ttypath = pathbuf;
726131209Sgad		if (strncmp(pathbuf, _PATH_TTY, strlen(_PATH_TTY)) == 0)
727131010Sgad			break;
728131010Sgad		if (strcmp(pathbuf, _PATH_CONSOLE) == 0)
729131010Sgad			break;
730131209Sgad		/* Check to see if /dev/tty${elem} exists */
731131209Sgad		strlcpy(pathbuf2, _PATH_TTY, sizeof(pathbuf2));
732131209Sgad		strlcat(pathbuf2, elem, sizeof(pathbuf2));
733131209Sgad		if (stat(pathbuf2, &sb) == 0 && S_ISCHR(sb.st_mode)) {
734131010Sgad			/* No need to repeat stat() && S_ISCHR() checks */
735131010Sgad			ttypath = NULL;
736131010Sgad			break;
737131010Sgad		}
738131010Sgad		break;
739127499Sgad	}
740131010Sgad	if (ttypath) {
741131010Sgad		if (stat(ttypath, &sb) == -1) {
742131209Sgad			if (pathbuf2[0] != '\0')
743131209Sgad				warn("%s and %s", pathbuf2, ttypath);
744131209Sgad			else
745131209Sgad				warn("%s", ttypath);
746131010Sgad			optfatal = 1;
747131010Sgad			return (0);
748131010Sgad		}
749131010Sgad		if (!S_ISCHR(sb.st_mode)) {
750131209Sgad			if (pathbuf2[0] != '\0')
751131209Sgad				warnx("%s and %s: Not a terminal", pathbuf2,
752131209Sgad				    ttypath);
753131209Sgad			else
754131209Sgad				warnx("%s: Not a terminal", ttypath);
755131010Sgad			optfatal = 1;
756131010Sgad			return (0);
757131010Sgad		}
758127499Sgad	}
759127499Sgad	if (inf->count >= inf->maxcount)
760127499Sgad		expand_list(inf);
761127823Sgad	inf->l.ttys[(inf->count)++] = sb.st_rdev;
762127499Sgad	return (1);
763127149Sgad}
764127149Sgad
765127499Sgadstatic int
766127499Sgadaddelem_uid(struct listinfo *inf, const char *elem)
76766377Sbrian{
76866377Sbrian	struct passwd *pwd;
769127539Sgad	char *endp;
770127602Sgad	u_long bigtemp;
77166377Sbrian
772127499Sgad	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
773127499Sgad		if (*elem == '\0')
774127499Sgad			warnx("Invalid (zero-length) %s name", inf->lname);
775127499Sgad		else
776127499Sgad			warnx("%s name too long: %s", inf->lname, elem);
777127499Sgad		optfatal = 1;
778127542Sgad		return (0);		/* Do not add this value. */
779127499Sgad	}
78066377Sbrian
781127499Sgad	pwd = getpwnam(elem);
782127499Sgad	if (pwd == NULL) {
783127499Sgad		errno = 0;
784127602Sgad		bigtemp = strtoul(elem, &endp, 10);
785127602Sgad		if (errno != 0 || *endp != '\0' || bigtemp > UID_MAX)
786127499Sgad			warnx("No %s named '%s'", inf->lname, elem);
787127499Sgad		else {
788127499Sgad			/* The string is all digits, so it might be a userID. */
789127602Sgad			pwd = getpwuid((uid_t)bigtemp);
790127499Sgad			if (pwd == NULL)
791127499Sgad				warnx("No %s name or ID matches '%s'",
792127499Sgad				    inf->lname, elem);
79366377Sbrian		}
794127499Sgad	}
795127499Sgad	if (pwd == NULL) {
796127509Sgad		/*
797127509Sgad		 * These used to be treated as minor warnings (and the
798127509Sgad		 * option was simply ignored), but now they are fatal
799127509Sgad		 * errors (and the command will be aborted).
800127509Sgad		 */
801127509Sgad		optfatal = 1;
802129967Sgad		return (0);
803127499Sgad	}
804127499Sgad	if (inf->count >= inf->maxcount)
805127499Sgad		expand_list(inf);
806127823Sgad	inf->l.uids[(inf->count)++] = pwd->pw_uid;
807127499Sgad	return (1);
808127499Sgad}
809127499Sgad
810127499Sgadstatic void
811127499Sgadadd_list(struct listinfo *inf, const char *argp)
812127499Sgad{
813127499Sgad	const char *savep;
814127499Sgad	char *cp, *endp;
815127499Sgad	int toolong;
816127539Sgad	char elemcopy[PATH_MAX];
817127499Sgad
818129917Sgad	if (*argp == 0)
819129967Sgad		inf->addelem(inf, elemcopy);
820127499Sgad	while (*argp != '\0') {
821127499Sgad		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
822127499Sgad			argp++;
823127499Sgad		savep = argp;
824127499Sgad		toolong = 0;
825127499Sgad		cp = elemcopy;
826127499Sgad		if (strchr(T_SEP, *argp) == NULL) {
827127499Sgad			endp = elemcopy + sizeof(elemcopy) - 1;
828127499Sgad			while (*argp != '\0' && cp <= endp &&
829127499Sgad			    strchr(W_SEP T_SEP, *argp) == NULL)
830127499Sgad				*cp++ = *argp++;
831127499Sgad			if (cp > endp)
832127499Sgad				toolong = 1;
83366377Sbrian		}
834127499Sgad		if (!toolong) {
835127499Sgad			*cp = '\0';
836127542Sgad			/*
837129953Sgad			 * Add this single element to the given list.
838127542Sgad			 */
839127499Sgad			inf->addelem(inf, elemcopy);
840127499Sgad		} else {
841127499Sgad			/*
842127499Sgad			 * The string is too long to copy.  Find the end
843127499Sgad			 * of the string to print out the warning message.
844127499Sgad			 */
845127499Sgad			while (*argp != '\0' && strchr(W_SEP T_SEP,
846127499Sgad			    *argp) == NULL)
847127499Sgad				argp++;
848127499Sgad			warnx("Value too long: %.*s", (int)(argp - savep),
849127499Sgad			    savep);
850127499Sgad			optfatal = 1;
85166377Sbrian		}
852127499Sgad		/*
853127499Sgad		 * Skip over any number of trailing whitespace characters,
854127499Sgad		 * but only one (at most) trailing element-terminating
855127499Sgad		 * character.
856127499Sgad		 */
857127499Sgad		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
858127499Sgad			argp++;
859127499Sgad		if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) {
860127499Sgad			argp++;
861127499Sgad			/* Catch case where string ended with a comma. */
862127499Sgad			if (*argp == '\0')
863127499Sgad				inf->addelem(inf, argp);
864127499Sgad		}
86566377Sbrian	}
866127499Sgad}
86766377Sbrian
868127499Sgadstatic void *
869127499Sgadexpand_list(struct listinfo *inf)
870127499Sgad{
871127539Sgad	void *newlist;
872127499Sgad	int newmax;
87366377Sbrian
874127499Sgad	newmax = (inf->maxcount + 1) << 1;
875127823Sgad	newlist = realloc(inf->l.ptr, newmax * inf->elemsize);
876127499Sgad	if (newlist == NULL) {
877127823Sgad		free(inf->l.ptr);
878129967Sgad		errx(1, "realloc to %d %ss failed", newmax, inf->lname);
879127499Sgad	}
880127499Sgad	inf->maxcount = newmax;
881127823Sgad	inf->l.ptr = newlist;
882127499Sgad
883127499Sgad	return (newlist);
88466377Sbrian}
88566377Sbrian
886127499Sgadstatic void
887127499Sgadfree_list(struct listinfo *inf)
888127499Sgad{
889127499Sgad
890127499Sgad	inf->count = inf->elemsize = inf->maxcount = 0;
891127823Sgad	if (inf->l.ptr != NULL)
892127823Sgad		free(inf->l.ptr);
893127499Sgad	inf->addelem = NULL;
894127499Sgad	inf->lname = NULL;
895127823Sgad	inf->l.ptr = NULL;
896127499Sgad}
897127499Sgad
898127499Sgadstatic void
899127499Sgadinit_list(struct listinfo *inf, addelem_rtn artn, int elemsize,
900127499Sgad    const char *lname)
901127499Sgad{
902127499Sgad
903127499Sgad	inf->count = inf->maxcount = 0;
904127499Sgad	inf->elemsize = elemsize;
905127499Sgad	inf->addelem = artn;
906127499Sgad	inf->lname = lname;
907127823Sgad	inf->l.ptr = NULL;
908127499Sgad}
909127499Sgad
910109502SjmallettVARENT *
911109502Sjmallettfind_varentry(VAR *v)
912109502Sjmallett{
913109502Sjmallett	struct varent *vent;
914109502Sjmallett
915130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
916109502Sjmallett		if (strcmp(vent->var->name, v->name) == 0)
917109502Sjmallett			return vent;
918109502Sjmallett	}
919109502Sjmallett	return NULL;
920109502Sjmallett}
921109502Sjmallett
9221556Srgrimesstatic void
92390110Simpscanvars(void)
9241556Srgrimes{
9251556Srgrimes	struct varent *vent;
9261556Srgrimes	VAR *v;
92725271Sjkh
928130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
92925271Sjkh		v = vent->var;
93025271Sjkh		if (v->flag & DSIZ) {
93125271Sjkh			v->dwidth = v->width;
93225271Sjkh			v->width = 0;
93325271Sjkh		}
93425271Sjkh		if (v->flag & USER)
93525271Sjkh			needuser = 1;
93625271Sjkh		if (v->flag & COMM)
93725271Sjkh			needcomm = 1;
93825271Sjkh	}
93925271Sjkh}
94025271Sjkh
94125271Sjkhstatic void
94290110Simpdynsizevars(KINFO *ki)
94325271Sjkh{
94425271Sjkh	struct varent *vent;
94525271Sjkh	VAR *v;
9461556Srgrimes	int i;
9471556Srgrimes
948130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
9491556Srgrimes		v = vent->var;
95025271Sjkh		if (!(v->flag & DSIZ))
95125271Sjkh			continue;
95225271Sjkh		i = (v->sproc)( ki);
95325271Sjkh		if (v->width < i)
95425271Sjkh			v->width = i;
95525271Sjkh		if (v->width > v->dwidth)
95625271Sjkh			v->width = v->dwidth;
95725271Sjkh	}
95825271Sjkh}
95925271Sjkh
96025271Sjkhstatic void
96190110Simpsizevars(void)
96225271Sjkh{
96325271Sjkh	struct varent *vent;
96425271Sjkh	VAR *v;
96525271Sjkh	int i;
96625271Sjkh
967130999Sgad	STAILQ_FOREACH(vent, &varlist, next_ve) {
96825271Sjkh		v = vent->var;
969109504Sjmallett		i = strlen(vent->header);
9701556Srgrimes		if (v->width < i)
9711556Srgrimes			v->width = i;
9721556Srgrimes		totwidth += v->width + 1;	/* +1 for space */
9731556Srgrimes	}
9741556Srgrimes	totwidth--;
9751556Srgrimes}
9761556Srgrimes
97790143Smarkmstatic const char *
97890110Simpfmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki,
979127542Sgad    char *comm, int maxlen)
9801556Srgrimes{
98190143Smarkm	const char *s;
9821556Srgrimes
98390143Smarkm	s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen);
9841556Srgrimes	return (s);
9851556Srgrimes}
9861556Srgrimes
98771578Sjhb#define UREADOK(ki)	(forceuread || (ki->ki_p->ki_sflag & PS_INMEM))
98831552Sdyson
9891556Srgrimesstatic void
99090110Simpsaveuser(KINFO *ki)
9911556Srgrimes{
9921556Srgrimes
99371578Sjhb	if (ki->ki_p->ki_sflag & PS_INMEM) {
9941556Srgrimes		/*
9951556Srgrimes		 * The u-area might be swapped out, and we can't get
9961556Srgrimes		 * at it because we have a crashdump and no swap.
9971556Srgrimes		 * If it's here fill in these fields, otherwise, just
9981556Srgrimes		 * leave them 0.
9991556Srgrimes		 */
100069896Smckusick		ki->ki_valid = 1;
10011556Srgrimes	} else
100269896Smckusick		ki->ki_valid = 0;
10031556Srgrimes	/*
10041556Srgrimes	 * save arguments if needed
10051556Srgrimes	 */
1006130991Sgad	if (needcomm) {
1007130991Sgad		if (ki->ki_p->ki_stat == SZOMB)
1008130991Sgad			ki->ki_args = strdup("<defunct>");
1009130991Sgad		else if (UREADOK(ki) || (ki->ki_p->ki_args != NULL))
1010130991Sgad			ki->ki_args = strdup(fmt(kvm_getargv, ki,
1011130991Sgad			    ki->ki_p->ki_comm, MAXCOMLEN));
1012130991Sgad		else
1013130991Sgad			asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm);
1014131024Sgad		if (ki->ki_args == NULL)
1015130991Sgad			errx(1, "malloc failed");
101653276Speter	} else {
101753276Speter		ki->ki_args = NULL;
101853276Speter	}
1019130991Sgad	if (needenv) {
1020130991Sgad		if (UREADOK(ki))
1021130991Sgad			ki->ki_env = strdup(fmt(kvm_getenvv, ki,
1022130991Sgad			    (char *)NULL, 0));
1023130991Sgad		else
1024130991Sgad			ki->ki_env = strdup("()");
1025130991Sgad		if (ki->ki_env == NULL)
1026130991Sgad			errx(1, "malloc failed");
102753276Speter	} else {
102853276Speter		ki->ki_env = NULL;
102953276Speter	}
10301556Srgrimes}
10311556Srgrimes
1032130816Sgad/* A macro used to improve the readability of pscomp(). */
1033130816Sgad#define	DIFF_RETURN(a, b, field) do {	\
1034130816Sgad	if ((a)->field != (b)->field)	\
1035130816Sgad		return (((a)->field < (b)->field) ? -1 : 1); 	\
1036130816Sgad} while (0)
1037130816Sgad
10381556Srgrimesstatic int
103990110Simppscomp(const void *a, const void *b)
10401556Srgrimes{
1041127596Sgad	const KINFO *ka, *kb;
10421556Srgrimes
1043127596Sgad	ka = a;
1044127596Sgad	kb = b;
1045127596Sgad	/* SORTCPU and SORTMEM are sorted in descending order. */
1046130816Sgad	if (sortby == SORTCPU)
1047130816Sgad		DIFF_RETURN(kb, ka, ki_pcpu);
1048130816Sgad	if (sortby == SORTMEM)
1049130816Sgad		DIFF_RETURN(kb, ka, ki_memsize);
1050127596Sgad	/*
1051127596Sgad	 * TTY's are sorted in ascending order, except that all NODEV
1052127596Sgad	 * processes come before all processes with a device.
1053127596Sgad	 */
1054130816Sgad	if (ka->ki_p->ki_tdev != kb->ki_p->ki_tdev) {
1055130816Sgad		if (ka->ki_p->ki_tdev == NODEV)
1056130816Sgad			return (-1);
1057130816Sgad		if (kb->ki_p->ki_tdev == NODEV)
1058130816Sgad			return (1);
1059130816Sgad		DIFF_RETURN(ka, kb, ki_p->ki_tdev);
1060130816Sgad	}
1061130816Sgad
1062130972Sgad	/* PID's and TID's (threads) are sorted in ascending order. */
1063130816Sgad	DIFF_RETURN(ka, kb, ki_p->ki_pid);
1064130972Sgad	DIFF_RETURN(ka, kb, ki_p->ki_tid);
1065127596Sgad	return (0);
10661556Srgrimes}
1067130816Sgad#undef DIFF_RETURN
10681556Srgrimes
10691556Srgrimes/*
10701556Srgrimes * ICK (all for getopt), would rather hide the ugliness
10711556Srgrimes * here than taint the main code.
10721556Srgrimes *
10731556Srgrimes *  ps foo -> ps -foo
10741556Srgrimes *  ps 34 -> ps -p34
10751556Srgrimes *
10761556Srgrimes * The old convention that 't' with no trailing tty arg means the users
10771556Srgrimes * tty, is only supported if argv[1] doesn't begin with a '-'.  This same
10781556Srgrimes * feature is available with the option 'T', which takes no argument.
10791556Srgrimes */
10801556Srgrimesstatic char *
1081129915Sgadkludge_oldps_options(const char *optlist, char *origval, const char *nextarg)
10821556Srgrimes{
10831556Srgrimes	size_t len;
1084129914Sgad	char *argp, *cp, *newopts, *ns, *optp, *pidp;
10851556Srgrimes
1086102886Sjmallett	/*
1087129914Sgad	 * See if the original value includes any option which takes an
1088129914Sgad	 * argument (and will thus use up the remainder of the string).
1089102886Sjmallett	 */
1090129914Sgad	argp = NULL;
1091129914Sgad	if (optlist != NULL) {
1092129914Sgad		for (cp = origval; *cp != '\0'; cp++) {
1093129914Sgad			optp = strchr(optlist, *cp);
1094129914Sgad			if ((optp != NULL) && *(optp + 1) == ':') {
1095129914Sgad				argp = cp;
1096129914Sgad				break;
1097129914Sgad			}
1098129914Sgad		}
1099129914Sgad	}
1100129914Sgad	if (argp != NULL && *origval == '-')
1101129914Sgad		return (origval);
1102102886Sjmallett
11031556Srgrimes	/*
11041556Srgrimes	 * if last letter is a 't' flag with no argument (in the context
11051556Srgrimes	 * of the oldps options -- option string NOT starting with a '-' --
11061556Srgrimes	 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
110781743Sbrian	 *
1108129634Sgad	 * However, if a flag accepting a string argument is found earlier
1109129634Sgad	 * in the option string (including a possible `t' flag), then the
1110129634Sgad	 * remainder of the string must be the argument to that flag; so
1111129914Sgad	 * do not modify that argument.  Note that a trailing `t' would
1112129914Sgad	 * cause argp to be set, if argp was not already set by some
1113129914Sgad	 * earlier option.
11141556Srgrimes	 */
1115129914Sgad	len = strlen(origval);
1116129914Sgad	cp = origval + len - 1;
1117129914Sgad	pidp = NULL;
1118129915Sgad	if (*cp == 't' && *origval != '-' && cp == argp) {
1119129915Sgad		if (nextarg == NULL || *nextarg == '-' || isdigitch(*nextarg))
1120129915Sgad			*cp = 'T';
1121129915Sgad	} else if (argp == NULL) {
11221556Srgrimes		/*
1123129914Sgad		 * The original value did not include any option which takes
1124129914Sgad		 * an argument (and that would include `p' and `t'), so check
1125129914Sgad		 * the value for trailing number, or comma-separated list of
1126129914Sgad		 * numbers, which we will treat as a pid request.
11271556Srgrimes		 */
1128129914Sgad		if (isdigitch(*cp)) {
1129129914Sgad			while (cp >= origval && (*cp == ',' || isdigitch(*cp)))
1130129914Sgad				--cp;
1131129914Sgad			pidp = cp + 1;
1132129914Sgad		}
11331556Srgrimes	}
1134129914Sgad
11351556Srgrimes	/*
1136129914Sgad	 * If nothing needs to be added to the string, then return
1137129914Sgad	 * the "original" (although possibly modified) value.
11381556Srgrimes	 */
1139129914Sgad	if (*origval == '-' && pidp == NULL)
1140129914Sgad		return (origval);
1141129914Sgad
1142129914Sgad	/*
1143129914Sgad	 * Create a copy of the string to add '-' and/or 'p' to the
1144129914Sgad	 * original value.
1145129914Sgad	 */
1146129914Sgad	if ((newopts = ns = malloc(len + 3)) == NULL)
1147129914Sgad		errx(1, "malloc failed");
1148129914Sgad
1149129914Sgad	if (*origval != '-')
1150129914Sgad		*ns++ = '-';	/* add option flag */
1151129914Sgad
1152129914Sgad	if (pidp == NULL)
1153129914Sgad		strcpy(ns, origval);
1154129914Sgad	else {
1155129914Sgad		/*
1156129914Sgad		 * Copy everything before the pid string, add the `p',
1157129914Sgad		 * and then copy the pid string.
1158129914Sgad		 */
1159129914Sgad		len = pidp - origval;
1160129914Sgad		memcpy(ns, origval, len);
1161129914Sgad		ns += len;
11621556Srgrimes		*ns++ = 'p';
1163129914Sgad		strcpy(ns, pidp);
1164129914Sgad	}
11651556Srgrimes
11661556Srgrimes	return (newopts);
11671556Srgrimes}
11681556Srgrimes
11691556Srgrimesstatic void
117090110Simpusage(void)
11711556Srgrimes{
1172130973Sgad#define	SINGLE_OPTS	"[-aCc" OPT_LAZY_f "HhjlmrSTuvwXxZ]"
11731556Srgrimes
1174127499Sgad	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
1175127507Sgad	    "usage: ps " SINGLE_OPTS " [-G gid[,gid]] [-O|o fmt]",
1176127499Sgad	    "          [-p pid[,pid]] [-t tty[,tty]] [-U user[,user]]",
1177127499Sgad	    "          [-M core] [-N system]",
117826465Scharnier	    "       ps [-L]");
11791556Srgrimes	exit(1);
11801556Srgrimes}
1181