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