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