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