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