summaryrefslogtreecommitdiff
path: root/sysutils/fam
diff options
context:
space:
mode:
authorjmmv <jmmv>2004-10-17 19:20:53 +0000
committerjmmv <jmmv>2004-10-17 19:20:53 +0000
commit51ca66dbcff1a9e0367fe976249fd7b6aa821ba6 (patch)
tree51577a67ccbf14a8aa45f196c661bc17568591bf /sysutils/fam
parentf34d1e2c6966cdbac2813fb01acbbbb3c4dbf5d9 (diff)
downloadpkgsrc-51ca66dbcff1a9e0367fe976249fd7b6aa821ba6.tar.gz
Add kqueue support. This way, famd can be notified of changes to files and
directories in "real time", without having to periodically poll(2) for them after several seconds. This improves the responsiveness of applications using famd (specially GNOME) when changes to files occur from the "outside" (they are notified from changes immediately), or even from the application itself (for example, have you ever tried to rename several files from Nautilus? just a PITA). To enable kqueue, you have to pass the 'kqueue' option to the package. I'm not enabling it by default because it needs testing (but it should be enabled in a future). Furthermore, I'd like to send these patches to the FAM developers for comments too. And a review from somebody knowing kqueue could be good! (this is the first time I use the kqueue interface, so I'm not sure if everything is right). A description on how this works can be found in the files/IMonKQueue.c++ file. Note that, due to FAM's design, the easiest way to do this change is to "emulate" imon functionality. While here, add an rc.d script for famd, in case the user prefers to run it at system startup instead of from inetd (I created it while developing the kqueue functionality, so it's a good moment to add it). Adjust the MESSAGE accordingly. Bump PKGREVISION to 4.
Diffstat (limited to 'sysutils/fam')
-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