fstat.c revision 233760
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 233760 2012-04-01 18:22:48Z jhb $"); 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 60227239Sedstatic int fsflg, /* show files on same filesystem as file(s) argument */ 61227239Sed pflg, /* show files open by a particular pid */ 62227239Sed uflg; /* show files open by a particular (effective) user */ 63227239Sedstatic int checkfile; /* restrict to particular files or filesystems */ 64227239Sedstatic int nflg; /* (numerical) display f.s. and rdev as dev_t */ 65227239Sedstatic int mflg; /* include memory-mapped files */ 66227239Sedstatic int 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 75227239Sedstatic DEVS *devs; 76227239Sedstatic char *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); 87233760Sjhbstatic void print_shm_info(struct procstat *procstat, 88233760Sjhb struct filestat *fst); 89221807Sstasstatic void print_socket_info(struct procstat *procstat, 90221807Sstas struct filestat *fst); 91221807Sstasstatic void print_vnode_info(struct procstat *procstat, 92221807Sstas struct filestat *fst); 93221807Sstasstatic void usage(void) __dead2; 941590Srgrimes 9517808Speterint 96221807Sstasdo_fstat(int argc, char **argv) 971590Srgrimes{ 98221807Sstas struct kinfo_proc *p; 9993427Sdwmalone struct passwd *passwd; 100221807Sstas struct procstat *procstat; 1011590Srgrimes int arg, ch, what; 102221807Sstas int cnt, i; 1031590Srgrimes 1041590Srgrimes arg = 0; 105167367Semaste what = KERN_PROC_PROC; 1061590Srgrimes nlistf = memf = NULL; 10759029Sgreen while ((ch = getopt(argc, argv, "fmnp:u:vN:M:")) != -1) 1081590Srgrimes switch((char)ch) { 1091590Srgrimes case 'f': 1101590Srgrimes fsflg = 1; 1111590Srgrimes break; 1121590Srgrimes case 'M': 1131590Srgrimes memf = optarg; 1141590Srgrimes break; 1151590Srgrimes case 'N': 1161590Srgrimes nlistf = optarg; 1171590Srgrimes break; 11859029Sgreen case 'm': 11959029Sgreen mflg = 1; 12059029Sgreen break; 1211590Srgrimes case 'n': 1221590Srgrimes nflg = 1; 1231590Srgrimes break; 1241590Srgrimes case 'p': 1251590Srgrimes if (pflg++) 1261590Srgrimes usage(); 1271590Srgrimes if (!isdigit(*optarg)) { 12827311Scharnier warnx("-p requires a process id"); 1291590Srgrimes usage(); 1301590Srgrimes } 1311590Srgrimes what = KERN_PROC_PID; 1321590Srgrimes arg = atoi(optarg); 1331590Srgrimes break; 1341590Srgrimes case 'u': 1351590Srgrimes if (uflg++) 1361590Srgrimes usage(); 13727272Scharnier if (!(passwd = getpwnam(optarg))) 13827272Scharnier errx(1, "%s: unknown uid", optarg); 1391590Srgrimes what = KERN_PROC_UID; 1401590Srgrimes arg = passwd->pw_uid; 1411590Srgrimes break; 1421590Srgrimes case 'v': 1431590Srgrimes vflg = 1; 1441590Srgrimes break; 1451590Srgrimes case '?': 1461590Srgrimes default: 1471590Srgrimes usage(); 1481590Srgrimes } 1491590Srgrimes 1501590Srgrimes if (*(argv += optind)) { 1511590Srgrimes for (; *argv; ++argv) { 1521590Srgrimes if (getfname(*argv)) 1531590Srgrimes checkfile = 1; 1541590Srgrimes } 155228992Suqs if (!checkfile) /* file(s) specified, but none accessible */ 1561590Srgrimes exit(1); 1571590Srgrimes } 1581590Srgrimes 1598874Srgrimes if (fsflg && !checkfile) { 1601590Srgrimes /* -f with no files means use wd */ 1611590Srgrimes if (getfname(".") == 0) 1621590Srgrimes exit(1); 1631590Srgrimes checkfile = 1; 1641590Srgrimes } 1651590Srgrimes 16697946Sdes if (memf != NULL) 167221807Sstas procstat = procstat_open_kvm(nlistf, memf); 16897946Sdes else 169221807Sstas procstat = procstat_open_sysctl(); 170221807Sstas if (procstat == NULL) 171221807Sstas errx(1, "procstat_open()"); 172221807Sstas p = procstat_getprocs(procstat, what, arg, &cnt); 173221807Sstas if (p == NULL) 174221807Sstas errx(1, "procstat_getprocs()"); 17597946Sdes 176221807Sstas /* 177221807Sstas * Print header. 178221807Sstas */ 17997946Sdes if (nflg) 18097946Sdes printf("%s", 18197946Sdes"USER CMD PID FD DEV INUM MODE SZ|DV R/W"); 18297946Sdes else 18397946Sdes printf("%s", 18497946Sdes"USER CMD PID FD MOUNT INUM MODE SZ|DV R/W"); 18597946Sdes if (checkfile && fsflg == 0) 18697946Sdes printf(" NAME\n"); 18797946Sdes else 18897946Sdes putchar('\n'); 18997946Sdes 1901590Srgrimes /* 191221807Sstas * Go through the process list. 1921590Srgrimes */ 193221807Sstas for (i = 0; i < cnt; i++) { 194221807Sstas if (p[i].ki_stat == SZOMB) 1951590Srgrimes continue; 196221807Sstas dofiles(procstat, &p[i]); 1971590Srgrimes } 198221807Sstas procstat_freeprocs(procstat, p); 199221807Sstas procstat_close(procstat); 200221807Sstas return (0); 2011590Srgrimes} 2021590Srgrimes 20397946Sdesstatic void 204221807Sstasdofiles(struct procstat *procstat, struct kinfo_proc *kp) 20597946Sdes{ 206221807Sstas const char *cmd; 207221807Sstas const char *uname; 208221807Sstas struct filestat *fst; 209221807Sstas struct filestat_list *head; 210221807Sstas int pid; 21197946Sdes 212221807Sstas uname = user_from_uid(kp->ki_uid, 0); 213221807Sstas pid = kp->ki_pid; 214221807Sstas cmd = kp->ki_comm; 21597946Sdes 216221807Sstas head = procstat_getfiles(procstat, kp, mflg); 217221807Sstas if (head == NULL) 2181590Srgrimes return; 219221807Sstas STAILQ_FOREACH(fst, head, next) 220221807Sstas print_file_info(procstat, fst, uname, cmd, pid); 221221807Sstas procstat_freefiles(procstat, head); 222221807Sstas} 223140958Sphk 224140958Sphk 225221807Sstasstatic void 226221807Sstasprint_file_info(struct procstat *procstat, struct filestat *fst, 227221807Sstas const char *uname, const char *cmd, int pid) 22859029Sgreen{ 229221807Sstas struct vnstat vn; 230221807Sstas DEVS *d; 231221807Sstas const char *filename; 232221807Sstas int error, fsmatch = 0; 233221807Sstas char errbuf[_POSIX2_LINE_MAX]; 23459029Sgreen 235221807Sstas filename = NULL; 236221807Sstas if (checkfile != 0) { 237232233Spluknet if (fst->fs_type != PS_FST_TYPE_VNODE && 238232233Spluknet fst->fs_type != PS_FST_TYPE_FIFO) 23959029Sgreen return; 240232233Spluknet error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); 241221807Sstas if (error != 0) 242221807Sstas return; 24359029Sgreen 2441590Srgrimes for (d = devs; d != NULL; d = d->next) 245232233Spluknet if (d->fsid == vn.vn_fsid) { 246232233Spluknet fsmatch = 1; 247232233Spluknet if ((unsigned)d->ino == vn.vn_fileid) { 248232233Spluknet filename = d->name; 249232233Spluknet break; 2501590Srgrimes } 2511590Srgrimes } 2521590Srgrimes if (fsmatch == 0 || (filename == NULL && fsflg == 0)) 2531590Srgrimes return; 2541590Srgrimes } 2551590Srgrimes 25653133Sgreen /* 257221807Sstas * Print entry prefix. 25853133Sgreen */ 259221807Sstas printf("%-8.8s %-10s %5d", uname, cmd, pid); 260221807Sstas if (fst->fs_uflags & PS_FST_UFLAG_TEXT) 261221807Sstas printf(" text"); 262221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_CDIR) 263221807Sstas printf(" wd"); 264221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_RDIR) 265221807Sstas printf(" root"); 266221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_TRACE) 267221807Sstas printf(" tr"); 268221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_MMAP) 269221807Sstas printf(" mmap"); 270221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_JAIL) 271221807Sstas printf(" jail"); 272221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_CTTY) 273221807Sstas printf(" ctty"); 274116780Sjmg else 275221807Sstas printf(" %4d", fst->fs_fd); 2761590Srgrimes 277221807Sstas /* 278221807Sstas * Print type-specific data. 279221807Sstas */ 280221807Sstas switch (fst->fs_type) { 281221807Sstas case PS_FST_TYPE_FIFO: 282221807Sstas case PS_FST_TYPE_VNODE: 283221807Sstas print_vnode_info(procstat, fst); 2841590Srgrimes break; 285221807Sstas case PS_FST_TYPE_SOCKET: 286221807Sstas print_socket_info(procstat, fst); 2871590Srgrimes break; 288221807Sstas case PS_FST_TYPE_PIPE: 289221807Sstas print_pipe_info(procstat, fst); 2901590Srgrimes break; 291221807Sstas case PS_FST_TYPE_PTS: 292221807Sstas print_pts_info(procstat, fst); 2931590Srgrimes break; 294233760Sjhb case PS_FST_TYPE_SHM: 295233760Sjhb print_shm_info(procstat, fst); 296233760Sjhb break; 297221807Sstas default: 298221807Sstas if (vflg) 299221807Sstas fprintf(stderr, 300221807Sstas "unknown file type %d for file %d of pid %d\n", 301221807Sstas fst->fs_type, fst->fs_fd, pid); 3021590Srgrimes } 303221807Sstas if (filename && !fsflg) 304221807Sstas printf(" %s", filename); 30517808Speter putchar('\n'); 30617808Speter} 30717808Speter 308221807Sstasstatic void 309221807Sstasprint_socket_info(struct procstat *procstat, struct filestat *fst) 3101590Srgrimes{ 31193427Sdwmalone static const char *stypename[] = { 3121590Srgrimes "unused", /* 0 */ 313221807Sstas "stream", /* 1 */ 3141590Srgrimes "dgram", /* 2 */ 3151590Srgrimes "raw", /* 3 */ 3161590Srgrimes "rdm", /* 4 */ 3171590Srgrimes "seqpak" /* 5 */ 3181590Srgrimes }; 319221807Sstas#define STYPEMAX 5 320221807Sstas struct sockstat sock; 321221807Sstas struct protoent *pe; 322221807Sstas char errbuf[_POSIX2_LINE_MAX]; 323221807Sstas int error; 324221807Sstas static int isopen; 3251590Srgrimes 326221807Sstas error = procstat_get_socket_info(procstat, fst, &sock, errbuf); 327221807Sstas if (error != 0) { 328221807Sstas printf("* error"); 329221807Sstas return; 3301590Srgrimes } 331221807Sstas if (sock.type > STYPEMAX) 332221807Sstas printf("* %s ?%d", sock.dname, sock.type); 3331590Srgrimes else 334221807Sstas printf("* %s %s", sock.dname, stypename[sock.type]); 3351590Srgrimes 3368874Srgrimes /* 3371590Srgrimes * protocol specific formatting 3381590Srgrimes * 3391590Srgrimes * Try to find interesting things to print. For tcp, the interesting 3401590Srgrimes * thing is the address of the tcpcb, for udp and others, just the 3411590Srgrimes * inpcb (socket pcb). For unix domain, its the address of the socket 3421590Srgrimes * pcb and the address of the connected pcb (if connected). Otherwise 3431590Srgrimes * just print the protocol number and address of the socket itself. 3441590Srgrimes * The idea is not to duplicate netstat, but to make available enough 3451590Srgrimes * information for further analysis. 3461590Srgrimes */ 347221807Sstas switch (sock.dom_family) { 3481590Srgrimes case AF_INET: 34957345Sshin case AF_INET6: 350221807Sstas if (!isopen) 351221807Sstas setprotoent(++isopen); 352221807Sstas if ((pe = getprotobynumber(sock.proto)) != NULL) 353221807Sstas printf(" %s", pe->p_name); 354221807Sstas else 355221807Sstas printf(" %d", sock.proto); 356221807Sstas if (sock.proto == IPPROTO_TCP ) { 357221807Sstas if (sock.inp_ppcb != 0) 358221807Sstas printf(" %lx", (u_long)sock.inp_ppcb); 3591590Srgrimes } 360221807Sstas else if (sock.so_pcb != 0) 361221807Sstas printf(" %lx", (u_long)sock.so_pcb); 3621590Srgrimes break; 3631590Srgrimes case AF_UNIX: 3641590Srgrimes /* print address of pcb and connected pcb */ 365221807Sstas if (sock.so_pcb != 0) { 366221807Sstas printf(" %lx", (u_long)sock.so_pcb); 367221807Sstas if (sock.unp_conn) { 3681590Srgrimes char shoconn[4], *cp; 3691590Srgrimes 3701590Srgrimes cp = shoconn; 371221807Sstas if (!(sock.so_rcv_sb_state & SBS_CANTRCVMORE)) 3721590Srgrimes *cp++ = '<'; 3731590Srgrimes *cp++ = '-'; 374221807Sstas if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE)) 3751590Srgrimes *cp++ = '>'; 3761590Srgrimes *cp = '\0'; 37780355Smjacob printf(" %s %lx", shoconn, 378221807Sstas (u_long)sock.unp_conn); 379221807Sstas } 3801590Srgrimes } 3811590Srgrimes break; 3821590Srgrimes default: 3831590Srgrimes /* print protocol number and socket address */ 384221807Sstas printf(" %d %lx", sock.proto, (u_long)sock.so_addr); 3851590Srgrimes } 3861590Srgrimes} 3871590Srgrimes 388221807Sstasstatic void 389221807Sstasprint_pipe_info(struct procstat *procstat, struct filestat *fst) 390181905Sed{ 391221807Sstas struct pipestat ps; 392221807Sstas char errbuf[_POSIX2_LINE_MAX]; 393221807Sstas int error; 39453133Sgreen 395221807Sstas error = procstat_get_pipe_info(procstat, fst, &ps, errbuf); 396221807Sstas if (error != 0) { 397221807Sstas printf("* error"); 398221807Sstas return; 399181905Sed } 400221807Sstas printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer); 401221807Sstas printf(" %6zd", ps.buffer_cnt); 402221807Sstas print_access_flags(fst->fs_fflags); 403221807Sstas} 404181905Sed 405221807Sstasstatic void 406221807Sstasprint_pts_info(struct procstat *procstat, struct filestat *fst) 407221807Sstas{ 408221807Sstas struct ptsstat pts; 409221807Sstas char errbuf[_POSIX2_LINE_MAX]; 410221807Sstas int error; 411221807Sstas 412221807Sstas error = procstat_get_pts_info(procstat, fst, &pts, errbuf); 413221807Sstas if (error != 0) { 414221807Sstas printf("* error"); 415221807Sstas return; 416181905Sed } 417181905Sed printf("* pseudo-terminal master "); 418221807Sstas if (nflg || !*pts.devname) { 419225847Sed printf("%#10jx", (uintmax_t)pts.dev); 420181905Sed } else { 421221807Sstas printf("%10s", pts.devname); 422181905Sed } 423221807Sstas print_access_flags(fst->fs_fflags); 424181905Sed} 425181905Sed 426221807Sstasstatic void 427233760Sjhbprint_shm_info(struct procstat *procstat, struct filestat *fst) 428233760Sjhb{ 429233760Sjhb struct shmstat shm; 430233760Sjhb char errbuf[_POSIX2_LINE_MAX]; 431233760Sjhb char mode[15]; 432233760Sjhb int error; 433233760Sjhb 434233760Sjhb error = procstat_get_shm_info(procstat, fst, &shm, errbuf); 435233760Sjhb if (error != 0) { 436233760Sjhb printf("* error"); 437233760Sjhb return; 438233760Sjhb } 439233760Sjhb if (nflg) { 440233760Sjhb printf(" "); 441233760Sjhb (void)snprintf(mode, sizeof(mode), "%o", shm.mode); 442233760Sjhb } else { 443233760Sjhb printf(" %-15s", fst->fs_path != NULL ? fst->fs_path : "-"); 444233760Sjhb strmode(shm.mode, mode); 445233760Sjhb } 446233760Sjhb printf(" %10s %6ju", mode, shm.size); 447233760Sjhb print_access_flags(fst->fs_fflags); 448233760Sjhb} 449233760Sjhb 450233760Sjhbstatic void 451221807Sstasprint_vnode_info(struct procstat *procstat, struct filestat *fst) 45253133Sgreen{ 453221807Sstas struct vnstat vn; 454221807Sstas char errbuf[_POSIX2_LINE_MAX]; 455221807Sstas char mode[15]; 456221807Sstas const char *badtype; 457221807Sstas int error; 45853133Sgreen 459221807Sstas badtype = NULL; 460221807Sstas error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); 461221807Sstas if (error != 0) 462221807Sstas badtype = errbuf; 463221807Sstas else if (vn.vn_type == PS_FST_VTYPE_VBAD) 464221807Sstas badtype = "bad"; 465221807Sstas else if (vn.vn_type == PS_FST_VTYPE_VNON) 466221807Sstas badtype = "none"; 467221807Sstas if (badtype != NULL) { 468221807Sstas printf(" - - %10s -", badtype); 469221807Sstas return; 47053133Sgreen } 471221807Sstas 472221807Sstas if (nflg) 473225893Sed printf(" %#5jx", (uintmax_t)vn.vn_fsid); 474221807Sstas else if (vn.vn_mntdir != NULL) 475221807Sstas (void)printf(" %-8s", vn.vn_mntdir); 476221807Sstas 477221807Sstas /* 478221807Sstas * Print access mode. 479221807Sstas */ 480221807Sstas if (nflg) 481221807Sstas (void)snprintf(mode, sizeof(mode), "%o", vn.vn_mode); 482221807Sstas else { 483221807Sstas strmode(vn.vn_mode, mode); 484221807Sstas } 485221816Simp (void)printf(" %6jd %10s", (intmax_t)vn.vn_fileid, mode); 486221807Sstas 487221807Sstas if (vn.vn_type == PS_FST_VTYPE_VBLK || vn.vn_type == PS_FST_VTYPE_VCHR) { 488221807Sstas if (nflg || !*vn.vn_devname) 489225847Sed printf(" %#6jx", (uintmax_t)vn.vn_dev); 490221807Sstas else { 491221807Sstas printf(" %6s", vn.vn_devname); 492221807Sstas } 493221807Sstas } else 494221819Simp printf(" %6ju", (uintmax_t)vn.vn_size); 495221807Sstas print_access_flags(fst->fs_fflags); 49653133Sgreen} 49753133Sgreen 498221807Sstasstatic void 499221807Sstasprint_access_flags(int flags) 5001590Srgrimes{ 501221807Sstas char rw[3]; 5021590Srgrimes 503221807Sstas rw[0] = '\0'; 504221807Sstas if (flags & PS_FST_FFLAG_READ) 505221807Sstas strcat(rw, "r"); 506221807Sstas if (flags & PS_FST_FFLAG_WRITE) 507221807Sstas strcat(rw, "w"); 508221807Sstas printf(" %2s", rw); 5091590Srgrimes} 5101590Srgrimes 51117808Speterint 512131293Sdwmalonegetfname(const char *filename) 5131590Srgrimes{ 5141590Srgrimes struct stat statbuf; 5151590Srgrimes DEVS *cur; 5161590Srgrimes 5171590Srgrimes if (stat(filename, &statbuf)) { 51827272Scharnier warn("%s", filename); 519221807Sstas return (0); 5201590Srgrimes } 52127272Scharnier if ((cur = malloc(sizeof(DEVS))) == NULL) 52227272Scharnier err(1, NULL); 5231590Srgrimes cur->next = devs; 5241590Srgrimes devs = cur; 5251590Srgrimes 5261590Srgrimes cur->ino = statbuf.st_ino; 52786100Sdwmalone cur->fsid = statbuf.st_dev; 5281590Srgrimes cur->name = filename; 529221807Sstas return (1); 5301590Srgrimes} 5311590Srgrimes 532221807Sstasstatic void 533131293Sdwmaloneusage(void) 5341590Srgrimes{ 5351590Srgrimes (void)fprintf(stderr, 536146466Sru "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] [file ...]\n"); 5371590Srgrimes exit(1); 5381590Srgrimes} 539