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