--- dovecot-1.0-test76/config.h.in	2005-07-06 21:38:46.030495803 +0200
+++ dovecot-1.0-test76.inotify/config.h.in	2005-07-07 21:13:53.585774828 +0200
@@ -240,6 +240,9 @@
 /* Use Linux dnotify */
 #undef IOLOOP_NOTIFY_DNOTIFY
 
+/* Use Linux inotify */
+#undef IOLOOP_NOTIFY_INOTIFY
+
 /* No special notify support */
 #undef IOLOOP_NOTIFY_NONE
 
--- dovecot-1.0-test76/configure.in	2005-07-06 21:38:45.019457974 +0200
+++ dovecot-1.0-test76.inotify/configure.in	2005-07-07 22:07:06.594244690 +0200
@@ -57,6 +57,13 @@
 	ioloop=$withval,
 	ioloop=)
 
+AC_ARG_WITH(notify,
+[  --with-notify=IOLOOP    Specify the file system notification method to use
+                          (dnotify,inotify,none;
+                           default dnotify if compilable, otherwise none)],
+	notify=$withval,
+	notify=)
+
 AC_ARG_WITH(passwd,
 [  --with-passwd           Build with /etc/passwd support (default)],
 	if test x$withval = xno; then
@@ -324,20 +331,56 @@
   ioloop="select"
 fi
 
-dnl * dnotify?
-AC_TRY_COMPILE([
-  #define _GNU_SOURCE
-  #include <fcntl.h>
-  #include <signal.h>
-  #include <unistd.h>
-], [
-  fcntl(0, F_SETSIG, SIGRTMIN);
-  fcntl(0, F_NOTIFY, DN_CREATE | DN_DELETE | DN_RENAME | DN_MULTISHOT);
-], [
-  AC_DEFINE(IOLOOP_NOTIFY_DNOTIFY,, Use Linux dnotify)
-], [
+have_notify=no
+
+if test "$notify" = "none"; then
   AC_DEFINE(IOLOOP_NOTIFY_NONE,, No special notify support)
-])
+fi
+
+if test "$notify" = "" || test "$notify" = "dnotify"; then
+  dnl * dnotify?
+  AC_TRY_COMPILE([
+    #define _GNU_SOURCE
+    #include <fcntl.h>
+    #include <signal.h>
+    #include <unistd.h>
+  ], [
+    fcntl(0, F_SETSIG, SIGRTMIN);
+    fcntl(0, F_NOTIFY, DN_CREATE | DN_DELETE | DN_RENAME | DN_MULTISHOT);
+  ], [
+    AC_DEFINE(IOLOOP_NOTIFY_DNOTIFY,, Use Linux dnotify)
+  ], [
+    if test "$notify" = "dnotify"; then
+      AC_MSG_ERROR([dnotify requested but not available])
+    fi
+    AC_DEFINE(IOLOOP_NOTIFY_NONE,, No special notify support)
+  ])
+fi
+
+if test "$notify" = "inotify"; then
+  dnl * inotify?
+  AC_TRY_COMPILE([
+    #define _GNU_SOURCE
+    #include <sys/ioctl.h>
+    #include <fcntl.h>
+    #include <linux/inotify.h>
+  ], [
+    struct inotify_watch_request req;
+    int wd, dev_fd;
+    char * fn = "/tmp";
+    dev_fd = open ("/dev/inotify", O_RDONLY);
+    req.fd = open (fn, O_RDONLY);
+    if (req.fd < 0) return -1;
+    req.mask = IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO | IN_MODIFY | IN_ACCESS;
+    wd = ioctl (dev_fd, INOTIFY_WATCH, &req);
+    close(req.fd);
+    ioctl (dev_fd, INOTIFY_IGNORE, &wd);
+  ], [
+    AC_DEFINE(IOLOOP_NOTIFY_INOTIFY,, Use Linux inotify)
+  ], [
+    AC_MSG_ERROR([inotify requested but not available, check for existence of <linux/inotify.h>])
+  ])
+fi
 
 dnl * GLIBC?
 AC_TRY_COMPILE([
@@ -1409,6 +1452,7 @@
 echo "Install prefix ...................... : $prefix"
 echo "File offsets ........................ : ${offt_bits}bit"
 echo "I/O loop method ..................... : $ioloop"
+echo "file change notification method ..... : $notify"
 echo "Building with SSL support ........... : $have_ssl"
 echo "Building with IPv6 support .......... : $want_ipv6"
 echo "Building with pop3 server ........... : $want_pop3d"
--- dovecot-1.0-test76/src/lib/ioloop-internal.h	2005-07-06 21:38:32.437987225 +0200
+++ dovecot-1.0-test76.inotify/src/lib/ioloop-internal.h	2005-07-07 21:31:11.048592621 +0200
@@ -26,6 +26,8 @@
 
 	io_callback_t *callback;
         void *context;
+
+        int notify_context;
 };
 
 struct timeout {
--- dovecot-1.0-test76/src/lib/ioloop-notify-inotify.c	1970-01-01 01:00:00.000000000 +0100
+++ dovecot-1.0-test76.inotify/src/lib/ioloop-notify-inotify.c	2005-07-07 22:51:20.420540468 +0200
@@ -0,0 +1,167 @@
+/* Copyright (C) 2005 Johannes Berg */
+
+#define _GNU_SOURCE
+#include "lib.h"
+
+#ifdef IOLOOP_NOTIFY_INOTIFY
+
+#include "ioloop-internal.h"
+#include "network.h"
+#include "imem.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/inotify.h>
+
+/* this gets opened as soon as needed, and we leak it.
+ * I don't think that matters since it will be needed
+ * any time we want notifications, and we only open
+ * it once.
+ */
+static int inotify_fd = -1;
+
+static void event_callback(void *context)
+{
+	struct ioloop *ioloop = context;
+	struct io *io;
+	int ret;
+	int required_bytes;
+	int currentbuflen = 0;
+	int record_length;
+	
+#define INITIAL_INOTIFY_BUFLEN FILENAME_MAX + 32
+#define MAXIMAL_INOTIFY_BUFLEN 32*1024
+	char static_buffer[INITIAL_INOTIFY_BUFLEN];
+	struct inotify_event * event = (struct inotify_event *) static_buffer;
+
+	while (1) {
+		if (ioctl(inotify_fd, FIONREAD, &required_bytes))
+			i_fatal("ioctl(inotify_fd, FIONREAD) failed: %m");
+			
+		if (required_bytes == 0)
+			break;
+	
+		if (required_bytes > MAXIMAL_INOTIFY_BUFLEN)
+			required_bytes = MAXIMAL_INOTIFY_BUFLEN;
+		
+		if (required_bytes > INITIAL_INOTIFY_BUFLEN) {
+			if (currentbuflen != 0) {
+				if (required_bytes > currentbuflen) {
+					event = (struct inotify_event *) i_realloc((void*)event, currentbuflen, required_bytes);
+					currentbuflen = required_bytes;
+				}
+			} else {
+				event = (struct inotify_event *) i_malloc(required_bytes);
+				currentbuflen = required_bytes;
+			}
+		}
+
+		ret = read(inotify_fd, (void *) event, required_bytes);
+
+		if (ret == 0)
+			break;
+
+		if (ret < 0)
+			i_fatal("read(inotify_fd) failed: %m");
+
+		if (gettimeofday(&ioloop_timeval, &ioloop_timezone) < 0)
+			i_fatal("gettimeofday(): %m");
+		ioloop_time = ioloop_timeval.tv_sec;
+
+		while (required_bytes > 0) {
+			for (io = ioloop->notifys; io != NULL; io = io->next) {
+				if (io->notify_context == event->wd) {
+					io->callback(io->context);
+					break;
+				}
+			}
+			record_length = event->len + sizeof(struct inotify_event);
+			required_bytes -= record_length;
+			
+			/* this might point outside the area if the loop won't run again */
+			event = (struct inotify_event *) ((char *)event + record_length);
+		}
+	}
+	
+	if (currentbuflen != 0)
+		i_free(event);
+
+#undef INITIAL_INOTIFY_BUFLEN
+#undef MAXIMAL_INOTIFY_BUFLEN
+}
+
+struct io *io_loop_notify_add(struct ioloop *ioloop, int fd,
+			      enum io_condition condition,
+			      io_callback_t *callback, void *context)
+{
+	struct io *io;
+	struct inotify_watch_request req;
+	int added = FALSE;
+	int watchdescriptor;
+
+	if ((condition & IO_FILE_NOTIFY) != 0)
+		return NULL;
+
+	if (inotify_fd < 0) {
+		inotify_fd = open("/dev/inotify", O_RDONLY);
+		if (inotify_fd < 0)
+			return NULL;
+	}
+	if (ioloop->event_io == NULL) {
+		added = TRUE;
+		ioloop->event_io =
+			io_add(inotify_fd, IO_READ, event_callback, ioloop);
+	}
+
+	/* now set up the notification request and shoot it off */
+	req.fd = fd;
+	req.mask = IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE | IN_MODIFY;
+	watchdescriptor = ioctl (inotify_fd, INOTIFY_WATCH, &req);
+	
+	if (watchdescriptor < 0) {
+		i_error("ioctl(INOTIFY_WATCH) failed: %m");
+		if (added) {
+			io_remove(ioloop->event_io);
+			ioloop->event_io = NULL;
+		}
+		return FALSE;
+	}
+
+	io = p_new(ioloop->pool, struct io, 1);
+	io->fd = fd;
+	io->condition = condition;
+
+	io->callback = callback;
+	io->context = context;
+	io->notify_context = watchdescriptor;
+
+	io->next = ioloop->notifys;
+	ioloop->notifys = io;
+	return io;
+}
+
+void io_loop_notify_remove(struct ioloop *ioloop, struct io *io)
+{
+	struct io **io_p;
+
+	for (io_p = &ioloop->notifys; *io_p != NULL; io_p = &(*io_p)->next) {
+		if (*io_p == io) {
+			*io_p = io->next;
+			break;
+		}
+	}
+
+	if (ioctl (inotify_fd, INOTIFY_IGNORE, &io->notify_context) < 0)
+		i_error("ioctl(INOTIFY_IGNORE) failed: %m");
+
+	p_free(ioloop->pool, io);
+
+	if (ioloop->notifys == NULL) {
+		io_remove(ioloop->event_io);
+		ioloop->event_io = NULL;
+	}
+}
+
+#endif
--- dovecot-1.0-test76/src/lib/Makefile.am	2005-07-06 21:38:31.365947113 +0200
+++ dovecot-1.0-test76.inotify/src/lib/Makefile.am	2005-07-07 21:16:44.472168727 +0200
@@ -29,6 +29,7 @@
 	ioloop.c \
 	ioloop-notify-none.c \
 	ioloop-notify-dn.c \
+	ioloop-notify-inotify.c \
 	ioloop-poll.c \
 	ioloop-select.c \
 	ioloop-epoll.c \
