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