xref: /openbsd-src/bin/ps/print.c (revision 4b70baf6e17fc8b27fc1f7fa7929335753fa94c3)
1 /*	$OpenBSD: print.c,v 1.72 2019/03/24 04:55:01 deraadt 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 #include <sys/param.h>	/* MAXCOMLEN PZERO NODEV */
34 #include <sys/types.h>
35 #include <sys/proc.h>
36 #include <sys/stat.h>
37 
38 #include <sys/sysctl.h>
39 
40 #include <err.h>
41 #include <grp.h>
42 #include <kvm.h>
43 #include <math.h>
44 #include <nlist.h>
45 #include <stddef.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <limits.h>
51 #include <pwd.h>
52 
53 #include "ps.h"
54 
55 extern kvm_t *kd;
56 extern int needenv, needcomm, neednlist, commandonly;
57 
58 int mbswprint(const char *, int, int);  /* utf8.c */
59 
60 static char *cmdpart(char *);
61 
62 #define	min(a,b)	((a) < (b) ? (a) : (b))
63 
64 static char *
65 cmdpart(char *arg0)
66 {
67 	char *cp;
68 
69 	return ((cp = strrchr(arg0, '/')) != NULL ? cp + 1 : arg0);
70 }
71 
72 void
73 printheader(void)
74 {
75 	VAR *v;
76 	struct varent *vent;
77 
78 	if (!needheader)
79 		return;
80 	for (vent = vhead; vent; vent = vent->next) {
81 		v = vent->var;
82 		if (v->flag & LJUST) {
83 			if (vent->next == NULL)	/* last one */
84 				(void)printf("%s", v->header);
85 			else
86 				(void)printf("%-*s", v->width, v->header);
87 		} else
88 			(void)printf("%*s", v->width, v->header);
89 		if (vent->next != NULL)
90 			(void)putchar(' ');
91 	}
92 	(void)putchar('\n');
93 }
94 
95 void
96 command(const struct kinfo_proc *kp, VARENT *ve)
97 {
98 	VAR *v;
99 	int left, wantspace = 0;
100 	char **p;
101 
102 	/*
103 	 * Determine the available number of display columns.
104 	 * Always decrement and check after writing.
105 	 * No check is needed before mbswprint()
106 	 * and after writing the last data, though.
107 	 */
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 = INT_MAX;
119 
120 	if (needenv && kd != NULL) {
121 		char **envp = kvm_getenvv(kd, kp, termwidth);
122 		if ((p = envp) != NULL) {
123 			while (*p) {
124 				if (wantspace) {
125 					putchar(' ');
126 					left--;
127 				}
128 				left -= mbswprint(*p, left, 0);
129 				if (left == 0)
130 					return;
131 				p++;
132 				wantspace = 1;
133 			}
134 		}
135 	}
136 
137 	if (needcomm) {
138 		if (!commandonly) {
139 			char **argv = NULL;
140 
141 			if (kd != NULL) {
142 				argv = kvm_getargv(kd, kp, termwidth);
143 				if ((p = argv) != NULL) {
144 					while (*p) {
145 						if (wantspace) {
146 							putchar(' ');
147 							left--;
148 						}
149 						left -= mbswprint(*p, left, 0);
150 						if (left == 0)
151 							return;
152 						p++;
153 						wantspace = 1;
154 					}
155 				}
156 			}
157 			if (argv == NULL || argv[0] == '\0' ||
158 			    strcmp(cmdpart(argv[0]), kp->p_comm)) {
159 				if (wantspace) {
160 					putchar(' ');
161 					if (--left == 0)
162 						return;
163 				}
164 				putchar('(');
165 				left--;
166 				left -= mbswprint(kp->p_comm, left, 0);
167 				if (left == 0)
168 					return;
169 				putchar(')');
170 				left--;
171 			}
172 		} else {
173 			if (wantspace) {
174 				putchar(' ');
175 				left--;
176 			}
177 			left -= mbswprint(kp->p_comm, left, 0);
178 		}
179 	}
180 	if (ve->next != NULL)
181 		while (left-- > 0)
182 			putchar(' ');
183 }
184 
185 void
186 ucomm(const struct kinfo_proc *kp, VARENT *ve)
187 {
188 	mbswprint(kp->p_comm, ve->var->width, ve->next != NULL);
189 }
190 
191 void
192 curwd(const struct kinfo_proc *kp, VARENT *ve)
193 {
194 	int name[] = { CTL_KERN, KERN_PROC_CWD, kp->p_pid };
195 	char path[PATH_MAX];
196 	size_t pathlen = sizeof path;
197 
198 	if (!kvm_sysctl_only || sysctl(name, 3, path, &pathlen, NULL, 0) != 0)
199 		*path = '\0';
200 
201 	mbswprint(path, ve->var->width, ve->next != NULL);
202 }
203 
204 void
205 logname(const struct kinfo_proc *kp, VARENT *ve)
206 {
207 	VAR *v;
208 
209 	v = ve->var;
210 	if (kp->p_login[0]) {
211 		int n = min(v->width, LOGIN_NAME_MAX);
212 		mbswprint(kp->p_login, n, ve->next != NULL);
213 		if (ve->next != NULL)
214 			while (n++ < v->width)
215 				putchar(' ');
216 	} else
217 		(void)printf("%-*s", v->width, "-");
218 }
219 
220 #define pgtok(a)	(((unsigned long long)(a)*getpagesize())/1024)
221 
222 void
223 printstate(const struct kinfo_proc *kp, VARENT *ve)
224 {
225 	int flag;
226 	char *cp, state = '\0';
227 	VAR *v;
228 	char buf[16];
229 
230 	v = ve->var;
231 	flag = kp->p_flag;
232 	cp = buf;
233 
234 	switch (kp->p_stat) {
235 
236 	case SSTOP:
237 		*cp = 'T';
238 		break;
239 
240 	case SSLEEP:
241 		if (flag & P_SINTR)	/* interruptible (long) */
242 			*cp = kp->p_slptime >= maxslp ? 'I' : 'S';
243 		else
244 			*cp = 'D';
245 		break;
246 
247 	case SRUN:
248 	case SIDL:
249 	case SONPROC:
250 		state = *cp = 'R';
251 		break;
252 
253 	case SDEAD:
254 		*cp = 'Z';
255 		break;
256 
257 	default:
258 		*cp = '?';
259 	}
260 	cp++;
261 
262 	if (kp->p_nice < NZERO)
263 		*cp++ = '<';
264 	else if (kp->p_nice > NZERO)
265 		*cp++ = 'N';
266 	if (kp->p_psflags & PS_TRACED)
267 		*cp++ = 'X';
268 	if ((kp->p_psflags & (PS_EXITING | PS_ZOMBIE)) == PS_EXITING)
269 		*cp++ = 'E';
270 	if (kp->p_psflags & PS_ISPWAIT)
271 		*cp++ = 'V';
272 	if (flag & P_SYSTEM)
273 		*cp++ = 'K';
274 	if ((flag & P_SYSTEM) == 0 &&
275 	    kp->p_rlim_rss_cur / 1024 < pgtok(kp->p_vm_rssize))
276 		*cp++ = '>';
277 	if (kp->p_eflag & EPROC_SLEADER)
278 		*cp++ = 's';
279 	if ((kp->p_psflags & PS_CONTROLT) && kp->p__pgid == kp->p_tpgid)
280 		*cp++ = '+';
281 	if (kp->p_psflags & PS_PLEDGE)
282 		*cp++ = 'p';
283 	*cp = '\0';
284 
285 	if (state == 'R' && kp->p_cpuid != KI_NOCPU) {
286 		char pbuf[16];
287 
288 		snprintf(pbuf, sizeof pbuf, "/%llu", kp->p_cpuid);
289 		*++cp = '\0';
290 		strlcat(buf, pbuf, sizeof buf);
291 		cp = buf + strlen(buf);
292 	}
293 
294 	(void)printf("%-*s", v->width, buf);
295 }
296 
297 void
298 pri(const struct kinfo_proc *kp, VARENT *ve)
299 {
300 	VAR *v;
301 
302 	v = ve->var;
303 	(void)printf("%*d", v->width, kp->p_priority - PZERO);
304 }
305 
306 void
307 pnice(const struct kinfo_proc *kp, VARENT *ve)
308 {
309 	VAR *v;
310 	v = ve->var;
311 	(void)printf("%*d", v->width, kp->p_nice - NZERO);
312 }
313 
314 void
315 euname(const struct kinfo_proc *kp, VARENT *ve)
316 {
317 	mbswprint(user_from_uid(kp->p_uid, 0), ve->var->width,
318 	    ve->next != NULL);
319 }
320 
321 void
322 runame(const struct kinfo_proc *kp, VARENT *ve)
323 {
324 	mbswprint(user_from_uid(kp->p_ruid, 0), ve->var->width,
325 	    ve->next != NULL);
326 }
327 
328 void
329 gname(const struct kinfo_proc *kp, VARENT *ve)
330 {
331 	mbswprint(group_from_gid(kp->p_gid, 0), ve->var->width,
332 	    ve->next != NULL);
333 }
334 
335 void
336 rgname(const struct kinfo_proc *kp, VARENT *ve)
337 {
338 	mbswprint(group_from_gid(kp->p_rgid, 0), ve->var->width,
339 	    ve->next != NULL);
340 }
341 
342 void
343 tdev(const struct kinfo_proc *kp, VARENT *ve)
344 {
345 	VAR *v;
346 	dev_t dev;
347 
348 	v = ve->var;
349 	dev = kp->p_tdev;
350 	if (dev == NODEV)
351 		(void)printf("%*s", v->width, "??");
352 	else {
353 		char buff[10+1+10+1];
354 
355 		(void)snprintf(buff, sizeof(buff),
356 		    "%u/%u", major(dev), minor(dev));
357 		(void)printf("%*s", v->width, buff);
358 	}
359 }
360 
361 void
362 tname(const struct kinfo_proc *kp, VARENT *ve)
363 {
364 	VAR *v;
365 	dev_t dev;
366 	char *ttname;
367 
368 	v = ve->var;
369 	dev = kp->p_tdev;
370 	if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL)
371 		(void)printf("%-*s", v->width, "??");
372 	else {
373 		if (strncmp(ttname, "tty", 3) == 0)
374 			ttname += 3;
375 		(void)printf("%*.*s%c", v->width-1, v->width-1, ttname,
376 			kp->p_eflag & EPROC_CTTY ? ' ' : '-');
377 	}
378 }
379 
380 void
381 longtname(const struct kinfo_proc *kp, VARENT *ve)
382 {
383 	VAR *v;
384 	dev_t dev;
385 	char *ttname;
386 
387 	v = ve->var;
388 	dev = kp->p_tdev;
389 	if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL)
390 		(void)printf("%-*s", v->width, "??");
391 	else
392 		(void)printf("%-*s", v->width, ttname);
393 }
394 
395 void
396 started(const struct kinfo_proc *kp, VARENT *ve)
397 {
398 	VAR *v;
399 	static time_t now;
400 	time_t startt;
401 	struct tm *tp;
402 	char buf[100];
403 
404 	v = ve->var;
405 	if (!kp->p_uvalid) {
406 		(void)printf("%-*s", v->width, "-");
407 		return;
408 	}
409 
410 #define SECSPERHOUR	(60 * 60)
411 #define SECSPERDAY	(24 * 60 * 60)
412 
413 	startt = kp->p_ustart_sec;
414 	tp = localtime(&startt);
415 	if (!now)
416 		(void)time(&now);
417 	if (now - kp->p_ustart_sec < 12 * SECSPERHOUR) {
418 		(void)strftime(buf, sizeof(buf) - 1, "%l:%M%p", tp);
419 	} else if (now - kp->p_ustart_sec < 7 * SECSPERDAY) {
420 		(void)strftime(buf, sizeof(buf) - 1, "%a%I%p", tp);
421 	} else
422 		(void)strftime(buf, sizeof(buf) - 1, "%e%b%y", tp);
423 	(void)printf("%-*s", v->width, buf);
424 }
425 
426 void
427 lstarted(const struct kinfo_proc *kp, VARENT *ve)
428 {
429 	VAR *v;
430 	time_t startt;
431 	char buf[100];
432 
433 	v = ve->var;
434 	if (!kp->p_uvalid) {
435 		(void)printf("%-*s", v->width, "-");
436 		return;
437 	}
438 	startt = kp->p_ustart_sec;
439 	(void)strftime(buf, sizeof(buf) -1, "%c",
440 	    localtime(&startt));
441 	(void)printf("%-*s", v->width, buf);
442 }
443 
444 void elapsed(const struct kinfo_proc *kp, VARENT *ve)
445 {
446 	VAR *v;
447 	static time_t now;
448 	time_t secs;
449 	char buf[64];
450 	long days, hours, minutes, seconds;
451 
452 	v = ve->var;
453 	if (!kp->p_uvalid) {
454 		(void)printf("%*s", v->width, "-");
455 		return;
456 	}
457 
458 	if (!now)
459 		(void)time(&now);
460 	secs = now - kp->p_ustart_sec;
461 
462 	if (secs < 0) {
463 		(void)printf("%*s", v->width, "-");
464 		return;
465 	}
466 
467 	days = secs / SECSPERDAY;
468 	secs %= SECSPERDAY;
469 
470 	hours = secs / SECSPERHOUR;
471 	secs %= SECSPERHOUR;
472 
473 	minutes = secs / 60;
474 	seconds = secs % 60;
475 
476 	if (days > 0)
477 		(void)snprintf(buf, sizeof(buf), "%ld-%02ld:%02ld:%02ld",
478 		    days, hours, minutes, seconds);
479 	else if (hours > 0)
480 		(void)snprintf(buf, sizeof(buf), "%02ld:%02ld:%02ld",
481 		    hours, minutes, seconds);
482 	else
483 		(void)snprintf(buf, sizeof(buf), "%02ld:%02ld",
484 		    minutes, seconds);
485 	(void)printf("%*s", v->width, buf);
486 }
487 
488 void
489 wchan(const struct kinfo_proc *kp, VARENT *ve)
490 {
491 	VAR *v;
492 
493 	v = ve->var;
494 	if (kp->p_wmesg[0]) {
495 		(void)printf("%-*s", (int)v->width, kp->p_wmesg);
496 	} else
497 		(void)printf("%-*s", v->width, "-");
498 }
499 
500 void
501 vsize(const struct kinfo_proc *kp, VARENT *ve)
502 {
503 	VAR *v;
504 
505 	v = ve->var;
506 	(void)printf("%*llu", v->width,
507 	    pgtok(kp->p_vm_dsize + kp->p_vm_ssize + kp->p_vm_tsize));
508 }
509 
510 void
511 rssize(const struct kinfo_proc *kp, VARENT *ve)
512 {
513 	VAR *v;
514 
515 	v = ve->var;
516 	/* XXX don't have info about shared */
517 	(void)printf("%*llu", v->width, (kp->p_flag & P_SYSTEM) ? 0 :
518 	    pgtok(kp->p_vm_rssize));
519 }
520 
521 void
522 p_rssize(const struct kinfo_proc *kp, VARENT *ve)
523 {
524 	VAR *v;
525 
526 	v = ve->var;
527 	(void)printf("%*llu", v->width, (kp->p_flag & P_SYSTEM) ? 0 :
528 	    pgtok(kp->p_vm_rssize));
529 }
530 
531 void
532 cputime(const struct kinfo_proc *kp, VARENT *ve)
533 {
534 	VAR *v;
535 	long secs;
536 	long psecs;	/* "parts" of a second. first micro, then centi */
537 	char obuff[128];
538 
539 	v = ve->var;
540 	if (kp->p_stat == SDEAD || !kp->p_uvalid) {
541 		secs = 0;
542 		psecs = 0;
543 	} else {
544 		/*
545 		 * This counts time spent handling interrupts.  XXX
546 		 */
547 		secs = kp->p_rtime_sec;
548 		psecs = kp->p_rtime_usec;
549 		if (sumrusage) {
550 			secs += kp->p_uctime_sec;
551 			psecs += kp->p_uctime_usec;
552 		}
553 		/*
554 		 * round and scale to 100's
555 		 */
556 		psecs = (psecs + 5000) / 10000;
557 		secs += psecs / 100;
558 		psecs = psecs % 100;
559 	}
560 	(void)snprintf(obuff, sizeof(obuff),
561 	    "%3ld:%02ld.%02ld", secs/60, secs%60, psecs);
562 	(void)printf("%*s", v->width, obuff);
563 }
564 
565 double
566 getpcpu(const struct kinfo_proc *kp)
567 {
568 	if (fscale == 0)
569 		return (0.0);
570 
571 #define	fxtofl(fixpt)	((double)(fixpt) / fscale)
572 
573 	return (100.0 * fxtofl(kp->p_pctcpu));
574 }
575 
576 void
577 pcpu(const struct kinfo_proc *kp, VARENT *ve)
578 {
579 	VAR *v;
580 
581 	v = ve->var;
582 	(void)printf("%*.1f", v->width, getpcpu(kp));
583 }
584 
585 double
586 getpmem(const struct kinfo_proc *kp)
587 {
588 	double fracmem;
589 
590 	if (mempages == 0)
591 		return (0.0);
592 
593 	if (kp->p_flag & P_SYSTEM)
594 		return (0.0);
595 	/* XXX don't have info about shared */
596 	fracmem = ((float)kp->p_vm_rssize)/mempages;
597 	return (100.0 * fracmem);
598 }
599 
600 void
601 pmem(const struct kinfo_proc *kp, VARENT *ve)
602 {
603 	VAR *v;
604 
605 	v = ve->var;
606 	(void)printf("%*.1f", v->width, getpmem(kp));
607 }
608 
609 void
610 pagein(const struct kinfo_proc *kp, VARENT *ve)
611 {
612 	VAR *v;
613 
614 	v = ve->var;
615 	(void)printf("%*llu", v->width,
616 	    kp->p_uvalid ? kp->p_uru_majflt : 0);
617 }
618 
619 void
620 maxrss(const struct kinfo_proc *kp, VARENT *ve)
621 {
622 	VAR *v;
623 
624 	v = ve->var;
625 	(void)printf("%*llu", v->width, kp->p_rlim_rss_cur / 1024);
626 }
627 
628 void
629 tsize(const struct kinfo_proc *kp, VARENT *ve)
630 {
631 	VAR *v;
632 
633 	v = ve->var;
634 	(void)printf("%*llu", v->width, pgtok(kp->p_vm_tsize));
635 }
636 
637 void
638 dsize(const struct kinfo_proc *kp, VARENT *ve)
639 {
640 	VAR *v;
641 
642 	v = ve->var;
643 	(void)printf("%*llu", v->width, pgtok(kp->p_vm_dsize));
644 }
645 
646 void
647 ssize(const struct kinfo_proc *kp, VARENT *ve)
648 {
649 	VAR *v;
650 
651 	v = ve->var;
652 	(void)printf("%*llu", v->width, pgtok(kp->p_vm_ssize));
653 }
654 
655 /*
656  * Generic output routines.  Print fields from various prototype
657  * structures.
658  */
659 static void
660 printval(char *bp, VAR *v)
661 {
662 	char ofmt[32];
663 
664 	snprintf(ofmt, sizeof(ofmt), "%%%s*%s", (v->flag & LJUST) ? "-" : "",
665 	    v->fmt);
666 
667 	/*
668 	 * Note that the "INF127" check is nonsensical for types
669 	 * that are or can be signed.
670 	 */
671 #define	GET(type)		(*(type *)bp)
672 #define	CHK_INF127(n)		(((n) > 127) && (v->flag & INF127) ? 127 : (n))
673 
674 	switch (v->type) {
675 	case INT8:
676 		(void)printf(ofmt, v->width, GET(int8_t));
677 		break;
678 	case UINT8:
679 		(void)printf(ofmt, v->width, CHK_INF127(GET(u_int8_t)));
680 		break;
681 	case INT16:
682 		(void)printf(ofmt, v->width, GET(int16_t));
683 		break;
684 	case UINT16:
685 		(void)printf(ofmt, v->width, CHK_INF127(GET(u_int16_t)));
686 		break;
687 	case INT32:
688 		(void)printf(ofmt, v->width, GET(int32_t));
689 		break;
690 	case UINT32:
691 		(void)printf(ofmt, v->width, CHK_INF127(GET(u_int32_t)));
692 		break;
693 	case INT64:
694 		(void)printf(ofmt, v->width, GET(int64_t));
695 		break;
696 	case UINT64:
697 		(void)printf(ofmt, v->width, CHK_INF127(GET(u_int64_t)));
698 		break;
699 	default:
700 		errx(1, "unknown type %d", v->type);
701 	}
702 #undef GET
703 #undef CHK_INF127
704 }
705 
706 void
707 pvar(const struct kinfo_proc *kp, VARENT *ve)
708 {
709 	VAR *v;
710 
711 	v = ve->var;
712 	if ((v->flag & USER) && !kp->p_uvalid)
713 		(void)printf("%*s", v->width, "-");
714 	else
715 		printval((char *)kp + v->off, v);
716 }
717 
718 void
719 emulname(const struct kinfo_proc *kp, VARENT *ve)
720 {
721 	VAR *v;
722 
723 	v = ve->var;
724 
725 	(void)printf("%-*s", (int)v->width, kp->p_emul);
726 }
727