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