pstat.c revision 97367
1/*-
2 * Copyright (c) 1980, 1991, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)pstat.c	8.16 (Berkeley) 5/9/95";
43#endif
44static const char rcsid[] =
45  "$FreeBSD: head/usr.sbin/pstat/pstat.c 97367 2002-05-28 05:42:32Z des $";
46#endif /* not lint */
47
48#include <sys/param.h>
49#include <sys/time.h>
50#define _KERNEL
51#include <sys/file.h>
52#include <sys/uio.h>
53#undef _KERNEL
54#include <sys/stat.h>
55#include <sys/stdint.h>
56#include <sys/ioctl.h>
57#include <sys/ioctl_compat.h>	/* XXX NTTYDISC is too well hidden */
58#include <sys/tty.h>
59#include <sys/conf.h>
60#include <sys/blist.h>
61
62#include <sys/user.h>
63#include <sys/sysctl.h>
64
65#include <err.h>
66#include <fcntl.h>
67#include <kvm.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
75static struct nlist nl[] = {
76#define NLMANDATORYBEG	0
77#define	V_MOUNTLIST	0
78	{ "_mountlist" },	/* address of head of mount list. */
79#define V_NUMV		1
80	{ "_numvnodes" },
81#define	FNL_NFILES	2
82	{"_nfiles"},
83#define FNL_MAXFILES	3
84	{"_maxfiles"},
85#define NLMANDATORYEND FNL_MAXFILES	/* names up to here are mandatory */
86#define	SCONS		NLMANDATORYEND + 1
87	{ "_constty" },
88#define	SPTY		NLMANDATORYEND + 2
89	{ "_pt_tty" },
90#define	SNPTY		NLMANDATORYEND + 3
91	{ "_npty" },
92
93
94
95#ifdef __FreeBSD__
96#define SCCONS	(SNPTY+1)
97	{ "_sccons" },
98#define NSCCONS	(SNPTY+2)
99	{ "_nsccons" },
100#define SIO  (SNPTY+3)
101	{ "_sio_tty" },
102#define NSIO (SNPTY+4)
103	{ "_nsio_tty" },
104#define RC  (SNPTY+5)
105	{ "_rc_tty" },
106#define NRC (SNPTY+6)
107	{ "_nrc_tty" },
108#define CY  (SNPTY+7)
109	{ "_cy_tty" },
110#define NCY (SNPTY+8)
111	{ "_ncy_tty" },
112#define SI  (SNPTY+9)
113	{ "_si_tty" },
114#define NSI (SNPTY+10)
115	{ "_si_Nports" },
116#endif
117	{ "" }
118};
119
120static int	usenumflag;
121static int	totalflag;
122static int	swapflag;
123static char	*nlistf;
124static char	*memf;
125static kvm_t	*kd;
126
127static char	*usagestr;
128
129#define	SVAR(var) __STRING(var)	/* to force expansion */
130#define	KGET(idx, var)							\
131	KGET1(idx, &var, sizeof(var), SVAR(var))
132#define	KGET1(idx, p, s, msg)						\
133	KGET2(nl[idx].n_value, p, s, msg)
134#define	KGET2(addr, p, s, msg)						\
135	if (kvm_read(kd, (u_long)(addr), p, s) != s)			\
136		warnx("cannot read %s: %s", msg, kvm_geterr(kd))
137#define	KGETN(idx, var)							\
138	KGET1N(idx, &var, sizeof(var), SVAR(var))
139#define	KGET1N(idx, p, s, msg)						\
140	KGET2N(nl[idx].n_value, p, s, msg)
141#define	KGET2N(addr, p, s, msg)						\
142	((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0)
143#define	KGETRET(addr, p, s, msg)					\
144	if (kvm_read(kd, (u_long)(addr), p, s) != s) {			\
145		warnx("cannot read %s: %s", msg, kvm_geterr(kd));	\
146		return (0);						\
147	}
148
149static void	filemode(void);
150static int	getfiles(char **, size_t *);
151static void	swapmode(void);
152static void	ttymode(void);
153static void	ttyprt(struct xtty *);
154#if 0
155/* XXX */
156static void	ttytype(struct tty *, char *, int, int, int);
157#endif
158static void	usage(void);
159
160int
161main(int argc, char *argv[])
162{
163	int ch, i, quit, ret;
164	int fileflag, ttyflag;
165	char buf[_POSIX2_LINE_MAX],*opts;
166
167	fileflag = swapflag = ttyflag = 0;
168
169	/* We will behave like good old swapinfo if thus invoked */
170	opts = strrchr(argv[0],'/');
171	if (opts)
172		opts++;
173	else
174		opts = argv[0];
175	if (!strcmp(opts,"swapinfo")) {
176		swapflag = 1;
177		opts = "kM:N:";
178		usagestr = "swapinfo [-k] [-M core] [-N system]";
179	} else {
180		opts = "TM:N:fknst";
181		usagestr = "pstat [-Tfknst] [-M core] [-N system]";
182	}
183
184	while ((ch = getopt(argc, argv, opts)) != -1)
185		switch (ch) {
186		case 'f':
187			fileflag = 1;
188			break;
189		case 'k':
190			putenv("BLOCKSIZE=1K");
191			break;
192		case 'M':
193			memf = optarg;
194			break;
195		case 'N':
196			nlistf = optarg;
197			break;
198		case 'n':
199			usenumflag = 1;
200			break;
201		case 's':
202			++swapflag;
203			break;
204		case 'T':
205			totalflag = 1;
206			break;
207		case 't':
208			ttyflag = 1;
209			break;
210		default:
211			usage();
212		}
213	argc -= optind;
214	argv += optind;
215
216	if (memf != NULL) {
217		kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf);
218		if (kd == NULL)
219			errx(1, "kvm_openfiles: %s", buf);
220		if ((ret = kvm_nlist(kd, nl)) != 0) {
221			if (ret == -1)
222				errx(1, "kvm_nlist: %s", kvm_geterr(kd));
223			quit = 0;
224			for (i = NLMANDATORYBEG; i <= NLMANDATORYEND; i++)
225				if (!nl[i].n_value) {
226					quit = 1;
227					warnx("undefined symbol: %s",
228					    nl[i].n_name);
229				}
230			if (quit)
231				exit(1);
232		}
233	}
234	if (!(fileflag | ttyflag | swapflag | totalflag))
235		usage();
236	if (fileflag || totalflag)
237		filemode();
238	if (ttyflag)
239		ttymode();
240	if (swapflag || totalflag)
241		swapmode();
242	exit (0);
243}
244
245static void
246usage(void)
247{
248	fprintf(stderr, "usage: %s\n", usagestr);
249	exit (1);
250}
251
252static const char hdr[] =
253"  LINE RAW CAN OUT IHIWT ILOWT OHWT LWT     COL STATE  SESS      PGID DISC\n";
254#if 0
255/* XXX */
256static int ttyspace = 128;
257#endif
258
259static void
260ttymode(void)
261{
262	struct xtty *xt, *end;
263	void *xttys;
264	size_t len;
265
266	(void)printf("%s", hdr);
267	if ((xttys = malloc(len = sizeof *xt)) == NULL)
268		err(1, "malloc()");
269	while (sysctlbyname("kern.ttys", xttys, &len, 0, 0) == -1) {
270		if (errno != ENOMEM)
271			err(1, "sysctlbyname()");
272		len *= 2;
273		if ((xttys = realloc(xttys, len)) == NULL)
274			err(1, "realloc()");
275	}
276	if (len > 0) {
277		end = (struct xtty *)((char *)xttys + len);
278		for (xt = xttys; xt < end; xt++)
279			ttyprt(xt);
280	}
281#if 0
282	/* XXX */
283	if ((tty = malloc(ttyspace * sizeof(*tty))) == NULL)
284		errx(1, "malloc");
285	if (nl[SCONS].n_type != 0) {
286		(void)printf("1 console\n");
287		KGET(SCONS, *tty);
288		ttyprt(&tty[0], 0);
289	}
290#ifdef __FreeBSD__
291	if (nl[NSCCONS].n_type != 0)
292		ttytype(tty, "vty", SCCONS, NSCCONS, 0);
293	if (nl[NSIO].n_type != 0)
294		ttytype(tty, "sio", SIO, NSIO, 0);
295	if (nl[NRC].n_type != 0)
296		ttytype(tty, "rc", RC, NRC, 0);
297	if (nl[NCY].n_type != 0)
298		ttytype(tty, "cy", CY, NCY, 0);
299	if (nl[NSI].n_type != 0)
300		ttytype(tty, "si", SI, NSI, 1);
301#endif
302	if (nl[SNPTY].n_type != 0)
303		ttytype(tty, "pty", SPTY, SNPTY, 0);
304}
305
306static void
307ttytype(struct tty *tty, char *name, int type, int number, int indir)
308{
309	struct tty *tp;
310	int ntty;
311	struct tty **ttyaddr;
312
313	if (tty == NULL)
314		return;
315	KGET(number, ntty);
316	(void)printf("%d %s %s\n", ntty, name, (ntty == 1) ? "line" : "lines");
317	if (ntty > ttyspace) {
318		ttyspace = ntty;
319		if ((tty = realloc(tty, ttyspace * sizeof(*tty))) == 0)
320			errx(1, "realloc");
321	}
322	if (indir) {
323		KGET(type, ttyaddr);
324		KGET2(ttyaddr, tty, ntty * sizeof(struct tty), "tty structs");
325	} else {
326		KGET1(type, tty, ntty * sizeof(struct tty), "tty structs");
327	}
328	(void)printf("%s", hdr);
329	for (tp = tty; tp < &tty[ntty]; tp++)
330		ttyprt(tp, tp - tty);
331#endif
332}
333
334static struct {
335	int flag;
336	char val;
337} ttystates[] = {
338#ifdef TS_WOPEN
339	{ TS_WOPEN,	'W'},
340#endif
341	{ TS_ISOPEN,	'O'},
342	{ TS_CARR_ON,	'C'},
343#ifdef TS_CONNECTED
344	{ TS_CONNECTED,	'c'},
345#endif
346	{ TS_TIMEOUT,	'T'},
347	{ TS_FLUSH,	'F'},
348	{ TS_BUSY,	'B'},
349#ifdef TS_ASLEEP
350	{ TS_ASLEEP,	'A'},
351#endif
352#ifdef TS_SO_OLOWAT
353	{ TS_SO_OLOWAT,	'A'},
354#endif
355#ifdef TS_SO_OCOMPLETE
356	{ TS_SO_OCOMPLETE, 'a'},
357#endif
358	{ TS_XCLUDE,	'X'},
359	{ TS_TTSTOP,	'S'},
360#ifdef TS_CAR_OFLOW
361	{ TS_CAR_OFLOW,	'm'},
362#endif
363#ifdef TS_CTS_OFLOW
364	{ TS_CTS_OFLOW,	'o'},
365#endif
366#ifdef TS_DSR_OFLOW
367	{ TS_DSR_OFLOW,	'd'},
368#endif
369	{ TS_TBLOCK,	'K'},
370	{ TS_ASYNC,	'Y'},
371	{ TS_BKSL,	'D'},
372	{ TS_ERASE,	'E'},
373	{ TS_LNCH,	'L'},
374	{ TS_TYPEN,	'P'},
375	{ TS_CNTTB,	'N'},
376#ifdef TS_CAN_BYPASS_L_RINT
377	{ TS_CAN_BYPASS_L_RINT, 'l'},
378#endif
379#ifdef TS_SNOOP
380	{ TS_SNOOP,     's'},
381#endif
382#ifdef TS_ZOMBIE
383	{ TS_ZOMBIE,	'Z'},
384#endif
385	{ 0,	       '\0'},
386};
387
388static void
389ttyprt(struct xtty *xt)
390{
391	int i, j;
392	pid_t pgid;
393	char *name, state[20];
394
395	if (xt->xt_size != sizeof *xt)
396		errx(1, "struct xtty size mismatch");
397	if (usenumflag || xt->xt_dev == 0 ||
398	   (name = devname(xt->xt_dev, S_IFCHR)) == NULL)
399		(void)printf("   %2d,%-2d", major(xt->xt_dev), minor(xt->xt_dev));
400	else
401		(void)printf("%7s ", name);
402	(void)printf("%2ld %3ld ", xt->xt_rawcc, xt->xt_cancc);
403	(void)printf("%3ld %5d %5d %4d %3d %7d ", xt->xt_outcc,
404		xt->xt_ihiwat, xt->xt_ilowat, xt->xt_ohiwat, xt->xt_olowat,
405		xt->xt_column);
406	for (i = j = 0; ttystates[i].flag; i++)
407		if (xt->xt_state & ttystates[i].flag)
408			state[j++] = ttystates[i].val;
409	if (j == 0)
410		state[j++] = '-';
411	state[j] = '\0';
412	(void)printf("%-6s %8d", state, xt->xt_sid);
413	pgid = 0;
414	(void)printf("%6d ", xt->xt_pgid);
415	switch (xt->xt_line) {
416	case TTYDISC:
417		(void)printf("term\n");
418		break;
419	case NTTYDISC:
420		(void)printf("ntty\n");
421		break;
422	case SLIPDISC:
423		(void)printf("slip\n");
424		break;
425	case PPPDISC:
426		(void)printf("ppp\n");
427		break;
428	default:
429		(void)printf("%d\n", xt->xt_line);
430		break;
431	}
432}
433
434static void
435filemode(void)
436{
437	struct file *fp;
438	struct file *addr;
439	char *buf, flagbuf[16], *fbp;
440	int maxfile, nfiles;
441	size_t len;
442	static char *dtypes[] = { "???", "inode", "socket" };
443
444	if (kd != NULL) {
445		KGET(FNL_MAXFILES, maxfile);
446		KGET(FNL_NFILES, nfiles);
447	} else {
448		len = sizeof maxfile;
449		if (sysctlbyname("kern.maxfiles", &maxfile, &len, 0, 0) == -1)
450			err(1, "sysctlbyname()");
451		len = sizeof nfiles;
452		if (sysctlbyname("kern.openfiles", &nfiles, &len, 0, 0) == -1)
453			err(1, "sysctlbyname()");
454	}
455
456	if (totalflag) {
457		(void)printf("%3d/%3d files\n", nfiles, maxfile);
458		return;
459	}
460	if (getfiles(&buf, &len) == -1)
461		return;
462	/*
463	 * Getfiles returns in malloc'd memory a pointer to the first file
464	 * structure, and then an array of file structs (whose addresses are
465	 * derivable from the previous entry).
466	 */
467	addr = LIST_FIRST((struct filelist *)buf);
468	fp = (struct file *)(buf + sizeof(struct filelist));
469	nfiles = (len - sizeof(struct filelist)) / sizeof(struct file);
470
471	(void)printf("%d/%d open files\n", nfiles, maxfile);
472	(void)printf("   LOC   TYPE    FLG     CNT  MSG    DATA    OFFSET\n");
473	for (; (char *)fp < buf + len; addr = LIST_NEXT(fp, f_list), fp++) {
474		if ((unsigned)fp->f_type > DTYPE_SOCKET)
475			continue;
476		(void)printf("%8lx ", (u_long)(void *)addr);
477		(void)printf("%-8.8s", dtypes[fp->f_type]);
478		fbp = flagbuf;
479		if (fp->f_flag & FREAD)
480			*fbp++ = 'R';
481		if (fp->f_flag & FWRITE)
482			*fbp++ = 'W';
483		if (fp->f_flag & FAPPEND)
484			*fbp++ = 'A';
485		if (fp->f_flag & FASYNC)
486			*fbp++ = 'I';
487		*fbp = '\0';
488		(void)printf("%6s  %3d", flagbuf, fp->f_count);
489		(void)printf("  %3d", fp->f_msgcount);
490		(void)printf("  %8lx", (u_long)(void *)fp->f_data);
491		(void)printf("  %jx\n", (uintmax_t)fp->f_offset);
492	}
493	free(buf);
494}
495
496static int
497getfiles(char **abuf, size_t *alen)
498{
499	size_t len;
500	int mib[2];
501	char *buf;
502
503	/*
504	 * XXX
505	 * Add emulation of KINFO_FILE here.
506	 */
507	if (kd != NULL)
508		errx(1, "files on dead kernel, not implemented");
509
510	mib[0] = CTL_KERN;
511	mib[1] = KERN_FILE;
512	if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) {
513		warn("sysctl: KERN_FILE");
514		return (-1);
515	}
516	if ((buf = malloc(len)) == NULL)
517		errx(1, "malloc");
518	if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
519		warn("sysctl: KERN_FILE");
520		return (-1);
521	}
522	*abuf = buf;
523	*alen = len;
524	return (0);
525}
526
527/*
528 * swapmode is based on a program called swapinfo written
529 * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
530 */
531
532#define CONVERT(v)	((int)((intmax_t)(v) * pagesize / blocksize))
533static struct kvm_swap swtot;
534static int nswdev;
535
536static void
537print_swap_header(void)
538{
539	int hlen;
540	long blocksize;
541	const char *header;
542
543	header = getbsize(&hlen, &blocksize);
544	if (totalflag == 0)
545		(void)printf("%-15s %*s %8s %8s %8s  %s\n",
546		    "Device", hlen, header,
547		    "Used", "Avail", "Capacity", "Type");
548}
549
550static void
551print_swap(struct kvm_swap *ksw)
552{
553	int hlen, pagesize;
554	long blocksize;
555
556	pagesize = getpagesize();
557	getbsize(&hlen, &blocksize);
558	swtot.ksw_total += ksw->ksw_total;
559	swtot.ksw_used += ksw->ksw_used;
560	++nswdev;
561	if (totalflag == 0) {
562		(void)printf("/dev/%-10s %*d ",
563		    ksw->ksw_devname, hlen,
564		    CONVERT(ksw->ksw_total));
565		(void)printf("%8d %8d %5.0f%%    %s\n",
566		    CONVERT(ksw->ksw_used),
567		    CONVERT(ksw->ksw_total - ksw->ksw_used),
568		    (ksw->ksw_used * 100.0) / ksw->ksw_total,
569		    (ksw->ksw_flags & SW_SEQUENTIAL) ?
570		    "Sequential" : "Interleaved");
571	}
572}
573
574static void
575print_swap_total(void)
576{
577	int hlen, pagesize;
578	long blocksize;
579
580	pagesize = getpagesize();
581	getbsize(&hlen, &blocksize);
582	if (totalflag) {
583		blocksize = 1024 * 1024;
584		(void)printf("%dM/%dM swap space\n",
585		    CONVERT(swtot.ksw_used), CONVERT(swtot.ksw_total));
586	} else if (nswdev > 1) {
587		(void)printf("%-15s %*d %8d %8d %5.0f%%\n",
588		    "Total", hlen, CONVERT(swtot.ksw_total),
589		    CONVERT(swtot.ksw_used),
590		    CONVERT(swtot.ksw_total - swtot.ksw_used),
591		    (swtot.ksw_used * 100.0) / swtot.ksw_total);
592	}
593}
594
595static void
596swapmode_kvm(void)
597{
598	struct kvm_swap kswap[16];
599	int i, n;
600
601	n = kvm_getswapinfo(kd, kswap, sizeof kswap / sizeof kswap[0],
602	    ((swapflag > 1) ? SWIF_DUMP_TREE : 0) | SWIF_DEV_PREFIX);
603
604	print_swap_header();
605	for (i = 0; i < n; ++i)
606		print_swap(&kswap[i]);
607	print_swap_total();
608}
609
610static void
611swapmode_sysctl(void)
612{
613	struct kvm_swap ksw;
614	struct xswdev xsw;
615	size_t mibsize, size;
616	int mib[16], n;
617
618	print_swap_header();
619	mibsize = sizeof mib / sizeof mib[0];
620	if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1)
621		err(1, "sysctlnametomib()");
622	for (n = 0; ; ++n) {
623		mib[mibsize] = n;
624		size = sizeof xsw;
625		if (sysctl(mib, mibsize + 1, &xsw, &size, NULL, NULL) == -1)
626			break;
627		if (xsw.xsw_version != XSWDEV_VERSION)
628			errx(1, "xswdev version mismatch");
629		snprintf(ksw.ksw_devname, sizeof ksw.ksw_devname,
630		    "/dev/%s", devname(xsw.xsw_dev, S_IFCHR));
631		ksw.ksw_used = xsw.xsw_used;
632		ksw.ksw_total = xsw.xsw_nblks;
633		ksw.ksw_flags = xsw.xsw_flags;
634		print_swap(&ksw);
635	}
636	if (errno != ENOENT)
637		err(1, "sysctl()");
638	print_swap_total();
639}
640
641static void
642swapmode(void)
643{
644	if (kd != NULL)
645		swapmode_kvm();
646	else
647		swapmode_sysctl();
648}
649