xref: /original-bsd/usr.sbin/pstat/pstat.c (revision f737e041)
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.9 (Berkeley) 02/16/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 (num = 0, mp = mountlist.tqh_first;
667 	    mp != NULL; mp = mp->mnt_list.tqe_next) {
668 		KGET2(mp, &mount, sizeof(mount), "mount entry");
669 		for (vp = mount.mnt_vnodelist.lh_first;
670 		    vp != NULL; vp = vp->v_mntvnodes.le_next) {
671 			KGET2(vp, &vnode, sizeof(vnode), "vnode");
672 			if ((bp + VPTRSZ + VNODESZ) > evbuf)
673 				/* XXX - should realloc */
674 				errx(1, "no more room for vnodes");
675 			memmove(bp, &vp, VPTRSZ);
676 			bp += VPTRSZ;
677 			memmove(bp, &vnode, VNODESZ);
678 			bp += VNODESZ;
679 			num++;
680 		}
681 	}
682 	*avnodes = num;
683 	return ((struct e_vnode *)vbuf);
684 }
685 
686 char hdr[]="  LINE RAW CAN OUT  HWT LWT     COL STATE  SESS  PGID DISC\n";
687 int ttyspace = 128;
688 
689 void
690 ttymode()
691 {
692 	struct tty *tty;
693 
694 	if ((tty = malloc(ttyspace * sizeof(*tty))) == NULL)
695 		err(1, NULL);
696 #ifndef hp300
697 	(void)printf("1 console\n");
698 	KGET(SCONS, *tty);
699 	(void)printf(hdr);
700 	ttyprt(&tty[0], 0);
701 #endif
702 #ifdef vax
703 	if (nl[SNQD].n_type != 0)
704 		qdss();
705 	if (nl[SNDZ].n_type != 0)
706 		ttytype(tty, "dz", SDZ, SNDZ);
707 	if (nl[SNDH].n_type != 0)
708 		ttytype(tty, "dh", SDH, SNDH);
709 	if (nl[SNDMF].n_type != 0)
710 		ttytype(tty, "dmf", SDMF, SNDMF);
711 	if (nl[SNDHU].n_type != 0)
712 		ttytype(tty, "dhu", SDHU, SNDHU);
713 	if (nl[SNDMZ].n_type != 0)
714 		ttytype(tty, "dmz", SDMZ, SNDMZ);
715 #endif
716 #ifdef tahoe
717 	if (nl[SNVX].n_type != 0)
718 		ttytype(tty, "vx", SVX, SNVX);
719 	if (nl[SNMP].n_type != 0)
720 		ttytype(tty, "mp", SMP, SNMP);
721 #endif
722 #ifdef hp300
723 	if (nl[SNITE].n_type != 0)
724 		ttytype(tty, "ite", SITE, SNITE);
725 	if (nl[SNDCA].n_type != 0)
726 		ttytype(tty, "dca", SDCA, SNDCA);
727 	if (nl[SNDCM].n_type != 0)
728 		ttytype(tty, "dcm", SDCM, SNDCM);
729 	if (nl[SNDCL].n_type != 0)
730 		ttytype(tty, "dcl", SDCL, SNDCL);
731 #endif
732 #ifdef mips
733 	if (nl[SNDC].n_type != 0)
734 		ttytype(tty, "dc", SDC, SNDC);
735 #endif
736 	if (nl[SNPTY].n_type != 0)
737 		ttytype(tty, "pty", SPTY, SNPTY);
738 }
739 
740 void
741 ttytype(tty, name, type, number)
742 	register struct tty *tty;
743 	char *name;
744 	int type, number;
745 {
746 	register struct tty *tp;
747 	int ntty;
748 
749 	if (tty == NULL)
750 		return;
751 	KGET(number, ntty);
752 	(void)printf("%d %s %s\n", ntty, name, (ntty == 1) ? "line" : "lines");
753 	if (ntty > ttyspace) {
754 		ttyspace = ntty;
755 		if ((tty = realloc(tty, ttyspace * sizeof(*tty))) == 0)
756 			err(1, NULL);
757 	}
758 	KGET1(type, tty, ntty * sizeof(struct tty), "tty structs");
759 	(void)printf(hdr);
760 	for (tp = tty; tp < &tty[ntty]; tp++)
761 		ttyprt(tp, tp - tty);
762 }
763 
764 struct {
765 	int flag;
766 	char val;
767 } ttystates[] = {
768 	{ TS_WOPEN,	'W'},
769 	{ TS_ISOPEN,	'O'},
770 	{ TS_CARR_ON,	'C'},
771 	{ TS_TIMEOUT,	'T'},
772 	{ TS_FLUSH,	'F'},
773 	{ TS_BUSY,	'B'},
774 	{ TS_ASLEEP,	'A'},
775 	{ TS_XCLUDE,	'X'},
776 	{ TS_TTSTOP,	'S'},
777 	{ TS_TBLOCK,	'K'},
778 	{ TS_ASYNC,	'Y'},
779 	{ TS_BKSL,	'D'},
780 	{ TS_ERASE,	'E'},
781 	{ TS_LNCH,	'L'},
782 	{ TS_TYPEN,	'P'},
783 	{ TS_CNTTB,	'N'},
784 	{ 0,	       '\0'},
785 };
786 
787 void
788 ttyprt(tp, line)
789 	register struct tty *tp;
790 	int line;
791 {
792 	register int i, j;
793 	pid_t pgid;
794 	char *name, state[20];
795 
796 	if (usenumflag || tp->t_dev == 0 ||
797 	   (name = devname(tp->t_dev, S_IFCHR)) == NULL)
798 		(void)printf("%7d ", line);
799 	else
800 		(void)printf("%7s ", name);
801 	(void)printf("%2d %3d ", tp->t_rawq.c_cc, tp->t_canq.c_cc);
802 	(void)printf("%3d %4d %3d %3d ", tp->t_outq.c_cc,
803 		tp->t_hiwat, tp->t_lowat, tp->t_column);
804 	for (i = j = 0; ttystates[i].flag; i++)
805 		if (tp->t_state&ttystates[i].flag)
806 			state[j++] = ttystates[i].val;
807 	if (j == 0)
808 		state[j++] = '-';
809 	state[j] = '\0';
810 	(void)printf("%-4s %6x", state, (u_long)tp->t_session & ~KERNBASE);
811 	pgid = 0;
812 	if (tp->t_pgrp != NULL)
813 		KGET2(&tp->t_pgrp->pg_id, &pgid, sizeof(pid_t), "pgid");
814 	(void)printf("%6d ", pgid);
815 	switch (tp->t_line) {
816 	case TTYDISC:
817 		(void)printf("term\n");
818 		break;
819 	case TABLDISC:
820 		(void)printf("tab\n");
821 		break;
822 	case SLIPDISC:
823 		(void)printf("slip\n");
824 		break;
825 	default:
826 		(void)printf("%d\n", tp->t_line);
827 		break;
828 	}
829 }
830 
831 void
832 filemode()
833 {
834 	register struct file *fp;
835 	struct file *addr;
836 	char *buf, flagbuf[16], *fbp;
837 	int len, maxfile, nfile;
838 	static char *dtypes[] = { "???", "inode", "socket" };
839 
840 	KGET(FNL_MAXFILE, maxfile);
841 	if (totalflag) {
842 		KGET(FNL_NFILE, nfile);
843 		(void)printf("%3d/%3d files\n", nfile, maxfile);
844 		return;
845 	}
846 	if (getfiles(&buf, &len) == -1)
847 		return;
848 	/*
849 	 * Getfiles returns in malloc'd memory a pointer to the first file
850 	 * structure, and then an array of file structs (whose addresses are
851 	 * derivable from the previous entry).
852 	 */
853 	addr = *((struct file **)buf);
854 	fp = (struct file *)(buf + sizeof(struct file *));
855 	nfile = (len - sizeof(struct file *)) / sizeof(struct file);
856 
857 	(void)printf("%d/%d open files\n", nfile, maxfile);
858 	(void)printf("   LOC   TYPE    FLG     CNT  MSG    DATA    OFFSET\n");
859 	for (; (char *)fp < buf + len; addr = fp->f_filef, fp++) {
860 		if ((unsigned)fp->f_type > DTYPE_SOCKET)
861 			continue;
862 		(void)printf("%x ", addr);
863 		(void)printf("%-8.8s", dtypes[fp->f_type]);
864 		fbp = flagbuf;
865 		if (fp->f_flag & FREAD)
866 			*fbp++ = 'R';
867 		if (fp->f_flag & FWRITE)
868 			*fbp++ = 'W';
869 		if (fp->f_flag & FAPPEND)
870 			*fbp++ = 'A';
871 #ifdef FSHLOCK	/* currently gone */
872 		if (fp->f_flag & FSHLOCK)
873 			*fbp++ = 'S';
874 		if (fp->f_flag & FEXLOCK)
875 			*fbp++ = 'X';
876 #endif
877 		if (fp->f_flag & FASYNC)
878 			*fbp++ = 'I';
879 		*fbp = '\0';
880 		(void)printf("%6s  %3d", flagbuf, fp->f_count);
881 		(void)printf("  %3d", fp->f_msgcount);
882 		(void)printf("  %8.1x", fp->f_data);
883 		if (fp->f_offset < 0)
884 			(void)printf("  %qx\n", fp->f_offset);
885 		else
886 			(void)printf("  %qd\n", fp->f_offset);
887 	}
888 	free(buf);
889 }
890 
891 int
892 getfiles(abuf, alen)
893 	char **abuf;
894 	int *alen;
895 {
896 	size_t len;
897 	int mib[2];
898 	char *buf;
899 
900 	/*
901 	 * XXX
902 	 * Add emulation of KINFO_FILE here.
903 	 */
904 	if (memf != NULL)
905 		errx(1, "files on dead kernel, not implemented\n");
906 
907 	mib[0] = CTL_KERN;
908 	mib[1] = KERN_FILE;
909 	if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) {
910 		warn("sysctl: KERN_FILE");
911 		return (-1);
912 	}
913 	if ((buf = malloc(len)) == NULL)
914 		err(1, NULL);
915 	if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
916 		warn("sysctl: KERN_FILE");
917 		return (-1);
918 	}
919 	*abuf = buf;
920 	*alen = len;
921 	return (0);
922 }
923 
924 /*
925  * swapmode is based on a program called swapinfo written
926  * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
927  */
928 void
929 swapmode()
930 {
931 	char *header;
932 	int hlen, nswap, nswdev, dmmax, nswapmap, niswap, niswdev;
933 	int s, e, div, i, l, avail, nfree, npfree, used;
934 	struct swdevt *sw;
935 	long blocksize, *perdev;
936 	struct map *swapmap, *kswapmap;
937 	struct mapent *mp;
938 
939 	KGET(VM_NSWAP, nswap);
940 	KGET(VM_NSWDEV, nswdev);
941 	KGET(VM_DMMAX, dmmax);
942 	KGET(VM_NSWAPMAP, nswapmap);
943 	KGET(VM_SWAPMAP, kswapmap);	/* kernel `swapmap' is a pointer */
944 	if ((sw = malloc(nswdev * sizeof(*sw))) == NULL ||
945 	    (perdev = malloc(nswdev * sizeof(*perdev))) == NULL ||
946 	    (mp = malloc(nswapmap * sizeof(*mp))) == NULL)
947 		err(1, "malloc");
948 	KGET1(VM_SWDEVT, sw, nswdev * sizeof(*sw), "swdevt");
949 	KGET2((long)kswapmap, mp, nswapmap * sizeof(*mp), "swapmap");
950 
951 	/* Supports sequential swap */
952 	if (nl[VM_NISWAP].n_value != 0) {
953 		KGET(VM_NISWAP, niswap);
954 		KGET(VM_NISWDEV, niswdev);
955 	} else {
956 		niswap = nswap;
957 		niswdev = nswdev;
958 	}
959 
960 	/* First entry in map is `struct map'; rest are mapent's. */
961 	swapmap = (struct map *)mp;
962 	if (nswapmap != swapmap->m_limit - (struct mapent *)kswapmap)
963 		errx(1, "panic: nswapmap goof");
964 
965 	/* Count up swap space. */
966 	nfree = 0;
967 	memset(perdev, 0, nswdev * sizeof(*perdev));
968 	for (mp++; mp->m_addr != 0; mp++) {
969 		s = mp->m_addr;			/* start of swap region */
970 		e = mp->m_addr + mp->m_size;	/* end of region */
971 		nfree += mp->m_size;
972 
973 		/*
974 		 * Swap space is split up among the configured disks.
975 		 *
976 		 * For interleaved swap devices, the first dmmax blocks
977 		 * of swap space some from the first disk, the next dmmax
978 		 * blocks from the next, and so on up to niswap blocks.
979 		 *
980 		 * Sequential swap devices follow the interleaved devices
981 		 * (i.e. blocks starting at niswap) in the order in which
982 		 * they appear in the swdev table.  The size of each device
983 		 * will be a multiple of dmmax.
984 		 *
985 		 * The list of free space joins adjacent free blocks,
986 		 * ignoring device boundries.  If we want to keep track
987 		 * of this information per device, we'll just have to
988 		 * extract it ourselves.  We know that dmmax-sized chunks
989 		 * cannot span device boundaries (interleaved or sequential)
990 		 * so we loop over such chunks assigning them to devices.
991 		 */
992 		i = -1;
993 		while (s < e) {		/* XXX this is inefficient */
994 			int bound = roundup(s+1, dmmax);
995 
996 			if (bound > e)
997 				bound = e;
998 			if (bound <= niswap) {
999 				/* Interleaved swap chunk. */
1000 				if (i == -1)
1001 					i = (s / dmmax) % niswdev;
1002 				perdev[i] += bound - s;
1003 				if (++i >= niswdev)
1004 					i = 0;
1005 			} else {
1006 				/* Sequential swap chunk. */
1007 				if (i < niswdev) {
1008 					i = niswdev;
1009 					l = niswap + sw[i].sw_nblks;
1010 				}
1011 				while (s >= l) {
1012 					/* XXX don't die on bogus blocks */
1013 					if (i == nswdev-1)
1014 						break;
1015 					l += sw[++i].sw_nblks;
1016 				}
1017 				perdev[i] += bound - s;
1018 			}
1019 			s = bound;
1020 		}
1021 	}
1022 
1023 	header = getbsize(&hlen, &blocksize);
1024 	if (!totalflag)
1025 		(void)printf("%-11s %*s %8s %8s %8s  %s\n",
1026 		    "Device", hlen, header,
1027 		    "Used", "Avail", "Capacity", "Type");
1028 	div = blocksize / 512;
1029 	avail = npfree = 0;
1030 	for (i = 0; i < nswdev; i++) {
1031 		int xsize, xfree;
1032 
1033 		if (!totalflag)
1034 			(void)printf("/dev/%-6s %*d ",
1035 			    devname(sw[i].sw_dev, S_IFBLK),
1036 			    hlen, sw[i].sw_nblks / div);
1037 
1038 		/*
1039 		 * Don't report statistics for partitions which have not
1040 		 * yet been activated via swapon(8).
1041 		 */
1042 		if (!(sw[i].sw_flags & SW_FREED)) {
1043 			if (totalflag)
1044 				continue;
1045 			(void)printf(" *** not available for swapping ***\n");
1046 			continue;
1047 		}
1048 		xsize = sw[i].sw_nblks;
1049 		xfree = perdev[i];
1050 		used = xsize - xfree;
1051 		npfree++;
1052 		avail += xsize;
1053 		if (totalflag)
1054 			continue;
1055 		(void)printf("%8d %8d %5.0f%%    %s\n",
1056 		    used / div, xfree / div,
1057 		    (double)used / (double)xsize * 100.0,
1058 		    (sw[i].sw_flags & SW_SEQUENTIAL) ?
1059 			     "Sequential" : "Interleaved");
1060 	}
1061 
1062 	/*
1063 	 * If only one partition has been set up via swapon(8), we don't
1064 	 * need to bother with totals.
1065 	 */
1066 	used = avail - nfree;
1067 	if (totalflag) {
1068 		(void)printf("%dM/%dM swap space\n", used / 2048, avail / 2048);
1069 		return;
1070 	}
1071 	if (npfree > 1) {
1072 		(void)printf("%-11s %*d %8d %8d %5.0f%%\n",
1073 		    "Total", hlen, avail / div, used / div, nfree / div,
1074 		    (double)used / (double)avail * 100.0);
1075 	}
1076 }
1077 
1078 void
1079 usage()
1080 {
1081 	(void)fprintf(stderr,
1082 	    "usage: pstat -Tfnstv [system] [-M core] [-N system]\n");
1083 	exit(1);
1084 }
1085