xref: /netbsd-src/usr.sbin/pstat/pstat.c (revision bcc8ec9959e7b01e313d813067bfb43a3ad70551)
1 /*	$NetBSD: pstat.c,v 1.57 2001/01/05 03:27:28 lukem Exp $	*/
2 
3 /*-
4  * Copyright (c) 1980, 1991, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
39 	The Regents of the University of California.  All rights reserved.\n");
40 #endif /* not lint */
41 
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)pstat.c	8.16 (Berkeley) 5/9/95";
45 #else
46 __RCSID("$NetBSD: pstat.c,v 1.57 2001/01/05 03:27:28 lukem Exp $");
47 #endif
48 #endif /* not lint */
49 
50 #include <sys/param.h>
51 #include <sys/time.h>
52 #include <sys/vnode.h>
53 #include <sys/map.h>
54 #include <sys/ucred.h>
55 #define _KERNEL
56 #define _LKM
57 #include <sys/file.h>
58 #include <ufs/ufs/quota.h>
59 #include <ufs/ufs/inode.h>
60 #define NFS
61 #include <sys/mount.h>
62 #undef NFS
63 #include <sys/uio.h>
64 #include <sys/namei.h>
65 #include <miscfs/genfs/layer.h>
66 #include <miscfs/union/union.h>
67 #undef _KERNEL
68 #include <sys/stat.h>
69 #include <nfs/nfsproto.h>
70 #include <nfs/rpcv2.h>
71 #include <nfs/nfs.h>
72 #include <nfs/nfsnode.h>
73 #include <sys/ioctl.h>
74 #include <sys/tty.h>
75 #include <sys/conf.h>
76 #include <sys/device.h>
77 
78 #include <sys/sysctl.h>
79 
80 #include <err.h>
81 #include <kvm.h>
82 #include <limits.h>
83 #include <nlist.h>
84 #include <stdio.h>
85 #include <stdlib.h>
86 #include <string.h>
87 #include <unistd.h>
88 
89 #include "swapctl.h"
90 
91 struct nlist nl[] = {
92 #define	V_MOUNTLIST	0
93 	{ "_mountlist" },	/* address of head of mount list. */
94 #define	V_NUMV		1
95 	{ "_numvnodes" },
96 #define	FNL_NFILE	2
97 	{ "_nfiles" },
98 #define FNL_MAXFILE	3
99 	{ "_maxfiles" },
100 #define TTY_NTTY	4
101 	{ "_tty_count" },
102 #define TTY_TTYLIST	5
103 	{ "_ttylist" },
104 #define NLMANDATORY TTY_TTYLIST	/* names up to here are mandatory */
105 	{ "" }
106 };
107 
108 int	usenumflag;
109 int	totalflag;
110 int	kflag;
111 char	*nlistf	= NULL;
112 char	*memf	= NULL;
113 kvm_t	*kd;
114 
115 struct {
116 	int m_flag;
117 	const char *m_name;
118 } mnt_flags[] = {
119 	{ MNT_RDONLY, "rdonly" },
120 	{ MNT_SYNCHRONOUS, "sync" },
121 	{ MNT_NOEXEC, "noexec" },
122 	{ MNT_NOSUID, "nosuid" },
123 	{ MNT_NODEV, "nodev" },
124 	{ MNT_UNION, "union" },
125 	{ MNT_ASYNC, "async" },
126 	{ MNT_NOCOREDUMP, "nocoredump" },
127 	{ MNT_NOATIME, "noatime" },
128 	{ MNT_SYMPERM, "symperm" },
129 	{ MNT_NODEVMTIME, "nodevmtime" },
130 	{ MNT_SOFTDEP, "softdep" },
131 	{ MNT_EXRDONLY, "exrdonly" },
132 	{ MNT_EXPORTED, "exported" },
133 	{ MNT_DEFEXPORTED, "defexported" },
134 	{ MNT_EXPORTANON, "exportanon" },
135 	{ MNT_EXKERB, "exkerb" },
136 	{ MNT_EXNORESPORT, "exnoresport" },
137 	{ MNT_EXPUBLIC, "expublic" },
138 	{ MNT_LOCAL, "local" },
139 	{ MNT_QUOTA, "quota" },
140 	{ MNT_ROOTFS, "rootfs" },
141 	{ MNT_UPDATE, "update" },
142 	{ MNT_DELEXPORT, "delexport" },
143 	{ MNT_RELOAD, "reload" },
144 	{ MNT_FORCE, "force" },
145 	{ MNT_GONE, "gone" },
146 	{ MNT_UNMOUNT, "unmount" },
147 	{ MNT_WANTRDWR, "wantrdwr" },
148 	{ 0 }
149 };
150 
151 #define	SVAR(var) __STRING(var)	/* to force expansion */
152 #define	KGET(idx, var)							\
153 	KGET1(idx, &var, sizeof(var), SVAR(var))
154 #define	KGET1(idx, p, s, msg)						\
155 	KGET2(nl[idx].n_value, p, s, msg)
156 #define	KGET2(addr, p, s, msg)						\
157 	if (kvm_read(kd, (u_long)(addr), p, s) != s)			\
158 		warnx("cannot read %s: %s", msg, kvm_geterr(kd))
159 #define	KGETRET(addr, p, s, msg)					\
160 	if (kvm_read(kd, (u_long)(addr), p, s) != s) {			\
161 		warnx("cannot read %s: %s", msg, kvm_geterr(kd));	\
162 		return (0);						\
163 	}
164 
165 void	filemode __P((void));
166 int	getfiles __P((char **, int *));
167 struct mount *
168 	getmnt __P((struct mount *));
169 struct e_vnode *
170 	kinfo_vnodes __P((int *));
171 void	layer_header __P((void));
172 int	layer_print __P((struct vnode *));
173 struct e_vnode *
174 	loadvnodes __P((int *));
175 int	main __P((int, char **));
176 void	mount_print __P((struct mount *));
177 void	nfs_header __P((void));
178 int	nfs_print __P((struct vnode *));
179 void	ttymode __P((void));
180 void	ttyprt __P((struct tty *));
181 void	ufs_getflags __P((struct vnode *, struct inode *, char *));
182 void	ufs_header __P((void));
183 int	ufs_print __P((struct vnode *));
184 int	ext2fs_print __P((struct vnode *));
185 void	union_header __P((void));
186 int	union_print __P((struct vnode *));
187 void	usage __P((void));
188 void	vnode_header __P((void));
189 void	vnode_print __P((struct vnode *, struct vnode *));
190 void	vnodemode __P((void));
191 
192 int
193 main(argc, argv)
194 	int argc;
195 	char *argv[];
196 {
197 	int ch, i, quit, ret;
198 	int fileflag, swapflag, ttyflag, vnodeflag;
199 	gid_t egid = getegid();
200 	char buf[_POSIX2_LINE_MAX];
201 
202 	setegid(getgid());
203 	fileflag = swapflag = ttyflag = vnodeflag = 0;
204 	while ((ch = getopt(argc, argv, "TM:N:fiknstv")) != -1)
205 		switch (ch) {
206 		case 'f':
207 			fileflag = 1;
208 			break;
209 		case 'M':
210 			memf = optarg;
211 			break;
212 		case 'N':
213 			nlistf = optarg;
214 			break;
215 		case 'n':
216 			usenumflag = 1;
217 			break;
218 		case 's':
219 			swapflag = 1;
220 			break;
221 		case 'T':
222 			totalflag = 1;
223 			break;
224 		case 't':
225 			ttyflag = 1;
226 			break;
227 		case 'k':
228 			kflag = 1;
229 			break;
230 		case 'v':
231 		case 'i':		/* Backward compatibility. */
232 			vnodeflag = 1;
233 			break;
234 		default:
235 			usage();
236 		}
237 	argc -= optind;
238 	argv += optind;
239 
240 	/*
241 	 * Discard setgid privileges.  If not the running kernel, we toss
242 	 * them away totally so that bad guys can't print interesting stuff
243 	 * from kernel memory, otherwise switch back to kmem for the
244 	 * duration of the kvm_openfiles() call.
245 	 */
246 	if (nlistf != NULL || memf != NULL)
247 		(void)setgid(getgid());
248 	else
249 		(void)setegid(egid);
250 
251 	if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf)) == 0)
252 		errx(1, "kvm_openfiles: %s", buf);
253 
254 	/* get rid of it now anyway */
255 	if (nlistf == NULL && memf == NULL)
256 		(void)setgid(getgid());
257 	if ((ret = kvm_nlist(kd, nl)) != 0) {
258 		if (ret == -1)
259 			errx(1, "kvm_nlist: %s", kvm_geterr(kd));
260 		for (i = quit = 0; i <= NLMANDATORY; i++)
261 			if (!nl[i].n_value) {
262 				quit = 1;
263 				warnx("undefined symbol: %s", nl[i].n_name);
264 			}
265 		if (quit)
266 			exit(1);
267 	}
268 	if (!(fileflag | vnodeflag | ttyflag | swapflag | totalflag))
269 		usage();
270 	if (fileflag || totalflag)
271 		filemode();
272 	if (vnodeflag || totalflag)
273 		vnodemode();
274 	if (ttyflag)
275 		ttymode();
276 	if (swapflag || totalflag)
277 		list_swap(0, kflag, 0, totalflag, 1);
278 	exit (0);
279 }
280 
281 struct e_vnode {
282 	struct vnode *avnode;
283 	struct vnode vnode;
284 };
285 
286 void
287 vnodemode()
288 {
289 	struct e_vnode *e_vnodebase, *endvnode, *evp;
290 	struct vnode *vp;
291 	struct mount *maddr, *mp;
292 	int numvnodes;
293 	int (*vnode_fsprint) __P((struct vnode *)); /* per-fs data printer */
294 
295 	mp = NULL;
296 	e_vnodebase = loadvnodes(&numvnodes);
297 	if (totalflag) {
298 		(void)printf("%7d vnodes\n", numvnodes);
299 		return;
300 	}
301 	endvnode = e_vnodebase + numvnodes;
302 	(void)printf("%d active vnodes\n", numvnodes);
303 
304 #define	ST	mp->mnt_stat
305 #define	FSTYPE_IS(mp, name)						\
306 	(strncmp((mp)->mnt_stat.f_fstypename, (name), MFSNAMELEN) == 0)
307 	maddr = NULL;
308 	vnode_fsprint = NULL;
309 	for (evp = e_vnodebase; evp < endvnode; evp++) {
310 		vp = &evp->vnode;
311 		if (vp->v_mount != maddr) {
312 			/*
313 			 * New filesystem
314 			 */
315 			if ((mp = getmnt(vp->v_mount)) == NULL)
316 				continue;
317 			maddr = vp->v_mount;
318 			mount_print(mp);
319 			vnode_header();
320 			if (FSTYPE_IS(mp, MOUNT_FFS) ||
321 			    FSTYPE_IS(mp, MOUNT_MFS)) {
322 				ufs_header();
323 				vnode_fsprint = ufs_print;
324 			} else if (FSTYPE_IS(mp, MOUNT_NFS)) {
325 				nfs_header();
326 				vnode_fsprint = nfs_print;
327 			} else if (FSTYPE_IS(mp, MOUNT_EXT2FS)) {
328 				ufs_header();
329 				vnode_fsprint = ext2fs_print;
330 			} else if (FSTYPE_IS(mp, MOUNT_NULL) ||
331 			    FSTYPE_IS(mp, MOUNT_OVERLAY) ||
332 			    FSTYPE_IS(mp, MOUNT_UMAP)) {
333 				layer_header();
334 				vnode_fsprint = layer_print;
335 			} else if (FSTYPE_IS(mp, MOUNT_UNION)) {
336 				union_header();
337 				vnode_fsprint = union_print;
338 			} else
339 				vnode_fsprint = NULL;
340 			(void)printf("\n");
341 		}
342 		vnode_print(evp->avnode, vp);
343 		if (VTOI(vp) != NULL && vnode_fsprint != NULL)
344 			(*vnode_fsprint)(vp);
345 		(void)printf("\n");
346 	}
347 	free(e_vnodebase);
348 }
349 
350 void
351 vnode_header()
352 {
353 
354 	(void)printf("ADDR     TYP VFLAG  USE HOLD TAG");
355 }
356 
357 void
358 vnode_print(avnode, vp)
359 	struct vnode *avnode;
360 	struct vnode *vp;
361 {
362 	char *type, flags[16];
363 	char *fp = flags;
364 	int flag;
365 
366 	/*
367 	 * set type
368 	 */
369 	switch (vp->v_type) {
370 	case VNON:
371 		type = "non"; break;
372 	case VREG:
373 		type = "reg"; break;
374 	case VDIR:
375 		type = "dir"; break;
376 	case VBLK:
377 		type = "blk"; break;
378 	case VCHR:
379 		type = "chr"; break;
380 	case VLNK:
381 		type = "lnk"; break;
382 	case VSOCK:
383 		type = "soc"; break;
384 	case VFIFO:
385 		type = "fif"; break;
386 	case VBAD:
387 		type = "bad"; break;
388 	default:
389 		type = "unk"; break;
390 	}
391 	/*
392 	 * gather flags
393 	 */
394 	flag = vp->v_flag;
395 	if (flag & VROOT)
396 		*fp++ = 'R';
397 	if (flag & VTEXT)
398 		*fp++ = 'T';
399 	if (flag & VSYSTEM)
400 		*fp++ = 'S';
401 	if (flag & VISTTY)
402 		*fp++ = 'I';
403 	if (flag & VXLOCK)
404 		*fp++ = 'L';
405 	if (flag & VXWANT)
406 		*fp++ = 'W';
407 	if (flag & VBWAIT)
408 		*fp++ = 'B';
409 	if (flag & VALIASED)
410 		*fp++ = 'A';
411 	if (flag & VDIROP)
412 		*fp++ = 'D';
413 	if (flag & VLAYER)
414 		*fp++ = 'Y';
415 	if (flag & VONWORKLST)
416 		*fp++ = 'O';
417 	if (flag == 0)
418 		*fp++ = '-';
419 	*fp = '\0';
420 	(void)printf("%8lx %s %5s %4ld %4ld %3d",
421 	    (long)avnode, type, flags, (long)vp->v_usecount,
422 	    (long)vp->v_holdcnt, vp->v_tag);
423 }
424 
425 void
426 ufs_getflags(vp, ip, flags)
427 	struct vnode *vp;
428 	struct inode *ip;
429 	char *flags;
430 {
431 	int flag;
432 
433 	/*
434 	 * XXX need to to locking state.
435 	 */
436 
437 	flag = ip->i_flag;
438 	if (flag & IN_ACCESS)
439 		*flags++ = 'A';
440 	if (flag & IN_CHANGE)
441 		*flags++ = 'C';
442 	if (flag & IN_UPDATE)
443 		*flags++ = 'U';
444 	if (flag & IN_MODIFIED)
445 		*flags++ = 'M';
446 	if (flag & IN_ACCESSED)
447 		*flags++ = 'a';
448 	if (flag & IN_RENAME)
449 		*flags++ = 'R';
450 	if (flag & IN_SHLOCK)
451 		*flags++ = 'S';
452 	if (flag & IN_EXLOCK)
453 		*flags++ = 'E';
454 	if (flag & IN_CLEANING)
455 		*flags++ = 'c';
456 	if (flag & IN_ADIROP)
457 		*flags++ = 'a';
458 	if (flag == 0)
459 		*flags++ = '-';
460 	*flags = '\0';
461 
462 }
463 
464 void
465 ufs_header()
466 {
467 
468 	(void)printf(" FILEID IFLAG RDEV|SZ");
469 }
470 
471 int
472 ufs_print(vp)
473 	struct vnode *vp;
474 {
475 	struct inode inode, *ip = &inode;
476 	char flagbuf[16];
477 	char *name;
478 	mode_t type;
479 
480 	KGETRET(VTOI(vp), &inode, sizeof(struct inode), "vnode's inode");
481 	ufs_getflags(vp, ip, flagbuf);
482 	(void)printf(" %6d %5s", ip->i_number, flagbuf);
483 	type = ip->i_ffs_mode & S_IFMT;
484 	if (S_ISCHR(ip->i_ffs_mode) || S_ISBLK(ip->i_ffs_mode))
485 		if (usenumflag ||
486 		    ((name = devname(ip->i_ffs_rdev, type)) == NULL))
487 			(void)printf("   %2d,%-2d",
488 			    major(ip->i_ffs_rdev), minor(ip->i_ffs_rdev));
489 		else
490 			(void)printf(" %7s", name);
491 	else
492 		(void)printf(" %7lld", (long long)ip->i_ffs_size);
493 	return (0);
494 }
495 
496 int
497 ext2fs_print(vp)
498 	struct vnode *vp;
499 {
500 	struct inode inode, *ip = &inode;
501 	char flagbuf[16];
502 	char *name;
503 	mode_t type;
504 
505 	KGETRET(VTOI(vp), &inode, sizeof(struct inode), "vnode's inode");
506 	ufs_getflags(vp, ip, flagbuf);
507 	(void)printf(" %6d %5s", ip->i_number, flagbuf);
508 	type = ip->i_e2fs_mode & S_IFMT;
509 	if (S_ISCHR(ip->i_e2fs_mode) || S_ISBLK(ip->i_e2fs_mode))
510 		if (usenumflag ||
511 		    ((name = devname(ip->i_e2fs_rdev, type)) == NULL))
512 			(void)printf("   %2d,%-2d",
513 			    major(ip->i_e2fs_rdev), minor(ip->i_e2fs_rdev));
514 		else
515 			(void)printf(" %7s", name);
516 	else
517 		(void)printf(" %7u", (u_int)ip->i_e2fs_size);
518 	return (0);
519 }
520 
521 void
522 nfs_header()
523 {
524 
525 	(void)printf(" FILEID NFLAG RDEV|SZ");
526 }
527 
528 int
529 nfs_print(vp)
530 	struct vnode *vp;
531 {
532 	struct nfsnode nfsnode, *np = &nfsnode;
533 	char flagbuf[16], *flags = flagbuf;
534 	int flag;
535 	struct vattr va;
536 	char *name;
537 	mode_t type;
538 
539 	KGETRET(VTONFS(vp), &nfsnode, sizeof(nfsnode), "vnode's nfsnode");
540 	flag = np->n_flag;
541 	if (flag & NFLUSHWANT)
542 		*flags++ = 'W';
543 	if (flag & NFLUSHINPROG)
544 		*flags++ = 'P';
545 	if (flag & NMODIFIED)
546 		*flags++ = 'M';
547 	if (flag & NWRITEERR)
548 		*flags++ = 'E';
549 	if (flag & NQNFSNONCACHE)
550 		*flags++ = 'X';
551 	if (flag & NQNFSWRITE)
552 		*flags++ = 'O';
553 	if (flag & NQNFSEVICTED)
554 		*flags++ = 'G';
555 	if (flag & NACC)
556 		*flags++ = 'A';
557 	if (flag & NUPD)
558 		*flags++ = 'U';
559 	if (flag & NCHG)
560 		*flags++ = 'C';
561 	if (flag == 0)
562 		*flags++ = '-';
563 	*flags = '\0';
564 
565 	KGETRET(np->n_vattr, &va, sizeof(va), "vnode attr");
566 	(void)printf(" %6ld %5s", (long)va.va_fileid, flagbuf);
567 	switch (va.va_type) {
568 	case VCHR:
569 		type = S_IFCHR;
570 		goto device;
571 
572 	case VBLK:
573 		type = S_IFBLK;
574 	device:
575 		if (usenumflag || ((name = devname(va.va_rdev, type)) == NULL))
576 			(void)printf("   %2d,%-2d",
577 			    major(va.va_rdev), minor(va.va_rdev));
578 		else
579 			(void)printf(" %7s", name);
580 		break;
581 	default:
582 		(void)printf(" %7lld", (long long)np->n_size);
583 		break;
584 	}
585 	return (0);
586 }
587 
588 void
589 layer_header()
590 {
591 
592 	(void)printf("    LOWER");
593 }
594 
595 int
596 layer_print(vp)
597 	struct vnode *vp;
598 {
599 	struct layer_node lnode, *lp = &lnode;
600 
601 	KGETRET(VTOLAYER(vp), &lnode, sizeof(lnode), "layer vnode");
602 
603 	(void)printf(" %8lx", (long)lp->layer_lowervp);
604 	return (0);
605 }
606 
607 void
608 union_header()
609 {
610 
611 	(void)printf("    UPPER    LOWER");
612 }
613 
614 int
615 union_print(vp)
616 	struct vnode *vp;
617 {
618 	struct union_node unode, *up = &unode;
619 
620 	KGETRET(VTOUNION(vp), &unode, sizeof(unode), "vnode's unode");
621 
622 	(void)printf(" %8lx %8lx", (long)up->un_uppervp, (long)up->un_lowervp);
623 	return (0);
624 }
625 
626 /*
627  * Given a pointer to a mount structure in kernel space,
628  * read it in and return a usable pointer to it.
629  */
630 struct mount *
631 getmnt(maddr)
632 	struct mount *maddr;
633 {
634 	static struct mtab {
635 		struct mtab *next;
636 		struct mount *maddr;
637 		struct mount mount;
638 	} *mhead = NULL;
639 	struct mtab *mt;
640 
641 	for (mt = mhead; mt != NULL; mt = mt->next)
642 		if (maddr == mt->maddr)
643 			return (&mt->mount);
644 	if ((mt = malloc(sizeof(struct mtab))) == NULL)
645 		err(1, "malloc");
646 	KGETRET(maddr, &mt->mount, sizeof(struct mount), "mount table");
647 	mt->maddr = maddr;
648 	mt->next = mhead;
649 	mhead = mt;
650 	return (&mt->mount);
651 }
652 
653 void
654 mount_print(mp)
655 	struct mount *mp;
656 {
657 	int flags;
658 
659 	(void)printf("*** MOUNT %s %s on %s", ST.f_fstypename,
660 	    ST.f_mntfromname, ST.f_mntonname);
661 	if ((flags = mp->mnt_flag) != 0) {
662 		int i;
663 		const char *sep = " (";
664 
665 		for (i = 0; mnt_flags[i].m_flag; i++) {
666 			if (flags & mnt_flags[i].m_flag) {
667 				(void)printf("%s%s", sep, mnt_flags[i].m_name);
668 				flags &= ~mnt_flags[i].m_flag;
669 				sep = ",";
670 			}
671 		}
672 		if (flags)
673 			(void)printf("%sunknown_flags:%x", sep, flags);
674 		(void)printf(")");
675 	}
676 	(void)printf("\n");
677 }
678 
679 struct e_vnode *
680 loadvnodes(avnodes)
681 	int *avnodes;
682 {
683 	int mib[2];
684 	size_t copysize;
685 	struct e_vnode *vnodebase;
686 
687 	if (memf != NULL) {
688 		/*
689 		 * do it by hand
690 		 */
691 		return (kinfo_vnodes(avnodes));
692 	}
693 	mib[0] = CTL_KERN;
694 	mib[1] = KERN_VNODE;
695 	if (sysctl(mib, 2, NULL, &copysize, NULL, 0) == -1)
696 		err(1, "sysctl: KERN_VNODE");
697 	if ((vnodebase = malloc(copysize)) == NULL)
698 		err(1, "malloc");
699 	if (sysctl(mib, 2, vnodebase, &copysize, NULL, 0) == -1)
700 		err(1, "sysctl: KERN_VNODE");
701 	if (copysize % sizeof(struct e_vnode))
702 		errx(1, "vnode size mismatch");
703 	*avnodes = copysize / sizeof(struct e_vnode);
704 
705 	return (vnodebase);
706 }
707 
708 /*
709  * simulate what a running kernel does in in kinfo_vnode
710  */
711 struct e_vnode *
712 kinfo_vnodes(avnodes)
713 	int *avnodes;
714 {
715 	struct mntlist mountlist;
716 	struct mount *mp, mount;
717 	struct vnode *vp, vnode;
718 	char *vbuf, *evbuf, *bp;
719 	int num, numvnodes;
720 
721 #define VPTRSZ  sizeof(struct vnode *)
722 #define VNODESZ sizeof(struct vnode)
723 
724 	KGET(V_NUMV, numvnodes);
725 	if ((vbuf = malloc((numvnodes + 20) * (VPTRSZ + VNODESZ))) == NULL)
726 		err(1, "malloc");
727 	bp = vbuf;
728 	evbuf = vbuf + (numvnodes + 20) * (VPTRSZ + VNODESZ);
729 	KGET(V_MOUNTLIST, mountlist);
730 	for (num = 0, mp = mountlist.cqh_first;;
731 	    mp = mount.mnt_list.cqe_next) {
732 		KGET2(mp, &mount, sizeof(mount), "mount entry");
733 		for (vp = mount.mnt_vnodelist.lh_first;
734 		    vp != NULL; vp = vnode.v_mntvnodes.le_next) {
735 			KGET2(vp, &vnode, sizeof(vnode), "vnode");
736 			if ((bp + VPTRSZ + VNODESZ) > evbuf)
737 				/* XXX - should realloc */
738 				errx(1, "no more room for vnodes");
739 			memmove(bp, &vp, VPTRSZ);
740 			bp += VPTRSZ;
741 			memmove(bp, &vnode, VNODESZ);
742 			bp += VNODESZ;
743 			num++;
744 		}
745 		if (mp == mountlist.cqh_last)
746 			break;
747 	}
748 	*avnodes = num;
749 	return ((struct e_vnode *)vbuf);
750 }
751 
752 const char hdr[]="  LINE RAW CAN OUT  HWT LWT     COL STATE  SESS      PGID DISC\n";
753 int ttyspace = 128;
754 
755 void
756 ttymode()
757 {
758 	int ntty;
759 	struct ttylist_head tty_head;
760 	struct tty *tp, tty;
761 
762 	KGET(TTY_NTTY, ntty);
763 	(void)printf("%d terminal device%s\n", ntty, ntty == 1 ? "" : "s");
764 	KGET(TTY_TTYLIST, tty_head);
765 	(void)printf(hdr);
766 	for (tp = tty_head.tqh_first; tp; tp = tty.tty_link.tqe_next) {
767 		KGET2(tp, &tty, sizeof tty, "tty struct");
768 		ttyprt(&tty);
769 	}
770 }
771 
772 struct {
773 	int flag;
774 	char val;
775 } ttystates[] = {
776 	{ TS_ISOPEN,	'O'},
777 	{ TS_DIALOUT,	'>'},
778 	{ TS_CARR_ON,	'C'},
779 	{ TS_TIMEOUT,	'T'},
780 	{ TS_FLUSH,	'F'},
781 	{ TS_BUSY,	'B'},
782 	{ TS_ASLEEP,	'A'},
783 	{ TS_XCLUDE,	'X'},
784 	{ TS_TTSTOP,	'S'},
785 	{ TS_TBLOCK,	'K'},
786 	{ TS_ASYNC,	'Y'},
787 	{ TS_BKSL,	'D'},
788 	{ TS_ERASE,	'E'},
789 	{ TS_LNCH,	'L'},
790 	{ TS_TYPEN,	'P'},
791 	{ TS_CNTTB,	'N'},
792 	{ 0,	       '\0'},
793 };
794 
795 void
796 ttyprt(tp)
797 	struct tty *tp;
798 {
799 	int i, j;
800 	pid_t pgid;
801 	char *name, state[20], buffer;
802 	struct linesw t_linesw;
803 
804 	if (usenumflag || (name = devname(tp->t_dev, S_IFCHR)) == NULL)
805 		(void)printf("0x%3x:%1x ", major(tp->t_dev), minor(tp->t_dev));
806 	else
807 		(void)printf("%-7s ", name);
808 	(void)printf("%2d %3d ", tp->t_rawq.c_cc, tp->t_canq.c_cc);
809 	(void)printf("%3d %4d %3d %7d ", tp->t_outq.c_cc,
810 		tp->t_hiwat, tp->t_lowat, tp->t_column);
811 	for (i = j = 0; ttystates[i].flag; i++)
812 		if (tp->t_state&ttystates[i].flag)
813 			state[j++] = ttystates[i].val;
814 	if (tp->t_wopen)
815 		state[j++] = 'W';
816 	if (j == 0)
817 		state[j++] = '-';
818 	state[j] = '\0';
819 	(void)printf("%-6s %8lX", state, (u_long)tp->t_session);
820 	pgid = 0;
821 	if (tp->t_pgrp != NULL)
822 		KGET2(&tp->t_pgrp->pg_id, &pgid, sizeof(pid_t), "pgid");
823 	(void)printf("%6d ", pgid);
824 	KGET2(tp->t_linesw, &t_linesw, sizeof(t_linesw),
825 		"line discipline switch table");
826 	name = t_linesw.l_name;
827 	for (;;) {
828 		KGET2(name, &buffer, sizeof(buffer), "line discipline name");
829 		if (buffer == '\0')
830 			break;
831 		(void)putchar(buffer);
832 		name++;
833 	}
834 	(void)putchar('\n');
835 }
836 
837 void
838 filemode()
839 {
840 	struct file *fp;
841 	struct file *addr;
842 	char *buf, flagbuf[16], *fbp;
843 	int len, maxfile, nfile;
844 	static char *dtypes[] = { "???", "inode", "socket" };
845 
846 	KGET(FNL_MAXFILE, maxfile);
847 	if (totalflag) {
848 		KGET(FNL_NFILE, nfile);
849 		(void)printf("%3d/%3d files\n", nfile, maxfile);
850 		return;
851 	}
852 	if (getfiles(&buf, &len) == -1)
853 		return;
854 	/*
855 	 * Getfiles returns in malloc'd memory a pointer to the first file
856 	 * structure, and then an array of file structs (whose addresses are
857 	 * derivable from the previous entry).
858 	 */
859 	addr = ((struct filelist *)buf)->lh_first;
860 	fp = (struct file *)(buf + sizeof(struct filelist));
861 	nfile = (len - sizeof(struct filelist)) / sizeof(struct file);
862 
863 	(void)printf("%d/%d open files\n", nfile, maxfile);
864 	(void)printf("   LOC   TYPE    FLG     CNT  MSG    DATA    OFFSET\n");
865 	for (; (char *)fp < buf + len; addr = fp->f_list.le_next, fp++) {
866 		if ((unsigned)fp->f_type > DTYPE_SOCKET)
867 			continue;
868 		(void)printf("%lx ", (long)addr);
869 		(void)printf("%-8.8s", dtypes[fp->f_type]);
870 		fbp = flagbuf;
871 		if (fp->f_flag & FREAD)
872 			*fbp++ = 'R';
873 		if (fp->f_flag & FWRITE)
874 			*fbp++ = 'W';
875 		if (fp->f_flag & FAPPEND)
876 			*fbp++ = 'A';
877 #ifdef FSHLOCK	/* currently gone */
878 		if (fp->f_flag & FSHLOCK)
879 			*fbp++ = 'S';
880 		if (fp->f_flag & FEXLOCK)
881 			*fbp++ = 'X';
882 #endif
883 		if (fp->f_flag & FASYNC)
884 			*fbp++ = 'I';
885 		*fbp = '\0';
886 		(void)printf("%6s  %3d", flagbuf, fp->f_count);
887 		(void)printf("  %3d", fp->f_msgcount);
888 		(void)printf("  %8.1lx", (long)fp->f_data);
889 		if (fp->f_offset < 0)
890 			(void)printf("  %llx\n", (long long)fp->f_offset);
891 		else
892 			(void)printf("  %lld\n", (long long)fp->f_offset);
893 	}
894 	free(buf);
895 }
896 
897 int
898 getfiles(abuf, alen)
899 	char **abuf;
900 	int *alen;
901 {
902 	size_t len;
903 	int mib[2];
904 	char *buf;
905 
906 	/*
907 	 * XXX
908 	 * Add emulation of KINFO_FILE here.
909 	 */
910 	if (memf != NULL)
911 		errx(1, "files on dead kernel, not implemented\n");
912 
913 	mib[0] = CTL_KERN;
914 	mib[1] = KERN_FILE;
915 	if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) {
916 		warn("sysctl: KERN_FILE");
917 		return (-1);
918 	}
919 	if ((buf = malloc(len)) == NULL)
920 		err(1, "malloc");
921 	if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
922 		warn("sysctl: KERN_FILE");
923 		return (-1);
924 	}
925 	*abuf = buf;
926 	*alen = len;
927 	return (0);
928 }
929 
930 void
931 usage()
932 {
933 
934 	(void)fprintf(stderr,
935 	    "usage: pstat [-T|-f|-s|-t|-v] [-kn] [-M core] [-N system]\n");
936 	exit(1);
937 }
938