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