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