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