summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2009-07-08 16:04:01 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2009-07-08 16:04:01 +0000
commitdb88960d516b2c5ac6c28f48ad6f552239017182 (patch)
tree2eb62c4b472e6eab4b3b59c0b4c498be8772e383
parentadaaa9d9490a77fc421f46a2b0f652b6d9c9199e (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/Makefile5
-rw-r--r--usr.bin/fstat/fstat.16
-rw-r--r--usr.bin/fstat/fstat.c210
-rw-r--r--usr.bin/fstat/fstat.h49
-rw-r--r--usr.bin/fstat/fuser.1144
-rw-r--r--usr.bin/fstat/fuser.c177
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");
+ }
+}