xref: /original-bsd/bin/ps/ps.c (revision 6ab384a1)
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1990 The Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)ps.c	5.29 (Berkeley) 08/30/90";
16 #endif /* not lint */
17 
18 #include <machine/pte.h>
19 
20 #include <sys/param.h>
21 #include <sys/ioctl.h>
22 #include <sys/tty.h>
23 #include <sys/user.h>
24 #include <sys/proc.h>
25 #include <sys/vm.h>
26 #include <sys/text.h>
27 #include <sys/stat.h>
28 #include <sys/mbuf.h>
29 #include <nlist.h>
30 #include <pwd.h>
31 #include <math.h>
32 #include <errno.h>
33 #include <stddef.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <varargs.h>
39 #include <kvm.h>
40 #include "pathnames.h"
41 
42 struct usave {
43 	struct	proc *u_procp;
44 	struct	timeval u_start;
45 	struct	rusage u_ru;
46 	struct	rusage u_cru;
47 	short	u_cmask;
48 	char	u_acflag;
49 };
50 
51 /*
52  * to compute offset in common structures
53  */
54 #define	POFF(x)		offsetof(struct proc, x)
55 #define	EOFF(x)		offsetof(struct eproc, x)
56 #define	UOFF(x)		offsetof(struct usave, x)
57 #define	ROFF(x)		offsetof(struct rusage, x)
58 
59 enum type	{ CHAR, UCHAR, SHORT, USHORT, LONG, ULONG, KPTR };
60 
61 #define	UIDFMT	"u"
62 #define UIDLEN	5
63 #define PIDFMT	"d"
64 #define PIDLEN	5
65 #define	USERLEN	8
66 
67 int needuser, needcomm, neednlist;
68 
69 int 	command(), ucomm(), logname(), pvar(), evar(), uvar(), rvar(), uname(),
70 	runame(), state(), pri(), tdev(), tname(), longtname(), started(),
71 	lstarted(), wchan(), vsize(), rssize(), p_rssize(), cputime(),
72 	pmem(), pcpu(), pagein(), maxrss(), tsize(), trss();
73 	/**
74 	utime(), stime(), ixrss(), idrss(), isrss();
75 	**/
76 
77 uid_t	getuid();
78 char	*ttyname();
79 
80 struct var {
81 	char	*name[4];	/* name(s) of variable */
82 	char	*header;	/* default header */
83 	int	flag;
84 #define	USER	0x01	/* requires user structure */
85 #define	LJUST	0x02	/* left adjust on output (trailing blanks) */
86 #define	COMM	0x04	/* requires exec arguments and environment (XXX) */
87 #define	NLIST	0x08	/* requires nlist to get extra variables */
88 	int	(*oproc)();	/* output routine */
89 	short	width;		/* printing width */
90 	/*
91 	 * The following (optional) elements are hooks for passing information
92 	 * to the generic output routines: pvar, evar, uvar (those which print
93 	 * simple elements from well known structures: proc, eproc, usave)
94 	 */
95 	int	off;		/* offset in structure */
96 	enum	type type;	/* type of element */
97 	char	*fmt;		/* printf format */
98 	/*
99 	 * glue to link selected fields together
100 	 */
101 	struct	var *next;
102 }  var[] = {
103 	{{"command", "comm", "args"}, "COMMAND", USER|LJUST|COMM,
104 		command, 16},
105 	{{"ucomm"}, "COMMAND",	LJUST, ucomm, MAXCOMLEN},
106 	{{"logname"}, "LOGNAME", LJUST, logname, MAXLOGNAME},
107 	{{"flag", "f"}, "F", 0, pvar, 7, POFF(p_flag), LONG, "x"},
108 	{{"uid"}, "UID", 0, pvar, UIDLEN, POFF(p_uid),USHORT, UIDFMT},
109 	{{"ruid"}, "RUID", 0, pvar, UIDLEN, POFF(p_ruid), USHORT, UIDFMT},
110 	{{"svuid"}, "SVUID", 0, pvar, UIDLEN, POFF(p_svuid), USHORT, UIDFMT},
111 	{{"rgid"}, "RGID", 0, pvar, UIDLEN, POFF(p_rgid), USHORT, UIDFMT},
112 	{{"svgid"}, "SVGID", 0, pvar, UIDLEN, POFF(p_svgid), USHORT, UIDFMT},
113 	{{"pid"}, "PID", 0, pvar, PIDLEN, POFF(p_pid),SHORT, PIDFMT},
114 	{{"ppid"}, "PPID", 0, pvar, PIDLEN, POFF(p_ppid), SHORT, PIDFMT},
115 	{{"cp", "cpu"}, "CP", 0, pvar, 3, POFF(p_cpu), UCHAR, "d"},
116 	{{"xstat"}, "XSTAT", 0, pvar, 4, POFF(p_xstat), USHORT, "x"},
117 	{{"poip"}, "POIP", 0, pvar, 4, POFF(p_poip), SHORT, "d"},
118 	{{"nwchan"}, "WCHAN", 0, pvar, 6, POFF(p_wchan), KPTR, "x"},
119 	{{"wchan"}, "WCHAN", LJUST, wchan, 6},
120 	{{"rlink"}, "RLINK", 0, pvar, 8, POFF(p_rlink), KPTR, "x"},
121 	{{"ktrace", "traceflag"}, "KTRACE",
122 		0, pvar, 8, POFF(p_traceflag), LONG, "x"},
123 	{{"ktracep", "tracep"}, "KTRACEP",
124 		0, pvar, 8, POFF(p_tracep), LONG, "x"},
125 	{{"sig", "pending"}, "PENDING",
126 		0, pvar, 8, POFF(p_sig), LONG, "x"},
127 	{{"sigmask", "blocked"}, "BLOCKED",
128 		0, pvar, 8, POFF(p_sigmask), LONG, "x"},
129 	{{"sigignore", "ignored"}, "IGNORED",
130 		0, pvar, 8, POFF(p_sigignore), LONG, "x"},
131 	{{"sigcatch", "caught"}, "CAUGHT",
132 		0, pvar, 8, POFF(p_sigcatch), LONG, "x"},
133 	{{"user", "uname"}, "USER", LJUST, uname, USERLEN},
134 	{{"ruser", "runame"}, "RUSER", LJUST, runame, USERLEN},
135 	{{"pgid"}, "PGID", 0, evar, PIDLEN, EOFF(e_pgid), USHORT, PIDFMT},
136 	{{"jobc"}, "JOBC", 0, evar, 4, EOFF(e_jobc), SHORT, "d"},
137 	{{"sess", "session"}, "SESS", 0, evar, 6, EOFF(e_sess), KPTR, "x"},
138 	{{"tdev", "dev"}, "TDEV", 0, tdev, 4},
139 	{{"tname", "tty", "tt"}, "TT", LJUST, tname, 3},
140 	{{"longtname", "longtty"}, "TT", LJUST, longtname, 8},
141 	{{"tpgid"}, "TPGID", 0, evar, 4, EOFF(e_tpgid), USHORT, PIDFMT},
142 	{{"tsession", "tsess"}, "TSESS",
143 		0, evar, 6, EOFF(e_tsess), KPTR, "x"},
144 	{{"paddr", "procaddr"}, "PADDR",
145 		0, evar, 6, EOFF(e_paddr), KPTR, "x"},
146 	{{"state", "stat"}, "STAT", 0, state, 4},
147 	{{"pri"}, "PRI", 0, pri, 3},
148 	{{"usrpri"}, "UPR", 0, pvar, 3, POFF(p_usrpri), CHAR, "d"},
149 	{{"nice", "ni"}, "NI", 0, pvar, 2, POFF(p_nice), CHAR, "d"},
150 	{{"vsize", "vsz"}, "VSZ", 0, vsize, 5},
151 	{{"rssize", "rsz"}, "RSZ", 0, rssize, 4},
152 	{{"rss", "p_rss"}, "RSS", 0, p_rssize, 4},
153 	{{"u_procp", "uprocp"}, "UPROCP",
154 		USER, uvar, 6, UOFF(u_procp), KPTR, "x"},
155 	{{"umask", "u_cmask"}, "UMASK",
156 		USER, uvar, 3, UOFF(u_cmask), CHAR, "#o"},
157 	{{"acflag", "acflg"}, "ACFLG",
158 		USER, uvar, 3, UOFF(u_acflag), SHORT, "x"},
159 	{{"start"}, "STARTED", USER|LJUST, started, 8},
160 	{{"lstart"}, "STARTED", USER|LJUST, lstarted, 28},
161 	{{"cputime", "time"}, "TIME", USER, cputime, 9},
162 	{{"p_ru"}, "P_RU", 0, pvar, 6, POFF(p_ru), KPTR, "x"},
163 	{{"pcpu", "%cpu"}, "%CPU", NLIST, pcpu, 4},
164 	{{"pmem", "%mem"}, "%MEM", NLIST, pmem, 4},
165 	{{"sl", "slp", "slptime"}, "SL",
166 		0, pvar, 3, POFF(p_slptime), CHAR, "d"},
167 	{{"re", "resident"}, "RE",
168 		0, pvar, 3, POFF(p_time), CHAR, "d"},
169 	{{"pagein", "majflt"}, "PAGEIN", USER, pagein, 6},
170 	{{"lim", "maxrss"}, "LIM", 0, maxrss, 5},
171 	{{"tsiz"}, "TSIZ", 0, tsize, 4},
172 	{{"trs"}, "TRS", 0, trss, 3},
173 	/***
174 	{{"utime"}, "UTIME", USER, utime, 4},
175 	{{"stime"}, "STIME", USER, stime, 4},
176 	{{"ixrss"}, "IXRSS", USER, ixrss, 4},
177 	{{"idrss"}, "IDRSS", USER, idrss, 4},
178 	{{"isrss"}, "ISRSS", USER, isrss, 4},
179 	***/
180 	{{"minflt"}, "MINFLT",
181 		USER, rvar, 4, ROFF(ru_minflt), LONG, "d"},
182 	{{"majflt"}, "MAJFLT",
183 		USER, rvar, 4, ROFF(ru_majflt), LONG, "d"},
184 	{{"nswap"}, "NSWAP",
185 		USER, rvar, 4, ROFF(ru_nswap), LONG, "d"},
186 	{{"inblock", "inblk"}, "INBLK",
187 		USER, rvar, 4, ROFF(ru_inblock), LONG, "d"},
188 	{{"oublock", "oublk"}, "OUBLK",
189 		USER, rvar, 4, ROFF(ru_oublock), LONG, "d"},
190 	{{"msgsnd"}, "MSGSND",
191 		USER, rvar, 4, ROFF(ru_msgsnd), LONG, "d"},
192 	{{"msgrcv"}, "MSGRCV",
193 		USER, rvar, 4, ROFF(ru_msgrcv), LONG, "d"},
194 	{{"nsignals", "nsigs"}, "NSIGS",
195 		USER, rvar, 4, ROFF(ru_nsignals), LONG, "d"},
196 	{{"nvcsw", "vcsw"}, "VCSW",
197 		USER, rvar, 5, ROFF(ru_nvcsw), LONG, "d"},
198 	{{"nivcsw", "ivcsw"}, "IVCSW",
199 		USER, rvar, 5, ROFF(ru_nivcsw), LONG, "d"},
200 	NULL
201 };
202 
203 /*
204  * combination variables
205  */
206 struct combovar {
207 	char *name;
208 	char *replace;
209 } combovar[] = {
210 	"RUSAGE", "minflt majflt nswap inblock oublock \
211 		msgsnd msgrcv nsigs nvcsw nivcsw",
212 	0, 0
213 };
214 #define DFMT	"pid tname state cputime comm"
215 #define LFMT \
216 	"uid pid ppid cp pri nice vsz rss wchan state tname cputime comm"
217 #define	JFMT	"user pid ppid pgid sess jobc state tname cputime comm"
218 #define	SFMT	"uid pid sig sigmask sigignore sigcatch stat tname comm"
219 #define	VFMT \
220 	"pid tt state time sl re pagein vsz rss lim tsiz trs %cpu %mem comm"
221 #define UFMT \
222 	"uname pid %cpu %mem vsz rss tt state start time comm"
223 
224 struct kinfo {
225 	struct proc *ki_p;	/* proc structure */
226 	struct eproc *ki_e;	/* extra stuff */
227 	struct usave *ki_u;	/* interesting parts of user */
228 	char *ki_args;		/* exec args (should be char **) */
229 	char *ki_env;		/* environment (should be char **) */
230 } *kinfo;
231 
232 struct	var *vhead, *vtail;
233 int	termwidth;	/* width of screen (0 == infinity) */
234 #define UNLIMITED	0
235 int	totwidth;	/* calculated width of requested variables */
236 int	sumrusage;
237 int	rawcpu;
238 int	sortby;
239 #define	SORTMEM	1
240 #define	SORTCPU 2
241 
242 int	uid = -1;
243 dev_t	ttydev = NODEV;
244 int	pid = -1;
245 int	all;
246 int	xflg;
247 int	prtheader;
248 int	lineno;
249 
250 /*
251  * variables retrieved via nlist
252  */
253 struct	nlist psnl[] = {
254 	{"_ecmx"},
255 #define	X_ECMX		0
256 	{"_fscale"},
257 #define	X_FSCALE	1
258 	{"_ccpu"},
259 #define	X_CCPU		2
260 	{NULL}
261 };
262 int	fscale;
263 int	ecmx;
264 fixpt_t	ccpu;
265 
266 #define USAGE	"ps [ -(o|O) fmt ] [ -wlvujnsaxSCLmcr ] [ -p pid ] [ -t tty ]"
267 
268 main (argc, argv)
269 	char *argv[];
270 {
271 	extern char *optarg;
272 	extern int optind;
273 	int ch;
274 	register int i;
275 	register struct var *v;
276 	register struct proc *p;
277 	struct winsize ws;
278 	size_t nentries;
279 	int fmt = 0;
280 	int pscomp();
281 	int what, flag;
282 	char *kludge_oldps_options();
283 
284 	if ((ioctl(1, TIOCGWINSZ, (char *)&ws) == -1 &&
285 	     ioctl(2, TIOCGWINSZ, (char *)&ws) == -1 &&
286 	     ioctl(0, TIOCGWINSZ, (char *)&ws) == -1) ||
287 	     ws.ws_col == 0)
288 		termwidth = 79;
289 	else
290 		termwidth = ws.ws_col - 1;
291 	if (argc > 1)
292 		argv[1] = kludge_oldps_options(argv[1]);
293 
294 	while ((ch = getopt(argc, argv, "o:O:wlvujnsaxt:p:SCLmrhTg")) != EOF)
295 		switch((char)ch) {
296 		case 'o':
297 			parsefmt(optarg);
298 			fmt++;
299 			break;
300 		case 'O':
301 			parsefmt("pid");
302 			parsefmt(optarg);
303 			parsefmt("state tt time command");
304 			fmt++;
305 			break;
306 		case 'w':
307 			if (termwidth < 131)
308 				termwidth = 131;
309 			else
310 				termwidth = UNLIMITED;
311 			break;
312 		case 'l':
313 			parsefmt(LFMT);
314 			fmt++;
315 			break;
316 		case 'v':
317 			parsefmt(VFMT);
318 			sortby = SORTMEM;
319 			fmt++;
320 			break;
321 		case 'u':
322 			parsefmt(UFMT);
323 			sortby = SORTCPU;
324 			fmt++;
325 			break;
326 		case 'j':
327 			parsefmt(JFMT);
328 			fmt++;
329 			break;
330 		case 's':
331 			parsefmt(SFMT);
332 			fmt++;
333 			break;
334 		case 'T':
335 			if ((optarg = ttyname(0)) == NULL)
336 				error("<stdin>: not a terminal");
337 			/* FALLTHROUGH */
338 		case 't': {
339 			char *ttypath;
340 			struct stat stbuf;
341 			char pathbuf[MAXPATHLEN];
342 
343 			if (strcmp(optarg, "co") == 0)
344 				ttypath = _PATH_CONSOLE;
345 			else if (*optarg != '/')
346 				(void) sprintf(ttypath = pathbuf, "%s%s",
347 				    _PATH_TTY, optarg);
348 			else
349 				ttypath = optarg;
350 			if (stat(ttypath, &stbuf) == -1)
351 				syserror(ttypath);
352 			if ((stbuf.st_mode & S_IFMT) != S_IFCHR)
353 				error("%s: not a terminal", ttypath);
354 			ttydev = stbuf.st_rdev;
355 			break;
356 		}
357 		case 'p':
358 			pid = atoi(optarg);
359 			xflg++;
360 			break;
361 		case 'S':
362 			sumrusage++;
363 			break;
364 		case 'C':
365 			rawcpu++;
366 			break;
367 		case 'L': {
368 			struct combovar *cb = &combovar[0];
369 			char *cp, *sep = "";
370 
371 			for (i = 0, v = &var[0];;) {
372 				if (v->name[0] != NULL)
373 					cp = v++->name[0];
374 				else if (cb->name != NULL)
375 					cp = cb++->name;
376 				else
377 					break;
378 				if (termwidth &&
379 				    (i += strlen(cp) + 1) > termwidth)
380 					i = strlen(cp), sep = "\n";
381 				(void) printf("%s%s", sep, cp);
382 				sep = " ";
383 			}
384 			(void) printf("\n");
385 			exit(0);
386 		}
387 		case 'a':
388 			all++;
389 			break;
390 		case 'x':
391 			xflg++;
392 			break;
393 		case 'm':
394 			sortby = SORTMEM;
395 			break;
396 		case 'r':
397 			sortby = SORTCPU;
398 			break;
399 		case 'h':
400 			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
401 			break;
402 		case 'g':
403 			break;	/* no-op */
404 		case '?':
405 		default:
406 			(void) fprintf(stderr, "usage: %s\n", USAGE);
407 			exit(1);
408 		}
409 	argc -= optind;
410 	argv += optind;
411 
412 	if (*argv) {
413 		char *nlistf, *memf = NULL, *swapf = NULL;
414 
415 		nlistf = *argv++;
416 		if (*argv) {
417 			memf = *argv++;
418 			if (*argv)
419 				swapf = *argv++;
420 		}
421 		if (kvm_openfiles(nlistf, memf, swapf) == -1)
422 			error("kvm_openfiles: %s", kvm_geterr());
423 	}
424 
425 	if (!fmt)
426 		parsefmt(DFMT);
427 
428 	if (!all && ttydev == NODEV && pid == -1)  /* XXX - should be cleaner */
429 		uid = getuid();
430 
431 	/*
432 	 * scan requested variables, noting what structures are needed,
433 	 * and adjusting header widths as appropiate.
434 	 */
435 	scanvars();
436 #ifdef notdef
437 	if (sortby == SORTCPU)
438 		neednlist = 1;
439 #endif
440 	if (neednlist)
441 		donlist();
442 	/*
443 	 * get proc list
444 	 */
445 	if (uid != -1) {
446 		what = KINFO_PROC_UID;
447 		flag = uid;
448 	} else if (ttydev != NODEV) {
449 		what = KINFO_PROC_TTY;
450 		flag = ttydev;
451 	} else if (pid != -1) {
452 		what = KINFO_PROC_PID;
453 		flag = pid;
454 	} else
455 		what = KINFO_PROC_ALL;
456 	/*
457 	 * select procs
458 	 */
459 	if ((nentries = kvm_getprocs(what, flag)) == -1) {
460 		(void) fprintf(stderr, "ps: %s\n", kvm_geterr());
461 		exit(1);
462 	}
463 	kinfo = (struct kinfo *)malloc(nentries * sizeof (struct kinfo));
464 	if (kinfo == NULL)
465 		error("out of memory");
466 	i = 0;
467 	while ((p = kvm_nextproc()) != NULL) {
468 		kinfo[i].ki_p = p;
469 		kinfo[i].ki_e = kvm_geteproc(p);
470 		if (needuser)
471 			saveuser(&kinfo[i]);
472 		i++;
473 	}
474 	nentries = i;
475 	/*
476 	 * print header
477 	 */
478 	printheader();
479 	if (nentries == 0)
480 		exit(0);
481 	/*
482 	 * sort proc list
483 	 */
484 	qsort((void *)kinfo, nentries, sizeof (struct kinfo), pscomp);
485 	/*
486 	 * for each proc, call each variable output function.
487 	 */
488 	for (i = 0; i < nentries; i++) {
489 		if (xflg == 0 && (kinfo[i].ki_e->e_tdev == NODEV ||
490 		    (kinfo[i].ki_p->p_flag & SCTTY ) == 0))
491 			continue;
492 		for (v = vhead; v != NULL; v = v->next) {
493 			(*v->oproc)(&kinfo[i], v);
494 			if (v->next != NULL)
495 				(void) putchar(' ');
496 		}
497 		(void) putchar('\n');
498 		if (prtheader && lineno++ == prtheader-4) {
499 			(void) putchar('\n');
500 			printheader();
501 			lineno = 0;
502 		}
503 	}
504 
505 	exit(0);
506 }
507 
508 #define FMTSEP	" \t,\n"
509 
510 parsefmt(fmt)
511 	char *fmt;
512 {
513 	register char *cp, *hp;
514 	struct var *v;
515 	char *lookupcombo();
516 	struct var *lookupvar();
517 
518 	while (fmt) {
519 		while ((cp = strsep(&fmt, FMTSEP)) != NULL && *cp == '\0')
520 			/* void */;
521 		if ((hp = lookupcombo(cp)) != NULL) {
522 			parsefmt(hp);
523 			continue;
524 		}
525 		if ((hp = index(cp, '=')) != NULL)
526 			*hp++ = '\0';
527 		v = lookupvar(cp);
528 		if (v == NULL)
529 			error("unknown variable in format: %s", cp);
530 		if (v->next != NULL || vtail == v) {
531 			(void) fprintf(stderr,
532 			    "ps: can't specify a variable twice: %s\n", cp);
533 			continue;
534 		}
535 		if (hp)
536 			v->header = hp;
537 		if (vhead == NULL)
538 			vhead = vtail = v;
539 		else {
540 			vtail->next = v;
541 			vtail = v;
542 		}
543 	}
544 }
545 
546 scanvars()
547 {
548 	register i;
549 	register struct var *v;
550 
551 	for (v = vhead; v != NULL; v = v->next) {
552 		i = strlen(v->header);
553 		if (v->width < i)
554 			v->width = i;
555 		totwidth += v->width + 1;	/* +1 for space */
556 		if (v->flag & USER)
557 			needuser = 1;
558 		if (v->flag & COMM)
559 			needcomm = 1;
560 		if (v->flag & NLIST)
561 			neednlist = 1;
562 	}
563 	totwidth--;
564 }
565 
566 printheader()
567 {
568 	register struct var *v;
569 
570 	for (v = vhead; v != NULL; v = v->next) {
571 		if (v->flag & LJUST) {
572 			if (v->next == NULL)	/* last one */
573 				(void) printf("%s", v->header);
574 			else
575 				(void) printf("%-*s", v->width, v->header);
576 		} else
577 			(void) printf("%*s", v->width, v->header);
578 		if (v->next != NULL)
579 			(void) putchar(' ');
580 	}
581 	(void) putchar('\n');
582 }
583 
584 command(k, v)
585 	struct kinfo *k;
586 	struct var *v;
587 {
588 
589 	if (v->next == NULL) {
590 		/* last field */
591 		if (termwidth == UNLIMITED)
592 			(void) printf("%s", k->ki_args);
593 		else {
594 			register int left = termwidth - (totwidth - v->width);
595 			register char *cp = k->ki_args;
596 
597 			if (left < 1) /* already wrapped, just use std width */
598 				left = v->width;
599 			while (--left >= 0 && *cp)
600 				(void) putchar(*cp++);
601 		}
602 	} else
603 		(void) printf("%-*.*s", v->width, v->width, k->ki_args);
604 
605 }
606 
607 ucomm(k, v)
608 	struct kinfo *k;
609 	struct var *v;
610 {
611 
612 	(void) printf("%-*s", v->width, k->ki_p->p_comm);
613 }
614 
615 logname(k, v)
616 	struct kinfo *k;
617 	struct var *v;
618 {
619 
620 	(void) printf("%-*s", v->width, k->ki_p->p_logname);
621 }
622 
623 state(k, v)
624 	struct kinfo *k;
625 	struct var *v;
626 {
627 	char buf[16];
628 	register char *cp = buf;
629 	register struct proc *p = k->ki_p;
630 	register flag = p->p_flag;
631 
632 	switch (p->p_stat) {
633 
634 	case SSTOP:
635 		*cp = 'T';
636 		break;
637 
638 	case SSLEEP:
639 		if (flag & SSINTR)	/* interuptable (long) */
640 			*cp = p->p_slptime >= MAXSLP ? 'I' : 'S';
641 		else
642 			*cp = (flag & SPAGE) ? 'P' : 'D';
643 		break;
644 
645 	case SRUN:
646 	case SIDL:
647 		*cp = 'R';
648 		break;
649 
650 	case SZOMB:
651 		*cp = 'Z';
652 		break;
653 
654 	default:
655 		*cp = '?';
656 	}
657 	cp++;
658 	if (flag & SLOAD) {
659 		if (p->p_rssize > p->p_maxrss)
660 			*cp++ = '>';
661 	} else
662 		*cp++ = 'W';
663 	if (p->p_nice < NZERO)
664 		*cp++ = '<';
665 	else if (p->p_nice > NZERO)
666 		*cp++ = 'N';
667 	if (flag & SUANOM)
668 		*cp++ = 'A';
669 	else if (flag & SSEQL)
670 		*cp++ = 'S';
671 	if (flag & STRC)
672 		*cp++ = 'X';
673 	if (flag & SWEXIT)
674 		*cp++ = 'E';
675 	if (flag & SVFORK)
676 		*cp++ = 'V';
677 	if (flag & (SSYS|SLOCK|SULOCK|SKEEP|SPHYSIO))
678 		*cp++ = 'L';
679 	if (k->ki_e->e_flag & EPROC_SLEADER)
680 		*cp++ = 's';
681 	if ((flag & SCTTY) && k->ki_e->e_pgid == k->ki_e->e_tpgid)
682 		*cp++ = '+';
683 	*cp = '\0';
684 	(void) printf("%-*s", v->width, buf);
685 }
686 
687 pri(k, v)
688 	struct kinfo *k;
689 	struct var *v;
690 {
691 
692 	(void) printf("%*d", v->width, k->ki_p->p_pri - PZERO);
693 }
694 
695 uname(k, v)
696 	struct kinfo *k;
697 	struct var *v;
698 {
699 
700 	(void) printf("%-*s", v->width, user_from_uid(k->ki_p->p_uid, 0));
701 }
702 
703 runame(k, v)
704 	struct kinfo *k;
705 	struct var *v;
706 {
707 
708 	(void) printf("%-*s", v->width, user_from_uid(k->ki_p->p_ruid, 0));
709 }
710 
711 tdev(k, v)
712 	struct kinfo *k;
713 	struct var *v;
714 {
715 	dev_t dev = k->ki_e->e_tdev;
716 
717 	if (dev == NODEV)
718 		(void) printf("%*s", v->width, "??");
719 	else {
720 		char buff[16];
721 
722 		(void) sprintf(buff, "%d/%d", major(dev), minor(dev));
723 		(void) printf("%*s", v->width, buff);
724 	}
725 }
726 
727 extern char *devname();
728 
729 tname(k, v)
730 	struct kinfo *k;
731 	struct var *v;
732 {
733 	dev_t dev = k->ki_e->e_tdev;
734 	char *ttname;
735 
736 	if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL)
737 		(void) printf("%-*s", v->width, "??");
738 	else {
739 		if (strncmp(ttname, "tty", 3) == 0)
740 			ttname += 3;
741 		(void) printf("%*.*s%c", v->width-1, v->width-1, ttname,
742 			k->ki_e->e_flag & EPROC_CTTY ? ' ' : '-');
743 	}
744 }
745 
746 longtname(k, v)
747 	struct kinfo *k;
748 	struct var *v;
749 {
750 	dev_t dev = k->ki_e->e_tdev;
751 	char *ttname;
752 
753 	if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL)
754 		(void) printf("%-*s", v->width, "??");
755 	else
756 		(void) printf("%-*s", v->width, ttname);
757 }
758 
759 #include <sys/time.h>
760 
761 started(k, v)
762 	struct kinfo *k;
763 	struct var *v;
764 {
765 	extern char *attime();
766 
767 	(void) printf("%-*s", v->width, k->ki_u ?
768 		attime(&k->ki_u->u_start.tv_sec) : "-");
769 
770 }
771 
772 lstarted(k, v)
773 	struct kinfo *k;
774 	struct var *v;
775 {
776 	extern char *ctime();
777 	char *tp;
778 
779 	if (k->ki_u)
780 		(tp = ctime(&k->ki_u->u_start.tv_sec))[24] = '\0';
781 	else
782 		tp = "-";
783 	(void) printf("%-*s", v->width, tp);
784 }
785 
786 wchan(k, v)
787 	struct kinfo *k;
788 	struct var *v;
789 {
790 
791 	if (k->ki_p->p_wchan) {
792 		if (k->ki_p->p_pri > PZERO)
793 			(void) printf("%-*.*s", v->width, v->width, k->ki_e->e_wmesg);
794 		else
795 			(void) printf("%*x", v->width,
796 				(int)k->ki_p->p_wchan &~ KERNBASE);
797 	} else
798 		(void) printf("%-*s", v->width, "-");
799 }
800 
801 #define pgtok(a)        (((a)*NBPG)/1024)
802 #define pgtok(a)        (((a)*NBPG)/1024)
803 
804 vsize(k, v)
805 	struct kinfo *k;
806 	struct var *v;
807 {
808 
809 	(void) printf("%*d", v->width,
810 		pgtok(k->ki_p->p_dsize + k->ki_p->p_ssize + k->ki_e->e_xsize));
811 }
812 
813 rssize(k, v)
814 	struct kinfo *k;
815 	struct var *v;
816 {
817 
818 	(void) printf("%*d", v->width,
819 		pgtok(k->ki_p->p_rssize + (k->ki_e->e_xccount ?
820 		      (k->ki_e->e_xrssize / k->ki_e->e_xccount) : 0)));
821 }
822 
823 p_rssize(k, v)		/* doesn't account for text */
824 	struct kinfo *k;
825 	struct var *v;
826 {
827 
828 	(void) printf("%*d", v->width, pgtok(k->ki_p->p_rssize));
829 }
830 
831 cputime(k, v)
832 	struct kinfo *k;
833 	struct var *v;
834 {
835 	long secs;
836 	long psecs;	/* "parts" of a second. first micro, then centi */
837 	char obuff[128];
838 
839 	if (k->ki_p->p_stat == SZOMB || k->ki_u == NULL) {
840 		secs = 0;
841 		psecs = 0;
842 	} else {
843 		secs = k->ki_p->p_utime.tv_sec +
844 			k->ki_p->p_stime.tv_sec;
845 		psecs = k->ki_p->p_utime.tv_usec +
846 			k->ki_p->p_stime.tv_usec;
847 		if (sumrusage) {
848 			secs += k->ki_u->u_cru.ru_utime.tv_sec +
849 				k->ki_u->u_cru.ru_stime.tv_sec;
850 			psecs += k->ki_u->u_cru.ru_utime.tv_usec +
851 				k->ki_u->u_cru.ru_stime.tv_usec;
852 		}
853 		/*
854 		 * round and scale to 100's
855 		 */
856 		psecs = (psecs + 5000) / 10000;
857 		if (psecs >= 100) {
858 			psecs -= 100;
859 			secs++;
860 		}
861 	}
862 	(void) sprintf(obuff, "%3ld:%02ld.%02ld", secs/60, secs%60, psecs);
863 	(void) printf("%*s", v->width, obuff);
864 }
865 
866 double
867 getpcpu(k)
868 	struct kinfo *k;
869 {
870 	/*
871 	 * note: this routine requires ccpu and fscale
872 	 * be initialized.  If you call this routine from
873 	 * somewhere new, insure that the "neednlist" flag
874 	 * gets set.
875 	 */
876 	struct proc *p = k->ki_p;
877 #define	fxtofl(fixpt)	((double)(fixpt) / fscale)
878 
879 	if (p->p_time == 0 || (p->p_flag & SLOAD) == 0)	/* XXX - I don't like this */
880 		return (0.0);
881 	if (rawcpu)
882 		return (100.0 * fxtofl(p->p_pctcpu));
883 	return (100.0 * fxtofl(p->p_pctcpu) /
884 		(1.0 - exp(p->p_time * log(fxtofl(ccpu)))));
885 }
886 
887 pcpu(k, v)
888 	struct kinfo *k;
889 	struct var *v;
890 {
891 
892 	(void) printf("%*.1f", v->width, getpcpu(k));
893 }
894 
895 double
896 getpmem(k)
897 	struct kinfo *k;
898 {
899 	struct proc *p = k->ki_p;
900 	struct eproc *e = k->ki_e;
901 	double fracmem;
902 	int szptudot;
903 	/*
904 	 * note: this routine requires that ecmx
905 	 * be initialized.  If you call this routine from
906 	 * somewhere new, insure that the "neednlist" flag
907 	 * gets set.
908 	 */
909 
910 	if ((p->p_flag & SLOAD) == 0)
911 		return (0.0);
912 	szptudot = UPAGES + clrnd(ctopt(p->p_dsize + p->p_ssize + e->e_xsize));
913 	fracmem = ((float)p->p_rssize + szptudot)/CLSIZE/ecmx;
914 	if (p->p_textp && e->e_xccount)
915 		fracmem += ((float)e->e_xrssize)/CLSIZE/e->e_xccount/ecmx;
916 	return (100.0 * fracmem);
917 }
918 
919 pmem(k, v)
920 	struct kinfo *k;
921 	struct var *v;
922 {
923 
924 	(void) printf("%*.1f", v->width, getpmem(k));
925 }
926 
927 pagein(k, v)
928 	struct kinfo *k;
929 	struct var *v;
930 {
931 
932 	(void) printf("%*d", v->width, k->ki_u ? k->ki_u->u_ru.ru_majflt : 0);
933 }
934 
935 maxrss(k, v)
936 	struct kinfo *k;
937 	struct var *v;
938 {
939 
940 	if (k->ki_p->p_maxrss != (RLIM_INFINITY/NBPG))
941 		(void) printf("%*d", v->width, pgtok(k->ki_p->p_maxrss));
942 	else
943 		(void) printf("%*s", v->width, "-");
944 }
945 
946 tsize(k, v)
947 	struct kinfo *k;
948 	struct var *v;
949 {
950 
951 	(void) printf("%*d", v->width, pgtok(k->ki_e->e_xsize));
952 }
953 
954 trss(k, v)
955 	struct kinfo *k;
956 	struct var *v;
957 {
958 
959 	(void) printf("%*d", v->width, pgtok(k->ki_e->e_xrssize));
960 }
961 
962 /*
963  * Generic output routines.  Print fields from various prototype
964  * structures.
965  */
966 pvar(k, v)
967 	struct kinfo *k;
968 	struct var *v;
969 {
970 
971 	printval((char *)((char *)k->ki_p + v->off), v);
972 }
973 
974 evar(k, v)
975 	struct kinfo *k;
976 	struct var *v;
977 {
978 
979 	printval((char *)((char *)k->ki_e + v->off), v);
980 }
981 
982 uvar(k, v)
983 	struct kinfo *k;
984 	struct var *v;
985 {
986 
987 	if (k->ki_u)
988 		printval((char *)((char *)k->ki_u + v->off), v);
989 	else
990 		(void) printf("%*s", v->width, "-");
991 }
992 
993 rvar(k, v)
994 	struct kinfo *k;
995 	struct var *v;
996 {
997 
998 	if (k->ki_u)
999 		printval((char *)((char *)(&k->ki_u->u_ru) + v->off), v);
1000 	else
1001 		(void) printf("%*s", v->width, "-");
1002 }
1003 
1004 char *
1005 lookupcombo(cp)
1006 	char *cp;
1007 {
1008 	register struct combovar *cv = &combovar[0];
1009 
1010 	for (; cv->name; cv++)
1011 		if (strcmp(cp, cv->name) == 0)
1012 			return (cv->replace);
1013 	return (NULL);
1014 }
1015 
1016 struct var *
1017 lookupvar(cp)
1018 	char *cp;
1019 {
1020 	register int i, j;
1021 
1022 	for (i=0; var[i].name[0] != NULL; i++)
1023 		for (j=0; var[i].name[j] != NULL; j++)
1024 			if (strcmp(cp, var[i].name[j]) == 0)
1025 				return (&var[i]);
1026 	return (NULL);
1027 }
1028 
1029 printval(bp, v)
1030 	char *bp;
1031 	struct var *v;
1032 {
1033 	static char ofmt[32] = "%";
1034 	register char *cp = ofmt+1, *fcp = v->fmt;
1035 
1036 	if (v->flag & LJUST)
1037 		*cp++ = '-';
1038 	*cp++ = '*';
1039 	while (*cp++ = *fcp++)
1040 		;
1041 
1042 	switch (v->type) {
1043 	case CHAR:
1044 		(void) printf(ofmt, v->width, *(char *)bp);
1045 		break;
1046 
1047 	case UCHAR:
1048 		(void) printf(ofmt, v->width, *(u_char *)bp);
1049 		break;
1050 
1051 	case SHORT:
1052 		(void) printf(ofmt, v->width, *(short *)bp);
1053 		break;
1054 
1055 	case USHORT:
1056 		(void) printf(ofmt, v->width, *(u_short *)bp);
1057 		break;
1058 
1059 	case LONG:
1060 		(void) printf(ofmt, v->width, *(long *)bp);
1061 		break;
1062 
1063 	case ULONG:
1064 		(void) printf(ofmt, v->width, *(u_long *)bp);
1065 		break;
1066 
1067 	case KPTR:
1068 		(void) printf(ofmt, v->width, *(u_long *)bp &~ KERNBASE);
1069 		break;
1070 
1071 	default:
1072 		error("unknown type %d", v->type);
1073 	}
1074 }
1075 
1076 /* XXX - redo */
1077 saveuser(ki)
1078 	struct kinfo *ki;
1079 {
1080 	register struct usave *usp;
1081 	register struct user *up;
1082 
1083 	if ((usp = (struct usave *)calloc(1, sizeof (struct usave))) == NULL) {
1084 		(void) fprintf(stderr, "ps: out of memory\n");
1085 		exit(1);
1086 	}
1087 	ki->ki_u = usp;
1088 	up = kvm_getu(ki->ki_p);
1089 	/*
1090 	 * save arguments if needed
1091 	 */
1092 	if (needcomm)
1093 		ki->ki_args = strdup(kvm_getargs(ki->ki_p, up));
1094 	else
1095 		ki->ki_args = NULL;
1096 	if (up != NULL) {
1097 		/*
1098 		 * save important fields
1099 		 */
1100 		usp->u_procp = up->u_procp;
1101 		usp->u_start = up->u_start;
1102 		usp->u_ru = up->u_ru;
1103 		usp->u_cru = up->u_cru;
1104 		usp->u_cmask = up->u_cmask;
1105 		usp->u_acflag = up->u_acflag;
1106 	}
1107 }
1108 
1109 
1110 pscomp(k1, k2)
1111 	struct kinfo *k1, *k2;
1112 {
1113 	int i;
1114 #define VSIZE(k) ((k)->ki_p->p_dsize + (k)->ki_p->p_ssize + (k)->ki_e->e_xsize)
1115 
1116 	if (sortby == SORTCPU)
1117 		return (getpcpu(k2) - getpcpu(k1));
1118 #ifdef notyet
1119 	if (sortby == SORTRUN)
1120 		return (proc_compare(k1->ki_p, k2->ki_p));
1121 #endif
1122 	if (sortby == SORTMEM)
1123 		return (VSIZE(k2) - VSIZE(k1));
1124 	i =  k1->ki_e->e_tdev - k2->ki_e->e_tdev;
1125 	if (i == 0)
1126 		i = k1->ki_p->p_pid - k2->ki_p->p_pid;
1127 	return (i);
1128 }
1129 
1130 donlist()
1131 {
1132 #define kread(x, v) \
1133 	kvm_read(psnl[x].n_value, (char *)&v, sizeof v) != sizeof(v)
1134 
1135 	if (kvm_nlist(psnl) != 0)
1136 		error("can't get namelist");
1137 	if (kread(X_FSCALE, fscale))
1138 		error("error reading fscale: %s", kvm_geterr());
1139 	if (kread(X_ECMX, ecmx))
1140 		error("error reading ecmx: %s", kvm_geterr());
1141 	if (kread(X_CCPU, ccpu))
1142 		error("error reading ccpu: %s", kvm_geterr());
1143 #undef kread
1144 }
1145 
1146 #ifdef lint
1147 /* VARARGS1 */
1148 error(fmt) char *fmt; { (void) fputs(fmt, stderr); exit(1); /* NOTREACHED */ }
1149 #else
1150 error(va_alist)
1151 	va_dcl
1152 {
1153 	char *fmt;
1154 	va_list ap;
1155 
1156 	va_start(ap);
1157 	fmt = va_arg(ap, char *);
1158 	(void) fprintf(stderr, "ps: ");
1159 	(void) vfprintf(stderr, fmt, ap);
1160 	(void) fprintf(stderr, "\n");
1161 	exit(1);
1162 }
1163 #endif
1164 
1165 syserror(a)
1166 	char *a;
1167 {
1168 	extern errno;
1169 
1170 	error("%s: %s", a, strerror(errno));
1171 }
1172 
1173 /*
1174  * ICK (all for getopt), would rather hide the ugliness
1175  * here than taint the main code.
1176  *
1177  *  ps foo -> ps -foo
1178  *  ps 34 -> ps -p34
1179  *
1180  * The old convention that 't' with no trailing tty arg means the users
1181  * tty, is only supported if argv[1] doesn't begin with a '-'.  This same
1182  * feature is available with the option 'T', which takes no argument.
1183  */
1184 char *
1185 kludge_oldps_options(s)
1186 	char *s;
1187 {
1188 	size_t len = strlen(s);
1189 	char *newopts, *ns, *cp;
1190 
1191 	if ((newopts = ns = malloc(len + 2)) == NULL)
1192 		error("out of memory");
1193 	/*
1194 	 * options begin with '-'
1195 	 */
1196 	if (*s != '-')
1197 		*ns++ = '-';	/* add option flag */
1198 	/*
1199 	 * gaze to end of argv[1]
1200 	 */
1201 	cp = s + len - 1;
1202 	/*
1203 	 * if last letter is a 't' flag with no argument (in the context
1204 	 * of the oldps options -- option string NOT starting with a '-' --
1205 	 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
1206 	 */
1207 	if (*cp == 't' && *s != '-')
1208 		*cp = 'T';
1209 	else {
1210 		/*
1211 		 * otherwise check for trailing number, which *may* be a
1212 		 * pid.
1213 		 */
1214 		while (cp >= s && isdigit(*cp))
1215 			--cp;
1216 	}
1217 	cp++;
1218 	bcopy(s, ns, (size_t)(cp - s));	/* copy up to trailing number */
1219 	ns += cp - s;
1220 	/*
1221 	 * if there's a trailing number, and not a preceding 'p' (pid) or
1222 	 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag.
1223 	 */
1224 	if (isdigit(*cp) && (cp == s || cp[-1] != 't' && cp[-1] != 'p' &&
1225 	    (cp - 1 == s || cp[-2] != 't')))
1226 		*ns++ = 'p';
1227 	(void) strcpy(ns, cp);		/* and append the number */
1228 
1229 	return (newopts);
1230 }
1231