summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sysutils/fam/MESSAGE19
-rw-r--r--sysutils/fam/MESSAGE.kqueue10
-rw-r--r--sysutils/fam/Makefile32
-rw-r--r--sysutils/fam/distinfo4
-rw-r--r--sysutils/fam/files/IMonKQueue.c++407
-rw-r--r--sysutils/fam/files/famd.sh17
-rw-r--r--sysutils/fam/files/imon-compat.h53
-rw-r--r--sysutils/fam/patches/patch-ag10
8 files changed, 536 insertions, 16 deletions
diff --git a/sysutils/fam/MESSAGE b/sysutils/fam/MESSAGE
index 07d709071cf..65120dd1c34 100644
--- a/sysutils/fam/MESSAGE
+++ b/sysutils/fam/MESSAGE
@@ -1,17 +1,20 @@
===========================================================================
-$NetBSD: MESSAGE,v 1.3 2004/03/28 22:00:04 minskim Exp $
+$NetBSD: MESSAGE,v 1.4 2004/10/17 19:20:53 jmmv Exp $
-FAM uses RPC and is usually started by the inetd(8) superserver. You need
-to enable it manually, by issuing the following steps:
-
-1) Add FAM to the system's portmapper by appending the following line to
- the /etc/rpc file:
+Because FAM uses RPC, you have to add the following line to the system's
+portmapper file (/etc/rpc):
sgi_fam 391002 fam # File Alteration Monitor
-2) Add FAM to the system's superserver by appending the following line to
- the /etc/inetd.conf file:
+After that, restart the rpcbind(8) server.
+
+Furthermore, you have to enable the FAM daemon. There are two ways to do
+this: through inetd(8) or as a standalone server. If you prefer the former
+way, add the following line to /etc/inetd.conf:
sgi_fam/1-2 stream rpc/tcp wait root ${PREFIX}/sbin/famd famd
+and reload inetd(8)'s daemon. However, if you prefer the later way, use
+the provided famd rc.d(8) script.
+
===========================================================================
diff --git a/sysutils/fam/MESSAGE.kqueue b/sysutils/fam/MESSAGE.kqueue
new file mode 100644
index 00000000000..809a0b41d6f
--- /dev/null
+++ b/sysutils/fam/MESSAGE.kqueue
@@ -0,0 +1,10 @@
+===========================================================================
+$NetBSD: MESSAGE.kqueue,v 1.1 2004/10/17 19:20:53 jmmv Exp $
+
+FAM has been built with kqueue support. Consider raising the amount of
+open files the kernel can handle (i.e., change kern.maxfiles using
+sysctl(8)). FAM will consume a maximum of a half of that limit; this can
+be quickly exhausted if you monitor large directories (a quite common thing
+if you navigate through your filesystem using Nautilus).
+
+===========================================================================
diff --git a/sysutils/fam/Makefile b/sysutils/fam/Makefile
index cfa64668fa0..2e1c8ab1b22 100644
--- a/sysutils/fam/Makefile
+++ b/sysutils/fam/Makefile
@@ -1,8 +1,8 @@
-# $NetBSD: Makefile,v 1.17 2004/10/16 14:40:23 jmmv Exp $
+# $NetBSD: Makefile,v 1.18 2004/10/17 19:20:53 jmmv Exp $
#
DISTNAME= fam-2.7.0
-PKGREVISION= 3
+PKGREVISION= 4
CATEGORIES= sysutils devel
MASTER_SITES= ftp://oss.sgi.com/projects/fam/download/stable/ \
ftp://ftp.tuwien.ac.at/opsys/linux/gentoo/distfiles/ \
@@ -26,10 +26,36 @@ CONFIGURE_ENV+= CPPFLAGS="${CPPFLAGS} -DNDEBUG"
EGDIR= ${PREFIX}/share/examples/fam
CONF_FILES= ${EGDIR}/fam.conf ${PKG_SYSCONFDIR}/fam.conf
+RCD_SCRIPTS= famd
+
SUBST_CLASSES+= paths
SUBST_MESSAGE.paths= "Fixing hardcoded paths."
-SUBST_STAGE.paths= post-patch
+SUBST_STAGE.paths= pre-configure
SUBST_FILES.paths= man/famd.conf.5 man/famd.8
SUBST_SED.paths= -e 's,/usr/local/etc/,${PKG_SYSCONFDIR}/,g'
+PKG_OPTIONS_VAR= PKG_OPTIONS.fam
+PKG_SUPPORTED_OPTIONS= kqueue
+
+.include "../../mk/bsd.options.mk"
+
+.if !empty(PKG_OPTIONS:Mkqueue) && ${OPSYS} == "NetBSD"
+CPPFLAGS+= -DHAVE_KQUEUE
+LIBS+= -lpthread
+
+SUBST_CLASSES+= kqueue
+SUBST_MESSAGE.kqueue= "Enabling kqueue monitoring."
+SUBST_STAGE.kqueue= pre-configure
+SUBST_FILES.kqueue= configure
+SUBST_SED.kqueue= -e 's,IMonNone,IMonKQueue,g'
+
+MESSAGE_SRC= ${.CURDIR}/MESSAGE ${.CURDIR}/MESSAGE.kqueue
+
+.include "../../mk/pthread.buildlink3.mk"
+.endif
+
+post-extract:
+ ${CP} ${FILESDIR}/IMonKQueue.c++ ${WRKSRC}/src
+ ${CP} ${FILESDIR}/imon-compat.h ${WRKSRC}/src
+
.include "../../mk/bsd.pkg.mk"
diff --git a/sysutils/fam/distinfo b/sysutils/fam/distinfo
index 845c060a0cc..8172ca11b7e 100644
--- a/sysutils/fam/distinfo
+++ b/sysutils/fam/distinfo
@@ -1,4 +1,4 @@
-$NetBSD: distinfo,v 1.14 2004/10/16 14:40:23 jmmv Exp $
+$NetBSD: distinfo,v 1.15 2004/10/17 19:20:53 jmmv Exp $
SHA1 (fam-2.7.0.tar.gz) = 6c2316f02acf89a41c42ffc3d7fd9cf5eada83a8
Size (fam-2.7.0.tar.gz) = 301974 bytes
@@ -8,7 +8,7 @@ SHA1 (patch-ac) = 676966b0372780af4ee5536276132a04dd038863
SHA1 (patch-ad) = b8e621acd36811a76a84af82e6f2b5962973e344
SHA1 (patch-ae) = 225a0bd5195be3d3d75edf021b27bed19d84dc15
SHA1 (patch-af) = 57946b3837479b641bb002620ae41008f49af995
-SHA1 (patch-ag) = fa5889ad6d93af72d7efe83784caf61b2ac39d6a
+SHA1 (patch-ag) = 978fa2a582c5f9d2c8660c0a8d933211e97ef500
SHA1 (patch-ah) = d7763198df76d1f0783342a8961b59879e8e1241
SHA1 (patch-ai) = b80aafbb3849fc8c828b6829d8975b910e4d0fd5
SHA1 (patch-aj) = 39391961fd7929d6a5fb49ecb492585cb821afaa
diff --git a/sysutils/fam/files/IMonKQueue.c++ b/sysutils/fam/files/IMonKQueue.c++
new file mode 100644
index 00000000000..17da4d9cb6e
--- /dev/null
+++ b/sysutils/fam/files/IMonKQueue.c++
@@ -0,0 +1,407 @@
+// $NetBSD: IMonKQueue.c++,v 1.1 2004/10/17 19:20:53 jmmv Exp $
+//
+// Copyright (c) 2004 Julio M. Merino Vidal.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of version 2 of the GNU General Public License as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it would be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Further, any
+// license provided herein, whether implied or otherwise, is limited to
+// this program in accordance with the express provisions of the GNU
+// General Public License. Patent licenses, if any, provided herein do not
+// apply to combinations of this program with other product or programs, or
+// any other product whatsoever. This program is distributed without any
+// warranty that the program is delivered free of the rightful claim of any
+// third person by way of infringement or the like. See the GNU General
+// Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write the Free Software Foundation, Inc., 59
+// Temple Place - Suite 330, Boston MA 02111-1307, USA.
+
+// ------------------------------------------------------------------------
+
+// imon emulation through kqueue
+// -----------------------------
+//
+// The code in this file provides an imon-like interface to FAM using kqueue,
+// the kernel event notification mechanism found in NetBSD and OpenBSD.
+//
+// The idea is the following: a thread, kqueue_monitor, simulates the kernel
+// part of imon. This thread can receive commands (ioctl(2)s) and produces
+// notifications when there is something to notify. The thread is constantly
+// running in the background, calling kevent(2) to see if there are new
+// events in the monitored files since the last call.
+//
+// Communication with kqueue_monitor is accomplished by using two pipes.
+// pipe_out is used by the monitor to provide notifications; i.e., it is the
+// same as the read end of the regular /dev/imon device, and produces
+// compatible messages. On the other hand we have pipe_in, which is used
+// to give commands to the monitor (express and revoke); we can't emulate
+// ioctl(2)s from user space, so we have to go this route.
+//
+// Why we use pipe_in to provide commands to the thread, instead of some
+// mutexes? If we used mutexes, we'd have to give kevent(2) a timeout, to
+// let it "reload" the list of changes to be monitored in case it was
+// externally modified. By using a pipe, we can tell kqueue(2) to monitor
+// it for us, and let kevent(2) immediately return when there is a command
+// to process.
+//
+// However, there is a little problem when using kqueue instead of imon or
+// polling. kqueue(2) works by monitoring open file descriptors, instead
+// of inodes on the disk. Therefore we must keep all files being monitored
+// open, and the number of open files can quickly raise in some environments.
+// This is why the code unlimits the number of open files in imon_open and
+// sets a reasonable maximum based on kern.maxfiles (to avoid overflowing
+// it quickly). If we overflow this limit, the poller will enter the game
+// (because we will return an error).
+//
+// Known problem: if we receive *lots* of events quickly, we may end up
+// locked in pipewr. I haven't located where the problem is, but I
+// suspect of the read code in read_handler in IMon.c++. To reproduce,
+// run the test program provided by fam against a local directory, say
+// /tmp/foo, and run the following:
+// cd /tmp/foo; for f in $(jot 1000); do touch $(jot 100); rm *; done
+//
+// Having said all this, let's go to the code...
+
+// ------------------------------------------------------------------------
+
+#include "IMon.h"
+#include "Log.h"
+
+#include "config.h"
+#include "imon-compat.h"
+
+#include <sys/event.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <map>
+
+// ------------------------------------------------------------------------
+
+// devino is a structure that holds a device/inode pair. It is used as an
+// indentifier of files managed by imon.
+struct devino {
+ dev_t di_dev;
+ ino_t di_ino;
+
+ bool operator<(const struct devino& di) const
+ { return (di_dev < di.di_dev) || (di_ino < di.di_ino); }
+};
+
+// imon_cmd simulates commands thrown to imon as ioctl(2)s (but remember
+// we use a pipe).
+struct imon_cmd {
+#define IMON_CMD_EXPRESS 0
+#define IMON_CMD_REVOKE 1
+ int ic_type;
+
+ // imon identifies files through a device/inode pair.
+ struct devino ic_di;
+
+ // A pipe that will be used to receive the result of the command
+ // (asynchronously).
+ int ic_stat[2];
+
+ // If this is an 'express' command, we need the descriptor to monitor.
+ int ic_fd;
+};
+
+// ------------------------------------------------------------------------
+
+static int max_changes;
+static int last_change;
+static int kqueue_fd;
+static int pipe_in[2], pipe_out[2];
+static pthread_t kevent_thread;
+static struct kevent *changes;
+
+typedef std::map<struct devino, int> DEVINOFD_MAP;
+static DEVINOFD_MAP devino_to_fd;
+typedef std::map<int, struct devino> FDDEVINO_MAP;
+static FDDEVINO_MAP fd_to_devino;
+
+// ------------------------------------------------------------------------
+
+static void *kqueue_monitor(void *data);
+static void process_command(void);
+
+// ------------------------------------------------------------------------
+
+int
+IMon::imon_open(void)
+{
+ // Get the kernel event queue. We only need one during all the life
+ // of famd.
+ kqueue_fd = kqueue();
+ if (kqueue_fd == -1)
+ return -1;
+
+ // Create "emulation" pipes.
+ if (pipe(pipe_in) == -1) {
+ close(kqueue_fd);
+ return -1;
+ }
+ if (pipe(pipe_out) == -1) {
+ close(kqueue_fd);
+ close(pipe_in[0]); close(pipe_in[1]);
+ return -1;
+ }
+
+ // Get the maximum number of files we can open and use it to set a
+ // limit of the files we can monitor.
+ size_t len = sizeof(max_changes);
+ if (sysctlbyname("kern.maxfiles", &max_changes, &len, NULL, 0) == -1)
+ max_changes = 128;
+ else
+ max_changes /= 2;
+
+ // Unlimit maximum number of open files. We don't go to RLIM_INFINITY
+ // to avoid possible open descriptor leaks produce a system DoS. 75%
+ // of the system limit seems a good number (we request more than the
+ // number calculated previously to leave room for temporary pipes).
+ // We need to be root to do this.
+ uid_t olduid = geteuid();
+ seteuid(0);
+ struct rlimit rlp;
+ rlp.rlim_cur = rlp.rlim_max = max_changes * 3 / 2;
+ if (setrlimit(RLIMIT_NOFILE, &rlp) == -1)
+ Log::error("can't unlimit number of open files");
+ seteuid(olduid);
+
+ changes = new struct kevent[max_changes];
+
+ // We must monitor pipe_in for any commands that may alter the actual
+ // set of files being monitored.
+ EV_SET(&changes[0], pipe_in[0], EVFILT_READ,
+ EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, 0);
+ last_change = 1;
+
+ // Create a thread that will run the kevent(2) function continuously.
+ if (pthread_create(&kevent_thread, NULL, kqueue_monitor, NULL) != 0) {
+ close(kqueue_fd);
+ close(pipe_in[0]); close(pipe_in[1]);
+ close(pipe_out[0]); close(pipe_out[1]);
+ return -1;
+ }
+
+ return pipe_out[0];
+}
+
+// ------------------------------------------------------------------------
+
+IMon::Status
+IMon::imon_express(const char *name, struct stat *status)
+{
+ // Get file information.
+ if (status == NULL) {
+ // XXX This leaks memory. AFAICT, the code calling this function
+ // expects status to be filled if it was NULL. If this is true,
+ // the code in IMonLinux does worse than us, because it returns a
+ // pointer to a local variable...
+ status = new struct stat;
+ }
+ if (lstat(name, status) == -1)
+ return BAD;
+
+ // Open the file to be monitored; kqueue only works with open descriptors
+ // so we have to keep this descriptor during the life of this 'interest'.
+ int fd = open(name, O_RDONLY);
+ if (fd == -1)
+ return BAD;
+
+ // Construct a command to 'express' interest in a file. This will be
+ // handled by the kqueue_monitor thread as soon as possible.
+ struct imon_cmd cmd;
+ cmd.ic_type = IMON_CMD_EXPRESS;
+ cmd.ic_di.di_dev = status->st_dev;
+ cmd.ic_di.di_ino = status->st_ino;
+ cmd.ic_fd = fd;
+ if (pipe(cmd.ic_stat) == -1) {
+ close(fd);
+ return BAD;
+ }
+ write(pipe_in[1], &cmd, sizeof(struct imon_cmd));
+
+ // Wait for a result form the previous operation.
+ bool result;
+ read(cmd.ic_stat[0], &result, sizeof(bool));
+ close(cmd.ic_stat[0]); close(cmd.ic_stat[1]);
+ if (!result) {
+ close(fd);
+ Log::error("kqueue can't monitor more than %d files", max_changes);
+ return BAD;
+ }
+
+ Log::debug("told kqueue to monitor \"%s\", descriptor = %d, dev = %d, "
+ "ino = %d", name, cmd.ic_fd, cmd.ic_di.di_dev,
+ cmd.ic_di.di_ino);
+
+ return OK;
+}
+
+// ------------------------------------------------------------------------
+
+IMon::Status
+IMon::imon_revoke(const char *name, dev_t dev, ino_t ino)
+{
+ // Construct a command to 'revoke' interest from a file. This will be
+ // handled by the kqueue_monitor thread as soon as possible.
+ struct imon_cmd cmd;
+ cmd.ic_type = IMON_CMD_REVOKE;
+ cmd.ic_di.di_dev = dev;
+ cmd.ic_di.di_ino = ino;
+ if (pipe(cmd.ic_stat) == -1)
+ return BAD;
+ write(pipe_in[1], &cmd, sizeof(struct imon_cmd));
+
+ // Wait for a result form the previous operation.
+ bool result;
+ read(cmd.ic_stat[0], &result, sizeof(bool));
+ close(cmd.ic_stat[0]); close(cmd.ic_stat[1]);
+ if (!result) {
+ Log::error("kqueue can't revoke \"%s\", dev = %d, ino = %d", name,
+ cmd.ic_di.di_dev, cmd.ic_di.di_ino);
+ return BAD;
+ }
+
+ Log::debug("told kqueue to forget \"%s\", dev = %d, ino = %d", name,
+ cmd.ic_di.di_dev, cmd.ic_di.di_ino);
+
+ return OK;
+}
+
+// ------------------------------------------------------------------------
+
+static void *
+kqueue_monitor(void *data)
+{
+ struct kevent event;
+
+ for (;;) {
+ int nev = kevent(kqueue_fd, changes, last_change, &event, 1, NULL);
+ if (nev == -1)
+ Log::perror("kevent");
+ else if (nev > 0) {
+ if (event.ident == pipe_in[0]) {
+ // We have got a control command, so process it.
+ process_command();
+ } else {
+ // One of the descriptors we are monitoring has got activity.
+ FDDEVINO_MAP::const_iterator iter =
+ fd_to_devino.find(event.ident);
+ if (iter != fd_to_devino.end()) {
+ qelem_t elem;
+
+ // Set device/inode identifier on imon element.
+ const struct devino &di = (*iter).second;
+ elem.qe_dev = di.di_dev;
+ elem.qe_inode = di.di_ino;
+
+ // Convert the modification flags reported by kqueue to
+ // flags understood by imon.
+ elem.qe_what = 0;
+ if (event.fflags & NOTE_DELETE)
+ elem.qe_what |= IMON_DELETE;
+ if (event.fflags & NOTE_RENAME)
+ elem.qe_what |= IMON_RENAME;
+ if (event.fflags & NOTE_ATTRIB || event.fflags & NOTE_LINK)
+ elem.qe_what |= IMON_ATTRIBUTE;
+ if (event.fflags & NOTE_WRITE || event.fflags & NOTE_EXTEND)
+ elem.qe_what |= IMON_CONTENT;
+
+ // Deliver the element.
+ write(pipe_out[1], &elem, sizeof(qelem_t));
+ } else
+ Log::error("got an event from an unhandled device/inode "
+ "pair");
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------
+
+static void
+process_command(void)
+{
+ bool result = false;
+ struct imon_cmd cmd;
+
+ // Read the command from the control pipe.
+ read(pipe_in[0], &cmd, sizeof(struct imon_cmd));
+ if (cmd.ic_type == IMON_CMD_EXPRESS) {
+ if (devino_to_fd.find(cmd.ic_di) != devino_to_fd.end()) {
+ // The file is already being monitored.
+ close(cmd.ic_fd);
+ result = true;
+ } else if (fd_to_devino.find(cmd.ic_fd) != fd_to_devino.end()) {
+ // We can't receive a new interest of a descriptor that is
+ // already being monitored. If this happens, there is an
+ // inconsistency in the data somewhere.
+ assert(false);
+ } else if (last_change < max_changes) {
+ // Add the new descriptor to the list of changes to monitor.
+ // We watch for any change that happens on it.
+ EV_SET(&changes[last_change], cmd.ic_fd, EVFILT_VNODE,
+ EV_ADD | EV_ENABLE | EV_ONESHOT,
+ NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB |
+ NOTE_LINK | NOTE_RENAME | NOTE_REVOKE,
+ 0, 0);
+ last_change++;
+
+ // Map the device/inode pair to the file descriptor associated
+ // to it and viceversa. We will need this information during
+ // 'revoke' and when we receive events. We use two different
+ // maps to speed up searches in both directions later.
+ devino_to_fd.insert
+ (DEVINOFD_MAP::value_type(cmd.ic_di, cmd.ic_fd));
+ fd_to_devino.insert
+ (FDDEVINO_MAP::value_type(cmd.ic_fd, cmd.ic_di));
+
+ result = true;
+ }
+ } else if (cmd.ic_type == IMON_CMD_REVOKE) {
+ DEVINOFD_MAP::const_iterator iter = devino_to_fd.find(cmd.ic_di);
+ if (iter != devino_to_fd.end()) {
+ // Get the descriptor associated to the given device/inode pair
+ // and remove the mapping from the required structure.
+ int fd = (*iter).second;
+ devino_to_fd.erase(cmd.ic_di);
+ fd_to_devino.erase(fd);
+
+ // Remove the entry associated to the descriptor from the list
+ // of changes monitored by kqueue.
+ int i;
+ for (i = 1; i < last_change; i++)
+ if (changes[i].ident == fd)
+ break;
+ for (int j = i; j < last_change - 1; j++)
+ changes[j] = changes[j + 1];
+ last_change--;
+
+ close(fd);
+
+ result = true;
+ }
+ } else {
+ // Huh? Unknown command received.
+ assert(false);
+ }
+
+ // Deliver the result of the operation.
+ write(cmd.ic_stat[1], &result, sizeof(bool));
+}
diff --git a/sysutils/fam/files/famd.sh b/sysutils/fam/files/famd.sh
new file mode 100644
index 00000000000..9ca6126fb1d
--- /dev/null
+++ b/sysutils/fam/files/famd.sh
@@ -0,0 +1,17 @@
+#!@RCD_SCRIPTS_SHELL@
+#
+# $NetBSD: famd.sh,v 1.1 2004/10/17 19:20:53 jmmv Exp $
+#
+# PROVIDE: famd
+# REQUIRE: DAEMON
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="famd"
+rcvar=$name
+command="@PREFIX@/sbin/famd"
+command_args="-T 0 -L &"
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/sysutils/fam/files/imon-compat.h b/sysutils/fam/files/imon-compat.h
new file mode 100644
index 00000000000..65d9fdb198a
--- /dev/null
+++ b/sysutils/fam/files/imon-compat.h
@@ -0,0 +1,53 @@
+// $NetBSD: imon-compat.h,v 1.1 2004/10/17 19:20:53 jmmv Exp $
+//
+// Copyright (c) 2004 Julio M. Merino Vidal.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of version 2 of the GNU General Public License as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it would be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Further, any
+// license provided herein, whether implied or otherwise, is limited to
+// this program in accordance with the express provisions of the GNU
+// General Public License. Patent licenses, if any, provided herein do not
+// apply to combinations of this program with other product or programs, or
+// any other product whatsoever. This program is distributed without any
+// warranty that the program is delivered free of the rightful claim of any
+// third person by way of infringement or the like. See the GNU General
+// Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write the Free Software Foundation, Inc., 59
+// Temple Place - Suite 330, Boston MA 02111-1307, USA.
+
+#if !defined(IMON_COMPAT_H)
+#define IMON_COMPAT_H
+
+#if defined(HAVE_IMON)
+# error "cannot include imon-compat.h if imon is really present"
+#endif
+
+#if defined(HAVE_KQUEUE)
+#define HAVE_IMON 1
+
+typedef int intmask_t;
+
+typedef struct {
+ dev_t qe_dev;
+ ino_t qe_inode;
+ intmask_t qe_what;
+} qelem_t;
+
+#define IMON_CONTENT (1 << 0)
+#define IMON_ATTRIBUTE (1 << 1)
+#define IMON_DELETE (1 << 2)
+#define IMON_EXEC (1 << 3)
+#define IMON_EXIT (1 << 4)
+#define IMON_RENAME (1 << 5)
+#define IMON_OVER 0xff
+
+#endif // defined(HAVE_KQUEUE)
+
+#endif // !defined(IMON_COMPAT_H)
diff --git a/sysutils/fam/patches/patch-ag b/sysutils/fam/patches/patch-ag
index 3d8fce2cc61..01224f0acb9 100644
--- a/sysutils/fam/patches/patch-ag
+++ b/sysutils/fam/patches/patch-ag
@@ -1,6 +1,6 @@
-$NetBSD: patch-ag,v 1.4 2004/03/28 22:00:05 minskim Exp $
+$NetBSD: patch-ag,v 1.5 2004/10/17 19:20:53 jmmv Exp $
---- src/IMon.c++.orig 2003-01-18 08:18:12.000000000 -0600
+--- src/IMon.c++.orig 2003-01-18 15:18:12.000000000 +0100
+++ src/IMon.c++
@@ -25,6 +25,7 @@
#include <assert.h>
@@ -10,8 +10,12 @@ $NetBSD: patch-ag,v 1.4 2004/03/28 22:00:05 minskim Exp $
#if HAVE_IMON
#ifdef __sgi
-@@ -34,7 +35,9 @@
+@@ -32,9 +33,13 @@
+ #else
+ #include <linux/imon.h>
#endif
++#else // HAVE_IMON
++#include "imon-compat.h"
#endif
+#if HAVE_SYS_SYSMACROS_H