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$"); 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); 87250223Sjhbstatic void print_sem_info(struct procstat *procstat, 88250223Sjhb struct filestat *fst); 89233760Sjhbstatic void print_shm_info(struct procstat *procstat, 90233760Sjhb struct filestat *fst); 91221807Sstasstatic void print_socket_info(struct procstat *procstat, 92221807Sstas struct filestat *fst); 93221807Sstasstatic void print_vnode_info(struct procstat *procstat, 94221807Sstas struct filestat *fst); 95221807Sstasstatic void usage(void) __dead2; 961590Srgrimes 9717808Speterint 98221807Sstasdo_fstat(int argc, char **argv) 991590Srgrimes{ 100221807Sstas struct kinfo_proc *p; 10193427Sdwmalone struct passwd *passwd; 102221807Sstas struct procstat *procstat; 1031590Srgrimes int arg, ch, what; 104221807Sstas int cnt, i; 1051590Srgrimes 1061590Srgrimes arg = 0; 107167367Semaste what = KERN_PROC_PROC; 1081590Srgrimes nlistf = memf = NULL; 10959029Sgreen while ((ch = getopt(argc, argv, "fmnp:u:vN:M:")) != -1) 1101590Srgrimes switch((char)ch) { 1111590Srgrimes case 'f': 1121590Srgrimes fsflg = 1; 1131590Srgrimes break; 1141590Srgrimes case 'M': 1151590Srgrimes memf = optarg; 1161590Srgrimes break; 1171590Srgrimes case 'N': 1181590Srgrimes nlistf = optarg; 1191590Srgrimes break; 12059029Sgreen case 'm': 12159029Sgreen mflg = 1; 12259029Sgreen break; 1231590Srgrimes case 'n': 1241590Srgrimes nflg = 1; 1251590Srgrimes break; 1261590Srgrimes case 'p': 1271590Srgrimes if (pflg++) 1281590Srgrimes usage(); 1291590Srgrimes if (!isdigit(*optarg)) { 13027311Scharnier warnx("-p requires a process id"); 1311590Srgrimes usage(); 1321590Srgrimes } 1331590Srgrimes what = KERN_PROC_PID; 1341590Srgrimes arg = atoi(optarg); 1351590Srgrimes break; 1361590Srgrimes case 'u': 1371590Srgrimes if (uflg++) 1381590Srgrimes usage(); 13927272Scharnier if (!(passwd = getpwnam(optarg))) 14027272Scharnier errx(1, "%s: unknown uid", optarg); 1411590Srgrimes what = KERN_PROC_UID; 1421590Srgrimes arg = passwd->pw_uid; 1431590Srgrimes break; 1441590Srgrimes case 'v': 1451590Srgrimes vflg = 1; 1461590Srgrimes break; 1471590Srgrimes case '?': 1481590Srgrimes default: 1491590Srgrimes usage(); 1501590Srgrimes } 1511590Srgrimes 1521590Srgrimes if (*(argv += optind)) { 1531590Srgrimes for (; *argv; ++argv) { 1541590Srgrimes if (getfname(*argv)) 1551590Srgrimes checkfile = 1; 1561590Srgrimes } 157228992Suqs if (!checkfile) /* file(s) specified, but none accessible */ 1581590Srgrimes exit(1); 1591590Srgrimes } 1601590Srgrimes 1618874Srgrimes if (fsflg && !checkfile) { 1621590Srgrimes /* -f with no files means use wd */ 1631590Srgrimes if (getfname(".") == 0) 1641590Srgrimes exit(1); 1651590Srgrimes checkfile = 1; 1661590Srgrimes } 1671590Srgrimes 16897946Sdes if (memf != NULL) 169221807Sstas procstat = procstat_open_kvm(nlistf, memf); 17097946Sdes else 171221807Sstas procstat = procstat_open_sysctl(); 172221807Sstas if (procstat == NULL) 173221807Sstas errx(1, "procstat_open()"); 174221807Sstas p = procstat_getprocs(procstat, what, arg, &cnt); 175221807Sstas if (p == NULL) 176221807Sstas errx(1, "procstat_getprocs()"); 17797946Sdes 178221807Sstas /* 179221807Sstas * Print header. 180221807Sstas */ 18197946Sdes if (nflg) 18297946Sdes printf("%s", 18397946Sdes"USER CMD PID FD DEV INUM MODE SZ|DV R/W"); 18497946Sdes else 18597946Sdes printf("%s", 18697946Sdes"USER CMD PID FD MOUNT INUM MODE SZ|DV R/W"); 18797946Sdes if (checkfile && fsflg == 0) 18897946Sdes printf(" NAME\n"); 18997946Sdes else 19097946Sdes putchar('\n'); 19197946Sdes 1921590Srgrimes /* 193221807Sstas * Go through the process list. 1941590Srgrimes */ 195221807Sstas for (i = 0; i < cnt; i++) { 196221807Sstas if (p[i].ki_stat == SZOMB) 1971590Srgrimes continue; 198221807Sstas dofiles(procstat, &p[i]); 1991590Srgrimes } 200221807Sstas procstat_freeprocs(procstat, p); 201221807Sstas procstat_close(procstat); 202221807Sstas return (0); 2031590Srgrimes} 2041590Srgrimes 20597946Sdesstatic void 206221807Sstasdofiles(struct procstat *procstat, struct kinfo_proc *kp) 20797946Sdes{ 208221807Sstas const char *cmd; 209221807Sstas const char *uname; 210221807Sstas struct filestat *fst; 211221807Sstas struct filestat_list *head; 212221807Sstas int pid; 21397946Sdes 214221807Sstas uname = user_from_uid(kp->ki_uid, 0); 215221807Sstas pid = kp->ki_pid; 216221807Sstas cmd = kp->ki_comm; 21797946Sdes 218221807Sstas head = procstat_getfiles(procstat, kp, mflg); 219221807Sstas if (head == NULL) 2201590Srgrimes return; 221221807Sstas STAILQ_FOREACH(fst, head, next) 222221807Sstas print_file_info(procstat, fst, uname, cmd, pid); 223221807Sstas procstat_freefiles(procstat, head); 224221807Sstas} 225140958Sphk 226140958Sphk 227221807Sstasstatic void 228221807Sstasprint_file_info(struct procstat *procstat, struct filestat *fst, 229221807Sstas const char *uname, const char *cmd, int pid) 23059029Sgreen{ 231221807Sstas struct vnstat vn; 232221807Sstas DEVS *d; 233221807Sstas const char *filename; 234221807Sstas int error, fsmatch = 0; 235221807Sstas char errbuf[_POSIX2_LINE_MAX]; 23659029Sgreen 237221807Sstas filename = NULL; 238221807Sstas if (checkfile != 0) { 239232233Spluknet if (fst->fs_type != PS_FST_TYPE_VNODE && 240232233Spluknet fst->fs_type != PS_FST_TYPE_FIFO) 24159029Sgreen return; 242232233Spluknet error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); 243221807Sstas if (error != 0) 244221807Sstas return; 24559029Sgreen 2461590Srgrimes for (d = devs; d != NULL; d = d->next) 247232233Spluknet if (d->fsid == vn.vn_fsid) { 248232233Spluknet fsmatch = 1; 249235602Sgleb if (d->ino == vn.vn_fileid) { 250232233Spluknet filename = d->name; 251232233Spluknet break; 2521590Srgrimes } 2531590Srgrimes } 2541590Srgrimes if (fsmatch == 0 || (filename == NULL && fsflg == 0)) 2551590Srgrimes return; 2561590Srgrimes } 2571590Srgrimes 25853133Sgreen /* 259221807Sstas * Print entry prefix. 26053133Sgreen */ 261221807Sstas printf("%-8.8s %-10s %5d", uname, cmd, pid); 262221807Sstas if (fst->fs_uflags & PS_FST_UFLAG_TEXT) 263221807Sstas printf(" text"); 264221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_CDIR) 265221807Sstas printf(" wd"); 266221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_RDIR) 267221807Sstas printf(" root"); 268221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_TRACE) 269221807Sstas printf(" tr"); 270221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_MMAP) 271221807Sstas printf(" mmap"); 272221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_JAIL) 273221807Sstas printf(" jail"); 274221807Sstas else if (fst->fs_uflags & PS_FST_UFLAG_CTTY) 275221807Sstas printf(" ctty"); 276116780Sjmg else 277221807Sstas printf(" %4d", fst->fs_fd); 2781590Srgrimes 279221807Sstas /* 280221807Sstas * Print type-specific data. 281221807Sstas */ 282221807Sstas switch (fst->fs_type) { 283221807Sstas case PS_FST_TYPE_FIFO: 284221807Sstas case PS_FST_TYPE_VNODE: 285221807Sstas print_vnode_info(procstat, fst); 2861590Srgrimes break; 287221807Sstas case PS_FST_TYPE_SOCKET: 288221807Sstas print_socket_info(procstat, fst); 2891590Srgrimes break; 290221807Sstas case PS_FST_TYPE_PIPE: 291221807Sstas print_pipe_info(procstat, fst); 2921590Srgrimes break; 293221807Sstas case PS_FST_TYPE_PTS: 294221807Sstas print_pts_info(procstat, fst); 2951590Srgrimes break; 296233760Sjhb case PS_FST_TYPE_SHM: 297233760Sjhb print_shm_info(procstat, fst); 298233760Sjhb break; 299250223Sjhb case PS_FST_TYPE_SEM: 300250223Sjhb print_sem_info(procstat, fst); 301250223Sjhb break; 302221807Sstas default: 303221807Sstas if (vflg) 304221807Sstas fprintf(stderr, 305221807Sstas "unknown file type %d for file %d of pid %d\n", 306221807Sstas fst->fs_type, fst->fs_fd, pid); 3071590Srgrimes } 308221807Sstas if (filename && !fsflg) 309221807Sstas printf(" %s", filename); 31017808Speter putchar('\n'); 31117808Speter} 31217808Speter 313221807Sstasstatic void 314221807Sstasprint_socket_info(struct procstat *procstat, struct filestat *fst) 3151590Srgrimes{ 31693427Sdwmalone static const char *stypename[] = { 3171590Srgrimes "unused", /* 0 */ 318221807Sstas "stream", /* 1 */ 3191590Srgrimes "dgram", /* 2 */ 3201590Srgrimes "raw", /* 3 */ 3211590Srgrimes "rdm", /* 4 */ 3221590Srgrimes "seqpak" /* 5 */ 3231590Srgrimes }; 324221807Sstas#define STYPEMAX 5 325221807Sstas struct sockstat sock; 326221807Sstas struct protoent *pe; 327221807Sstas char errbuf[_POSIX2_LINE_MAX]; 328221807Sstas int error; 329221807Sstas static int isopen; 3301590Srgrimes 331221807Sstas error = procstat_get_socket_info(procstat, fst, &sock, errbuf); 332221807Sstas if (error != 0) { 333221807Sstas printf("* error"); 334221807Sstas return; 3351590Srgrimes } 336221807Sstas if (sock.type > STYPEMAX) 337221807Sstas printf("* %s ?%d", sock.dname, sock.type); 3381590Srgrimes else 339221807Sstas printf("* %s %s", sock.dname, stypename[sock.type]); 3401590Srgrimes 3418874Srgrimes /* 3421590Srgrimes * protocol specific formatting 3431590Srgrimes * 3441590Srgrimes * Try to find interesting things to print. For tcp, the interesting 3451590Srgrimes * thing is the address of the tcpcb, for udp and others, just the 3461590Srgrimes * inpcb (socket pcb). For unix domain, its the address of the socket 3471590Srgrimes * pcb and the address of the connected pcb (if connected). Otherwise 3481590Srgrimes * just print the protocol number and address of the socket itself. 3491590Srgrimes * The idea is not to duplicate netstat, but to make available enough 3501590Srgrimes * information for further analysis. 3511590Srgrimes */ 352221807Sstas switch (sock.dom_family) { 3531590Srgrimes case AF_INET: 35457345Sshin case AF_INET6: 355221807Sstas if (!isopen) 356221807Sstas setprotoent(++isopen); 357221807Sstas if ((pe = getprotobynumber(sock.proto)) != NULL) 358221807Sstas printf(" %s", pe->p_name); 359221807Sstas else 360221807Sstas printf(" %d", sock.proto); 361221807Sstas if (sock.proto == IPPROTO_TCP ) { 362221807Sstas if (sock.inp_ppcb != 0) 363221807Sstas printf(" %lx", (u_long)sock.inp_ppcb); 3641590Srgrimes } 365221807Sstas else if (sock.so_pcb != 0) 366221807Sstas printf(" %lx", (u_long)sock.so_pcb); 3671590Srgrimes break; 3681590Srgrimes case AF_UNIX: 3691590Srgrimes /* print address of pcb and connected pcb */ 370221807Sstas if (sock.so_pcb != 0) { 371221807Sstas printf(" %lx", (u_long)sock.so_pcb); 372221807Sstas if (sock.unp_conn) { 3731590Srgrimes char shoconn[4], *cp; 3741590Srgrimes 3751590Srgrimes cp = shoconn; 376221807Sstas if (!(sock.so_rcv_sb_state & SBS_CANTRCVMORE)) 3771590Srgrimes *cp++ = '<'; 3781590Srgrimes *cp++ = '-'; 379221807Sstas if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE)) 3801590Srgrimes *cp++ = '>'; 3811590Srgrimes *cp = '\0'; 38280355Smjacob printf(" %s %lx", shoconn, 383221807Sstas (u_long)sock.unp_conn); 384221807Sstas } 3851590Srgrimes } 3861590Srgrimes break; 3871590Srgrimes default: 3881590Srgrimes /* print protocol number and socket address */ 389221807Sstas printf(" %d %lx", sock.proto, (u_long)sock.so_addr); 3901590Srgrimes } 3911590Srgrimes} 3921590Srgrimes 393221807Sstasstatic void 394221807Sstasprint_pipe_info(struct procstat *procstat, struct filestat *fst) 395181905Sed{ 396221807Sstas struct pipestat ps; 397221807Sstas char errbuf[_POSIX2_LINE_MAX]; 398221807Sstas int error; 39953133Sgreen 400221807Sstas error = procstat_get_pipe_info(procstat, fst, &ps, errbuf); 401221807Sstas if (error != 0) { 402221807Sstas printf("* error"); 403221807Sstas return; 404181905Sed } 405221807Sstas printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer); 406221807Sstas printf(" %6zd", ps.buffer_cnt); 407221807Sstas print_access_flags(fst->fs_fflags); 408221807Sstas} 409181905Sed 410221807Sstasstatic void 411221807Sstasprint_pts_info(struct procstat *procstat, struct filestat *fst) 412221807Sstas{ 413221807Sstas struct ptsstat pts; 414221807Sstas char errbuf[_POSIX2_LINE_MAX]; 415221807Sstas int error; 416221807Sstas 417221807Sstas error = procstat_get_pts_info(procstat, fst, &pts, errbuf); 418221807Sstas if (error != 0) { 419221807Sstas printf("* error"); 420221807Sstas return; 421181905Sed } 422181905Sed printf("* pseudo-terminal master "); 423221807Sstas if (nflg || !*pts.devname) { 424225847Sed printf("%#10jx", (uintmax_t)pts.dev); 425181905Sed } else { 426221807Sstas printf("%10s", pts.devname); 427181905Sed } 428221807Sstas print_access_flags(fst->fs_fflags); 429181905Sed} 430181905Sed 431221807Sstasstatic void 432250223Sjhbprint_sem_info(struct procstat *procstat, struct filestat *fst) 433250223Sjhb{ 434250223Sjhb struct semstat sem; 435250223Sjhb char errbuf[_POSIX2_LINE_MAX]; 436250223Sjhb char mode[15]; 437250223Sjhb int error; 438250223Sjhb 439250223Sjhb error = procstat_get_sem_info(procstat, fst, &sem, errbuf); 440250223Sjhb if (error != 0) { 441250223Sjhb printf("* error"); 442250223Sjhb return; 443250223Sjhb } 444250223Sjhb if (nflg) { 445250223Sjhb printf(" "); 446250223Sjhb (void)snprintf(mode, sizeof(mode), "%o", sem.mode); 447250223Sjhb } else { 448250223Sjhb printf(" %-15s", fst->fs_path != NULL ? fst->fs_path : "-"); 449250223Sjhb strmode(sem.mode, mode); 450250223Sjhb } 451250223Sjhb printf(" %10s %6u", mode, sem.value); 452250223Sjhb print_access_flags(fst->fs_fflags); 453250223Sjhb} 454250223Sjhb 455250223Sjhbstatic void 456233760Sjhbprint_shm_info(struct procstat *procstat, struct filestat *fst) 457233760Sjhb{ 458233760Sjhb struct shmstat shm; 459233760Sjhb char errbuf[_POSIX2_LINE_MAX]; 460233760Sjhb char mode[15]; 461233760Sjhb int error; 462233760Sjhb 463233760Sjhb error = procstat_get_shm_info(procstat, fst, &shm, errbuf); 464233760Sjhb if (error != 0) { 465233760Sjhb printf("* error"); 466233760Sjhb return; 467233760Sjhb } 468233760Sjhb if (nflg) { 469233760Sjhb printf(" "); 470233760Sjhb (void)snprintf(mode, sizeof(mode), "%o", shm.mode); 471233760Sjhb } else { 472233760Sjhb printf(" %-15s", fst->fs_path != NULL ? fst->fs_path : "-"); 473233760Sjhb strmode(shm.mode, mode); 474233760Sjhb } 475233760Sjhb printf(" %10s %6ju", mode, shm.size); 476233760Sjhb print_access_flags(fst->fs_fflags); 477233760Sjhb} 478233760Sjhb 479233760Sjhbstatic void 480221807Sstasprint_vnode_info(struct procstat *procstat, struct filestat *fst) 48153133Sgreen{ 482221807Sstas struct vnstat vn; 483221807Sstas char errbuf[_POSIX2_LINE_MAX]; 484221807Sstas char mode[15]; 485221807Sstas const char *badtype; 486221807Sstas int error; 48753133Sgreen 488221807Sstas badtype = NULL; 489221807Sstas error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); 490221807Sstas if (error != 0) 491221807Sstas badtype = errbuf; 492221807Sstas else if (vn.vn_type == PS_FST_VTYPE_VBAD) 493221807Sstas badtype = "bad"; 494221807Sstas else if (vn.vn_type == PS_FST_VTYPE_VNON) 495221807Sstas badtype = "none"; 496221807Sstas if (badtype != NULL) { 497221807Sstas printf(" - - %10s -", badtype); 498221807Sstas return; 49953133Sgreen } 500221807Sstas 501221807Sstas if (nflg) 502225893Sed printf(" %#5jx", (uintmax_t)vn.vn_fsid); 503221807Sstas else if (vn.vn_mntdir != NULL) 504221807Sstas (void)printf(" %-8s", vn.vn_mntdir); 505221807Sstas 506221807Sstas /* 507221807Sstas * Print access mode. 508221807Sstas */ 509221807Sstas if (nflg) 510221807Sstas (void)snprintf(mode, sizeof(mode), "%o", vn.vn_mode); 511221807Sstas else { 512221807Sstas strmode(vn.vn_mode, mode); 513221807Sstas } 514221816Simp (void)printf(" %6jd %10s", (intmax_t)vn.vn_fileid, mode); 515221807Sstas 516221807Sstas if (vn.vn_type == PS_FST_VTYPE_VBLK || vn.vn_type == PS_FST_VTYPE_VCHR) { 517221807Sstas if (nflg || !*vn.vn_devname) 518225847Sed printf(" %#6jx", (uintmax_t)vn.vn_dev); 519221807Sstas else { 520221807Sstas printf(" %6s", vn.vn_devname); 521221807Sstas } 522221807Sstas } else 523221819Simp printf(" %6ju", (uintmax_t)vn.vn_size); 524221807Sstas print_access_flags(fst->fs_fflags); 52553133Sgreen} 52653133Sgreen 527221807Sstasstatic void 528221807Sstasprint_access_flags(int flags) 5291590Srgrimes{ 530221807Sstas char rw[3]; 5311590Srgrimes 532221807Sstas rw[0] = '\0'; 533221807Sstas if (flags & PS_FST_FFLAG_READ) 534221807Sstas strcat(rw, "r"); 535221807Sstas if (flags & PS_FST_FFLAG_WRITE) 536221807Sstas strcat(rw, "w"); 537221807Sstas printf(" %2s", rw); 5381590Srgrimes} 5391590Srgrimes 54017808Speterint 541131293Sdwmalonegetfname(const char *filename) 5421590Srgrimes{ 5431590Srgrimes struct stat statbuf; 5441590Srgrimes DEVS *cur; 5451590Srgrimes 5461590Srgrimes if (stat(filename, &statbuf)) { 54727272Scharnier warn("%s", filename); 548221807Sstas return (0); 5491590Srgrimes } 55027272Scharnier if ((cur = malloc(sizeof(DEVS))) == NULL) 55127272Scharnier err(1, NULL); 5521590Srgrimes cur->next = devs; 5531590Srgrimes devs = cur; 5541590Srgrimes 5551590Srgrimes cur->ino = statbuf.st_ino; 55686100Sdwmalone cur->fsid = statbuf.st_dev; 5571590Srgrimes cur->name = filename; 558221807Sstas return (1); 5591590Srgrimes} 5601590Srgrimes 561221807Sstasstatic void 562131293Sdwmaloneusage(void) 5631590Srgrimes{ 5641590Srgrimes (void)fprintf(stderr, 565146466Sru "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] [file ...]\n"); 5661590Srgrimes exit(1); 5671590Srgrimes} 568