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