fstat.c revision 232233
1248619Sdes/*- 260573Skris * Copyright (c) 2009 Stanislav Sedov <stas@FreeBSD.org> 360573Skris * Copyright (c) 1988, 1993 460573Skris * The Regents of the University of California. All rights reserved. 560573Skris * 660573Skris * Redistribution and use in source and binary forms, with or without 760573Skris * modification, are permitted provided that the following conditions 860573Skris * are met: 960573Skris * 1. Redistributions of source code must retain the above copyright 1060573Skris * notice, this list of conditions and the following disclaimer. 1160573Skris * 2. Redistributions in binary form must reproduce the above copyright 1260573Skris * notice, this list of conditions and the following disclaimer in the 1360573Skris * documentation and/or other materials provided with the distribution. 1460573Skris * 4. Neither the name of the University nor the names of its contributors 1560573Skris * may be used to endorse or promote products derived from this software 1660573Skris * without specific prior written permission. 1760573Skris * 1860573Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1960573Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2060573Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2160573Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2260573Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2360573Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2460573Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2565674Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2660573Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27162856Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2860573Skris * SUCH DAMAGE. 29162856Sdes */ 30181111Sdes 31181111Sdes#include <sys/cdefs.h> 32162856Sdes__FBSDID("$FreeBSD: head/usr.bin/fstat/fstat.c 232233 2012-02-27 17:39:34Z pluknet $"); 33181111Sdes 34162856Sdes#include <sys/param.h> 35162856Sdes#include <sys/user.h> 36162856Sdes#include <sys/stat.h> 37181111Sdes#include <sys/socket.h> 38162856Sdes#include <sys/socketvar.h> 39197679Sdes#include <sys/sysctl.h> 40162856Sdes#include <sys/queue.h> 4176262Sgreen 4260573Skris#include <netinet/in.h> 4376262Sgreen 44162856Sdes#include <assert.h> 4560573Skris#include <ctype.h> 4660573Skris#include <err.h> 47162856Sdes#include <libprocstat.h> 48162856Sdes#include <limits.h> 4960573Skris#include <pwd.h> 5060573Skris#include <stdint.h> 5176262Sgreen#include <stdio.h> 52147005Sdes#include <stdlib.h> 53162856Sdes#include <stddef.h> 5460573Skris#include <string.h> 55124211Sdes#include <unistd.h> 56124211Sdes#include <netdb.h> 57124211Sdes 58162856Sdes#include "functions.h" 59124211Sdes 6060573Skrisstatic int fsflg, /* show files on same filesystem as file(s) argument */ 6160573Skris pflg, /* show files open by a particular pid */ 6276262Sgreen uflg; /* show files open by a particular (effective) user */ 63124211Sdesstatic int checkfile; /* restrict to particular files or filesystems */ 64147005Sdesstatic int nflg; /* (numerical) display f.s. and rdev as dev_t */ 6560573Skrisstatic int mflg; /* include memory-mapped files */ 6698684Sdesstatic int vflg; /* be verbose */ 6798684Sdes 6898684Sdestypedef struct devs { 6998684Sdes struct devs *next; 7098684Sdes uint32_t fsid; 7198684Sdes uint64_t ino; 7298684Sdes const char *name; 73124211Sdes} DEVS; 74124211Sdes 75124211Sdesstatic DEVS *devs; 76192595Sdesstatic char *memf, *nlistf; 77192595Sdes 78192595Sdesstatic int getfname(const char *filename); 7998684Sdesstatic void dofiles(struct procstat *procstat, struct kinfo_proc *p); 8098684Sdesstatic void print_access_flags(int flags); 8198684Sdesstatic void print_file_info(struct procstat *procstat, 8298684Sdes struct filestat *fst, const char *uname, const char *cmd, int pid); 83124211Sdesstatic void print_pipe_info(struct procstat *procstat, 84124211Sdes struct filestat *fst); 85124211Sdesstatic void print_pts_info(struct procstat *procstat, 86192595Sdes struct filestat *fst); 87192595Sdesstatic void print_socket_info(struct procstat *procstat, 88192595Sdes struct filestat *fst); 8998684Sdesstatic void print_vnode_info(struct procstat *procstat, 9098684Sdes struct filestat *fst); 9198684Sdesstatic void usage(void) __dead2; 9298684Sdes 9369591Sgreenint 9469591Sgreendo_fstat(int argc, char **argv) 9560573Skris{ 9660573Skris struct kinfo_proc *p; 9792559Sdes struct passwd *passwd; 9892559Sdes struct procstat *procstat; 9960573Skris int arg, ch, what; 10060573Skris int cnt, i; 101248619Sdes 102248619Sdes arg = 0; 103248619Sdes what = KERN_PROC_PROC; 104248619Sdes nlistf = memf = NULL; 10560573Skris while ((ch = getopt(argc, argv, "fmnp:u:vN:M:")) != -1) 106181111Sdes switch((char)ch) { 107181111Sdes case 'f': 108181111Sdes fsflg = 1; 109181111Sdes break; 110181111Sdes case 'M': 111181111Sdes memf = optarg; 112181111Sdes break; 113181111Sdes case 'N': 114181111Sdes nlistf = optarg; 115181111Sdes break; 116181111Sdes case 'm': 117181111Sdes mflg = 1; 118181111Sdes break; 119181111Sdes case 'n': 120240075Sdes nflg = 1; 121181111Sdes break; 122181111Sdes case 'p': 123181111Sdes if (pflg++) 124181111Sdes usage(); 125181111Sdes if (!isdigit(*optarg)) { 126181111Sdes warnx("-p requires a process id"); 127181111Sdes usage(); 128181111Sdes } 129181111Sdes what = KERN_PROC_PID; 130181111Sdes arg = atoi(optarg); 131181111Sdes break; 132181111Sdes case 'u': 133181111Sdes if (uflg++) 134181111Sdes usage(); 135181111Sdes if (!(passwd = getpwnam(optarg))) 136181111Sdes errx(1, "%s: unknown uid", optarg); 137181111Sdes what = KERN_PROC_UID; 138181111Sdes arg = passwd->pw_uid; 139181111Sdes break; 140181111Sdes case 'v': 141181111Sdes vflg = 1; 142181111Sdes break; 143181111Sdes case '?': 144181111Sdes default: 145181111Sdes usage(); 146181111Sdes } 147181111Sdes 148181111Sdes if (*(argv += optind)) { 149181111Sdes for (; *argv; ++argv) { 150181111Sdes if (getfname(*argv)) 151181111Sdes checkfile = 1; 152181111Sdes } 153181111Sdes if (!checkfile) /* file(s) specified, but none accessible */ 154181111Sdes exit(1); 155181111Sdes } 156181111Sdes 157181111Sdes if (fsflg && !checkfile) { 158181111Sdes /* -f with no files means use wd */ 159181111Sdes if (getfname(".") == 0) 160181111Sdes exit(1); 161181111Sdes checkfile = 1; 162181111Sdes } 163181111Sdes 164181111Sdes if (memf != NULL) 165181111Sdes procstat = procstat_open_kvm(nlistf, memf); 166181111Sdes else 167181111Sdes procstat = procstat_open_sysctl(); 168181111Sdes if (procstat == NULL) 169181111Sdes errx(1, "procstat_open()"); 170181111Sdes p = procstat_getprocs(procstat, what, arg, &cnt); 17160573Skris if (p == NULL) 17269591Sgreen errx(1, "procstat_getprocs()"); 17360573Skris 174126277Sdes /* 175126277Sdes * Print header. 17660573Skris */ 17792559Sdes if (nflg) 17860573Skris printf("%s", 17969591Sgreen"USER CMD PID FD DEV INUM MODE SZ|DV R/W"); 18060573Skris else 18160573Skris printf("%s", 182162856Sdes"USER CMD PID FD MOUNT INUM MODE SZ|DV R/W"); 18392559Sdes if (checkfile && fsflg == 0) 18492559Sdes printf(" NAME\n"); 18560573Skris else 18669591Sgreen putchar('\n'); 18776262Sgreen 188106130Sdes /* 189221420Sdes * Go through the process list. 19092559Sdes */ 19160573Skris for (i = 0; i < cnt; i++) { 19269591Sgreen if (p[i].ki_stat == SZOMB) 19369591Sgreen continue; 19469591Sgreen dofiles(procstat, &p[i]); 19560573Skris } 19669591Sgreen procstat_freeprocs(procstat, p); 197106130Sdes procstat_close(procstat); 19860573Skris return (0); 19960573Skris} 20060573Skris 20160573Skrisstatic void 20260573Skrisdofiles(struct procstat *procstat, struct kinfo_proc *kp) 20360573Skris{ 204106130Sdes const char *cmd; 20560573Skris const char *uname; 20660573Skris struct filestat *fst; 20760573Skris struct filestat_list *head; 20860573Skris int pid; 20960573Skris 21060573Skris uname = user_from_uid(kp->ki_uid, 0); 21160573Skris pid = kp->ki_pid; 21260573Skris cmd = kp->ki_comm; 21360573Skris 21460573Skris head = procstat_getfiles(procstat, kp, mflg); 21560573Skris if (head == NULL) 216162856Sdes return; 21792559Sdes STAILQ_FOREACH(fst, head, next) 21892559Sdes print_file_info(procstat, fst, uname, cmd, pid); 21960573Skris procstat_freefiles(procstat, head); 22069591Sgreen} 22169591Sgreen 22276262Sgreen 22360573Skrisstatic void 22499053Sdesprint_file_info(struct procstat *procstat, struct filestat *fst, 22599053Sdes const char *uname, const char *cmd, int pid) 22699053Sdes{ 22760573Skris struct vnstat vn; 228231584Sed DEVS *d; 229231584Sed const char *filename; 23099053Sdes int error, fsmatch = 0; 23199053Sdes char errbuf[_POSIX2_LINE_MAX]; 23269591Sgreen 23369591Sgreen filename = NULL; 23460573Skris if (checkfile != 0) { 235221420Sdes if (fst->fs_type != PS_FST_TYPE_VNODE && 236221420Sdes fst->fs_type != PS_FST_TYPE_FIFO) 237221420Sdes return; 23860573Skris error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); 23976262Sgreen if (error != 0) 24060573Skris return; 24176262Sgreen 24276262Sgreen for (d = devs; d != NULL; d = d->next) 24376262Sgreen if (d->fsid == vn.vn_fsid) { 24476262Sgreen fsmatch = 1; 24569591Sgreen if ((unsigned)d->ino == vn.vn_fileid) { 24698684Sdes filename = d->name; 247128460Sdes break; 24898684Sdes } 24969591Sgreen } 25069591Sgreen if (fsmatch == 0 || (filename == NULL && fsflg == 0)) 25169591Sgreen return; 252137019Sdes } 253124211Sdes 254147005Sdes /* 255147005Sdes * Print entry prefix. 256147005Sdes */ 25760573Skris printf("%-8.8s %-10s %5d", uname, cmd, pid); 258157019Sdes if (fst->fs_uflags & PS_FST_UFLAG_TEXT) 259157019Sdes printf(" text"); 260157019Sdes else if (fst->fs_uflags & PS_FST_UFLAG_CDIR) 261157019Sdes printf(" wd"); 262137019Sdes else if (fst->fs_uflags & PS_FST_UFLAG_RDIR) 26398684Sdes printf(" root"); 26469591Sgreen else if (fst->fs_uflags & PS_FST_UFLAG_TRACE) 26592559Sdes printf(" tr"); 26698684Sdes else if (fst->fs_uflags & PS_FST_UFLAG_MMAP) 26798684Sdes printf(" mmap"); 268181111Sdes else if (fst->fs_uflags & PS_FST_UFLAG_JAIL) 269248619Sdes printf(" jail"); 270248619Sdes else if (fst->fs_uflags & PS_FST_UFLAG_CTTY) 27192559Sdes printf(" ctty"); 27292559Sdes else 27392559Sdes printf(" %4d", fst->fs_fd); 27492559Sdes 27592559Sdes /* 27660573Skris * Print type-specific data. 27799053Sdes */ 27899053Sdes switch (fst->fs_type) { 279231584Sed case PS_FST_TYPE_FIFO: 280231584Sed case PS_FST_TYPE_VNODE: 281231584Sed print_vnode_info(procstat, fst); 282231584Sed break; 283231584Sed case PS_FST_TYPE_SOCKET: 284231584Sed print_socket_info(procstat, fst); 285231584Sed break; 286231584Sed case PS_FST_TYPE_PIPE: 287231584Sed print_pipe_info(procstat, fst); 288231584Sed break; 289231584Sed case PS_FST_TYPE_PTS: 290231584Sed print_pts_info(procstat, fst); 291231584Sed break; 292231584Sed default: 293231584Sed if (vflg) 294231584Sed fprintf(stderr, 295231584Sed "unknown file type %d for file %d of pid %d\n", 29699053Sdes fst->fs_type, fst->fs_fd, pid); 29799053Sdes } 29876262Sgreen if (filename && !fsflg) 29992559Sdes printf(" %s", filename); 300192595Sdes putchar('\n'); 301192595Sdes} 302192595Sdes 303124211Sdesstatic void 304124211Sdesprint_socket_info(struct procstat *procstat, struct filestat *fst) 305192595Sdes{ 306124211Sdes static const char *stypename[] = { 307124211Sdes "unused", /* 0 */ 308124211Sdes "stream", /* 1 */ 309124211Sdes "dgram", /* 2 */ 31076262Sgreen "raw", /* 3 */ 311226046Sdes "rdm", /* 4 */ 31268704Sgreen "seqpak" /* 5 */ 31376262Sgreen }; 314248619Sdes#define STYPEMAX 5 315181111Sdes struct sockstat sock; 31669591Sgreen struct protoent *pe; 31769591Sgreen char errbuf[_POSIX2_LINE_MAX]; 31869591Sgreen int error; 319248619Sdes static int isopen; 32069591Sgreen 32169591Sgreen error = procstat_get_socket_info(procstat, fst, &sock, errbuf); 32269591Sgreen if (error != 0) { 32369591Sgreen printf("* error"); 32469591Sgreen return; 32569591Sgreen } 32669591Sgreen if (sock.type > STYPEMAX) 327248619Sdes printf("* %s ?%d", sock.dname, sock.type); 328248619Sdes else 32969591Sgreen printf("* %s %s", sock.dname, stypename[sock.type]); 33092559Sdes 331248619Sdes /* 33292559Sdes * protocol specific formatting 33376262Sgreen * 33476262Sgreen * Try to find interesting things to print. For tcp, the interesting 33576262Sgreen * thing is the address of the tcpcb, for udp and others, just the 336248619Sdes * inpcb (socket pcb). For unix domain, its the address of the socket 337248619Sdes * pcb and the address of the connected pcb (if connected). Otherwise 33869591Sgreen * just print the protocol number and address of the socket itself. 33976262Sgreen * The idea is not to duplicate netstat, but to make available enough 340113911Sdes * information for further analysis. 341147005Sdes */ 34276262Sgreen switch (sock.dom_family) { 343147005Sdes case AF_INET: 344147005Sdes case AF_INET6: 345147005Sdes if (!isopen) 346147005Sdes setprotoent(++isopen); 34760573Skris if ((pe = getprotobynumber(sock.proto)) != NULL) 348248619Sdes printf(" %s", pe->p_name); 349248619Sdes else 350248619Sdes printf(" %d", sock.proto); 351248619Sdes if (sock.proto == IPPROTO_TCP ) { 352248619Sdes if (sock.inp_ppcb != 0) 353248619Sdes printf(" %lx", (u_long)sock.inp_ppcb); 354248619Sdes } 355248619Sdes else if (sock.so_pcb != 0) 356248619Sdes printf(" %lx", (u_long)sock.so_pcb); 357248619Sdes break; 358248619Sdes case AF_UNIX: 359248619Sdes /* print address of pcb and connected pcb */ 360248619Sdes if (sock.so_pcb != 0) { 36198941Sdes printf(" %lx", (u_long)sock.so_pcb); 362147005Sdes if (sock.unp_conn) { 363147005Sdes char shoconn[4], *cp; 364147005Sdes 365147005Sdes cp = shoconn; 366147005Sdes if (!(sock.so_rcv_sb_state & SBS_CANTRCVMORE)) 367147005Sdes *cp++ = '<'; 368147005Sdes *cp++ = '-'; 369147005Sdes if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE)) 370147005Sdes *cp++ = '>'; 371149753Sdes *cp = '\0'; 372147005Sdes printf(" %s %lx", shoconn, 373147005Sdes (u_long)sock.unp_conn); 374124211Sdes } 37598941Sdes } 376106130Sdes break; 377106130Sdes default: 378106130Sdes /* print protocol number and socket address */ 379248619Sdes printf(" %d %lx", sock.proto, (u_long)sock.so_addr); 380106130Sdes } 381106130Sdes} 382106130Sdes 38392559Sdesstatic void 38492559Sdesprint_pipe_info(struct procstat *procstat, struct filestat *fst) 38592559Sdes{ 38692559Sdes struct pipestat ps; 38792559Sdes char errbuf[_POSIX2_LINE_MAX]; 38892559Sdes int error; 38992559Sdes 39092559Sdes error = procstat_get_pipe_info(procstat, fst, &ps, errbuf); 39192559Sdes if (error != 0) { 392181111Sdes printf("* error"); 393181111Sdes return; 394226046Sdes } 395226046Sdes printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer); 396181111Sdes printf(" %6zd", ps.buffer_cnt); 397181111Sdes print_access_flags(fst->fs_fflags); 398147005Sdes} 399147005Sdes 400147005Sdesstatic void 40192559Sdesprint_pts_info(struct procstat *procstat, struct filestat *fst) 402147005Sdes{ 403248619Sdes struct ptsstat pts; 404248619Sdes char errbuf[_POSIX2_LINE_MAX]; 405248619Sdes int error; 40692559Sdes 40792559Sdes error = procstat_get_pts_info(procstat, fst, &pts, errbuf); 408248619Sdes if (error != 0) { 40992559Sdes printf("* error"); 41092559Sdes return; 41192559Sdes } 41292559Sdes printf("* pseudo-terminal master "); 41376262Sgreen if (nflg || !*pts.devname) { 41469591Sgreen printf("%#10jx", (uintmax_t)pts.dev); 415248619Sdes } else { 416248619Sdes printf("%10s", pts.devname); 417248619Sdes } 418248619Sdes print_access_flags(fst->fs_fflags); 419248619Sdes} 420248619Sdes 421248619Sdesstatic void 422248619Sdesprint_vnode_info(struct procstat *procstat, struct filestat *fst) 423248619Sdes{ 424248619Sdes struct vnstat vn; 425248619Sdes char errbuf[_POSIX2_LINE_MAX]; 426248619Sdes char mode[15]; 427248619Sdes const char *badtype; 428248619Sdes int error; 429248619Sdes 430248619Sdes badtype = NULL; 431248619Sdes error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); 432248619Sdes if (error != 0) 433248619Sdes badtype = errbuf; 434248619Sdes else if (vn.vn_type == PS_FST_VTYPE_VBAD) 435248619Sdes badtype = "bad"; 436248619Sdes else if (vn.vn_type == PS_FST_VTYPE_VNON) 437248619Sdes badtype = "none"; 43892559Sdes if (badtype != NULL) { 439248619Sdes printf(" - - %10s -", badtype); 44060573Skris return; 44192559Sdes } 44269591Sgreen 443248619Sdes if (nflg) 44460573Skris printf(" %#5jx", (uintmax_t)vn.vn_fsid); 44592559Sdes else if (vn.vn_mntdir != NULL) 44698684Sdes (void)printf(" %-8s", vn.vn_mntdir); 44798684Sdes 44869591Sgreen /* 449248619Sdes * Print access mode. 450248619Sdes */ 451248619Sdes if (nflg) 452248619Sdes (void)snprintf(mode, sizeof(mode), "%o", vn.vn_mode); 453248619Sdes else { 454248619Sdes strmode(vn.vn_mode, mode); 455248619Sdes } 456248619Sdes (void)printf(" %6jd %10s", (intmax_t)vn.vn_fileid, mode); 457248619Sdes 45869591Sgreen if (vn.vn_type == PS_FST_VTYPE_VBLK || vn.vn_type == PS_FST_VTYPE_VCHR) { 45992559Sdes if (nflg || !*vn.vn_devname) 46092559Sdes printf(" %#6jx", (uintmax_t)vn.vn_dev); 46192559Sdes else { 46269591Sgreen printf(" %6s", vn.vn_devname); 46360573Skris } 46460573Skris } else 46592559Sdes printf(" %6ju", (uintmax_t)vn.vn_size); 466248619Sdes print_access_flags(fst->fs_fflags); 46769591Sgreen} 46898684Sdes 46998684Sdesstatic void 47069591Sgreenprint_access_flags(int flags) 47198684Sdes{ 47298684Sdes char rw[3]; 47398684Sdes 474248619Sdes rw[0] = '\0'; 475248619Sdes if (flags & PS_FST_FFLAG_READ) 47698684Sdes strcat(rw, "r"); 47798684Sdes if (flags & PS_FST_FFLAG_WRITE) 47898684Sdes strcat(rw, "w"); 47969591Sgreen printf(" %2s", rw); 48069591Sgreen} 481181111Sdes 482248619Sdesint 483248619Sdesgetfname(const char *filename) 484248619Sdes{ 485248619Sdes struct stat statbuf; 486248619Sdes DEVS *cur; 487248619Sdes 488248619Sdes if (stat(filename, &statbuf)) { 489248619Sdes warn("%s", filename); 490248619Sdes return (0); 491248619Sdes } 492248619Sdes if ((cur = malloc(sizeof(DEVS))) == NULL) 493248619Sdes err(1, NULL); 494248619Sdes cur->next = devs; 495248619Sdes devs = cur; 496248619Sdes 497248619Sdes cur->ino = statbuf.st_ino; 498248619Sdes cur->fsid = statbuf.st_dev; 499248619Sdes cur->name = filename; 500248619Sdes return (1); 501248619Sdes} 502248619Sdes 503248619Sdesstatic void 504248619Sdesusage(void) 505248619Sdes{ 506248619Sdes (void)fprintf(stderr, 507248619Sdes "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] [file ...]\n"); 508248619Sdes exit(1); 509248619Sdes} 510248619Sdes