xref: /dflybsd-src/usr.bin/fstat/fstat.c (revision 064e1fb31f522fb9804a689dd25f42073f1b79e5)
1 /*-
2  * Copyright (c) 1988, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1988, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)fstat.c	8.3 (Berkeley) 5/2/95
35  * $FreeBSD: src/usr.bin/fstat/fstat.c,v 1.21.2.7 2001/11/21 10:49:37 dwmalone Exp $
36  * $DragonFly: src/usr.bin/fstat/fstat.c,v 1.6 2003/10/04 20:36:44 hmp Exp $
37  */
38 
39 #define	_KERNEL_STRUCTURES
40 
41 #include <sys/param.h>
42 #include <sys/time.h>
43 #include <sys/proc.h>
44 #include <sys/user.h>
45 #include <sys/stat.h>
46 #include <sys/vnode.h>
47 #include <sys/socket.h>
48 #include <sys/socketvar.h>
49 #include <sys/domain.h>
50 #include <sys/protosw.h>
51 #include <sys/un.h>
52 #include <sys/unpcb.h>
53 #include <sys/sysctl.h>
54 #include <sys/filedesc.h>
55 #include <sys/queue.h>
56 #include <sys/pipe.h>
57 #include <sys/conf.h>
58 #include <sys/file.h>
59 #include <vfs/ufs/quota.h>
60 #include <vfs/ufs/inode.h>
61 #include <sys/mount.h>
62 #include <nfs/nfsproto.h>
63 #include <nfs/rpcv2.h>
64 #include <nfs/nfs.h>
65 #include <nfs/nfsnode.h>
66 
67 
68 #include <vm/vm.h>
69 #include <vm/vm_map.h>
70 #include <vm/vm_object.h>
71 
72 #include <net/route.h>
73 #include <netinet/in.h>
74 #include <netinet/in_systm.h>
75 #include <netinet/ip.h>
76 #include <netinet/in_pcb.h>
77 
78 #include <ctype.h>
79 #include <err.h>
80 #include <fcntl.h>
81 #include <kvm.h>
82 #include <limits.h>
83 #include <nlist.h>
84 #include <paths.h>
85 #include <pwd.h>
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <string.h>
89 #include <unistd.h>
90 #include <netdb.h>
91 
92 #include "fstat.h"
93 
94 #define	TEXT	-1
95 #define	CDIR	-2
96 #define	RDIR	-3
97 #define	TRACE	-4
98 #define	MMAP	-5
99 
100 DEVS *devs;
101 
102 #ifdef notdef
103 struct nlist nl[] = {
104 	{ "" },
105 };
106 #endif
107 
108 int 	fsflg,	/* show files on same filesystem as file(s) argument */
109 	pflg,	/* show files open by a particular pid */
110 	uflg;	/* show files open by a particular (effective) user */
111 int 	checkfile; /* true if restricting to particular files or filesystems */
112 int	nflg;	/* (numerical) display f.s. and rdev as dev_t */
113 int	vflg;	/* display errors in locating kernel data objects etc... */
114 int	mflg;	/* include memory-mapped files */
115 
116 
117 struct file **ofiles;	/* buffer of pointers to file structures */
118 int maxfiles;
119 #define ALLOC_OFILES(d)	\
120 	if ((d) > maxfiles) { \
121 		free(ofiles); \
122 		ofiles = malloc((d) * sizeof(struct file *)); \
123 		if (ofiles == NULL) { \
124 			err(1, NULL); \
125 		} \
126 		maxfiles = (d); \
127 	}
128 
129 kvm_t *kd;
130 
131 void dofiles(struct kinfo_proc *kp);
132 void dommap(struct kinfo_proc *kp);
133 void vtrans(struct vnode *vp, int i, int flag);
134 int  ufs_filestat(struct vnode *vp, struct filestat *fsp);
135 int  nfs_filestat(struct vnode *vp, struct filestat *fsp);
136 char *getmnton(struct mount *m);
137 void pipetrans(struct pipe *pi, int i, int flag);
138 void socktrans(struct socket *sock, int i);
139 void getinetproto(int number);
140 int  getfname(char *filename);
141 void usage(void);
142 
143 
144 int
145 main(int argc, char **argv)
146 {
147 	register struct passwd *passwd;
148 	struct kinfo_proc *p, *plast;
149 	int arg, ch, what;
150 	char *memf, *nlistf;
151 	char buf[_POSIX2_LINE_MAX];
152 	int cnt;
153 
154 	arg = 0;
155 	what = KERN_PROC_ALL;
156 	nlistf = memf = NULL;
157 	while ((ch = getopt(argc, argv, "fmnp:u:vN:M:")) != -1)
158 		switch((char)ch) {
159 		case 'f':
160 			fsflg = 1;
161 			break;
162 		case 'M':
163 			memf = optarg;
164 			break;
165 		case 'N':
166 			nlistf = optarg;
167 			break;
168 		case 'm':
169 			mflg = 1;
170 			break;
171 		case 'n':
172 			nflg = 1;
173 			break;
174 		case 'p':
175 			if (pflg++)
176 				usage();
177 			if (!isdigit(*optarg)) {
178 				warnx("-p requires a process id");
179 				usage();
180 			}
181 			what = KERN_PROC_PID;
182 			arg = atoi(optarg);
183 			break;
184 		case 'u':
185 			if (uflg++)
186 				usage();
187 			if (!(passwd = getpwnam(optarg)))
188 				errx(1, "%s: unknown uid", optarg);
189 			what = KERN_PROC_UID;
190 			arg = passwd->pw_uid;
191 			break;
192 		case 'v':
193 			vflg = 1;
194 			break;
195 		case '?':
196 		default:
197 			usage();
198 		}
199 
200 	if (*(argv += optind)) {
201 		for (; *argv; ++argv) {
202 			if (getfname(*argv))
203 				checkfile = 1;
204 		}
205 		if (!checkfile)	/* file(s) specified, but none accessable */
206 			exit(1);
207 	}
208 
209 	ALLOC_OFILES(256);	/* reserve space for file pointers */
210 
211 	if (fsflg && !checkfile) {
212 		/* -f with no files means use wd */
213 		if (getfname(".") == 0)
214 			exit(1);
215 		checkfile = 1;
216 	}
217 
218 	/*
219 	 * Discard setgid privileges if not the running kernel so that bad
220 	 * guys can't print interesting stuff from kernel memory.
221 	 */
222 	if (nlistf != NULL || memf != NULL)
223 		setgid(getgid());
224 
225 	if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf)) == NULL)
226 		errx(1, "%s", buf);
227 #ifdef notdef
228 	if (kvm_nlist(kd, nl) != 0)
229 		errx(1, "no namelist: %s", kvm_geterr(kd));
230 #endif
231 	if ((p = kvm_getprocs(kd, what, arg, &cnt)) == NULL)
232 		errx(1, "%s", kvm_geterr(kd));
233 	if (nflg)
234 		printf("%s",
235 "USER     CMD          PID   FD  DEV    INUM       MODE SZ|DV R/W");
236 	else
237 		printf("%s",
238 "USER     CMD          PID   FD MOUNT      INUM MODE         SZ|DV R/W");
239 	if (checkfile && fsflg == 0)
240 		printf(" NAME\n");
241 	else
242 		putchar('\n');
243 
244 	for (plast = &p[cnt]; p < plast; ++p) {
245 		if (p->kp_proc.p_stat == SZOMB)
246 			continue;
247 		dofiles(p);
248 		if (mflg)
249 			dommap(p);
250 	}
251 	exit(0);
252 }
253 
254 char	*Uname, *Comm;
255 int	Pid;
256 
257 #define PREFIX(i) printf("%-8.8s %-10s %5d", Uname, Comm, Pid); \
258 	switch(i) { \
259 	case TEXT: \
260 		printf(" text"); \
261 		break; \
262 	case CDIR: \
263 		printf("   wd"); \
264 		break; \
265 	case RDIR: \
266 		printf(" root"); \
267 		break; \
268 	case TRACE: \
269 		printf("   tr"); \
270 		break; \
271 	case MMAP: \
272 		printf(" mmap"); \
273 		break; \
274 	default: \
275 		printf(" %4d", i); \
276 		break; \
277 	}
278 
279 /*
280  * print open files attributed to this process
281  */
282 void
283 dofiles(struct kinfo_proc *kp)
284 {
285 	int i;
286 	struct file file;
287 	struct filedesc0 filed0;
288 #define	filed	filed0.fd_fd
289 	struct proc *p = &kp->kp_proc;
290 	struct eproc *ep = &kp->kp_eproc;
291 
292 	Uname = user_from_uid(ep->e_ucred.cr_uid, 0);
293 	Pid = p->p_pid;
294 	Comm = kp->kp_thread.td_comm;
295 
296 	if (p->p_fd == NULL)
297 		return;
298 	if (!KVM_READ(p->p_fd, &filed0, sizeof (filed0))) {
299 		dprintf(stderr, "can't read filedesc at %p for pid %d\n",
300 		    (void *)p->p_fd, Pid);
301 		return;
302 	}
303 	/*
304 	 * root directory vnode, if one
305 	 */
306 	if (filed.fd_rdir)
307 		vtrans(filed.fd_rdir, RDIR, FREAD);
308 	/*
309 	 * current working directory vnode
310 	 */
311 	vtrans(filed.fd_cdir, CDIR, FREAD);
312 	/*
313 	 * ktrace vnode, if one
314 	 */
315 	if (p->p_tracep)
316 		vtrans(p->p_tracep, TRACE, FREAD|FWRITE);
317 	/*
318 	 * text vnode, if one
319 	 */
320 	if (p->p_textvp)
321 		vtrans(p->p_textvp, TEXT, FREAD);
322 	/*
323 	 * open files
324 	 */
325 #define FPSIZE	(sizeof (struct file *))
326 	ALLOC_OFILES(filed.fd_lastfile+1);
327 	if (filed.fd_nfiles > NDFILE) {
328 		if (!KVM_READ(filed.fd_ofiles, ofiles,
329 		    (filed.fd_lastfile+1) * FPSIZE)) {
330 			dprintf(stderr,
331 			    "can't read file structures at %p for pid %d\n",
332 			    (void *)filed.fd_ofiles, Pid);
333 			return;
334 		}
335 	} else
336 		bcopy(filed0.fd_dfiles, ofiles, (filed.fd_lastfile+1) * FPSIZE);
337 	for (i = 0; i <= filed.fd_lastfile; i++) {
338 		if (ofiles[i] == NULL)
339 			continue;
340 		if (!KVM_READ(ofiles[i], &file, sizeof (struct file))) {
341 			dprintf(stderr, "can't read file %d at %p for pid %d\n",
342 			    i, (void *)ofiles[i], Pid);
343 			continue;
344 		}
345 		if (file.f_type == DTYPE_VNODE)
346 			vtrans((struct vnode *)file.f_data, i, file.f_flag);
347 		else if (file.f_type == DTYPE_SOCKET) {
348 			if (checkfile == 0)
349 				socktrans((struct socket *)file.f_data, i);
350 		}
351 #ifdef DTYPE_PIPE
352 		else if (file.f_type == DTYPE_PIPE) {
353 			if (checkfile == 0)
354 				pipetrans((struct pipe *)file.f_data, i,
355 				    file.f_flag);
356 		}
357 #endif
358 #ifdef DTYPE_FIFO
359 		else if (file.f_type == DTYPE_FIFO) {
360 			if (checkfile == 0)
361 				vtrans((struct vnode *)file.f_data, i,
362 				    file.f_flag);
363 		}
364 #endif
365 		else {
366 			dprintf(stderr,
367 			    "unknown file type %d for file %d of pid %d\n",
368 			    file.f_type, i, Pid);
369 		}
370 	}
371 }
372 
373 void
374 dommap(struct kinfo_proc *kp)
375 {
376 	struct proc *p = &kp->kp_proc;
377 	struct vmspace vmspace;
378 	vm_map_t map;
379 	struct vm_map_entry entry;
380 	vm_map_entry_t entryp;
381 	struct vm_object object;
382 	vm_object_t objp;
383 	int prot, fflags;
384 
385 	if (!KVM_READ(p->p_vmspace, &vmspace, sizeof(vmspace))) {
386 		dprintf(stderr, "can't read vmspace at %p for pid %d\n",
387 		    (void *)p->p_vmspace, Pid);
388 		return;
389 	}
390 
391 	map = &vmspace.vm_map;
392 
393 	for (entryp = map->header.next; entryp != &p->p_vmspace->vm_map.header;
394 	    entryp = entry.next) {
395 		if (!KVM_READ(entryp, &entry, sizeof(entry))) {
396 			dprintf(stderr,
397 			    "can't read vm_map_entry at %p for pid %d\n",
398 			    (void *)entryp, Pid);
399 			return;
400 		}
401 
402 		if (entry.eflags & MAP_ENTRY_IS_SUB_MAP)
403 			continue;
404 
405 		if ((objp = entry.object.vm_object) == NULL)
406 			continue;
407 
408 		for (; objp; objp = object.backing_object) {
409 			if (!KVM_READ(objp, &object, sizeof(object))) {
410 				dprintf(stderr,
411 				    "can't read vm_object at %p for pid %d\n",
412 				    (void *)objp, Pid);
413 				return;
414 			}
415 		}
416 
417 		prot = entry.protection;
418 		fflags = (prot & VM_PROT_READ ? FREAD : 0) |
419 		    (prot & VM_PROT_WRITE ? FWRITE : 0);
420 
421 		switch (object.type) {
422 		case OBJT_VNODE:
423 			vtrans((struct vnode *)object.handle, MMAP, fflags);
424 			break;
425 		default:
426 			break;
427 		}
428 	}
429 }
430 
431 void
432 vtrans(struct vnode *vp, int i, int flag)
433 {
434 	struct vnode vn;
435 	struct filestat fst;
436 	char rw[3], mode[15];
437 	char *badtype = NULL, *filename, *getmnton();
438 
439 	filename = badtype = NULL;
440 	if (!KVM_READ(vp, &vn, sizeof (struct vnode))) {
441 		dprintf(stderr, "can't read vnode at %p for pid %d\n",
442 		    (void *)vp, Pid);
443 		return;
444 	}
445 	if (vn.v_type == VNON || vn.v_tag == VT_NON)
446 		badtype = "none";
447 	else if (vn.v_type == VBAD)
448 		badtype = "bad";
449 	else
450 		switch (vn.v_tag) {
451 		case VT_UFS:
452 			if (!ufs_filestat(&vn, &fst))
453 				badtype = "error";
454 			break;
455 		case VT_MFS:
456 			if (!ufs_filestat(&vn, &fst))
457 				badtype = "error";
458 			break;
459 		case VT_NFS:
460 			if (!nfs_filestat(&vn, &fst))
461 				badtype = "error";
462 			break;
463 
464 		case VT_MSDOSFS:
465 			if (!msdosfs_filestat(&vn, &fst))
466 				badtype = "error";
467 			break;
468 
469 		case VT_ISOFS:
470 			if (!isofs_filestat(&vn, &fst))
471 				badtype = "error";
472 			break;
473 
474 		default: {
475 			static char unknown[10];
476 			sprintf(badtype = unknown, "?(%x)", vn.v_tag);
477 			break;;
478 		}
479 	}
480 	if (checkfile) {
481 		int fsmatch = 0;
482 		register DEVS *d;
483 
484 		if (badtype)
485 			return;
486 		for (d = devs; d != NULL; d = d->next)
487 			if (d->fsid == fst.fsid) {
488 				fsmatch = 1;
489 				if (d->ino == fst.fileid) {
490 					filename = d->name;
491 					break;
492 				}
493 			}
494 		if (fsmatch == 0 || (filename == NULL && fsflg == 0))
495 			return;
496 	}
497 	PREFIX(i);
498 	if (badtype) {
499 		(void)printf(" -         -  %10s    -\n", badtype);
500 		return;
501 	}
502 	if (nflg)
503 		(void)printf(" %2d,%-2d", major(fst.fsid), minor(fst.fsid));
504 	else
505 		(void)printf(" %-8s", getmnton(vn.v_mount));
506 	if (nflg)
507 		(void)sprintf(mode, "%o", fst.mode);
508 	else
509 		strmode(fst.mode, mode);
510 	(void)printf(" %6ld %10s", fst.fileid, mode);
511 	switch (vn.v_type) {
512 	case VBLK:
513 	case VCHR: {
514 		char *name;
515 
516 		if (nflg || ((name = devname(fst.rdev, vn.v_type == VCHR ?
517 		    S_IFCHR : S_IFBLK)) == NULL))
518 			printf("  %2d,%-2d", major(fst.rdev), minor(fst.rdev));
519 		else
520 			printf(" %6s", name);
521 		break;
522 	}
523 	default:
524 		printf(" %6lu", fst.size);
525 	}
526 	rw[0] = '\0';
527 	if (flag & FREAD)
528 		strcat(rw, "r");
529 	if (flag & FWRITE)
530 		strcat(rw, "w");
531 	printf(" %2s", rw);
532 	if (filename && !fsflg)
533 		printf("  %s", filename);
534 	putchar('\n');
535 }
536 
537 int
538 ufs_filestat(struct vnode *vp, struct filestat *fsp)
539 {
540 	struct inode inode;
541 
542 	if (!KVM_READ(VTOI(vp), &inode, sizeof (inode))) {
543 		dprintf(stderr, "can't read inode at %p for pid %d\n",
544 		    (void *)VTOI(vp), Pid);
545 		return 0;
546 	}
547 	/*
548 	 * The st_dev from stat(2) is a udev_t. These kernel structures
549 	 * contain dev_t structures. We need to convert to udev to make
550 	 * comparisons
551 	 */
552 	fsp->fsid = dev2udev(inode.i_dev);
553 	fsp->fileid = (long)inode.i_number;
554 	fsp->mode = (mode_t)inode.i_mode;
555 	fsp->size = (u_long)inode.i_size;
556 	fsp->rdev = inode.i_rdev;
557 
558 	return 1;
559 }
560 
561 int
562 nfs_filestat(struct vnode *vp, struct filestat *fsp)
563 {
564 	struct nfsnode nfsnode;
565 	register mode_t mode;
566 
567 	if (!KVM_READ(VTONFS(vp), &nfsnode, sizeof (nfsnode))) {
568 		dprintf(stderr, "can't read nfsnode at %p for pid %d\n",
569 		    (void *)VTONFS(vp), Pid);
570 		return 0;
571 	}
572 	fsp->fsid = nfsnode.n_vattr.va_fsid;
573 	fsp->fileid = nfsnode.n_vattr.va_fileid;
574 	fsp->size = nfsnode.n_size;
575 	fsp->rdev = nfsnode.n_vattr.va_rdev;
576 	mode = (mode_t)nfsnode.n_vattr.va_mode;
577 	switch (vp->v_type) {
578 	case VREG:
579 		mode |= S_IFREG;
580 		break;
581 	case VDIR:
582 		mode |= S_IFDIR;
583 		break;
584 	case VBLK:
585 		mode |= S_IFBLK;
586 		break;
587 	case VCHR:
588 		mode |= S_IFCHR;
589 		break;
590 	case VLNK:
591 		mode |= S_IFLNK;
592 		break;
593 	case VSOCK:
594 		mode |= S_IFSOCK;
595 		break;
596 	case VFIFO:
597 		mode |= S_IFIFO;
598 		break;
599 	case VNON:
600 	case VBAD:
601 		return 0;
602 	};
603 	fsp->mode = mode;
604 
605 	return 1;
606 }
607 
608 
609 char *
610 getmnton(struct mount *m)
611 {
612 	static struct mount mount;
613 	static struct mtab {
614 		struct mtab *next;
615 		struct mount *m;
616 		char mntonname[MNAMELEN];
617 	} *mhead = NULL;
618 	register struct mtab *mt;
619 
620 	for (mt = mhead; mt != NULL; mt = mt->next)
621 		if (m == mt->m)
622 			return (mt->mntonname);
623 	if (!KVM_READ(m, &mount, sizeof(struct mount))) {
624 		warnx("can't read mount table at %p", (void *)m);
625 		return (NULL);
626 	}
627 	if ((mt = malloc(sizeof (struct mtab))) == NULL)
628 		err(1, NULL);
629 	mt->m = m;
630 	bcopy(&mount.mnt_stat.f_mntonname[0], &mt->mntonname[0], MNAMELEN);
631 	mt->next = mhead;
632 	mhead = mt;
633 	return (mt->mntonname);
634 }
635 
636 void
637 pipetrans(struct pipe *pi, int i, int flag)
638 {
639 	struct pipe pip;
640 	char rw[3];
641 
642 	PREFIX(i);
643 
644 	/* fill in socket */
645 	if (!KVM_READ(pi, &pip, sizeof(struct pipe))) {
646 		dprintf(stderr, "can't read pipe at %p\n", (void *)pi);
647 		goto bad;
648 	}
649 
650 	printf("* pipe %8lx <-> %8lx", (u_long)pi, (u_long)pip.pipe_peer);
651 	printf(" %6d", (int)pip.pipe_buffer.cnt);
652 	rw[0] = '\0';
653 	if (flag & FREAD)
654 		strcat(rw, "r");
655 	if (flag & FWRITE)
656 		strcat(rw, "w");
657 	printf(" %2s", rw);
658 	putchar('\n');
659 	return;
660 
661 bad:
662 	printf("* error\n");
663 }
664 
665 void
666 socktrans(struct socket *sock, int i)
667 {
668 	static char *stypename[] = {
669 		"unused",	/* 0 */
670 		"stream", 	/* 1 */
671 		"dgram",	/* 2 */
672 		"raw",		/* 3 */
673 		"rdm",		/* 4 */
674 		"seqpak"	/* 5 */
675 	};
676 #define	STYPEMAX 5
677 	struct socket	so;
678 	struct protosw	proto;
679 	struct domain	dom;
680 	struct inpcb	inpcb;
681 	struct unpcb	unpcb;
682 	int len;
683 	char dname[32], *strcpy();
684 
685 	PREFIX(i);
686 
687 	/* fill in socket */
688 	if (!KVM_READ(sock, &so, sizeof(struct socket))) {
689 		dprintf(stderr, "can't read sock at %p\n", (void *)sock);
690 		goto bad;
691 	}
692 
693 	/* fill in protosw entry */
694 	if (!KVM_READ(so.so_proto, &proto, sizeof(struct protosw))) {
695 		dprintf(stderr, "can't read protosw at %p",
696 		    (void *)so.so_proto);
697 		goto bad;
698 	}
699 
700 	/* fill in domain */
701 	if (!KVM_READ(proto.pr_domain, &dom, sizeof(struct domain))) {
702 		dprintf(stderr, "can't read domain at %p\n",
703 		    (void *)proto.pr_domain);
704 		goto bad;
705 	}
706 
707 	if ((len = kvm_read(kd, (u_long)dom.dom_name, dname,
708 	    sizeof(dname) - 1)) < 0) {
709 		dprintf(stderr, "can't read domain name at %p\n",
710 		    (void *)dom.dom_name);
711 		dname[0] = '\0';
712 	}
713 	else
714 		dname[len] = '\0';
715 
716 	if ((u_short)so.so_type > STYPEMAX)
717 		printf("* %s ?%d", dname, so.so_type);
718 	else
719 		printf("* %s %s", dname, stypename[so.so_type]);
720 
721 	/*
722 	 * protocol specific formatting
723 	 *
724 	 * Try to find interesting things to print.  For tcp, the interesting
725 	 * thing is the address of the tcpcb, for udp and others, just the
726 	 * inpcb (socket pcb).  For unix domain, its the address of the socket
727 	 * pcb and the address of the connected pcb (if connected).  Otherwise
728 	 * just print the protocol number and address of the socket itself.
729 	 * The idea is not to duplicate netstat, but to make available enough
730 	 * information for further analysis.
731 	 */
732 	switch(dom.dom_family) {
733 	case AF_INET:
734 	case AF_INET6:
735 		getinetproto(proto.pr_protocol);
736 		if (proto.pr_protocol == IPPROTO_TCP ) {
737 			if (so.so_pcb) {
738 				if (kvm_read(kd, (u_long)so.so_pcb,
739 				    (char *)&inpcb, sizeof(struct inpcb))
740 				    != sizeof(struct inpcb)) {
741 					dprintf(stderr,
742 					    "can't read inpcb at %p\n",
743 					    (void *)so.so_pcb);
744 					goto bad;
745 				}
746 				printf(" %lx", (u_long)inpcb.inp_ppcb);
747 			}
748 		}
749 		else if (so.so_pcb)
750 			printf(" %lx", (u_long)so.so_pcb);
751 		break;
752 	case AF_UNIX:
753 		/* print address of pcb and connected pcb */
754 		if (so.so_pcb) {
755 			printf(" %lx", (u_long)so.so_pcb);
756 			if (kvm_read(kd, (u_long)so.so_pcb, (char *)&unpcb,
757 			    sizeof(struct unpcb)) != sizeof(struct unpcb)){
758 				dprintf(stderr, "can't read unpcb at %p\n",
759 				    (void *)so.so_pcb);
760 				goto bad;
761 			}
762 			if (unpcb.unp_conn) {
763 				char shoconn[4], *cp;
764 
765 				cp = shoconn;
766 				if (!(so.so_state & SS_CANTRCVMORE))
767 					*cp++ = '<';
768 				*cp++ = '-';
769 				if (!(so.so_state & SS_CANTSENDMORE))
770 					*cp++ = '>';
771 				*cp = '\0';
772 				printf(" %s %lx", shoconn,
773 				    (u_long)unpcb.unp_conn);
774 			}
775 		}
776 		break;
777 	default:
778 		/* print protocol number and socket address */
779 		printf(" %d %lx", proto.pr_protocol, (u_long)sock);
780 	}
781 	printf("\n");
782 	return;
783 bad:
784 	printf("* error\n");
785 }
786 
787 
788 /*
789  * Read the specinfo structure in the kernel (as pointed to by a dev_t)
790  * in order to work out the associated udev_t
791  */
792 udev_t
793 dev2udev(dev_t dev)
794 {
795 	struct specinfo si;
796 
797 	if (KVM_READ(dev, &si, sizeof si)) {
798 		return si.si_udev;
799 	} else {
800 		dprintf(stderr, "can't convert dev_t %x to a udev_t\n", dev);
801 		return -1;
802 	}
803 }
804 
805 /*
806  * getinetproto --
807  *	print name of protocol number
808  */
809 void
810 getinetproto(int number)
811 {
812 	static int isopen;
813 	register struct protoent *pe;
814 
815 	if (!isopen)
816 		setprotoent(++isopen);
817 	if ((pe = getprotobynumber(number)) != NULL)
818 		printf(" %s", pe->p_name);
819 	else
820 		printf(" %d", number);
821 }
822 
823 int
824 getfname(char *filename)
825 {
826 	struct stat statbuf;
827 	DEVS *cur;
828 
829 	if (stat(filename, &statbuf)) {
830 		warn("%s", filename);
831 		return(0);
832 	}
833 	if ((cur = malloc(sizeof(DEVS))) == NULL)
834 		err(1, NULL);
835 	cur->next = devs;
836 	devs = cur;
837 
838 	cur->ino = statbuf.st_ino;
839 	cur->fsid = statbuf.st_dev;
840 	cur->name = filename;
841 	return(1);
842 }
843 
844 void
845 usage(void)
846 {
847 	(void)fprintf(stderr,
848  "usage: fstat [-fmnv] [-p pid] [-u user] [-N system] [-M core] [file ...]\n");
849 	exit(1);
850 }
851