ps.c revision 1556
1/*-
2 * Copyright (c) 1990, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char copyright[] =
36"@(#) Copyright (c) 1990, 1993, 1994\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)ps.c	8.4 (Berkeley) 4/2/94";
42#endif /* not lint */
43
44#include <sys/param.h>
45#include <sys/user.h>
46#include <sys/time.h>
47#include <sys/resource.h>
48#include <sys/proc.h>
49#include <sys/stat.h>
50#include <sys/ioctl.h>
51#include <sys/sysctl.h>
52
53#include <ctype.h>
54#include <err.h>
55#include <errno.h>
56#include <fcntl.h>
57#include <kvm.h>
58#include <nlist.h>
59#include <paths.h>
60#include <stdio.h>
61#include <stdlib.h>
62#include <string.h>
63#include <unistd.h>
64
65#include "ps.h"
66
67#ifdef P_PPWAIT
68#define NEWVM
69#endif
70
71KINFO *kinfo;
72struct varent *vhead, *vtail;
73
74int	eval;			/* exit value */
75int	rawcpu;			/* -C */
76int	sumrusage;		/* -S */
77int	termwidth;		/* width of screen (0 == infinity) */
78int	totwidth;		/* calculated width of requested variables */
79
80static int needuser, needcomm, needenv;
81
82enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
83
84static char	*fmt __P((char **(*)(kvm_t *, const struct kinfo_proc *, int),
85		    KINFO *, char *, int));
86static char	*kludge_oldps_options __P((char *));
87static int	 pscomp __P((const void *, const void *));
88static void	 saveuser __P((KINFO *));
89static void	 scanvars __P((void));
90static void	 usage __P((void));
91
92char dfmt[] = "pid tt state time command";
93char jfmt[] = "user pid ppid pgid sess jobc state tt time command";
94char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command";
95char   o1[] = "pid";
96char   o2[] = "tt state time command";
97char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command";
98char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command";
99
100kvm_t *kd;
101
102int
103main(argc, argv)
104	int argc;
105	char *argv[];
106{
107	struct kinfo_proc *kp;
108	struct varent *vent;
109	struct winsize ws;
110	dev_t ttydev;
111	pid_t pid;
112	uid_t uid;
113	int all, ch, flag, i, fmt, lineno, nentries;
114	int prtheader, wflag, what, xflg;
115	char *nlistf, *memf, *swapf, errbuf[256];
116
117	if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
118	     ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
119	     ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) == -1) ||
120	     ws.ws_col == 0)
121		termwidth = 79;
122	else
123		termwidth = ws.ws_col - 1;
124
125	if (argc > 1)
126		argv[1] = kludge_oldps_options(argv[1]);
127
128	all = fmt = prtheader = wflag = xflg = 0;
129	pid = -1;
130	uid = (uid_t) -1;
131	ttydev = NODEV;
132	memf = nlistf = swapf = NULL;
133	while ((ch = getopt(argc, argv,
134	    "aCeghjLlM:mN:O:o:p:rSTt:uvW:wx")) != EOF)
135		switch((char)ch) {
136		case 'a':
137			all = 1;
138			break;
139		case 'e':			/* XXX set ufmt */
140			needenv = 1;
141			break;
142		case 'C':
143			rawcpu = 1;
144			break;
145		case 'g':
146			break;			/* no-op */
147		case 'h':
148			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
149			break;
150		case 'j':
151			parsefmt(jfmt);
152			fmt = 1;
153			jfmt[0] = '\0';
154			break;
155		case 'L':
156			showkey();
157			exit(0);
158		case 'l':
159			parsefmt(lfmt);
160			fmt = 1;
161			lfmt[0] = '\0';
162			break;
163		case 'M':
164			memf = optarg;
165			break;
166		case 'm':
167			sortby = SORTMEM;
168			break;
169		case 'N':
170			nlistf = optarg;
171			break;
172		case 'O':
173			parsefmt(o1);
174			parsefmt(optarg);
175			parsefmt(o2);
176			o1[0] = o2[0] = '\0';
177			fmt = 1;
178			break;
179		case 'o':
180			parsefmt(optarg);
181			fmt = 1;
182			break;
183		case 'p':
184			pid = atol(optarg);
185			xflg = 1;
186			break;
187		case 'r':
188			sortby = SORTCPU;
189			break;
190		case 'S':
191			sumrusage = 1;
192			break;
193		case 'T':
194			if ((optarg = ttyname(STDIN_FILENO)) == NULL)
195				errx(1, "stdin: not a terminal");
196			/* FALLTHROUGH */
197		case 't': {
198			struct stat sb;
199			char *ttypath, pathbuf[MAXPATHLEN];
200
201			if (strcmp(optarg, "co") == 0)
202				ttypath = _PATH_CONSOLE;
203			else if (*optarg != '/')
204				(void)snprintf(ttypath = pathbuf,
205				    sizeof(pathbuf), "%s%s", _PATH_TTY, optarg);
206			else
207				ttypath = optarg;
208			if (stat(ttypath, &sb) == -1)
209				err(1, "%s", ttypath);
210			if (!S_ISCHR(sb.st_mode))
211				errx(1, "%s: not a terminal", ttypath);
212			ttydev = sb.st_rdev;
213			break;
214		}
215		case 'u':
216			parsefmt(ufmt);
217			sortby = SORTCPU;
218			fmt = 1;
219			ufmt[0] = '\0';
220			break;
221		case 'v':
222			parsefmt(vfmt);
223			sortby = SORTMEM;
224			fmt = 1;
225			vfmt[0] = '\0';
226			break;
227		case 'W':
228			swapf = optarg;
229			break;
230		case 'w':
231			if (wflag)
232				termwidth = UNLIMITED;
233			else if (termwidth < 131)
234				termwidth = 131;
235			wflag++;
236			break;
237		case 'x':
238			xflg = 1;
239			break;
240		case '?':
241		default:
242			usage();
243		}
244	argc -= optind;
245	argv += optind;
246
247#define	BACKWARD_COMPATIBILITY
248#ifdef	BACKWARD_COMPATIBILITY
249	if (*argv) {
250		nlistf = *argv;
251		if (*++argv) {
252			memf = *argv;
253			if (*++argv)
254				swapf = *argv;
255		}
256	}
257#endif
258	/*
259	 * Discard setgid privileges if not the running kernel so that bad
260	 * guys can't print interesting stuff from kernel memory.
261	 */
262	if (nlistf != NULL || memf != NULL || swapf != NULL)
263		setgid(getgid());
264
265	kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf);
266	if (kd == 0)
267		errx(1, "%s", errbuf);
268
269	if (!fmt)
270		parsefmt(dfmt);
271
272	if (!all && ttydev == NODEV && pid == -1)  /* XXX - should be cleaner */
273		uid = getuid();
274
275	/*
276	 * scan requested variables, noting what structures are needed,
277	 * and adjusting header widths as appropiate.
278	 */
279	scanvars();
280	/*
281	 * get proc list
282	 */
283	if (uid != (uid_t) -1) {
284		what = KERN_PROC_UID;
285		flag = uid;
286	} else if (ttydev != NODEV) {
287		what = KERN_PROC_TTY;
288		flag = ttydev;
289	} else if (pid != -1) {
290		what = KERN_PROC_PID;
291		flag = pid;
292	} else {
293		what = KERN_PROC_ALL;
294		flag = 0;
295	}
296	/*
297	 * select procs
298	 */
299	if ((kp = kvm_getprocs(kd, what, flag, &nentries)) == 0)
300		errx(1, "%s", kvm_geterr(kd));
301	if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
302		err(1, NULL);
303	for (i = nentries; --i >= 0; ++kp) {
304		kinfo[i].ki_p = kp;
305		if (needuser)
306			saveuser(&kinfo[i]);
307	}
308	/*
309	 * print header
310	 */
311	printheader();
312	if (nentries == 0)
313		exit(0);
314	/*
315	 * sort proc list
316	 */
317	qsort(kinfo, nentries, sizeof(KINFO), pscomp);
318	/*
319	 * for each proc, call each variable output function.
320	 */
321	for (i = lineno = 0; i < nentries; i++) {
322		if (xflg == 0 && (KI_EPROC(&kinfo[i])->e_tdev == NODEV ||
323		    (KI_PROC(&kinfo[i])->p_flag & P_CONTROLT ) == 0))
324			continue;
325		for (vent = vhead; vent; vent = vent->next) {
326			(vent->var->oproc)(&kinfo[i], vent);
327			if (vent->next != NULL)
328				(void)putchar(' ');
329		}
330		(void)putchar('\n');
331		if (prtheader && lineno++ == prtheader - 4) {
332			(void)putchar('\n');
333			printheader();
334			lineno = 0;
335		}
336	}
337	exit(eval);
338}
339
340static void
341scanvars()
342{
343	struct varent *vent;
344	VAR *v;
345	int i;
346
347	for (vent = vhead; vent; vent = vent->next) {
348		v = vent->var;
349		i = strlen(v->header);
350		if (v->width < i)
351			v->width = i;
352		totwidth += v->width + 1;	/* +1 for space */
353		if (v->flag & USER)
354			needuser = 1;
355		if (v->flag & COMM)
356			needcomm = 1;
357	}
358	totwidth--;
359}
360
361static char *
362fmt(fn, ki, comm, maxlen)
363	char **(*fn) __P((kvm_t *, const struct kinfo_proc *, int));
364	KINFO *ki;
365	char *comm;
366	int maxlen;
367{
368	char *s;
369
370	if ((s =
371	    fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen)) == NULL)
372		err(1, NULL);
373	return (s);
374}
375
376static void
377saveuser(ki)
378	KINFO *ki;
379{
380	struct pstats pstats;
381	struct usave *usp;
382
383	usp = &ki->ki_u;
384	if (kvm_read(kd, (u_long)&KI_PROC(ki)->p_addr->u_stats,
385	    (char *)&pstats, sizeof(pstats)) == sizeof(pstats)) {
386		/*
387		 * The u-area might be swapped out, and we can't get
388		 * at it because we have a crashdump and no swap.
389		 * If it's here fill in these fields, otherwise, just
390		 * leave them 0.
391		 */
392		usp->u_start = pstats.p_start;
393		usp->u_ru = pstats.p_ru;
394		usp->u_cru = pstats.p_cru;
395		usp->u_valid = 1;
396	} else
397		usp->u_valid = 0;
398	/*
399	 * save arguments if needed
400	 */
401	if (needcomm)
402		ki->ki_args = fmt(kvm_getargv, ki, KI_PROC(ki)->p_comm,
403		    MAXCOMLEN);
404	else
405		ki->ki_args = NULL;
406	if (needenv)
407		ki->ki_env = fmt(kvm_getenvv, ki, (char *)NULL, 0);
408	else
409		ki->ki_env = NULL;
410}
411
412static int
413pscomp(a, b)
414	const void *a, *b;
415{
416	int i;
417#ifdef NEWVM
418#define VSIZE(k) (KI_EPROC(k)->e_vm.vm_dsize + KI_EPROC(k)->e_vm.vm_ssize + \
419		  KI_EPROC(k)->e_vm.vm_tsize)
420#else
421#define VSIZE(k) ((k)->ki_p->p_dsize + (k)->ki_p->p_ssize + (k)->ki_e->e_xsize)
422#endif
423
424	if (sortby == SORTCPU)
425		return (getpcpu((KINFO *)b) - getpcpu((KINFO *)a));
426	if (sortby == SORTMEM)
427		return (VSIZE((KINFO *)b) - VSIZE((KINFO *)a));
428	i =  KI_EPROC((KINFO *)a)->e_tdev - KI_EPROC((KINFO *)b)->e_tdev;
429	if (i == 0)
430		i = KI_PROC((KINFO *)a)->p_pid - KI_PROC((KINFO *)b)->p_pid;
431	return (i);
432}
433
434/*
435 * ICK (all for getopt), would rather hide the ugliness
436 * here than taint the main code.
437 *
438 *  ps foo -> ps -foo
439 *  ps 34 -> ps -p34
440 *
441 * The old convention that 't' with no trailing tty arg means the users
442 * tty, is only supported if argv[1] doesn't begin with a '-'.  This same
443 * feature is available with the option 'T', which takes no argument.
444 */
445static char *
446kludge_oldps_options(s)
447	char *s;
448{
449	size_t len;
450	char *newopts, *ns, *cp;
451
452	len = strlen(s);
453	if ((newopts = ns = malloc(len + 2)) == NULL)
454		err(1, NULL);
455	/*
456	 * options begin with '-'
457	 */
458	if (*s != '-')
459		*ns++ = '-';	/* add option flag */
460	/*
461	 * gaze to end of argv[1]
462	 */
463	cp = s + len - 1;
464	/*
465	 * if last letter is a 't' flag with no argument (in the context
466	 * of the oldps options -- option string NOT starting with a '-' --
467	 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
468	 */
469	if (*cp == 't' && *s != '-')
470		*cp = 'T';
471	else {
472		/*
473		 * otherwise check for trailing number, which *may* be a
474		 * pid.
475		 */
476		while (cp >= s && isdigit(*cp))
477			--cp;
478	}
479	cp++;
480	memmove(ns, s, (size_t)(cp - s));	/* copy up to trailing number */
481	ns += cp - s;
482	/*
483	 * if there's a trailing number, and not a preceding 'p' (pid) or
484	 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag.
485	 */
486	if (isdigit(*cp) && (cp == s || cp[-1] != 't' && cp[-1] != 'p' &&
487	    (cp - 1 == s || cp[-2] != 't')))
488		*ns++ = 'p';
489	(void)strcpy(ns, cp);		/* and append the number */
490
491	return (newopts);
492}
493
494static void
495usage()
496{
497
498	(void)fprintf(stderr,
499	    "usage:\t%s\n\t   %s\n\t%s\n",
500	    "ps [-aChjlmrSTuvwx] [-O|o fmt] [-p pid] [-t tty]",
501	    "[-M core] [-N system] [-W swap]",
502	    "ps [-L]");
503	exit(1);
504}
505