fstat.c revision 225893
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 225893 2011-10-01 09:16:07Z ed $"); 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> 50221816Simp#include <stdint.h> 511590Srgrimes#include <stdio.h> 521590Srgrimes#include <stdlib.h> 53179828Skib#include <stddef.h> 541590Srgrimes#include <string.h> 5523693Speter#include <unistd.h> 5648463Sru#include <netdb.h> 571590Srgrimes 58221807Sstas#include "functions.h" 5958125Sgreen 601590Srgrimesint fsflg, /* show files on same filesystem as file(s) argument */ 611590Srgrimes pflg, /* show files open by a particular pid */ 621590Srgrimes uflg; /* show files open by a particular (effective) user */ 631590Srgrimesint checkfile; /* true if restricting to particular files or filesystems */ 641590Srgrimesint nflg; /* (numerical) display f.s. and rdev as dev_t */ 6559029Sgreenint mflg; /* include memory-mapped files */ 66221807Sstasint vflg; /* be verbose */ 671590Srgrimes 68221807Sstastypedef struct devs { 69221807Sstas struct devs *next; 70221807Sstas uint32_t fsid; 71221807Sstas uint64_t ino; 72221807Sstas const char *name; 73221807Sstas} DEVS; 741590Srgrimes 75221807SstasDEVS *devs; 7697946Sdeschar *memf, *nlistf; 771590Srgrimes 78221807Sstasstatic int getfname(const char *filename); 79221807Sstasstatic void dofiles(struct procstat *procstat, struct kinfo_proc *p); 80221807Sstasstatic void print_access_flags(int flags); 81221807Sstasstatic void print_file_info(struct procstat *procstat, 82221807Sstas struct filestat *fst, const char *uname, const char *cmd, int pid); 83221807Sstasstatic void print_pipe_info(struct procstat *procstat, 84221807Sstas struct filestat *fst); 85221807Sstasstatic void print_pts_info(struct procstat *procstat, 86221807Sstas struct filestat *fst); 87221807Sstasstatic void print_socket_info(struct procstat *procstat, 88221807Sstas struct filestat *fst); 89221807Sstasstatic void print_vnode_info(struct procstat *procstat, 90221807Sstas struct filestat *fst); 91221807Sstasstatic void usage(void) __dead2; 921590Srgrimes 9317808Speterint 94221807Sstasdo_fstat(int argc, char **argv) 951590Srgrimes{ 96221807Sstas struct kinfo_proc *p; 9793427Sdwmalone struct passwd *passwd; 98221807Sstas struct procstat *procstat; 991590Srgrimes int arg, ch, what; 100221807Sstas int cnt, i; 1011590Srgrimes 1021590Srgrimes arg = 0; 103167367Semaste what = KERN_PROC_PROC; 1041590Srgrimes nlistf = memf = NULL; 10559029Sgreen while ((ch = getopt(argc, argv, "fmnp:u:vN:M:")) != -1) 1061590Srgrimes switch((char)ch) { 1071590Srgrimes case 'f': 1081590Srgrimes fsflg = 1; 1091590Srgrimes break; 1101590Srgrimes case 'M': 1111590Srgrimes memf = optarg; 1121590Srgrimes break; 1131590Srgrimes case 'N': 1141590Srgrimes nlistf = optarg; 1151590Srgrimes break; 11659029Sgreen case 'm': 11759029Sgreen mflg = 1; 11859029Sgreen break; 1191590Srgrimes case 'n': 1201590Srgrimes nflg = 1; 1211590Srgrimes break; 1221590Srgrimes case 'p': 1231590Srgrimes if (pflg++) 1241590Srgrimes usage(); 1251590Srgrimes if (!isdigit(*optarg)) { 12627311Scharnier warnx("-p requires a process id"); 1271590Srgrimes usage(); 1281590Srgrimes } 1291590Srgrimes what = KERN_PROC_PID; 1301590Srgrimes arg = atoi(optarg); 1311590Srgrimes break; 1321590Srgrimes case 'u': 1331590Srgrimes if (uflg++) 1341590Srgrimes usage(); 13527272Scharnier if (!(passwd = getpwnam(optarg))) 13627272Scharnier errx(1, "%s: unknown uid", optarg); 1371590Srgrimes what = KERN_PROC_UID; 1381590Srgrimes arg = passwd->pw_uid; 1391590Srgrimes break; 1401590Srgrimes case 'v': 1411590Srgrimes vflg = 1; 1421590Srgrimes break; 1431590Srgrimes case '?': 1441590Srgrimes default: 1451590Srgrimes usage(); 1461590Srgrimes } 1471590Srgrimes 1481590Srgrimes if (*(argv += optind)) { 1491590Srgrimes for (; *argv; ++argv) { 1501590Srgrimes if (getfname(*argv)) 1511590Srgrimes checkfile = 1; 1521590Srgrimes } 1531590Srgrimes if (!checkfile) /* file(s) specified, but none accessable */ 1541590Srgrimes exit(1); 1551590Srgrimes } 1561590Srgrimes 1578874Srgrimes if (fsflg && !checkfile) { 1581590Srgrimes /* -f with no files means use wd */ 1591590Srgrimes if (getfname(".") == 0) 1601590Srgrimes exit(1); 1611590Srgrimes checkfile = 1; 1621590Srgrimes } 1631590Srgrimes 16497946Sdes if (memf != NULL) 165221807Sstas procstat = procstat_open_kvm(nlistf, memf); 16697946Sdes else 167221807Sstas procstat = procstat_open_sysctl(); 168221807Sstas if (procstat == NULL) 169221807Sstas errx(1, "procstat_open()"); 170221807Sstas p = procstat_getprocs(procstat, what, arg, &cnt); 171221807Sstas if (p == NULL) 172221807Sstas errx(1, "procstat_getprocs()"); 17397946Sdes 174221807Sstas /* 175221807Sstas * Print header. 176221807Sstas */ 17797946Sdes if (nflg) 17897946Sdes printf("%s", 17997946Sdes"USER CMD PID FD DEV INUM MODE SZ|DV R/W"); 18097946Sdes else 18197946Sdes printf("%s", 18297946Sdes"USER CMD PID FD MOUNT INUM MODE SZ|DV R/W"); 18397946Sdes if (checkfile && fsflg == 0) 18497946Sdes printf(" NAME\n"); 18597946Sdes else 18697946Sdes putchar('\n'); 18797946Sdes 1881590Srgrimes /* 189221807Sstas * Go through the process list. 1901590Srgrimes */ 191221807Sstas for (i = 0; i < cnt; i++) { 192221807Sstas if (p[i].ki_stat == SZOMB) 1931590Srgrimes continue; 194221807Sstas dofiles(procstat, &p[i]); 1951590Srgrimes } 196221807Sstas procstat_freeprocs(procstat, p); 197221807Sstas procstat_close(procstat); 198221807Sstas return (0); 1991590Srgrimes} 2001590Srgrimes 20197946Sdesstatic void 202221807Sstasdofiles(struct procstat *procstat, struct kinfo_proc *kp) 20397946Sdes{ 204221807Sstas const char *cmd; 205221807Sstas const char *uname; 206221807Sstas struct filestat *fst; 207221807Sstas struct filestat_list *head; 208221807Sstas int pid; 20997946Sdes 210221807Sstas uname = user_from_uid(kp->ki_uid, 0); 211221807Sstas pid = kp->ki_pid; 212221807Sstas cmd = kp->ki_comm; 21397946Sdes 214221807Sstas head = procstat_getfiles(procstat, kp, mflg); 215221807Sstas if (head == NULL) 2161590Srgrimes return; 217221807Sstas STAILQ_FOREACH(fst, head, next) 218221807Sstas print_file_info(procstat, fst, uname, cmd, pid); 219221807Sstas procstat_freefiles(procstat, head); 220221807Sstas} 221140958Sphk 222140958Sphk 223221807Sstasstatic void 224221807Sstasprint_file_info(struct procstat *procstat, struct filestat *fst, 225221807Sstas const char *uname, const char *cmd, int pid) 22659029Sgreen{ 227221807Sstas struct vnstat vn; 228221807Sstas DEVS *d; 229221807Sstas const char *filename; 230221807Sstas int error, fsmatch = 0; 231221807Sstas char errbuf[_POSIX2_LINE_MAX]; 23259029Sgreen 233221807Sstas filename = NULL; 234221807Sstas if (checkfile != 0) { 235221807Sstas if (fst->fs_type != PS_FST_TYPE_VNODE && 236221807Sstas fst->fs_type != PS_FST_TYPE_FIFO) 23759029Sgreen return; 238221807Sstas error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); 239221807Sstas if (error != 0) 240221807Sstas return; 24159029Sgreen 2421590Srgrimes for (d = devs; d != NULL; d = d->next) 243221807Sstas if (d->fsid == vn.vn_fsid) { 2441590Srgrimes fsmatch = 1; 245221807Sstas if ((unsigned)d->ino == vn.vn_fileid) { 2461590Srgrimes filename = d->name; 2471590Srgrimes break; 2481590Srgrimes } 2491590Srgrimes } 2501590Srgrimes if (fsmatch == 0 || (filename == NULL && fsflg == 0)) 2511590Srgrimes return; 2521590Srgrimes } 2531590Srgrimes 25453133Sgreen /* 255221807Sstas * Print entry prefix. 25653133Sgreen */ 257221807Sstas printf("%-8.8s %-10s %5d", uname, cmd, pid); 258221807Sstas if (fst->fs_uflags & PS_FST_UFLAG_TEXT) 259221807Sstas printf(" text"); 260221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_CDIR) 261221807Sstas printf(" wd"); 262221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_RDIR) 263221807Sstas printf(" root"); 264221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_TRACE) 265221807Sstas printf(" tr"); 266221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_MMAP) 267221807Sstas printf(" mmap"); 268221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_JAIL) 269221807Sstas printf(" jail"); 270221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_CTTY) 271221807Sstas printf(" ctty"); 272116780Sjmg else 273221807Sstas printf(" %4d", fst->fs_fd); 2741590Srgrimes 275221807Sstas /* 276221807Sstas * Print type-specific data. 277221807Sstas */ 278221807Sstas switch (fst->fs_type) { 279221807Sstas case PS_FST_TYPE_FIFO: 280221807Sstas case PS_FST_TYPE_VNODE: 281221807Sstas print_vnode_info(procstat, fst); 2821590Srgrimes break; 283221807Sstas case PS_FST_TYPE_SOCKET: 284221807Sstas print_socket_info(procstat, fst); 2851590Srgrimes break; 286221807Sstas case PS_FST_TYPE_PIPE: 287221807Sstas print_pipe_info(procstat, fst); 2881590Srgrimes break; 289221807Sstas case PS_FST_TYPE_PTS: 290221807Sstas print_pts_info(procstat, fst); 2911590Srgrimes break; 292221807Sstas default: 293221807Sstas if (vflg) 294221807Sstas fprintf(stderr, 295221807Sstas "unknown file type %d for file %d of pid %d\n", 296221807Sstas fst->fs_type, fst->fs_fd, pid); 2971590Srgrimes } 298221807Sstas if (filename && !fsflg) 299221807Sstas printf(" %s", filename); 30017808Speter putchar('\n'); 30117808Speter} 30217808Speter 303221807Sstasstatic void 304221807Sstasprint_socket_info(struct procstat *procstat, struct filestat *fst) 3051590Srgrimes{ 30693427Sdwmalone static const char *stypename[] = { 3071590Srgrimes "unused", /* 0 */ 308221807Sstas "stream", /* 1 */ 3091590Srgrimes "dgram", /* 2 */ 3101590Srgrimes "raw", /* 3 */ 3111590Srgrimes "rdm", /* 4 */ 3121590Srgrimes "seqpak" /* 5 */ 3131590Srgrimes }; 314221807Sstas#define STYPEMAX 5 315221807Sstas struct sockstat sock; 316221807Sstas struct protoent *pe; 317221807Sstas char errbuf[_POSIX2_LINE_MAX]; 318221807Sstas int error; 319221807Sstas static int isopen; 3201590Srgrimes 321221807Sstas error = procstat_get_socket_info(procstat, fst, &sock, errbuf); 322221807Sstas if (error != 0) { 323221807Sstas printf("* error"); 324221807Sstas return; 3251590Srgrimes } 326221807Sstas if (sock.type > STYPEMAX) 327221807Sstas printf("* %s ?%d", sock.dname, sock.type); 3281590Srgrimes else 329221807Sstas printf("* %s %s", sock.dname, stypename[sock.type]); 3301590Srgrimes 3318874Srgrimes /* 3321590Srgrimes * protocol specific formatting 3331590Srgrimes * 3341590Srgrimes * Try to find interesting things to print. For tcp, the interesting 3351590Srgrimes * thing is the address of the tcpcb, for udp and others, just the 3361590Srgrimes * inpcb (socket pcb). For unix domain, its the address of the socket 3371590Srgrimes * pcb and the address of the connected pcb (if connected). Otherwise 3381590Srgrimes * just print the protocol number and address of the socket itself. 3391590Srgrimes * The idea is not to duplicate netstat, but to make available enough 3401590Srgrimes * information for further analysis. 3411590Srgrimes */ 342221807Sstas switch (sock.dom_family) { 3431590Srgrimes case AF_INET: 34457345Sshin case AF_INET6: 345221807Sstas if (!isopen) 346221807Sstas setprotoent(++isopen); 347221807Sstas if ((pe = getprotobynumber(sock.proto)) != NULL) 348221807Sstas printf(" %s", pe->p_name); 349221807Sstas else 350221807Sstas printf(" %d", sock.proto); 351221807Sstas if (sock.proto == IPPROTO_TCP ) { 352221807Sstas if (sock.inp_ppcb != 0) 353221807Sstas printf(" %lx", (u_long)sock.inp_ppcb); 3541590Srgrimes } 355221807Sstas else if (sock.so_pcb != 0) 356221807Sstas printf(" %lx", (u_long)sock.so_pcb); 3571590Srgrimes break; 3581590Srgrimes case AF_UNIX: 3591590Srgrimes /* print address of pcb and connected pcb */ 360221807Sstas if (sock.so_pcb != 0) { 361221807Sstas printf(" %lx", (u_long)sock.so_pcb); 362221807Sstas if (sock.unp_conn) { 3631590Srgrimes char shoconn[4], *cp; 3641590Srgrimes 3651590Srgrimes cp = shoconn; 366221807Sstas if (!(sock.so_rcv_sb_state & SBS_CANTRCVMORE)) 3671590Srgrimes *cp++ = '<'; 3681590Srgrimes *cp++ = '-'; 369221807Sstas if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE)) 3701590Srgrimes *cp++ = '>'; 3711590Srgrimes *cp = '\0'; 37280355Smjacob printf(" %s %lx", shoconn, 373221807Sstas (u_long)sock.unp_conn); 374221807Sstas } 3751590Srgrimes } 3761590Srgrimes break; 3771590Srgrimes default: 3781590Srgrimes /* print protocol number and socket address */ 379221807Sstas printf(" %d %lx", sock.proto, (u_long)sock.so_addr); 3801590Srgrimes } 3811590Srgrimes} 3821590Srgrimes 383221807Sstasstatic void 384221807Sstasprint_pipe_info(struct procstat *procstat, struct filestat *fst) 385181905Sed{ 386221807Sstas struct pipestat ps; 387221807Sstas char errbuf[_POSIX2_LINE_MAX]; 388221807Sstas int error; 38953133Sgreen 390221807Sstas error = procstat_get_pipe_info(procstat, fst, &ps, errbuf); 391221807Sstas if (error != 0) { 392221807Sstas printf("* error"); 393221807Sstas return; 394181905Sed } 395221807Sstas printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer); 396221807Sstas printf(" %6zd", ps.buffer_cnt); 397221807Sstas print_access_flags(fst->fs_fflags); 398221807Sstas} 399181905Sed 400221807Sstasstatic void 401221807Sstasprint_pts_info(struct procstat *procstat, struct filestat *fst) 402221807Sstas{ 403221807Sstas struct ptsstat pts; 404221807Sstas char errbuf[_POSIX2_LINE_MAX]; 405221807Sstas int error; 406221807Sstas 407221807Sstas error = procstat_get_pts_info(procstat, fst, &pts, errbuf); 408221807Sstas if (error != 0) { 409221807Sstas printf("* error"); 410221807Sstas return; 411181905Sed } 412181905Sed printf("* pseudo-terminal master "); 413221807Sstas if (nflg || !*pts.devname) { 414225847Sed printf("%#10jx", (uintmax_t)pts.dev); 415181905Sed } else { 416221807Sstas printf("%10s", pts.devname); 417181905Sed } 418221807Sstas print_access_flags(fst->fs_fflags); 419181905Sed} 420181905Sed 421221807Sstasstatic void 422221807Sstasprint_vnode_info(struct procstat *procstat, struct filestat *fst) 42353133Sgreen{ 424221807Sstas struct vnstat vn; 425221807Sstas char errbuf[_POSIX2_LINE_MAX]; 426221807Sstas char mode[15]; 427221807Sstas const char *badtype; 428221807Sstas int error; 42953133Sgreen 430221807Sstas badtype = NULL; 431221807Sstas error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); 432221807Sstas if (error != 0) 433221807Sstas badtype = errbuf; 434221807Sstas else if (vn.vn_type == PS_FST_VTYPE_VBAD) 435221807Sstas badtype = "bad"; 436221807Sstas else if (vn.vn_type == PS_FST_VTYPE_VNON) 437221807Sstas badtype = "none"; 438221807Sstas if (badtype != NULL) { 439221807Sstas printf(" - - %10s -", badtype); 440221807Sstas return; 44153133Sgreen } 442221807Sstas 443221807Sstas if (nflg) 444225893Sed printf(" %#5jx", (uintmax_t)vn.vn_fsid); 445221807Sstas else if (vn.vn_mntdir != NULL) 446221807Sstas (void)printf(" %-8s", vn.vn_mntdir); 447221807Sstas 448221807Sstas /* 449221807Sstas * Print access mode. 450221807Sstas */ 451221807Sstas if (nflg) 452221807Sstas (void)snprintf(mode, sizeof(mode), "%o", vn.vn_mode); 453221807Sstas else { 454221807Sstas strmode(vn.vn_mode, mode); 455221807Sstas } 456221816Simp (void)printf(" %6jd %10s", (intmax_t)vn.vn_fileid, mode); 457221807Sstas 458221807Sstas if (vn.vn_type == PS_FST_VTYPE_VBLK || vn.vn_type == PS_FST_VTYPE_VCHR) { 459221807Sstas if (nflg || !*vn.vn_devname) 460225847Sed printf(" %#6jx", (uintmax_t)vn.vn_dev); 461221807Sstas else { 462221807Sstas printf(" %6s", vn.vn_devname); 463221807Sstas } 464221807Sstas } else 465221819Simp printf(" %6ju", (uintmax_t)vn.vn_size); 466221807Sstas print_access_flags(fst->fs_fflags); 46753133Sgreen} 46853133Sgreen 469221807Sstasstatic void 470221807Sstasprint_access_flags(int flags) 4711590Srgrimes{ 472221807Sstas char rw[3]; 4731590Srgrimes 474221807Sstas rw[0] = '\0'; 475221807Sstas if (flags & PS_FST_FFLAG_READ) 476221807Sstas strcat(rw, "r"); 477221807Sstas if (flags & PS_FST_FFLAG_WRITE) 478221807Sstas strcat(rw, "w"); 479221807Sstas printf(" %2s", rw); 4801590Srgrimes} 4811590Srgrimes 48217808Speterint 483131293Sdwmalonegetfname(const char *filename) 4841590Srgrimes{ 4851590Srgrimes struct stat statbuf; 4861590Srgrimes DEVS *cur; 4871590Srgrimes 4881590Srgrimes if (stat(filename, &statbuf)) { 48927272Scharnier warn("%s", filename); 490221807Sstas return (0); 4911590Srgrimes } 49227272Scharnier if ((cur = malloc(sizeof(DEVS))) == NULL) 49327272Scharnier err(1, NULL); 4941590Srgrimes cur->next = devs; 4951590Srgrimes devs = cur; 4961590Srgrimes 4971590Srgrimes cur->ino = statbuf.st_ino; 49886100Sdwmalone cur->fsid = statbuf.st_dev; 4991590Srgrimes cur->name = filename; 500221807Sstas return (1); 5011590Srgrimes} 5021590Srgrimes 503221807Sstasstatic void 504131293Sdwmaloneusage(void) 5051590Srgrimes{ 5061590Srgrimes (void)fprintf(stderr, 507146466Sru "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] [file ...]\n"); 5081590Srgrimes exit(1); 5091590Srgrimes} 510