xref: /openbsd-src/bin/ps/print.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: print.c,v 1.44 2008/03/08 19:20:12 millert Exp $	*/
2 /*	$NetBSD: print.c,v 1.27 1995/09/29 21:58:12 cgd Exp $	*/
3 
4 /*-
5  * Copyright (c) 1990, 1993, 1994
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)print.c	8.6 (Berkeley) 4/16/94";
36 #else
37 static char rcsid[] = "$OpenBSD: print.c,v 1.44 2008/03/08 19:20:12 millert Exp $";
38 #endif
39 #endif /* not lint */
40 
41 #include <sys/param.h>
42 #include <sys/time.h>
43 #include <sys/resource.h>
44 #include <sys/proc.h>
45 #include <sys/stat.h>
46 
47 #include <sys/ucred.h>
48 #include <sys/sysctl.h>
49 #include <uvm/uvm_extern.h>
50 
51 #include <err.h>
52 #include <grp.h>
53 #include <kvm.h>
54 #include <math.h>
55 #include <nlist.h>
56 #include <stddef.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <tzfile.h>
61 #include <unistd.h>
62 #include <pwd.h>
63 
64 #include "ps.h"
65 
66 extern kvm_t *kd;
67 extern int needenv, needcomm, neednlist, commandonly;
68 
69 static char *cmdpart(char *);
70 
71 #define	min(a,b)	((a) < (b) ? (a) : (b))
72 
73 static char *
74 cmdpart(char *arg0)
75 {
76 	char *cp;
77 
78 	return ((cp = strrchr(arg0, '/')) != NULL ? cp + 1 : arg0);
79 }
80 
81 void
82 printheader(void)
83 {
84 	VAR *v;
85 	struct varent *vent;
86 
87 	for (vent = vhead; vent; vent = vent->next) {
88 		v = vent->var;
89 		if (v->flag & LJUST) {
90 			if (vent->next == NULL)	/* last one */
91 				(void)printf("%s", v->header);
92 			else
93 				(void)printf("%-*s", v->width, v->header);
94 		} else
95 			(void)printf("%*s", v->width, v->header);
96 		if (vent->next != NULL)
97 			(void)putchar(' ');
98 	}
99 	(void)putchar('\n');
100 }
101 
102 void
103 command(const struct kinfo_proc2 *kp, VARENT *ve)
104 {
105 	VAR *v;
106 	int left, wantspace = 0;
107 	char **argv, **p;
108 
109 	v = ve->var;
110 	if (ve->next != NULL || termwidth != UNLIMITED) {
111 		if (ve->next == NULL) {
112 			left = termwidth - (totwidth - v->width);
113 			if (left < 1) /* already wrapped, just use std width */
114 				left = v->width;
115 		} else
116 			left = v->width;
117 	} else
118 		left = -1;
119 	if (needenv && kd != NULL) {
120 		argv = kvm_getenvv2(kd, kp, termwidth);
121 		if ((p = argv) != NULL) {
122 			while (*p) {
123 				fmt_puts(*p, &left);
124 				p++;
125 				if (*p)
126 					fmt_putc(' ', &left);
127 				else
128 					wantspace = 1;
129 			}
130 		}
131 	} else
132 		argv = NULL;
133 	if (needcomm) {
134 		if (!commandonly) {
135 			if (kd != NULL) {
136 				argv = kvm_getargv2(kd, kp, termwidth);
137 				if ((p = argv) != NULL) {
138 					if (wantspace) {
139 						fmt_putc(' ', &left);
140 						wantspace = 0;
141 					}
142 					while (*p) {
143 						fmt_puts(*p, &left);
144 						p++;
145 						if (*p)
146 							fmt_putc(' ', &left);
147 						else
148 							wantspace = 1;
149 					}
150 				}
151 			}
152 			if (argv == NULL || argv[0] == '\0' ||
153 			    strcmp(cmdpart(argv[0]), kp->p_comm)) {
154 				if (wantspace) {
155 					fmt_putc(' ', &left);
156 					wantspace = 0;
157 				}
158 				fmt_putc('(', &left);
159 				fmt_puts(kp->p_comm, &left);
160 				fmt_putc(')', &left);
161 			}
162 		} else {
163 			if (wantspace) {
164 				fmt_putc(' ', &left);
165 				wantspace = 0;
166 			}
167 			fmt_puts(kp->p_comm, &left);
168 		}
169 	}
170 	if (ve->next && left > 0) {
171 		if (wantspace) {
172 			fmt_putc(' ', &left);
173 			wantspace = 0;
174 		}
175 		printf("%*s", left, "");
176 	}
177 }
178 
179 void
180 ucomm(const struct kinfo_proc2 *kp, VARENT *ve)
181 {
182 	VAR *v;
183 
184 	v = ve->var;
185 	(void)printf("%-*s", v->width, kp->p_comm);
186 }
187 
188 void
189 logname(const struct kinfo_proc2 *kp, VARENT *ve)
190 {
191 	VAR *v;
192 
193 	v = ve->var;
194 	if (kp->p_login[0]) {
195 		int n = min(v->width, MAXLOGNAME);
196 		(void)printf("%-*.*s", n, n, kp->p_login);
197 		if (v->width > n)
198 			(void)printf("%*s", v->width - n, "");
199 	} else
200 		(void)printf("%-*s", v->width, "-");
201 }
202 
203 #define pgtok(a)	(((a)*getpagesize())/1024)
204 
205 void
206 state(const struct kinfo_proc2 *kp, VARENT *ve)
207 {
208 	extern int ncpu;
209 	int flag;
210 	char *cp, state = '\0';
211 	VAR *v;
212 	char buf[16];
213 
214 	v = ve->var;
215 	flag = kp->p_flag;
216 	cp = buf;
217 
218 	switch (kp->p_stat) {
219 
220 	case SSTOP:
221 		*cp = 'T';
222 		break;
223 
224 	case SSLEEP:
225 		if (flag & P_SINTR)	/* interruptible (long) */
226 			*cp = kp->p_slptime >= maxslp ? 'I' : 'S';
227 		else
228 			*cp = 'D';
229 		break;
230 
231 	case SRUN:
232 	case SIDL:
233 	case SONPROC:
234 		state = *cp = 'R';
235 		break;
236 
237 	case SZOMB:
238 		*cp = 'Z';
239 		break;
240 
241 	default:
242 		*cp = '?';
243 	}
244 	cp++;
245 
246 	if (kp->p_nice < NZERO)
247 		*cp++ = '<';
248 	else if (kp->p_nice > NZERO)
249 		*cp++ = 'N';
250 	if (flag & P_TRACED)
251 		*cp++ = 'X';
252 	if (flag & P_SYSTRACE)
253 		*cp++ = 'x';
254 	if (flag & P_WEXIT && kp->p_stat != SZOMB)
255 		*cp++ = 'E';
256 	if (flag & P_PPWAIT)
257 		*cp++ = 'V';
258 	if (flag & P_SYSTEM)
259 		*cp++ = 'K';
260 	if ((flag & P_SYSTEM) == 0 &&
261 	    kp->p_rlim_rss_cur / 1024 < pgtok(kp->p_vm_rssize))
262 		*cp++ = '>';
263 	if (kp->p_eflag & EPROC_SLEADER)
264 		*cp++ = 's';
265 	if ((flag & P_CONTROLT) && kp->p__pgid == kp->p_tpgid)
266 		*cp++ = '+';
267 	*cp = '\0';
268 
269 	if (state == 'R' && ncpu && kp->p_cpuid != KI_NOCPU) {
270 		char pbuf[16];
271 
272 		snprintf(pbuf, sizeof pbuf, "/%llu", kp->p_cpuid);
273 		*++cp = '\0';
274 		strlcat(buf, pbuf, sizeof buf);
275 		cp = buf + strlen(buf);
276 	}
277 
278 	(void)printf("%-*s", v->width, buf);
279 }
280 
281 void
282 pri(const struct kinfo_proc2 *kp, VARENT *ve)
283 {
284 	VAR *v;
285 
286 	v = ve->var;
287 	(void)printf("%*d", v->width, kp->p_priority - PZERO);
288 }
289 
290 void
291 pnice(const struct kinfo_proc2 *kp, VARENT *ve)
292 {
293 	VAR *v;
294 	v = ve->var;
295 	(void)printf("%*d", v->width, kp->p_nice - NZERO);
296 }
297 
298 void
299 euname(const struct kinfo_proc2 *kp, VARENT *ve)
300 {
301 	VAR *v;
302 
303 	v = ve->var;
304 	(void)printf("%-*s",
305 	    (int)v->width, user_from_uid(kp->p_uid, 0));
306 }
307 
308 void
309 runame(const struct kinfo_proc2 *kp, VARENT *ve)
310 {
311 	VAR *v;
312 
313 	v = ve->var;
314 	(void)printf("%-*s",
315 	    (int)v->width, user_from_uid(kp->p_ruid, 0));
316 }
317 
318 void
319 gname(const struct kinfo_proc2 *kp, VARENT *ve)
320 {
321 	VAR *v;
322 
323 	v = ve->var;
324 	(void)printf("%-*s",
325 	    (int)v->width, group_from_gid(kp->p_gid, 0));
326 }
327 
328 void
329 rgname(const struct kinfo_proc2 *kp, VARENT *ve)
330 {
331 	VAR *v;
332 
333 	v = ve->var;
334 	(void)printf("%-*s",
335 	    (int)v->width, group_from_gid(kp->p_rgid, 0));
336 }
337 
338 void
339 tdev(const struct kinfo_proc2 *kp, VARENT *ve)
340 {
341 	VAR *v;
342 	dev_t dev;
343 	char buff[16];
344 
345 	v = ve->var;
346 	dev = kp->p_tdev;
347 	if (dev == NODEV)
348 		(void)printf("%*s", v->width, "??");
349 	else {
350 		(void)snprintf(buff, sizeof(buff),
351 		    "%d/%d", major(dev), minor(dev));
352 		(void)printf("%*s", v->width, buff);
353 	}
354 }
355 
356 void
357 tname(const struct kinfo_proc2 *kp, VARENT *ve)
358 {
359 	VAR *v;
360 	dev_t dev;
361 	char *ttname;
362 
363 	v = ve->var;
364 	dev = kp->p_tdev;
365 	if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL)
366 		(void)printf("%-*s", v->width, "??");
367 	else {
368 		if (strncmp(ttname, "tty", 3) == 0)
369 			ttname += 3;
370 		(void)printf("%*.*s%c", v->width-1, v->width-1, ttname,
371 			kp->p_eflag & EPROC_CTTY ? ' ' : '-');
372 	}
373 }
374 
375 void
376 longtname(const struct kinfo_proc2 *kp, VARENT *ve)
377 {
378 	VAR *v;
379 	dev_t dev;
380 	char *ttname;
381 
382 	v = ve->var;
383 	dev = kp->p_tdev;
384 	if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL)
385 		(void)printf("%-*s", v->width, "??");
386 	else
387 		(void)printf("%-*s", v->width, ttname);
388 }
389 
390 void
391 started(const struct kinfo_proc2 *kp, VARENT *ve)
392 {
393 	VAR *v;
394 	static time_t now;
395 	time_t startt;
396 	struct tm *tp;
397 	char buf[100];
398 
399 	v = ve->var;
400 	if (!kp->p_uvalid) {
401 		(void)printf("%-*s", v->width, "-");
402 		return;
403 	}
404 
405 	startt = kp->p_ustart_sec;
406 	tp = localtime(&startt);
407 	if (!now)
408 		(void)time(&now);
409 	if (now - kp->p_ustart_sec < 24 * SECSPERHOUR) {
410 		(void)strftime(buf, sizeof(buf) - 1, "%l:%M%p", tp);
411 	} else if (now - kp->p_ustart_sec < 7 * SECSPERDAY) {
412 		(void)strftime(buf, sizeof(buf) - 1, "%a%I%p", tp);
413 	} else
414 		(void)strftime(buf, sizeof(buf) - 1, "%e%b%y", tp);
415 	(void)printf("%-*s", v->width, buf);
416 }
417 
418 void
419 lstarted(const struct kinfo_proc2 *kp, VARENT *ve)
420 {
421 	VAR *v;
422 	time_t startt;
423 	char buf[100];
424 
425 	v = ve->var;
426 	if (!kp->p_uvalid) {
427 		(void)printf("%-*s", v->width, "-");
428 		return;
429 	}
430 	startt = kp->p_ustart_sec;
431 	(void)strftime(buf, sizeof(buf) -1, "%c",
432 	    localtime(&startt));
433 	(void)printf("%-*s", v->width, buf);
434 }
435 
436 void
437 wchan(const struct kinfo_proc2 *kp, VARENT *ve)
438 {
439 	VAR *v;
440 
441 	v = ve->var;
442 	if (kp->p_wchan) {
443 		int n;
444 
445 		if (kp->p_wmesg) {
446 			n = min(v->width, WMESGLEN);
447 			(void)printf("%-*.*s", n, n, kp->p_wmesg);
448 			if (v->width > n)
449 				(void)printf("%*s", v->width - n, "");
450 		} else
451 			(void)printf("%-*lx", v->width,
452 			    (long)kp->p_wchan &~ KERNBASE);
453 	} else
454 		(void)printf("%-*s", v->width, "-");
455 }
456 
457 void
458 vsize(const struct kinfo_proc2 *kp, VARENT *ve)
459 {
460 	VAR *v;
461 
462 	v = ve->var;
463 	(void)printf("%*d", v->width,
464 	    pgtok(kp->p_vm_dsize + kp->p_vm_ssize + kp->p_vm_tsize));
465 }
466 
467 void
468 rssize(const struct kinfo_proc2 *kp, VARENT *ve)
469 {
470 	VAR *v;
471 
472 	v = ve->var;
473 	/* XXX don't have info about shared */
474 	(void)printf("%*d", v->width, (kp->p_flag & P_SYSTEM) ? 0 :
475 	    pgtok(kp->p_vm_rssize));
476 }
477 
478 void
479 p_rssize(const struct kinfo_proc2 *kp, VARENT *ve)
480 {
481 	VAR *v;
482 
483 	v = ve->var;
484 	(void)printf("%*d", v->width, (kp->p_flag & P_SYSTEM) ? 0 :
485 	    pgtok(kp->p_vm_rssize));
486 }
487 
488 void
489 cputime(const struct kinfo_proc2 *kp, VARENT *ve)
490 {
491 	VAR *v;
492 	long secs;
493 	long psecs;	/* "parts" of a second. first micro, then centi */
494 	char obuff[128];
495 
496 	v = ve->var;
497 	if (kp->p_stat == SZOMB || !kp->p_uvalid) {
498 		secs = 0;
499 		psecs = 0;
500 	} else {
501 		/*
502 		 * This counts time spent handling interrupts.  We could
503 		 * fix this, but it is not 100% trivial (and interrupt
504 		 * time fractions only work on the sparc anyway).	XXX
505 		 */
506 		secs = kp->p_rtime_sec;
507 		psecs = kp->p_rtime_usec;
508 		if (sumrusage) {
509 			secs += kp->p_uctime_sec;
510 			psecs += kp->p_uctime_usec;
511 		}
512 		/*
513 		 * round and scale to 100's
514 		 */
515 		psecs = (psecs + 5000) / 10000;
516 		secs += psecs / 100;
517 		psecs = psecs % 100;
518 	}
519 	(void)snprintf(obuff, sizeof(obuff),
520 	    "%3ld:%02ld.%02ld", secs/60, secs%60, psecs);
521 	(void)printf("%*s", v->width, obuff);
522 }
523 
524 double
525 getpcpu(const struct kinfo_proc2 *kp)
526 {
527 	double d;
528 
529 	if (fscale == 0)
530 		return (0.0);
531 
532 #define	fxtofl(fixpt)	((double)(fixpt) / fscale)
533 
534 	/* XXX - I don't like this */
535 	if (kp->p_swtime == 0)
536 		return (0.0);
537 	if (rawcpu)
538 		return (100.0 * fxtofl(kp->p_pctcpu));
539 
540 	d = kp->p_swtime * log(fxtofl(ccpu));
541 	if (d < -700.0)
542 		d = 0.0;		/* avoid IEEE underflow */
543 	else
544 		d = exp(d);
545 	if (d == 1.0)
546 		return (0.0);
547 	return (100.0 * fxtofl(kp->p_pctcpu) /
548 		(1.0 - d));
549 }
550 
551 void
552 pcpu(const struct kinfo_proc2 *kp, VARENT *ve)
553 {
554 	VAR *v;
555 
556 	v = ve->var;
557 	(void)printf("%*.1f", v->width, getpcpu(kp));
558 }
559 
560 double
561 getpmem(const struct kinfo_proc2 *kp)
562 {
563 	double fracmem;
564 	int szptudot;
565 
566 	if (mempages == 0)
567 		return (0.0);
568 
569 	if (kp->p_flag & P_SYSTEM)
570 		return (0.0);
571 	/* XXX want pmap ptpages, segtab, etc. (per architecture) */
572 	szptudot = USPACE/getpagesize();
573 	/* XXX don't have info about shared */
574 	fracmem = ((float)kp->p_vm_rssize + szptudot)/mempages;
575 	return (100.0 * fracmem);
576 }
577 
578 void
579 pmem(const struct kinfo_proc2 *kp, VARENT *ve)
580 {
581 	VAR *v;
582 
583 	v = ve->var;
584 	(void)printf("%*.1f", v->width, getpmem(kp));
585 }
586 
587 void
588 pagein(const struct kinfo_proc2 *kp, VARENT *ve)
589 {
590 	VAR *v;
591 
592 	v = ve->var;
593 	(void)printf("%*llu", v->width,
594 	    kp->p_uvalid ? kp->p_uru_majflt : 0);
595 }
596 
597 void
598 maxrss(const struct kinfo_proc2 *kp, VARENT *ve)
599 {
600 	VAR *v;
601 
602 	v = ve->var;
603 	(void)printf("%*lld", v->width, kp->p_rlim_rss_cur / 1024);
604 }
605 
606 void
607 tsize(const struct kinfo_proc2 *kp, VARENT *ve)
608 {
609 	VAR *v;
610 
611 	v = ve->var;
612 	(void)printf("%*d", v->width, pgtok(kp->p_vm_tsize));
613 }
614 
615 void
616 dsize(const struct kinfo_proc2 *kp, VARENT *ve)
617 {
618 	VAR *v;
619 
620 	v = ve->var;
621 	(void)printf("%*d", v->width, pgtok(kp->p_vm_dsize));
622 }
623 
624 void
625 ssize(const struct kinfo_proc2 *kp, VARENT *ve)
626 {
627 	VAR *v;
628 
629 	v = ve->var;
630 	(void)printf("%*d", v->width, pgtok(kp->p_vm_ssize));
631 }
632 
633 /*
634  * Generic output routines.  Print fields from various prototype
635  * structures.
636  */
637 static void
638 printval(char *bp, VAR *v)
639 {
640 	char ofmt[32];
641 
642 	snprintf(ofmt, sizeof(ofmt), "%%%s*%s", (v->flag & LJUST) ? "-" : "",
643 	    v->fmt);
644 
645 	/*
646 	 * Note that the "INF127" check is nonsensical for types
647 	 * that are or can be signed.
648 	 */
649 #define	GET(type)		(*(type *)bp)
650 #define	CHK_INF127(n)		(((n) > 127) && (v->flag & INF127) ? 127 : (n))
651 
652 	switch (v->type) {
653 	case INT8:
654 		(void)printf(ofmt, v->width, GET(int8_t));
655 		break;
656 	case UINT8:
657 		(void)printf(ofmt, v->width, CHK_INF127(GET(u_int8_t)));
658 		break;
659 	case INT16:
660 		(void)printf(ofmt, v->width, GET(int16_t));
661 		break;
662 	case UINT16:
663 		(void)printf(ofmt, v->width, CHK_INF127(GET(u_int16_t)));
664 		break;
665 	case INT32:
666 		(void)printf(ofmt, v->width, GET(int32_t));
667 		break;
668 	case UINT32:
669 		(void)printf(ofmt, v->width, CHK_INF127(GET(u_int32_t)));
670 		break;
671 	case INT64:
672 		(void)printf(ofmt, v->width, GET(int64_t));
673 		break;
674 	case UINT64:
675 		(void)printf(ofmt, v->width, CHK_INF127(GET(u_int64_t)));
676 		break;
677 	default:
678 		errx(1, "unknown type %d", v->type);
679 	}
680 #undef GET
681 #undef CHK_INF127
682 }
683 
684 void
685 pvar(const struct kinfo_proc2 *kp, VARENT *ve)
686 {
687 	VAR *v;
688 
689 	v = ve->var;
690 	if ((v->flag & USER) && !kp->p_uvalid)
691 		(void)printf("%*s", v->width, "-");
692 	else
693 		printval((char *)kp + v->off, v);
694 }
695 
696 void
697 emulname(const struct kinfo_proc2 *kp, VARENT *ve)
698 {
699 	VAR *v;
700 
701 	v = ve->var;
702 
703 	(void)printf("%-*s", (int)v->width, kp->p_emul);
704 }
705