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