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