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