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