xref: /freebsd/usr.sbin/pstat/pstat.c (revision b9c36cc755002809a7d7c7109e3425fdfca036d2)
1 /*-
2  * Copyright (c) 1980, 1991, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 2002 Networks Associates Technologies, Inc.
5  * All rights reserved.
6  *
7  * Portions of this software were developed for the FreeBSD Project by
8  * ThinkSec AS and NAI Labs, the Security Research Division of Network
9  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10  * ("CBOSS"), as part of the DARPA CHATS research program.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #if 0
38 #ifndef lint
39 static const char copyright[] =
40 "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
41 	The Regents of the University of California.  All rights reserved.\n";
42 #endif /* not lint */
43 
44 #ifndef lint
45 static char sccsid[] = "@(#)pstat.c	8.16 (Berkeley) 5/9/95";
46 #endif /* not lint */
47 #endif
48 #include <sys/cdefs.h>
49 __FBSDID("$FreeBSD$");
50 
51 #include <sys/param.h>
52 #include <sys/time.h>
53 #include <sys/file.h>
54 #include <sys/stat.h>
55 #include <sys/stdint.h>
56 #include <sys/ioctl.h>
57 #include <sys/tty.h>
58 #include <sys/blist.h>
59 
60 #include <sys/sysctl.h>
61 #include <vm/vm_param.h>
62 
63 #include <err.h>
64 #include <errno.h>
65 #include <fcntl.h>
66 #include <kvm.h>
67 #include <libutil.h>
68 #include <limits.h>
69 #include <nlist.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <unistd.h>
74 
75 enum {
76 	NL_CONSTTY,
77 	NL_MAXFILES,
78 	NL_NFILES,
79 	NL_TTY_LIST,
80 	NL_MARKER
81 };
82 
83 static struct {
84 	int order;
85 	const char *name;
86 } namelist[] = {
87 	{ NL_CONSTTY, "_constty" },
88 	{ NL_MAXFILES, "_maxfiles" },
89 	{ NL_NFILES, "_openfiles" },
90 	{ NL_TTY_LIST, "_tty_list" },
91 	{ NL_MARKER, "" },
92 };
93 #define NNAMES	(sizeof(namelist) / sizeof(*namelist))
94 static struct nlist nl[NNAMES];
95 
96 static int	humanflag;
97 static int	usenumflag;
98 static int	totalflag;
99 static int	swapflag;
100 static char	*nlistf;
101 static char	*memf;
102 static kvm_t	*kd;
103 
104 static const char *usagestr;
105 
106 static void	filemode(void);
107 static int	getfiles(struct xfile **, size_t *);
108 static void	swapmode(void);
109 static void	ttymode(void);
110 static void	ttyprt(struct xtty *);
111 static void	usage(void);
112 
113 int
114 main(int argc, char *argv[])
115 {
116 	int ch, quit, ret;
117 	int fileflag, ttyflag;
118 	unsigned int i;
119 	char buf[_POSIX2_LINE_MAX];
120 	const char *opts;
121 
122 	fileflag = swapflag = ttyflag = 0;
123 
124 	/* We will behave like good old swapinfo if thus invoked */
125 	opts = strrchr(argv[0], '/');
126 	if (opts)
127 		opts++;
128 	else
129 		opts = argv[0];
130 	if (!strcmp(opts, "swapinfo")) {
131 		swapflag = 1;
132 		opts = "ghkmM:N:";
133 		usagestr = "swapinfo [-ghkm] [-M core [-N system]]";
134 	} else {
135 		opts = "TM:N:fghkmnst";
136 		usagestr = "pstat [-Tfghkmnst] [-M core [-N system]]";
137 	}
138 
139 	while ((ch = getopt(argc, argv, opts)) != -1)
140 		switch (ch) {
141 		case 'f':
142 			fileflag = 1;
143 			break;
144 		case 'g':
145 			setenv("BLOCKSIZE", "1G", 1);
146 			break;
147 		case 'h':
148 			humanflag = 1;
149 			break;
150 		case 'k':
151 			setenv("BLOCKSIZE", "1K", 1);
152 			break;
153 		case 'm':
154 			setenv("BLOCKSIZE", "1M", 1);
155 			break;
156 		case 'M':
157 			memf = optarg;
158 			break;
159 		case 'N':
160 			nlistf = optarg;
161 			break;
162 		case 'n':
163 			usenumflag = 1;
164 			break;
165 		case 's':
166 			++swapflag;
167 			break;
168 		case 'T':
169 			totalflag = 1;
170 			break;
171 		case 't':
172 			ttyflag = 1;
173 			break;
174 		default:
175 			usage();
176 		}
177 
178 	/*
179 	 * Initialize symbol names list.
180 	 */
181 	for (i = 0; i < NNAMES; i++)
182 		nl[namelist[i].order].n_name = strdup(namelist[i].name);
183 
184 	if (memf != NULL) {
185 		kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf);
186 		if (kd == NULL)
187 			errx(1, "kvm_openfiles: %s", buf);
188 		if ((ret = kvm_nlist(kd, nl)) != 0) {
189 			if (ret == -1)
190 				errx(1, "kvm_nlist: %s", kvm_geterr(kd));
191 			quit = 0;
192 			for (i = 0; nl[i].n_name[0] != '\0'; ++i)
193 				if (nl[i].n_value == 0) {
194 					quit = 1;
195 					warnx("undefined symbol: %s",
196 					    nl[i].n_name);
197 				}
198 			if (quit)
199 				exit(1);
200 		}
201 	}
202 	if (!(fileflag | ttyflag | swapflag | totalflag))
203 		usage();
204 	if (fileflag || totalflag)
205 		filemode();
206 	if (ttyflag)
207 		ttymode();
208 	if (swapflag || totalflag)
209 		swapmode();
210 	exit (0);
211 }
212 
213 static void
214 usage(void)
215 {
216 	fprintf(stderr, "usage: %s\n", usagestr);
217 	exit (1);
218 }
219 
220 static const char fhdr32[] =
221   "   LOC   TYPE   FLG  CNT MSG   DATA        OFFSET\n";
222 /* c0000000 ------ RWAI 123 123 c0000000 1000000000000000 */
223 
224 static const char fhdr64[] =
225   "       LOC       TYPE   FLG  CNT MSG       DATA            OFFSET\n";
226 /* c000000000000000 ------ RWAI 123 123 c000000000000000 1000000000000000 */
227 
228 static const char hdr[] =
229 "      LINE   INQ  CAN  LIN  LOW  OUTQ  USE  LOW   COL  SESS  PGID STATE\n";
230 
231 static void
232 ttymode_kvm(void)
233 {
234 	TAILQ_HEAD(, tty) tl;
235 	struct tty *tp, tty;
236 	struct xtty xt;
237 
238 	(void)printf("%s", hdr);
239 	bzero(&xt, sizeof xt);
240 	xt.xt_size = sizeof xt;
241 	if (kvm_read(kd, nl[NL_TTY_LIST].n_value, &tl, sizeof tl) != sizeof tl)
242 		errx(1, "kvm_read(): %s", kvm_geterr(kd));
243 	tp = TAILQ_FIRST(&tl);
244 	while (tp != NULL) {
245 		if (kvm_read(kd, (u_long)tp, &tty, sizeof tty) != sizeof tty)
246 			errx(1, "kvm_read(): %s", kvm_geterr(kd));
247 		xt.xt_insize = tty.t_inq.ti_nblocks * TTYINQ_DATASIZE;
248 		xt.xt_incc = tty.t_inq.ti_linestart - tty.t_inq.ti_begin;
249 		xt.xt_inlc = tty.t_inq.ti_end - tty.t_inq.ti_linestart;
250 		xt.xt_inlow = tty.t_inlow;
251 		xt.xt_outsize = tty.t_outq.to_nblocks * TTYOUTQ_DATASIZE;
252 		xt.xt_outcc = tty.t_outq.to_end - tty.t_outq.to_begin;
253 		xt.xt_outlow = tty.t_outlow;
254 		xt.xt_column = tty.t_column;
255 		/* xt.xt_pgid = ... */
256 		/* xt.xt_sid = ... */
257 		xt.xt_flags = tty.t_flags;
258 		xt.xt_dev = NODEV;
259 		ttyprt(&xt);
260 		tp = TAILQ_NEXT(&tty, t_list);
261 	}
262 }
263 
264 static void
265 ttymode_sysctl(void)
266 {
267 	struct xtty *xttys;
268 	size_t len;
269 	unsigned int i, n;
270 
271 	(void)printf("%s", hdr);
272 	if ((xttys = malloc(len = sizeof(*xttys))) == NULL)
273 		err(1, "malloc()");
274 	while (sysctlbyname("kern.ttys", xttys, &len, 0, 0) == -1) {
275 		if (errno != ENOMEM)
276 			err(1, "sysctlbyname()");
277 		len *= 2;
278 		if ((xttys = realloc(xttys, len)) == NULL)
279 			err(1, "realloc()");
280 	}
281 	n = len / sizeof(*xttys);
282 	for (i = 0; i < n; i++)
283 		ttyprt(&xttys[i]);
284 }
285 
286 static void
287 ttymode(void)
288 {
289 
290 	if (kd != NULL)
291 		ttymode_kvm();
292 	else
293 		ttymode_sysctl();
294 }
295 
296 static struct {
297 	int flag;
298 	char val;
299 } ttystates[] = {
300 #if 0
301 	{ TF_NOPREFIX,		'N' },
302 #endif
303 	{ TF_INITLOCK,		'I' },
304 	{ TF_CALLOUT,		'C' },
305 
306 	/* Keep these together -> 'Oi' and 'Oo'. */
307 	{ TF_OPENED,		'O' },
308 	{ TF_OPENED_IN,		'i' },
309 	{ TF_OPENED_OUT,	'o' },
310 	{ TF_OPENED_CONS,	'c' },
311 
312 	{ TF_GONE,		'G' },
313 	{ TF_OPENCLOSE,		'B' },
314 	{ TF_ASYNC,		'Y' },
315 	{ TF_LITERAL,		'L' },
316 
317 	/* Keep these together -> 'Hi' and 'Ho'. */
318 	{ TF_HIWAT,		'H' },
319 	{ TF_HIWAT_IN,		'i' },
320 	{ TF_HIWAT_OUT,		'o' },
321 
322 	{ TF_STOPPED,		'S' },
323 	{ TF_EXCLUDE,		'X' },
324 	{ TF_BYPASS,		'l' },
325 	{ TF_ZOMBIE,		'Z' },
326 	{ TF_HOOK,		's' },
327 
328 	/* Keep these together -> 'bi' and 'bo'. */
329 	{ TF_BUSY,		'b' },
330 	{ TF_BUSY_IN,		'i' },
331 	{ TF_BUSY_OUT,		'o' },
332 
333 	{ 0,			'\0'},
334 };
335 
336 static void
337 ttyprt(struct xtty *xt)
338 {
339 	int i, j;
340 	const char *name;
341 
342 	if (xt->xt_size != sizeof *xt)
343 		errx(1, "struct xtty size mismatch");
344 	if (usenumflag || xt->xt_dev == 0 ||
345 	   (name = devname(xt->xt_dev, S_IFCHR)) == NULL)
346 		printf("%#10jx ", (uintmax_t)xt->xt_dev);
347 	else
348 		printf("%10s ", name);
349 	printf("%5zu %4zu %4zu %4zu %5zu %4zu %4zu %5u %5d %5d ",
350 	    xt->xt_insize, xt->xt_incc, xt->xt_inlc,
351 	    (xt->xt_insize - xt->xt_inlow), xt->xt_outsize,
352 	    xt->xt_outcc, (xt->xt_outsize - xt->xt_outlow),
353 	    MIN(xt->xt_column, 99999), xt->xt_sid, xt->xt_pgid);
354 	for (i = j = 0; ttystates[i].flag; i++)
355 		if (xt->xt_flags & ttystates[i].flag) {
356 			putchar(ttystates[i].val);
357 			j++;
358 		}
359 	if (j == 0)
360 		putchar('-');
361 	putchar('\n');
362 }
363 
364 static void
365 filemode(void)
366 {
367 	struct xfile *fp, *buf;
368 	char flagbuf[16], *fbp;
369 	int maxf, openf;
370 	size_t len;
371 	static char const * const dtypes[] = { "???", "inode", "socket",
372 	    "pipe", "fifo", "kqueue", "crypto" };
373 	int i;
374 	int wid;
375 
376 	if (kd != NULL) {
377 		if (kvm_read(kd, nl[NL_MAXFILES].n_value,
378 			&maxf, sizeof maxf) != sizeof maxf ||
379 		    kvm_read(kd, nl[NL_NFILES].n_value,
380 			&openf, sizeof openf) != sizeof openf)
381 			errx(1, "kvm_read(): %s", kvm_geterr(kd));
382 	} else {
383 		len = sizeof(int);
384 		if (sysctlbyname("kern.maxfiles", &maxf, &len, 0, 0) == -1 ||
385 		    sysctlbyname("kern.openfiles", &openf, &len, 0, 0) == -1)
386 			err(1, "sysctlbyname()");
387 	}
388 
389 	if (totalflag) {
390 		(void)printf("%3d/%3d files\n", openf, maxf);
391 		return;
392 	}
393 	if (getfiles(&buf, &len) == -1)
394 		return;
395 	openf = len / sizeof *fp;
396 
397 	(void)printf("%d/%d open files\n", openf, maxf);
398 	printf(sizeof(uintptr_t) == 4 ? fhdr32 : fhdr64);
399 	wid = (int)sizeof(uintptr_t) * 2;
400 	for (fp = (struct xfile *)buf, i = 0; i < openf; ++fp, ++i) {
401 		if ((size_t)fp->xf_type >= sizeof(dtypes) / sizeof(dtypes[0]))
402 			continue;
403 		(void)printf("%*jx", wid, (uintmax_t)(uintptr_t)fp->xf_file);
404 		(void)printf(" %-6.6s", dtypes[fp->xf_type]);
405 		fbp = flagbuf;
406 		if (fp->xf_flag & FREAD)
407 			*fbp++ = 'R';
408 		if (fp->xf_flag & FWRITE)
409 			*fbp++ = 'W';
410 		if (fp->xf_flag & FAPPEND)
411 			*fbp++ = 'A';
412 		if (fp->xf_flag & FASYNC)
413 			*fbp++ = 'I';
414 		*fbp = '\0';
415 		(void)printf(" %4s %3d", flagbuf, fp->xf_count);
416 		(void)printf(" %3d", fp->xf_msgcount);
417 		(void)printf(" %*jx", wid, (uintmax_t)(uintptr_t)fp->xf_data);
418 		(void)printf(" %*jx\n", (int)sizeof(fp->xf_offset) * 2,
419 		    (uintmax_t)fp->xf_offset);
420 	}
421 	free(buf);
422 }
423 
424 static int
425 getfiles(struct xfile **abuf, size_t *alen)
426 {
427 	struct xfile *buf;
428 	size_t len;
429 	int mib[2];
430 
431 	/*
432 	 * XXX
433 	 * Add emulation of KINFO_FILE here.
434 	 */
435 	if (kd != NULL)
436 		errx(1, "files on dead kernel, not implemented");
437 
438 	mib[0] = CTL_KERN;
439 	mib[1] = KERN_FILE;
440 	if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) {
441 		warn("sysctl: KERN_FILE");
442 		return (-1);
443 	}
444 	if ((buf = malloc(len)) == NULL)
445 		errx(1, "malloc");
446 	if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
447 		warn("sysctl: KERN_FILE");
448 		return (-1);
449 	}
450 	*abuf = buf;
451 	*alen = len;
452 	return (0);
453 }
454 
455 /*
456  * swapmode is based on a program called swapinfo written
457  * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
458  */
459 
460 #define CONVERT(v)	((int64_t)(v) * pagesize / blocksize)
461 #define CONVERT_BLOCKS(v)	((int64_t)(v) * pagesize)
462 static struct kvm_swap swtot;
463 static int nswdev;
464 
465 static void
466 print_swap_header(void)
467 {
468 	int hlen;
469 	long blocksize;
470 	const char *header;
471 
472 	header = getbsize(&hlen, &blocksize);
473 	if (totalflag == 0)
474 		(void)printf("%-15s %*s %8s %8s %8s\n",
475 		    "Device", hlen, header,
476 		    "Used", "Avail", "Capacity");
477 }
478 
479 static void
480 print_swap_line(const char *swdevname, intmax_t nblks, intmax_t bused,
481     intmax_t bavail, float bpercent)
482 {
483 	char usedbuf[5];
484 	char availbuf[5];
485 	int hlen, pagesize;
486 	long blocksize;
487 
488 	pagesize = getpagesize();
489 	getbsize(&hlen, &blocksize);
490 
491 	printf("%-15s %*jd ", swdevname, hlen, CONVERT(nblks));
492 	if (humanflag) {
493 		humanize_number(usedbuf, sizeof(usedbuf),
494 		    CONVERT_BLOCKS(bused), "",
495 		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
496 		humanize_number(availbuf, sizeof(availbuf),
497 		    CONVERT_BLOCKS(bavail), "",
498 		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
499 		printf("%8s %8s %5.0f%%\n", usedbuf, availbuf, bpercent);
500 	} else {
501 		printf("%8jd %8jd %5.0f%%\n", (intmax_t)CONVERT(bused),
502 		    (intmax_t)CONVERT(bavail), bpercent);
503 	}
504 }
505 
506 static void
507 print_swap(struct kvm_swap *ksw)
508 {
509 
510 	swtot.ksw_total += ksw->ksw_total;
511 	swtot.ksw_used += ksw->ksw_used;
512 	++nswdev;
513 	if (totalflag == 0)
514 		print_swap_line(ksw->ksw_devname, ksw->ksw_total,
515 		    ksw->ksw_used, ksw->ksw_total - ksw->ksw_used,
516 		    (ksw->ksw_used * 100.0) / ksw->ksw_total);
517 }
518 
519 static void
520 print_swap_total(void)
521 {
522 	int hlen, pagesize;
523 	long blocksize;
524 
525 	pagesize = getpagesize();
526 	getbsize(&hlen, &blocksize);
527 	if (totalflag) {
528 		blocksize = 1024 * 1024;
529 		(void)printf("%jdM/%jdM swap space\n",
530 		    CONVERT(swtot.ksw_used), CONVERT(swtot.ksw_total));
531 	} else if (nswdev > 1) {
532 		print_swap_line("Total", swtot.ksw_total, swtot.ksw_used,
533 		    swtot.ksw_total - swtot.ksw_used,
534 		    (swtot.ksw_used * 100.0) / swtot.ksw_total);
535 	}
536 }
537 
538 static void
539 swapmode_kvm(void)
540 {
541 	struct kvm_swap kswap[16];
542 	int i, n;
543 
544 	n = kvm_getswapinfo(kd, kswap, sizeof kswap / sizeof kswap[0],
545 	    SWIF_DEV_PREFIX);
546 
547 	print_swap_header();
548 	for (i = 0; i < n; ++i)
549 		print_swap(&kswap[i]);
550 	print_swap_total();
551 }
552 
553 static void
554 swapmode_sysctl(void)
555 {
556 	struct kvm_swap ksw;
557 	struct xswdev xsw;
558 	size_t mibsize, size;
559 	int mib[16], n;
560 
561 	print_swap_header();
562 	mibsize = sizeof mib / sizeof mib[0];
563 	if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1)
564 		err(1, "sysctlnametomib()");
565 	for (n = 0; ; ++n) {
566 		mib[mibsize] = n;
567 		size = sizeof xsw;
568 		if (sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1)
569 			break;
570 		if (xsw.xsw_version != XSWDEV_VERSION)
571 			errx(1, "xswdev version mismatch");
572 		if (xsw.xsw_dev == NODEV)
573 			snprintf(ksw.ksw_devname, sizeof ksw.ksw_devname,
574 			    "<NFSfile>");
575 		else
576 			snprintf(ksw.ksw_devname, sizeof ksw.ksw_devname,
577 			    "/dev/%s", devname(xsw.xsw_dev, S_IFCHR));
578 		ksw.ksw_used = xsw.xsw_used;
579 		ksw.ksw_total = xsw.xsw_nblks;
580 		ksw.ksw_flags = xsw.xsw_flags;
581 		print_swap(&ksw);
582 	}
583 	if (errno != ENOENT)
584 		err(1, "sysctl()");
585 	print_swap_total();
586 }
587 
588 static void
589 swapmode(void)
590 {
591 	if (kd != NULL)
592 		swapmode_kvm();
593 	else
594 		swapmode_sysctl();
595 }
596