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