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