fstat.c revision 221807
11590Srgrimes/*- 2221807Sstas * Copyright (c) 2009 Stanislav Sedov <stas@FreeBSD.org> 31590Srgrimes * Copyright (c) 1988, 1993 41590Srgrimes * The Regents of the University of California. All rights reserved. 51590Srgrimes * 61590Srgrimes * Redistribution and use in source and binary forms, with or without 71590Srgrimes * modification, are permitted provided that the following conditions 81590Srgrimes * are met: 91590Srgrimes * 1. Redistributions of source code must retain the above copyright 101590Srgrimes * notice, this list of conditions and the following disclaimer. 111590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121590Srgrimes * notice, this list of conditions and the following disclaimer in the 131590Srgrimes * documentation and/or other materials provided with the distribution. 141590Srgrimes * 4. Neither the name of the University nor the names of its contributors 151590Srgrimes * may be used to endorse or promote products derived from this software 161590Srgrimes * without specific prior written permission. 171590Srgrimes * 181590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 191590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 201590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 211590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 221590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 231590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 241590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 251590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 261590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 271590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 281590Srgrimes * SUCH DAMAGE. 291590Srgrimes */ 301590Srgrimes 3199112Sobrien#include <sys/cdefs.h> 3299112Sobrien__FBSDID("$FreeBSD: head/usr.bin/fstat/fstat.c 221807 2011-05-12 10:11:39Z stas $"); 331590Srgrimes 341590Srgrimes#include <sys/param.h> 351590Srgrimes#include <sys/user.h> 361590Srgrimes#include <sys/stat.h> 371590Srgrimes#include <sys/socket.h> 381590Srgrimes#include <sys/socketvar.h> 391590Srgrimes#include <sys/sysctl.h> 407726Sdg#include <sys/queue.h> 411590Srgrimes 421590Srgrimes#include <netinet/in.h> 431590Srgrimes 44221807Sstas#include <assert.h> 451590Srgrimes#include <ctype.h> 4627272Scharnier#include <err.h> 47221807Sstas#include <libprocstat.h> 4823693Speter#include <limits.h> 491590Srgrimes#include <pwd.h> 501590Srgrimes#include <stdio.h> 511590Srgrimes#include <stdlib.h> 52179828Skib#include <stddef.h> 531590Srgrimes#include <string.h> 5423693Speter#include <unistd.h> 5548463Sru#include <netdb.h> 561590Srgrimes 57221807Sstas#include "functions.h" 5858125Sgreen 591590Srgrimesint fsflg, /* show files on same filesystem as file(s) argument */ 601590Srgrimes pflg, /* show files open by a particular pid */ 611590Srgrimes uflg; /* show files open by a particular (effective) user */ 621590Srgrimesint checkfile; /* true if restricting to particular files or filesystems */ 631590Srgrimesint nflg; /* (numerical) display f.s. and rdev as dev_t */ 6459029Sgreenint mflg; /* include memory-mapped files */ 65221807Sstasint vflg; /* be verbose */ 661590Srgrimes 67221807Sstastypedef struct devs { 68221807Sstas struct devs *next; 69221807Sstas uint32_t fsid; 70221807Sstas uint64_t ino; 71221807Sstas const char *name; 72221807Sstas} DEVS; 731590Srgrimes 74221807SstasDEVS *devs; 7597946Sdeschar *memf, *nlistf; 761590Srgrimes 77221807Sstasstatic int getfname(const char *filename); 78221807Sstasstatic void dofiles(struct procstat *procstat, struct kinfo_proc *p); 79221807Sstasstatic void print_access_flags(int flags); 80221807Sstasstatic void print_file_info(struct procstat *procstat, 81221807Sstas struct filestat *fst, const char *uname, const char *cmd, int pid); 82221807Sstasstatic void print_pipe_info(struct procstat *procstat, 83221807Sstas struct filestat *fst); 84221807Sstasstatic void print_pts_info(struct procstat *procstat, 85221807Sstas struct filestat *fst); 86221807Sstasstatic void print_socket_info(struct procstat *procstat, 87221807Sstas struct filestat *fst); 88221807Sstasstatic void print_vnode_info(struct procstat *procstat, 89221807Sstas struct filestat *fst); 90221807Sstasstatic void usage(void) __dead2; 911590Srgrimes 9217808Speterint 93221807Sstasdo_fstat(int argc, char **argv) 941590Srgrimes{ 95221807Sstas struct kinfo_proc *p; 9693427Sdwmalone struct passwd *passwd; 97221807Sstas struct procstat *procstat; 981590Srgrimes int arg, ch, what; 99221807Sstas int cnt, i; 1001590Srgrimes 1011590Srgrimes arg = 0; 102167367Semaste what = KERN_PROC_PROC; 1031590Srgrimes nlistf = memf = NULL; 10459029Sgreen while ((ch = getopt(argc, argv, "fmnp:u:vN:M:")) != -1) 1051590Srgrimes switch((char)ch) { 1061590Srgrimes case 'f': 1071590Srgrimes fsflg = 1; 1081590Srgrimes break; 1091590Srgrimes case 'M': 1101590Srgrimes memf = optarg; 1111590Srgrimes break; 1121590Srgrimes case 'N': 1131590Srgrimes nlistf = optarg; 1141590Srgrimes break; 11559029Sgreen case 'm': 11659029Sgreen mflg = 1; 11759029Sgreen break; 1181590Srgrimes case 'n': 1191590Srgrimes nflg = 1; 1201590Srgrimes break; 1211590Srgrimes case 'p': 1221590Srgrimes if (pflg++) 1231590Srgrimes usage(); 1241590Srgrimes if (!isdigit(*optarg)) { 12527311Scharnier warnx("-p requires a process id"); 1261590Srgrimes usage(); 1271590Srgrimes } 1281590Srgrimes what = KERN_PROC_PID; 1291590Srgrimes arg = atoi(optarg); 1301590Srgrimes break; 1311590Srgrimes case 'u': 1321590Srgrimes if (uflg++) 1331590Srgrimes usage(); 13427272Scharnier if (!(passwd = getpwnam(optarg))) 13527272Scharnier errx(1, "%s: unknown uid", optarg); 1361590Srgrimes what = KERN_PROC_UID; 1371590Srgrimes arg = passwd->pw_uid; 1381590Srgrimes break; 1391590Srgrimes case 'v': 1401590Srgrimes vflg = 1; 1411590Srgrimes break; 1421590Srgrimes case '?': 1431590Srgrimes default: 1441590Srgrimes usage(); 1451590Srgrimes } 1461590Srgrimes 1471590Srgrimes if (*(argv += optind)) { 1481590Srgrimes for (; *argv; ++argv) { 1491590Srgrimes if (getfname(*argv)) 1501590Srgrimes checkfile = 1; 1511590Srgrimes } 1521590Srgrimes if (!checkfile) /* file(s) specified, but none accessable */ 1531590Srgrimes exit(1); 1541590Srgrimes } 1551590Srgrimes 1568874Srgrimes if (fsflg && !checkfile) { 1571590Srgrimes /* -f with no files means use wd */ 1581590Srgrimes if (getfname(".") == 0) 1591590Srgrimes exit(1); 1601590Srgrimes checkfile = 1; 1611590Srgrimes } 1621590Srgrimes 16397946Sdes if (memf != NULL) 164221807Sstas procstat = procstat_open_kvm(nlistf, memf); 16597946Sdes else 166221807Sstas procstat = procstat_open_sysctl(); 167221807Sstas if (procstat == NULL) 168221807Sstas errx(1, "procstat_open()"); 169221807Sstas p = procstat_getprocs(procstat, what, arg, &cnt); 170221807Sstas if (p == NULL) 171221807Sstas errx(1, "procstat_getprocs()"); 17297946Sdes 173221807Sstas /* 174221807Sstas * Print header. 175221807Sstas */ 17697946Sdes if (nflg) 17797946Sdes printf("%s", 17897946Sdes"USER CMD PID FD DEV INUM MODE SZ|DV R/W"); 17997946Sdes else 18097946Sdes printf("%s", 18197946Sdes"USER CMD PID FD MOUNT INUM MODE SZ|DV R/W"); 18297946Sdes if (checkfile && fsflg == 0) 18397946Sdes printf(" NAME\n"); 18497946Sdes else 18597946Sdes putchar('\n'); 18697946Sdes 1871590Srgrimes /* 188221807Sstas * Go through the process list. 1891590Srgrimes */ 190221807Sstas for (i = 0; i < cnt; i++) { 191221807Sstas if (p[i].ki_stat == SZOMB) 1921590Srgrimes continue; 193221807Sstas dofiles(procstat, &p[i]); 1941590Srgrimes } 195221807Sstas procstat_freeprocs(procstat, p); 196221807Sstas procstat_close(procstat); 197221807Sstas return (0); 1981590Srgrimes} 1991590Srgrimes 20097946Sdesstatic void 201221807Sstasdofiles(struct procstat *procstat, struct kinfo_proc *kp) 20297946Sdes{ 203221807Sstas const char *cmd; 204221807Sstas const char *uname; 205221807Sstas struct filestat *fst; 206221807Sstas struct filestat_list *head; 207221807Sstas int pid; 20897946Sdes 209221807Sstas uname = user_from_uid(kp->ki_uid, 0); 210221807Sstas pid = kp->ki_pid; 211221807Sstas cmd = kp->ki_comm; 21297946Sdes 213221807Sstas head = procstat_getfiles(procstat, kp, mflg); 214221807Sstas if (head == NULL) 2151590Srgrimes return; 216221807Sstas STAILQ_FOREACH(fst, head, next) 217221807Sstas print_file_info(procstat, fst, uname, cmd, pid); 218221807Sstas procstat_freefiles(procstat, head); 219221807Sstas} 220140958Sphk 221140958Sphk 222221807Sstasstatic void 223221807Sstasprint_file_info(struct procstat *procstat, struct filestat *fst, 224221807Sstas const char *uname, const char *cmd, int pid) 22559029Sgreen{ 226221807Sstas struct vnstat vn; 227221807Sstas DEVS *d; 228221807Sstas const char *filename; 229221807Sstas int error, fsmatch = 0; 230221807Sstas char errbuf[_POSIX2_LINE_MAX]; 23159029Sgreen 232221807Sstas filename = NULL; 233221807Sstas if (checkfile != 0) { 234221807Sstas if (fst->fs_type != PS_FST_TYPE_VNODE && 235221807Sstas fst->fs_type != PS_FST_TYPE_FIFO) 23659029Sgreen return; 237221807Sstas error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); 238221807Sstas if (error != 0) 239221807Sstas return; 24059029Sgreen 2411590Srgrimes for (d = devs; d != NULL; d = d->next) 242221807Sstas if (d->fsid == vn.vn_fsid) { 2431590Srgrimes fsmatch = 1; 244221807Sstas if ((unsigned)d->ino == vn.vn_fileid) { 2451590Srgrimes filename = d->name; 2461590Srgrimes break; 2471590Srgrimes } 2481590Srgrimes } 2491590Srgrimes if (fsmatch == 0 || (filename == NULL && fsflg == 0)) 2501590Srgrimes return; 2511590Srgrimes } 2521590Srgrimes 25353133Sgreen /* 254221807Sstas * Print entry prefix. 25553133Sgreen */ 256221807Sstas printf("%-8.8s %-10s %5d", uname, cmd, pid); 257221807Sstas if (fst->fs_uflags & PS_FST_UFLAG_TEXT) 258221807Sstas printf(" text"); 259221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_CDIR) 260221807Sstas printf(" wd"); 261221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_RDIR) 262221807Sstas printf(" root"); 263221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_TRACE) 264221807Sstas printf(" tr"); 265221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_MMAP) 266221807Sstas printf(" mmap"); 267221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_JAIL) 268221807Sstas printf(" jail"); 269221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_CTTY) 270221807Sstas printf(" ctty"); 271116780Sjmg else 272221807Sstas printf(" %4d", fst->fs_fd); 2731590Srgrimes 274221807Sstas /* 275221807Sstas * Print type-specific data. 276221807Sstas */ 277221807Sstas switch (fst->fs_type) { 278221807Sstas case PS_FST_TYPE_FIFO: 279221807Sstas case PS_FST_TYPE_VNODE: 280221807Sstas print_vnode_info(procstat, fst); 2811590Srgrimes break; 282221807Sstas case PS_FST_TYPE_SOCKET: 283221807Sstas print_socket_info(procstat, fst); 2841590Srgrimes break; 285221807Sstas case PS_FST_TYPE_PIPE: 286221807Sstas print_pipe_info(procstat, fst); 2871590Srgrimes break; 288221807Sstas case PS_FST_TYPE_PTS: 289221807Sstas print_pts_info(procstat, fst); 2901590Srgrimes break; 291221807Sstas default: 292221807Sstas if (vflg) 293221807Sstas fprintf(stderr, 294221807Sstas "unknown file type %d for file %d of pid %d\n", 295221807Sstas fst->fs_type, fst->fs_fd, pid); 2961590Srgrimes } 297221807Sstas if (filename && !fsflg) 298221807Sstas printf(" %s", filename); 29917808Speter putchar('\n'); 30017808Speter} 30117808Speter 302221807Sstasstatic void 303221807Sstasprint_socket_info(struct procstat *procstat, struct filestat *fst) 3041590Srgrimes{ 30593427Sdwmalone static const char *stypename[] = { 3061590Srgrimes "unused", /* 0 */ 307221807Sstas "stream", /* 1 */ 3081590Srgrimes "dgram", /* 2 */ 3091590Srgrimes "raw", /* 3 */ 3101590Srgrimes "rdm", /* 4 */ 3111590Srgrimes "seqpak" /* 5 */ 3121590Srgrimes }; 313221807Sstas#define STYPEMAX 5 314221807Sstas struct sockstat sock; 315221807Sstas struct protoent *pe; 316221807Sstas char errbuf[_POSIX2_LINE_MAX]; 317221807Sstas int error; 318221807Sstas static int isopen; 3191590Srgrimes 320221807Sstas error = procstat_get_socket_info(procstat, fst, &sock, errbuf); 321221807Sstas if (error != 0) { 322221807Sstas printf("* error"); 323221807Sstas return; 3241590Srgrimes } 325221807Sstas if (sock.type > STYPEMAX) 326221807Sstas printf("* %s ?%d", sock.dname, sock.type); 3271590Srgrimes else 328221807Sstas printf("* %s %s", sock.dname, stypename[sock.type]); 3291590Srgrimes 3308874Srgrimes /* 3311590Srgrimes * protocol specific formatting 3321590Srgrimes * 3331590Srgrimes * Try to find interesting things to print. For tcp, the interesting 3341590Srgrimes * thing is the address of the tcpcb, for udp and others, just the 3351590Srgrimes * inpcb (socket pcb). For unix domain, its the address of the socket 3361590Srgrimes * pcb and the address of the connected pcb (if connected). Otherwise 3371590Srgrimes * just print the protocol number and address of the socket itself. 3381590Srgrimes * The idea is not to duplicate netstat, but to make available enough 3391590Srgrimes * information for further analysis. 3401590Srgrimes */ 341221807Sstas switch (sock.dom_family) { 3421590Srgrimes case AF_INET: 34357345Sshin case AF_INET6: 344221807Sstas if (!isopen) 345221807Sstas setprotoent(++isopen); 346221807Sstas if ((pe = getprotobynumber(sock.proto)) != NULL) 347221807Sstas printf(" %s", pe->p_name); 348221807Sstas else 349221807Sstas printf(" %d", sock.proto); 350221807Sstas if (sock.proto == IPPROTO_TCP ) { 351221807Sstas if (sock.inp_ppcb != 0) 352221807Sstas printf(" %lx", (u_long)sock.inp_ppcb); 3531590Srgrimes } 354221807Sstas else if (sock.so_pcb != 0) 355221807Sstas printf(" %lx", (u_long)sock.so_pcb); 3561590Srgrimes break; 3571590Srgrimes case AF_UNIX: 3581590Srgrimes /* print address of pcb and connected pcb */ 359221807Sstas if (sock.so_pcb != 0) { 360221807Sstas printf(" %lx", (u_long)sock.so_pcb); 361221807Sstas if (sock.unp_conn) { 3621590Srgrimes char shoconn[4], *cp; 3631590Srgrimes 3641590Srgrimes cp = shoconn; 365221807Sstas if (!(sock.so_rcv_sb_state & SBS_CANTRCVMORE)) 3661590Srgrimes *cp++ = '<'; 3671590Srgrimes *cp++ = '-'; 368221807Sstas if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE)) 3691590Srgrimes *cp++ = '>'; 3701590Srgrimes *cp = '\0'; 37180355Smjacob printf(" %s %lx", shoconn, 372221807Sstas (u_long)sock.unp_conn); 373221807Sstas } 3741590Srgrimes } 3751590Srgrimes break; 3761590Srgrimes default: 3771590Srgrimes /* print protocol number and socket address */ 378221807Sstas printf(" %d %lx", sock.proto, (u_long)sock.so_addr); 3791590Srgrimes } 3801590Srgrimes} 3811590Srgrimes 382221807Sstasstatic void 383221807Sstasprint_pipe_info(struct procstat *procstat, struct filestat *fst) 384181905Sed{ 385221807Sstas struct pipestat ps; 386221807Sstas char errbuf[_POSIX2_LINE_MAX]; 387221807Sstas int error; 38853133Sgreen 389221807Sstas error = procstat_get_pipe_info(procstat, fst, &ps, errbuf); 390221807Sstas if (error != 0) { 391221807Sstas printf("* error"); 392221807Sstas return; 393181905Sed } 394221807Sstas printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer); 395221807Sstas printf(" %6zd", ps.buffer_cnt); 396221807Sstas print_access_flags(fst->fs_fflags); 397221807Sstas} 398181905Sed 399221807Sstasstatic void 400221807Sstasprint_pts_info(struct procstat *procstat, struct filestat *fst) 401221807Sstas{ 402221807Sstas struct ptsstat pts; 403221807Sstas char errbuf[_POSIX2_LINE_MAX]; 404221807Sstas int error; 405221807Sstas 406221807Sstas error = procstat_get_pts_info(procstat, fst, &pts, errbuf); 407221807Sstas if (error != 0) { 408221807Sstas printf("* error"); 409221807Sstas return; 410181905Sed } 411181905Sed printf("* pseudo-terminal master "); 412221807Sstas if (nflg || !*pts.devname) { 413221807Sstas printf("%10d,%-2d", major(pts.dev), minor(pts.dev)); 414181905Sed } else { 415221807Sstas printf("%10s", pts.devname); 416181905Sed } 417221807Sstas print_access_flags(fst->fs_fflags); 418181905Sed} 419181905Sed 420221807Sstasstatic void 421221807Sstasprint_vnode_info(struct procstat *procstat, struct filestat *fst) 42253133Sgreen{ 423221807Sstas struct vnstat vn; 424221807Sstas char errbuf[_POSIX2_LINE_MAX]; 425221807Sstas char mode[15]; 426221807Sstas const char *badtype; 427221807Sstas int error; 42853133Sgreen 429221807Sstas badtype = NULL; 430221807Sstas error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); 431221807Sstas if (error != 0) 432221807Sstas badtype = errbuf; 433221807Sstas else if (vn.vn_type == PS_FST_VTYPE_VBAD) 434221807Sstas badtype = "bad"; 435221807Sstas else if (vn.vn_type == PS_FST_VTYPE_VNON) 436221807Sstas badtype = "none"; 437221807Sstas if (badtype != NULL) { 438221807Sstas printf(" - - %10s -", badtype); 439221807Sstas return; 44053133Sgreen } 441221807Sstas 442221807Sstas if (nflg) 443221807Sstas printf(" %2d,%-2d", major(vn.vn_fsid), minor(vn.vn_fsid)); 444221807Sstas else if (vn.vn_mntdir != NULL) 445221807Sstas (void)printf(" %-8s", vn.vn_mntdir); 446221807Sstas 447221807Sstas /* 448221807Sstas * Print access mode. 449221807Sstas */ 450221807Sstas if (nflg) 451221807Sstas (void)snprintf(mode, sizeof(mode), "%o", vn.vn_mode); 452221807Sstas else { 453221807Sstas strmode(vn.vn_mode, mode); 454221807Sstas } 455221807Sstas (void)printf(" %6ld %10s", vn.vn_fileid, mode); 456221807Sstas 457221807Sstas if (vn.vn_type == PS_FST_VTYPE_VBLK || vn.vn_type == PS_FST_VTYPE_VCHR) { 458221807Sstas if (nflg || !*vn.vn_devname) 459221807Sstas printf(" %2d,%-2d", major(vn.vn_dev), minor(vn.vn_dev)); 460221807Sstas else { 461221807Sstas printf(" %6s", vn.vn_devname); 462221807Sstas } 463221807Sstas } else 464221807Sstas printf(" %6lu", vn.vn_size); 465221807Sstas print_access_flags(fst->fs_fflags); 46653133Sgreen} 46753133Sgreen 468221807Sstasstatic void 469221807Sstasprint_access_flags(int flags) 4701590Srgrimes{ 471221807Sstas char rw[3]; 4721590Srgrimes 473221807Sstas rw[0] = '\0'; 474221807Sstas if (flags & PS_FST_FFLAG_READ) 475221807Sstas strcat(rw, "r"); 476221807Sstas if (flags & PS_FST_FFLAG_WRITE) 477221807Sstas strcat(rw, "w"); 478221807Sstas printf(" %2s", rw); 4791590Srgrimes} 4801590Srgrimes 48117808Speterint 482131293Sdwmalonegetfname(const char *filename) 4831590Srgrimes{ 4841590Srgrimes struct stat statbuf; 4851590Srgrimes DEVS *cur; 4861590Srgrimes 4871590Srgrimes if (stat(filename, &statbuf)) { 48827272Scharnier warn("%s", filename); 489221807Sstas return (0); 4901590Srgrimes } 49127272Scharnier if ((cur = malloc(sizeof(DEVS))) == NULL) 49227272Scharnier err(1, NULL); 4931590Srgrimes cur->next = devs; 4941590Srgrimes devs = cur; 4951590Srgrimes 4961590Srgrimes cur->ino = statbuf.st_ino; 49786100Sdwmalone cur->fsid = statbuf.st_dev; 4981590Srgrimes cur->name = filename; 499221807Sstas return (1); 5001590Srgrimes} 5011590Srgrimes 502221807Sstasstatic void 503131293Sdwmaloneusage(void) 5041590Srgrimes{ 5051590Srgrimes (void)fprintf(stderr, 506146466Sru "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] [file ...]\n"); 5071590Srgrimes exit(1); 5081590Srgrimes} 509