ps.c revision 127149
1270096Strasz/*-
2332596Strasz * Copyright (c) 1990, 1993, 1994
3332596Strasz *	The Regents of the University of California.  All rights reserved.
4270096Strasz *
5270096Strasz * Redistribution and use in source and binary forms, with or without
6270096Strasz * modification, are permitted provided that the following conditions
7270096Strasz * are met:
8270096Strasz * 1. Redistributions of source code must retain the above copyright
9270096Strasz *    notice, this list of conditions and the following disclaimer.
10270096Strasz * 2. Redistributions in binary form must reproduce the above copyright
11270096Strasz *    notice, this list of conditions and the following disclaimer in the
12270096Strasz *    documentation and/or other materials provided with the distribution.
13270096Strasz * 3. All advertising materials mentioning features or use of this software
14270096Strasz *    must display the following acknowledgement:
15270096Strasz *	This product includes software developed by the University of
16270096Strasz *	California, Berkeley and its contributors.
17270096Strasz * 4. Neither the name of the University nor the names of its contributors
18270096Strasz *    may be used to endorse or promote products derived from this software
19270096Strasz *    without specific prior written permission.
20270096Strasz *
21270096Strasz * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22270096Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23270096Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24270096Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25270096Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26270096Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27270096Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28270096Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29270096Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30270096Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31270096Strasz * SUCH DAMAGE.
32270096Strasz */
33270096Strasz
34270096Strasz#ifndef lint
35270096Straszstatic const char copyright[] =
36270096Strasz"@(#) Copyright (c) 1990, 1993, 1994\n\
37341074Smarkj	The Regents of the University of California.  All rights reserved.\n";
38270096Strasz#endif /* not lint */
39270096Strasz
40270096Strasz#if 0
41270096Strasz#ifndef lint
42270096Straszstatic char sccsid[] = "@(#)ps.c	8.4 (Berkeley) 4/2/94";
43270096Strasz#endif /* not lint */
44270096Strasz#endif
45270096Strasz
46270096Strasz#include <sys/cdefs.h>
47296718Strasz__FBSDID("$FreeBSD: head/bin/ps/ps.c 127149 2004-03-17 22:46:58Z gad $");
48272403Strasz
49297236Strasz#include <sys/param.h>
50270096Strasz#include <sys/user.h>
51270096Strasz#include <sys/stat.h>
52270096Strasz#include <sys/ioctl.h>
53270096Strasz#include <sys/sysctl.h>
54270281Strasz
55270096Strasz#include <ctype.h>
56270096Strasz#include <err.h>
57270096Strasz#include <errno.h>
58270096Strasz#include <fcntl.h>
59270402Strasz#include <kvm.h>
60270402Strasz#include <limits.h>
61270096Strasz#include <locale.h>
62270096Strasz#include <paths.h>
63270096Strasz#include <pwd.h>
64270096Strasz#include <stdio.h>
65270096Strasz#include <stdlib.h>
66270096Strasz#include <string.h>
67270096Strasz#include <unistd.h>
68270096Strasz
69270096Strasz#include "ps.h"
70270096Strasz
71270096Strasz#define SEP ", \t"		/* username separators */
72270096Strasz
73270096Straszstatic KINFO *kinfo;
74270096Straszstruct varent *vhead;
75270096Strasz
76270096Straszint	eval;			/* exit value */
77270096Straszint	cflag;			/* -c */
78270096Straszint	rawcpu;			/* -C */
79270096Straszint	sumrusage;		/* -S */
80270096Straszint	termwidth;		/* width of screen (0 == infinity) */
81270096Straszint	totwidth;		/* calculated width of requested variables */
82270096Strasz
83270096Strasztime_t	now;			/* current time(3) value */
84270096Strasz
85270096Straszstatic int needuser, needcomm, needenv;
86270096Strasz#if defined(LAZY_PS)
87270096Straszstatic int forceuread=0;
88270096Strasz#else
89270096Straszstatic int forceuread=1;
90270096Strasz#endif
91270096Strasz
92270096Straszstatic enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
93270096Strasz
94270096Straszstatic const	 char *fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int),
95270096Strasz		    KINFO *, char *, int);
96270096Straszstatic char	*kludge_oldps_options(char *);
97270096Straszstatic int	 pscomp(const void *, const void *);
98270096Straszstatic void	 saveuser(KINFO *);
99270096Straszstatic void	 scanvars(void);
100270096Straszstatic void	 dynsizevars(KINFO *);
101270096Straszstatic void	 sizevars(void);
102270096Straszstatic void	 usage(void);
103270096Straszstatic pid_t	*getpids(const char *, int *);
104270096Straszstatic uid_t	*getuids(const char *, int *);
105270096Strasz
106270096Straszstatic char dfmt[] = "pid,tt,state,time,command";
107270096Straszstatic char jfmt[] = "user,pid,ppid,pgid,jobc,state,tt,time,command";
108270096Straszstatic char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,tt,time,command";
109270096Straszstatic char   o1[] = "pid";
110270096Straszstatic char   o2[] = "tt,state,time,command";
111270096Straszstatic char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
112270096Straszstatic char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,%cpu,%mem,command";
113270096Straszstatic char Zfmt[] = "label";
114270096Strasz
115270096Straszstatic kvm_t *kd;
116270096Strasz
117296718Strasz#if defined(LAZY_PS)
118296718Strasz#define PS_ARGS	"aCcefgHhjLlM:mN:O:o:p:rSTt:U:uvwxZ"
119270096Strasz#else
120270096Strasz#define PS_ARGS	"aCcegHhjLlM:mN:O:o:p:rSTt:U:uvwxZ"
121270096Strasz#endif
122270096Strasz
123270096Straszint
124270096Straszmain(int argc, char *argv[])
125270096Strasz{
126296718Strasz	struct kinfo_proc *kp;
127270096Strasz	struct varent *vent;
128270096Strasz	struct winsize ws;
129270096Strasz	dev_t ttydev;
130270096Strasz	pid_t *pids;
131270096Strasz	uid_t *uids;
132270096Strasz	int all, ch, flag, i, _fmt, lineno, nentries, nocludge, dropgid;
133270096Strasz	int prtheader, wflag, what, xflg, pid, uid, npids, nuids, showthreads;
134270096Strasz	char *cols;
135270096Strasz	char errbuf[_POSIX2_LINE_MAX];
136270096Strasz	const char *cp, *nlistf, *memf;
137270096Strasz
138270096Strasz	(void) setlocale(LC_ALL, "");
139270096Strasz	/* Set the time to what it is right now. */
140270096Strasz	time(&now);
141270096Strasz
142270096Strasz	if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0')
143270096Strasz		termwidth = atoi(cols);
144270096Strasz	else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
145270096Strasz	     ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
146270096Strasz	     ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) == -1) ||
147270096Strasz	     ws.ws_col == 0)
148270096Strasz		termwidth = 79;
149270096Strasz	else
150270096Strasz		termwidth = ws.ws_col - 1;
151270096Strasz
152270096Strasz	/*
153270096Strasz	 * Don't apply a kludge if the first argument is an option taking an
154270096Strasz	 * argument
155270096Strasz	 */
156270096Strasz	if (argc > 1) {
157270402Strasz		nocludge = 0;
158270096Strasz		if (argv[1][0] == '-') {
159270096Strasz			for (cp = PS_ARGS; *cp != '\0'; cp++) {
160270096Strasz				if (*cp != ':')
161270096Strasz					continue;
162270096Strasz				if (*(cp - 1) == argv[1][1]) {
163270096Strasz					nocludge = 1;
164270096Strasz					break;
165270096Strasz				}
166270096Strasz			}
167270096Strasz		}
168270096Strasz		if (nocludge == 0)
169270402Strasz			argv[1] = kludge_oldps_options(argv[1]);
170270096Strasz	}
171270096Strasz
172270096Strasz	all = _fmt = prtheader = wflag = xflg = 0;
173270096Strasz	npids = nuids = 0;
174270096Strasz	pids = uids = NULL;
175270096Strasz	ttydev = NODEV;
176270096Strasz	dropgid = 0;
177270096Strasz	memf = nlistf = _PATH_DEVNULL;
178270096Strasz	showthreads = 0;
179270096Strasz	while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
180270096Strasz		switch((char)ch) {
181270096Strasz		case 'a':
182270096Strasz			all = 1;
183270096Strasz			break;
184270096Strasz		case 'C':
185270096Strasz			rawcpu = 1;
186270096Strasz			break;
187270096Strasz		case 'c':
188270096Strasz			cflag = 1;
189270096Strasz			break;
190270096Strasz		case 'e':			/* XXX set ufmt */
191270096Strasz			needenv = 1;
192270096Strasz			break;
193270096Strasz		case 'g':
194270096Strasz			break;			/* no-op */
195270096Strasz		case 'H':
196270096Strasz			showthreads = KERN_PROC_INC_THREAD;
197270096Strasz			break;
198270096Strasz		case 'h':
199270096Strasz			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
200270096Strasz			break;
201270096Strasz		case 'j':
202270096Strasz			parsefmt(jfmt, 0);
203272512Strasz			_fmt = 1;
204270207Strasz			jfmt[0] = '\0';
205270207Strasz			break;
206270207Strasz		case 'L':
207270207Strasz			showkey();
208272512Strasz			exit(0);
209270207Strasz		case 'l':
210270207Strasz			parsefmt(lfmt, 0);
211270207Strasz			_fmt = 1;
212270096Strasz			lfmt[0] = '\0';
213270096Strasz			break;
214270096Strasz		case 'M':
215270096Strasz			memf = optarg;
216270096Strasz			dropgid = 1;
217270096Strasz			break;
218270096Strasz		case 'm':
219296715Strasz			sortby = SORTMEM;
220270096Strasz			break;
221270096Strasz		case 'N':
222270096Strasz			nlistf = optarg;
223270096Strasz			dropgid = 1;
224270096Strasz			break;
225270096Strasz		case 'O':
226270096Strasz			parsefmt(o1, 1);
227270096Strasz			parsefmt(optarg, 1);
228270096Strasz			parsefmt(o2, 1);
229270096Strasz			o1[0] = o2[0] = '\0';
230270096Strasz			_fmt = 1;
231270207Strasz			break;
232270207Strasz		case 'o':
233270207Strasz			parsefmt(optarg, 1);
234270207Strasz			_fmt = 1;
235270207Strasz			break;
236270096Strasz#if defined(LAZY_PS)
237270207Strasz		case 'f':
238272512Strasz			if (getuid() == 0 || getgid() == 0)
239270096Strasz			    forceuread = 1;
240270207Strasz			break;
241270096Strasz#endif
242270207Strasz		case 'p':
243270096Strasz			pids = getpids(optarg, &npids);
244270096Strasz			xflg = 1;
245270096Strasz			break;
246270096Strasz		case 'r':
247270096Strasz			sortby = SORTCPU;
248270096Strasz			break;
249270096Strasz		case 'S':
250270096Strasz			sumrusage = 1;
251270096Strasz			break;
252270096Strasz		case 'T':
253270096Strasz			if ((optarg = ttyname(STDIN_FILENO)) == NULL)
254270096Strasz				errx(1, "stdin: not a terminal");
255270096Strasz			/* FALLTHROUGH */
256270096Strasz		case 't': {
257270096Strasz			struct stat sb;
258270096Strasz			char *ttypath, pathbuf[PATH_MAX];
259270096Strasz
260270096Strasz			if (strcmp(optarg, "co") == 0)
261270096Strasz				ttypath = strdup(_PATH_CONSOLE);
262270096Strasz			else if (*optarg != '/')
263296715Strasz				(void)snprintf(ttypath = pathbuf,
264296715Strasz				    sizeof(pathbuf), "%s%s", _PATH_TTY, optarg);
265296715Strasz			else
266270096Strasz				ttypath = optarg;
267296715Strasz			if (stat(ttypath, &sb) == -1)
268296715Strasz				err(1, "%s", ttypath);
269270096Strasz			if (!S_ISCHR(sb.st_mode))
270270096Strasz				errx(1, "%s: not a terminal", ttypath);
271270096Strasz			ttydev = sb.st_rdev;
272272470Strasz			break;
273270096Strasz		}
274270096Strasz		case 'U':
275270096Strasz			uids = getuids(optarg, &nuids);
276272470Strasz			xflg++;		/* XXX: intuitive? */
277270096Strasz			break;
278270096Strasz		case 'u':
279270096Strasz			parsefmt(ufmt, 0);
280272470Strasz			sortby = SORTCPU;
281270096Strasz			_fmt = 1;
282270096Strasz			ufmt[0] = '\0';
283270096Strasz			break;
284270096Strasz		case 'v':
285270096Strasz			parsefmt(vfmt, 0);
286270096Strasz			sortby = SORTMEM;
287272470Strasz			_fmt = 1;
288270096Strasz			vfmt[0] = '\0';
289272512Strasz			break;
290270096Strasz		case 'w':
291270096Strasz			if (wflag)
292270096Strasz				termwidth = UNLIMITED;
293270096Strasz			else if (termwidth < 131)
294270096Strasz				termwidth = 131;
295270096Strasz			wflag++;
296270096Strasz			break;
297270096Strasz		case 'x':
298270096Strasz			xflg = 1;
299270096Strasz			break;
300270096Strasz		case 'Z':
301270096Strasz			parsefmt(Zfmt, 0);
302270096Strasz			Zfmt[0] = '\0';
303270096Strasz			break;
304270096Strasz		case '?':
305270096Strasz		default:
306270096Strasz			usage();
307270096Strasz		}
308270096Strasz	argc -= optind;
309270096Strasz	argv += optind;
310270096Strasz
311270096Strasz#define	BACKWARD_COMPATIBILITY
312270096Strasz#ifdef	BACKWARD_COMPATIBILITY
313270096Strasz	if (*argv) {
314270096Strasz		nlistf = *argv;
315270096Strasz		if (*++argv) {
316270096Strasz			memf = *argv;
317270096Strasz		}
318270096Strasz	}
319270096Strasz#endif
320272470Strasz	/*
321270096Strasz	 * Discard setgid privileges if not the running kernel so that bad
322270096Strasz	 * guys can't print interesting stuff from kernel memory.
323270096Strasz	 */
324272470Strasz	if (dropgid) {
325270096Strasz		setgid(getgid());
326270096Strasz		setuid(getuid());
327272470Strasz	}
328270096Strasz
329272512Strasz	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
330270096Strasz	if (kd == 0)
331270096Strasz		errx(1, "%s", errbuf);
332270096Strasz
333270096Strasz	if (!_fmt)
334308253Strasz		parsefmt(dfmt, 0);
335308253Strasz
336308253Strasz	/* XXX - should be cleaner */
337308253Strasz	if (!all && ttydev == NODEV && !npids && !nuids) {
338308253Strasz		if ((uids = malloc(sizeof (*uids))) == NULL)
339308253Strasz			errx(1, "malloc failed");
340308253Strasz		nuids = 1;
341308253Strasz		*uids = getuid();
342308253Strasz	}
343308253Strasz
344308253Strasz	/*
345308253Strasz	 * scan requested variables, noting what structures are needed,
346308253Strasz	 * and adjusting header widths as appropriate.
347308253Strasz	 */
348308253Strasz	scanvars();
349296798Strasz	/*
350296798Strasz	 * get proc list
351296798Strasz	 */
352270096Strasz	if (nuids == 1) {
353296798Strasz		what = KERN_PROC_UID | showthreads;
354296798Strasz		flag = *uids;
355270096Strasz	} else if (ttydev != NODEV) {
356270096Strasz		what = KERN_PROC_TTY | showthreads;
357296798Strasz		flag = ttydev;
358296798Strasz	} else if (npids == 1) {
359270096Strasz		what = KERN_PROC_PID | showthreads;
360296798Strasz		flag = *pids;
361296798Strasz	} else {
362296798Strasz		what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
363296798Strasz		flag = 0;
364296798Strasz	}
365296798Strasz
366296798Strasz	/*
367296798Strasz	 * select procs
368296798Strasz	 */
369296798Strasz	kp = kvm_getprocs(kd, what, flag, &nentries);
370296798Strasz	if ((kp == 0 && nentries != 0) || nentries < 0)
371296798Strasz		errx(1, "%s", kvm_geterr(kd));
372296798Strasz	if (nentries > 0) {
373296798Strasz		if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
374296798Strasz			errx(1, "malloc failed");
375270096Strasz		for (i = nentries; --i >= 0; ++kp) {
376296798Strasz			kinfo[i].ki_p = kp;
377296798Strasz			if (needuser)
378341074Smarkj				saveuser(&kinfo[i]);
379296798Strasz			dynsizevars(&kinfo[i]);
380270096Strasz		}
381270096Strasz	}
382270096Strasz
383270096Strasz	sizevars();
384296798Strasz
385296798Strasz	/*
386296798Strasz	 * print header
387296798Strasz	 */
388296798Strasz	printheader();
389296937Strasz	if (nentries == 0)
390296798Strasz		exit(1);
391296798Strasz	/*
392296798Strasz	 * sort proc list
393296798Strasz	 */
394270096Strasz	qsort(kinfo, nentries, sizeof(KINFO), pscomp);
395270096Strasz	/*
396270096Strasz	 * for each proc, call each variable output function.
397270096Strasz	 */
398270096Strasz	for (i = lineno = 0; i < nentries; i++) {
399270096Strasz		if (xflg == 0 && ((&kinfo[i])->ki_p->ki_tdev == NODEV ||
400270096Strasz		    ((&kinfo[i])->ki_p->ki_flag & P_CONTROLT ) == 0))
401296798Strasz			continue;
402296798Strasz		if (npids > 1) {
403296798Strasz			for (pid = 0; pid < npids; pid++)
404270096Strasz				if ((&kinfo[i])->ki_p->ki_pid == pids[pid])
405270096Strasz					break;
406270096Strasz			if (pid == npids)
407270096Strasz				continue;
408270096Strasz		}
409296798Strasz		if (nuids > 1) {
410270096Strasz			for (uid = 0; uid < nuids; uid++)
411270096Strasz				if ((&kinfo[i])->ki_p->ki_uid == uids[uid])
412270096Strasz					break;
413270096Strasz			if (uid == nuids)
414270096Strasz				continue;
415270096Strasz		}
416270096Strasz		for (vent = vhead; vent; vent = vent->next) {
417270096Strasz			(vent->var->oproc)(&kinfo[i], vent);
418270096Strasz			if (vent->next != NULL)
419270096Strasz				(void)putchar(' ');
420270096Strasz		}
421270096Strasz		(void)putchar('\n');
422270096Strasz		if (prtheader && lineno++ == prtheader - 4) {
423270096Strasz			(void)putchar('\n');
424270096Strasz			printheader();
425270096Strasz			lineno = 0;
426270096Strasz		}
427296798Strasz	}
428270096Strasz	free(uids);
429270096Strasz
430270096Strasz	exit(eval);
431296798Strasz}
432270096Strasz
433296798Strasz#define	BSD_PID_MAX	99999		/* Copy of PID_MAX from sys/proc.h */
434296798Straszpid_t *
435296798Straszgetpids(const char *arg, int *npids)
436296798Strasz{
437296798Strasz	char copyarg[32];
438296798Strasz	char *copyp, *endp;
439296798Strasz	pid_t *pids, *morepids;
440270096Strasz	int alloc;
441296798Strasz	long tempid;
442270096Strasz
443296798Strasz	alloc = 0;
444270096Strasz	*npids = 0;
445296798Strasz	pids = NULL;
446296798Strasz	while (*arg != '\0') {
447296798Strasz		while (*arg != '\0' && strchr(SEP, *arg) != NULL)
448296798Strasz			arg++;
449296798Strasz		if (*arg == '\0' || strchr(SEP, *arg) != NULL)
450296798Strasz			tempid = 0;
451270096Strasz		else {
452296798Strasz			copyp = copyarg;
453296798Strasz			endp = copyarg + sizeof(copyarg) - 1;
454270096Strasz			while (*arg != '\0' && strchr(SEP, *arg) == NULL &&
455270096Strasz			    copyp <= endp)
456296798Strasz				*copyp++ = *arg++;
457270096Strasz			if (copyp > endp) {
458270096Strasz				*endp = '\0';
459296798Strasz				tempid = -1;
460270096Strasz				while (*arg != '\0' &&
461270096Strasz				    strchr(SEP, *arg) == NULL)
462296798Strasz					arg++;
463296798Strasz			} else {
464296798Strasz				*copyp = '\0';
465296798Strasz				errno = 0;
466296798Strasz				tempid = strtol(copyarg, &endp, 10);
467272470Strasz			}
468297236Strasz			/*
469296798Strasz			 * Write warning messages for any values which
470296798Strasz			 * would never be a valid process number.
471296798Strasz			 */
472296798Strasz			if (*endp != '\0' || tempid < 0 || copyarg == endp) {
473296798Strasz				warnx("invalid process number: %s", copyarg);
474296798Strasz				errno = ERANGE;
475296798Strasz			} else if (errno != 0 || tempid > BSD_PID_MAX) {
476270096Strasz				warnx("process number too large: %s", copyarg);
477270096Strasz				errno = ERANGE;
478270096Strasz			}
479296798Strasz			if (errno == ERANGE) {
480270096Strasz				/* Ignore this value from the given list. */
481296798Strasz				continue;
482296798Strasz			}
483296798Strasz		}
484296798Strasz		if (*npids >= alloc) {
485270096Strasz			alloc = (alloc + 1) << 1;
486270096Strasz			morepids = realloc(pids, alloc * sizeof (*pids));
487296798Strasz			if (morepids == NULL) {
488296798Strasz				free(pids);
489270096Strasz				errx(1, "realloc failed");
490272470Strasz			}
491296798Strasz			pids = morepids;
492270096Strasz		}
493270096Strasz		pids[(*npids)++] = tempid;
494296798Strasz	}
495270096Strasz
496296798Strasz	if (!*npids)
497296798Strasz		errx(1, "No valid process numbers specified");
498296798Strasz
499270096Strasz	return (pids);
500296798Strasz}
501296798Strasz
502296798Straszuid_t *
503296798Straszgetuids(const char *arg, int *nuids)
504296798Strasz{
505296798Strasz	char name[MAXLOGNAME];
506296798Strasz	struct passwd *pwd;
507296798Strasz	uid_t *uids, *moreuids;
508296798Strasz	int alloc;
509296798Strasz	size_t l;
510296798Strasz
511296798Strasz
512296798Strasz	alloc = 0;
513296798Strasz	*nuids = 0;
514296798Strasz	uids = NULL;
515270096Strasz	for (; (l = strcspn(arg, SEP)) > 0; arg += l + strspn(arg + l, SEP)) {
516270096Strasz		if (l >= sizeof name) {
517270096Strasz			warnx("%.*s: name too long", (int)l, arg);
518270096Strasz			continue;
519270096Strasz		}
520272836Strasz		strncpy(name, arg, l);
521272836Strasz		name[l] = '\0';
522270096Strasz		if ((pwd = getpwnam(name)) == NULL) {
523270096Strasz			warnx("%s: no such user", name);
524270096Strasz			continue;
525270096Strasz		}
526270096Strasz		if (*nuids >= alloc) {
527270096Strasz			alloc = (alloc + 1) << 1;
528270096Strasz			moreuids = realloc(uids, alloc * sizeof (*uids));
529270096Strasz			if (moreuids == NULL) {
530270096Strasz				free(uids);
531270096Strasz				errx(1, "realloc failed");
532270096Strasz			}
533270096Strasz			uids = moreuids;
534270096Strasz		}
535270096Strasz		uids[(*nuids)++] = pwd->pw_uid;
536270096Strasz	}
537270096Strasz	endpwent();
538270096Strasz
539270096Strasz	if (!*nuids)
540270096Strasz		errx(1, "No users specified");
541270096Strasz
542270096Strasz	return uids;
543270096Strasz}
544270096Strasz
545270096StraszVARENT *
546270096Straszfind_varentry(VAR *v)
547270096Strasz{
548308253Strasz	struct varent *vent;
549270096Strasz
550270096Strasz	for (vent = vhead; vent; vent = vent->next) {
551270096Strasz		if (strcmp(vent->var->name, v->name) == 0)
552270096Strasz			return vent;
553270096Strasz	}
554270096Strasz	return NULL;
555270096Strasz}
556270096Strasz
557270096Straszstatic void
558270096Straszscanvars(void)
559270096Strasz{
560270096Strasz	struct varent *vent;
561270096Strasz	VAR *v;
562270096Strasz
563270096Strasz	for (vent = vhead; vent; vent = vent->next) {
564270096Strasz		v = vent->var;
565270096Strasz		if (v->flag & DSIZ) {
566272931Strasz			v->dwidth = v->width;
567272470Strasz			v->width = 0;
568270096Strasz		}
569272931Strasz		if (v->flag & USER)
570272931Strasz			needuser = 1;
571272931Strasz		if (v->flag & COMM)
572272931Strasz			needcomm = 1;
573270096Strasz	}
574270096Strasz}
575270096Strasz
576270096Straszstatic void
577270096Straszdynsizevars(KINFO *ki)
578270096Strasz{
579270096Strasz	struct varent *vent;
580270096Strasz	VAR *v;
581270096Strasz	int i;
582270096Strasz
583270096Strasz	for (vent = vhead; vent; vent = vent->next) {
584270096Strasz		v = vent->var;
585270096Strasz		if (!(v->flag & DSIZ))
586270096Strasz			continue;
587270096Strasz		i = (v->sproc)( ki);
588270096Strasz		if (v->width < i)
589270096Strasz			v->width = i;
590270096Strasz		if (v->width > v->dwidth)
591270096Strasz			v->width = v->dwidth;
592270096Strasz	}
593270096Strasz}
594297236Strasz
595297236Straszstatic void
596270096Straszsizevars(void)
597270096Strasz{
598270096Strasz	struct varent *vent;
599270096Strasz	VAR *v;
600270096Strasz	int i;
601270096Strasz
602270096Strasz	for (vent = vhead; vent; vent = vent->next) {
603270096Strasz		v = vent->var;
604270096Strasz		i = strlen(vent->header);
605297236Strasz		if (v->width < i)
606297236Strasz			v->width = i;
607270096Strasz		totwidth += v->width + 1;	/* +1 for space */
608270096Strasz	}
609270096Strasz	totwidth--;
610297236Strasz}
611297236Strasz
612297236Straszstatic const char *
613297236Straszfmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki,
614270096Strasz  char *comm, int maxlen)
615297236Strasz{
616297236Strasz	const char *s;
617297236Strasz
618270096Strasz	s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen);
619270096Strasz	return (s);
620297236Strasz}
621297236Strasz
622270096Strasz#define UREADOK(ki)	(forceuread || (ki->ki_p->ki_sflag & PS_INMEM))
623270096Strasz
624297236Straszstatic void
625297236Straszsaveuser(KINFO *ki)
626297236Strasz{
627270096Strasz
628270096Strasz	if (ki->ki_p->ki_sflag & PS_INMEM) {
629270096Strasz		/*
630270096Strasz		 * The u-area might be swapped out, and we can't get
631270096Strasz		 * at it because we have a crashdump and no swap.
632270096Strasz		 * If it's here fill in these fields, otherwise, just
633270096Strasz		 * leave them 0.
634272470Strasz		 */
635297236Strasz		ki->ki_valid = 1;
636270096Strasz	} else
637270096Strasz		ki->ki_valid = 0;
638270096Strasz	/*
639270096Strasz	 * save arguments if needed
640270096Strasz	 */
641297236Strasz	if (needcomm && (UREADOK(ki) || (ki->ki_p->ki_args != NULL))) {
642270096Strasz		ki->ki_args = strdup(fmt(kvm_getargv, ki, ki->ki_p->ki_comm,
643270096Strasz		    MAXCOMLEN));
644270096Strasz	} else if (needcomm) {
645270096Strasz		asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm);
646270096Strasz	} else {
647270096Strasz		ki->ki_args = NULL;
648272512Strasz	}
649272512Strasz	if (needenv && UREADOK(ki)) {
650270096Strasz		ki->ki_env = strdup(fmt(kvm_getenvv, ki, (char *)NULL, 0));
651270096Strasz	} else if (needenv) {
652270096Strasz		ki->ki_env = malloc(3);
653270096Strasz		strcpy(ki->ki_env, "()");
654270096Strasz	} else {
655270096Strasz		ki->ki_env = NULL;
656270096Strasz	}
657270096Strasz}
658270096Strasz
659270096Straszstatic int
660272512Straszpscomp(const void *a, const void *b)
661270096Strasz{
662270096Strasz	int i;
663270096Strasz#define VSIZE(k) ((k)->ki_p->ki_dsize + (k)->ki_p->ki_ssize + \
664270096Strasz		  (k)->ki_p->ki_tsize)
665270096Strasz
666270096Strasz	if (sortby == SORTCPU)
667270096Strasz		return (getpcpu((const KINFO *)b) - getpcpu((const KINFO *)a));
668270096Strasz	if (sortby == SORTMEM)
669270096Strasz		return (VSIZE((const KINFO *)b) - VSIZE((const KINFO *)a));
670270096Strasz	i =  (int)((const KINFO *)a)->ki_p->ki_tdev - (int)((const KINFO *)b)->ki_p->ki_tdev;
671270096Strasz	if (i == 0)
672270096Strasz		i = ((const KINFO *)a)->ki_p->ki_pid - ((const KINFO *)b)->ki_p->ki_pid;
673270096Strasz	return (i);
674270096Strasz}
675270096Strasz
676270096Strasz/*
677270096Strasz * ICK (all for getopt), would rather hide the ugliness
678270096Strasz * here than taint the main code.
679270096Strasz *
680270096Strasz *  ps foo -> ps -foo
681270096Strasz *  ps 34 -> ps -p34
682270096Strasz *
683270096Strasz * The old convention that 't' with no trailing tty arg means the users
684270096Strasz * tty, is only supported if argv[1] doesn't begin with a '-'.  This same
685270096Strasz * feature is available with the option 'T', which takes no argument.
686270096Strasz */
687270096Straszstatic char *
688270096Straszkludge_oldps_options(char *s)
689270096Strasz{
690270096Strasz	int have_fmt;
691270096Strasz	size_t len;
692270096Strasz	char *newopts, *ns, *cp;
693270096Strasz
694270096Strasz	/*
695270096Strasz	 * If we have an 'o' option, then note it, since we don't want to do
696270096Strasz	 * some types of munging.
697270096Strasz	 */
698270096Strasz	have_fmt = index(s, 'o') != NULL;
699270096Strasz
700272512Strasz	len = strlen(s);
701272512Strasz	if ((newopts = ns = malloc(len + 2)) == NULL)
702270096Strasz		errx(1, "malloc failed");
703270096Strasz	/*
704300047Strasz	 * options begin with '-'
705270096Strasz	 */
706270096Strasz	if (*s != '-')
707270096Strasz		*ns++ = '-';	/* add option flag */
708270096Strasz	/*
709270096Strasz	 * gaze to end of argv[1]
710270096Strasz	 */
711270096Strasz	cp = s + len - 1;
712270096Strasz	/*
713270096Strasz	 * if last letter is a 't' flag with no argument (in the context
714270096Strasz	 * of the oldps options -- option string NOT starting with a '-' --
715270096Strasz	 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
716270096Strasz	 *
717	 * However, if a flag accepting a string argument is found in the
718	 * option string, the remainder of the string is the argument to
719	 * that flag; do not modify that argument.
720	 */
721	if (strcspn(s, "MNOoU") == len && *cp == 't' && *s != '-')
722		*cp = 'T';
723	else {
724		/*
725		 * otherwise check for trailing number, which *may* be a
726		 * pid.
727		 */
728		while (cp >= s && isdigit(*cp))
729			--cp;
730	}
731	cp++;
732	memmove(ns, s, (size_t)(cp - s));	/* copy up to trailing number */
733	ns += cp - s;
734	/*
735	 * if there's a trailing number, and not a preceding 'p' (pid) or
736	 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag.
737	 */
738	if (isdigit(*cp) &&
739	    (cp == s || (cp[-1] != 't' && cp[-1] != 'p')) &&
740	    (cp - 1 == s || cp[-2] != 't') && !have_fmt)
741		*ns++ = 'p';
742	(void)strcpy(ns, cp);		/* and append the number */
743
744	return (newopts);
745}
746
747static void
748usage(void)
749{
750
751	(void)fprintf(stderr, "%s\n%s\n%s\n",
752	    "usage: ps [-aCHhjlmrSTuvwxZ] [-O|o fmt] [-p pid[,pid]] [-t tty]",
753	    "          [-U user[,user]] [-M core] [-N system]",
754	    "       ps [-L]");
755	exit(1);
756}
757