diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2009-07-08 16:04:01 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2009-07-08 16:04:01 +0000 |
commit | db88960d516b2c5ac6c28f48ad6f552239017182 (patch) | |
tree | 2eb62c4b472e6eab4b3b59c0b4c498be8772e383 | |
parent | adaaa9d9490a77fc421f46a2b0f652b6d9c9199e (diff) |
Add POSIX-compliant fuser mode to fstat. Originally based on
a diff from Peter Werner but largely rewritten to use kinfo_file2.
OK deraadt@ with man fixes from jmc@ and sobrado@
-rw-r--r-- | usr.bin/fstat/Makefile | 5 | ||||
-rw-r--r-- | usr.bin/fstat/fstat.1 | 6 | ||||
-rw-r--r-- | usr.bin/fstat/fstat.c | 210 | ||||
-rw-r--r-- | usr.bin/fstat/fstat.h | 49 | ||||
-rw-r--r-- | usr.bin/fstat/fuser.1 | 144 | ||||
-rw-r--r-- | usr.bin/fstat/fuser.c | 177 |
6 files changed, 536 insertions, 55 deletions
diff --git a/usr.bin/fstat/Makefile b/usr.bin/fstat/Makefile index 6e2829ee779..a36d3a4094c 100644 --- a/usr.bin/fstat/Makefile +++ b/usr.bin/fstat/Makefile @@ -1,8 +1,11 @@ -# $OpenBSD: Makefile,v 1.9 2009/06/07 03:10:09 millert Exp $ +# $OpenBSD: Makefile,v 1.10 2009/07/08 16:04:00 millert Exp $ PROG= fstat +SRCS= fstat.c fuser.c +MAN= fstat.1 fuser.1 DPADD= ${LIBKVM} LDADD= -lkvm +LINKS= ${BINDIR}/fstat ${BINDIR}/fuser CFLAGS+=-DINET6 diff --git a/usr.bin/fstat/fstat.1 b/usr.bin/fstat/fstat.1 index bf4f249ea69..e7c419ebce0 100644 --- a/usr.bin/fstat/fstat.1 +++ b/usr.bin/fstat/fstat.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: fstat.1,v 1.40 2008/12/15 09:49:59 jmc Exp $ +.\" $OpenBSD: fstat.1,v 1.41 2009/07/08 16:04:00 millert Exp $ .\" .\" Copyright (c) 1987, 1991, 1993 .\" The Regents of the University of California. All rights reserved. @@ -29,7 +29,7 @@ .\" .\" from: @(#)fstat.1 8.3 (Berkeley) 2/25/94 .\" -.Dd $Mdocdate: December 15 2008 $ +.Dd $Mdocdate: July 8 2009 $ .Dt FSTAT 1 .Os .Sh NAME @@ -42,7 +42,7 @@ .Op Fl N Ar system .Op Fl p Ar pid .Op Fl u Ar user -.Op Ar file ... +.Op Ar .Sh DESCRIPTION .Nm identifies open files. diff --git a/usr.bin/fstat/fstat.c b/usr.bin/fstat/fstat.c index afb704630f2..491ba0d8738 100644 --- a/usr.bin/fstat/fstat.c +++ b/usr.bin/fstat/fstat.c @@ -1,4 +1,4 @@ -/* $OpenBSD: fstat.c,v 1.67 2009/06/15 04:19:59 miod Exp $ */ +/* $OpenBSD: fstat.c,v 1.68 2009/07/08 16:04:00 millert Exp $ */ /* * Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com> @@ -46,17 +46,19 @@ */ #ifndef lint -static char copyright[] = +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 -/*static char sccsid[] = "from: @(#)fstat.c 8.1 (Berkeley) 6/6/93";*/ -static char *rcsid = "$OpenBSD: fstat.c,v 1.67 2009/06/15 04:19:59 miod Exp $"; +/*static const char sccsid[] = "from: @(#)fstat.c 8.1 (Berkeley) 6/6/93";*/ +static const char rcsid[] = "$OpenBSD: fstat.c,v 1.68 2009/07/08 16:04:00 millert Exp $"; #endif /* not lint */ #include <sys/param.h> +#include <sys/queue.h> +#include <sys/mount.h> #include <sys/stat.h> #include <sys/vnode.h> #include <sys/socket.h> @@ -82,6 +84,7 @@ static char *rcsid = "$OpenBSD: fstat.c,v 1.67 2009/06/15 04:19:59 miod Exp $"; #include <limits.h> #include <nlist.h> #include <pwd.h> +#include <signal.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> @@ -89,13 +92,9 @@ static char *rcsid = "$OpenBSD: fstat.c,v 1.67 2009/06/15 04:19:59 miod Exp $"; #include <unistd.h> #include <err.h> -typedef struct devs { - struct devs *next; - long fsid; - ino_t ino; - char *name; -} DEVS; -DEVS *devs; +#include "fstat.h" + +struct fileargs fileargs = SLIST_HEAD_INITIALIZER(fileargs); int fsflg; /* show files on same filesystem as file(s) argument */ int pflg; /* show files open by a particular pid */ @@ -105,12 +104,17 @@ int nflg; /* (numerical) display f.s. and rdev as dev_t */ int oflg; /* display file offset */ int sflg; /* display file xfer/bytes counters */ int vflg; /* display errors in locating kernel data objects etc... */ +int cflg; /* fuser only */ + +int fuser; /* 1 if we are fuser, 0 if we are fstat */ +int signo; /* signal to send (fuser only) */ kvm_t *kd; uid_t uid; - -void dofiles(struct kinfo_file2 *); +void fstat_dofile(struct kinfo_file2 *); +void fstat_header(void); +void fuser_dofile(struct kinfo_file2 *); void getinetproto(int); void usage(void); int getfname(char *); @@ -121,6 +125,7 @@ void socktrans(struct kinfo_file2 *); void systracetrans(struct kinfo_file2 *); void vtrans(struct kinfo_file2 *); const char *inet6_addrstr(struct in6_addr *); +int signame_to_signum(char *); int main(int argc, char *argv[]) @@ -128,7 +133,7 @@ main(int argc, char *argv[]) struct passwd *passwd; struct kinfo_file2 *kf, *kflast; int arg, ch, what; - char *memf, *nlistf; + char *memf, *nlistf, *optstr; char buf[_POSIX2_LINE_MAX]; const char *errstr; int cnt, flags; @@ -137,11 +142,37 @@ main(int argc, char *argv[]) what = KERN_FILE_BYPID; nlistf = memf = NULL; oflg = 0; - while ((ch = getopt(argc, argv, "fnop:su:vN:M:")) != -1) + + /* are we fstat(1) or fuser(1)? */ + if (strcmp(__progname, "fuser") == 0) { + fuser = 1; + optstr = "cfks:uM:N:"; + } else { + fuser = 0; + optstr = "fnop:su:vN:M:"; + } + + /* + * fuser and fstat share three flags: -f, -s and -u. In both cases + * -f is a boolean, but for -u fstat wants an argument while fuser + * does not and for -s fuser wants an argument whereas fstat does not. + */ + while ((ch = getopt(argc, argv, optstr)) != -1) switch ((char)ch) { + case 'c': + if (fsflg) + usage(); + cflg = 1; + break; case 'f': + if (cflg) + usage(); fsflg = 1; break; + case 'k': + sflg = 1; + signo = SIGKILL; + break; case 'M': memf = optarg; break; @@ -167,14 +198,23 @@ main(int argc, char *argv[]) break; case 's': sflg = 1; + if (fuser) { + signo = signame_to_signum(optarg); + if (signo == -1) { + warnx("invalid signal %s", optarg); + usage(); + } + } break; case 'u': if (uflg++) usage(); - if (!(passwd = getpwnam(optarg))) - errx(1, "%s: unknown uid", optarg); - what = KERN_FILE_BYUID; - arg = passwd->pw_uid; + if (!fuser) { + if (!(passwd = getpwnam(optarg))) + errx(1, "%s: unknown uid", optarg); + what = KERN_FILE_BYUID; + arg = passwd->pw_uid; + } break; case 'v': vflg = 1; @@ -204,12 +244,14 @@ main(int argc, char *argv[]) if (getfname(*argv)) checkfile = 1; } - if (!checkfile) /* file(s) specified, but none accessible */ + /* file(s) specified, but none accessible */ + if (!checkfile) exit(1); - } + } else if (fuser) + usage(); - if (fsflg && !checkfile) { - /* -f with no files means use wd */ + if (!fuser && fsflg && !checkfile) { + /* fstat -f with no files means use wd */ if (getfname(".") == 0) exit(1); checkfile = 1; @@ -217,6 +259,24 @@ main(int argc, char *argv[]) if ((kf = kvm_getfile2(kd, what, arg, sizeof(*kf), &cnt)) == NULL) errx(1, "%s", kvm_geterr(kd)); + + if (!fuser) + fstat_header(); + for (kflast = &kf[cnt]; kf < kflast; ++kf) { + if (fuser) + fuser_check(kf); + else + fstat_dofile(kf); + } + if (fuser) + fuser_run(); + + exit(0); +} + +void +fstat_header(void) +{ if (nflg) printf("%s", "USER CMD PID FD DEV INUM MODE R/W SZ|DV"); @@ -230,10 +290,6 @@ main(int argc, char *argv[]) if (sflg) printf(" XFERS KBYTES"); putchar('\n'); - - for (kflast = &kf[cnt]; kf < kflast; ++kf) - dofiles(kf); - exit(0); } char *Uname, *Comm; @@ -265,7 +321,7 @@ pid_t Pid; * print open files attributed to this process */ void -dofiles(struct kinfo_file2 *kf) +fstat_dofile(struct kinfo_file2 *kf) { Uname = user_from_uid(kf->p_uid, 0); @@ -322,15 +378,15 @@ vtrans(struct kinfo_file2 *kf) if (checkfile) { int fsmatch = 0; - DEVS *d; + struct filearg *fa; if (badtype) return; - for (d = devs; d != NULL; d = d->next) { - if (d->fsid == kf->va_fsid) { + SLIST_FOREACH(fa, &fileargs, next) { + if (fa->dev == kf->va_fsid) { fsmatch = 1; - if (d->ino == kf->va_fileid) { - filename = d->name; + if (fa->ino == kf->va_fileid) { + filename = fa->name; break; } } @@ -537,7 +593,7 @@ socktrans(struct kinfo_file2 *kf) memcpy(&faddr, kf->inp_faddru, sizeof(faddr)); getinetproto(kf->so_protocol); if (kf->so_protocol == IPPROTO_TCP) { - printf(" %p", kf->inp_ppcb); + printf(" %p", (void *)(uintptr_t)kf->inp_ppcb); printf(" %s:%d", laddr.s_addr == INADDR_ANY ? "*" : inet_ntoa(laddr), ntohs(kf->inp_lport)); if (kf->inp_fport) { @@ -558,7 +614,7 @@ socktrans(struct kinfo_file2 *kf) inet_ntoa(faddr), ntohs(kf->inp_fport)); } } else if (kf->so_pcb) - printf(" %p", kf->so_pcb); + printf(" %p", (void *)(uintptr_t)kf->so_pcb); break; #ifdef INET6 case AF_INET6: @@ -567,7 +623,7 @@ socktrans(struct kinfo_file2 *kf) memcpy(&faddr6, kf->inp_faddru, sizeof(faddr6)); getinetproto(kf->so_protocol); if (kf->so_protocol == IPPROTO_TCP) { - printf(" %p", kf->inp_ppcb); + printf(" %p", (void *)(uintptr_t)kf->inp_ppcb); snprintf(xaddrbuf, sizeof(xaddrbuf), "[%s]", inet6_addrstr(&laddr6)); printf(" %s:%d", @@ -598,14 +654,14 @@ socktrans(struct kinfo_file2 *kf) xaddrbuf, ntohs(kf->inp_fport)); } } else if (kf->so_pcb) - printf(" %p", kf->so_pcb); + printf(" %p", (void *)(uintptr_t)kf->so_pcb); break; #endif case AF_UNIX: /* print address of pcb and connected pcb */ printf("* unix %s", stype); if (kf->so_pcb) { - printf(" %p", kf->so_pcb); + printf(" %p", (void *)(uintptr_t)kf->so_pcb); if (kf->unp_conn) { char shoconn[4], *cp; @@ -686,28 +742,80 @@ getinetproto(number) int getfname(char *filename) { - struct stat statbuf; - DEVS *cur; + static struct statfs *mntbuf; + static int nmounts; + int i; + struct stat sb; + struct filearg *cur; - if (stat(filename, &statbuf)) { + if (stat(filename, &sb)) { warn("%s", filename); - return(0); + return (0); } - if ((cur = malloc(sizeof(DEVS))) == NULL) - err(1, "malloc"); - cur->next = devs; - devs = cur; - cur->ino = statbuf.st_ino; - cur->fsid = statbuf.st_dev & 0xffff; + /* + * POSIX specifies "For block special devices, all processes using any + * file on that device are listed". However the -f flag description + * states "The report shall be only for the named files", so we only + * look up a block device if the -f flag has not be specified. + */ + if (fuser && !fsflg && S_ISBLK(sb.st_mode)) { + if (mntbuf == NULL) { + nmounts = getmntinfo(&mntbuf, MNT_NOWAIT); + if (nmounts == -1) + err(1, "getmntinfo"); + } + for (i = 0; i < nmounts; i++) { + if (!strcmp(mntbuf[i].f_mntfromname, filename)) { + if (stat(mntbuf[i].f_mntonname, &sb) == -1) { + warn("%s", filename); + return (0); + } + cflg = 1; + break; + } + } + } + + if ((cur = malloc(sizeof(*cur))) == NULL) + err(1, NULL); + + cur->ino = sb.st_ino; + cur->dev = sb.st_dev & 0xffff; cur->name = filename; - return(1); + TAILQ_INIT(&cur->fusers); + SLIST_INSERT_HEAD(&fileargs, cur, next); + return (1); +} + +int +signame_to_signum(char *sig) +{ + int n; + const char *errstr = NULL; + + if (isdigit((unsigned char)*sig)) { + n = strtonum(sig, 0, NSIG - 1, &errstr); + return (errstr ? -1 : n); + } + if (!strncasecmp(sig, "sig", 3)) + sig += 3; + for (n = 1; n < NSIG; n++) { + if (!strcasecmp(sys_signame[n], sig)) + return (n); + } + return (-1); } void usage(void) { - fprintf(stderr, "usage: fstat [-fnosv] [-M core] [-N system] " - "[-p pid] [-u user] [file ...]\n"); + if (fuser) { + fprintf(stderr, "usage: fuser [-cfku] [-M core] " + "[-N system] [-s signal] file ...\n"); + } else { + fprintf(stderr, "usage: fstat [-fnosv] [-M core] [-N system] " + "[-p pid] [-u user] [file ...]\n"); + } exit(1); } diff --git a/usr.bin/fstat/fstat.h b/usr.bin/fstat/fstat.h new file mode 100644 index 00000000000..ba0337b5ff2 --- /dev/null +++ b/usr.bin/fstat/fstat.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct fuser { + TAILQ_ENTRY(fuser) tq; + uid_t uid; + pid_t pid; + int flags; +#define F_ROOT 0x01 /* is procs root directory */ +#define F_CWD 0x02 /* is procs cwd */ +#define F_OPEN 0x04 /* just has it open */ +}; + +struct filearg { + SLIST_ENTRY(filearg) next; + dev_t dev; + ino_t ino; + char *name; + TAILQ_HEAD(fuserhead, fuser) fusers; +}; + +SLIST_HEAD(fileargs, filearg); + +extern int uflg; +extern int cflg; +extern int fsflg; +extern int sflg; +extern int signo; +extern int error; +extern struct fileargs fileargs; + +extern char *__progname; + +void fuser_check(struct kinfo_file2 *); +void fuser_run(void); +void usage(void); diff --git a/usr.bin/fstat/fuser.1 b/usr.bin/fstat/fuser.1 new file mode 100644 index 00000000000..f855c644d29 --- /dev/null +++ b/usr.bin/fstat/fuser.1 @@ -0,0 +1,144 @@ +.\" $OpenBSD: fuser.1,v 1.1 2009/07/08 16:04:00 millert Exp $ +.\" +.\" Copyright (c) 2002 Peter Werner <peterw@ifost.org.au> +.\" 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. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED ``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. +.\" +.Dd $Mdocdate: July 8 2009 $ +.Dt FUSER 1 +.Os +.Sh NAME +.Nm fuser +.Nd identify process IDs holding specific files open +.Sh SYNOPSIS +.Nm +.Op Fl cfku +.Op Fl M Ar core +.Op Fl N Ar system +.Op Fl s Ar signal +.Ar +.Sh DESCRIPTION +The +.Nm +utility writes to standard output the process IDs of processes running +on the local system that have one or more of the named files open. +If +.Ar file +is a mounted block device, the output will show all processes having +files opened on that file system. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl c +The file is treated as a mount point and +.Nm +will report on any files open in the file system. +.It Fl f +Report only for the named file(s). +.It Fl k +Send the SIGKILL signal to each process. +.It Fl M Ar core +Extract values associated with the name list from the specified core +instead of the running kernel. +.It Fl N Ar system +Extract the name list from the specified system instead of the running kernel. +.It Fl s Ar signal +Send the specified +.Ar signal +to each process. +The +.Ar signal +may be specified as a signal number or as a symbolic name either with +or without the +.Dq SIG +prefix. +Signal names are not case sensitive. +For example, the following signal arguments are equivalent: +.Dq 9 , +.Dq kill , +.Dq SIGKILL . +.It Fl u +The real user ID of the the process using the file will be printed in brackets +to standard error. +If the username is unable to be determined, the real user ID will be printed +instead. +.El +.Pp +The +.Fl c +and +.Fl f +options are mutually exclusive. +.Pp +The name of the file followed by a colon +.Pq Sq \&: +is printed to standard error. +The following characters may be printed to standard error after the process ID +if the described conditions are true: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It c +The file is the process's current working directory. +.It r +The file is the process's root directory. +.El +.Pp +.Ex -std fuser +.Sh EXAMPLES +Print the process IDs of any processes holding files open under the +.Ar /mnt +filesystem: +.Pp +.Dl $ fuser -c /mnt +.Pp +Send SIGTERM to any process holding a file open under the +.Ar /mnt +filesystem: +.Pp +.Dl # fuser -c -s term /mnt +.Pp +Report on all files opened under the filesystem on which +.Ar /dev/wd0a +is mounted: +.Pp +.Dl $ fuser /dev/wd0a +.Pp +Report on all processes currently holding +.Ar /dev/wd0a +open: +.Pp +.Dl $ fuser -f /dev/wd0a +.Sh SEE ALSO +.Xr fstat 1 , +.Xr kill 1 , +.Xr signal 3 , +.Xr mount 8 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The flags +.Op Fl ks +are extensions to that specification. diff --git a/usr.bin/fstat/fuser.c b/usr.bin/fstat/fuser.c new file mode 100644 index 00000000000..3f146373d72 --- /dev/null +++ b/usr.bin/fstat/fuser.c @@ -0,0 +1,177 @@ +/* $OpenBSD: fuser.c,v 1.1 2009/07/08 16:04:00 millert Exp $ */ + +/* + * Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (c) 2002 Peter Werner <peterw@ifost.org.au> + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``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/param.h> +#include <sys/queue.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#define _KERNEL /* for DTYPE_VNODE */ +#include <sys/file.h> +#undef _KERNEL + +#include <err.h> +#include <fcntl.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "fstat.h" + +/* + * Returns 1 if the file watched (fa) is equivalent + * to a file held by a process (kf), else 0. + */ +static int +match(struct filearg *fa, struct kinfo_file2 *kf) +{ + if (fa->dev == kf->va_fsid) { + if (cflg) + return (1); + if (fa->ino == kf->va_fileid) + return (1); + } + return (0); +} + +/* + * Examine kinfo_file2 struct and record the details if they + * match a watched file. + */ +void +fuser_check(struct kinfo_file2 *kf) +{ + struct filearg *fa; + struct fuser *fu; + + if (kf->f_type != DTYPE_VNODE) + return; + + SLIST_FOREACH(fa, &fileargs, next) { + if (!match(fa, kf)) + continue; + + /* + * This assumes that kinfo_files2 returns all files + * associated with a process in a contiguous block. + */ + if (TAILQ_EMPTY(&fa->fusers) || kf->p_pid != + (fu = TAILQ_LAST(&fa->fusers, fuserhead))->pid) { + fu = malloc(sizeof(*fu)); + if (fu == NULL) + err(1, NULL); + fu->pid = kf->p_pid; + fu->uid = kf->p_uid; + fu->flags = 0; + TAILQ_INSERT_TAIL(&fa->fusers, fu, tq); + } + switch (kf->fd_fd) { + case KERN_FILE_CDIR: + fu->flags |= F_CWD; + break; + case KERN_FILE_RDIR: + fu->flags |= F_ROOT; + break; + case KERN_FILE_TRACE: + case KERN_FILE_TEXT: + /* ignore */ + break; + default: + fu->flags |= F_OPEN; + break; + } + } +} + +/* + * Print out the specfics for a given file/filesystem + */ +static void +printfu(struct fuser *fu) +{ + struct passwd *pwd; + + printf("%d", fu->pid); + fflush(stdout); + + if (fu->flags & F_CWD) + fprintf(stderr, "c"); + + if (fu->flags & F_ROOT) + fprintf(stderr, "r"); + + if (uflg) { + pwd = getpwuid(fu->uid); + if (pwd != NULL) + fprintf(stderr, "(%s)", pwd->pw_name); + else + fprintf(stderr, "(%d)", fu->uid); + } + + putchar(' '); +} + +/* + * For each file, print matching process info and optionally send a signal. + */ +void +fuser_run(void) +{ + struct filearg *fa; + struct fuser *fu; + pid_t mypid = getpid(); + + SLIST_FOREACH(fa, &fileargs, next) { + fprintf(stderr, "%s: ", fa->name); + TAILQ_FOREACH(fu, &fa->fusers, tq) { + printfu(fu); + if (sflg && fu->pid != mypid) { + kill(fu->pid, signo); + } + } + fflush(stdout); + fprintf(stderr, "\n"); + } +} |