xref: /netbsd-src/bin/ps/print.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: print.c,v 1.90 2004/04/23 02:58:27 simonb Exp $	*/
2 
3 /*
4  * Copyright (c) 2000 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Simon Burge.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Copyright (c) 1990, 1993, 1994
41  *	The Regents of the University of California.  All rights reserved.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  * 3. Neither the name of the University nor the names of its contributors
52  *    may be used to endorse or promote products derived from this software
53  *    without specific prior written permission.
54  *
55  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65  * SUCH DAMAGE.
66  */
67 
68 #include <sys/cdefs.h>
69 #ifndef lint
70 #if 0
71 static char sccsid[] = "@(#)print.c	8.6 (Berkeley) 4/16/94";
72 #else
73 __RCSID("$NetBSD: print.c,v 1.90 2004/04/23 02:58:27 simonb Exp $");
74 #endif
75 #endif /* not lint */
76 
77 #include <sys/param.h>
78 #include <sys/time.h>
79 #include <sys/resource.h>
80 #include <sys/lwp.h>
81 #include <sys/proc.h>
82 #include <sys/stat.h>
83 #include <sys/ucred.h>
84 #include <sys/sysctl.h>
85 
86 #include <err.h>
87 #include <grp.h>
88 #include <kvm.h>
89 #include <math.h>
90 #include <nlist.h>
91 #include <pwd.h>
92 #include <stddef.h>
93 #include <stdio.h>
94 #include <stdlib.h>
95 #include <string.h>
96 #include <time.h>
97 #include <tzfile.h>
98 #include <unistd.h>
99 
100 #include "ps.h"
101 
102 static char *cmdpart(char *);
103 static void  printval(void *, VAR *, int);
104 static int   titlecmp(char *, char **);
105 
106 static void  doubleprintorsetwidth(VAR *, double, int, int);
107 static void  intprintorsetwidth(VAR *, int, int);
108 static void  strprintorsetwidth(VAR *, const char *, int);
109 
110 static time_t now;
111 
112 #define	min(a,b)	((a) <= (b) ? (a) : (b))
113 
114 static int
115 iwidth(u_int64_t v)
116 {
117 	u_int64_t nlim, lim;
118 	int w = 1;
119 
120 	for (lim = 10; v >= lim; lim = nlim) {
121 		nlim = lim * 10;
122 		w++;
123 		if (nlim < lim)
124 			break;
125 	}
126 	return w;
127 }
128 
129 static char *
130 cmdpart(char *arg0)
131 {
132 	char *cp;
133 
134 	return ((cp = strrchr(arg0, '/')) != NULL ? cp + 1 : arg0);
135 }
136 
137 void
138 printheader(void)
139 {
140 	int len;
141 	VAR *v;
142 	struct varent *vent;
143 	static int firsttime = 1;
144 
145 	for (vent = vhead; vent; vent = vent->next) {
146 		v = vent->var;
147 		if (firsttime) {
148 			len = strlen(v->header);
149 			if (len > v->width)
150 				v->width = len;
151 			totwidth += v->width + 1;	/* +1 for space */
152 		}
153 		if (v->flag & LJUST) {
154 			if (vent->next == NULL)	/* last one */
155 				(void)printf("%s", v->header);
156 			else
157 				(void)printf("%-*s", v->width,
158 				    v->header);
159 		} else
160 			(void)printf("%*s", v->width, v->header);
161 		if (vent->next != NULL)
162 			(void)putchar(' ');
163 	}
164 	(void)putchar('\n');
165 	if (firsttime) {
166 		firsttime = 0;
167 		totwidth--;	/* take off last space */
168 	}
169 }
170 
171 /*
172  * Return 1 if the command name in the argument vector (u-area) does
173  * not match the command name (p_comm)
174  */
175 static int
176 titlecmp(char *name, char **argv)
177 {
178 	char *title;
179 	int namelen;
180 
181 
182 	/* no argument vector == no match; system processes/threads do that */
183 	if (argv == 0 || argv[0] == 0)
184 		return (1);
185 
186 	title = cmdpart(argv[0]);
187 
188 	/* the basename matches */
189 	if (!strcmp(name, title))
190 		return (0);
191 
192 	/* handle login shells, by skipping the leading - */
193 	if (title[0] == '-' && !strcmp(name, title + 1))
194 		return (0);
195 
196 	namelen = strlen(name);
197 
198 	/* handle daemons that report activity as daemonname: activity */
199 	if (argv[1] == 0 &&
200 	    !strncmp(name, title, namelen) &&
201 	    title[namelen + 0] == ':' &&
202 	    title[namelen + 1] == ' ')
203 		return (0);
204 
205 	return (1);
206 }
207 
208 static void
209 doubleprintorsetwidth(VAR *v, double val, int prec, int mode)
210 {
211 	int fmtlen;
212 
213 	if (mode == WIDTHMODE) {
214 		if (val < 0.0 && val < v->longestnd) {
215 			fmtlen = (int)log10(-val) + prec + 2;
216 			v->longestnd = val;
217 			if (fmtlen > v->width)
218 				v->width = fmtlen;
219 		} else if (val > 0.0 && val > v->longestpd) {
220 			fmtlen = (int)log10(val) + prec + 1;
221 			v->longestpd = val;
222 			if (fmtlen > v->width)
223 				v->width = fmtlen;
224 		}
225 	} else {
226 		(void)printf("%*.*f", v->width, prec, val);
227 	}
228 }
229 
230 static void
231 intprintorsetwidth(VAR *v, int val, int mode)
232 {
233 	int fmtlen;
234 
235 	if (mode == WIDTHMODE) {
236 		if (val < 0 && val < v->longestn) {
237 			v->longestn = val;
238 			fmtlen = iwidth(-val) + 1;
239 			if (fmtlen > v->width)
240 				v->width = fmtlen;
241 		} else if (val > 0 && val > v->longestp) {
242 			v->longestp = val;
243 			fmtlen = iwidth(val);
244 			if (fmtlen > v->width)
245 				v->width = fmtlen;
246 		}
247 	} else
248 		(void)printf("%*d", v->width, val);
249 }
250 
251 static void
252 strprintorsetwidth(VAR *v, const char *str, int mode)
253 {
254 	int len;
255 
256 	if (mode == WIDTHMODE) {
257 		len = strlen(str);
258 		if (len > v->width)
259 			v->width = len;
260 	} else {
261 		if (v->flag & LJUST)
262 			(void)printf("%-*.*s", v->width, v->width, str);
263 		else
264 			(void)printf("%*.*s", v->width, v->width, str);
265 	}
266 }
267 
268 void
269 command(void *arg, VARENT *ve, int mode)
270 {
271 	struct kinfo_proc2 *ki;
272 	VAR *v;
273 	int left;
274 	char **argv, **p, *name;
275 
276 	if (mode == WIDTHMODE)
277 		return;
278 
279 	ki = arg;
280 	v = ve->var;
281 	if (ve->next != NULL || termwidth != UNLIMITED) {
282 		if (ve->next == NULL) {
283 			left = termwidth - (totwidth - v->width);
284 			if (left < 1) /* already wrapped, just use std width */
285 				left = v->width;
286 		} else
287 			left = v->width;
288 	} else
289 		left = -1;
290 	if (needenv && kd) {
291 		argv = kvm_getenvv2(kd, ki, termwidth);
292 		if ((p = argv) != NULL) {
293 			while (*p) {
294 				fmt_puts(*p, &left);
295 				p++;
296 				fmt_putc(' ', &left);
297 			}
298 		}
299 	}
300 	if (needcomm) {
301 		name = ki->p_comm;
302 		if (!commandonly) {
303 			argv = kvm_getargv2(kd, ki, termwidth);
304 			if ((p = argv) != NULL) {
305 				while (*p) {
306 					fmt_puts(*p, &left);
307 					p++;
308 					fmt_putc(' ', &left);
309 					if (v->flag & ARGV0)
310 						break;
311 				}
312 				if (!(v->flag & ARGV0) &&
313 				    titlecmp(name, argv)) {
314 					/*
315 					 * append the real command name within
316 					 * parentheses, if the command name
317 					 * does not match the one in the
318 					 * argument vector
319 					 */
320 					fmt_putc('(', &left);
321 					fmt_puts(name, &left);
322 					fmt_putc(')', &left);
323 				}
324 			} else {
325 				/*
326 				 * Commands that don't set an argv vector
327 				 * are printed with square brackets if they
328 				 * are system commands.  Otherwise they are
329 				 * printed within parentheses.
330 				 */
331 				if (ki->p_flag & P_SYSTEM) {
332 					fmt_putc('[', &left);
333 					fmt_puts(name, &left);
334 					fmt_putc(']', &left);
335 				} else {
336 					fmt_putc('(', &left);
337 					fmt_puts(name, &left);
338 					fmt_putc(')', &left);
339 				}
340 			}
341 		} else {
342 			fmt_puts(name, &left);
343 		}
344 	}
345 	if (ve->next && left > 0)
346 		(void)printf("%*s", left, "");
347 }
348 
349 void
350 groups(void *arg, VARENT *ve, int mode)
351 {
352 	struct kinfo_proc2 *ki;
353 	VAR *v;
354 	int left, i;
355 	char buf[16], *p;
356 
357 	if (mode == WIDTHMODE)
358 		return;
359 
360 	ki = arg;
361 	v = ve->var;
362 	if (ve->next != NULL || termwidth != UNLIMITED) {
363 		if (ve->next == NULL) {
364 			left = termwidth - (totwidth - v->width);
365 			if (left < 1) /* already wrapped, just use std width */
366 				left = v->width;
367 		} else
368 			left = v->width;
369 	} else
370 		left = -1;
371 
372 	if (ki->p_ngroups == 0) {
373 		fmt_putc('-', &left);
374 		return;
375 	}
376 
377 	for (i = 0; i < ki->p_ngroups; i++) {
378 		(void)snprintf(buf, sizeof(buf), "%d", ki->p_groups[i]);
379 		if (i)
380 			fmt_putc(' ', &left);
381 		for (p = &buf[0]; *p; p++)
382 			fmt_putc(*p, &left);
383 	}
384 
385 	if (ve->next && left > 0)
386 		(void)printf("%*s", left, "");
387 }
388 
389 void
390 groupnames(void *arg, VARENT *ve, int mode)
391 {
392 	struct kinfo_proc2 *ki;
393 	VAR *v;
394 	int left, i;
395 	const char *p;
396 
397 	if (mode == WIDTHMODE)
398 		return;
399 
400 	ki = arg;
401 	v = ve->var;
402 	if (ve->next != NULL || termwidth != UNLIMITED) {
403 		if (ve->next == NULL) {
404 			left = termwidth - (totwidth - v->width);
405 			if (left < 1) /* already wrapped, just use std width */
406 				left = v->width;
407 		} else
408 			left = v->width;
409 	} else
410 		left = -1;
411 
412 	if (ki->p_ngroups == 0) {
413 		fmt_putc('-', &left);
414 		return;
415 	}
416 
417 	for (i = 0; i < ki->p_ngroups; i++) {
418 		if (i)
419 			fmt_putc(' ', &left);
420 		for (p = group_from_gid(ki->p_groups[i], 0); *p; p++)
421 			fmt_putc(*p, &left);
422 	}
423 
424 	if (ve->next && left > 0)
425 		(void)printf("%*s", left, "");
426 }
427 
428 void
429 ucomm(void *arg, VARENT *ve, int mode)
430 {
431 	struct kinfo_proc2 *k;
432 	VAR *v;
433 
434 	k = arg;
435 	v = ve->var;
436 	strprintorsetwidth(v, k->p_comm, mode);
437 }
438 
439 void
440 logname(void *arg, VARENT *ve, int mode)
441 {
442 	struct kinfo_proc2 *k;
443 	VAR *v;
444 
445 	k = arg;
446 	v = ve->var;
447 	strprintorsetwidth(v, k->p_login, mode);
448 }
449 
450 void
451 state(void *arg, VARENT *ve, int mode)
452 {
453 	struct kinfo_proc2 *k;
454 	int flag, is_zombie;
455 	char *cp;
456 	VAR *v;
457 	char buf[16];
458 
459 	k = arg;
460 	is_zombie = 0;
461 	v = ve->var;
462 	flag = k->p_flag;
463 	cp = buf;
464 
465 	switch (k->p_stat) {
466 
467 	case LSSTOP:
468 		*cp = 'T';
469 		break;
470 
471 	case LSSLEEP:
472 		if (flag & L_SINTR)	/* interruptable (long) */
473 			*cp = k->p_slptime >= maxslp ? 'I' : 'S';
474 		else
475 			*cp = 'D';
476 		break;
477 
478 	case LSRUN:
479 	case LSIDL:
480 	case LSONPROC:
481 		*cp = 'R';
482 		break;
483 
484 	case LSZOMB:
485 	case LSDEAD:
486 		*cp = 'Z';
487 		is_zombie = 1;
488 		break;
489 
490 	case LSSUSPENDED:
491 		*cp = 'U';
492 		break;
493 
494 	default:
495 		*cp = '?';
496 	}
497 	cp++;
498 	if (flag & L_INMEM) {
499 	} else
500 		*cp++ = 'W';
501 	if (k->p_nice < NZERO)
502 		*cp++ = '<';
503 	else if (k->p_nice > NZERO)
504 		*cp++ = 'N';
505 	if (flag & P_TRACED)
506 		*cp++ = 'X';
507 	if (flag & P_SYSTRACE)
508 		*cp++ = 'x';
509 	if (flag & P_WEXIT && !is_zombie)
510 		*cp++ = 'E';
511 	if (flag & P_PPWAIT)
512 		*cp++ = 'V';
513 	if (flag & P_SYSTEM)
514 		*cp++ = 'K';
515 	/* system process might have this too, don't need to double up */
516 	else if (k->p_holdcnt)
517 		*cp++ = 'L';
518 	if (k->p_eflag & EPROC_SLEADER)
519 		*cp++ = 's';
520 	if (flag & P_SA)
521 		*cp++ = 'a';
522 	else if (k->p_nlwps > 1)
523 		*cp++ = 'l';
524 	if ((flag & P_CONTROLT) && k->p__pgid == k->p_tpgid)
525 		*cp++ = '+';
526 	*cp = '\0';
527 	strprintorsetwidth(v, buf, mode);
528 }
529 
530 void
531 lstate(void *arg, VARENT *ve, int mode)
532 {
533 	struct kinfo_lwp *k;
534 	int flag, is_zombie;
535 	char *cp;
536 	VAR *v;
537 	char buf[16];
538 
539 	k = arg;
540 	is_zombie = 0;
541 	v = ve->var;
542 	flag = k->l_flag;
543 	cp = buf;
544 
545 	switch (k->l_stat) {
546 
547 	case LSSTOP:
548 		*cp = 'T';
549 		break;
550 
551 	case LSSLEEP:
552 		if (flag & L_SINTR)	/* interruptible (long) */
553 			*cp = k->l_slptime >= maxslp ? 'I' : 'S';
554 		else
555 			*cp = 'D';
556 		break;
557 
558 	case LSRUN:
559 	case LSIDL:
560 	case LSONPROC:
561 		*cp = 'R';
562 		break;
563 
564 	case LSZOMB:
565 	case LSDEAD:
566 		*cp = 'Z';
567 		is_zombie = 1;
568 		break;
569 
570 	case LSSUSPENDED:
571 		*cp = 'U';
572 		break;
573 
574 	default:
575 		*cp = '?';
576 	}
577 	cp++;
578 	if (flag & L_INMEM) {
579 	} else
580 		*cp++ = 'W';
581 	if (k->l_holdcnt)
582 		*cp++ = 'L';
583 	if (flag & L_DETACHED)
584 		*cp++ = '-';
585 	*cp = '\0';
586 	strprintorsetwidth(v, buf, mode);
587 }
588 
589 void
590 pnice(void *arg, VARENT *ve, int mode)
591 {
592 	struct kinfo_proc2 *k;
593 	VAR *v;
594 
595 	k = arg;
596 	v = ve->var;
597 	intprintorsetwidth(v, k->p_nice - NZERO, mode);
598 }
599 
600 void
601 pri(void *arg, VARENT *ve, int mode)
602 {
603 	struct kinfo_lwp *l;
604 	VAR *v;
605 
606 	l = arg;
607 	v = ve->var;
608 	intprintorsetwidth(v, l->l_priority - PZERO, mode);
609 }
610 
611 void
612 uname(void *arg, VARENT *ve, int mode)
613 {
614 	struct kinfo_proc2 *k;
615 	VAR *v;
616 
617 	k = arg;
618 	v = ve->var;
619 	strprintorsetwidth(v, user_from_uid(k->p_uid, 0), mode);
620 }
621 
622 void
623 runame(void *arg, VARENT *ve, int mode)
624 {
625 	struct kinfo_proc2 *k;
626 	VAR *v;
627 
628 	k = arg;
629 	v = ve->var;
630 	strprintorsetwidth(v, user_from_uid(k->p_ruid, 0), mode);
631 }
632 
633 void
634 svuname(void *arg, VARENT *ve, int mode)
635 {
636 	struct kinfo_proc2 *k;
637 	VAR *v;
638 
639 	k = arg;
640 	v = ve->var;
641 	strprintorsetwidth(v, user_from_uid(k->p_svuid, 0), mode);
642 }
643 
644 void
645 gname(void *arg, VARENT *ve, int mode)
646 {
647 	struct kinfo_proc2 *k;
648 	VAR *v;
649 
650 	k = arg;
651 	v = ve->var;
652 	strprintorsetwidth(v, group_from_gid(k->p_gid, 0), mode);
653 }
654 
655 void
656 rgname(void *arg, VARENT *ve, int mode)
657 {
658 	struct kinfo_proc2 *k;
659 	VAR *v;
660 
661 	k = arg;
662 	v = ve->var;
663 	strprintorsetwidth(v, group_from_gid(k->p_rgid, 0), mode);
664 }
665 
666 void
667 svgname(void *arg, VARENT *ve, int mode)
668 {
669 	struct kinfo_proc2 *k;
670 	VAR *v;
671 
672 	k = arg;
673 	v = ve->var;
674 	strprintorsetwidth(v, group_from_gid(k->p_svgid, 0), mode);
675 }
676 
677 void
678 tdev(void *arg, VARENT *ve, int mode)
679 {
680 	struct kinfo_proc2 *k;
681 	VAR *v;
682 	dev_t dev;
683 	char buff[16];
684 
685 	k = arg;
686 	v = ve->var;
687 	dev = k->p_tdev;
688 	if (dev == NODEV) {
689 		if (mode == PRINTMODE)
690 			(void)printf("%*s", v->width, "??");
691 		else
692 			if (v->width < 2)
693 				v->width = 2;
694 	} else {
695 		(void)snprintf(buff, sizeof(buff),
696 		    "%d/%d", major(dev), minor(dev));
697 		strprintorsetwidth(v, buff, mode);
698 	}
699 }
700 
701 void
702 tname(void *arg, VARENT *ve, int mode)
703 {
704 	struct kinfo_proc2 *k;
705 	VAR *v;
706 	dev_t dev;
707 	const char *ttname;
708 	int noctty;
709 
710 	k = arg;
711 	v = ve->var;
712 	dev = k->p_tdev;
713 	if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) {
714 		if (mode == PRINTMODE)
715 			(void)printf("%-*s", v->width, "??");
716 		else
717 			if (v->width < 2)
718 				v->width = 2;
719 	} else {
720 		if (strncmp(ttname, "tty", 3) == 0 ||
721 		    strncmp(ttname, "dty", 3) == 0)
722 			ttname += 3;
723 		noctty = !(k->p_eflag & EPROC_CTTY) ? 1 : 0;
724 		if (mode == WIDTHMODE) {
725 			int fmtlen;
726 
727 			fmtlen = strlen(ttname) + noctty;
728 			if (v->width < fmtlen)
729 				v->width = fmtlen;
730 		} else {
731 			if (noctty)
732 				(void)printf("%-*s-", v->width - 1, ttname);
733 			else
734 				(void)printf("%-*s", v->width, ttname);
735 		}
736 	}
737 }
738 
739 void
740 longtname(void *arg, VARENT *ve, int mode)
741 {
742 	struct kinfo_proc2 *k;
743 	VAR *v;
744 	dev_t dev;
745 	const char *ttname;
746 
747 	k = arg;
748 	v = ve->var;
749 	dev = k->p_tdev;
750 	if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) {
751 		if (mode == PRINTMODE)
752 			(void)printf("%-*s", v->width, "??");
753 		else
754 			if (v->width < 2)
755 				v->width = 2;
756 	} else {
757 		strprintorsetwidth(v, ttname, mode);
758 	}
759 }
760 
761 void
762 started(void *arg, VARENT *ve, int mode)
763 {
764 	struct kinfo_proc2 *k;
765 	VAR *v;
766 	time_t startt;
767 	struct tm *tp;
768 	char buf[100], *cp;
769 
770 	k = arg;
771 	v = ve->var;
772 	if (!k->p_uvalid) {
773 		if (mode == PRINTMODE)
774 			(void)printf("%*s", v->width, "-");
775 		return;
776 	}
777 
778 	startt = k->p_ustart_sec;
779 	tp = localtime(&startt);
780 	if (now == 0)
781 		(void)time(&now);
782 	if (now - k->p_ustart_sec < SECSPERDAY)
783 		/* I *hate* SCCS... */
784 		(void)strftime(buf, sizeof(buf) - 1, "%l:%" "M%p", tp);
785 	else if (now - k->p_ustart_sec < DAYSPERWEEK * SECSPERDAY)
786 		/* I *hate* SCCS... */
787 		(void)strftime(buf, sizeof(buf) - 1, "%a%" "I%p", tp);
788 	else
789 		(void)strftime(buf, sizeof(buf) - 1, "%e%b%y", tp);
790 	/* %e and %l can start with a space. */
791 	cp = buf;
792 	if (*cp == ' ')
793 		cp++;
794 	strprintorsetwidth(v, cp, mode);
795 }
796 
797 void
798 lstarted(void *arg, VARENT *ve, int mode)
799 {
800 	struct kinfo_proc2 *k;
801 	VAR *v;
802 	time_t startt;
803 	char buf[100];
804 
805 	k = arg;
806 	v = ve->var;
807 	if (!k->p_uvalid) {
808 		/*
809 		 * Minimum width is less than header - we don't
810 		 * need to check it every time.
811 		 */
812 		if (mode == PRINTMODE)
813 			(void)printf("%*s", v->width, "-");
814 		return;
815 	}
816 	startt = k->p_ustart_sec;
817 
818 	/* assume all times are the same length */
819 	if (mode != WIDTHMODE || v->width == 0) {
820 		(void)strftime(buf, sizeof(buf) -1, "%c",
821 		    localtime(&startt));
822 		strprintorsetwidth(v, buf, mode);
823 	}
824 }
825 
826 void
827 elapsed(void *arg, VARENT *ve, int mode)
828 {
829 	struct kinfo_proc2 *k;
830 	VAR *v;
831 	int32_t origseconds, secs, mins, hours, days;
832 	int fmtlen, printed_something;
833 
834 	k = arg;
835 	v = ve->var;
836 	if (k->p_uvalid == 0) {
837 		origseconds = 0;
838 	} else {
839 		if (now == 0)
840 			(void)time(&now);
841 		origseconds = now - k->p_ustart_sec;
842 		if (origseconds < 0) {
843 			/*
844 			 * Don't try to be fancy if the machine's
845 			 * clock has been rewound to before the
846 			 * process "started".
847 			 */
848 			origseconds = 0;
849 		}
850 	}
851 
852 	secs = origseconds;
853 	mins = secs / SECSPERMIN;
854 	secs %= SECSPERMIN;
855 	hours = mins / MINSPERHOUR;
856 	mins %= MINSPERHOUR;
857 	days = hours / HOURSPERDAY;
858 	hours %= HOURSPERDAY;
859 
860 	if (mode == WIDTHMODE) {
861 		if (origseconds == 0)
862 			/* non-zero so fmtlen is calculated at least once */
863 			origseconds = 1;
864 
865 		if (origseconds > v->longestp) {
866 			v->longestp = origseconds;
867 
868 			if (days > 0) {
869 				/* +9 for "-hh:mm:ss" */
870 				fmtlen = iwidth(days) + 9;
871 			} else if (hours > 0) {
872 				/* +6 for "mm:ss" */
873 				fmtlen = iwidth(hours) + 6;
874 			} else {
875 				/* +3 for ":ss" */
876 				fmtlen = iwidth(mins) + 3;
877 			}
878 
879 			if (fmtlen > v->width)
880 				v->width = fmtlen;
881 		}
882 	} else {
883 		printed_something = 0;
884 		fmtlen = v->width;
885 
886 		if (days > 0) {
887 			(void)printf("%*d", fmtlen - 9, days);
888 			printed_something = 1;
889 		} else if (fmtlen > 9) {
890 			(void)printf("%*s", fmtlen - 9, "");
891 		}
892 		if (fmtlen > 9)
893 			fmtlen = 9;
894 
895 		if (printed_something) {
896 			(void)printf("-%.*d", fmtlen - 7, hours);
897 			printed_something = 1;
898 		} else if (hours > 0) {
899 			(void)printf("%*d", fmtlen - 6, hours);
900 			printed_something = 1;
901 		} else if (fmtlen > 6) {
902 			(void)printf("%*s", fmtlen - 6, "");
903 		}
904 		if (fmtlen > 6)
905 			fmtlen = 6;
906 
907 		/* Don't need to set fmtlen or printed_something any more... */
908 		if (printed_something) {
909 			(void)printf(":%.*d", fmtlen - 4, mins);
910 		} else if (mins > 0) {
911 			(void)printf("%*d", fmtlen - 3, mins);
912 		} else if (fmtlen > 3) {
913 			(void)printf("%*s", fmtlen - 3, "0");
914 		}
915 
916 		(void)printf(":%.2d", secs);
917 	}
918 }
919 
920 void
921 wchan(void *arg, VARENT *ve, int mode)
922 {
923 	struct kinfo_lwp *l;
924 	VAR *v;
925 	char *buf;
926 
927 	l = arg;
928 	v = ve->var;
929 	if (l->l_wchan) {
930 		if (l->l_wmesg) {
931 			strprintorsetwidth(v, l->l_wmesg, mode);
932 			v->width = min(v->width, KI_WMESGLEN);
933 		} else {
934 			(void)asprintf(&buf, "%-*" PRIx64, v->width,
935 			    l->l_wchan);
936 			if (buf == NULL)
937 				err(1, "%s", "");
938 			strprintorsetwidth(v, buf, mode);
939 			v->width = min(v->width, KI_WMESGLEN);
940 			free(buf);
941 		}
942 	} else {
943 		if (mode == PRINTMODE)
944 			(void)printf("%-*s", v->width, "-");
945 	}
946 }
947 
948 #define	pgtok(a)        (((a)*getpagesize())/1024)
949 
950 void
951 vsize(void *arg, VARENT *ve, int mode)
952 {
953 	struct kinfo_proc2 *k;
954 	VAR *v;
955 
956 	k = arg;
957 	v = ve->var;
958 	intprintorsetwidth(v,
959 	    pgtok(k->p_vm_dsize + k->p_vm_ssize + k->p_vm_tsize), mode);
960 }
961 
962 void
963 rssize(void *arg, VARENT *ve, int mode)
964 {
965 	struct kinfo_proc2 *k;
966 	VAR *v;
967 
968 	k = arg;
969 	v = ve->var;
970 	/* XXX don't have info about shared */
971 	intprintorsetwidth(v, pgtok(k->p_vm_rssize), mode);
972 }
973 
974 void
975 p_rssize(void *arg, VARENT *ve, int mode)	/* doesn't account for text */
976 {
977 	struct kinfo_proc2 *k;
978 	VAR *v;
979 
980 	k = arg;
981 	v = ve->var;
982 	intprintorsetwidth(v, pgtok(k->p_vm_rssize), mode);
983 }
984 
985 void
986 cputime(void *arg, VARENT *ve, int mode)
987 {
988 	struct kinfo_proc2 *k;
989 	VAR *v;
990 	int32_t secs;
991 	int32_t psecs;	/* "parts" of a second. first micro, then centi */
992 	int fmtlen;
993 
994 	k = arg;
995 	v = ve->var;
996 	if (P_ZOMBIE(k) || k->p_uvalid == 0) {
997 		secs = 0;
998 		psecs = 0;
999 	} else {
1000 		/*
1001 		 * This counts time spent handling interrupts.  We could
1002 		 * fix this, but it is not 100% trivial (and interrupt
1003 		 * time fractions only work on the sparc anyway).	XXX
1004 		 */
1005 		secs = k->p_rtime_sec;
1006 		psecs = k->p_rtime_usec;
1007 		if (sumrusage) {
1008 			secs += k->p_uctime_sec;
1009 			psecs += k->p_uctime_usec;
1010 		}
1011 		/*
1012 		 * round and scale to 100's
1013 		 */
1014 		psecs = (psecs + 5000) / 10000;
1015 		secs += psecs / 100;
1016 		psecs = psecs % 100;
1017 	}
1018 	if (mode == WIDTHMODE) {
1019 		/*
1020 		 * Ugg, this is the only field where a value of 0 is longer
1021 		 * than the column title.
1022 		 * Use SECSPERMIN, because secs is divided by that when
1023 		 * passed to iwidth().
1024 		 */
1025 		if (secs == 0)
1026 			secs = SECSPERMIN;
1027 
1028 		if (secs > v->longestp) {
1029 			v->longestp = secs;
1030 			/* "+6" for the ":%02ld.%02ld" in the printf() below */
1031 			fmtlen = iwidth(secs / SECSPERMIN) + 6;
1032 			if (fmtlen > v->width)
1033 				v->width = fmtlen;
1034 		}
1035 	} else {
1036 		(void)printf("%*ld:%02ld.%02ld", v->width - 6,
1037 		    (long)(secs / SECSPERMIN), (long)(secs % SECSPERMIN),
1038 		    (long)psecs);
1039 	}
1040 }
1041 
1042 double
1043 getpcpu(k)
1044 	struct kinfo_proc2 *k;
1045 {
1046 	static int failure;
1047 
1048 	if (!nlistread)
1049 		failure = (kd) ? donlist() : 1;
1050 	if (failure)
1051 		return (0.0);
1052 
1053 #define	fxtofl(fixpt)	((double)(fixpt) / fscale)
1054 
1055 	/* XXX - I don't like this */
1056 	if (k->p_swtime == 0 || (k->p_flag & L_INMEM) == 0 ||
1057 	    k->p_stat == SZOMB)
1058 		return (0.0);
1059 	if (rawcpu)
1060 		return (100.0 * fxtofl(k->p_pctcpu));
1061 	return (100.0 * fxtofl(k->p_pctcpu) /
1062 		(1.0 - exp(k->p_swtime * log(ccpu))));
1063 }
1064 
1065 void
1066 pcpu(void *arg, VARENT *ve, int mode)
1067 {
1068 	struct kinfo_proc2 *k;
1069 	VAR *v;
1070 
1071 	k = arg;
1072 	v = ve->var;
1073 	doubleprintorsetwidth(v, getpcpu(k), 1, mode);
1074 }
1075 
1076 double
1077 getpmem(k)
1078 	struct kinfo_proc2 *k;
1079 {
1080 	static int failure;
1081 	double fracmem;
1082 	int szptudot;
1083 
1084 	if (!nlistread)
1085 		failure = (kd) ? donlist() : 1;
1086 	if (failure)
1087 		return (0.0);
1088 
1089 	if ((k->p_flag & L_INMEM) == 0)
1090 		return (0.0);
1091 	/* XXX want pmap ptpages, segtab, etc. (per architecture) */
1092 	szptudot = uspace/getpagesize();
1093 	/* XXX don't have info about shared */
1094 	fracmem = ((float)k->p_vm_rssize + szptudot)/mempages;
1095 	return (100.0 * fracmem);
1096 }
1097 
1098 void
1099 pmem(void *arg, VARENT *ve, int mode)
1100 {
1101 	struct kinfo_proc2 *k;
1102 	VAR *v;
1103 
1104 	k = arg;
1105 	v = ve->var;
1106 	doubleprintorsetwidth(v, getpmem(k), 1, mode);
1107 }
1108 
1109 void
1110 pagein(void *arg, VARENT *ve, int mode)
1111 {
1112 	struct kinfo_proc2 *k;
1113 	VAR *v;
1114 
1115 	k = arg;
1116 	v = ve->var;
1117 	intprintorsetwidth(v, k->p_uvalid ? k->p_uru_majflt : 0, mode);
1118 }
1119 
1120 void
1121 maxrss(void *arg, VARENT *ve, int mode)
1122 {
1123 	VAR *v;
1124 
1125 	v = ve->var;
1126 	/* No need to check width! */
1127 	if (mode == PRINTMODE)
1128 		(void)printf("%*s", v->width, "-");
1129 }
1130 
1131 void
1132 tsize(void *arg, VARENT *ve, int mode)
1133 {
1134 	struct kinfo_proc2 *k;
1135 	VAR *v;
1136 
1137 	k = arg;
1138 	v = ve->var;
1139 	intprintorsetwidth(v, pgtok(k->p_vm_tsize), mode);
1140 }
1141 
1142 /*
1143  * Generic output routines.  Print fields from various prototype
1144  * structures.
1145  */
1146 static void
1147 printval(bp, v, mode)
1148 	void *bp;
1149 	VAR *v;
1150 	int mode;
1151 {
1152 	static char ofmt[32] = "%";
1153 	int width, vok, fmtlen;
1154 	char *fcp, *cp;
1155 	int64_t val;
1156 	u_int64_t uval;
1157 
1158 	/*
1159 	 * Note that the "INF127" check is nonsensical for types
1160 	 * that are or can be signed.
1161 	 */
1162 #define	GET(type)		(*(type *)bp)
1163 #define	CHK_INF127(n)		(((n) > 127) && (v->flag & INF127) ? 127 : (n))
1164 
1165 #define	VSIGN	1
1166 #define	VUNSIGN	2
1167 #define	VPTR	3
1168 
1169 	if (mode == WIDTHMODE) {
1170 		vok = 0;
1171 		switch (v->type) {
1172 		case CHAR:
1173 			val = GET(char);
1174 			vok = VSIGN;
1175 			break;
1176 		case UCHAR:
1177 			uval = CHK_INF127(GET(u_char));
1178 			vok = VUNSIGN;
1179 			break;
1180 		case SHORT:
1181 			val = GET(short);
1182 			vok = VSIGN;
1183 			break;
1184 		case USHORT:
1185 			uval = CHK_INF127(GET(u_short));
1186 			vok = VUNSIGN;
1187 			break;
1188 		case INT32:
1189 			val = GET(int32_t);
1190 			vok = VSIGN;
1191 			break;
1192 		case INT:
1193 			val = GET(int);
1194 			vok = VSIGN;
1195 			break;
1196 		case UINT:
1197 		case UINT32:
1198 			uval = CHK_INF127(GET(u_int));
1199 			vok = VUNSIGN;
1200 			break;
1201 		case LONG:
1202 			val = GET(long);
1203 			vok = VSIGN;
1204 			break;
1205 		case ULONG:
1206 			uval = CHK_INF127(GET(u_long));
1207 			vok = VUNSIGN;
1208 			break;
1209 		case KPTR:
1210 			uval = GET(u_int64_t);
1211 			vok = VPTR;
1212 			break;
1213 		case KPTR24:
1214 			uval = GET(u_int64_t);
1215 			uval &= 0xffffff;
1216 			vok = VPTR;
1217 			break;
1218 		case INT64:
1219 			val = GET(int64_t);
1220 			vok = VSIGN;
1221 			break;
1222 		case UINT64:
1223 			uval = CHK_INF127(GET(u_int64_t));
1224 			vok = VUNSIGN;
1225 			break;
1226 
1227 		case SIGLIST:
1228 		default:
1229 			/* nothing... */;
1230 		}
1231 		switch (vok) {
1232 		case VSIGN:
1233 			if (val < 0 && val < v->longestn) {
1234 				v->longestn = val;
1235 				fmtlen = iwidth(-val) + 1;
1236 				if (fmtlen > v->width)
1237 					v->width = fmtlen;
1238 			} else if (val > 0 && val > v->longestp) {
1239 				v->longestp = val;
1240 				fmtlen = iwidth(val);
1241 				if (fmtlen > v->width)
1242 					v->width = fmtlen;
1243 			}
1244 			return;
1245 		case VUNSIGN:
1246 			if (uval > v->longestu) {
1247 				v->longestu = uval;
1248 				v->width = iwidth(uval);
1249 			}
1250 			return;
1251 		case VPTR:
1252 			fmtlen = 0;
1253 			while (uval > 0) {
1254 				uval >>= 4;
1255 				fmtlen++;
1256 			}
1257 			if (fmtlen > v->width)
1258 				v->width = fmtlen;
1259 			return;
1260 		}
1261 	}
1262 
1263 	width = v->width;
1264 	cp = ofmt + 1;
1265 	fcp = v->fmt;
1266 	if (v->flag & LJUST)
1267 		*cp++ = '-';
1268 	*cp++ = '*';
1269 	while ((*cp++ = *fcp++) != '\0')
1270 		continue;
1271 
1272 	switch (v->type) {
1273 	case CHAR:
1274 		(void)printf(ofmt, width, GET(char));
1275 		return;
1276 	case UCHAR:
1277 		(void)printf(ofmt, width, CHK_INF127(GET(u_char)));
1278 		return;
1279 	case SHORT:
1280 		(void)printf(ofmt, width, GET(short));
1281 		return;
1282 	case USHORT:
1283 		(void)printf(ofmt, width, CHK_INF127(GET(u_short)));
1284 		return;
1285 	case INT:
1286 		(void)printf(ofmt, width, GET(int));
1287 		return;
1288 	case UINT:
1289 		(void)printf(ofmt, width, CHK_INF127(GET(u_int)));
1290 		return;
1291 	case LONG:
1292 		(void)printf(ofmt, width, GET(long));
1293 		return;
1294 	case ULONG:
1295 		(void)printf(ofmt, width, CHK_INF127(GET(u_long)));
1296 		return;
1297 	case KPTR:
1298 		(void)printf(ofmt, width, GET(u_int64_t));
1299 		return;
1300 	case KPTR24:
1301 		(void)printf(ofmt, width, GET(u_int64_t) & 0xffffff);
1302 		return;
1303 	case INT32:
1304 		(void)printf(ofmt, width, GET(int32_t));
1305 		return;
1306 	case UINT32:
1307 		(void)printf(ofmt, width, CHK_INF127(GET(u_int32_t)));
1308 		return;
1309 	case SIGLIST:
1310 		{
1311 			sigset_t *s = (sigset_t *)(void *)bp;
1312 			size_t i;
1313 #define	SIGSETSIZE	(sizeof(s->__bits) / sizeof(s->__bits[0]))
1314 			char buf[SIGSETSIZE * 8 + 1];
1315 
1316 			for (i = 0; i < SIGSETSIZE; i++)
1317 				(void)snprintf(&buf[i * 8], 9, "%.8x",
1318 				    s->__bits[(SIGSETSIZE - 1) - i]);
1319 
1320 			/* Skip leading zeroes */
1321 			for (i = 0; buf[i] == '0'; i++)
1322 				continue;
1323 
1324 			if (buf[i] == '\0')
1325 				i--;
1326 			strprintorsetwidth(v, buf + i, mode);
1327 #undef SIGSETSIZE
1328 		}
1329 		return;
1330 	case INT64:
1331 		(void)printf(ofmt, width, GET(int64_t));
1332 		return;
1333 	case UINT64:
1334 		(void)printf(ofmt, width, CHK_INF127(GET(u_int64_t)));
1335 		return;
1336 	default:
1337 		errx(1, "unknown type %d", v->type);
1338 	}
1339 #undef GET
1340 #undef CHK_INF127
1341 }
1342 
1343 void
1344 pvar(void *arg, VARENT *ve, int mode)
1345 {
1346 	VAR *v;
1347 
1348 	v = ve->var;
1349 	if (v->flag & UAREA && !((struct kinfo_proc2 *)arg)->p_uvalid) {
1350 		if (mode == PRINTMODE)
1351 			(void)printf("%*s", v->width, "-");
1352 		return;
1353 	}
1354 
1355 	(void)printval((char *)arg + v->off, v, mode);
1356 }
1357 
1358 void
1359 putimeval(void *arg, VARENT *ve, int mode)
1360 {
1361 	VAR *v = ve->var;
1362 	struct kinfo_proc2 *k = arg;
1363 	ulong secs = *(uint32_t *)((char *)arg + v->off);
1364 	ulong usec = *(uint32_t *)((char *)arg + v->off + sizeof (uint32_t));
1365 	int fmtlen;
1366 
1367 	if (!k->p_uvalid) {
1368 		if (mode == PRINTMODE)
1369 			(void)printf("%*s", v->width, "-");
1370 		return;
1371 	}
1372 
1373 	if (mode == WIDTHMODE) {
1374 		if (secs == 0)
1375 			/* non-zero so fmtlen is calculated at least once */
1376 			secs = 1;
1377 		if (secs > v->longestu) {
1378 			v->longestu = secs;
1379 			if (secs <= 999)
1380 				/* sss.ssssss */
1381 				fmtlen = iwidth(secs) + 6 + 1;
1382 			else
1383 				/* hh:mm:ss.ss */
1384 				fmtlen = iwidth((secs + 1) / SECSPERHOUR)
1385 					+ 2 + 1 + 2 + 1 + 2 + 1;
1386 			if (fmtlen > v->width)
1387 				v->width = fmtlen;
1388 		}
1389 		return;
1390 	}
1391 
1392 	if (secs < 999)
1393 		(void)printf( "%*lu.%.6lu", v->width - 6 - 1, secs, usec);
1394 	else {
1395 		uint h, m;
1396 		usec += 5000;
1397 		if (usec >= 1000000) {
1398 			usec -= 1000000;
1399 			secs++;
1400 		}
1401 		m = secs / SECSPERMIN;
1402 		secs -= m * SECSPERMIN;
1403 		h = m / MINSPERHOUR;
1404 		m -= h * MINSPERHOUR;
1405 		(void)printf( "%*u:%.2u:%.2lu.%.2lu", v->width - 9, h, m, secs,
1406 		    usec / 10000u );
1407 	}
1408 }
1409