1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2009 Stanislav Sedov <stas@FreeBSD.org> 5 * Copyright (c) 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 #include <sys/param.h> 35 #include <sys/user.h> 36 #include <sys/stat.h> 37 #include <sys/socket.h> 38 #include <sys/socketvar.h> 39 #include <sys/sysctl.h> 40 #include <sys/queue.h> 41 #include <sys/un.h> 42 43 #include <netinet/in.h> 44 45 #include <arpa/inet.h> 46 47 #include <assert.h> 48 #include <ctype.h> 49 #include <err.h> 50 #include <libprocstat.h> 51 #include <limits.h> 52 #include <pwd.h> 53 #include <stdint.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <stddef.h> 57 #include <string.h> 58 #include <unistd.h> 59 #include <netdb.h> 60 61 #include "functions.h" 62 63 static int fsflg, /* show files on same filesystem as file(s) argument */ 64 pflg, /* show files open by a particular pid */ 65 sflg, /* show socket details */ 66 uflg; /* show files open by a particular (effective) user */ 67 static int checkfile; /* restrict to particular files or filesystems */ 68 static int nflg; /* (numerical) display f.s. and rdev as dev_t */ 69 static int mflg; /* include memory-mapped files */ 70 static int vflg; /* be verbose */ 71 72 typedef struct devs { 73 struct devs *next; 74 uint64_t fsid; 75 uint64_t ino; 76 const char *name; 77 } DEVS; 78 79 static DEVS *devs; 80 static char *memf, *nlistf; 81 82 static int getfname(const char *filename); 83 static void dofiles(struct procstat *procstat, struct kinfo_proc *p); 84 static void print_access_flags(int flags); 85 static void print_file_info(struct procstat *procstat, 86 struct filestat *fst, const char *uname, const char *cmd, int pid); 87 static void print_pipe_info(struct procstat *procstat, 88 struct filestat *fst); 89 static void print_pts_info(struct procstat *procstat, 90 struct filestat *fst); 91 static void print_sem_info(struct procstat *procstat, 92 struct filestat *fst); 93 static void print_shm_info(struct procstat *procstat, 94 struct filestat *fst); 95 static void print_socket_info(struct procstat *procstat, 96 struct filestat *fst); 97 static void print_vnode_info(struct procstat *procstat, 98 struct filestat *fst); 99 static void usage(void) __dead2; 100 101 int 102 do_fstat(int argc, char **argv) 103 { 104 struct kinfo_proc *p; 105 struct passwd *passwd; 106 struct procstat *procstat; 107 int arg, ch, what; 108 int cnt, i; 109 110 arg = 0; 111 what = KERN_PROC_PROC; 112 nlistf = memf = NULL; 113 while ((ch = getopt(argc, argv, "fmnp:su:vN:M:")) != -1) 114 switch((char)ch) { 115 case 'f': 116 fsflg = 1; 117 break; 118 case 'M': 119 memf = optarg; 120 break; 121 case 'N': 122 nlistf = optarg; 123 break; 124 case 'm': 125 mflg = 1; 126 break; 127 case 'n': 128 nflg = 1; 129 break; 130 case 'p': 131 if (pflg++) 132 usage(); 133 if (!isdigit(*optarg)) { 134 warnx("-p requires a process id"); 135 usage(); 136 } 137 what = KERN_PROC_PID; 138 arg = atoi(optarg); 139 break; 140 case 's': 141 sflg = 1; 142 break; 143 case 'u': 144 if (uflg++) 145 usage(); 146 if (!(passwd = getpwnam(optarg))) 147 errx(1, "%s: unknown uid", optarg); 148 what = KERN_PROC_UID; 149 arg = passwd->pw_uid; 150 break; 151 case 'v': 152 vflg = 1; 153 break; 154 case '?': 155 default: 156 usage(); 157 } 158 159 if (*(argv += optind)) { 160 for (; *argv; ++argv) { 161 if (getfname(*argv)) 162 checkfile = 1; 163 } 164 if (!checkfile) /* file(s) specified, but none accessible */ 165 exit(1); 166 } 167 168 if (fsflg && !checkfile) { 169 /* -f with no files means use wd */ 170 if (getfname(".") == 0) 171 exit(1); 172 checkfile = 1; 173 } 174 175 if (memf != NULL) 176 procstat = procstat_open_kvm(nlistf, memf); 177 else 178 procstat = procstat_open_sysctl(); 179 if (procstat == NULL) 180 errx(1, "procstat_open()"); 181 p = procstat_getprocs(procstat, what, arg, &cnt); 182 if (p == NULL) 183 errx(1, "procstat_getprocs()"); 184 185 /* 186 * Print header. 187 */ 188 if (nflg) 189 printf("%s", 190 "USER CMD PID FD DEV INUM MODE SZ|DV R/W"); 191 else 192 printf("%s", 193 "USER CMD PID FD MOUNT INUM MODE SZ|DV R/W"); 194 if (checkfile && fsflg == 0) 195 printf(" NAME\n"); 196 else 197 putchar('\n'); 198 199 /* 200 * Go through the process list. 201 */ 202 for (i = 0; i < cnt; i++) { 203 if (p[i].ki_stat == SZOMB) 204 continue; 205 dofiles(procstat, &p[i]); 206 } 207 procstat_freeprocs(procstat, p); 208 procstat_close(procstat); 209 return (0); 210 } 211 212 static void 213 dofiles(struct procstat *procstat, struct kinfo_proc *kp) 214 { 215 const char *cmd; 216 const char *uname; 217 struct filestat *fst; 218 struct filestat_list *head; 219 int pid; 220 221 uname = user_from_uid(kp->ki_uid, 0); 222 pid = kp->ki_pid; 223 cmd = kp->ki_comm; 224 225 head = procstat_getfiles(procstat, kp, mflg); 226 if (head == NULL) 227 return; 228 STAILQ_FOREACH(fst, head, next) 229 print_file_info(procstat, fst, uname, cmd, pid); 230 procstat_freefiles(procstat, head); 231 } 232 233 234 static void 235 print_file_info(struct procstat *procstat, struct filestat *fst, 236 const char *uname, const char *cmd, int pid) 237 { 238 struct vnstat vn; 239 DEVS *d; 240 const char *filename; 241 int error, fsmatch = 0; 242 char errbuf[_POSIX2_LINE_MAX]; 243 244 filename = NULL; 245 if (checkfile != 0) { 246 if (fst->fs_type != PS_FST_TYPE_VNODE && 247 fst->fs_type != PS_FST_TYPE_FIFO) 248 return; 249 error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); 250 if (error != 0) 251 return; 252 253 for (d = devs; d != NULL; d = d->next) 254 if (d->fsid == vn.vn_fsid) { 255 fsmatch = 1; 256 if (d->ino == vn.vn_fileid) { 257 filename = d->name; 258 break; 259 } 260 } 261 if (fsmatch == 0 || (filename == NULL && fsflg == 0)) 262 return; 263 } 264 265 /* 266 * Print entry prefix. 267 */ 268 printf("%-8.8s %-10s %5d", uname, cmd, pid); 269 if (fst->fs_uflags & PS_FST_UFLAG_TEXT) 270 printf(" text"); 271 else if (fst->fs_uflags & PS_FST_UFLAG_CDIR) 272 printf(" wd"); 273 else if (fst->fs_uflags & PS_FST_UFLAG_RDIR) 274 printf(" root"); 275 else if (fst->fs_uflags & PS_FST_UFLAG_TRACE) 276 printf(" tr"); 277 else if (fst->fs_uflags & PS_FST_UFLAG_MMAP) 278 printf(" mmap"); 279 else if (fst->fs_uflags & PS_FST_UFLAG_JAIL) 280 printf(" jail"); 281 else if (fst->fs_uflags & PS_FST_UFLAG_CTTY) 282 printf(" ctty"); 283 else 284 printf(" %4d", fst->fs_fd); 285 286 /* 287 * Print type-specific data. 288 */ 289 switch (fst->fs_type) { 290 case PS_FST_TYPE_FIFO: 291 case PS_FST_TYPE_VNODE: 292 print_vnode_info(procstat, fst); 293 break; 294 case PS_FST_TYPE_SOCKET: 295 print_socket_info(procstat, fst); 296 break; 297 case PS_FST_TYPE_PIPE: 298 print_pipe_info(procstat, fst); 299 break; 300 case PS_FST_TYPE_PTS: 301 print_pts_info(procstat, fst); 302 break; 303 case PS_FST_TYPE_SHM: 304 print_shm_info(procstat, fst); 305 break; 306 case PS_FST_TYPE_SEM: 307 print_sem_info(procstat, fst); 308 break; 309 case PS_FST_TYPE_DEV: 310 break; 311 default: 312 if (vflg) 313 fprintf(stderr, 314 "unknown file type %d for file %d of pid %d\n", 315 fst->fs_type, fst->fs_fd, pid); 316 } 317 if (filename && !fsflg) 318 printf(" %s", filename); 319 putchar('\n'); 320 } 321 322 static char * 323 addr_to_string(struct sockaddr_storage *ss, char *buffer, int buflen) 324 { 325 char buffer2[INET6_ADDRSTRLEN]; 326 struct sockaddr_in6 *sin6; 327 struct sockaddr_in *sin; 328 struct sockaddr_un *sun; 329 330 switch (ss->ss_family) { 331 case AF_LOCAL: 332 sun = (struct sockaddr_un *)ss; 333 if (strlen(sun->sun_path) == 0) 334 strlcpy(buffer, "-", buflen); 335 else 336 strlcpy(buffer, sun->sun_path, buflen); 337 break; 338 339 case AF_INET: 340 sin = (struct sockaddr_in *)ss; 341 if (sin->sin_addr.s_addr == INADDR_ANY) 342 snprintf(buffer, buflen, "%s:%d", "*", 343 ntohs(sin->sin_port)); 344 else if (inet_ntop(AF_INET, &sin->sin_addr, buffer2, 345 sizeof(buffer2)) != NULL) 346 snprintf(buffer, buflen, "%s:%d", buffer2, 347 ntohs(sin->sin_port)); 348 break; 349 350 case AF_INET6: 351 sin6 = (struct sockaddr_in6 *)ss; 352 if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) 353 snprintf(buffer, buflen, "%s.%d", "*", 354 ntohs(sin6->sin6_port)); 355 else if (inet_ntop(AF_INET6, &sin6->sin6_addr, buffer2, 356 sizeof(buffer2)) != NULL) 357 snprintf(buffer, buflen, "%s.%d", buffer2, 358 ntohs(sin6->sin6_port)); 359 else 360 strlcpy(buffer, "-", buflen); 361 break; 362 363 default: 364 strlcpy(buffer, "", buflen); 365 break; 366 } 367 return buffer; 368 } 369 370 371 static void 372 print_socket_info(struct procstat *procstat, struct filestat *fst) 373 { 374 static const char *stypename[] = { 375 "unused", /* 0 */ 376 "stream", /* 1 */ 377 "dgram", /* 2 */ 378 "raw", /* 3 */ 379 "rdm", /* 4 */ 380 "seqpak" /* 5 */ 381 }; 382 #define STYPEMAX 5 383 struct sockstat sock; 384 struct protoent *pe; 385 char errbuf[_POSIX2_LINE_MAX]; 386 char src_addr[PATH_MAX], dst_addr[PATH_MAX]; 387 struct sockaddr_un *sun; 388 int error; 389 static int isopen; 390 391 error = procstat_get_socket_info(procstat, fst, &sock, errbuf); 392 if (error != 0) { 393 printf("* error"); 394 return; 395 } 396 if (sock.type > STYPEMAX) 397 printf("* %s ?%d", sock.dname, sock.type); 398 else 399 printf("* %s %s", sock.dname, stypename[sock.type]); 400 401 /* 402 * protocol specific formatting 403 * 404 * Try to find interesting things to print. For tcp, the interesting 405 * thing is the address of the tcpcb, for udp and others, just the 406 * inpcb (socket pcb). For unix domain, its the address of the socket 407 * pcb and the address of the connected pcb (if connected). Otherwise 408 * just print the protocol number and address of the socket itself. 409 * The idea is not to duplicate netstat, but to make available enough 410 * information for further analysis. 411 */ 412 switch (sock.dom_family) { 413 case AF_INET: 414 case AF_INET6: 415 if (!isopen) 416 setprotoent(++isopen); 417 if ((pe = getprotobynumber(sock.proto)) != NULL) 418 printf(" %s", pe->p_name); 419 else 420 printf(" %d", sock.proto); 421 if (sock.proto == IPPROTO_TCP ) { 422 if (sock.inp_ppcb != 0) 423 printf(" %lx", (u_long)sock.inp_ppcb); 424 } 425 else if (sock.so_pcb != 0) 426 printf(" %lx", (u_long)sock.so_pcb); 427 if (!sflg) 428 break; 429 printf(" %s <-> %s", 430 addr_to_string(&sock.sa_local, src_addr, sizeof(src_addr)), 431 addr_to_string(&sock.sa_peer, dst_addr, sizeof(dst_addr))); 432 break; 433 case AF_UNIX: 434 /* print address of pcb and connected pcb */ 435 if (sock.so_pcb != 0) { 436 printf(" %lx", (u_long)sock.so_pcb); 437 if (sock.unp_conn) { 438 char shoconn[4], *cp; 439 440 cp = shoconn; 441 if (!(sock.so_rcv_sb_state & SBS_CANTRCVMORE)) 442 *cp++ = '<'; 443 *cp++ = '-'; 444 if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE)) 445 *cp++ = '>'; 446 *cp = '\0'; 447 printf(" %s %lx", shoconn, 448 (u_long)sock.unp_conn); 449 } 450 } 451 if (!sflg) 452 break; 453 sun = (struct sockaddr_un *)&sock.sa_local; 454 /* 455 * While generally we like to print two addresses, 456 * local and peer, for sockets, it turns out to be 457 * more useful to print the first non-null address for 458 * local sockets, as typically they aren't bound and 459 * connected, and the path strings can get long. 460 */ 461 if (sun->sun_path[0] != 0) 462 addr_to_string(&sock.sa_local, 463 src_addr, sizeof(src_addr)); 464 else 465 addr_to_string(&sock.sa_peer, 466 src_addr, sizeof(src_addr)); 467 printf(" %s", src_addr); 468 break; 469 default: 470 /* print protocol number and socket address */ 471 printf(" %d %lx", sock.proto, (u_long)sock.so_addr); 472 } 473 } 474 475 static void 476 print_pipe_info(struct procstat *procstat, struct filestat *fst) 477 { 478 struct pipestat ps; 479 char errbuf[_POSIX2_LINE_MAX]; 480 int error; 481 482 error = procstat_get_pipe_info(procstat, fst, &ps, errbuf); 483 if (error != 0) { 484 printf("* error"); 485 return; 486 } 487 printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer); 488 printf(" %6zd", ps.buffer_cnt); 489 print_access_flags(fst->fs_fflags); 490 } 491 492 static void 493 print_pts_info(struct procstat *procstat, struct filestat *fst) 494 { 495 struct ptsstat pts; 496 char errbuf[_POSIX2_LINE_MAX]; 497 int error; 498 499 error = procstat_get_pts_info(procstat, fst, &pts, errbuf); 500 if (error != 0) { 501 printf("* error"); 502 return; 503 } 504 printf("* pseudo-terminal master "); 505 if (nflg || !*pts.devname) { 506 printf("%#10jx", (uintmax_t)pts.dev); 507 } else { 508 printf("%10s", pts.devname); 509 } 510 print_access_flags(fst->fs_fflags); 511 } 512 513 static void 514 print_sem_info(struct procstat *procstat, struct filestat *fst) 515 { 516 struct semstat sem; 517 char errbuf[_POSIX2_LINE_MAX]; 518 char mode[15]; 519 int error; 520 521 error = procstat_get_sem_info(procstat, fst, &sem, errbuf); 522 if (error != 0) { 523 printf("* error"); 524 return; 525 } 526 if (nflg) { 527 printf(" "); 528 (void)snprintf(mode, sizeof(mode), "%o", sem.mode); 529 } else { 530 printf(" %-15s", fst->fs_path != NULL ? fst->fs_path : "-"); 531 strmode(sem.mode, mode); 532 } 533 printf(" %10s %6u", mode, sem.value); 534 print_access_flags(fst->fs_fflags); 535 } 536 537 static void 538 print_shm_info(struct procstat *procstat, struct filestat *fst) 539 { 540 struct shmstat shm; 541 char errbuf[_POSIX2_LINE_MAX]; 542 char mode[15]; 543 int error; 544 545 error = procstat_get_shm_info(procstat, fst, &shm, errbuf); 546 if (error != 0) { 547 printf("* error"); 548 return; 549 } 550 if (nflg) { 551 printf(" "); 552 (void)snprintf(mode, sizeof(mode), "%o", shm.mode); 553 } else { 554 printf(" %-15s", fst->fs_path != NULL ? fst->fs_path : "-"); 555 strmode(shm.mode, mode); 556 } 557 printf(" %10s %6ju", mode, shm.size); 558 print_access_flags(fst->fs_fflags); 559 } 560 561 static void 562 print_vnode_info(struct procstat *procstat, struct filestat *fst) 563 { 564 struct vnstat vn; 565 char errbuf[_POSIX2_LINE_MAX]; 566 char mode[15]; 567 const char *badtype; 568 int error; 569 570 badtype = NULL; 571 error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); 572 if (error != 0) 573 badtype = errbuf; 574 else if (vn.vn_type == PS_FST_VTYPE_VBAD) 575 badtype = "bad"; 576 else if (vn.vn_type == PS_FST_VTYPE_VNON) 577 badtype = "none"; 578 if (badtype != NULL) { 579 printf(" - - %10s -", badtype); 580 return; 581 } 582 583 if (nflg) 584 printf(" %#5jx", (uintmax_t)vn.vn_fsid); 585 else if (vn.vn_mntdir != NULL) 586 (void)printf(" %-8s", vn.vn_mntdir); 587 588 /* 589 * Print access mode. 590 */ 591 if (nflg) 592 (void)snprintf(mode, sizeof(mode), "%o", vn.vn_mode); 593 else { 594 strmode(vn.vn_mode, mode); 595 } 596 (void)printf(" %6jd %10s", (intmax_t)vn.vn_fileid, mode); 597 598 if (vn.vn_type == PS_FST_VTYPE_VBLK || vn.vn_type == PS_FST_VTYPE_VCHR) { 599 if (nflg || !*vn.vn_devname) 600 printf(" %#6jx", (uintmax_t)vn.vn_dev); 601 else { 602 printf(" %6s", vn.vn_devname); 603 } 604 } else 605 printf(" %6ju", (uintmax_t)vn.vn_size); 606 print_access_flags(fst->fs_fflags); 607 } 608 609 static void 610 print_access_flags(int flags) 611 { 612 char rw[3]; 613 614 rw[0] = '\0'; 615 if (flags & PS_FST_FFLAG_READ) 616 strcat(rw, "r"); 617 if (flags & PS_FST_FFLAG_WRITE) 618 strcat(rw, "w"); 619 printf(" %2s", rw); 620 } 621 622 int 623 getfname(const char *filename) 624 { 625 struct stat statbuf; 626 DEVS *cur; 627 628 if (stat(filename, &statbuf)) { 629 warn("%s", filename); 630 return (0); 631 } 632 if ((cur = malloc(sizeof(DEVS))) == NULL) 633 err(1, NULL); 634 cur->next = devs; 635 devs = cur; 636 637 cur->ino = statbuf.st_ino; 638 cur->fsid = statbuf.st_dev; 639 cur->name = filename; 640 return (1); 641 } 642 643 static void 644 usage(void) 645 { 646 (void)fprintf(stderr, 647 "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] [file ...]\n"); 648 exit(1); 649 } 650