xref: /openbsd-src/usr.bin/fstat/fstat.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*	$OpenBSD: fstat.c,v 1.99 2019/02/05 02:17:32 deraadt 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 		if (checkfile == 0)
412 			socktrans(kf);
413 		break;
414 	case DTYPE_PIPE:
415 		if (checkfile == 0)
416 			pipetrans(kf);
417 		break;
418 	case DTYPE_KQUEUE:
419 		if (checkfile == 0)
420 			kqueuetrans(kf);
421 		break;
422 	default:
423 		if (vflg) {
424 			warnx("unknown file type %d for file %d of pid %ld",
425 			    kf->f_type, kf->fd_fd, (long)Pid);
426 		}
427 		break;
428 	}
429 }
430 
431 void
432 vtrans(struct kinfo_file *kf)
433 {
434 	const char *badtype = NULL;
435 	char rwep[5], mode[12];
436 	char *filename = NULL;
437 
438 	if (kf->v_type == VNON)
439 		badtype = "none";
440 	else if (kf->v_type == VBAD)
441 		badtype = "bad";
442 	else if (kf->v_tag == VT_NON && !(kf->v_flag & VCLONE))
443 		badtype = "none";	/* not a clone */
444 
445 	if (checkfile) {
446 		int fsmatch = 0;
447 		struct filearg *fa;
448 
449 		if (badtype)
450 			return;
451 		SLIST_FOREACH(fa, &fileargs, next) {
452 			if (fa->dev == kf->va_fsid) {
453 				fsmatch = 1;
454 				if (fa->ino == kf->va_fileid) {
455 					filename = fa->name;
456 					break;
457 				}
458 			}
459 		}
460 		if (fsmatch == 0 || (filename == NULL && fsflg == 0))
461 			return;
462 	}
463 	PREFIX(kf->fd_fd);
464 	if (badtype) {
465 		(void)printf(" -           -  %10s    -\n", badtype);
466 		return;
467 	}
468 
469 	if (nflg)
470 		(void)printf(" %2lu,%-2lu", (long)major(kf->va_fsid),
471 		    (long)minor(kf->va_fsid));
472 	else if (!(kf->v_flag & VCLONE))
473 		(void)printf(" %-8s", kf->f_mntonname);
474 	else
475 		(void)printf(" clone   ");
476 	if (nflg)
477 		(void)snprintf(mode, sizeof(mode), "%o", kf->va_mode);
478 	else
479 		strmode(kf->va_mode, mode);
480 	printf(" %8llu%s %11s", kf->va_fileid,
481 	    kf->va_nlink == 0 ? "*" : " ",
482 	    mode);
483 	rwep[0] = '\0';
484 	if (kf->f_flag & FREAD)
485 		strlcat(rwep, "r", sizeof rwep);
486 	if (kf->f_flag & FWRITE)
487 		strlcat(rwep, "w", sizeof rwep);
488 	if (kf->fd_ofileflags & UF_EXCLOSE)
489 		strlcat(rwep, "e", sizeof rwep);
490 	if (kf->fd_ofileflags & UF_PLEDGED)
491 		strlcat(rwep, "p", sizeof rwep);
492 	printf(" %4s", rwep);
493 	switch (kf->v_type) {
494 	case VBLK:
495 	case VCHR: {
496 		char *name;
497 
498 		if (nflg || ((name = devname(kf->va_rdev,
499 		    kf->v_type == VCHR ?  S_IFCHR : S_IFBLK)) == NULL))
500 			printf("   %2u,%-3u", major(kf->va_rdev), minor(kf->va_rdev));
501 		else
502 			printf("  %7s", name);
503 		if (oflg)
504 			printf("         ");
505 		break;
506 	}
507 	default:
508 		printf(" %8llu", kf->va_size);
509 		if (oflg) {
510 			if (uid == 0 || uid == *procuid)
511 				printf(":%-8llu", kf->f_offset);
512 			else
513 				printf(":%-8s", "*");
514 		}
515 	}
516 	if (sflg) {
517 		if (uid == 0 || uid == *procuid) {
518 			printf(" %8llu %8llu",
519 			    (kf->f_rxfer + kf->f_rwfer),
520 			    (kf->f_rbytes + kf->f_wbytes) / 1024);
521 		} else {
522 			printf(" %8s %8s", "*", "*");
523 		}
524 	}
525 	if (filename && !fsflg)
526 		printf(" %s", filename);
527 	putchar('\n');
528 }
529 
530 void
531 pipetrans(struct kinfo_file *kf)
532 {
533 	void *maxaddr;
534 
535 	PREFIX(kf->fd_fd);
536 
537 	printf(" ");
538 
539 	/*
540 	 * We don't have enough space to fit both peer and own address, so
541 	 * we select the higher address so both ends of the pipe have the
542 	 * same visible addr. (it's the higher address because when the other
543 	 * end closes, it becomes 0)
544 	 */
545 	maxaddr = (void *)(uintptr_t)MAXIMUM(kf->f_data, kf->pipe_peer);
546 
547 	printf("pipe ");
548 	hide(maxaddr);
549 	printf(" state: %s%s%s",
550 	    (kf->pipe_state & PIPE_WANTR) ? "R" : "",
551 	    (kf->pipe_state & PIPE_WANTW) ? "W" : "",
552 	    (kf->pipe_state & PIPE_EOF) ? "E" : "");
553 	if (sflg)
554 		printf("\t%8llu %8llu",
555 		    (kf->f_rxfer + kf->f_rwfer),
556 		    (kf->f_rbytes + kf->f_wbytes) / 1024);
557 	printf("\n");
558 	return;
559 }
560 
561 void
562 kqueuetrans(struct kinfo_file *kf)
563 {
564 	PREFIX(kf->fd_fd);
565 
566 	printf(" ");
567 
568 	printf("kqueue ");
569 	hide((void *)(uintptr_t)kf->f_data);
570 	printf(" %d state: %s%s\n",
571 	    kf->kq_count,
572 	    (kf->kq_state & KQ_SEL) ? "S" : "",
573 	    (kf->kq_state & KQ_SLEEP) ? "W" : "");
574 	return;
575 }
576 
577 const char *
578 inet6_addrstr(struct in6_addr *p)
579 {
580 	struct sockaddr_in6 sin6;
581 	static char hbuf[NI_MAXHOST];
582 	const int niflags = NI_NUMERICHOST;
583 
584 	memset(&sin6, 0, sizeof(sin6));
585 	sin6.sin6_family = AF_INET6;
586 	sin6.sin6_len = sizeof(struct sockaddr_in6);
587 	sin6.sin6_addr = *p;
588 	if (IN6_IS_ADDR_LINKLOCAL(p) &&
589 	    *(u_int16_t *)&sin6.sin6_addr.s6_addr[2] != 0) {
590 		sin6.sin6_scope_id =
591 		    ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
592 		sin6.sin6_addr.s6_addr[2] = sin6.sin6_addr.s6_addr[3] = 0;
593 	}
594 
595 	if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
596 	    hbuf, sizeof(hbuf), NULL, 0, niflags))
597 		return "invalid";
598 
599 	return hbuf;
600 }
601 
602 void
603 splice_insert(char type, u_int64_t ptr, struct kinfo_file *data)
604 {
605 	ENTRY entry, *found;
606 
607 	if (asprintf(&entry.key, "%c%llx", type, hideroot ? 0 : ptr) == -1)
608 		err(1, NULL);
609 	entry.data = data;
610 	if ((found = hsearch(entry, ENTER)) == NULL)
611 		err(1, "hsearch");
612 	/* if it's ambiguous, set the data to NULL */
613 	if (found->data != data)
614 		found->data = NULL;
615 }
616 
617 struct kinfo_file *
618 splice_find(char type, u_int64_t ptr)
619 {
620 	ENTRY entry, *found;
621 	char buf[20];
622 
623 	snprintf(buf, sizeof(buf), "%c%llx", type, hideroot ? 0 : ptr);
624 	entry.key = buf;
625 	found = hsearch(entry, FIND);
626 	return (found != NULL ? found->data : NULL);
627 }
628 
629 void
630 find_splices(struct kinfo_file *kf, int cnt)
631 {
632 	int i, created;
633 
634 	created = 0;
635 	for (i = 0; i < cnt; i++) {
636 		if (kf[i].f_type != DTYPE_SOCKET ||
637 		    (kf[i].so_splice == 0 && kf[i].so_splicelen != -1))
638 			continue;
639 		if (created++ == 0) {
640 			if (hcreate(1000) == 0)
641 				err(1, "hcreate");
642 		}
643 		splice_insert('>', kf[i].f_data, &kf[i]);
644 		if (kf[i].so_splice != 0)
645 			splice_insert('<', kf[i].so_splice, &kf[i]);
646 	}
647 }
648 
649 void
650 print_inet_details(struct kinfo_file *kf)
651 {
652 	struct in_addr laddr, faddr;
653 
654 	memcpy(&laddr, kf->inp_laddru, sizeof(laddr));
655 	memcpy(&faddr, kf->inp_faddru, sizeof(faddr));
656 	if (kf->so_protocol == IPPROTO_TCP) {
657 		printf(" ");
658 		hide((void *)(uintptr_t)kf->inp_ppcb);
659 		printf(" %s:%d", laddr.s_addr == INADDR_ANY ? "*" :
660 		    inet_ntoa(laddr), ntohs(kf->inp_lport));
661 		if (kf->inp_fport) {
662 			if (kf->so_state & SS_CONNECTOUT)
663 				printf(" --> ");
664 			else
665 				printf(" <-- ");
666 			printf("%s:%d",
667 			    faddr.s_addr == INADDR_ANY ? "*" :
668 			    inet_ntoa(faddr), ntohs(kf->inp_fport));
669 		}
670 	} else if (kf->so_protocol == IPPROTO_UDP) {
671 		printf(" %s:%d", laddr.s_addr == INADDR_ANY ? "*" :
672 		    inet_ntoa(laddr), ntohs(kf->inp_lport));
673 		if (kf->inp_fport) {
674 			printf(" <-> %s:%d",
675 			    faddr.s_addr == INADDR_ANY ? "*" :
676 			    inet_ntoa(faddr), ntohs(kf->inp_fport));
677 		}
678 	} else if (kf->so_pcb) {
679 		printf(" ");
680 		hide((void *)(uintptr_t)kf->so_pcb);
681 	}
682 }
683 
684 void
685 print_inet6_details(struct kinfo_file *kf)
686 {
687 	char xaddrbuf[NI_MAXHOST + 2];
688 	struct in6_addr laddr6, faddr6;
689 
690 	memcpy(&laddr6, kf->inp_laddru, sizeof(laddr6));
691 	memcpy(&faddr6, kf->inp_faddru, sizeof(faddr6));
692 	if (kf->so_protocol == IPPROTO_TCP) {
693 		printf(" ");
694 		hide((void *)(uintptr_t)kf->inp_ppcb);
695 		snprintf(xaddrbuf, sizeof(xaddrbuf), "[%s]",
696 		    inet6_addrstr(&laddr6));
697 		printf(" %s:%d",
698 		    IN6_IS_ADDR_UNSPECIFIED(&laddr6) ? "*" :
699 		    xaddrbuf, ntohs(kf->inp_lport));
700 		if (kf->inp_fport) {
701 			if (kf->so_state & SS_CONNECTOUT)
702 				printf(" --> ");
703 			else
704 				printf(" <-- ");
705 			snprintf(xaddrbuf, sizeof(xaddrbuf), "[%s]",
706 			    inet6_addrstr(&faddr6));
707 			printf("%s:%d",
708 			    IN6_IS_ADDR_UNSPECIFIED(&faddr6) ? "*" :
709 			    xaddrbuf, ntohs(kf->inp_fport));
710 		}
711 	} else if (kf->so_protocol == IPPROTO_UDP) {
712 		snprintf(xaddrbuf, sizeof(xaddrbuf), "[%s]",
713 		    inet6_addrstr(&laddr6));
714 		printf(" %s:%d",
715 		    IN6_IS_ADDR_UNSPECIFIED(&laddr6) ? "*" :
716 		    xaddrbuf, ntohs(kf->inp_lport));
717 		if (kf->inp_fport) {
718 			snprintf(xaddrbuf, sizeof(xaddrbuf), "[%s]",
719 			    inet6_addrstr(&faddr6));
720 			printf(" <-> %s:%d",
721 			    IN6_IS_ADDR_UNSPECIFIED(&faddr6) ? "*" :
722 			    xaddrbuf, ntohs(kf->inp_fport));
723 		}
724 	} else if (kf->so_pcb) {
725 		printf(" ");
726 		hide((void *)(uintptr_t)kf->so_pcb);
727 	}
728 }
729 
730 void
731 print_sock_details(struct kinfo_file *kf)
732 {
733 	if (kf->so_family == AF_INET)
734 		print_inet_details(kf);
735 	else if (kf->so_family == AF_INET6)
736 		print_inet6_details(kf);
737 }
738 
739 void
740 socktrans(struct kinfo_file *kf)
741 {
742 	static char *stypename[] = {
743 		"unused",	/* 0 */
744 		"stream",	/* 1 */
745 		"dgram",	/* 2 */
746 		"raw",		/* 3 */
747 		"rdm",		/* 4 */
748 		"seqpak"	/* 5 */
749 	};
750 #define	STYPEMAX 5
751 	char *stype, stypebuf[24];
752 
753 	PREFIX(kf->fd_fd);
754 
755 	if (kf->so_type > STYPEMAX) {
756 		snprintf(stypebuf, sizeof(stypebuf), "?%d", kf->so_type);
757 		stype = stypebuf;
758 	} else {
759 		stype = stypename[kf->so_type];
760 	}
761 
762 	/*
763 	 * protocol specific formatting
764 	 *
765 	 * Try to find interesting things to print.  For tcp, the interesting
766 	 * thing is the address of the tcpcb, for udp and others, just the
767 	 * inpcb (socket pcb).  For unix domain, its the address of the socket
768 	 * pcb and the address of the connected pcb (if connected).  Otherwise
769 	 * just print the protocol number and address of the socket itself.
770 	 * The idea is not to duplicate netstat, but to make available enough
771 	 * information for further analysis.
772 	 */
773 	switch (kf->so_family) {
774 	case AF_INET:
775 		printf("* internet %s", stype);
776 		getinetproto(kf->so_protocol);
777 		print_inet_details(kf);
778 		if (kf->inp_rtableid)
779 			printf(" rtable %u", kf->inp_rtableid);
780 		break;
781 	case AF_INET6:
782 		printf("* internet6 %s", stype);
783 		getinetproto(kf->so_protocol);
784 		print_inet6_details(kf);
785 		if (kf->inp_rtableid)
786 			printf(" rtable %u", kf->inp_rtableid);
787 		break;
788 	case AF_UNIX:
789 		/* print address of pcb and connected pcb */
790 		printf("* unix %s", stype);
791 		if (kf->so_pcb) {
792 			printf(" ");
793 			hide((void *)(uintptr_t)kf->so_pcb);
794 			if (kf->unp_conn) {
795 				char shoconn[4], *cp;
796 
797 				cp = shoconn;
798 				if (!(kf->so_state & SS_CANTRCVMORE))
799 					*cp++ = '<';
800 				*cp++ = '-';
801 				if (!(kf->so_state & SS_CANTSENDMORE))
802 					*cp++ = '>';
803 				*cp = '\0';
804 				printf(" %s ", shoconn);
805 				hide((void *)(uintptr_t)kf->unp_conn);
806 			}
807 		}
808 		break;
809 	case AF_MPLS:
810 		/* print protocol number and socket address */
811 		printf("* mpls %s", stype);
812 		printf(" %d ", kf->so_protocol);
813 		hide((void *)(uintptr_t)kf->f_data);
814 		break;
815 	case AF_ROUTE:
816 		/* print protocol number and socket address */
817 		printf("* route %s", stype);
818 		printf(" %d ", kf->so_protocol);
819 		hide((void *)(uintptr_t)kf->f_data);
820 		break;
821 	case AF_KEY:
822 		printf("* pfkey %s", stype);
823 		printf(" %d ", kf->so_protocol);
824 		hide((void *)(uintptr_t)kf->f_data);
825 		break;
826 	default:
827 		/* print protocol number and socket address */
828 		printf("* %d %s", kf->so_family, stype);
829 		printf(" %d ", kf->so_protocol);
830 		hide((void *)(uintptr_t)kf->f_data);
831 	}
832 	if (kf->so_splice != 0 || kf->so_splicelen == -1) {
833 		struct kinfo_file *from, *to;
834 
835 		from = splice_find('<', kf->f_data);
836 		to = NULL;
837 		if (kf->so_splice != 0)
838 			to = splice_find('>', kf->so_splice);
839 
840 		if (to != NULL && from == to) {
841 			printf(" <==>");
842 			print_sock_details(to);
843 		} else if (kf->so_splice != 0) {
844 			printf(" ==>");
845 			if (to != NULL)
846 				print_sock_details(to);
847 		} else if (kf->so_splicelen == -1) {
848 			printf(" <==");
849 			if (from != NULL)
850 				print_sock_details(from);
851 		}
852 	}
853 	if (sflg)
854 		printf("\t%8llu %8llu",
855 		    (kf->f_rxfer + kf->f_rwfer),
856 		    (kf->f_rbytes + kf->f_wbytes) / 1024);
857 	printf("\n");
858 }
859 
860 /*
861  * getinetproto --
862  *	print name of protocol number
863  */
864 void
865 getinetproto(int number)
866 {
867 	static int isopen;
868 	struct protoent *pe;
869 
870 	if (!isopen)
871 		setprotoent(++isopen);
872 	if ((pe = getprotobynumber(number)) != NULL)
873 		printf(" %s", pe->p_name);
874 	else
875 		printf(" %d", number);
876 }
877 
878 int
879 getfname(char *filename)
880 {
881 	static struct statfs *mntbuf;
882 	static int nmounts;
883 	int i;
884 	struct stat sb;
885 	struct filearg *cur;
886 
887 	if (stat(filename, &sb)) {
888 		warn("%s", filename);
889 		return (0);
890 	}
891 
892 	/*
893 	 * POSIX specifies "For block special devices, all processes using any
894 	 * file on that device are listed".  However the -f flag description
895 	 * states "The report shall be only for the named files", so we only
896 	 * look up a block device if the -f flag has not be specified.
897 	 */
898 	if (fuser && !fsflg && S_ISBLK(sb.st_mode)) {
899 		if (mntbuf == NULL) {
900 			nmounts = getmntinfo(&mntbuf, MNT_NOWAIT);
901 			if (nmounts == -1)
902 				err(1, "getmntinfo");
903 		}
904 		for (i = 0; i < nmounts; i++) {
905 			if (!strcmp(mntbuf[i].f_mntfromname, filename)) {
906 				if (stat(mntbuf[i].f_mntonname, &sb) == -1) {
907 					warn("%s", filename);
908 					return (0);
909 				}
910 				cflg = 1;
911 				break;
912 			}
913 		}
914 	}
915 
916 	if ((cur = malloc(sizeof(*cur))) == NULL)
917 		err(1, NULL);
918 
919 	cur->ino = sb.st_ino;
920 	cur->dev = sb.st_dev & 0xffff;
921 	cur->name = filename;
922 	TAILQ_INIT(&cur->fusers);
923 	SLIST_INSERT_HEAD(&fileargs, cur, next);
924 	return (1);
925 }
926 
927 int
928 signame_to_signum(char *sig)
929 {
930 	int n;
931 	const char *errstr = NULL;
932 
933 	if (isdigit((unsigned char)*sig)) {
934 		n = strtonum(sig, 0, NSIG - 1, &errstr);
935 		return (errstr ? -1 : n);
936 	}
937 	if (!strncasecmp(sig, "sig", 3))
938 		sig += 3;
939 	for (n = 1; n < NSIG; n++) {
940 		if (!strcasecmp(sys_signame[n], sig))
941 			return (n);
942 	}
943 	return (-1);
944 }
945 
946 void
947 usage(void)
948 {
949 	if (fuser) {
950 		fprintf(stderr, "usage: fuser [-cfku] [-M core] "
951 		    "[-N system] [-s signal] file ...\n");
952 	} else {
953 		fprintf(stderr, "usage: fstat [-fnosv] [-M core] [-N system] "
954 		    "[-p pid] [-u user] [file ...]\n");
955 	}
956 	exit(1);
957 }
958