xref: /openbsd/usr.bin/fstat/fstat.c (revision 264ca280)
1 /*	$OpenBSD: fstat.c,v 1.88 2016/05/04 19:48:08 jca Exp $	*/
2 
3 /*
4  * Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*-
20  * Copyright (c) 1988, 1993
21  *	The Regents of the University of California.  All rights reserved.
22  *
23  * Redistribution and use in source and binary forms, with or without
24  * modification, are permitted provided that the following conditions
25  * are met:
26  * 1. Redistributions of source code must retain the above copyright
27  *    notice, this list of conditions and the following disclaimer.
28  * 2. Redistributions in binary form must reproduce the above copyright
29  *    notice, this list of conditions and the following disclaimer in the
30  *    documentation and/or other materials provided with the distribution.
31  * 3. Neither the name of the University nor the names of its contributors
32  *    may be used to endorse or promote products derived from this software
33  *    without specific prior written permission.
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
36  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
39  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45  * SUCH DAMAGE.
46  */
47 
48 #include <sys/types.h>
49 #include <sys/queue.h>
50 #include <sys/mount.h>
51 #include <sys/stat.h>
52 #include <sys/vnode.h>
53 #include <sys/socket.h>
54 #include <sys/socketvar.h>
55 #include <sys/eventvar.h>
56 #include <sys/sysctl.h>
57 #define _KERNEL /* for DTYPE_* */
58 #include <sys/file.h>
59 #undef _KERNEL
60 
61 #include <net/route.h>
62 #include <netinet/in.h>
63 
64 #include <netdb.h>
65 #include <arpa/inet.h>
66 
67 #include <sys/pipe.h>
68 
69 #include <ctype.h>
70 #include <errno.h>
71 #include <fcntl.h>
72 #include <kvm.h>
73 #include <limits.h>
74 #include <nlist.h>
75 #include <pwd.h>
76 #include <search.h>
77 #include <signal.h>
78 #include <stdio.h>
79 #include <stdint.h>
80 #include <stdlib.h>
81 #include <string.h>
82 #include <unistd.h>
83 #include <err.h>
84 
85 #include "fstat.h"
86 
87 #define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
88 
89 struct fileargs fileargs = SLIST_HEAD_INITIALIZER(fileargs);
90 
91 int	fsflg;	/* show files on same filesystem as file(s) argument */
92 int	pflg;	/* show files open by a particular pid */
93 int	uflg;	/* show files open by a particular (effective) user */
94 int	checkfile; /* true if restricting to particular files or filesystems */
95 int	nflg;	/* (numerical) display f.s. and rdev as dev_t */
96 int	oflg;	/* display file offset */
97 int	sflg;	/* display file xfer/bytes counters */
98 int	vflg;	/* display errors in locating kernel data objects etc... */
99 int	cflg; 	/* fuser only */
100 
101 int	fuser;	/* 1 if we are fuser, 0 if we are fstat */
102 int	signo;	/* signal to send (fuser only) */
103 
104 kvm_t *kd;
105 uid_t uid;
106 
107 void fstat_dofile(struct kinfo_file *);
108 void fstat_header(void);
109 void getinetproto(int);
110 __dead void usage(void);
111 int getfname(char *);
112 void kqueuetrans(struct kinfo_file *);
113 void pipetrans(struct kinfo_file *);
114 struct kinfo_file *splice_find(char, u_int64_t);
115 void splice_insert(char, u_int64_t, struct kinfo_file *);
116 void find_splices(struct kinfo_file *, int);
117 void print_inet_details(struct kinfo_file *);
118 void print_inet6_details(struct kinfo_file *);
119 void print_sock_details(struct kinfo_file *);
120 void socktrans(struct kinfo_file *);
121 void vtrans(struct kinfo_file *);
122 const char *inet6_addrstr(struct in6_addr *);
123 int signame_to_signum(char *);
124 void hide(void *p);
125 
126 int hideroot;
127 
128 void
129 hide(void *p)
130 {
131 	printf("%p", hideroot ? NULL : p);
132 }
133 
134 int
135 main(int argc, char *argv[])
136 {
137 	struct passwd *passwd;
138 	struct kinfo_file *kf, *kflast;
139 	int arg, ch, what;
140 	char *memf, *nlistf, *optstr;
141 	char buf[_POSIX2_LINE_MAX];
142 	const char *errstr;
143 	int cnt, flags;
144 
145 	hideroot = getuid();
146 
147 	arg = -1;
148 	what = KERN_FILE_BYPID;
149 	nlistf = memf = NULL;
150 	oflg = 0;
151 
152 	/* are we fstat(1) or fuser(1)? */
153 	if (strcmp(__progname, "fuser") == 0) {
154 		fuser = 1;
155 		optstr = "cfks:uM:N:";
156 	} else {
157 		fuser = 0;
158 		optstr = "fnop:su:vN:M:";
159 	}
160 
161 	/*
162 	 * fuser and fstat share three flags: -f, -s and -u.  In both cases
163 	 * -f is a boolean, but for -u fstat wants an argument while fuser
164 	 * does not and for -s fuser wants an argument whereas fstat does not.
165 	 */
166 	while ((ch = getopt(argc, argv, optstr)) != -1)
167 		switch ((char)ch) {
168 		case 'c':
169 			if (fsflg)
170 				usage();
171 			cflg = 1;
172 			break;
173 		case 'f':
174 			if (cflg)
175 				usage();
176 			fsflg = 1;
177 			break;
178 		case 'k':
179 			sflg = 1;
180 			signo = SIGKILL;
181 			break;
182 		case 'M':
183 			memf = optarg;
184 			break;
185 		case 'N':
186 			nlistf = optarg;
187 			break;
188 		case 'n':
189 			nflg = 1;
190 			break;
191 		case 'o':
192 			oflg = 1;
193 			break;
194 		case 'p':
195 			if (pflg++)
196 				usage();
197 			arg = strtonum(optarg, 0, INT_MAX, &errstr);
198 			if (errstr != NULL) {
199 				warnx("-p requires a process id, %s: %s",
200 					errstr, optarg);
201 				usage();
202 			}
203 			what = KERN_FILE_BYPID;
204 			break;
205 		case 's':
206 			sflg = 1;
207 			if (fuser) {
208 				signo = signame_to_signum(optarg);
209 				if (signo == -1) {
210 					warnx("invalid signal %s", optarg);
211 					usage();
212 				}
213 			}
214 			break;
215 		case 'u':
216 			if (uflg++)
217 				usage();
218 			if (!fuser) {
219 				if (!(passwd = getpwnam(optarg))) {
220 					arg = strtonum(optarg, 0, UID_MAX,
221 					    &errstr);
222 					if (errstr != NULL) {
223 						errx(1, "%s: unknown uid",
224 						    optarg);
225 					}
226 				} else
227 					arg = passwd->pw_uid;
228 				what = KERN_FILE_BYUID;
229 			}
230 			break;
231 		case 'v':
232 			vflg = 1;
233 			break;
234 		default:
235 			usage();
236 		}
237 
238 	/*
239 	 * get the uid, for oflg and sflg
240 	 */
241 	uid = getuid();
242 
243 	/*
244 	 * Use sysctl unless inspecting an alternate kernel.
245 	 */
246 	if (nlistf == NULL || memf == NULL)
247 		flags = KVM_NO_FILES;
248 	else
249 		flags = O_RDONLY;
250 
251 	if ((kd = kvm_openfiles(nlistf, memf, NULL, flags, buf)) == NULL)
252 		errx(1, "%s", buf);
253 
254 	if (*(argv += optind)) {
255 		for (; *argv; ++argv) {
256 			if (getfname(*argv))
257 				checkfile = 1;
258 		}
259 		/* file(s) specified, but none accessible */
260 		if (!checkfile)
261 			exit(1);
262 	} else if (fuser)
263 		usage();
264 
265 	if (!fuser && fsflg && !checkfile) {
266 		/* fstat -f with no files means use wd */
267 		if (getfname(".") == 0)
268 			exit(1);
269 		checkfile = 1;
270 	}
271 
272 	if ((kf = kvm_getfiles(kd, what, arg, sizeof(*kf), &cnt)) == NULL)
273 		errx(1, "%s", kvm_geterr(kd));
274 
275 	if (fuser) {
276 		/*
277 		 * fuser
278 		 *  uflg: need "getpw"
279 		 *  sflg: need "proc" (might call kill(2))
280 		 */
281 		if (uflg && sflg) {
282 			if (pledge("stdio rpath getpw proc", NULL) == -1)
283 				err(1, "pledge");
284 		} else if (uflg) {
285 			if (pledge("stdio rpath getpw", NULL) == -1)
286 				err(1, "pledge");
287 		} else if (sflg) {
288 			if (pledge("stdio rpath proc", NULL) == -1)
289 				err(1, "pledge");
290 		} else {
291 			if (pledge("stdio rpath", NULL) == -1)
292 				err(1, "pledge");
293 		}
294 	} else {
295 		/* fstat */
296 		if (pledge("stdio rpath getpw", NULL) == -1)
297 			err(1, "pledge");
298 	}
299 
300 	find_splices(kf, cnt);
301 	if (!fuser)
302 		fstat_header();
303 	for (kflast = &kf[cnt]; kf < kflast; ++kf) {
304 		if (fuser)
305 			fuser_check(kf);
306 		else
307 			fstat_dofile(kf);
308 	}
309 	if (fuser)
310 		fuser_run();
311 
312 	exit(0);
313 }
314 
315 void
316 fstat_header(void)
317 {
318 	if (nflg)
319 		printf("%s",
320 "USER     CMD          PID   FD  DEV      INUM       MODE R/W    SZ|DV");
321 	else
322 		printf("%s",
323 "USER     CMD          PID   FD MOUNT        INUM MODE       R/W    SZ|DV");
324 	if (oflg)
325 		printf("%s", ":OFFSET  ");
326 	if (checkfile && fsflg == 0)
327 		printf(" NAME");
328 	if (sflg)
329 		printf("    XFERS   KBYTES");
330 	putchar('\n');
331 }
332 
333 char	*Uname, *Comm;
334 uid_t	*procuid;
335 pid_t	Pid;
336 
337 #define PREFIX(i) do { \
338 	printf("%-8.8s %-10s %5ld", Uname, Comm, (long)Pid); \
339 	switch (i) { \
340 	case KERN_FILE_TEXT: \
341 		printf(" text"); \
342 		break; \
343 	case KERN_FILE_CDIR: \
344 		printf("   wd"); \
345 		break; \
346 	case KERN_FILE_RDIR: \
347 		printf(" root"); \
348 		break; \
349 	case KERN_FILE_TRACE: \
350 		printf("   tr"); \
351 		break; \
352 	default: \
353 		printf(" %4d", i); \
354 		break; \
355 	} \
356 } while (0)
357 
358 /*
359  * print open files attributed to this process
360  */
361 void
362 fstat_dofile(struct kinfo_file *kf)
363 {
364 
365 	Uname = user_from_uid(kf->p_uid, 0);
366 	procuid = &kf->p_uid;
367 	Pid = kf->p_pid;
368 	Comm = kf->p_comm;
369 
370 	switch (kf->f_type) {
371 	case DTYPE_VNODE:
372 		vtrans(kf);
373 		break;
374 	case DTYPE_SOCKET:
375 		if (checkfile == 0)
376 			socktrans(kf);
377 		break;
378 	case DTYPE_PIPE:
379 		if (checkfile == 0)
380 			pipetrans(kf);
381 		break;
382 	case DTYPE_KQUEUE:
383 		if (checkfile == 0)
384 			kqueuetrans(kf);
385 		break;
386 	default:
387 		if (vflg) {
388 			warnx("unknown file type %d for file %d of pid %ld",
389 			    kf->f_type, kf->fd_fd, (long)Pid);
390 		}
391 		break;
392 	}
393 }
394 
395 void
396 vtrans(struct kinfo_file *kf)
397 {
398 	const char *badtype = NULL;
399 	char rw[3], mode[12];
400 	char *filename = NULL;
401 
402 	if (kf->v_type == VNON)
403 		badtype = "none";
404 	else if (kf->v_type == VBAD)
405 		badtype = "bad";
406 	else if (kf->v_tag == VT_NON && !(kf->v_flag & VCLONE))
407 		badtype = "none";	/* not a clone */
408 
409 	if (checkfile) {
410 		int fsmatch = 0;
411 		struct filearg *fa;
412 
413 		if (badtype)
414 			return;
415 		SLIST_FOREACH(fa, &fileargs, next) {
416 			if (fa->dev == kf->va_fsid) {
417 				fsmatch = 1;
418 				if (fa->ino == kf->va_fileid) {
419 					filename = fa->name;
420 					break;
421 				}
422 			}
423 		}
424 		if (fsmatch == 0 || (filename == NULL && fsflg == 0))
425 			return;
426 	}
427 	PREFIX(kf->fd_fd);
428 	if (badtype) {
429 		(void)printf(" -           -  %10s    -\n", badtype);
430 		return;
431 	}
432 
433 	if (nflg)
434 		(void)printf(" %2ld,%-2ld", (long)major(kf->va_fsid),
435 		    (long)minor(kf->va_fsid));
436 	else if (!(kf->v_flag & VCLONE))
437 		(void)printf(" %-8s", kf->f_mntonname);
438 	else
439 		(void)printf(" clone   ");
440 	if (nflg)
441 		(void)snprintf(mode, sizeof(mode), "%o", kf->va_mode);
442 	else
443 		strmode(kf->va_mode, mode);
444 	printf(" %8llu %11s", kf->va_fileid, mode);
445 	rw[0] = '\0';
446 	if (kf->f_flag & FREAD)
447 		strlcat(rw, "r", sizeof rw);
448 	if (kf->f_flag & FWRITE)
449 		strlcat(rw, "w", sizeof rw);
450 	printf(" %2s", rw);
451 	switch (kf->v_type) {
452 	case VBLK:
453 	case VCHR: {
454 		char *name;
455 
456 		if (nflg || ((name = devname(kf->va_rdev,
457 		    kf->v_type == VCHR ?  S_IFCHR : S_IFBLK)) == NULL))
458 			printf("   %2d,%-3d", major(kf->va_rdev), minor(kf->va_rdev));
459 		else
460 			printf("  %7s", name);
461 		if (oflg)
462 			printf("         ");
463 		break;
464 	}
465 	default:
466 		printf(" %8llu", kf->va_size);
467 		if (oflg) {
468 			if (uid == 0 || uid == *procuid)
469 				printf(":%-8llu", kf->f_offset);
470 			else
471 				printf(":%-8s", "*");
472 		}
473 	}
474 	if (sflg) {
475 		if (uid == 0 || uid == *procuid) {
476 			printf(" %8llu %8llu",
477 			    (kf->f_rxfer + kf->f_rwfer),
478 			    (kf->f_rbytes + kf->f_wbytes) / 1024);
479 		} else {
480 			printf(" %8s %8s", "*", "*");
481 		}
482 	}
483 	if (filename && !fsflg)
484 		printf(" %s", filename);
485 	putchar('\n');
486 }
487 
488 void
489 pipetrans(struct kinfo_file *kf)
490 {
491 	void *maxaddr;
492 
493 	PREFIX(kf->fd_fd);
494 
495 	printf(" ");
496 
497 	/*
498 	 * We don't have enough space to fit both peer and own address, so
499 	 * we select the higher address so both ends of the pipe have the
500 	 * same visible addr. (it's the higher address because when the other
501 	 * end closes, it becomes 0)
502 	 */
503 	maxaddr = (void *)(uintptr_t)MAXIMUM(kf->f_data, kf->pipe_peer);
504 
505 	printf("pipe ");
506 	hide(maxaddr);
507 	printf(" state: %s%s%s",
508 	    (kf->pipe_state & PIPE_WANTR) ? "R" : "",
509 	    (kf->pipe_state & PIPE_WANTW) ? "W" : "",
510 	    (kf->pipe_state & PIPE_EOF) ? "E" : "");
511 	if (sflg)
512 		printf("\t%8llu %8llu",
513 		    (kf->f_rxfer + kf->f_rwfer),
514 		    (kf->f_rbytes + kf->f_wbytes) / 1024);
515 	printf("\n");
516 	return;
517 }
518 
519 void
520 kqueuetrans(struct kinfo_file *kf)
521 {
522 	PREFIX(kf->fd_fd);
523 
524 	printf(" ");
525 
526 	printf("kqueue ");
527 	hide((void *)(uintptr_t)kf->f_data);
528 	printf(" %d state: %s%s\n",
529 	    kf->kq_count,
530 	    (kf->kq_state & KQ_SEL) ? "S" : "",
531 	    (kf->kq_state & KQ_SLEEP) ? "W" : "");
532 	return;
533 }
534 
535 const char *
536 inet6_addrstr(struct in6_addr *p)
537 {
538 	struct sockaddr_in6 sin6;
539 	static char hbuf[NI_MAXHOST];
540 	const int niflags = NI_NUMERICHOST;
541 
542 	memset(&sin6, 0, sizeof(sin6));
543 	sin6.sin6_family = AF_INET6;
544 	sin6.sin6_len = sizeof(struct sockaddr_in6);
545 	sin6.sin6_addr = *p;
546 	if (IN6_IS_ADDR_LINKLOCAL(p) &&
547 	    *(u_int16_t *)&sin6.sin6_addr.s6_addr[2] != 0) {
548 		sin6.sin6_scope_id =
549 		    ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
550 		sin6.sin6_addr.s6_addr[2] = sin6.sin6_addr.s6_addr[3] = 0;
551 	}
552 
553 	if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
554 	    hbuf, sizeof(hbuf), NULL, 0, niflags))
555 		return "invalid";
556 
557 	return hbuf;
558 }
559 
560 void
561 splice_insert(char type, u_int64_t ptr, struct kinfo_file *data)
562 {
563 	ENTRY entry, *found;
564 
565 	if (asprintf(&entry.key, "%c%llx", type, hideroot ? 0 : ptr) == -1)
566 		err(1, NULL);
567 	entry.data = data;
568 	if ((found = hsearch(entry, ENTER)) == NULL)
569 		err(1, "hsearch");
570 	/* if it's ambiguous, set the data to NULL */
571 	if (found->data != data)
572 		found->data = NULL;
573 }
574 
575 struct kinfo_file *
576 splice_find(char type, u_int64_t ptr)
577 {
578 	ENTRY entry, *found;
579 	char buf[20];
580 
581 	snprintf(buf, sizeof(buf), "%c%llx", type, hideroot ? 0 : ptr);
582 	entry.key = buf;
583 	found = hsearch(entry, FIND);
584 	return (found != NULL ? found->data : NULL);
585 }
586 
587 void
588 find_splices(struct kinfo_file *kf, int cnt)
589 {
590 	int i, created;
591 
592 	created = 0;
593 	for (i = 0; i < cnt; i++) {
594 		if (kf[i].f_type != DTYPE_SOCKET ||
595 		    (kf[i].so_splice == 0 && kf[i].so_splicelen != -1))
596 			continue;
597 		if (created++ == 0) {
598 			if (hcreate(1000) == 0)
599 				err(1, "hcreate");
600 		}
601 		splice_insert('>', kf[i].f_data, &kf[i]);
602 		if (kf[i].so_splice != 0)
603 			splice_insert('<', kf[i].so_splice, &kf[i]);
604 	}
605 }
606 
607 void
608 print_inet_details(struct kinfo_file *kf)
609 {
610 	struct in_addr laddr, faddr;
611 
612 	memcpy(&laddr, kf->inp_laddru, sizeof(laddr));
613 	memcpy(&faddr, kf->inp_faddru, sizeof(faddr));
614 	if (kf->so_protocol == IPPROTO_TCP) {
615 		printf(" ");
616 		hide((void *)(uintptr_t)kf->inp_ppcb);
617 		printf(" %s:%d", laddr.s_addr == INADDR_ANY ? "*" :
618 		    inet_ntoa(laddr), ntohs(kf->inp_lport));
619 		if (kf->inp_fport) {
620 			if (kf->so_state & SS_CONNECTOUT)
621 				printf(" --> ");
622 			else
623 				printf(" <-- ");
624 			printf("%s:%d",
625 			    faddr.s_addr == INADDR_ANY ? "*" :
626 			    inet_ntoa(faddr), ntohs(kf->inp_fport));
627 		}
628 	} else if (kf->so_protocol == IPPROTO_UDP) {
629 		printf(" %s:%d", laddr.s_addr == INADDR_ANY ? "*" :
630 		    inet_ntoa(laddr), ntohs(kf->inp_lport));
631 		if (kf->inp_fport) {
632 			printf(" <-> %s:%d",
633 			    faddr.s_addr == INADDR_ANY ? "*" :
634 			    inet_ntoa(faddr), ntohs(kf->inp_fport));
635 		}
636 	} else if (kf->so_pcb) {
637 		printf(" ");
638 		hide((void *)(uintptr_t)kf->so_pcb);
639 	}
640 }
641 
642 void
643 print_inet6_details(struct kinfo_file *kf)
644 {
645 	char xaddrbuf[NI_MAXHOST + 2];
646 	struct in6_addr laddr6, faddr6;
647 
648 	memcpy(&laddr6, kf->inp_laddru, sizeof(laddr6));
649 	memcpy(&faddr6, kf->inp_faddru, sizeof(faddr6));
650 	if (kf->so_protocol == IPPROTO_TCP) {
651 		printf(" ");
652 		hide((void *)(uintptr_t)kf->inp_ppcb);
653 		snprintf(xaddrbuf, sizeof(xaddrbuf), "[%s]",
654 		    inet6_addrstr(&laddr6));
655 		printf(" %s:%d",
656 		    IN6_IS_ADDR_UNSPECIFIED(&laddr6) ? "*" :
657 		    xaddrbuf, ntohs(kf->inp_lport));
658 		if (kf->inp_fport) {
659 			if (kf->so_state & SS_CONNECTOUT)
660 				printf(" --> ");
661 			else
662 				printf(" <-- ");
663 			snprintf(xaddrbuf, sizeof(xaddrbuf), "[%s]",
664 			    inet6_addrstr(&faddr6));
665 			printf("%s:%d",
666 			    IN6_IS_ADDR_UNSPECIFIED(&faddr6) ? "*" :
667 			    xaddrbuf, ntohs(kf->inp_fport));
668 		}
669 	} else if (kf->so_protocol == IPPROTO_UDP) {
670 		snprintf(xaddrbuf, sizeof(xaddrbuf), "[%s]",
671 		    inet6_addrstr(&laddr6));
672 		printf(" %s:%d",
673 		    IN6_IS_ADDR_UNSPECIFIED(&laddr6) ? "*" :
674 		    xaddrbuf, ntohs(kf->inp_lport));
675 		if (kf->inp_fport) {
676 			snprintf(xaddrbuf, sizeof(xaddrbuf), "[%s]",
677 			    inet6_addrstr(&faddr6));
678 			printf(" <-> %s:%d",
679 			    IN6_IS_ADDR_UNSPECIFIED(&faddr6) ? "*" :
680 			    xaddrbuf, ntohs(kf->inp_fport));
681 		}
682 	} else if (kf->so_pcb) {
683 		printf(" ");
684 		hide((void *)(uintptr_t)kf->so_pcb);
685 	}
686 }
687 
688 void
689 print_sock_details(struct kinfo_file *kf)
690 {
691 	if (kf->so_family == AF_INET)
692 		print_inet_details(kf);
693 	else if (kf->so_family == AF_INET6)
694 		print_inet6_details(kf);
695 }
696 
697 void
698 socktrans(struct kinfo_file *kf)
699 {
700 	static char *stypename[] = {
701 		"unused",	/* 0 */
702 		"stream",	/* 1 */
703 		"dgram",	/* 2 */
704 		"raw",		/* 3 */
705 		"rdm",		/* 4 */
706 		"seqpak"	/* 5 */
707 	};
708 #define	STYPEMAX 5
709 	char *stype, stypebuf[24];
710 
711 	PREFIX(kf->fd_fd);
712 
713 	if (kf->so_type > STYPEMAX) {
714 		snprintf(stypebuf, sizeof(stypebuf), "?%d", kf->so_type);
715 		stype = stypebuf;
716 	} else {
717 		stype = stypename[kf->so_type];
718 	}
719 
720 	/*
721 	 * protocol specific formatting
722 	 *
723 	 * Try to find interesting things to print.  For tcp, the interesting
724 	 * thing is the address of the tcpcb, for udp and others, just the
725 	 * inpcb (socket pcb).  For unix domain, its the address of the socket
726 	 * pcb and the address of the connected pcb (if connected).  Otherwise
727 	 * just print the protocol number and address of the socket itself.
728 	 * The idea is not to duplicate netstat, but to make available enough
729 	 * information for further analysis.
730 	 */
731 	switch (kf->so_family) {
732 	case AF_INET:
733 		printf("* internet %s", stype);
734 		getinetproto(kf->so_protocol);
735 		print_inet_details(kf);
736 		break;
737 	case AF_INET6:
738 		printf("* internet6 %s", stype);
739 		getinetproto(kf->so_protocol);
740 		print_inet6_details(kf);
741 		break;
742 	case AF_UNIX:
743 		/* print address of pcb and connected pcb */
744 		printf("* unix %s", stype);
745 		if (kf->so_pcb) {
746 			printf(" ");
747 			hide((void *)(uintptr_t)kf->so_pcb);
748 			if (kf->unp_conn) {
749 				char shoconn[4], *cp;
750 
751 				cp = shoconn;
752 				if (!(kf->so_state & SS_CANTRCVMORE))
753 					*cp++ = '<';
754 				*cp++ = '-';
755 				if (!(kf->so_state & SS_CANTSENDMORE))
756 					*cp++ = '>';
757 				*cp = '\0';
758 				printf(" %s ", shoconn);
759 				hide((void *)(uintptr_t)kf->unp_conn);
760 			}
761 		}
762 		break;
763 	case AF_MPLS:
764 		/* print protocol number and socket address */
765 		printf("* mpls %s", stype);
766 		printf(" %d ", kf->so_protocol);
767 		hide((void *)(uintptr_t)kf->f_data);
768 		break;
769 	case AF_ROUTE:
770 		/* print protocol number and socket address */
771 		printf("* route %s", stype);
772 		printf(" %d ", kf->so_protocol);
773 		hide((void *)(uintptr_t)kf->f_data);
774 		break;
775 	default:
776 		/* print protocol number and socket address */
777 		printf("* %d %s", kf->so_family, stype);
778 		printf(" %d ", kf->so_protocol);
779 		hide((void *)(uintptr_t)kf->f_data);
780 	}
781 	if (kf->so_splice != 0 || kf->so_splicelen == -1) {
782 		struct kinfo_file *from, *to;
783 
784 		from = splice_find('<', kf->f_data);
785 		to = NULL;
786 		if (kf->so_splice != 0)
787 			to = splice_find('>', kf->so_splice);
788 
789 		if (to != NULL && from == to) {
790 			printf(" <==>");
791 			print_sock_details(to);
792 		} else if (kf->so_splice != 0) {
793 			printf(" ==>");
794 			if (to != NULL)
795 				print_sock_details(to);
796 		} else if (kf->so_splicelen == -1) {
797 			printf(" <==");
798 			if (from != NULL)
799 				print_sock_details(from);
800 		}
801 	}
802 	if (sflg)
803 		printf("\t%8llu %8llu",
804 		    (kf->f_rxfer + kf->f_rwfer),
805 		    (kf->f_rbytes + kf->f_wbytes) / 1024);
806 	printf("\n");
807 }
808 
809 /*
810  * getinetproto --
811  *	print name of protocol number
812  */
813 void
814 getinetproto(int number)
815 {
816 	static int isopen;
817 	struct protoent *pe;
818 
819 	if (!isopen)
820 		setprotoent(++isopen);
821 	if ((pe = getprotobynumber(number)) != NULL)
822 		printf(" %s", pe->p_name);
823 	else
824 		printf(" %d", number);
825 }
826 
827 int
828 getfname(char *filename)
829 {
830 	static struct statfs *mntbuf;
831 	static int nmounts;
832 	int i;
833 	struct stat sb;
834 	struct filearg *cur;
835 
836 	if (stat(filename, &sb)) {
837 		warn("%s", filename);
838 		return (0);
839 	}
840 
841 	/*
842 	 * POSIX specifies "For block special devices, all processes using any
843 	 * file on that device are listed".  However the -f flag description
844 	 * states "The report shall be only for the named files", so we only
845 	 * look up a block device if the -f flag has not be specified.
846 	 */
847 	if (fuser && !fsflg && S_ISBLK(sb.st_mode)) {
848 		if (mntbuf == NULL) {
849 			nmounts = getmntinfo(&mntbuf, MNT_NOWAIT);
850 			if (nmounts == -1)
851 				err(1, "getmntinfo");
852 		}
853 		for (i = 0; i < nmounts; i++) {
854 			if (!strcmp(mntbuf[i].f_mntfromname, filename)) {
855 				if (stat(mntbuf[i].f_mntonname, &sb) == -1) {
856 					warn("%s", filename);
857 					return (0);
858 				}
859 				cflg = 1;
860 				break;
861 			}
862 		}
863 	}
864 
865 	if ((cur = malloc(sizeof(*cur))) == NULL)
866 		err(1, NULL);
867 
868 	cur->ino = sb.st_ino;
869 	cur->dev = sb.st_dev & 0xffff;
870 	cur->name = filename;
871 	TAILQ_INIT(&cur->fusers);
872 	SLIST_INSERT_HEAD(&fileargs, cur, next);
873 	return (1);
874 }
875 
876 int
877 signame_to_signum(char *sig)
878 {
879 	int n;
880 	const char *errstr = NULL;
881 
882 	if (isdigit((unsigned char)*sig)) {
883 		n = strtonum(sig, 0, NSIG - 1, &errstr);
884 		return (errstr ? -1 : n);
885 	}
886 	if (!strncasecmp(sig, "sig", 3))
887 		sig += 3;
888 	for (n = 1; n < NSIG; n++) {
889 		if (!strcasecmp(sys_signame[n], sig))
890 			return (n);
891 	}
892 	return (-1);
893 }
894 
895 void
896 usage(void)
897 {
898 	if (fuser) {
899 		fprintf(stderr, "usage: fuser [-cfku] [-M core] "
900 		    "[-N system] [-s signal] file ...\n");
901 	} else {
902 		fprintf(stderr, "usage: fstat [-fnosv] [-M core] [-N system] "
903 		    "[-p pid] [-u user] [file ...]\n");
904 	}
905 	exit(1);
906 }
907