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