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