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