xref: /original-bsd/usr.sbin/pstat/pstat.c (revision a69cfb4b)
1 /*-
2  * Copyright (c) 1980, 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1980, 1991, 1993\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)pstat.c	8.1 (Berkeley) 06/06/93";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/time.h>
20 #include <sys/vnode.h>
21 #include <sys/map.h>
22 #define KERNEL
23 #include <sys/file.h>
24 #include <ufs/ufs/quota.h>
25 #include <ufs/ufs/inode.h>
26 #undef KERNEL
27 #define NFS
28 #include <sys/mount.h>
29 #undef NFS
30 #include <sys/stat.h>
31 #include <nfs/nfsnode.h>
32 #include <sys/ioctl.h>
33 #include <sys/tty.h>
34 #include <sys/conf.h>
35 
36 #include <sys/sysctl.h>
37 
38 #include <err.h>
39 #include <kvm.h>
40 #include <limits.h>
41 #include <nlist.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 
47 #include "pathnames.h"
48 
49 struct nlist nl[] = {
50 #define VM_SWAPMAP	0
51 	{ "_swapmap" },	/* list of free swap areas */
52 #define VM_NSWAPMAP	1
53 	{ "_nswapmap" },/* size of the swap map */
54 #define VM_SWDEVT	2
55 	{ "_swdevt" },	/* list of swap devices and sizes */
56 #define VM_NSWAP	3
57 	{ "_nswap" },	/* size of largest swap device */
58 #define VM_NSWDEV	4
59 	{ "_nswdev" },	/* number of swap devices */
60 #define VM_DMMAX	5
61 	{ "_dmmax" },	/* maximum size of a swap block */
62 #define V_NUMV		6
63 	{ "_numvnodes" },
64 #define V_ROOTFS	7
65 	{ "_rootfs" },
66 #define	FNL_NFILE	8
67 	{"_nfiles"},
68 #define FNL_MAXFILE	9
69 	{"_maxfiles"},
70 #define NLMANDATORY FNL_MAXFILE	/* names up to here are mandatory */
71 #define	SCONS	NLMANDATORY + 1
72 	{ "_cons" },
73 #define	SPTY	NLMANDATORY + 2
74 	{ "_pt_tty" },
75 #define	SNPTY	NLMANDATORY + 3
76 	{ "_npty" },
77 
78 #ifdef hp300
79 #define	SDCA	(SNPTY+1)
80 	{ "_dca_tty" },
81 #define	SNDCA	(SNPTY+2)
82 	{ "_ndca" },
83 #define	SDCM	(SNPTY+3)
84 	{ "_dcm_tty" },
85 #define	SNDCM	(SNPTY+4)
86 	{ "_ndcm" },
87 #define	SDCL	(SNPTY+5)
88 	{ "_dcl_tty" },
89 #define	SNDCL	(SNPTY+6)
90 	{ "_ndcl" },
91 #define	SITE	(SNPTY+7)
92 	{ "_ite_tty" },
93 #define	SNITE	(SNPTY+8)
94 	{ "_nite" },
95 #endif
96 
97 #ifdef mips
98 #define SDC	(SNPTY+1)
99 	{ "_dc_tty" },
100 #define SNDC	(SNPTY+2)
101 	{ "_dc_cnt" },
102 #endif
103 
104 	{ "" }
105 };
106 
107 int	usenumflag;
108 int	totalflag;
109 char	*nlistf	= NULL;
110 char	*memf	= NULL;
111 kvm_t	*kd;
112 
113 #define	SVAR(var) __STRING(var)	/* to force expansion */
114 #define	KGET(idx, var)							\
115 	KGET1(idx, &var, sizeof(var), SVAR(var))
116 #define	KGET1(idx, p, s, msg)						\
117 	KGET2(nl[idx].n_value, p, s, msg)
118 #define	KGET2(addr, p, s, msg)						\
119 	if (kvm_read(kd, (u_long)(addr), p, s) != s)			\
120 		warnx("cannot read %s: %s", msg, kvm_geterr(kd))
121 #define	KGETRET(addr, p, s, msg)					\
122 	if (kvm_read(kd, (u_long)(addr), p, s) != s) {			\
123 		warnx("cannot read %s: %s", msg, kvm_geterr(kd));	\
124 		return (0);						\
125 	}
126 
127 void	filemode __P((void));
128 int	getfiles __P((char **, int *));
129 struct mount *
130 	getmnt __P((struct mount *));
131 struct e_vnode *
132 	kinfo_vnodes __P((int *));
133 struct e_vnode *
134 	loadvnodes __P((int *));
135 void	mount_print __P((struct mount *));
136 void	nfs_header __P((void));
137 int	nfs_print __P((struct vnode *));
138 void	swapmode __P((void));
139 void	ttymode __P((void));
140 void	ttyprt __P((struct tty *, int));
141 void	ttytype __P((struct tty *, char *, int, int));
142 void	ufs_header __P((void));
143 int	ufs_print __P((struct vnode *));
144 void	usage __P((void));
145 void	vnode_header __P((void));
146 void	vnode_print __P((struct vnode *, struct vnode *));
147 void	vnodemode __P((void));
148 
149 int
150 main(argc, argv)
151 	int argc;
152 	char *argv[];
153 {
154 	extern char *optarg;
155 	extern int optind;
156 	int ch, i, quit, ret;
157 	int fileflag, swapflag, ttyflag, vnodeflag;
158 	char buf[_POSIX2_LINE_MAX];
159 
160 	fileflag = swapflag = ttyflag = vnodeflag = 0;
161 	while ((ch = getopt(argc, argv, "TM:N:finstv")) != EOF)
162 		switch (ch) {
163 		case 'f':
164 			fileflag = 1;
165 			break;
166 		case 'M':
167 			memf = optarg;
168 			break;
169 		case 'N':
170 			nlistf = optarg;
171 			break;
172 		case 'n':
173 			usenumflag = 1;
174 			break;
175 		case 's':
176 			swapflag = 1;
177 			break;
178 		case 'T':
179 			totalflag = 1;
180 			break;
181 		case 't':
182 			ttyflag = 1;
183 			break;
184 		case 'v':
185 		case 'i':		/* Backward compatibility. */
186 			vnodeflag = 1;
187 			break;
188 		default:
189 			usage();
190 		}
191 	argc -= optind;
192 	argv += optind;
193 
194 	/*
195 	 * Discard setgid privileges if not the running kernel so that bad
196 	 * guys can't print interesting stuff from kernel memory.
197 	 */
198 	if (nlistf != NULL || memf != NULL)
199 		(void)setgid(getgid());
200 
201 	if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf)) == 0)
202 		errx(1, "kvm_openfiles: %s", buf);
203 	if ((ret = kvm_nlist(kd, nl)) != 0) {
204 		if (ret == -1)
205 			errx(1, "kvm_nlist: %s", kvm_geterr(kd));
206 		for (i = quit = 0; i <= NLMANDATORY; i++)
207 			if (!nl[i].n_value) {
208 				quit = 1;
209 				warnx("undefined symbol: %s\n", nl[i].n_name);
210 			}
211 		if (quit)
212 			exit(1);
213 	}
214 	if (!(fileflag | vnodeflag | ttyflag | swapflag | totalflag))
215 		usage();
216 	if (fileflag || totalflag)
217 		filemode();
218 	if (vnodeflag || totalflag)
219 		vnodemode();
220 	if (ttyflag)
221 		ttymode();
222 	if (swapflag || totalflag)
223 		swapmode();
224 	exit (0);
225 }
226 
227 struct e_vnode {
228 	struct vnode *avnode;
229 	struct vnode vnode;
230 };
231 
232 void
233 vnodemode()
234 {
235 	register struct e_vnode *e_vnodebase, *endvnode, *evp;
236 	register struct vnode *vp;
237 	register struct mount *maddr, *mp;
238 	int numvnodes;
239 
240 	e_vnodebase = loadvnodes(&numvnodes);
241 	if (totalflag) {
242 		(void)printf("%7d vnodes\n", numvnodes);
243 		return;
244 	}
245 	endvnode = e_vnodebase + numvnodes;
246 	(void)printf("%d active vnodes\n", numvnodes);
247 
248 
249 #define ST	mp->mnt_stat
250 	maddr = NULL;
251 	for (evp = e_vnodebase; evp < endvnode; evp++) {
252 		vp = &evp->vnode;
253 		if (vp->v_mount != maddr) {
254 			/*
255 			 * New filesystem
256 			 */
257 			if ((mp = getmnt(vp->v_mount)) == NULL)
258 				continue;
259 			maddr = vp->v_mount;
260 			mount_print(mp);
261 			vnode_header();
262 			switch(ST.f_type) {
263 			case MOUNT_UFS:
264 			case MOUNT_MFS:
265 				ufs_header();
266 				break;
267 			case MOUNT_NFS:
268 				nfs_header();
269 				break;
270 			case MOUNT_NONE:
271 			case MOUNT_PC:
272 			default:
273 				break;
274 			}
275 			(void)printf("\n");
276 		}
277 		vnode_print(evp->avnode, vp);
278 		switch(ST.f_type) {
279 		case MOUNT_UFS:
280 		case MOUNT_MFS:
281 			ufs_print(vp);
282 			break;
283 		case MOUNT_NFS:
284 			nfs_print(vp);
285 			break;
286 		case MOUNT_NONE:
287 		case MOUNT_PC:
288 		default:
289 			break;
290 		}
291 		(void)printf("\n");
292 	}
293 	free(e_vnodebase);
294 }
295 
296 void
297 vnode_header()
298 {
299 	(void)printf("ADDR     TYP VFLAG  USE HOLD");
300 }
301 
302 void
303 vnode_print(avnode, vp)
304 	struct vnode *avnode;
305 	struct vnode *vp;
306 {
307 	char *type, flags[16];
308 	char *fp = flags;
309 	register int flag;
310 
311 	/*
312 	 * set type
313 	 */
314 	switch(vp->v_type) {
315 	case VNON:
316 		type = "non"; break;
317 	case VREG:
318 		type = "reg"; break;
319 	case VDIR:
320 		type = "dir"; break;
321 	case VBLK:
322 		type = "blk"; break;
323 	case VCHR:
324 		type = "chr"; break;
325 	case VLNK:
326 		type = "lnk"; break;
327 	case VSOCK:
328 		type = "soc"; break;
329 	case VFIFO:
330 		type = "fif"; break;
331 	case VBAD:
332 		type = "bad"; break;
333 	default:
334 		type = "unk"; break;
335 	}
336 	/*
337 	 * gather flags
338 	 */
339 	flag = vp->v_flag;
340 	if (flag & VROOT)
341 		*fp++ = 'R';
342 	if (flag & VTEXT)
343 		*fp++ = 'T';
344 	if (flag & VSYSTEM)
345 		*fp++ = 'S';
346 	if (flag & VXLOCK)
347 		*fp++ = 'L';
348 	if (flag & VXWANT)
349 		*fp++ = 'W';
350 	if (flag & VBWAIT)
351 		*fp++ = 'B';
352 	if (flag & VALIASED)
353 		*fp++ = 'A';
354 	if (flag == 0)
355 		*fp++ = '-';
356 	*fp = '\0';
357 	(void)printf("%8x %s %5s %4d %4d",
358 	    avnode, type, flags, vp->v_usecount, vp->v_holdcnt);
359 }
360 
361 void
362 ufs_header()
363 {
364 	(void)printf(" FILEID IFLAG RDEV|SZ");
365 }
366 
367 int
368 ufs_print(vp)
369 	struct vnode *vp;
370 {
371 	register int flag;
372 	struct inode inode, *ip = &inode;
373 	char flagbuf[16], *flags = flagbuf;
374 	char *name;
375 	mode_t type;
376 
377 	KGETRET(VTOI(vp), &inode, sizeof(struct inode), "vnode's inode");
378 	flag = ip->i_flag;
379 	if (flag & ILOCKED)
380 		*flags++ = 'L';
381 	if (flag & IWANT)
382 		*flags++ = 'W';
383 	if (flag & IRENAME)
384 		*flags++ = 'R';
385 	if (flag & IUPD)
386 		*flags++ = 'U';
387 	if (flag & IACC)
388 		*flags++ = 'A';
389 	if (flag & ICHG)
390 		*flags++ = 'C';
391 	if (flag & IMOD)
392 		*flags++ = 'M';
393 	if (flag & ISHLOCK)
394 		*flags++ = 'S';
395 	if (flag & IEXLOCK)
396 		*flags++ = 'E';
397 	if (flag & ILWAIT)
398 		*flags++ = 'Z';
399 	if (flag == 0)
400 		*flags++ = '-';
401 	*flags = '\0';
402 
403 	(void)printf(" %6d %5s", ip->i_number, flagbuf);
404 	type = ip->i_mode & S_IFMT;
405 	if (S_ISCHR(ip->i_mode) || S_ISBLK(ip->i_mode))
406 		if (usenumflag || ((name = devname(ip->i_rdev, type)) == NULL))
407 			(void)printf("   %2d,%-2d",
408 			    major(ip->i_rdev), minor(ip->i_rdev));
409 		else
410 			(void)printf(" %7s", name);
411 	else
412 		(void)printf(" %7qd", ip->i_size);
413 	return (0);
414 }
415 
416 void
417 nfs_header()
418 {
419 	(void)printf(" FILEID NFLAG RDEV|SZ");
420 }
421 
422 int
423 nfs_print(vp)
424 	struct vnode *vp;
425 {
426 	struct nfsnode nfsnode, *np = &nfsnode;
427 	char flagbuf[16], *flags = flagbuf;
428 	register int flag;
429 	char *name;
430 	mode_t type;
431 
432 	KGETRET(VTONFS(vp), &nfsnode, sizeof(nfsnode), "vnode's nfsnode");
433 	flag = np->n_flag;
434 	if (flag & NFLUSHWANT)
435 		*flags++ = 'W';
436 	if (flag & NFLUSHINPROG)
437 		*flags++ = 'P';
438 	if (flag & NMODIFIED)
439 		*flags++ = 'M';
440 	if (flag & NWRITEERR)
441 		*flags++ = 'E';
442 	if (flag & NQNFSNONCACHE)
443 		*flags++ = 'X';
444 	if (flag & NQNFSWRITE)
445 		*flags++ = 'O';
446 	if (flag & NQNFSEVICTED)
447 		*flags++ = 'G';
448 	if (flag == 0)
449 		*flags++ = '-';
450 	*flags = '\0';
451 
452 #define VT	np->n_vattr
453 	(void)printf(" %6d %5s", VT.va_fileid, flagbuf);
454 	type = VT.va_mode & S_IFMT;
455 	if (S_ISCHR(VT.va_mode) || S_ISBLK(VT.va_mode))
456 		if (usenumflag || ((name = devname(VT.va_rdev, type)) == NULL))
457 			(void)printf("   %2d,%-2d",
458 			    major(VT.va_rdev), minor(VT.va_rdev));
459 		else
460 			(void)printf(" %7s", name);
461 	else
462 		(void)printf(" %7qd", np->n_size);
463 	return (0);
464 }
465 
466 /*
467  * Given a pointer to a mount structure in kernel space,
468  * read it in and return a usable pointer to it.
469  */
470 struct mount *
471 getmnt(maddr)
472 	struct mount *maddr;
473 {
474 	static struct mtab {
475 		struct mtab *next;
476 		struct mount *maddr;
477 		struct mount mount;
478 	} *mhead = NULL;
479 	register struct mtab *mt;
480 
481 	for (mt = mhead; mt != NULL; mt = mt->next)
482 		if (maddr == mt->maddr)
483 			return (&mt->mount);
484 	if ((mt = malloc(sizeof(struct mtab))) == NULL)
485 		err(1, NULL);
486 	KGETRET(maddr, &mt->mount, sizeof(struct mount), "mount table");
487 	mt->maddr = maddr;
488 	mt->next = mhead;
489 	mhead = mt;
490 	return (&mt->mount);
491 }
492 
493 void
494 mount_print(mp)
495 	struct mount *mp;
496 {
497 	register int flags;
498 	char *type;
499 
500 #define ST	mp->mnt_stat
501 	(void)printf("*** MOUNT ");
502 	switch (ST.f_type) {
503 	case MOUNT_NONE:
504 		type = "none";
505 		break;
506 	case MOUNT_UFS:
507 		type = "ufs";
508 		break;
509 	case MOUNT_NFS:
510 		type = "nfs";
511 		break;
512 	case MOUNT_MFS:
513 		type = "mfs";
514 		break;
515 	case MOUNT_PC:
516 		type = "pc";
517 		break;
518 	default:
519 		type = "unknown";
520 		break;
521 	}
522 	(void)printf("%s %s on %s", type, ST.f_mntfromname, ST.f_mntonname);
523 	if (flags = mp->mnt_flag) {
524 		char *comma = "(";
525 
526 		putchar(' ');
527 		/* user visable flags */
528 		if (flags & MNT_RDONLY) {
529 			(void)printf("%srdonly", comma);
530 			flags &= ~MNT_RDONLY;
531 			comma = ",";
532 		}
533 		if (flags & MNT_SYNCHRONOUS) {
534 			(void)printf("%ssynchronous", comma);
535 			flags &= ~MNT_SYNCHRONOUS;
536 			comma = ",";
537 		}
538 		if (flags & MNT_NOEXEC) {
539 			(void)printf("%snoexec", comma);
540 			flags &= ~MNT_NOEXEC;
541 			comma = ",";
542 		}
543 		if (flags & MNT_NOSUID) {
544 			(void)printf("%snosuid", comma);
545 			flags &= ~MNT_NOSUID;
546 			comma = ",";
547 		}
548 		if (flags & MNT_NODEV) {
549 			(void)printf("%snodev", comma);
550 			flags &= ~MNT_NODEV;
551 			comma = ",";
552 		}
553 		if (flags & MNT_EXPORTED) {
554 			(void)printf("%sexport", comma);
555 			flags &= ~MNT_EXPORTED;
556 			comma = ",";
557 		}
558 		if (flags & MNT_EXRDONLY) {
559 			(void)printf("%sexrdonly", comma);
560 			flags &= ~MNT_EXRDONLY;
561 			comma = ",";
562 		}
563 		if (flags & MNT_LOCAL) {
564 			(void)printf("%slocal", comma);
565 			flags &= ~MNT_LOCAL;
566 			comma = ",";
567 		}
568 		if (flags & MNT_QUOTA) {
569 			(void)printf("%squota", comma);
570 			flags &= ~MNT_QUOTA;
571 			comma = ",";
572 		}
573 		/* filesystem control flags */
574 		if (flags & MNT_UPDATE) {
575 			(void)printf("%supdate", comma);
576 			flags &= ~MNT_UPDATE;
577 			comma = ",";
578 		}
579 		if (flags & MNT_MLOCK) {
580 			(void)printf("%slock", comma);
581 			flags &= ~MNT_MLOCK;
582 			comma = ",";
583 		}
584 		if (flags & MNT_MWAIT) {
585 			(void)printf("%swait", comma);
586 			flags &= ~MNT_MWAIT;
587 			comma = ",";
588 		}
589 		if (flags & MNT_MPBUSY) {
590 			(void)printf("%sbusy", comma);
591 			flags &= ~MNT_MPBUSY;
592 			comma = ",";
593 		}
594 		if (flags & MNT_MPWANT) {
595 			(void)printf("%swant", comma);
596 			flags &= ~MNT_MPWANT;
597 			comma = ",";
598 		}
599 		if (flags & MNT_UNMOUNT) {
600 			(void)printf("%sunmount", comma);
601 			flags &= ~MNT_UNMOUNT;
602 			comma = ",";
603 		}
604 		if (flags)
605 			(void)printf("%sunknown_flags:%x", comma, flags);
606 		(void)printf(")");
607 	}
608 	(void)printf("\n");
609 #undef ST
610 }
611 
612 struct e_vnode *
613 loadvnodes(avnodes)
614 	int *avnodes;
615 {
616 	int mib[2];
617 	size_t copysize;
618 	struct e_vnode *vnodebase;
619 
620 	if (memf != NULL) {
621 		/*
622 		 * do it by hand
623 		 */
624 		return (kinfo_vnodes(avnodes));
625 	}
626 	mib[0] = CTL_KERN;
627 	mib[1] = KERN_VNODE;
628 	if (sysctl(mib, 2, NULL, &copysize, NULL, 0) == -1)
629 		err(1, "sysctl: KERN_VNODE");
630 	if ((vnodebase = malloc(copysize)) == NULL)
631 		err(1, NULL);
632 	if (sysctl(mib, 2, vnodebase, &copysize, NULL, 0) == -1)
633 		err(1, "sysctl: KERN_VNODE");
634 	if (copysize % sizeof(struct e_vnode))
635 		errx(1, "vnode size mismatch");
636 	*avnodes = copysize / sizeof(struct e_vnode);
637 
638 	return (vnodebase);
639 }
640 
641 /*
642  * simulate what a running kernel does in in kinfo_vnode
643  */
644 struct e_vnode *
645 kinfo_vnodes(avnodes)
646 	int *avnodes;
647 {
648 	int numvnodes;
649 	struct mount *rootfs, *mp, mount;
650 	char *vbuf, *evbuf, *bp;
651 	struct vnode *vp, vnode;
652 	int num;
653 
654 #define VPTRSZ  sizeof(struct vnode *)
655 #define VNODESZ sizeof(struct vnode)
656 
657 	KGET(V_NUMV, numvnodes);
658 	if ((vbuf = malloc((numvnodes + 20) * (VPTRSZ + VNODESZ))) == NULL)
659 		err(1, NULL);
660 	bp = vbuf;
661 	evbuf = vbuf + (numvnodes + 20) * (VPTRSZ + VNODESZ);
662 	KGET(V_ROOTFS, rootfs);
663 	mp = rootfs;
664 	do {
665 		KGET2(mp, &mount, sizeof(mount), "mount entry");
666 		for (vp = mount.mnt_mounth; vp; vp = vnode.v_mountf) {
667 			KGET2(vp, &vnode, sizeof(vnode), "vnode");
668 			if ((bp + VPTRSZ + VNODESZ) > evbuf)
669 				/* XXX - should realloc */
670 				errx(1, "no more room for vnodes");
671 			memmove(bp, &vp, VPTRSZ);
672 			bp += VPTRSZ;
673 			memmove(bp, &vnode, VNODESZ);
674 			bp += VNODESZ;
675 			num++;
676 		}
677 		mp = mount.mnt_next;
678 	} while (mp != rootfs);
679 	*avnodes = num;
680 	return ((struct e_vnode *)vbuf);
681 }
682 
683 char hdr[]="  LINE RAW CAN OUT  HWT LWT     ADDR COL STATE  SESS  PGID DISC\n";
684 int ttyspace = 128;
685 
686 void
687 ttymode()
688 {
689 	struct tty *tty;
690 
691 	if ((tty = malloc(ttyspace * sizeof(*tty))) == NULL)
692 		err(1, NULL);
693 #ifndef hp300
694 	(void)printf("1 console\n");
695 	KGET(SCONS, *tty);
696 	(void)printf(hdr);
697 	ttyprt(&tty[0], 0);
698 #endif
699 #ifdef vax
700 	if (nl[SNQD].n_type != 0)
701 		qdss();
702 	if (nl[SNDZ].n_type != 0)
703 		ttytype(tty, "dz", SDZ, SNDZ);
704 	if (nl[SNDH].n_type != 0)
705 		ttytype(tty, "dh", SDH, SNDH);
706 	if (nl[SNDMF].n_type != 0)
707 		ttytype(tty, "dmf", SDMF, SNDMF);
708 	if (nl[SNDHU].n_type != 0)
709 		ttytype(tty, "dhu", SDHU, SNDHU);
710 	if (nl[SNDMZ].n_type != 0)
711 		ttytype(tty, "dmz", SDMZ, SNDMZ);
712 #endif
713 #ifdef tahoe
714 	if (nl[SNVX].n_type != 0)
715 		ttytype(tty, "vx", SVX, SNVX);
716 	if (nl[SNMP].n_type != 0)
717 		ttytype(tty, "mp", SMP, SNMP);
718 #endif
719 #ifdef hp300
720 	if (nl[SNITE].n_type != 0)
721 		ttytype(tty, "ite", SITE, SNITE);
722 	if (nl[SNDCA].n_type != 0)
723 		ttytype(tty, "dca", SDCA, SNDCA);
724 	if (nl[SNDCM].n_type != 0)
725 		ttytype(tty, "dcm", SDCM, SNDCM);
726 	if (nl[SNDCL].n_type != 0)
727 		ttytype(tty, "dcl", SDCL, SNDCL);
728 #endif
729 #ifdef mips
730 	if (nl[SNDC].n_type != 0)
731 		ttytype(tty, "dc", SDC, SNDC);
732 #endif
733 	if (nl[SNPTY].n_type != 0)
734 		ttytype(tty, "pty", SPTY, SNPTY);
735 }
736 
737 void
738 ttytype(tty, name, type, number)
739 	register struct tty *tty;
740 	char *name;
741 	int type, number;
742 {
743 	register struct tty *tp;
744 	int ntty;
745 
746 	if (tty == NULL)
747 		return;
748 	KGET(number, ntty);
749 	(void)printf("%d %s %s\n", ntty, name, (ntty == 1) ? "line" : "lines");
750 	if (ntty > ttyspace) {
751 		ttyspace = ntty;
752 		if ((tty = realloc(tty, ttyspace * sizeof(*tty))) == 0)
753 			err(1, NULL);
754 	}
755 	KGET1(type, tty, ntty * sizeof(struct tty), "tty structs");
756 	(void)printf(hdr);
757 	for (tp = tty; tp < &tty[ntty]; tp++)
758 		ttyprt(tp, tp - tty);
759 }
760 
761 struct {
762 	int flag;
763 	char val;
764 } ttystates[] = {
765 	{ TS_WOPEN,	'W'},
766 	{ TS_ISOPEN,	'O'},
767 	{ TS_CARR_ON,	'C'},
768 	{ TS_TIMEOUT,	'T'},
769 	{ TS_FLUSH,	'F'},
770 	{ TS_BUSY,	'B'},
771 	{ TS_ASLEEP,	'A'},
772 	{ TS_XCLUDE,	'X'},
773 	{ TS_TTSTOP,	'S'},
774 	{ TS_TBLOCK,	'K'},
775 	{ TS_ASYNC,	'Y'},
776 	{ TS_BKSL,	'D'},
777 	{ TS_ERASE,	'E'},
778 	{ TS_LNCH,	'L'},
779 	{ TS_TYPEN,	'P'},
780 	{ TS_CNTTB,	'N'},
781 	{ 0,	       '\0'},
782 };
783 
784 void
785 ttyprt(tp, line)
786 	register struct tty *tp;
787 	int line;
788 {
789 	register int i, j;
790 	pid_t pgid;
791 	char *name, state[20];
792 
793 	if (usenumflag || tp->t_dev == 0 ||
794 	   (name = devname(tp->t_dev, S_IFCHR)) == NULL)
795 		(void)printf("%7d ", line);
796 	else
797 		(void)printf("%7s ", name);
798 	(void)printf("%2d %3d ", tp->t_rawq.c_cc, tp->t_canq.c_cc);
799 	(void)printf("%3d %4d %3d %8x %3d ", tp->t_outq.c_cc,
800 		tp->t_hiwat, tp->t_lowat, tp->t_addr, tp->t_col);
801 	for (i = j = 0; ttystates[i].flag; i++)
802 		if (tp->t_state&ttystates[i].flag)
803 			state[j++] = ttystates[i].val;
804 	if (j == 0)
805 		state[j++] = '-';
806 	state[j] = '\0';
807 	(void)printf("%-4s %6x", state, (u_long)tp->t_session & ~KERNBASE);
808 	pgid = 0;
809 	if (tp->t_pgrp != NULL)
810 		KGET2(&tp->t_pgrp->pg_id, &pgid, sizeof(pid_t), "pgid");
811 	(void)printf("%6d ", pgid);
812 	switch (tp->t_line) {
813 	case TTYDISC:
814 		(void)printf("term\n");
815 		break;
816 	case TABLDISC:
817 		(void)printf("tab\n");
818 		break;
819 	case SLIPDISC:
820 		(void)printf("slip\n");
821 		break;
822 	default:
823 		(void)printf("%d\n", tp->t_line);
824 		break;
825 	}
826 }
827 
828 void
829 filemode()
830 {
831 	register struct file *fp;
832 	struct file *addr;
833 	char *buf, flagbuf[16], *fbp;
834 	int len, maxfile, nfile;
835 	static char *dtypes[] = { "???", "inode", "socket" };
836 
837 	KGET(FNL_MAXFILE, maxfile);
838 	if (totalflag) {
839 		KGET(FNL_NFILE, nfile);
840 		(void)printf("%3d/%3d files\n", nfile, maxfile);
841 		return;
842 	}
843 	if (getfiles(&buf, &len) == -1)
844 		return;
845 	/*
846 	 * Getfiles returns in malloc'd memory a pointer to the first file
847 	 * structure, and then an array of file structs (whose addresses are
848 	 * derivable from the previous entry).
849 	 */
850 	addr = *((struct file **)buf);
851 	fp = (struct file *)(buf + sizeof(struct file *));
852 	nfile = (len - sizeof(struct file *)) / sizeof(struct file);
853 
854 	(void)printf("%d/%d open files\n", nfile, maxfile);
855 	(void)printf("   LOC   TYPE    FLG     CNT  MSG    DATA    OFFSET\n");
856 	for (; (char *)fp < buf + len; addr = fp->f_filef, fp++) {
857 		if ((unsigned)fp->f_type > DTYPE_SOCKET)
858 			continue;
859 		(void)printf("%x ", addr);
860 		(void)printf("%-8.8s", dtypes[fp->f_type]);
861 		fbp = flagbuf;
862 		if (fp->f_flag & FREAD)
863 			*fbp++ = 'R';
864 		if (fp->f_flag & FWRITE)
865 			*fbp++ = 'W';
866 		if (fp->f_flag & FAPPEND)
867 			*fbp++ = 'A';
868 #ifdef FSHLOCK	/* currently gone */
869 		if (fp->f_flag & FSHLOCK)
870 			*fbp++ = 'S';
871 		if (fp->f_flag & FEXLOCK)
872 			*fbp++ = 'X';
873 #endif
874 		if (fp->f_flag & FASYNC)
875 			*fbp++ = 'I';
876 		*fbp = '\0';
877 		(void)printf("%6s  %3d", flagbuf, fp->f_count);
878 		(void)printf("  %3d", fp->f_msgcount);
879 		(void)printf("  %8.1x", fp->f_data);
880 		if (fp->f_offset < 0)
881 			(void)printf("  %qx\n", fp->f_offset);
882 		else
883 			(void)printf("  %qd\n", fp->f_offset);
884 	}
885 	free(buf);
886 }
887 
888 int
889 getfiles(abuf, alen)
890 	char **abuf;
891 	int *alen;
892 {
893 	size_t len;
894 	int mib[2];
895 	char *buf;
896 
897 	/*
898 	 * XXX
899 	 * Add emulation of KINFO_FILE here.
900 	 */
901 	if (memf != NULL)
902 		errx(1, "files on dead kernel, not implemented\n");
903 
904 	mib[0] = CTL_KERN;
905 	mib[1] = KERN_FILE;
906 	if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) {
907 		warn("sysctl: KERN_FILE");
908 		return (-1);
909 	}
910 	if ((buf = malloc(len)) == NULL)
911 		err(1, NULL);
912 	if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
913 		warn("sysctl: KERN_FILE");
914 		return (-1);
915 	}
916 	*abuf = buf;
917 	*alen = len;
918 	return (0);
919 }
920 
921 /*
922  * swapmode is based on a program called swapinfo written
923  * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
924  */
925 void
926 swapmode()
927 {
928 	char *header;
929 	int hlen, nswap, nswdev, dmmax, nswapmap;
930 	int s, e, div, i, avail, nfree, npfree, used;
931 	struct swdevt *sw;
932 	long blocksize, *perdev;
933 	struct map *swapmap, *kswapmap;
934 	struct mapent *mp;
935 
936 	KGET(VM_NSWAP, nswap);
937 	KGET(VM_NSWDEV, nswdev);
938 	KGET(VM_DMMAX, dmmax);
939 	KGET(VM_NSWAPMAP, nswapmap);
940 	KGET(VM_SWAPMAP, kswapmap);	/* kernel `swapmap' is a pointer */
941 	if ((sw = malloc(nswdev * sizeof(*sw))) == NULL ||
942 	    (perdev = malloc(nswdev * sizeof(*perdev))) == NULL ||
943 	    (mp = malloc(nswapmap * sizeof(*mp))) == NULL)
944 		err(1, "malloc");
945 	KGET1(VM_SWDEVT, sw, nswdev * sizeof(*sw), "swdevt");
946 	KGET2((long)kswapmap, mp, nswapmap * sizeof(*mp), "swapmap");
947 
948 	/* First entry in map is `struct map'; rest are mapent's. */
949 	swapmap = (struct map *)mp;
950 	if (nswapmap != swapmap->m_limit - (struct mapent *)kswapmap)
951 		errx(1, "panic: nswapmap goof");
952 
953 	/* Count up swap space. */
954 	nfree = 0;
955 	memset(perdev, 0, nswdev * sizeof(*perdev));
956 	for (mp++; mp->m_addr != 0; mp++) {
957 		s = mp->m_addr;			/* start of swap region */
958 		e = mp->m_addr + mp->m_size;	/* end of region */
959 		nfree += mp->m_size;
960 
961 		/*
962 		 * Swap space is split up among the configured disks.
963 		 * The first dmmax blocks of swap space some from the
964 		 * first disk, the next dmmax blocks from the next,
965 		 * and so on.  The list of free space joins adjacent
966 		 * free blocks, ignoring device boundries.  If we want
967 		 * to keep track of this information per device, we'll
968 		 * just have to extract it ourselves.
969 		 */
970 
971 		/* calculate first device on which this falls */
972 		i = (s / dmmax) % nswdev;
973 		while (s < e) {		/* XXX this is inefficient */
974 			int bound = roundup(s+1, dmmax);
975 
976 			if (bound > e)
977 				bound = e;
978 			perdev[i] += bound - s;
979 			if (++i >= nswdev)
980 				i = 0;
981 			s = bound;
982 		}
983 	}
984 
985 	header = getbsize(&hlen, &blocksize);
986 	if (!totalflag)
987 		(void)printf("%-10s %*s %10s %10s %10s\n",
988 		    "Device", hlen, header, "Used", "Available", "Capacity");
989 	div = blocksize / 512;
990 	avail = npfree = 0;
991 	for (i = 0; i < nswdev; i++) {
992 		int xsize, xfree;
993 
994 		if (!totalflag)
995 			(void)printf("/dev/%-5s %*d ",
996 			    devname(sw[i].sw_dev, S_IFBLK),
997 			    hlen, sw[i].sw_nblks / div);
998 
999 		/*
1000 		 * Don't report statistics for partitions which have not
1001 		 * yet been activated via swapon(8).
1002 		 */
1003 		if (!sw[i].sw_freed) {
1004 			if (totalflag)
1005 				continue;
1006 			(void)printf(" *** not available for swapping ***\n");
1007 			continue;
1008 		}
1009 		xsize = sw[i].sw_nblks;
1010 		xfree = perdev[i];
1011 		used = xsize - xfree;
1012 		npfree++;
1013 		avail += xsize;
1014 		if (totalflag)
1015 			continue;
1016 		(void)printf("%10d %10d %7.0f%%\n",
1017 		    used / div, xfree / div,
1018 		    (double)used / (double)xsize * 100.0);
1019 	}
1020 
1021 	/*
1022 	 * If only one partition has been set up via swapon(8), we don't
1023 	 * need to bother with totals.
1024 	 */
1025 	used = avail - nfree;
1026 	if (totalflag) {
1027 		(void)printf("%dM/%dM swap space\n", used / 2048, avail / 2048);
1028 		return;
1029 	}
1030 	if (npfree > 1) {
1031 		(void)printf("%-10s %*d %10d %10d %7.0f%%\n",
1032 		    "Total", hlen, avail / div, used / div, nfree / div,
1033 		    (double)used / (double)avail * 100.0);
1034 	}
1035 }
1036 
1037 void
1038 usage()
1039 {
1040 	(void)fprintf(stderr,
1041 	    "usage: pstat -Tfnstv [system] [-M core] [-N system]\n");
1042 	exit(1);
1043 }
1044