ps.c revision 127542
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1990, 1993, 1994
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * Redistribution and use in source and binary forms, with or without
61556Srgrimes * modification, are permitted provided that the following conditions
71556Srgrimes * are met:
81556Srgrimes * 1. Redistributions of source code must retain the above copyright
91556Srgrimes *    notice, this list of conditions and the following disclaimer.
101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111556Srgrimes *    notice, this list of conditions and the following disclaimer in the
121556Srgrimes *    documentation and/or other materials provided with the distribution.
131556Srgrimes * 3. All advertising materials mentioning features or use of this software
141556Srgrimes *    must display the following acknowledgement:
151556Srgrimes *	This product includes software developed by the University of
161556Srgrimes *	California, Berkeley and its contributors.
171556Srgrimes * 4. Neither the name of the University nor the names of its contributors
181556Srgrimes *    may be used to endorse or promote products derived from this software
191556Srgrimes *    without specific prior written permission.
201556Srgrimes *
211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311556Srgrimes * SUCH DAMAGE.
321556Srgrimes * ------+---------+---------+-------- + --------+---------+---------+---------*
331556Srgrimes * Copyright (c) 2004  - Garance Alistair Drosehn <gad@FreeBSD.org>.
341556Srgrimes * All rights reserved.
351556Srgrimes *
361556Srgrimes * Significant modifications made to bring `ps' options somewhat closer
371556Srgrimes * to the standard for `ps' as described in SingleUnixSpec-v3.
3836150Scharnier * ------+---------+---------+-------- + --------+---------+---------+---------*
3936150Scharnier */
4036150Scharnier
4136150Scharnier#ifndef lint
4250471Speterstatic const char copyright[] =
431556Srgrimes"@(#) Copyright (c) 1990, 1993, 1994\n\
441556Srgrimes	The Regents of the University of California.  All rights reserved.\n";
4517987Speter#endif /* not lint */
4617987Speter
471556Srgrimes#if 0
481556Srgrimes#ifndef lint
491556Srgrimesstatic char sccsid[] = "@(#)ps.c	8.4 (Berkeley) 4/2/94";
501556Srgrimes#endif /* not lint */
511556Srgrimes#endif
521556Srgrimes
531556Srgrimes#include <sys/cdefs.h>
541556Srgrimes__FBSDID("$FreeBSD: head/bin/ps/ps.c 127542 2004-03-29 01:15:27Z gad $");
551556Srgrimes
561556Srgrimes#include <sys/param.h>
571556Srgrimes#include <sys/user.h>
581556Srgrimes#include <sys/stat.h>
591556Srgrimes#include <sys/ioctl.h>
601556Srgrimes#include <sys/sysctl.h>
6117987Speter
6259436Scracauer#include <ctype.h>
6317987Speter#include <err.h>
641556Srgrimes#include <errno.h>
6517987Speter#include <fcntl.h>
661556Srgrimes#include <grp.h>
671556Srgrimes#include <kvm.h>
681556Srgrimes#include <limits.h>
691556Srgrimes#include <locale.h>
701556Srgrimes#include <paths.h>
711556Srgrimes#include <pwd.h>
721556Srgrimes#include <stdint.h>
731556Srgrimes#include <stdio.h>
7417987Speter#include <stdlib.h>
751556Srgrimes#include <string.h>
761556Srgrimes#include <unistd.h>
771556Srgrimes
781556Srgrimes#include "ps.h"
791556Srgrimes
801556Srgrimes#define	W_SEP	" \t"		/* "Whitespace" list separators */
811556Srgrimes#define	T_SEP	","		/* "Terminate-element" list separators */
821556Srgrimes
831556Srgrimes#ifdef LAZY_PS
841556Srgrimes#define	DEF_UREAD	0;
851556Srgrimes#define	OPT_LAZY_f	"f"
861556Srgrimes#else
871556Srgrimes#define	DEF_UREAD	1;	/* Always do the more-expensive read. */
881556Srgrimes#define	OPT_LAZY_f		/* I.e., the `-f' option is not added. */
891556Srgrimes#endif
901556Srgrimes
911556Srgrimesint	 cflag;			/* -c */
921556Srgrimesint	 eval;			/* Exit value */
931556Srgrimestime_t	 now;			/* Current time(3) value */
941556Srgrimesint	 rawcpu;		/* -C */
951556Srgrimesint	 sumrusage;		/* -S */
961556Srgrimesint	 termwidth;		/* Width of the screen (0 == infinity). */
971556Srgrimesint	 totwidth;		/* Calculated-width of requested variables. */
981556Srgrimes
991556Srgrimesstruct varent *vhead;
1001556Srgrimes
10118018Speterstatic int	 forceuread = DEF_UREAD; /* Do extra work to get u-area. */
10218018Speterstatic kvm_t	*kd;
1031556Srgrimesstatic KINFO	*kinfo;
1041556Srgrimesstatic int	 needcomm;	/* -o "command" */
1051556Srgrimesstatic int	 needenv;	/* -e */
1061556Srgrimesstatic int	 needuser;	/* -o "user" */
1071556Srgrimesstatic int	 optfatal;	/* Fatal error parsing some list-option. */
1081556Srgrimes
1091556Srgrimesstatic enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
1101556Srgrimes
11190111Simpstruct listinfo;
11290111Simptypedef	int	addelem_rtn(struct listinfo *_inf, const char *_elem);
11390111Simp
11490111Simpstruct listinfo {
11590111Simp	int		 count;
11690111Simp	int		 maxcount;
11790111Simp	int		 elemsize;
11890111Simp	addelem_rtn	*addelem;
11990111Simp	const char	*lname;
12090111Simp	union {
12190111Simp		gid_t	*gids;
12290111Simp		pid_t	*pids;
12390111Simp		dev_t	*ttys;
12490111Simp		uid_t	*uids;
12590111Simp		void	*ptr;
12690111Simp	};
1271556Srgrimes};
12817987Speter
1291556Srgrimesstatic int	 addelem_gid(struct listinfo *, const char *);
1301556Srgrimesstatic int	 addelem_pid(struct listinfo *, const char *);
1311556Srgrimesstatic int	 addelem_tty(struct listinfo *, const char *);
1321556Srgrimesstatic int	 addelem_uid(struct listinfo *, const char *);
1331556Srgrimesstatic void	 add_list(struct listinfo *, const char *);
1341556Srgrimesstatic void	 dynsizevars(KINFO *);
13590111Simpstatic void	*expand_list(struct listinfo *);
13617987Speterstatic const char *fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int),
1371556Srgrimes		    KINFO *, char *, int);
1381556Srgrimesstatic void	 free_list(struct listinfo *);
13960593Scracauerstatic void	 init_list(struct listinfo *, addelem_rtn, int, const char *);
1401556Srgrimesstatic char	*kludge_oldps_options(char *);
1411556Srgrimesstatic int	 pscomp(const void *, const void *);
1421556Srgrimesstatic void	 saveuser(KINFO *);
1431556Srgrimesstatic void	 scanvars(void);
1441556Srgrimesstatic void	 sizevars(void);
1451556Srgrimesstatic void	 usage(void);
1461556Srgrimes
1471556Srgrimesstatic char dfmt[] = "pid,tt,state,time,command";
1481556Srgrimesstatic char jfmt[] = "user,pid,ppid,pgid,jobc,state,tt,time,command";
1491556Srgrimesstatic char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,"
1501556Srgrimes			"tt,time,command";
1511556Srgrimesstatic char   o1[] = "pid";
1521556Srgrimesstatic char   o2[] = "tt,state,time,command";
1531556Srgrimesstatic char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
1541556Srgrimesstatic char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,"
1551556Srgrimes			"%cpu,%mem,command";
1561556Srgrimesstatic char Zfmt[] = "label";
15790111Simp
15817987Speter#define	PS_ARGS	"AaCc" OPT_LAZY_f "G:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ"
1591556Srgrimes
16017987Speterint
1611556Srgrimesmain(int argc, char *argv[])
1621556Srgrimes{
1631556Srgrimes	struct listinfo gidlist, pgrplist, pidlist;
1641556Srgrimes	struct listinfo ruidlist, sesslist, ttylist, uidlist;
16517987Speter	struct kinfo_proc *kp;
1661556Srgrimes	struct varent *vent;
16717987Speter	struct winsize ws;
16817987Speter	const char *cp, *nlistf, *memf;
16917987Speter	char *cols;
17017987Speter	int all, ch, dropgid, elem, flag, _fmt, i, lineno;
17117987Speter	int nentries, nocludge, nkept, nselectors;
17217987Speter	int prtheader, showthreads, wflag, what, xkeep, xkeep_implied;
17317987Speter	char errbuf[_POSIX2_LINE_MAX];
17417987Speter
17517987Speter	(void) setlocale(LC_ALL, "");
17617987Speter	time(&now);			/* Used by routines in print.c. */
17717987Speter
17817987Speter	if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0')
17917987Speter		termwidth = atoi(cols);
18017987Speter	else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
18117987Speter	     ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
18217987Speter	     ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) == -1) ||
18317987Speter	     ws.ws_col == 0)
18417987Speter		termwidth = 79;
18517987Speter	else
18617987Speter		termwidth = ws.ws_col - 1;
18717987Speter
18817987Speter	/*
18917987Speter	 * Don't apply a kludge if the first argument is an option taking an
19017987Speter	 * argument
19117987Speter	 */
19217987Speter	if (argc > 1) {
19313882Sjoerg		nocludge = 0;
19417987Speter		if (argv[1][0] == '-') {
19517987Speter			for (cp = PS_ARGS; *cp != '\0'; cp++) {
19617987Speter				if (*cp != ':')
1971556Srgrimes					continue;
19817987Speter				if (*(cp - 1) == argv[1][1]) {
19917987Speter					nocludge = 1;
20017987Speter					break;
20117987Speter				}
20217987Speter			}
20317987Speter		}
20417987Speter		if (nocludge == 0)
2051556Srgrimes			argv[1] = kludge_oldps_options(argv[1]);
2061556Srgrimes	}
2071556Srgrimes
2081556Srgrimes	all = dropgid = _fmt = nselectors = optfatal = 0;
2091556Srgrimes	prtheader = showthreads = wflag = xkeep_implied = 0;
2101556Srgrimes	xkeep = -1;			/* Neither -x nor -X. */
2111556Srgrimes	init_list(&gidlist, addelem_gid, sizeof(gid_t), "group");
2121556Srgrimes	init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group");
2131556Srgrimes	init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id");
2141556Srgrimes	init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser");
2151556Srgrimes	init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id");
2161556Srgrimes	init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty");
2171556Srgrimes	init_list(&uidlist, addelem_uid, sizeof(uid_t), "user");
2181556Srgrimes	memf = nlistf = _PATH_DEVNULL;
2191556Srgrimes	while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
2201556Srgrimes		switch((char)ch) {
2211556Srgrimes		case 'A':
2221556Srgrimes			/*
2231556Srgrimes			 * Exactly the same as `-ax'.   This has been
2241556Srgrimes			 * added for compatability with SUSv3, but for
2251556Srgrimes			 * now it will not be described in the man page.
2261556Srgrimes			 */
22790111Simp			nselectors++;
22890111Simp			all = xkeep = 1;
2291556Srgrimes			break;
2301556Srgrimes		case 'a':
2311556Srgrimes			nselectors++;
2321556Srgrimes			all = 1;
2331556Srgrimes			break;
2341556Srgrimes		case 'C':
2351556Srgrimes			rawcpu = 1;
2361556Srgrimes			break;
2371556Srgrimes		case 'c':
2381556Srgrimes			cflag = 1;
2391556Srgrimes			break;
2401556Srgrimes		case 'e':			/* XXX set ufmt */
2411556Srgrimes			needenv = 1;
2421556Srgrimes			break;
2431556Srgrimes#ifdef LAZY_PS
2441556Srgrimes		case 'f':
2451556Srgrimes			if (getuid() == 0 || getgid() == 0)
2461556Srgrimes				forceuread = 1;
2471556Srgrimes			break;
2481556Srgrimes#endif
2491556Srgrimes		case 'G':
2501556Srgrimes			add_list(&gidlist, optarg);
2511556Srgrimes			xkeep_implied = 1;
2521556Srgrimes			nselectors++;
2531556Srgrimes			break;
25490111Simp		case 'g':
25590111Simp#if 0
25675336Sbrian			/*
2571556Srgrimes			 * XXX - This SUSv3 behavior is still under debate
25875336Sbrian			 *	since it conflicts with the (undocumented)
2591556Srgrimes			 *	`-g' option.  So we skip it for now.
26075336Sbrian			 */
2611556Srgrimes			add_list(&pgrplist, optarg);
26275336Sbrian			xkeep_implied = 1;
26375336Sbrian			nselectors++;
26475336Sbrian			break;
2651556Srgrimes#else
2661556Srgrimes			/* The historical BSD-ish (from SunOS) behavior. */
2671556Srgrimes			break;			/* no-op */
2681556Srgrimes#endif
2691556Srgrimes		case 'H':
2701556Srgrimes			showthreads = KERN_PROC_INC_THREAD;
2711556Srgrimes			break;
2721556Srgrimes		case 'h':
2731556Srgrimes			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
2741556Srgrimes			break;
2751556Srgrimes		case 'j':
2761556Srgrimes			parsefmt(jfmt, 0);
2771556Srgrimes			_fmt = 1;
2781556Srgrimes			jfmt[0] = '\0';
2791556Srgrimes			break;
2801556Srgrimes		case 'L':
2811556Srgrimes			showkey();
2821556Srgrimes			exit(0);
28375336Sbrian		case 'l':
28475336Sbrian			parsefmt(lfmt, 0);
28575336Sbrian			_fmt = 1;
28675336Sbrian			lfmt[0] = '\0';
28775336Sbrian			break;
28875336Sbrian		case 'M':
28975336Sbrian			memf = optarg;
2901556Srgrimes			dropgid = 1;
2911556Srgrimes			break;
2921556Srgrimes		case 'm':
2931556Srgrimes			sortby = SORTMEM;
2941556Srgrimes			break;
29590111Simp		case 'N':
29690111Simp			nlistf = optarg;
2971556Srgrimes			dropgid = 1;
2981556Srgrimes			break;
2991556Srgrimes		case 'O':
3001556Srgrimes			parsefmt(o1, 1);
30175160Sbrian			parsefmt(optarg, 1);
3021556Srgrimes			parsefmt(o2, 1);
3031556Srgrimes			o1[0] = o2[0] = '\0';
30417987Speter			_fmt = 1;
30517987Speter			break;
3061556Srgrimes		case 'o':
30720425Ssteve			parsefmt(optarg, 1);
3081556Srgrimes			_fmt = 1;
3091556Srgrimes			break;
3101556Srgrimes		case 'p':
3111556Srgrimes			add_list(&pidlist, optarg);
3121556Srgrimes			/*
3131556Srgrimes			 * Note: `-p' does not *set* xkeep, but any values
3141556Srgrimes			 * from pidlist are checked before xkeep is.  That
3151556Srgrimes			 * way they are always matched, even if the user
31675160Sbrian			 * specifies `-X'.
31775160Sbrian			 */
31875160Sbrian			nselectors++;
31975160Sbrian			break;
32075160Sbrian#if 0
32175160Sbrian		case 'R':
3221556Srgrimes			/*
3231556Srgrimes			 * XXX - This un-standard option is still under
3241556Srgrimes			 *	debate.  This is what SUSv3 defines as
3251556Srgrimes			 *	the `-U' option, and while it would be
3261556Srgrimes			 *	nice to have, it could cause even more
3271556Srgrimes			 *	confusion to implement it as `-R'.
3281556Srgrimes			 */
3291556Srgrimes			add_list(&ruidlist, optarg);
3301556Srgrimes			xkeep_implied = 1;
3311556Srgrimes			nselectors++;
3321556Srgrimes			break;
3331556Srgrimes#endif
3341556Srgrimes		case 'r':
3351556Srgrimes			sortby = SORTCPU;
3361556Srgrimes			break;
3371556Srgrimes		case 'S':
3381556Srgrimes			sumrusage = 1;
3391556Srgrimes			break;
3401556Srgrimes#if 0
3411556Srgrimes		case 's':
3421556Srgrimes			/*
3431556Srgrimes			 * XXX - This non-standard option is still under
3441556Srgrimes			 *	debate.  This *is* supported on Solaris,
3451556Srgrimes			 *	Linux, and IRIX, but conflicts with `-s'
3461556Srgrimes			 *	on NetBSD and maybe some older BSD's.
3471556Srgrimes			 */
3481556Srgrimes			add_list(&sesslist, optarg);
3491556Srgrimes			xkeep_implied = 1;
3501556Srgrimes			nselectors++;
3511556Srgrimes			break;
3521556Srgrimes#endif
3531556Srgrimes		case 'T':
3541556Srgrimes			if ((optarg = ttyname(STDIN_FILENO)) == NULL)
3551556Srgrimes				errx(1, "stdin: not a terminal");
3561556Srgrimes			/* FALLTHROUGH */
3571556Srgrimes		case 't':
3581556Srgrimes			add_list(&ttylist, optarg);
3591556Srgrimes			xkeep_implied = 1;
3601556Srgrimes			nselectors++;
3611556Srgrimes			break;
3621556Srgrimes		case 'U':
3631556Srgrimes			/* This is what SUSv3 defines as the `-u' option. */
3641556Srgrimes			add_list(&uidlist, optarg);
3651556Srgrimes			xkeep_implied = 1;
3661556Srgrimes			nselectors++;
3671556Srgrimes			break;
3681556Srgrimes		case 'u':
3691556Srgrimes			parsefmt(ufmt, 0);
3701556Srgrimes			sortby = SORTCPU;
3711556Srgrimes			_fmt = 1;
3721556Srgrimes			ufmt[0] = '\0';
3731556Srgrimes			break;
3741556Srgrimes		case 'v':
3751556Srgrimes			parsefmt(vfmt, 0);
3761556Srgrimes			sortby = SORTMEM;
3771556Srgrimes			_fmt = 1;
3781556Srgrimes			vfmt[0] = '\0';
3791556Srgrimes			break;
3801556Srgrimes		case 'w':
3811556Srgrimes			if (wflag)
3821556Srgrimes				termwidth = UNLIMITED;
3831556Srgrimes			else if (termwidth < 131)
3841556Srgrimes				termwidth = 131;
3851556Srgrimes			wflag++;
3861556Srgrimes			break;
3871556Srgrimes		case 'X':
3881556Srgrimes			/*
3891556Srgrimes			 * Note that `-X' and `-x' are not standard "selector"
3901556Srgrimes			 * options. For most selector-options, we check *all*
3911556Srgrimes			 * processes to see if any are matched by the given
3921556Srgrimes			 * value(s).  After we have a set of all the matched
3931556Srgrimes			 * processes, then `-X' and `-x' govern whether we
3941556Srgrimes			 * modify that *matched* set for processes which do
3951556Srgrimes			 * not have a controlling terminal.  `-X' causes
3961556Srgrimes			 * those processes to be deleted from the matched
3971556Srgrimes			 * set, while `-x' causes them to be kept.
3981556Srgrimes			 */
3991556Srgrimes			xkeep = 0;
4001556Srgrimes			break;
4011556Srgrimes		case 'x':
4021556Srgrimes			xkeep = 1;
4031556Srgrimes			break;
4041556Srgrimes		case 'Z':
4051556Srgrimes			parsefmt(Zfmt, 0);
4061556Srgrimes			Zfmt[0] = '\0';
4071556Srgrimes			break;
4081556Srgrimes		case '?':
4091556Srgrimes		default:
4101556Srgrimes			usage();
4111556Srgrimes		}
4121556Srgrimes	argc -= optind;
4131556Srgrimes	argv += optind;
4141556Srgrimes	if (optfatal)
4151556Srgrimes		exit(1);		/* Error messages already printed. */
4161556Srgrimes	if (xkeep < 0)			/* Neither -X nor -x was specified. */
4171556Srgrimes		xkeep = xkeep_implied;
4181556Srgrimes
4191556Srgrimes#define	BACKWARD_COMPATIBILITY
4201556Srgrimes#ifdef	BACKWARD_COMPATIBILITY
4211556Srgrimes	if (*argv) {
4221556Srgrimes		nlistf = *argv;
4231556Srgrimes		if (*++argv)
4241556Srgrimes			memf = *argv;
4251556Srgrimes	}
4261556Srgrimes#endif
4271556Srgrimes	/*
4281556Srgrimes	 * Discard setgid privileges if not the running kernel so that bad
4291556Srgrimes	 * guys can't print interesting stuff from kernel memory.
43018018Speter	 */
4312760Ssef	if (dropgid) {
4322760Ssef		setgid(getgid());
4331556Srgrimes		setuid(getuid());
4341556Srgrimes	}
4351556Srgrimes
4361556Srgrimes	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
4371556Srgrimes	if (kd == 0)
4381556Srgrimes		errx(1, "%s", errbuf);
4391556Srgrimes
4401556Srgrimes	if (!_fmt)
4412760Ssef		parsefmt(dfmt, 0);
4421556Srgrimes
4431556Srgrimes	if (nselectors == 0) {
4442760Ssef		uidlist.ptr = malloc(sizeof(uid_t));
4451556Srgrimes		if (uidlist.ptr == NULL)
4461556Srgrimes			errx(1, "malloc failed");
4471556Srgrimes		nselectors = 1;
44818018Speter		uidlist.count = uidlist.maxcount = 1;
4491556Srgrimes		*uidlist.uids = getuid();
4502760Ssef	}
4512760Ssef
4522760Ssef	/*
4532760Ssef	 * scan requested variables, noting what structures are needed,
45418018Speter	 * and adjusting header widths as appropriate.
4552760Ssef	 */
4562760Ssef	scanvars();
4572760Ssef
4581556Srgrimes	/*
4592760Ssef	 * Get process list.  If the user requested just one selector-
46018018Speter	 * option, then kvm_getprocs can be asked to return just those
4611556Srgrimes	 * processes.  Otherwise, have it return all processes, and
4621556Srgrimes	 * then this routine will search that full list and select the
4631556Srgrimes	 * processes which match any of the user's selector-options.
4641556Srgrimes	 */
4651556Srgrimes	what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
4661556Srgrimes	flag = 0;
4671556Srgrimes	if (nselectors == 1) {
4681556Srgrimes		/* XXX - Apparently there's no KERN_PROC_GID flag. */
4691556Srgrimes		if (pgrplist.count == 1) {
4701556Srgrimes			what = KERN_PROC_PGRP | showthreads;
4711556Srgrimes			flag = *pgrplist.pids;
4721556Srgrimes			nselectors = 0;
4731556Srgrimes		} else if (pidlist.count == 1) {
4741556Srgrimes			what = KERN_PROC_PID | showthreads;
4751556Srgrimes			flag = *pidlist.pids;
4761556Srgrimes			nselectors = 0;
4771556Srgrimes		} else if (ruidlist.count == 1) {
4781556Srgrimes			what = KERN_PROC_RUID | showthreads;
4791556Srgrimes			flag = *ruidlist.uids;
48017987Speter			nselectors = 0;
48117987Speter#if 0
48217987Speter		/*
48317987Speter		 * XXX - KERN_PROC_SESSION causes error in kvm_getprocs?
48417987Speter		 *	For now, always do sid-matching in this routine.
48517987Speter		 */
48617987Speter		} else if (sesslist.count == 1) {
48720425Ssteve			what = KERN_PROC_SESSION | showthreads;
48820425Ssteve			flag = *sesslist.pids;
4891556Srgrimes			nselectors = 0;
49010399Sjoerg#endif
4911556Srgrimes		} else if (ttylist.count == 1) {
49217987Speter			what = KERN_PROC_TTY | showthreads;
4931556Srgrimes			flag = *ttylist.ttys;
49475160Sbrian			nselectors = 0;
49575160Sbrian		} else if (uidlist.count == 1) {
4961556Srgrimes			what = KERN_PROC_UID | showthreads;
4971556Srgrimes			flag = *uidlist.uids;
4981556Srgrimes			nselectors = 0;
4991556Srgrimes		} else if (all) {
5001556Srgrimes			/* No need for this routine to select processes. */
5011556Srgrimes			nselectors = 0;
5021556Srgrimes		}
5031556Srgrimes	}
5041556Srgrimes
5051556Srgrimes	/*
5061556Srgrimes	 * select procs
5071556Srgrimes	 */
5081556Srgrimes	nentries = -1;
5091556Srgrimes	kp = kvm_getprocs(kd, what, flag, &nentries);
5101556Srgrimes	if ((kp == 0 && nentries > 0) || (kp != 0 && nentries < 0))
5111556Srgrimes		errx(1, "%s", kvm_geterr(kd));
5121556Srgrimes	nkept = 0;
5131556Srgrimes	if (nentries > 0) {
5141556Srgrimes		if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
5151556Srgrimes			errx(1, "malloc failed");
5161556Srgrimes		for (i = nentries; --i >= 0; ++kp) {
51775160Sbrian			/*
51875160Sbrian			 * If the user specified multiple selection-criteria,
51975160Sbrian			 * then keep any process matched by the inclusive OR
52075160Sbrian			 * of all the selection-criteria given.
52175160Sbrian			 */
52275160Sbrian			if (pidlist.count > 0) {
52375160Sbrian				for (elem = 0; elem < pidlist.count; elem++)
52475160Sbrian					if (kp->ki_pid == pidlist.pids[elem])
52575160Sbrian						goto keepit;
52675160Sbrian			}
5271556Srgrimes			/*
5281556Srgrimes			 * Note that we had to process pidlist before
5291556Srgrimes			 * filtering out processes which do not have
5301556Srgrimes			 * a controlling terminal.
53190111Simp			 */
53290111Simp			if (xkeep == 0) {
5331556Srgrimes				if ((kp->ki_tdev == NODEV ||
5341556Srgrimes				    (kp->ki_flag & P_CONTROLT) == 0))
53575160Sbrian					continue;
53675160Sbrian			}
5371556Srgrimes			if (nselectors == 0)
5381556Srgrimes				goto keepit;
5391556Srgrimes			if (gidlist.count > 0) {
5401556Srgrimes				for (elem = 0; elem < gidlist.count; elem++)
5411556Srgrimes					if (kp->ki_rgid == gidlist.gids[elem])
5421556Srgrimes						goto keepit;
5431556Srgrimes			}
5441556Srgrimes			if (pgrplist.count > 0) {
5458855Srgrimes				for (elem = 0; elem < pgrplist.count; elem++)
5461556Srgrimes					if (kp->ki_pgid == pgrplist.pids[elem])
5471556Srgrimes						goto keepit;
5488855Srgrimes			}
5491556Srgrimes			if (ruidlist.count > 0) {
5501556Srgrimes				for (elem = 0; elem < ruidlist.count; elem++)
5511556Srgrimes					if (kp->ki_ruid == ruidlist.uids[elem])
55275160Sbrian						goto keepit;
55375160Sbrian			}
55475160Sbrian			if (sesslist.count > 0) {
55575160Sbrian				for (elem = 0; elem < sesslist.count; elem++)
55675160Sbrian					if (kp->ki_sid == sesslist.pids[elem])
55775160Sbrian						goto keepit;
5581556Srgrimes			}
5591556Srgrimes			if (ttylist.count > 0) {
5601556Srgrimes				for (elem = 0; elem < ttylist.count; elem++)
5611556Srgrimes					if (kp->ki_tdev == ttylist.ttys[elem])
5621556Srgrimes						goto keepit;
5631556Srgrimes			}
5641556Srgrimes			if (uidlist.count > 0) {
5651556Srgrimes				for (elem = 0; elem < uidlist.count; elem++)
5661556Srgrimes					if (kp->ki_uid == uidlist.uids[elem])
5671556Srgrimes						goto keepit;
5681556Srgrimes			}
5691556Srgrimes			/*
5701556Srgrimes			 * This process did not match any of the user's
5711556Srgrimes			 * selector-options, so skip the process.
5721556Srgrimes			 */
5731556Srgrimes			continue;
5741556Srgrimes
5751556Srgrimes		keepit:
5761556Srgrimes			kinfo[nkept].ki_p = kp;
5771556Srgrimes			if (needuser)
5781556Srgrimes				saveuser(&kinfo[nkept]);
5791556Srgrimes			dynsizevars(&kinfo[nkept]);
5801556Srgrimes			nkept++;
58175160Sbrian		}
5821556Srgrimes	}
5831556Srgrimes
5841556Srgrimes	sizevars();
5851556Srgrimes
5861556Srgrimes	/*
5871556Srgrimes	 * print header
5881556Srgrimes	 */
5891556Srgrimes	printheader();
5901556Srgrimes	if (nkept == 0)
5911556Srgrimes		exit(1);
5921556Srgrimes
5931556Srgrimes	/*
59475160Sbrian	 * sort proc list
59575160Sbrian	 */
59675160Sbrian	qsort(kinfo, nkept, sizeof(KINFO), pscomp);
59775160Sbrian	/*
59875160Sbrian	 * For each process, call each variable output function.
59975160Sbrian	 */
60075160Sbrian	for (i = lineno = 0; i < nkept; i++) {
60175160Sbrian		for (vent = vhead; vent; vent = vent->next) {
60275160Sbrian			(vent->var->oproc)(&kinfo[i], vent);
60375160Sbrian			if (vent->next != NULL)
6041556Srgrimes				(void)putchar(' ');
6051556Srgrimes		}
60617987Speter		(void)putchar('\n');
60790111Simp		if (prtheader && lineno++ == prtheader - 4) {
60890111Simp			(void)putchar('\n');
60917987Speter			printheader();
6101556Srgrimes			lineno = 0;
61117987Speter		}
61217987Speter	}
61317987Speter	free_list(&gidlist);
61417987Speter	free_list(&pidlist);
61517987Speter	free_list(&pgrplist);
61617987Speter	free_list(&ruidlist);
61717987Speter	free_list(&sesslist);
61817987Speter	free_list(&ttylist);
61990111Simp	free_list(&uidlist);
62090111Simp
62117987Speter	exit(eval);
62217987Speter}
62317987Speter
62417987Speterstatic int
62517987Speteraddelem_gid(struct listinfo *inf, const char *elem)
62617987Speter{
62717987Speter	struct group *grp;
62817987Speter	const char *nameorID;
62917987Speter	char *endp;
63020425Ssteve	intmax_t ltemp;
63117987Speter
63217987Speter	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
63317987Speter		if (*elem == '\0')
63417987Speter			warnx("Invalid (zero-length) %s name", inf->lname);
63517987Speter		else
63617987Speter			warnx("%s name too long: %s", inf->lname, elem);
63717987Speter		optfatal = 1;
63817987Speter		return (0);		/* Do not add this value. */
6391556Srgrimes	}
64090111Simp
64190111Simp	/*
6421556Srgrimes	 * SUSv3 states that `ps -G grouplist' should match "real-group
6431556Srgrimes	 * ID numbers", and does not mention group-names.  I do want to
6441556Srgrimes	 * also support group-names, so this tries for a group-id first,
6451556Srgrimes	 * and only tries for a name if that doesn't work.  This is the
6461556Srgrimes	 * opposite order of what is done in addelem_uid(), but in
6471556Srgrimes	 * practice the order would only matter for group-names which
6481556Srgrimes	 * are all-numeric.
6491556Srgrimes	 */
6501556Srgrimes	grp = NULL;
6511556Srgrimes	nameorID = "named";
6521556Srgrimes	errno = 0;
6531556Srgrimes	ltemp = strtol(elem, &endp, 10);
6541556Srgrimes	if (errno == 0 && *endp == '\0' && ltemp >= 0 && ltemp <= GID_MAX) {
6551556Srgrimes		nameorID = "name or ID matches";
6561556Srgrimes		grp = getgrgid((gid_t)ltemp);
6571556Srgrimes	}
6581556Srgrimes	if (grp == NULL)
6591556Srgrimes		grp = getgrnam(elem);
6601556Srgrimes	if (grp == NULL) {
6611556Srgrimes		warnx("No %s %s '%s'", inf->lname, nameorID, elem);
6621556Srgrimes		optfatal = 1;
6631556Srgrimes		return (0);		/* Do not add this value. */
6641556Srgrimes	}
6651556Srgrimes
6661556Srgrimes	if (inf->count >= inf->maxcount)
6671556Srgrimes		expand_list(inf);
6681556Srgrimes	inf->gids[(inf->count)++] = grp->gr_gid;
6691556Srgrimes	return (1);
67017987Speter}
6711556Srgrimes
67217987Speter#define	BSD_PID_MAX	99999	/* Copy of PID_MAX from sys/proc.h. */
6731556Srgrimesstatic int
6741556Srgrimesaddelem_pid(struct listinfo *inf, const char *elem)
6751556Srgrimes{
6761556Srgrimes	char *endp;
6771556Srgrimes	long tempid;
6781556Srgrimes
6791556Srgrimes	if (*elem == '\0')
6801556Srgrimes		tempid = 0L;
6811556Srgrimes	else {
68290111Simp		errno = 0;
68390111Simp		tempid = strtol(elem, &endp, 10);
6841556Srgrimes		if (*endp != '\0' || tempid < 0 || elem == endp) {
6851556Srgrimes			warnx("Invalid %s: %s", inf->lname, elem);
6861556Srgrimes			errno = ERANGE;
6871556Srgrimes		} else if (errno != 0 || tempid > BSD_PID_MAX) {
6881556Srgrimes			warnx("%s too large: %s", inf->lname, elem);
6891556Srgrimes			errno = ERANGE;
6901556Srgrimes		}
6911556Srgrimes		if (errno == ERANGE) {
6921556Srgrimes			optfatal = 1;
6931556Srgrimes			return (0);	/* Do not add this value. */
6941556Srgrimes		}
6951556Srgrimes	}
6961556Srgrimes
6971556Srgrimes	if (inf->count >= inf->maxcount)
6981556Srgrimes		expand_list(inf);
6991556Srgrimes	inf->pids[(inf->count)++] = tempid;
7001556Srgrimes	return (1);
7011556Srgrimes}
7021556Srgrimes#undef	BSD_PID_MAX
7031556Srgrimes
7041556Srgrimesstatic int
7051556Srgrimesaddelem_tty(struct listinfo *inf, const char *elem)
70690111Simp{
70790111Simp	const char *ttypath;
7081556Srgrimes	struct stat sb;
7091556Srgrimes	char pathbuf[PATH_MAX];
7101556Srgrimes
7111556Srgrimes	if (strcmp(elem, "co") == 0)
7121556Srgrimes		ttypath = strdup(_PATH_CONSOLE);
7131556Srgrimes	else if (*elem == '/')
7141556Srgrimes		ttypath = elem;
7151556Srgrimes	else {
71690111Simp		strlcpy(pathbuf, _PATH_TTY, sizeof(pathbuf));
71790111Simp		strlcat(pathbuf, elem, sizeof(pathbuf));
7181556Srgrimes		ttypath = pathbuf;
7191556Srgrimes	}
7201556Srgrimes
7211556Srgrimes	if (stat(ttypath, &sb) == -1) {
7221556Srgrimes		warn("%s", ttypath);
7231556Srgrimes		optfatal = 1;
7248855Srgrimes		return (0);		/* Do not add this value. */
7251556Srgrimes	}
7261556Srgrimes	if (!S_ISCHR(sb.st_mode)) {
7271556Srgrimes		warn("%s: Not a terminal", ttypath);
7281556Srgrimes		optfatal = 1;
7291556Srgrimes		return (0);		/* Do not add this value. */
7301556Srgrimes	}
7311556Srgrimes
7321556Srgrimes	if (inf->count >= inf->maxcount)
7331556Srgrimes		expand_list(inf);
7341556Srgrimes	inf->ttys[(inf->count)++] = sb.st_rdev;
7351556Srgrimes	return (1);
7361556Srgrimes}
7371556Srgrimes
7381556Srgrimesstatic int
7391556Srgrimesaddelem_uid(struct listinfo *inf, const char *elem)
7401556Srgrimes{
7411556Srgrimes	struct passwd *pwd;
7421556Srgrimes	char *endp;
74320425Ssteve	intmax_t ltemp;
74417987Speter
74525230Ssteve	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
7461556Srgrimes		if (*elem == '\0')
7471556Srgrimes			warnx("Invalid (zero-length) %s name", inf->lname);
74820425Ssteve		else
74917987Speter			warnx("%s name too long: %s", inf->lname, elem);
7501556Srgrimes		optfatal = 1;
7511556Srgrimes		return (0);		/* Do not add this value. */
7521556Srgrimes	}
7531556Srgrimes
7541556Srgrimes	pwd = getpwnam(elem);
75518018Speter	if (pwd == NULL) {
75618018Speter		errno = 0;
7571556Srgrimes		ltemp = strtol(elem, &endp, 10);
7581556Srgrimes		if (errno != 0 || *endp != '\0' || ltemp < 0 ||
7591556Srgrimes		    ltemp > UID_MAX)
7601556Srgrimes			warnx("No %s named '%s'", inf->lname, elem);
7611556Srgrimes		else {
7621556Srgrimes			/* The string is all digits, so it might be a userID. */
76375160Sbrian			pwd = getpwuid((uid_t)ltemp);
7641556Srgrimes			if (pwd == NULL)
7651556Srgrimes				warnx("No %s name or ID matches '%s'",
7661556Srgrimes				    inf->lname, elem);
7671556Srgrimes		}
7681556Srgrimes	}
7691556Srgrimes	if (pwd == NULL) {
7701556Srgrimes		/*
7711556Srgrimes		 * These used to be treated as minor warnings (and the
7721556Srgrimes		 * option was simply ignored), but now they are fatal
7731556Srgrimes		 * errors (and the command will be aborted).
7741556Srgrimes		 */
7751556Srgrimes		optfatal = 1;
7761556Srgrimes		return (0);		/* Do not add this value. */
7771556Srgrimes	}
7781556Srgrimes
7791556Srgrimes	if (inf->count >= inf->maxcount)
7801556Srgrimes		expand_list(inf);
7811556Srgrimes	inf->uids[(inf->count)++] = pwd->pw_uid;
7821556Srgrimes	return (1);
7831556Srgrimes}
7841556Srgrimes
7851556Srgrimesstatic void
7861556Srgrimesadd_list(struct listinfo *inf, const char *argp)
7871556Srgrimes{
7881556Srgrimes	const char *savep;
7891556Srgrimes	char *cp, *endp;
7901556Srgrimes	int toolong;
7911556Srgrimes	char elemcopy[PATH_MAX];
7921556Srgrimes
7931556Srgrimes	while (*argp != '\0') {
7941556Srgrimes		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
7951556Srgrimes			argp++;
79690111Simp		savep = argp;
79790111Simp		toolong = 0;
79825230Ssteve		cp = elemcopy;
7991556Srgrimes		if (strchr(T_SEP, *argp) == NULL) {
8001556Srgrimes			endp = elemcopy + sizeof(elemcopy) - 1;
8011556Srgrimes			while (*argp != '\0' && cp <= endp &&
8021556Srgrimes			    strchr(W_SEP T_SEP, *argp) == NULL)
8031556Srgrimes				*cp++ = *argp++;
8041556Srgrimes			if (cp > endp)
8051556Srgrimes				toolong = 1;
8061556Srgrimes		}
8071556Srgrimes		if (!toolong) {
8081556Srgrimes			*cp = '\0';
8091556Srgrimes#ifndef ADD_PS_LISTRESET
8101556Srgrimes			/*
8111556Srgrimes			 * This is how the standard expects lists to
8121556Srgrimes			 * be handled.
8131556Srgrimes			 */
8141556Srgrimes			inf->addelem(inf, elemcopy);
8151556Srgrimes#else
8161556Srgrimes			/*
8171556Srgrimes			 * This would add a simple non-standard-but-convienent
8181556Srgrimes			 * feature.
8191556Srgrimes			 *
8201556Srgrimes			 * XXX - The first time I tried to add this check,
8211556Srgrimes			 *	it increased the total size of `ps' by 3940
8221556Srgrimes			 *	bytes on i386!  That's 12% of the entire
8231556Srgrimes			 *	program!  The `ps.o' file grew by only about
8241556Srgrimes			 *	40 bytes, but the final (stripped) executable
8251556Srgrimes			 *	in /bin/ps grew by 12%.  I have not had time
8261556Srgrimes			 *	to investigate, so skip the feature for now.
8271556Srgrimes			 */
8281556Srgrimes			/*
8291556Srgrimes			 * We now have a single element.  Add it to the
8301556Srgrimes			 * list, unless the element is ":".  In that case,
8311556Srgrimes			 * reset the list so previous entries are ignored.
8321556Srgrimes			 */
8331556Srgrimes			if (strcmp(elemcopy, ":") == 0)
8341556Srgrimes				inf->count = 0;
8351556Srgrimes			else
8361556Srgrimes				inf->addelem(inf, elemcopy);
8371556Srgrimes#endif
8381556Srgrimes		} else {
8391556Srgrimes			/*
8401556Srgrimes			 * The string is too long to copy.  Find the end
8411556Srgrimes			 * of the string to print out the warning message.
8421556Srgrimes			 */
8431556Srgrimes			while (*argp != '\0' && strchr(W_SEP T_SEP,
8441556Srgrimes			    *argp) == NULL)
8451556Srgrimes				argp++;
8461556Srgrimes			warnx("Value too long: %.*s", (int)(argp - savep),
8471556Srgrimes			    savep);
8481556Srgrimes			optfatal = 1;
8491556Srgrimes		}
8501556Srgrimes		/*
8511556Srgrimes		 * Skip over any number of trailing whitespace characters,
8521556Srgrimes		 * but only one (at most) trailing element-terminating
8531556Srgrimes		 * character.
8541556Srgrimes		 */
8551556Srgrimes		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
8561556Srgrimes			argp++;
8571556Srgrimes		if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) {
8581556Srgrimes			argp++;
8591556Srgrimes			/* Catch case where string ended with a comma. */
8601556Srgrimes			if (*argp == '\0')
8611556Srgrimes				inf->addelem(inf, argp);
8621556Srgrimes		}
8631556Srgrimes	}
8641556Srgrimes}
8651556Srgrimes
8661556Srgrimesstatic void *
8671556Srgrimesexpand_list(struct listinfo *inf)
8681556Srgrimes{
8691556Srgrimes	void *newlist;
8701556Srgrimes	int newmax;
8711556Srgrimes
8721556Srgrimes	newmax = (inf->maxcount + 1) << 1;
8731556Srgrimes	newlist = realloc(inf->ptr, newmax * inf->elemsize);
8741556Srgrimes	if (newlist == NULL) {
8751556Srgrimes		free(inf->ptr);
8761556Srgrimes		errx(1, "realloc to %d %ss failed", newmax,
8771556Srgrimes		    inf->lname);
8781556Srgrimes	}
8791556Srgrimes	inf->maxcount = newmax;
8801556Srgrimes	inf->ptr = newlist;
8811556Srgrimes
8821556Srgrimes	return (newlist);
8831556Srgrimes}
8841556Srgrimes
8851556Srgrimesstatic void
8861556Srgrimesfree_list(struct listinfo *inf)
88790111Simp{
88890111Simp
88917987Speter	inf->count = inf->elemsize = inf->maxcount = 0;
89017987Speter	if (inf->ptr != NULL)
8911556Srgrimes		free(inf->ptr);
8921556Srgrimes	inf->addelem = NULL;
8931556Srgrimes	inf->lname = NULL;
8941556Srgrimes	inf->ptr = NULL;
8951556Srgrimes}
8961556Srgrimes
8971556Srgrimesstatic void
8981556Srgrimesinit_list(struct listinfo *inf, addelem_rtn artn, int elemsize,
8991556Srgrimes    const char *lname)
9001556Srgrimes{
90154679Scracauer
90217987Speter	inf->count = inf->maxcount = 0;
90317987Speter	inf->elemsize = elemsize;
90417987Speter	inf->addelem = artn;
90517987Speter	inf->lname = lname;
90617987Speter	inf->ptr = NULL;
90717987Speter}
90817987Speter
90917987SpeterVARENT *
91017987Speterfind_varentry(VAR *v)
91117987Speter{
91217987Speter	struct varent *vent;
91354679Scracauer
91417987Speter	for (vent = vhead; vent; vent = vent->next) {
9151556Srgrimes		if (strcmp(vent->var->name, v->name) == 0)
9161556Srgrimes			return vent;
9171556Srgrimes	}
9181556Srgrimes	return NULL;
9191556Srgrimes}
9201556Srgrimes
9211556Srgrimesstatic void
9221556Srgrimesscanvars(void)
9231556Srgrimes{
9241556Srgrimes	struct varent *vent;
9251556Srgrimes	VAR *v;
9261556Srgrimes
9271556Srgrimes	for (vent = vhead; vent; vent = vent->next) {
9281556Srgrimes		v = vent->var;
9291556Srgrimes		if (v->flag & DSIZ) {
9301556Srgrimes			v->dwidth = v->width;
9311556Srgrimes			v->width = 0;
9321556Srgrimes		}
9331556Srgrimes		if (v->flag & USER)
9341556Srgrimes			needuser = 1;
9351556Srgrimes		if (v->flag & COMM)
9361556Srgrimes			needcomm = 1;
9371556Srgrimes	}
9381556Srgrimes}
9391556Srgrimes
9401556Srgrimesstatic void
94154679Scracauerdynsizevars(KINFO *ki)
94264705Scracauer{
94354679Scracauer	struct varent *vent;
94454679Scracauer	VAR *v;
9451556Srgrimes	int i;
9461556Srgrimes
9471556Srgrimes	for (vent = vhead; vent; vent = vent->next) {
9481556Srgrimes		v = vent->var;
9491556Srgrimes		if (!(v->flag & DSIZ))
9501556Srgrimes			continue;
9511556Srgrimes		i = (v->sproc)( ki);
9521556Srgrimes		if (v->width < i)
9531556Srgrimes			v->width = i;
9541556Srgrimes		if (v->width > v->dwidth)
9551556Srgrimes			v->width = v->dwidth;
9561556Srgrimes	}
9571556Srgrimes}
9581556Srgrimes
9591556Srgrimesstatic void
9601556Srgrimessizevars(void)
9611556Srgrimes{
9621556Srgrimes	struct varent *vent;
9631556Srgrimes	VAR *v;
9641556Srgrimes	int i;
9651556Srgrimes
9661556Srgrimes	for (vent = vhead; vent; vent = vent->next) {
9671556Srgrimes		v = vent->var;
9681556Srgrimes		i = strlen(vent->header);
9691556Srgrimes		if (v->width < i)
9701556Srgrimes			v->width = i;
9711556Srgrimes		totwidth += v->width + 1;	/* +1 for space */
9721556Srgrimes	}
9731556Srgrimes	totwidth--;
9741556Srgrimes}
97554631Scracauer
97654631Scracauerstatic const char *
97754631Scracauerfmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki,
9781556Srgrimes    char *comm, int maxlen)
97983675Stegge{
9801556Srgrimes	const char *s;
98139137Stegge
98238887Stegge	s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen);
9831556Srgrimes	return (s);
9841556Srgrimes}
9851556Srgrimes
9861556Srgrimes#define UREADOK(ki)	(forceuread || (ki->ki_p->ki_sflag & PS_INMEM))
9871556Srgrimes
98839137Steggestatic void
98939137Steggesaveuser(KINFO *ki)
9901556Srgrimes{
9911556Srgrimes
9921556Srgrimes	if (ki->ki_p->ki_sflag & PS_INMEM) {
99339137Stegge		/*
99439137Stegge		 * The u-area might be swapped out, and we can't get
9951556Srgrimes		 * at it because we have a crashdump and no swap.
9961556Srgrimes		 * If it's here fill in these fields, otherwise, just
9971556Srgrimes		 * leave them 0.
9981556Srgrimes		 */
99939137Stegge		ki->ki_valid = 1;
100039137Stegge	} else
10011556Srgrimes		ki->ki_valid = 0;
10021556Srgrimes	/*
100339137Stegge	 * save arguments if needed
10041556Srgrimes	 */
100539137Stegge	if (needcomm && (UREADOK(ki) || (ki->ki_p->ki_args != NULL))) {
100639137Stegge		ki->ki_args = strdup(fmt(kvm_getargv, ki, ki->ki_p->ki_comm,
10071556Srgrimes		    MAXCOMLEN));
100839137Stegge	} else if (needcomm) {
100939137Stegge		asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm);
10101556Srgrimes	} else {
10111556Srgrimes		ki->ki_args = NULL;
10121556Srgrimes	}
10131556Srgrimes	if (needenv && UREADOK(ki)) {
10141556Srgrimes		ki->ki_env = strdup(fmt(kvm_getenvv, ki, (char *)NULL, 0));
10151556Srgrimes	} else if (needenv) {
10161556Srgrimes		ki->ki_env = malloc(3);
10171556Srgrimes		strcpy(ki->ki_env, "()");
10181556Srgrimes	} else {
10191556Srgrimes		ki->ki_env = NULL;
10201556Srgrimes	}
10211556Srgrimes}
10221556Srgrimes
10231556Srgrimesstatic int
10241556Srgrimespscomp(const void *a, const void *b)
10251556Srgrimes{
10261556Srgrimes	int i;
10271556Srgrimes#define VSIZE(k) ((k)->ki_p->ki_dsize + (k)->ki_p->ki_ssize + \
10281556Srgrimes		  (k)->ki_p->ki_tsize)
10291556Srgrimes
10301556Srgrimes	if (sortby == SORTCPU)
10311556Srgrimes		return (getpcpu((const KINFO *)b) - getpcpu((const KINFO *)a));
10321556Srgrimes	if (sortby == SORTMEM)
10331556Srgrimes		return (VSIZE((const KINFO *)b) - VSIZE((const KINFO *)a));
10341556Srgrimes	i =  (int)((const KINFO *)a)->ki_p->ki_tdev -
10351556Srgrimes	    (int)((const KINFO *)b)->ki_p->ki_tdev;
10361556Srgrimes	if (i == 0)
103739137Stegge		i = ((const KINFO *)a)->ki_p->ki_pid -
103839137Stegge		    ((const KINFO *)b)->ki_p->ki_pid;
103939137Stegge	return (i);
104039137Stegge}
10411556Srgrimes
10421556Srgrimes/*
10431556Srgrimes * ICK (all for getopt), would rather hide the ugliness
10448855Srgrimes * here than taint the main code.
10451556Srgrimes *
10461556Srgrimes *  ps foo -> ps -foo
10471556Srgrimes *  ps 34 -> ps -p34
10481556Srgrimes *
10491556Srgrimes * The old convention that 't' with no trailing tty arg means the users
10501556Srgrimes * tty, is only supported if argv[1] doesn't begin with a '-'.  This same
10511556Srgrimes * feature is available with the option 'T', which takes no argument.
10521556Srgrimes */
10531556Srgrimesstatic char *
10541556Srgrimeskludge_oldps_options(char *s)
10551556Srgrimes{
10561556Srgrimes	int have_fmt;
10571556Srgrimes	size_t len;
10581556Srgrimes	char *newopts, *ns, *cp;
10591556Srgrimes
10601556Srgrimes	/*
10611556Srgrimes	 * If we have an 'o' option, then note it, since we don't want to do
10621556Srgrimes	 * some types of munging.
10631556Srgrimes	 */
10641556Srgrimes	have_fmt = index(s, 'o') != NULL;
10651556Srgrimes
10661556Srgrimes	len = strlen(s);
10671556Srgrimes	if ((newopts = ns = malloc(len + 2)) == NULL)
10681556Srgrimes		errx(1, "malloc failed");
10691556Srgrimes	/*
10701556Srgrimes	 * options begin with '-'
10711556Srgrimes	 */
10721556Srgrimes	if (*s != '-')
10731556Srgrimes		*ns++ = '-';	/* add option flag */
10741556Srgrimes	/*
10751556Srgrimes	 * gaze to end of argv[1]
10761556Srgrimes	 */
10771556Srgrimes	cp = s + len - 1;
10781556Srgrimes	/*
10791556Srgrimes	 * if last letter is a 't' flag with no argument (in the context
10801556Srgrimes	 * of the oldps options -- option string NOT starting with a '-' --
10811556Srgrimes	 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
10821556Srgrimes	 *
10831556Srgrimes	 * However, if a flag accepting a string argument is found in the
10841556Srgrimes	 * option string, the remainder of the string is the argument to
10851556Srgrimes	 * that flag; do not modify that argument.
10861556Srgrimes	 */
10871556Srgrimes	if (strcspn(s, "MNOoU") == len && *cp == 't' && *s != '-')
10881556Srgrimes		*cp = 'T';
10891556Srgrimes	else {
10901556Srgrimes		/*
10911556Srgrimes		 * otherwise check for trailing number, which *may* be a
10921556Srgrimes		 * pid.
10931556Srgrimes		 */
10941556Srgrimes		while (cp >= s && isdigit(*cp))
10951556Srgrimes			--cp;
10961556Srgrimes	}
10971556Srgrimes	cp++;
10981556Srgrimes	memmove(ns, s, (size_t)(cp - s));	/* copy up to trailing number */
10991556Srgrimes	ns += cp - s;
11001556Srgrimes	/*
11011556Srgrimes	 * if there's a trailing number, and not a preceding 'p' (pid) or
11021556Srgrimes	 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag.
11031556Srgrimes	 */
11041556Srgrimes	if (isdigit(*cp) &&
11051556Srgrimes	    (cp == s || (cp[-1] != 't' && cp[-1] != 'p')) &&
11061556Srgrimes	    (cp - 1 == s || cp[-2] != 't') && !have_fmt)
11071556Srgrimes		*ns++ = 'p';
11081556Srgrimes	(void)strcpy(ns, cp);		/* and append the number */
11091556Srgrimes
11101556Srgrimes	return (newopts);
11111556Srgrimes}
111225230Ssteve
11131556Srgrimesstatic void
11141556Srgrimesusage(void)
11151556Srgrimes{
11161556Srgrimes#define	SINGLE_OPTS	"[-aC" OPT_LAZY_f "HhjlmrSTuvwXxZ]"
11171556Srgrimes
11181556Srgrimes	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
11191556Srgrimes	    "usage: ps " SINGLE_OPTS " [-G gid[,gid]] [-O|o fmt]",
11201556Srgrimes	    "          [-p pid[,pid]] [-t tty[,tty]] [-U user[,user]]",
11211556Srgrimes	    "          [-M core] [-N system]",
11221556Srgrimes	    "       ps [-L]");
11231556Srgrimes	exit(1);
11241556Srgrimes}
11251556Srgrimes