Index: projects/libprocstat/usr.bin/fstat/common.c =================================================================== --- projects/libprocstat/usr.bin/fstat/common.c (revision 195717) +++ projects/libprocstat/usr.bin/fstat/common.c (revision 195718) @@ -1,252 +1,256 @@ /*- * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _WANT_FILE #include #include #define _KERNEL #include #include #include #include #include #include #undef _KERNEL #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" int vflg = 0; void dprintf(FILE *file, const char *fmt, ...) { va_list ap; if (vflg != 0) { va_start(ap, fmt); vfprintf(file, fmt, ap); va_end(ap); } } int kvm_read_all(kvm_t *kd, unsigned long addr, void *buf, size_t nbytes) { ssize_t error; if (nbytes >= SSIZE_MAX) return (0); error = kvm_read(kd, addr, buf, nbytes); return (error == (ssize_t)(nbytes)); } -char * -kdevtoname(kvm_t *kd, struct cdev *dev) +int +kdevtoname(kvm_t *kd, struct cdev *dev, char *buf) { struct cdev si; + assert(buf); if (!kvm_read_all(kd, (unsigned long)dev, &si, sizeof(si))) - return (NULL); - return (strdup(si.__si_namebuf)); + return (1); + strlcpy(buf, si.__si_namebuf, SPECNAMELEN + 1); + return (0); } int ufs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn) { struct inode inode; if (!kvm_read_all(kd, (unsigned long)VTOI(vp), &inode, sizeof(inode))) { warnx("can't read inode at %p", (void *)VTOI(vp)); return (1); } /* * The st_dev from stat(2) is a dev_t. These kernel structures * contain cdev pointers. We need to convert to dev_t to make * comparisons */ vn->vn_fsid = dev2udev(kd, inode.i_dev); vn->vn_fileid = (long)inode.i_number; vn->vn_mode = (mode_t)inode.i_mode; vn->vn_size = (u_long)inode.i_size; return (0); } int devfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn) { struct devfs_dirent devfs_dirent; struct mount mount; if (!kvm_read_all(kd, (unsigned long)vp->v_data, &devfs_dirent, sizeof(devfs_dirent))) { warnx("can't read devfs_dirent at %p", (void *)vp->v_data); return (1); } if (!kvm_read_all(kd, (unsigned long)vp->v_mount, &mount, sizeof(mount))) { warnx("can't read mount at %p", (void *)vp->v_mount); return (1); } vn->vn_fsid = (long)mount.mnt_stat.f_fsid.val[0]; vn->vn_fileid = devfs_dirent.de_inode; vn->vn_mode = (devfs_dirent.de_mode & ~S_IFMT) | S_IFCHR; vn->vn_size = 0; return (0); } int nfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn) { struct nfsnode nfsnode; mode_t mode; if (!kvm_read_all(kd, (unsigned long)VTONFS(vp), &nfsnode, sizeof(nfsnode))) { warnx("can't read nfsnode at %p", (void *)VTONFS(vp)); return (1); } vn->vn_fsid = nfsnode.n_vattr.va_fsid; vn->vn_fileid = nfsnode.n_vattr.va_fileid; vn->vn_size = nfsnode.n_size; mode = (mode_t)nfsnode.n_vattr.va_mode; switch (vp->v_type) { case VREG: mode |= S_IFREG; break; case VDIR: mode |= S_IFDIR; break; case VBLK: mode |= S_IFBLK; break; case VCHR: mode |= S_IFCHR; break; case VLNK: mode |= S_IFLNK; break; case VSOCK: mode |= S_IFSOCK; break; case VFIFO: mode |= S_IFIFO; break; default: break; }; vn->vn_mode = mode; return (0); } /* * Read the cdev structure in the kernel in order to work out the * associated dev_t */ dev_t dev2udev(kvm_t *kd, struct cdev *dev) { struct cdev_priv priv; + assert(kd); if (kvm_read_all(kd, (unsigned long)cdev2priv(dev), &priv, sizeof(priv))) { return ((dev_t)priv.cdp_inode); } else { dprintf(stderr, "can't convert cdev *%p to a dev_t\n", dev); return -1; } } #ifdef ZFS void * getvnodedata(struct vnode *vp) { return (vp->v_data); } struct mount * getvnodemount(struct vnode *vp) { return (vp->v_mount); } #endif Index: projects/libprocstat/usr.bin/fstat/common.h =================================================================== --- projects/libprocstat/usr.bin/fstat/common.h (revision 195717) +++ projects/libprocstat/usr.bin/fstat/common.h (revision 195718) @@ -1,105 +1,107 @@ /*- * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef __COMMON_H__ #define __COMMON_H__ struct filestat { int fs_type; /* Descriptor type. */ int fs_flags; /* filestat specific flags. */ int fs_fflags; /* Descriptor access flags. */ int fs_fd; /* File descriptor number. */ void *fs_typedep; /* Type dependent data. */ STAILQ_ENTRY(filestat) next; }; struct vnstat { dev_t vn_dev; + char vn_devname[SPECNAMELEN + 1]; int vn_type; long vn_fsid; long vn_fileid; mode_t vn_mode; u_long vn_size; char *mntdir; }; struct ptsstat { dev_t dev; + char devname[SPECNAMELEN + 1]; }; struct pipestat { caddr_t addr; caddr_t peer; size_t buffer_cnt; }; struct sockstat { int type; int proto; int dom_family; caddr_t so_addr; caddr_t so_pcb; caddr_t inp_ppcb; caddr_t unp_conn; int so_snd_sb_state; int so_rcv_sb_state; char dname[32]; }; STAILQ_HEAD(filestat_list, filestat); extern int vflg; dev_t dev2udev(kvm_t *kd, struct cdev *dev); void dprintf(FILE *file, const char *fmt, ...); -char *kdevtoname(kvm_t *kd, struct cdev *dev); +int kdevtoname(kvm_t *kd, struct cdev *dev, char *); int kvm_read_all(kvm_t *kd, unsigned long addr, void *buf, size_t nbytes); /* * Filesystems specific access routines. */ int devfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int isofs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int msdosfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int nfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); int ufs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); #ifdef ZFS int zfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn); void *getvnodedata(struct vnode *vp); struct mount *getvnodemount(struct vnode *vp); #endif #endif /* __COMMON_H__ */ Index: projects/libprocstat/usr.bin/fstat/fstat.c =================================================================== --- projects/libprocstat/usr.bin/fstat/fstat.c (revision 195717) +++ projects/libprocstat/usr.bin/fstat/fstat.c (revision 195718) @@ -1,587 +1,584 @@ /*- * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)fstat.c 8.3 (Berkeley) 5/2/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _WANT_FILE #include #include #define _KERNEL #include #include #include #include #include #include #undef _KERNEL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "functions.h" #include "libprocstat.h" int fsflg, /* show files on same filesystem as file(s) argument */ pflg, /* show files open by a particular pid */ uflg; /* show files open by a particular (effective) user */ int checkfile; /* true if restricting to particular files or filesystems */ int nflg; /* (numerical) display f.s. and rdev as dev_t */ int mflg; /* include memory-mapped files */ typedef struct devs { struct devs *next; long fsid; long ino; const char *name; } DEVS; DEVS *devs; char *memf, *nlistf; static void fstat1(int what, int arg); static void dofiles(struct procstat *procstat, struct kinfo_proc *p); void dofiles_kinfo(struct kinfo_proc *kp); void dommap(struct kinfo_proc *kp); void vtrans(struct vnode *vp, int i, int flag, const char *uname, const char *cmd, int pid); char *getmnton(struct mount *m); void pipetrans(struct pipe *pi, int i, int flag, const char *uname, const char *cmd, int pid); void socktrans(struct socket *sock, int i, const char *uname, const char *cmd, int pid); void ptstrans(struct tty *tp, int i, int flag, const char *uname, const char *cmd, int pid); void getinetproto(int number); int getfname(const char *filename); void usage(void); void vtrans_kinfo(struct kinfo_file *, int i, int flag, const char *uname, const char *cmd, int pid); static void print_file_info(struct procstat *procstat, struct filestat *fst, const char *uname, const char *cmd, int pid); static void print_socket_info(struct procstat *procstat, struct filestat *fst); static void print_pipe_info(struct procstat *procstat, struct filestat *fst); static void print_pts_info(struct procstat *procstat, struct filestat *fst); static void print_vnode_info(struct procstat *procstat, struct filestat *fst); static void print_access_flags(int flags); int do_fstat(int argc, char **argv) { struct passwd *passwd; int arg, ch, what; arg = 0; what = KERN_PROC_PROC; nlistf = memf = NULL; while ((ch = getopt(argc, argv, "fmnp:u:vN:M:")) != -1) switch((char)ch) { case 'f': fsflg = 1; break; case 'M': memf = optarg; break; case 'N': nlistf = optarg; break; case 'm': mflg = 1; break; case 'n': nflg = 1; break; case 'p': if (pflg++) usage(); if (!isdigit(*optarg)) { warnx("-p requires a process id"); usage(); } what = KERN_PROC_PID; arg = atoi(optarg); break; case 'u': if (uflg++) usage(); if (!(passwd = getpwnam(optarg))) errx(1, "%s: unknown uid", optarg); what = KERN_PROC_UID; arg = passwd->pw_uid; break; case 'v': vflg = 1; break; case '?': default: usage(); } if (*(argv += optind)) { for (; *argv; ++argv) { if (getfname(*argv)) checkfile = 1; } if (!checkfile) /* file(s) specified, but none accessable */ exit(1); } if (fsflg && !checkfile) { /* -f with no files means use wd */ if (getfname(".") == 0) exit(1); checkfile = 1; } fstat1(what, arg); exit(0); } static void fstat1(int what, int arg) { struct kinfo_proc *p; struct procstat *procstat; int cnt; int i; procstat = procstat_open(nlistf, memf); if (procstat == NULL) errx(1, "procstat_open()"); p = procstat_getprocs(procstat, what, arg, &cnt); if (p == NULL) errx(1, "procstat_getprocs()"); /* * Print header. */ if (nflg) printf("%s", "USER CMD PID FD DEV INUM MODE SZ|DV R/W"); else printf("%s", "USER CMD PID FD MOUNT INUM MODE SZ|DV R/W"); if (checkfile && fsflg == 0) printf(" NAME\n"); else putchar('\n'); /* * Go through the process list. */ for (i = 0; i < cnt; i++) { if (p[i].ki_stat == SZOMB) continue; dofiles(procstat, &p[i]); /* if (mflg) dommap(procstat, &p[i]); */ } free(p); procstat_close(procstat); } static void dofiles(struct procstat *procstat, struct kinfo_proc *kp) { struct filestat_list *head; const char *cmd; const char *uname; int pid; struct filestat *fst; uname = user_from_uid(kp->ki_uid, 0); pid = kp->ki_pid; cmd = kp->ki_comm; head = procstat_getfiles(procstat, kp); if (head == NULL) return; STAILQ_FOREACH(fst, head, next) print_file_info(procstat, fst, uname, cmd, pid); } static void print_file_info(struct procstat *procstat, struct filestat *fst, const char *uname, const char *cmd, int pid) { const char *filename; struct vnstat vn; int error; int fsmatch = 0; DEVS *d; filename = NULL; if (checkfile != 0) { if (fst->fs_type != PS_FST_TYPE_VNODE && fst->fs_type == PS_FST_TYPE_FIFO) return; error = procstat_get_vnode_info(procstat, fst, &vn, NULL); if (error != 0) return; for (d = devs; d != NULL; d = d->next) if (d->fsid == vn.vn_fsid) { fsmatch = 1; if (d->ino == vn.vn_fileid) { filename = d->name; break; } } if (fsmatch == 0 || (filename == NULL && fsflg == 0)) return; } /* * Print entry prefix. */ printf("%-8.8s %-10s %5d", uname, cmd, pid); switch(fst->fs_fd) { case PS_FST_FD_TEXT: printf(" text"); break; case PS_FST_FD_CDIR: printf(" wd"); break; case PS_FST_FD_RDIR: printf(" root"); break; case PS_FST_FD_TRACE: printf(" tr"); break; case PS_FST_FD_MMAP: printf(" mmap"); break; case PS_FST_FD_JAIL: printf(" jail"); break; default: printf(" %4d", fst->fs_fd); break; } /* * Print type-specific data. */ switch (fst->fs_type) { case PS_FST_TYPE_FIFO: case PS_FST_TYPE_VNODE: print_vnode_info(procstat, fst); break; case PS_FST_TYPE_SOCKET: print_socket_info(procstat, fst); break; case PS_FST_TYPE_PIPE: print_pipe_info(procstat, fst); break; case PS_FST_TYPE_PTS: print_pts_info(procstat, fst); break; default: dprintf(stderr, "unknown file type %d for file %d of pid %d\n", fst->fs_type, fst->fs_fd, pid); } if (filename && !fsflg) printf(" %s", filename); putchar('\n'); } static void print_socket_info(struct procstat *procstat, struct filestat *fst) { static const char *stypename[] = { "unused", /* 0 */ "stream", /* 1 */ "dgram", /* 2 */ "raw", /* 3 */ "rdm", /* 4 */ "seqpak" /* 5 */ }; #define STYPEMAX 5 struct sockstat sock; char errbuf[_POSIX2_LINE_MAX]; static int isopen; struct protoent *pe; int error; error = procstat_get_socket_info(procstat, fst, &sock, errbuf); if (error != 0) { printf("* error"); return; } if (sock.type > STYPEMAX) printf("* %s ?%d", sock.dname, sock.type); else printf("* %s %s", sock.dname, stypename[sock.type]); /* * protocol specific formatting * * Try to find interesting things to print. For tcp, the interesting * thing is the address of the tcpcb, for udp and others, just the * inpcb (socket pcb). For unix domain, its the address of the socket * pcb and the address of the connected pcb (if connected). Otherwise * just print the protocol number and address of the socket itself. * The idea is not to duplicate netstat, but to make available enough * information for further analysis. */ switch (sock.dom_family) { case AF_INET: case AF_INET6: if (!isopen) setprotoent(++isopen); if ((pe = getprotobynumber(sock.proto)) != NULL) printf(" %s", pe->p_name); else printf(" %d", sock.proto); if (sock.proto == IPPROTO_TCP ) { if (sock.inp_ppcb != 0) printf(" %lx", (u_long)sock.inp_ppcb); } else if (sock.so_pcb != 0) printf(" %lx", (u_long)sock.so_pcb); break; case AF_UNIX: /* print address of pcb and connected pcb */ if (sock.so_pcb != 0) { printf(" %lx", (u_long)sock.so_pcb); if (sock.unp_conn) { char shoconn[4], *cp; cp = shoconn; if (!(sock.so_rcv_sb_state & SBS_CANTRCVMORE)) *cp++ = '<'; *cp++ = '-'; if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE)) *cp++ = '>'; *cp = '\0'; printf(" %s %lx", shoconn, (u_long)sock.unp_conn); } } break; default: /* print protocol number and socket address */ printf(" %d %lx", sock.proto, (u_long)sock.so_addr); } } static void print_pipe_info(struct procstat *procstat, struct filestat *fst) { struct pipestat pipe; char errbuf[_POSIX2_LINE_MAX]; int error; error = procstat_get_pipe_info(procstat, fst, &pipe, errbuf); if (error != 0) { printf("* error"); return; } printf("* pipe %8lx <-> %8lx", (u_long)pipe.addr, (u_long)pipe.peer); printf(" %6zd", pipe.buffer_cnt); print_access_flags(fst->fs_fflags); } static void print_pts_info(struct procstat *procstat, struct filestat *fst) { struct ptsstat pts; char errbuf[_POSIX2_LINE_MAX]; int error; error = procstat_get_pts_info(procstat, fst, &pts, errbuf); if (error != 0) { printf("* error"); return; } printf("* pseudo-terminal master "); - if (nflg) { + if (nflg || !*pts.devname) { printf("%10d,%-2d", major(pts.dev), minor(pts.dev)); - } /* else { - printf("%10s", pts.name); - } */ + } else { + printf("%10s", pts.devname); + } print_access_flags(fst->fs_fflags); } static void print_vnode_info(struct procstat *procstat, struct filestat *fst) { struct vnstat vn; - char *name; const char *badtype; char errbuf[_POSIX2_LINE_MAX]; char mode[15]; int error; badtype = NULL; error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); if (error != 0) badtype = errbuf; else if (vn.vn_type == PS_FST_VTYPE_VBAD) badtype = "bad"; else if (vn.vn_type == PS_FST_VTYPE_VNON) badtype = "none"; if (badtype != NULL) { printf(" - - %10s -", badtype); return; } if (nflg) printf(" %2d,%-2d", major(vn.vn_fsid), minor(vn.vn_fsid)); else if (vn.mntdir != NULL) (void)printf(" %-8s", vn.mntdir); /* * Print access mode. */ if (nflg) (void)snprintf(mode, sizeof(mode), "%o", vn.vn_mode); else { strmode(vn.vn_mode, mode); } (void)printf(" %6ld %10s", vn.vn_fileid, mode); if (vn.vn_type == PS_FST_VTYPE_VBLK || vn.vn_type == PS_FST_VTYPE_VCHR) { - name = NULL; -// name = fst->vnode.dev.name; - if (nflg || !name) + if (nflg || !*vn.vn_devname) printf(" %2d,%-2d", major(vn.vn_dev), minor(vn.vn_dev)); else { - printf(" %6s", name); + printf(" %6s", vn.vn_devname); } } else printf(" %6lu", vn.vn_size); print_access_flags(fst->fs_fflags); } static void print_access_flags(int flags) { char rw[3]; rw[0] = '\0'; if (flags & PS_FST_FFLAG_READ) strcat(rw, "r"); if (flags & PS_FST_FFLAG_WRITE) strcat(rw, "w"); printf(" %2s", rw); } int getfname(const char *filename) { struct stat statbuf; DEVS *cur; if (stat(filename, &statbuf)) { warn("%s", filename); return (0); } if ((cur = malloc(sizeof(DEVS))) == NULL) err(1, NULL); cur->next = devs; devs = cur; cur->ino = statbuf.st_ino; cur->fsid = statbuf.st_dev; cur->name = filename; return (1); } void usage(void) { (void)fprintf(stderr, "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] [file ...]\n"); exit(1); } Index: projects/libprocstat/usr.bin/fstat/libprocstat.c =================================================================== --- projects/libprocstat/usr.bin/fstat/libprocstat.c (revision 195717) +++ projects/libprocstat/usr.bin/fstat/libprocstat.c (revision 195718) @@ -1,786 +1,791 @@ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _WANT_FILE #include #include #define _KERNEL #include #include #include #include #include #include #undef _KERNEL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "libprocstat.h" static struct { int vtype; int fst_vtype; } vt2fst[] = { { VNON, PS_FST_VTYPE_VNON }, { VREG, PS_FST_VTYPE_VREG }, { VDIR, PS_FST_VTYPE_VDIR }, { VBLK, PS_FST_VTYPE_VBLK }, { VCHR, PS_FST_VTYPE_VCHR }, { VLNK, PS_FST_VTYPE_VLNK }, { VSOCK, PS_FST_VTYPE_VSOCK }, { VFIFO, PS_FST_VTYPE_VFIFO }, { VBAD, PS_FST_VTYPE_VBAD } }; #define NVFTYPES (sizeof(vt2fst) / sizeof(*vt2fst)) static struct { int flag; int fst_flag; } fstflags[] = { { FREAD, PS_FST_FFLAG_READ }, { FWRITE, PS_FST_FFLAG_WRITE }, { O_NONBLOCK, PS_FST_FFLAG_NONBLOCK }, { O_APPEND, PS_FST_FFLAG_APPEND }, { O_SHLOCK, PS_FST_FFLAG_SHLOCK }, { O_EXLOCK, PS_FST_FFLAG_EXLOCK }, { O_ASYNC, PS_FST_FFLAG_ASYNC }, { O_SYNC, PS_FST_FFLAG_SYNC }, { O_NOFOLLOW, PS_FST_FFLAG_NOFOLLOW }, { O_CREAT, PS_FST_FFLAG_CREAT }, { O_TRUNC, PS_FST_FFLAG_TRUNC }, { O_EXCL, PS_FST_FFLAG_EXCL } }; #define NFSTFLAGS (sizeof(fstflags) / sizeof(*fstflags)) char *getmnton(kvm_t *kd, struct mount *m); void socktrans(kvm_t *kd, struct socket *sock, int fd, int flags, struct filestat *fst); int procstat_get_vnode_info_kvm(kvm_t *kd, struct filestat *fst, struct vnstat *vn, char *errbuf); int procstat_get_vnode_info_sysctl(struct filestat *fst, struct vnstat *vn, char *errbuf); int procstat_get_pipe_info_sysctl(struct filestat *fst, struct pipestat *pipe, char *errbuf); int procstat_get_pipe_info_kvm(kvm_t *kd, struct filestat *fst, struct pipestat *pipe, char *errbuf); int procstat_get_pts_info_sysctl(struct filestat *fst, struct ptsstat *pts, char *errbuf); int procstat_get_pts_info_kvm(kvm_t *kd, struct filestat *fst, struct ptsstat *pts, char *errbuf); int procstat_get_socket_info_sysctl(struct filestat *fst, struct sockstat *sock, char *errbuf); int procstat_get_socket_info_kvm(kvm_t *kd, struct filestat *fst, struct sockstat *sock, char *errbuf); static int to_filestat_flags(int flags); /* * Filesystem specific handlers. */ #define FSTYPE(fst) {#fst, fst##_filestat} struct { const char *tag; int (*handler)(kvm_t *kd, struct vnode *vp, struct vnstat *vn); } fstypes[] = { FSTYPE(ufs), FSTYPE(devfs), FSTYPE(nfs), FSTYPE(msdosfs), FSTYPE(isofs), #ifdef ZFS FSTYPE(zfs), #endif /* FSTYPE(ntfs), FSTYPE(nwfs), FSTYPE(smbfs), FSTYPE(udf), */ }; #define NTYPES (sizeof(fstypes) / sizeof(*fstypes)) #define PROCSTAT_KVM 1 #define PROCSTAT_SYSCTL 2 void procstat_close(struct procstat *procstat) { assert(procstat); if (procstat->type == PROCSTAT_KVM) kvm_close(procstat->kd); } struct procstat * procstat_open(const char *nlistf, const char *memf) { kvm_t *kd; char buf[_POSIX2_LINE_MAX]; struct procstat *procstat; procstat = calloc(1, sizeof(*procstat)); if (procstat == NULL) { warn("malloc()"); return (NULL); } if (memf != NULL) { kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf); if (kd == NULL) { warnx("kvm_openfiles(): %s", buf); free(procstat); return (NULL); } procstat->type = PROCSTAT_KVM; procstat->kd = kd; } else { procstat->type = PROCSTAT_SYSCTL; } return (procstat); } struct kinfo_proc * procstat_getprocs(struct procstat *procstat, int what, int arg, unsigned int *count) { struct kinfo_proc *p0, *p; size_t len; int name[4]; int error; assert(procstat); assert(count); p = NULL; if (procstat->type == PROCSTAT_KVM) { p0 = kvm_getprocs(procstat->kd, what, arg, count); if (p0 == NULL || count == 0) return (NULL); len = *count * sizeof(*p); p = malloc(len); if (p == NULL) { warnx("malloc(%zd)", len); goto fail; } bcopy(p0, p, len); return (p); } else if (procstat->type == PROCSTAT_SYSCTL) { len = 0; name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = what; name[3] = arg; error = sysctl(name, 4, NULL, &len, NULL, 0); if (error < 0) { warn("sysctl(kern.proc)"); goto fail; } if (len == 0) { warnx("no processes?"); goto fail; } p = malloc(len); if (p == NULL) { warnx("malloc(%zd)", len); goto fail; } error = sysctl(name, 4, p, &len, NULL, 0); if (error < 0) { warn("sysctl(kern.proc)"); goto fail; } /* Perform simple consistency checks. */ if ((len % sizeof(*p)) != 0 || p->ki_structsize != sizeof(*p)) { warnx("kinfo_proc structure size mismatch"); goto fail; } *count = len / sizeof(*p); return (p); } else { warnx("unknown access method"); return (NULL); } fail: if (p) free(p); return (NULL); } struct filestat_list * procstat_getfiles(struct procstat *procstat, struct kinfo_proc *kp) { if (procstat->type == PROCSTAT_SYSCTL) return (procstat_getfiles_sysctl(kp)); else if (procstat->type == PROCSTAT_KVM) return (procstat_getfiles_kvm(procstat->kd, kp)); else return (NULL); } static struct filestat * filestat_new_entry(struct vnode *vp, int type, int fd, int fflags) { struct filestat *entry; entry = calloc(1, sizeof(*entry)); if (entry == NULL) { warn("malloc()"); return (NULL); } entry->fs_typedep = vp; entry->fs_fflags = fflags; entry->fs_fd = fd; entry->fs_type = type; return (entry); } struct filestat_list * procstat_getfiles_kvm(kvm_t *kd, struct kinfo_proc *kp) { int i; struct file file; struct filedesc filed; unsigned int nfiles; struct file **ofiles; struct filestat *entry; struct filestat_list *head; int type; void *data; assert(kd); if (kp->ki_fd == NULL) return (NULL); if (!kvm_read_all(kd, (unsigned long)kp->ki_fd, &filed, sizeof(filed))) { warnx("can't read filedesc at %p\n", (void *)kp->ki_fd); return (NULL); } /* * Allocate list head. */ head = malloc(sizeof(*head)); if (head == NULL) return (NULL); STAILQ_INIT(head); /* root directory vnode, if one. */ if (filed.fd_rdir) { entry = filestat_new_entry(filed.fd_rdir, PS_FST_TYPE_VNODE, PS_FST_FD_RDIR, PS_FST_FFLAG_READ); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } /* current working directory vnode. */ if (filed.fd_cdir) { entry = filestat_new_entry(filed.fd_cdir, PS_FST_TYPE_VNODE, PS_FST_FD_CDIR, PS_FST_FFLAG_READ); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } /* jail root, if any. */ if (filed.fd_jdir) { entry = filestat_new_entry(filed.fd_jdir, PS_FST_TYPE_VNODE, PS_FST_FD_JAIL, PS_FST_FFLAG_READ); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } /* ktrace vnode, if one */ if (kp->ki_tracep) { entry = filestat_new_entry(kp->ki_tracep, PS_FST_TYPE_VNODE, PS_FST_FD_TRACE, PS_FST_FFLAG_READ | PS_FST_FFLAG_WRITE); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } /* text vnode, if one */ if (kp->ki_textvp) { entry = filestat_new_entry(kp->ki_textvp, PS_FST_TYPE_VNODE, PS_FST_FD_TEXT, PS_FST_FFLAG_READ); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } nfiles = filed.fd_lastfile + 1; ofiles = malloc(nfiles * sizeof(struct file *)); if (ofiles == NULL) { warn("malloc(%zd)", nfiles * sizeof(struct file *)); goto exit; } if (!kvm_read_all(kd, (unsigned long)filed.fd_ofiles, ofiles, nfiles * sizeof(struct file *))) { warn("cannot read file structures at %p\n", (void *)filed.fd_ofiles); free(ofiles); goto exit; } for (i = 0; i <= filed.fd_lastfile; i++) { if (ofiles[i] == NULL) continue; if (!kvm_read_all(kd, (unsigned long)ofiles[i], &file, sizeof(struct file))) { warn("can't read file %d at %p\n", i, (void *)ofiles[i]); continue; } switch (file.f_type) { case DTYPE_VNODE: type = PS_FST_TYPE_VNODE; data = file.f_vnode; break; case DTYPE_SOCKET: type = PS_FST_TYPE_SOCKET; data = file.f_data; break; case DTYPE_PIPE: type = PS_FST_TYPE_PIPE; data = file.f_data; break; case DTYPE_FIFO: type = PS_FST_TYPE_FIFO; data = file.f_vnode; break; #ifdef DTYPE_PTS case DTYPE_PTS: type = PS_FST_TYPE_PTS; data = file.f_data; break; #endif default: warnx("unknown file type %d for file %d\n", file.f_type, i); continue; } entry = filestat_new_entry(data, type, i, to_filestat_flags(file.f_flag)); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } free(ofiles); exit: return (head); } static int to_filestat_flags(int flags) { int fst_flags; unsigned int i; fst_flags = 0; for (i = 0; i < NFSTFLAGS; i++) if (flags & fstflags[i].flag) fst_flags |= fstflags[i].fst_flag; return (fst_flags); } struct filestat_list * procstat_getfiles_sysctl(struct kinfo_proc *kp __unused) { return (NULL); } static int vntype2psfsttype(int type) { unsigned int i, fst_type; fst_type = PS_FST_VTYPE_UNKNOWN; for (i = 0; i < NVFTYPES; i++) { if (type == vt2fst[i].vtype) { fst_type = vt2fst[i].fst_vtype; break; } } return (fst_type); } char * getmnton(kvm_t *kd, struct mount *m) { static struct mount mnt; static struct mtab { struct mtab *next; struct mount *m; char mntonname[MNAMELEN + 1]; } *mhead = NULL; struct mtab *mt; for (mt = mhead; mt != NULL; mt = mt->next) if (m == mt->m) return (mt->mntonname); if (!kvm_read_all(kd, (unsigned long)m, &mnt, sizeof(struct mount))) { warnx("can't read mount table at %p", (void *)m); return (NULL); } if ((mt = malloc(sizeof (struct mtab))) == NULL) err(1, NULL); mt->m = m; bcopy(&mnt.mnt_stat.f_mntonname[0], &mt->mntonname[0], MNAMELEN); mnt.mnt_stat.f_mntonname[MNAMELEN] = '\0'; mt->next = mhead; mhead = mt; return (mt->mntonname); } int procstat_get_pipe_info(struct procstat *procstat, struct filestat *fst, struct pipestat *pipe, char *errbuf) { assert(pipe); if (procstat->type == PROCSTAT_KVM) { return (procstat_get_pipe_info_kvm(procstat->kd, fst, pipe, errbuf)); } else if (procstat->type == PROCSTAT_SYSCTL) { return (procstat_get_pipe_info_sysctl(fst, pipe, errbuf)); } else { warnx("unknow access method: %d", procstat->type); snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } } int procstat_get_pipe_info_kvm(kvm_t *kd, struct filestat *fst, struct pipestat *pipe, char *errbuf) { struct pipe pi; void *pipep; assert(kd); assert(pipe); assert(fst); bzero(pipe, sizeof(*pipe)); pipep = fst->fs_typedep; if (pipep == NULL) goto fail; if (!kvm_read_all(kd, (unsigned long)pipep, &pi, sizeof(struct pipe))) { warnx("can't read pipe at %p", (void *)pipep); goto fail; } pipe->addr = (caddr_t)pipep; pipe->peer = (caddr_t)pi.pipe_peer; pipe->buffer_cnt = pi.pipe_buffer.cnt; return (0); fail: snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } int procstat_get_pipe_info_sysctl(struct filestat *fst, struct pipestat *pipe, char *errbuf) { warnx("not implemented: %s:%d", __FUNCTION__, __LINE__); snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } int procstat_get_pts_info(struct procstat *procstat, struct filestat *fst, struct ptsstat *pts, char *errbuf) { assert(pts); if (procstat->type == PROCSTAT_KVM) { return (procstat_get_pts_info_kvm(procstat->kd, fst, pts, errbuf)); } else if (procstat->type == PROCSTAT_SYSCTL) { return (procstat_get_pts_info_sysctl(fst, pts, errbuf)); } else { warnx("unknow access method: %d", procstat->type); snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } } int procstat_get_pts_info_kvm(kvm_t *kd, struct filestat *fst, struct ptsstat *pts, char *errbuf) { struct tty tty; void *ttyp; assert(kd); assert(pts); assert(fst); bzero(pts, sizeof(*pts)); ttyp = fst->fs_typedep; if (ttyp == NULL) goto fail; if (!kvm_read_all(kd, (unsigned long)ttyp, &tty, sizeof(struct tty))) { warnx("can't read tty at %p", (void *)ttyp); goto fail; } - pts->dev = dev2udev(kd, tty.t_dev); + if (tty.t_dev != NULL) { + pts->dev = dev2udev(kd, tty.t_dev); + (void)kdevtoname(kd, tty.t_dev, pts->devname); + } return (0); fail: snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } int procstat_get_pts_info_sysctl(struct filestat *fst, struct ptsstat *pts, char *errbuf) { warnx("not implemented: %s:%d", __FUNCTION__, __LINE__); snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } int procstat_get_vnode_info(struct procstat *procstat, struct filestat *fst, struct vnstat *vn, char *errbuf) { assert(vn); if (procstat->type == PROCSTAT_KVM) { return (procstat_get_vnode_info_kvm(procstat->kd, fst, vn, errbuf)); } else if (procstat->type == PROCSTAT_SYSCTL) { return (procstat_get_vnode_info_sysctl(fst, vn, errbuf)); } else { warnx("unknow access method: %d", procstat->type); snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } } int procstat_get_vnode_info_kvm(kvm_t *kd, struct filestat *fst, struct vnstat *vn, char *errbuf) { char tagstr[12]; int error; int found; struct vnode vnode; void *vp; unsigned int i; assert(kd); assert(vn); assert(fst); vp = fst->fs_typedep; if (vp == NULL) goto fail; error = kvm_read_all(kd, (unsigned long)vp, &vnode, sizeof(vnode)); if (error == 0) { warnx("can't read vnode at %p\n", (void *)vp); goto fail; } bzero(vn, sizeof(*vn)); vn->vn_type = vntype2psfsttype(vnode.v_type); if (vnode.v_type == VNON || vnode.v_type == VBAD) return (0); error = kvm_read_all(kd, (unsigned long)vnode.v_tag, tagstr, sizeof(tagstr)); if (error == 0) { warnx("can't read v_tag at %p\n", (void *)vp); goto fail; } tagstr[sizeof(tagstr) - 1] = '\0'; /* * Find appropriate handler. */ for (i = 0, found = 0; i < NTYPES; i++) if (!strcmp(fstypes[i].tag, tagstr)) { if (fstypes[i].handler(kd, &vnode, vn) != 0) { goto fail; } break; } if (i == NTYPES) { snprintf(errbuf, _POSIX2_LINE_MAX, "?(%s)", tagstr); return (1); } vn->mntdir = getmnton(kd, vnode.v_mount); - if (vnode.v_rdev != NULL) + if (vnode.v_rdev != NULL) { vn->vn_dev = dev2udev(kd, vnode.v_rdev); + (void)kdevtoname(kd, vnode.v_rdev, vn->vn_devname); + } return (0); fail: snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } int procstat_get_vnode_info_sysctl(struct filestat *fst, struct vnstat *vn, char *errbuf) { warnx("not implemented: %s:%d", __FUNCTION__, __LINE__); snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } int procstat_get_socket_info(struct procstat *procstat, struct filestat *fst, struct sockstat *sock, char *errbuf) { assert(sock); if (procstat->type == PROCSTAT_KVM) { return (procstat_get_socket_info_kvm(procstat->kd, fst, sock, errbuf)); } else if (procstat->type == PROCSTAT_SYSCTL) { return (procstat_get_socket_info_sysctl(fst, sock, errbuf)); } else { warnx("unknow access method: %d", procstat->type); snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } } int procstat_get_socket_info_kvm(kvm_t *kd, struct filestat *fst, struct sockstat *sock, char *errbuf) { struct socket s; struct protosw proto; struct domain dom; struct inpcb inpcb; struct unpcb unpcb; ssize_t len; void *so; assert(kd); assert(sock); assert(fst); bzero(sock, sizeof(*sock)); so = fst->fs_typedep; if (so == NULL) goto fail; sock->so_addr = (caddr_t)so; /* fill in socket */ if (!kvm_read_all(kd, (unsigned long)so, &s, sizeof(struct socket))) { warnx("can't read sock at %p", (void *)so); goto fail; } /* fill in protosw entry */ if (!kvm_read_all(kd, (unsigned long)s.so_proto, &proto, sizeof(struct protosw))) { warnx("can't read protosw at %p", (void *)s.so_proto); goto fail; } /* fill in domain */ if (!kvm_read_all(kd, (unsigned long)proto.pr_domain, &dom, sizeof(struct domain))) { warnx("can't read domain at %p", (void *)proto.pr_domain); goto fail; } if ((len = kvm_read(kd, (unsigned long)dom.dom_name, sock->dname, sizeof(sock->dname) - 1)) < 0) { warnx("can't read domain name at %p", (void *)dom.dom_name); sock->dname[0] = '\0'; } else sock->dname[len] = '\0'; /* * Fill in known data. */ sock->type = s.so_type; sock->proto = proto.pr_protocol; sock->dom_family = dom.dom_family; sock->so_pcb = s.so_pcb; /* * Protocol specific data. */ switch(dom.dom_family) { case AF_INET: case AF_INET6: if (proto.pr_protocol == IPPROTO_TCP ) { if (s.so_pcb) { if (kvm_read(kd, (u_long)s.so_pcb, (char *)&inpcb, sizeof(struct inpcb)) != sizeof(struct inpcb)) { warnx("can't read inpcb at %p\n", (void *)s.so_pcb); } else sock->inp_ppcb = (caddr_t)inpcb.inp_ppcb; } } break; case AF_UNIX: if (s.so_pcb) { if (kvm_read(kd, (u_long)s.so_pcb, (char *)&unpcb, sizeof(struct unpcb)) != sizeof(struct unpcb)){ warnx("can't read unpcb at %p\n", (void *)s.so_pcb); } else if (unpcb.unp_conn) { sock->so_rcv_sb_state = s.so_rcv.sb_state; sock->so_snd_sb_state = s.so_snd.sb_state; sock->unp_conn = (caddr_t)unpcb.unp_conn; } } break; default: break; } return (0); fail: snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } int procstat_get_socket_info_sysctl(struct filestat *fst, struct sockstat *sock, char *errbuf) { warnx("not implemented: %s:%d", __FUNCTION__, __LINE__); snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); }