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