diff options
Diffstat (limited to 'usr.bin/fstat')
-rw-r--r-- | usr.bin/fstat/Makefile | 20 | ||||
-rw-r--r-- | usr.bin/fstat/cd9660.c | 79 | ||||
-rw-r--r-- | usr.bin/fstat/fstat.c | 986 | ||||
-rw-r--r-- | usr.bin/fstat/functions.h (renamed from usr.bin/fstat/fstat.h) | 56 | ||||
-rw-r--r-- | usr.bin/fstat/fuser.1 | 148 | ||||
-rw-r--r-- | usr.bin/fstat/fuser.c | 369 | ||||
-rw-r--r-- | usr.bin/fstat/main.c | 49 | ||||
-rw-r--r-- | usr.bin/fstat/msdosfs.c | 150 | ||||
-rw-r--r-- | usr.bin/fstat/zfs.c | 136 | ||||
-rw-r--r-- | usr.bin/fstat/zfs/Makefile | 23 |
10 files changed, 817 insertions, 1199 deletions
diff --git a/usr.bin/fstat/Makefile b/usr.bin/fstat/Makefile index 23e907b..c59e123 100644 --- a/usr.bin/fstat/Makefile +++ b/usr.bin/fstat/Makefile @@ -1,24 +1,12 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 # $FreeBSD$ -.include <bsd.own.mk> - PROG= fstat -SRCS= cd9660.c fstat.c msdosfs.c +SRCS= fstat.c fuser.c main.c +LINKS= ${BINDIR}/fstat ${BINDIR}/fuser DPADD= ${LIBKVM} -LDADD= -lkvm -BINGRP= kmem -BINMODE=2555 - -CFLAGS+=-D_KVM_VNODE +LDADD= -lkvm -lutil -lprocstat -# XXX This is a hack. -.if ${MK_CDDL} != "no" -CFLAGS+= -DZFS -OBJS+= zfs/zfs.o -SUBDIR= zfs -zfs/zfs.o: .PHONY - @cd ${.CURDIR}/zfs && ${MAKE} zfs.o -.endif +MAN1= fuser.1 fstat.1 .include <bsd.prog.mk> diff --git a/usr.bin/fstat/cd9660.c b/usr.bin/fstat/cd9660.c deleted file mode 100644 index 1c26e8d..0000000 --- a/usr.bin/fstat/cd9660.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2000 Peter Edwards - * Copyright (c) 1988, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by Peter Edwards - * - * 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. - */ - -/* - * XXX - - * This had to be separated from fstat.c because cd9660s has namespace - * conflicts with UFS. - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/vnode.h> -#include <sys/mount.h> - -#include <isofs/cd9660/cd9660_node.h> - -#include <kvm.h> -#include <stdio.h> - -#include "fstat.h" - -int -isofs_filestat(struct vnode *vp, struct filestat *fsp) -{ - struct iso_node isonode; - - if (!KVM_READ(VTOI(vp), &isonode, sizeof (isonode))) { - dprintf(stderr, "can't read iso_node at %p for pid %d\n", - (void *)VTOI(vp), Pid); - return 0; - } -#if 0 - fsp->fsid = dev2udev(isonode.i_dev); -#endif - fsp->mode = (mode_t)isonode.inode.iso_mode; - fsp->rdev = isonode.inode.iso_rdev; - - fsp->fileid = (long)isonode.i_number; - fsp->size = (u_long)isonode.i_size; - return 1; -} - diff --git a/usr.bin/fstat/fstat.c b/usr.bin/fstat/fstat.c index 7725c88..9d057e5 100644 --- a/usr.bin/fstat/fstat.c +++ b/usr.bin/fstat/fstat.c @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2009 Stanislav Sedov <stas@FreeBSD.org> * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * @@ -27,69 +28,24 @@ * 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 <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <sys/param.h> -#include <sys/time.h> -#include <sys/proc.h> #include <sys/user.h> #include <sys/stat.h> -#include <sys/vnode.h> #include <sys/socket.h> #include <sys/socketvar.h> -#include <sys/domain.h> -#include <sys/protosw.h> -#include <sys/un.h> -#include <sys/unpcb.h> #include <sys/sysctl.h> -#include <sys/tty.h> -#include <sys/filedesc.h> #include <sys/queue.h> -#define _WANT_FILE -#include <sys/file.h> -#include <sys/conf.h> -#define _KERNEL -#include <sys/pipe.h> -#include <sys/mount.h> -#include <ufs/ufs/quota.h> -#include <ufs/ufs/inode.h> -#include <fs/devfs/devfs.h> -#include <fs/devfs/devfs_int.h> -#undef _KERNEL -#include <nfs/nfsproto.h> -#include <nfsclient/nfs.h> -#include <nfsclient/nfsnode.h> - - -#include <vm/vm.h> -#include <vm/vm_map.h> -#include <vm/vm_object.h> - -#include <net/route.h> + #include <netinet/in.h> -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#include <netinet/in_pcb.h> +#include <assert.h> #include <ctype.h> #include <err.h> -#include <fcntl.h> -#include <kvm.h> +#include <libprocstat.h> #include <limits.h> -#include <nlist.h> -#include <paths.h> #include <pwd.h> #include <stdio.h> #include <stdlib.h> @@ -98,69 +54,49 @@ __FBSDID("$FreeBSD$"); #include <unistd.h> #include <netdb.h> -#include "fstat.h" - -#define TEXT -1 -#define CDIR -2 -#define RDIR -3 -#define TRACE -4 -#define MMAP -5 -#define JDIR -6 - -DEVS *devs; - -#ifdef notdef -struct nlist nl[] = { - { "" }, -}; -#endif +#include "functions.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 vflg; /* display errors in locating kernel data objects etc... */ int mflg; /* include memory-mapped files */ +int vflg; /* be verbose */ +typedef struct devs { + struct devs *next; + uint32_t fsid; + uint64_t ino; + const char *name; +} DEVS; -struct file **ofiles; /* buffer of pointers to file structures */ -int maxfiles; -#define ALLOC_OFILES(d) \ - if ((d) > maxfiles) { \ - free(ofiles); \ - ofiles = malloc((d) * sizeof(struct file *)); \ - if (ofiles == NULL) { \ - err(1, NULL); \ - } \ - maxfiles = (d); \ - } - +DEVS *devs; char *memf, *nlistf; -kvm_t *kd; - -static void fstat_kvm(int, int); -static void fstat_sysctl(int, int); -void dofiles(struct kinfo_proc *kp); -void dommap(struct kinfo_proc *kp); -void vtrans(struct vnode *vp, int i, int flag); -int ufs_filestat(struct vnode *vp, struct filestat *fsp); -int nfs_filestat(struct vnode *vp, struct filestat *fsp); -int devfs_filestat(struct vnode *vp, struct filestat *fsp); -char *getmnton(struct mount *m); -void pipetrans(struct pipe *pi, int i, int flag); -void socktrans(struct socket *sock, int i); -void ptstrans(struct tty *tp, int i, int flag); -void getinetproto(int number); -int getfname(const char *filename); -void usage(void); -char *kdevtoname(struct cdev *dev); + +static int getfname(const char *filename); +static void dofiles(struct procstat *procstat, struct kinfo_proc *p); +static void print_access_flags(int flags); +static void print_file_info(struct procstat *procstat, + struct filestat *fst, const char *uname, const char *cmd, int pid); +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_socket_info(struct procstat *procstat, + struct filestat *fst); +static void print_vnode_info(struct procstat *procstat, + struct filestat *fst); +static void usage(void) __dead2; int -main(int argc, char **argv) +do_fstat(int argc, char **argv) { + struct kinfo_proc *p; struct passwd *passwd; + struct procstat *procstat; int arg, ch, what; + int cnt, i; arg = 0; what = KERN_PROC_PROC; @@ -225,16 +161,18 @@ main(int argc, char **argv) } if (memf != NULL) - fstat_kvm(what, arg); + procstat = procstat_open_kvm(nlistf, memf); else - fstat_sysctl(what, arg); - exit(0); -} - -static void -print_header(void) -{ + procstat = procstat_open_sysctl(); + 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"); @@ -245,312 +183,65 @@ print_header(void) printf(" NAME\n"); else putchar('\n'); -} - -static void -fstat_kvm(int what, int arg) -{ - struct kinfo_proc *p, *plast; - char buf[_POSIX2_LINE_MAX]; - int cnt; - - ALLOC_OFILES(256); /* reserve space for file pointers */ /* - * Discard setgid privileges if not the running kernel so that bad - * guys can't print interesting stuff from kernel memory. + * Go through the process list. */ - if (nlistf != NULL || memf != NULL) - setgid(getgid()); - - if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf)) == NULL) - errx(1, "%s", buf); - setgid(getgid()); -#ifdef notdef - if (kvm_nlist(kd, nl) != 0) - errx(1, "no namelist: %s", kvm_geterr(kd)); -#endif - if ((p = kvm_getprocs(kd, what, arg, &cnt)) == NULL) - errx(1, "%s", kvm_geterr(kd)); - print_header(); - for (plast = &p[cnt]; p < plast; ++p) { - if (p->ki_stat == SZOMB) + for (i = 0; i < cnt; i++) { + if (p[i].ki_stat == SZOMB) continue; - dofiles(p); - if (mflg) - dommap(p); + dofiles(procstat, &p[i]); } + procstat_freeprocs(procstat, p); + procstat_close(procstat); + return (0); } static void -fstat_sysctl(int what, int arg) -{ - - /* not yet implemented */ - fstat_kvm(what, arg); -} - -const char *Uname, *Comm; -int Pid; - -#define PREFIX(i) printf("%-8.8s %-10s %5d", Uname, Comm, Pid); \ - switch(i) { \ - case TEXT: \ - printf(" text"); \ - break; \ - case CDIR: \ - printf(" wd"); \ - break; \ - case RDIR: \ - printf(" root"); \ - break; \ - case TRACE: \ - printf(" tr"); \ - break; \ - case MMAP: \ - printf(" mmap"); \ - break; \ - case JDIR: \ - printf(" jail"); \ - break; \ - default: \ - printf(" %4d", i); \ - break; \ - } - -/* - * print open files attributed to this process - */ -void -dofiles(struct kinfo_proc *kp) +dofiles(struct procstat *procstat, struct kinfo_proc *kp) { - int i; - struct file file; - struct filedesc filed; - - Uname = user_from_uid(kp->ki_uid, 0); - Pid = kp->ki_pid; - Comm = kp->ki_comm; - - if (kp->ki_fd == NULL) + const char *cmd; + const char *uname; + struct filestat *fst; + struct filestat_list *head; + int pid; + + uname = user_from_uid(kp->ki_uid, 0); + pid = kp->ki_pid; + cmd = kp->ki_comm; + + head = procstat_getfiles(procstat, kp, mflg); + if (head == NULL) return; - if (!KVM_READ(kp->ki_fd, &filed, sizeof (filed))) { - dprintf(stderr, "can't read filedesc at %p for pid %d\n", - (void *)kp->ki_fd, Pid); - return; - } - /* - * root directory vnode, if one - */ - if (filed.fd_rdir) - vtrans(filed.fd_rdir, RDIR, FREAD); - /* - * current working directory vnode - */ - if (filed.fd_cdir) - vtrans(filed.fd_cdir, CDIR, FREAD); - /* - * jail root, if any. - */ - if (filed.fd_jdir) - vtrans(filed.fd_jdir, JDIR, FREAD); - /* - * ktrace vnode, if one - */ - if (kp->ki_tracep) - vtrans(kp->ki_tracep, TRACE, FREAD|FWRITE); - /* - * text vnode, if one - */ - if (kp->ki_textvp) - vtrans(kp->ki_textvp, TEXT, FREAD); - /* - * open files - */ -#define FPSIZE (sizeof (struct file *)) -#define MAX_LASTFILE (0x1000000) - - /* Sanity check on filed.fd_lastfile */ - if (filed.fd_lastfile <= -1 || filed.fd_lastfile > MAX_LASTFILE) - return; - - ALLOC_OFILES(filed.fd_lastfile+1); - if (!KVM_READ(filed.fd_ofiles, ofiles, - (filed.fd_lastfile+1) * FPSIZE)) { - dprintf(stderr, - "can't read file structures at %p for pid %d\n", - (void *)filed.fd_ofiles, Pid); - return; - } - for (i = 0; i <= filed.fd_lastfile; i++) { - if (ofiles[i] == NULL) - continue; - if (!KVM_READ(ofiles[i], &file, sizeof (struct file))) { - dprintf(stderr, "can't read file %d at %p for pid %d\n", - i, (void *)ofiles[i], Pid); - continue; - } - if (file.f_type == DTYPE_VNODE) - vtrans(file.f_vnode, i, file.f_flag); - else if (file.f_type == DTYPE_SOCKET) { - if (checkfile == 0) - socktrans(file.f_data, i); - } -#ifdef DTYPE_PIPE - else if (file.f_type == DTYPE_PIPE) { - if (checkfile == 0) - pipetrans(file.f_data, i, file.f_flag); - } -#endif -#ifdef DTYPE_FIFO - else if (file.f_type == DTYPE_FIFO) { - if (checkfile == 0) - vtrans(file.f_vnode, i, file.f_flag); - } -#endif -#ifdef DTYPE_PTS - else if (file.f_type == DTYPE_PTS) { - if (checkfile == 0) - ptstrans(file.f_data, i, file.f_flag); - } -#endif - else { - dprintf(stderr, - "unknown file type %d for file %d of pid %d\n", - file.f_type, i, Pid); - } - } -} - -void -dommap(struct kinfo_proc *kp) -{ - vm_map_t map; - struct vmspace vmspace; - struct vm_map_entry entry; - vm_map_entry_t entryp; - struct vm_object object; - vm_object_t objp; - int prot, fflags; - - if (!KVM_READ(kp->ki_vmspace, &vmspace, sizeof(vmspace))) { - dprintf(stderr, - "can't read vmspace at %p for pid %d\n", - (void *)kp->ki_vmspace, Pid); - return; - } - map = &vmspace.vm_map; - - for (entryp = map->header.next; - entryp != &kp->ki_vmspace->vm_map.header; entryp = entry.next) { - if (!KVM_READ(entryp, &entry, sizeof(entry))) { - dprintf(stderr, - "can't read vm_map_entry at %p for pid %d\n", - (void *)entryp, Pid); - return; - } - - if (entry.eflags & MAP_ENTRY_IS_SUB_MAP) - continue; - - if ((objp = entry.object.vm_object) == NULL) - continue; - - for (; objp; objp = object.backing_object) { - if (!KVM_READ(objp, &object, sizeof(object))) { - dprintf(stderr, - "can't read vm_object at %p for pid %d\n", - (void *)objp, Pid); - return; - } - } - - prot = entry.protection; - fflags = (prot & VM_PROT_READ ? FREAD : 0) | - (prot & VM_PROT_WRITE ? FWRITE : 0); - - switch (object.type) { - case OBJT_VNODE: - vtrans((struct vnode *)object.handle, MMAP, fflags); - break; - default: - break; - } - } + STAILQ_FOREACH(fst, head, next) + print_file_info(procstat, fst, uname, cmd, pid); + procstat_freefiles(procstat, head); } -char * -kdevtoname(struct cdev *dev) -{ - struct cdev si; - if (!KVM_READ(dev, &si, sizeof si)) - return (NULL); - return (strdup(si.__si_namebuf)); -} - -void -vtrans(struct vnode *vp, int i, int flag) +static void +print_file_info(struct procstat *procstat, struct filestat *fst, + const char *uname, const char *cmd, int pid) { - struct vnode vn; - struct filestat fst; - char rw[3], mode[15], tagstr[12], *tagptr; - const char *badtype, *filename; - - filename = badtype = NULL; - if (!KVM_READ(vp, &vn, sizeof (struct vnode))) { - dprintf(stderr, "can't read vnode at %p for pid %d\n", - (void *)vp, Pid); - return; - } - if (!KVM_READ(&vp->v_tag, &tagptr, sizeof tagptr) || - !KVM_READ(tagptr, tagstr, sizeof tagstr)) { - dprintf(stderr, "can't read v_tag at %p for pid %d\n", - (void *)vp, Pid); - return; - } - tagstr[sizeof(tagstr) - 1] = '\0'; - if (vn.v_type == VNON) - badtype = "none"; - else if (vn.v_type == VBAD) - badtype = "bad"; - else { - if (!strcmp("ufs", tagstr)) { - if (!ufs_filestat(&vn, &fst)) - badtype = "error"; - } else if (!strcmp("devfs", tagstr)) { - if (!devfs_filestat(&vn, &fst)) - badtype = "error"; - } else if (!strcmp("nfs", tagstr)) { - if (!nfs_filestat(&vn, &fst)) - badtype = "error"; - } else if (!strcmp("msdosfs", tagstr)) { - if (!msdosfs_filestat(&vn, &fst)) - badtype = "error"; - } else if (!strcmp("isofs", tagstr)) { - if (!isofs_filestat(&vn, &fst)) - badtype = "error"; -#ifdef ZFS - } else if (!strcmp("zfs", tagstr)) { - if (!zfs_filestat(&vn, &fst)) - badtype = "error"; -#endif - } else { - static char unknown[32]; - snprintf(unknown, sizeof unknown, "?(%s)", tagstr); - badtype = unknown; - } - } - if (checkfile) { - int fsmatch = 0; - DEVS *d; - - if (badtype) + struct vnstat vn; + DEVS *d; + const char *filename; + int error, fsmatch = 0; + char errbuf[_POSIX2_LINE_MAX]; + + 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, errbuf); + if (error != 0) + return; + for (d = devs; d != NULL; d = d->next) - if (d->fsid == fst.fsid) { + if (d->fsid == vn.vn_fsid) { fsmatch = 1; - if (d->ino == fst.fileid) { + if ((unsigned)d->ino == vn.vn_fileid) { filename = d->name; break; } @@ -558,271 +249,83 @@ vtrans(struct vnode *vp, int i, int flag) if (fsmatch == 0 || (filename == NULL && fsflg == 0)) return; } - PREFIX(i); - if (badtype) { - (void)printf(" - - %10s -\n", badtype); - return; - } - if (nflg) - (void)printf(" %2d,%-2d", major(fst.fsid), minor(fst.fsid)); - else - (void)printf(" %-8s", getmnton(vn.v_mount)); - if (nflg) - (void)sprintf(mode, "%o", fst.mode); - else - strmode(fst.mode, mode); - (void)printf(" %6ld %10s", fst.fileid, mode); - switch (vn.v_type) { - case VBLK: - case VCHR: { - char *name; - - name = kdevtoname(vn.v_rdev); - if (nflg || !name) - printf(" %2d,%-2d", major(fst.rdev), minor(fst.rdev)); - else { - printf(" %6s", name); - free(name); - } - break; - } - default: - printf(" %6lu", fst.size); - } - rw[0] = '\0'; - if (flag & FREAD) - strcat(rw, "r"); - if (flag & FWRITE) - strcat(rw, "w"); - printf(" %2s", rw); - if (filename && !fsflg) - printf(" %s", filename); - putchar('\n'); -} - -int -ufs_filestat(struct vnode *vp, struct filestat *fsp) -{ - struct inode inode; - if (!KVM_READ(VTOI(vp), &inode, sizeof (inode))) { - dprintf(stderr, "can't read inode at %p for pid %d\n", - (void *)VTOI(vp), Pid); - return 0; - } /* - * 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 + * Print entry prefix. */ - fsp->fsid = dev2udev(inode.i_dev); - fsp->fileid = (long)inode.i_number; - fsp->mode = (mode_t)inode.i_mode; - fsp->size = (u_long)inode.i_size; -#if should_be_but_is_hard - /* XXX - need to load i_ump and i_din[12] from kernel memory */ - if (inode.i_ump->um_fstype == UFS1) - fsp->rdev = inode.i_din1->di_rdev; + printf("%-8.8s %-10s %5d", uname, cmd, pid); + if (fst->fs_uflags & PS_FST_UFLAG_TEXT) + printf(" text"); + else if (fst->fs_uflags & PS_FST_UFLAG_CDIR) + printf(" wd"); + else if (fst->fs_uflags & PS_FST_UFLAG_RDIR) + printf(" root"); + else if (fst->fs_uflags & PS_FST_UFLAG_TRACE) + printf(" tr"); + else if (fst->fs_uflags & PS_FST_UFLAG_MMAP) + printf(" mmap"); + else if (fst->fs_uflags & PS_FST_UFLAG_JAIL) + printf(" jail"); + else if (fst->fs_uflags & PS_FST_UFLAG_CTTY) + printf(" ctty"); else - fsp->rdev = inode.i_din2->di_rdev; -#else - fsp->rdev = 0; -#endif - - return 1; -} - -int -devfs_filestat(struct vnode *vp, struct filestat *fsp) -{ - struct devfs_dirent devfs_dirent; - struct mount mount; - struct vnode vnode; - - if (!KVM_READ(vp->v_data, &devfs_dirent, sizeof (devfs_dirent))) { - dprintf(stderr, "can't read devfs_dirent at %p for pid %d\n", - (void *)vp->v_data, Pid); - return 0; - } - if (!KVM_READ(vp->v_mount, &mount, sizeof (mount))) { - dprintf(stderr, "can't read mount at %p for pid %d\n", - (void *)vp->v_mount, Pid); - return 0; - } - if (!KVM_READ(devfs_dirent.de_vnode, &vnode, sizeof (vnode))) { - dprintf(stderr, "can't read vnode at %p for pid %d\n", - (void *)devfs_dirent.de_vnode, Pid); - return 0; - } - fsp->fsid = (long)(uint32_t)mount.mnt_stat.f_fsid.val[0]; - fsp->fileid = devfs_dirent.de_inode; - fsp->mode = (devfs_dirent.de_mode & ~S_IFMT) | S_IFCHR; - fsp->size = 0; - fsp->rdev = dev2udev(vnode.v_rdev); + printf(" %4d", fst->fs_fd); - return 1; -} - -int -nfs_filestat(struct vnode *vp, struct filestat *fsp) -{ - struct nfsnode nfsnode; - mode_t mode; - - if (!KVM_READ(VTONFS(vp), &nfsnode, sizeof (nfsnode))) { - dprintf(stderr, "can't read nfsnode at %p for pid %d\n", - (void *)VTONFS(vp), Pid); - return 0; - } - fsp->fsid = nfsnode.n_vattr.va_fsid; - fsp->fileid = nfsnode.n_vattr.va_fileid; - fsp->size = nfsnode.n_size; - fsp->rdev = nfsnode.n_vattr.va_rdev; - 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; + /* + * 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 VLNK: - mode |= S_IFLNK; + case PS_FST_TYPE_SOCKET: + print_socket_info(procstat, fst); break; - case VSOCK: - mode |= S_IFSOCK; + case PS_FST_TYPE_PIPE: + print_pipe_info(procstat, fst); break; - case VFIFO: - mode |= S_IFIFO; + case PS_FST_TYPE_PTS: + print_pts_info(procstat, fst); break; - case VNON: - case VBAD: - case VMARKER: - return 0; - }; - fsp->mode = mode; - - return 1; -} - - -char * -getmnton(struct mount *m) -{ - static struct mount mount; - static struct mtab { - struct mtab *next; - struct mount *m; - char mntonname[MNAMELEN]; - } *mhead = NULL; - struct mtab *mt; - - for (mt = mhead; mt != NULL; mt = mt->next) - if (m == mt->m) - return (mt->mntonname); - if (!KVM_READ(m, &mount, 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(&mount.mnt_stat.f_mntonname[0], &mt->mntonname[0], MNAMELEN); - mt->next = mhead; - mhead = mt; - return (mt->mntonname); -} - -void -pipetrans(struct pipe *pi, int i, int flag) -{ - struct pipe pip; - char rw[3]; - - PREFIX(i); - - /* fill in socket */ - if (!KVM_READ(pi, &pip, sizeof(struct pipe))) { - dprintf(stderr, "can't read pipe at %p\n", (void *)pi); - goto bad; + default: + if (vflg) + fprintf(stderr, + "unknown file type %d for file %d of pid %d\n", + fst->fs_type, fst->fs_fd, pid); } - - printf("* pipe %8lx <-> %8lx", (u_long)pi, (u_long)pip.pipe_peer); - printf(" %6d", (int)pip.pipe_buffer.cnt); - rw[0] = '\0'; - if (flag & FREAD) - strcat(rw, "r"); - if (flag & FWRITE) - strcat(rw, "w"); - printf(" %2s", rw); + if (filename && !fsflg) + printf(" %s", filename); putchar('\n'); - return; - -bad: - printf("* error\n"); } -void -socktrans(struct socket *sock, int i) +static void +print_socket_info(struct procstat *procstat, struct filestat *fst) { static const char *stypename[] = { "unused", /* 0 */ - "stream", /* 1 */ + "stream", /* 1 */ "dgram", /* 2 */ "raw", /* 3 */ "rdm", /* 4 */ "seqpak" /* 5 */ }; -#define STYPEMAX 5 - struct socket so; - struct protosw proto; - struct domain dom; - struct inpcb inpcb; - struct unpcb unpcb; - int len; - char dname[32]; - - PREFIX(i); - - /* fill in socket */ - if (!KVM_READ(sock, &so, sizeof(struct socket))) { - dprintf(stderr, "can't read sock at %p\n", (void *)sock); - goto bad; - } - - /* fill in protosw entry */ - if (!KVM_READ(so.so_proto, &proto, sizeof(struct protosw))) { - dprintf(stderr, "can't read protosw at %p", - (void *)so.so_proto); - goto bad; - } - - /* fill in domain */ - if (!KVM_READ(proto.pr_domain, &dom, sizeof(struct domain))) { - dprintf(stderr, "can't read domain at %p\n", - (void *)proto.pr_domain); - goto bad; - } +#define STYPEMAX 5 + struct sockstat sock; + struct protoent *pe; + char errbuf[_POSIX2_LINE_MAX]; + int error; + static int isopen; - if ((len = kvm_read(kd, (u_long)dom.dom_name, dname, - sizeof(dname) - 1)) < 0) { - dprintf(stderr, "can't read domain name at %p\n", - (void *)dom.dom_name); - dname[0] = '\0'; + 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 - dname[len] = '\0'; - - if ((u_short)so.so_type > STYPEMAX) - printf("* %s ?%d", dname, so.so_type); - else - printf("* %s %s", dname, stypename[so.so_type]); + printf("* %s %s", sock.dname, stypename[sock.type]); /* * protocol specific formatting @@ -835,139 +338,144 @@ socktrans(struct socket *sock, int i) * The idea is not to duplicate netstat, but to make available enough * information for further analysis. */ - switch(dom.dom_family) { + switch (sock.dom_family) { case AF_INET: case AF_INET6: - getinetproto(proto.pr_protocol); - if (proto.pr_protocol == IPPROTO_TCP ) { - if (so.so_pcb) { - if (kvm_read(kd, (u_long)so.so_pcb, - (char *)&inpcb, sizeof(struct inpcb)) - != sizeof(struct inpcb)) { - dprintf(stderr, - "can't read inpcb at %p\n", - (void *)so.so_pcb); - goto bad; - } - printf(" %lx", (u_long)inpcb.inp_ppcb); - } + 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 (so.so_pcb) - printf(" %lx", (u_long)so.so_pcb); + 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 (so.so_pcb) { - printf(" %lx", (u_long)so.so_pcb); - if (kvm_read(kd, (u_long)so.so_pcb, (char *)&unpcb, - sizeof(struct unpcb)) != sizeof(struct unpcb)){ - dprintf(stderr, "can't read unpcb at %p\n", - (void *)so.so_pcb); - goto bad; - } - if (unpcb.unp_conn) { + if (sock.so_pcb != 0) { + printf(" %lx", (u_long)sock.so_pcb); + if (sock.unp_conn) { char shoconn[4], *cp; cp = shoconn; - if (!(so.so_rcv.sb_state & SBS_CANTRCVMORE)) + if (!(sock.so_rcv_sb_state & SBS_CANTRCVMORE)) *cp++ = '<'; *cp++ = '-'; - if (!(so.so_snd.sb_state & SBS_CANTSENDMORE)) + if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE)) *cp++ = '>'; *cp = '\0'; printf(" %s %lx", shoconn, - (u_long)unpcb.unp_conn); - } + (u_long)sock.unp_conn); + } } break; default: /* print protocol number and socket address */ - printf(" %d %lx", proto.pr_protocol, (u_long)sock); + printf(" %d %lx", sock.proto, (u_long)sock.so_addr); } - printf("\n"); - return; -bad: - printf("* error\n"); } -void -ptstrans(struct tty *tp, int i, int flag) +static void +print_pipe_info(struct procstat *procstat, struct filestat *fst) { - struct tty tty; - char *name; - char rw[3]; - dev_t rdev; + struct pipestat ps; + char errbuf[_POSIX2_LINE_MAX]; + int error; - PREFIX(i); - - /* Obtain struct tty. */ - if (!KVM_READ(tp, &tty, sizeof(struct tty))) { - dprintf(stderr, "can't read tty at %p\n", (void *)tp); - goto bad; - } - - /* Figure out the device name. */ - name = kdevtoname(tty.t_dev); - if (name == NULL) { - dprintf(stderr, "can't determine tty name at %p\n", (void *)tp); - goto bad; + error = procstat_get_pipe_info(procstat, fst, &ps, errbuf); + if (error != 0) { + printf("* error"); + return; } + printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer); + printf(" %6zd", ps.buffer_cnt); + print_access_flags(fst->fs_fflags); +} - rw[0] = '\0'; - if (flag & FREAD) - strcat(rw, "r"); - if (flag & FWRITE) - strcat(rw, "w"); +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 || !name) { - rdev = dev2udev(tty.t_dev); - printf("%10d,%-2d", major(rdev), minor(rdev)); + if (nflg || !*pts.devname) { + printf("%10d,%-2d", major(pts.dev), minor(pts.dev)); } else { - printf("%10s", name); + printf("%10s", pts.devname); } - printf(" %2s\n", rw); - - free(name); - - return; -bad: - printf("* error\n"); + print_access_flags(fst->fs_fflags); } -/* - * Read the cdev structure in the kernel in order to work out the - * associated dev_t - */ -dev_t -dev2udev(struct cdev *dev) +static void +print_vnode_info(struct procstat *procstat, struct filestat *fst) { - struct cdev_priv priv; + struct vnstat vn; + char errbuf[_POSIX2_LINE_MAX]; + char mode[15]; + const char *badtype; + 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 (KVM_READ(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; + if (nflg) + printf(" %2d,%-2d", major(vn.vn_fsid), minor(vn.vn_fsid)); + else if (vn.vn_mntdir != NULL) + (void)printf(" %-8s", vn.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) { + if (nflg || !*vn.vn_devname) + printf(" %2d,%-2d", major(vn.vn_dev), minor(vn.vn_dev)); + else { + printf(" %6s", vn.vn_devname); + } + } else + printf(" %6lu", vn.vn_size); + print_access_flags(fst->fs_fflags); } -/* - * getinetproto -- - * print name of protocol number - */ -void -getinetproto(int number) +static void +print_access_flags(int flags) { - static int isopen; - struct protoent *pe; + char rw[3]; - if (!isopen) - setprotoent(++isopen); - if ((pe = getprotobynumber(number)) != NULL) - printf(" %s", pe->p_name); - else - printf(" %d", number); + 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 @@ -978,7 +486,7 @@ getfname(const char *filename) if (stat(filename, &statbuf)) { warn("%s", filename); - return(0); + return (0); } if ((cur = malloc(sizeof(DEVS))) == NULL) err(1, NULL); @@ -988,24 +496,10 @@ getfname(const char *filename) cur->ino = statbuf.st_ino; cur->fsid = statbuf.st_dev; cur->name = filename; - return(1); + return (1); } -#ifdef ZFS -void * -getvnodedata(struct vnode *vp) -{ - return (vp->v_data); -} - -struct mount * -getvnodemount(struct vnode *vp) -{ - return (vp->v_mount); -} -#endif - -void +static void usage(void) { (void)fprintf(stderr, diff --git a/usr.bin/fstat/fstat.h b/usr.bin/fstat/functions.h index 3533a60..a23d27e 100644 --- a/usr.bin/fstat/fstat.h +++ b/usr.bin/fstat/functions.h @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1988, 1993 - * The Regents of the University of California. All rights reserved. + * Copyright (c) 2009 Stanislav Sedov <stas@FreeBSD.org> + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,9 +10,6 @@ * 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. - * 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 @@ -28,49 +25,10 @@ * * $FreeBSD$ */ +#ifndef __FUNCTIONS_H__ +#define __FUNCTIONS_H__ -#ifndef __FSTAT_H__ -#define __FSTAT_H__ +int do_fstat(int argc, char *argv[]); +int do_fuser(int argc, char *argv[]); -/* - * a kvm_read that returns true if everything is read - */ -#define KVM_READ(kaddr, paddr, len) \ - ((len) < SSIZE_MAX && \ - kvm_read(kd, (u_long)(kaddr), (char *)(paddr), (len)) == (ssize_t)(len)) - -#define dprintf if (vflg) fprintf - -typedef struct devs { - struct devs *next; - long fsid; - long ino; - const char *name; -} DEVS; - -struct filestat { - long fsid; - long fileid; - mode_t mode; - u_long size; - dev_t rdev; -}; - -/* Ugh */ -extern kvm_t *kd; -extern int vflg; -extern int Pid; - -dev_t dev2udev(struct cdev *dev); - -/* Additional filesystem types */ -int isofs_filestat(struct vnode *vp, struct filestat *fsp); -int msdosfs_filestat(struct vnode *vp, struct filestat *fsp); - -#ifdef ZFS -int zfs_filestat(struct vnode *vp, struct filestat *fsp); -void *getvnodedata(struct vnode *vp); -struct mount *getvnodemount(struct vnode *vp); -#endif - -#endif /* __FSTAT_H__ */ +#endif /* !__FUNCTIONS_H__ */ diff --git a/usr.bin/fstat/fuser.1 b/usr.bin/fstat/fuser.1 new file mode 100644 index 0000000..999a72d --- /dev/null +++ b/usr.bin/fstat/fuser.1 @@ -0,0 +1,148 @@ +.\" Copyright (c) 2005-2009 Stanislav Sedov <stas@FreeBSD.org> +.\" 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. +.\" +.\" 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$ +.\" +.Dd July 31, 2009 +.Dt FUSER 1 +.Os +.Sh NAME +.Nm fuser +.Nd list IDs of all processes that have one or more files open +.Sh SYNOPSIS +.Nm +.Op Fl cfkmu +.Op Fl M Ar core +.Op Fl N Ar system +.Op Fl s Ar signal +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility writes to stdout the PIDs of processes that have one or +more named files open. +For block and character special devices, all processes using files +on that device are listed. +A file is considered open by a process if it was explicitly opened, +is the working directory, root directory, jail root directory, +active executable text, kernel trace file or the controlling terminal +of the process. +If +.Fl m +option is specified, the +.Nm +utility will also look through mmapped files. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl c +Treat files as mount point and report on any files open in the file system. +.It Fl f +The report must be only for named files. +.It Fl k +Send signal to reported processes +.Pq SIGKILL by default . +.It Fl m +Search through mmapped files too. +.It Fl u +Write the user name associated with each process to stdout. +.It Fl M +Extract values associated with the name list from the specified core +instead of the default +.Pa /dev/kmem . +.It Fl N +Extract the name list from the specified system instead of the default, +which is the kernel image the system has booted from. +.It Fl s +Use given signal name instead of default SIGKILL. +.El +.Pp +The following symbols, written to stderr will indicate how files is used: +.Bl -tag -width MOUNT +.It Cm r +The file is the root directory of the process. +.It Cm c +The file is the current workdir directory of the process. +.It Cm j +The file is the jail-root of the process. +.It Cm t +The file is the kernel tracing file for the process. +.It Cm x +The file is executable text of the process. +.It Cm y +The process use this file as its controlling tty. +.It Cm m +The file is mmapped. +.It Cm w +The file is open for writing. +.It Cm a +The file is open as append only +.Pq O_APPEND was specified . +.It Cm d +The process bypasses fs cache while writing to this file +.Pq O_DIRECT was specified . +.It Cm s +Shared lock is hold. +.It Cm e +Exclusive lock is hold. +.El +.Sh EXIT STATUS +The +.Nm +utility returns 0 on successful completion and >0 otherwise. +.Sh EXAMPLES +The command: +.Dq Li "fuser -fu ." +writes to standart output the process IDs of processes that are using the +current directory and writes to stderr an indication of how those processes are +using the direcory and user names associated with the processes that are using +this directory. +.Sh SEE ALSO +.Xr fstat 1 , +.Xr ps 1 , +.Xr systat 1 , +.Xr iostat 8 , +.Xr pstat 8 , +.Xr vmstat 8 +.Sh STANDARTS +The +.Nm +utility is expected to conform to +.St -p1003.1-2004 . +.Sh BUGS +Since +.Nm +takes a snapshot of the system, it is only correct for a very short period +of time. +When working via +.Xr kvm 3 +interface the report will be limited to filesystems the +.Nm +utility knows about (currently only cd9660, devfs, nfs, ntfs, nwfs, udf, +ufs and zfs). +.Sh AUTHORS +The +.Nm +utility and this manual page was written by +.An Stanislav Sedov Aq stas@FreeBSD.org . diff --git a/usr.bin/fstat/fuser.c b/usr.bin/fstat/fuser.c new file mode 100644 index 0000000..02975b3 --- /dev/null +++ b/usr.bin/fstat/fuser.c @@ -0,0 +1,369 @@ +/*- + * Copyright (c) 2005-2009 Stanislav Sedov <stas@FreeBSD.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/queue.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/user.h> + +#include <assert.h> +#include <ctype.h> +#include <err.h> +#include <fcntl.h> +#include <libprocstat.h> +#include <limits.h> +#include <paths.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#include "functions.h" + +/* + * File access mode flags table. + */ +struct { + int flag; + char ch; +} fflags[] = { + {PS_FST_FFLAG_WRITE, 'w'}, + {PS_FST_FFLAG_APPEND, 'a'}, + {PS_FST_FFLAG_DIRECT, 'd'}, + {PS_FST_FFLAG_SHLOCK, 's'}, + {PS_FST_FFLAG_EXLOCK, 'e'} +}; +#define NFFLAGS (sizeof(fflags) / sizeof(*fflags)) + +/* + * Usage flags translation table. + */ +struct { + int flag; + char ch; +} uflags[] = { + {PS_FST_UFLAG_RDIR, 'r'}, + {PS_FST_UFLAG_CDIR, 'c'}, + {PS_FST_UFLAG_JAIL, 'j'}, + {PS_FST_UFLAG_TRACE, 't'}, + {PS_FST_UFLAG_TEXT, 'x'}, + {PS_FST_UFLAG_MMAP, 'm'}, + {PS_FST_UFLAG_CTTY, 'y'} +}; +#define NUFLAGS (sizeof(uflags) / sizeof(*uflags)) + +struct consumer { + pid_t pid; + uid_t uid; + int fd; + int flags; + int uflags; + STAILQ_ENTRY(consumer) next; +}; +struct reqfile { + uint32_t fsid; + uint64_t fileid; + const char *name; + STAILQ_HEAD(, consumer) consumers; +}; + +/* + * Option flags. + */ +#define UFLAG 0x01 /* -u flag: show users */ +#define FFLAG 0x02 /* -f flag: specified files only */ +#define CFLAG 0x04 /* -c flag: treat as mpoints */ +#define MFLAG 0x10 /* -m flag: mmapped files too */ +#define KFLAG 0x20 /* -k flag: send signal (SIGKILL by default) */ + +static int flags = 0; /* Option flags. */ + +static void printflags(struct consumer *consumer); +static int str2sig(const char *str); +static void usage(void) __dead2; +static int addfile(const char *path, struct reqfile *reqfile); +static void dofiles(struct procstat *procstat, struct kinfo_proc *kp, + struct reqfile *reqfiles, size_t nfiles); + +static void +usage(void) +{ + + fprintf(stderr, +"usage: fuser [-cfhkmu] [-M core] [-N system] [-s signal] file ...\n"); + exit(EX_USAGE); +} + +static void +printflags(struct consumer *cons) +{ + unsigned int i; + + assert(cons); + for (i = 0; i < NUFLAGS; i++) + if ((cons->uflags & uflags[i].flag) != 0) + fputc(uflags[i].ch, stderr); + for (i = 0; i < NFFLAGS; i++) + if ((cons->flags & fflags[i].flag) != 0) + fputc(fflags[i].ch, stderr); +} + +/* + * Add file to the list. + */ +static int +addfile(const char *path, struct reqfile *reqfile) +{ + struct stat sb; + + assert(path); + if (stat(path, &sb) != 0) { + warn("%s", path); + return (1); + } + reqfile->fileid = sb.st_ino; + reqfile->fsid = sb.st_dev; + reqfile->name = path; + STAILQ_INIT(&reqfile->consumers); + return (0); +} + +int +do_fuser(int argc, char *argv[]) +{ + struct consumer *consumer; + struct kinfo_proc *p, *procs; + struct procstat *procstat; + struct reqfile *reqfiles; + char *ep, *nlistf, *memf; + int ch, cnt, sig; + unsigned int i, nfiles; + + sig = SIGKILL; /* Default to kill. */ + nlistf = NULL; + memf = NULL; + while ((ch = getopt(argc, argv, "M:N:cfhkms:u")) != -1) + switch(ch) { + case 'f': + if ((flags & CFLAG) != 0) + usage(); + flags |= FFLAG; + break; + case 'c': + if ((flags & FFLAG) != 0) + usage(); + flags |= CFLAG; + break; + case 'N': + nlistf = optarg; + break; + case 'M': + memf = optarg; + break; + case 'u': + flags |= UFLAG; + break; + case 'm': + flags |= MFLAG; + break; + case 'k': + flags |= KFLAG; + break; + case 's': + if (isdigit(*optarg)) { + sig = strtol(optarg, &ep, 10); + if (*ep != '\0' || sig < 0 || sig >= sys_nsig) + errx(EX_USAGE, "illegal signal number" ": %s", + optarg); + } else { + sig = str2sig(optarg); + if (sig < 0) + errx(EX_USAGE, "illegal signal name: " + "%s", optarg); + } + break; + case 'h': + /* PASSTHROUGH */ + default: + usage(); + /* NORETURN */ + } + argv += optind; + argc -= optind; + + assert(argc >= 0); + if (argc == 0) + usage(); + /* NORETURN */ + + /* + * Process named files. + */ + reqfiles = malloc(argc * sizeof(struct reqfile)); + if (reqfiles == NULL) + err(EX_OSERR, "malloc()"); + nfiles = 0; + while (argc--) + if (!addfile(*(argv++), &reqfiles[nfiles])) + nfiles++; + if (nfiles == 0) + errx(EX_IOERR, "files not accessible"); + + if (memf != NULL) + procstat = procstat_open_kvm(nlistf, memf); + else + procstat = procstat_open_sysctl(); + if (procstat == NULL) + errx(1, "procstat_open()"); + procs = procstat_getprocs(procstat, KERN_PROC_PROC, 0, &cnt); + if (procs == NULL) + errx(1, "procstat_getprocs()"); + + /* + * Walk through process table and look for matching files. + */ + p = procs; + while(cnt--) + if (p->ki_stat != SZOMB) + dofiles(procstat, p++, reqfiles, nfiles); + + for (i = 0; i < nfiles; i++) { + fprintf(stderr, "%s:", reqfiles[i].name); + fflush(stderr); + STAILQ_FOREACH(consumer, &reqfiles[i].consumers, next) { + if (consumer->flags != 0) { + fprintf(stdout, "%6d", consumer->pid); + fflush(stdout); + printflags(consumer); + if ((flags & UFLAG) != 0) + fprintf(stderr, "(%s)", + user_from_uid(consumer->uid, 0)); + if ((flags & KFLAG) != 0) + kill(consumer->pid, sig); + fflush(stderr); + } + } + (void)fprintf(stderr, "\n"); + } + procstat_freeprocs(procstat, procs); + procstat_close(procstat); + free(reqfiles); + return (0); +} + +static void +dofiles(struct procstat *procstat, struct kinfo_proc *kp, + struct reqfile *reqfiles, size_t nfiles) +{ + struct vnstat vn; + struct consumer *cons; + struct filestat *fst; + struct filestat_list *head; + int error, match; + unsigned int i; + char errbuf[_POSIX2_LINE_MAX]; + + head = procstat_getfiles(procstat, kp, flags & MFLAG); + if (head == NULL) + return; + STAILQ_FOREACH(fst, head, next) { + if (fst->fs_type != PS_FST_TYPE_VNODE) + continue; + error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); + if (error != 0) + continue; + for (i = 0; i < nfiles; i++) { + if (flags & CFLAG && reqfiles[i].fsid == vn.vn_fsid) { + break; + } + else if (reqfiles[i].fsid == vn.vn_fsid && + reqfiles[i].fileid == vn.vn_fileid) { + break; + } + else if (!(flags & FFLAG) && + (vn.vn_type == PS_FST_VTYPE_VCHR || + vn.vn_type == PS_FST_VTYPE_VBLK) && + vn.vn_fsid == reqfiles[i].fileid) { + break; + } + } + if (i == nfiles) + continue; /* No match. */ + + /* + * Look for existing entries. + */ + match = 0; + STAILQ_FOREACH(cons, &reqfiles[i].consumers, next) + if (cons->pid == kp->ki_pid) { + match = 1; + break; + } + if (match == 1) { /* Use old entry. */ + cons->flags |= fst->fs_fflags; + cons->uflags |= fst->fs_uflags; + } else { + /* + * Create new entry in the consumer chain. + */ + cons = calloc(1, sizeof(struct consumer)); + if (cons == NULL) { + warn("malloc()"); + continue; + } + cons->uid = kp->ki_uid; + cons->pid = kp->ki_pid; + cons->uflags = fst->fs_uflags; + cons->flags = fst->fs_fflags; + STAILQ_INSERT_TAIL(&reqfiles[i].consumers, cons, next); + } + } + procstat_freefiles(procstat, head); +} + +/* + * Returns signal number for it's string representation. + */ +static int +str2sig(const char *str) +{ + int i; + +#define SIGPREFIX "sig" + if (!strncasecmp(str, SIGPREFIX, sizeof(SIGPREFIX))) + str += sizeof(SIGPREFIX); + for (i = 1; i < sys_nsig; i++) { + if (!strcasecmp(sys_signame[i], str)) + return (i); + } + return (-1); +} diff --git a/usr.bin/fstat/main.c b/usr.bin/fstat/main.c new file mode 100644 index 0000000..4123e64 --- /dev/null +++ b/usr.bin/fstat/main.c @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2009 Stanislav Sedov <stas@FreeBSD.org>. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <err.h> +#include <libgen.h> +#include <stdlib.h> +#include <string.h> + +#include "functions.h" + +int +main(int argc, char *argv[]) +{ + char *p; + + p = basename(argv[0]); + if (p == NULL) + err(1, "basename(%s)", argv[0]); + if (!strcmp(p, "fuser")) + return (do_fuser(argc, argv)); + else + return (do_fstat(argc, argv)); +} diff --git a/usr.bin/fstat/msdosfs.c b/usr.bin/fstat/msdosfs.c deleted file mode 100644 index 7f80499..0000000 --- a/usr.bin/fstat/msdosfs.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2000 Peter Edwards - * Copyright (c) 1988, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by Peter Edwards - * - * 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 <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#include <sys/time.h> -#include <sys/stat.h> -#include <sys/vnode.h> - -#define _KERNEL -#include <sys/mount.h> -#include <fs/msdosfs/bpb.h> -#include <fs/msdosfs/msdosfsmount.h> -#undef _KERNEL - -#include <fs/msdosfs/denode.h> -#include <fs/msdosfs/direntry.h> -#include <fs/msdosfs/fat.h> - -#include <err.h> -#include <kvm.h> -#include <stdio.h> -#include <stdlib.h> - -/* - * XXX - - * VTODE is defined in denode.h only if _KERNEL is defined, but that leads to - * header explosion - */ -#define VTODE(vp) ((struct denode *)(vp)->v_data) - -#include "fstat.h" - -struct dosmount { - struct dosmount *next; - struct msdosfsmount *kptr; /* Pointer in kernel space */ - struct msdosfsmount data; /* User space copy of structure */ -}; - -int -msdosfs_filestat(struct vnode *vp, struct filestat *fsp) -{ - struct denode denode; - static struct dosmount *mounts; - struct dosmount *mnt; - u_long dirsperblk; - int fileid; - - if (!KVM_READ(VTODE(vp), &denode, sizeof (denode))) { - dprintf(stderr, "can't read denode at %p for pid %d\n", - (void *)VTODE(vp), Pid); - return 0; - } - - /* - * Find msdosfsmount structure for the vnode's filesystem. Needed - * for some filesystem parameters - */ - for (mnt = mounts; mnt; mnt = mnt->next) - if (mnt->kptr == denode.de_pmp) - break; - - if (!mnt) { - if ((mnt = malloc(sizeof(struct dosmount))) == NULL) - err(1, NULL); - if (!KVM_READ(denode.de_pmp, &mnt->data, sizeof mnt->data)) { - free(mnt); - dprintf(stderr, - "can't read mount info at %p for pid %d\n", - (void *)denode.de_pmp, Pid); - return 0; - } - mnt->next = mounts; - mounts = mnt; - mnt->kptr = denode.de_pmp; - } - - fsp->fsid = dev2udev(mnt->data.pm_dev); - fsp->mode = 0555; - fsp->mode |= denode.de_Attributes & ATTR_READONLY ? 0 : 0222; - fsp->mode &= mnt->data.pm_mask; - - /* Distinguish directories and files. No "special" files in FAT. */ - fsp->mode |= denode.de_Attributes & ATTR_DIRECTORY ? S_IFDIR : S_IFREG; - - fsp->size = denode.de_FileSize; - fsp->rdev = 0; - - /* - * XXX - - * Culled from msdosfs_vnops.c. There appears to be a problem - * here, in that a directory has the same inode number as the first - * file in the directory. stat(2) suffers from this problem also, so - * I won't try to fix it here. - * - * The following computation of the fileid must be the same as that - * used in msdosfs_readdir() to compute d_fileno. If not, pwd - * doesn't work. - */ - dirsperblk = mnt->data.pm_BytesPerSec / sizeof(struct direntry); - if (denode.de_Attributes & ATTR_DIRECTORY) { - fileid = cntobn(&mnt->data, denode.de_StartCluster) - * dirsperblk; - if (denode.de_StartCluster == MSDOSFSROOT) - fileid = 1; - } else { - fileid = cntobn(&mnt->data, denode.de_dirclust) * dirsperblk; - if (denode.de_dirclust == MSDOSFSROOT) - fileid = roottobn(&mnt->data, 0) * dirsperblk; - fileid += denode.de_diroffset / sizeof(struct direntry); - } - - fsp->fileid = fileid; - return 1; -} diff --git a/usr.bin/fstat/zfs.c b/usr.bin/fstat/zfs.c deleted file mode 100644 index cdca41f..0000000 --- a/usr.bin/fstat/zfs.c +++ /dev/null @@ -1,136 +0,0 @@ -/*- - * Copyright (c) 2007 Ulf Lilleengen - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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$ - */ - -#include <sys/param.h> -#define _KERNEL -#include <sys/mount.h> -#include <sys/taskqueue.h> -#undef _KERNEL -#include <sys/sysctl.h> - -#undef lbolt -#undef lbolt64 -#undef gethrestime_sec -#include <sys/zfs_context.h> -#include <sys/spa.h> -#include <sys/spa_impl.h> -#include <sys/dmu.h> -#include <sys/zap.h> -#include <sys/fs/zfs.h> -#include <sys/zfs_znode.h> -#include <sys/zfs_sa.h> - -#include <err.h> -#include <kvm.h> -#include <stdio.h> -#include <stdlib.h> - -#define ZFS -#undef dprintf -#include <fstat.h> - -/* - * Offset calculations that are used to get data from znode without having the - * definition. - */ -#define LOCATION_ZID (2 * sizeof(void *)) -#define LOCATION_ZPHYS(zsize) ((zsize) - (2 * sizeof(void *) + sizeof(struct task))) - -int -zfs_filestat(struct vnode *vp, struct filestat *fsp) -{ - - znode_phys_t zphys; - struct mount mount, *mountptr; - uint64_t *zid; - void *znodeptr, *vnodeptr; - char *dataptr; - void *zphys_addr; - size_t len; - int size; - - len = sizeof(size); - if (sysctlbyname("debug.sizeof.znode", &size, &len, NULL, 0) == -1) { - dprintf(stderr, "error getting sysctl\n"); - return (0); - } - znodeptr = malloc(size); - if (znodeptr == NULL) { - dprintf(stderr, "error allocating memory for znode storage\n"); - return (0); - } - - /* Since we have problems including vnode.h, we'll use the wrappers. */ - vnodeptr = getvnodedata(vp); - if (!KVM_READ(vnodeptr, znodeptr, (size_t)size)) { - dprintf(stderr, "can't read znode at %p for pid %d\n", - (void *)vnodeptr, Pid); - goto bad; - } - - /* - * z_id field is stored in the third pointer. We therefore skip the two - * first bytes. - * - * Pointer to the z_phys structure is the next last pointer. Therefore - * go back two bytes from the end. - */ - dataptr = znodeptr; - zid = (uint64_t *)(dataptr + LOCATION_ZID); - zphys_addr = *(void **)(dataptr + LOCATION_ZPHYS(size)); - - if (!KVM_READ(zphys_addr, &zphys, sizeof(zphys))) { - dprintf(stderr, "can't read znode_phys at %p for pid %d\n", - zphys_addr, Pid); - goto bad; - } - - /* Get the mount pointer, and read from the address. */ - mountptr = getvnodemount(vp); - if (!KVM_READ(mountptr, &mount, sizeof(mount))) { - dprintf(stderr, "can't read mount at %p for pid %d\n", - (void *)mountptr, Pid); - goto bad; - } - - fsp->fsid = (long)(uint32_t)mount.mnt_stat.f_fsid.val[0]; - fsp->fileid = *zid; - /* - * XXX: Shows up wrong in output, but UFS has this error too. Could - * be that we're casting mode-variables from 64-bit to 8-bit or simply - * error in the mode-to-string function. - */ - fsp->mode = (mode_t)zphys.zp_mode; - fsp->size = (u_long)zphys.zp_size; - fsp->rdev = (dev_t)zphys.zp_rdev; - free(znodeptr); - return (1); -bad: - free(znodeptr); - return (0); -} diff --git a/usr.bin/fstat/zfs/Makefile b/usr.bin/fstat/zfs/Makefile deleted file mode 100644 index 7ecfc85..0000000 --- a/usr.bin/fstat/zfs/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# $FreeBSD$ - -.PATH: ${.CURDIR}/.. - -SRCS= zfs.c -OBJS= zfs.o -WARNS?= 1 - -CFLAGS+= -I${.CURDIR}/../../../sys/cddl/compat/opensolaris -CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/include -CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/lib/libumem -CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzpool/common -CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/fs/zfs -CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common -CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys -CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/head -CFLAGS+= -I${.CURDIR}/.. -CFLAGS+= -DNEED_SOLARIS_BOOLEAN - -all: ${OBJS} -CLEANFILES= ${OBJS} - -.include <bsd.lib.mk> |