summaryrefslogtreecommitdiffstats
path: root/maildir
diff options
context:
space:
mode:
Diffstat (limited to 'maildir')
-rw-r--r--maildir/.gitignore4
-rw-r--r--maildir/Makefile.am35
-rw-r--r--maildir/configure.ac21
-rw-r--r--maildir/maildirmake.sgml1
-rw-r--r--maildir/maildirwatch.c672
-rw-r--r--maildir/maildirwatch.h50
-rw-r--r--maildir/maildirwatch.sgml114
-rw-r--r--maildir/maildirwatchprog.c201
8 files changed, 649 insertions, 449 deletions
diff --git a/maildir/.gitignore b/maildir/.gitignore
index 9939f4f..7fab6ba 100644
--- a/maildir/.gitignore
+++ b/maildir/.gitignore
@@ -9,7 +9,6 @@
/mailbot.h
/maildir.5
/maildir.html
-/maildir.libdeps
/maildiracl
/maildiracl.1
/maildiracl.1.in
@@ -29,6 +28,9 @@
/maildirquota.7
/maildirquota.html
/maildirsharedrc.h
+/maildirwatch
+/maildirwatch.1
+/maildirwatch.html
/quotawarnmsg.h
/sharedindexinstall
/sharedindexsplit
diff --git a/maildir/Makefile.am b/maildir/Makefile.am
index 60f0681..54c0602 100644
--- a/maildir/Makefile.am
+++ b/maildir/Makefile.am
@@ -12,6 +12,7 @@ DOCS= deliverquota.html.in deliverquota.8.in \
maildir.html maildir.5 \
maildiracl.html.in \
maildirmake.html.in maildirmake.1.in maildirquota.html maildirquota.7 \
+ maildirwatch.html maildirwatch.1 \
maildirkw.html maildirkw.1
if HAVE_SGML
@@ -23,7 +24,7 @@ BUILT_SOURCES=maildirsharedrc.h maildirfilterconfig.h quotawarnmsg.h \
endif
noinst_DATA=deliverquota.html maildirmake.html deliverquota.8 maildirmake.1 \
- maildiracl.html maildiracl.1 maildir.libdeps
+ maildiracl.html maildiracl.1
libmaildir_la_SOURCES=autoresponse.c autoresponse.h \
maildiraclt.c maildiraclt.h \
@@ -49,7 +50,7 @@ libmaildir_la_SOURCES=autoresponse.c autoresponse.h \
maildirwatch.c maildirwatch.h loginexec.c loginexec.h
noinst_PROGRAMS=deliverquota maildirmake testmaildirfilter maildirkwtest \
- maildirkw maildiracl maildiraclttest testmaildirsearch
+ maildirkw maildiracl maildiraclttest testmaildirsearch maildirwatch
deliverquota_SOURCES=deliverquota.c
deliverquota_DEPENDENCIES=libmaildir.la ../rfc822/librfc822.la \
@@ -77,24 +78,28 @@ maildirkwtest_DEPENDENCIES=libmaildir.la
maildirkwtest_LDFLAGS=-static
maildirkw_SOURCES=maildirkw.c
-maildirkw_LDADD=libmaildir.la ../liblock/liblock.la ../numlib/libnumlib.la \
- `cat maildir.libdeps`
+maildirkw_LDADD=libmaildir.la ../liblock/liblock.la ../numlib/libnumlib.la
maildirkw_DEPENDENCIES=libmaildir.la ../liblock/liblock.la \
- ../numlib/libnumlib.la maildir.libdeps
+ ../numlib/libnumlib.la
maildirkw_LDFLAGS=-static
+maildirwatch_SOURCES=maildirwatchprog.c
+maildirwatch_LDADD=libmaildir.la ../liblock/liblock.la ../numlib/libnumlib.la
+maildirwatch_DEPENDENCIES=libmaildir.la ../liblock/liblock.la \
+ ../numlib/libnumlib.la
+maildirwatch_LDFLAGS=-static
+
maildiracl=maildiracl.c
-maildiracl_LDADD=libmaildir.la ../liblock/liblock.la ../numlib/libnumlib.la \
- `cat maildir.libdeps`
+maildiracl_LDADD=libmaildir.la ../liblock/liblock.la ../numlib/libnumlib.la
maildiracl_DEPENDENCIES=libmaildir.la ../liblock/liblock.la \
- ../numlib/libnumlib.la maildir.libdeps
+ ../numlib/libnumlib.la
maildiracl_LDFLAGS=-static
maildiraclttest_SOURCES=testmaildiraclt.c
maildiraclttest_LDADD=libmaildir.la ../liblock/liblock.la \
- ../numlib/libnumlib.la `cat maildir.libdeps`
+ ../numlib/libnumlib.la
maildiraclttest_DEPENDENCIES=libmaildir.la ../liblock/liblock.la \
- ../numlib/libnumlib.la maildir.libdeps
+ ../numlib/libnumlib.la
maildiraclttest_LDFLAGS=-static
testmaildirsearch_SOURCES=testmaildirsearch.c
@@ -130,11 +135,9 @@ autoresponsequota.h: config.status
quotawarnmsg.h: config.status
echo '#define QUOTAWARNMSG "$(sysconfdir)/quotawarnmsg"' >quotawarnmsg.h
-maildir.libdeps: config.status
- echo @LIBFAM@ >maildir.libdeps
clean-local:
- rm -rf maildir.libdeps testmd
+ rm -rf testmd
check-am:
@SHELL@ $(srcdir)/testsuite 2>&1 | cmp - $(srcdir)/testsuite.txt
@@ -188,6 +191,12 @@ maildirkw.html: maildirkw.sgml ../docbook/sgml2html
maildirkw.1: maildirkw.sgml ../docbook/sgml2man
../docbook/sgml2man maildirkw.sgml maildirkw.1 "--stringparam man.base.url.for.relative.links http://www.courier-mta.org/"
+maildirwatch.html: maildirwatch.sgml ../docbook/sgml2html
+ ../docbook/sgml2html maildirwatch.sgml maildirwatch.html
+
+maildirwatch.1: maildirwatch.sgml ../docbook/sgml2man
+ ../docbook/sgml2man maildirwatch.sgml maildirwatch.1
+
endif
deliverquota.html: deliverquota.html.in
diff --git a/maildir/configure.ac b/maildir/configure.ac
index d35162a..fdbf576 100644
--- a/maildir/configure.ac
+++ b/maildir/configure.ac
@@ -74,26 +74,9 @@ AX_COURIER_UNICODE_CXXFLAGS
AC_SUBST(COURIER_UNICODE_CXXFLAGS)
dnl Checks for library functions.
-AC_CHECK_HEADER(fam.h, :, :)
AC_CHECK_FUNCS(symlink readlink strcasecmp utime utimes)
-AC_CHECK_LIB(fam, FAMOpen, [
- LIBFAM=-lfam
- AC_DEFINE_UNQUOTED(HAVE_FAM,1,
- [ Whether libfam.a is available ])
-
- AC_CHECK_HEADER(fam.h, : , [
-AC_MSG_WARN([[The development header files and libraries for fam,]])
-AC_MSG_WARN([[the File Alteration Monitor, are not installed.]])
-AC_MSG_WARN([[You appear to have the FAM runtime libraries installed,]])
-AC_MSG_WARN([[so you need to simply install the additional development]])
-AC_MSG_WARN([[package for your operating system.]])
-AC_MSG_ERROR([[FAM development libraries not found.]]) ]
- )
- ])
-
-AC_SUBST(LIBFAM)
-
-echo "$LIBFAM" >maildir.libdeps
+
+AC_CHECK_FUNCS(inotify_init inotify_init1)
AC_CACHE_CHECK([for missing gethostname prototype],maildir_cv_SYS_GETHOSTNAME,
diff --git a/maildir/maildirmake.sgml b/maildir/maildirmake.sgml
index e8f4d70..57aebd6 100644
--- a/maildir/maildirmake.sgml
+++ b/maildir/maildirmake.sgml
@@ -584,6 +584,7 @@ Updating /home/mrsam/.mailfilter</programlisting>
<ulink url="maildir.html"><citerefentry><refentrytitle>maildir</refentrytitle><manvolnum>5</manvolnum></citerefentry></ulink>,
<ulink url="maildiracl.html"><citerefentry><refentrytitle>maildiracl</refentrytitle><manvolnum>1</manvolnum></citerefentry></ulink>,
<ulink url="maildirkw.html"><citerefentry><refentrytitle>maildirkw</refentrytitle><manvolnum>1</manvolnum></citerefentry></ulink>,
+<ulink url="maildirwatch.html"><citerefentry><refentrytitle>maildirwatch</refentrytitle><manvolnum>1</manvolnum></citerefentry></ulink>,
<ulink url="maildrop.html"><citerefentry><refentrytitle>maildrop</refentrytitle><manvolnum>1</manvolnum></citerefentry></ulink>,
<ulink url="maildirquota.html"><citerefentry><refentrytitle>maildirquota</refentrytitle><manvolnum>7</manvolnum></citerefentry></ulink>,
<ulink url="deliverquota.html"><citerefentry><refentrytitle>deliverquota</refentrytitle><manvolnum>8</manvolnum></citerefentry></ulink>,
diff --git a/maildir/maildirwatch.c b/maildir/maildirwatch.c
index 36b85ab..4faac13 100644
--- a/maildir/maildirwatch.c
+++ b/maildir/maildirwatch.c
@@ -1,5 +1,5 @@
/*
-** Copyright 2002-2009 Double Precision, Inc.
+** Copyright 2002-2021 Double Precision, Inc.
** See COPYING for distribution information.
*/
@@ -14,25 +14,16 @@
#include <errno.h>
#include <signal.h>
#include <sys/signal.h>
+#if HAVE_INOTIFY_INIT
+#include <sys/inotify.h>
+#include <poll.h>
+#include <fcntl.h>
+#endif
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
-
-#if HAVE_FAM
-static struct maildirwatch_fam *maildirwatch_currentfam;
-
-static void alarm_handler(int signum)
-{
- static const char msg[]=
- "Timeout initializing the FAM library. Your FAM library is broken.\n";
-
- (void)write(2, msg, sizeof(msg)-1);
- kill(getpid(), SIGKILL);
-}
-#endif
-
struct maildirwatch *maildirwatch_alloc(const char *maildir)
{
char wd[PATH_MAX];
@@ -60,38 +51,36 @@ struct maildirwatch *maildirwatch_alloc(const char *maildir)
strcat(strcpy(w->maildir, wd), maildir);
-#if HAVE_FAM
- if (!maildirwatch_currentfam)
- {
- if ((maildirwatch_currentfam
- =malloc(sizeof(*maildirwatch_currentfam))) != NULL)
- {
- maildirwatch_currentfam->broken=0;
- maildirwatch_currentfam->refcnt=0;
+#if HAVE_INOTIFY_INIT
+#if HAVE_INOTIFY_INIT1
+#ifdef IN_CLOEXEC
+#else
+#undef HAVE_INOTIFY_INIT1
+#endif
+#ifdef IN_NONBLOCK
+#else
+#undef HAVE_INOTIFY_INIT1
+#endif
+#endif
- signal(SIGALRM, alarm_handler);
- alarm(15);
- if (FAMOpen(&maildirwatch_currentfam->fc) < 0)
- {
- errno=EIO;
- free(maildirwatch_currentfam);
- maildirwatch_currentfam=NULL;
- }
- alarm(0);
- signal(SIGALRM, SIG_DFL);
- }
- }
+#if HAVE_INOTIFY_INIT1
+ w->inotify_fd=inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
+#else
+ w->inotify_fd=inotify_init();
- if (!maildirwatch_currentfam)
+ if (w->inotify_fd >= 0 &&
+ (fcntl(w->inotify_fd, F_SETFL, O_NONBLOCK) < 0 ||
+ fcntl(w->inotify_fd, F_SETFD, FD_CLOEXEC)))
{
- free(w->maildir);
- free(w);
- w=NULL;
+ close(w->inotify_fd);
+ w->inotify_fd=-1;
}
- else
+#endif
+
+ if (w->inotify_fd < 0)
{
- w->fam=maildirwatch_currentfam;
- ++w->fam->refcnt;
+ maildirwatch_free(w);
+ w=NULL;
}
#endif
return w;
@@ -99,25 +88,10 @@ struct maildirwatch *maildirwatch_alloc(const char *maildir)
void maildirwatch_free(struct maildirwatch *w)
{
-#if HAVE_FAM
- if (--w->fam->refcnt == 0)
+#if HAVE_INOTIFY_INIT
+ if (w->inotify_fd >= 0)
{
- w->fam->broken=1;
- if (maildirwatch_currentfam &&
- maildirwatch_currentfam->broken)
- {
- /*
- ** Last reference to the current FAM connection,
- ** keep it active.
- */
-
- w->fam->broken=0;
- }
- else /* Some other connection, with no more refs */
- {
- FAMClose(&w->fam->fc);
- free(w->fam);
- }
+ close(w->inotify_fd);
}
#endif
@@ -127,125 +101,128 @@ void maildirwatch_free(struct maildirwatch *w)
void maildirwatch_cleanup()
{
-#if HAVE_FAM
-
- if (maildirwatch_currentfam && maildirwatch_currentfam->refcnt == 0)
- {
- FAMClose(&maildirwatch_currentfam->fc);
- free(maildirwatch_currentfam);
- maildirwatch_currentfam=NULL;
- }
-#endif
}
-#if HAVE_FAM
-static void maildirwatch_fambroken(struct maildirwatch *w)
-{
- w->fam->broken=1;
-
- if (maildirwatch_currentfam && maildirwatch_currentfam->broken)
- maildirwatch_currentfam=NULL;
- /* Broke the current connection, create another one, next time. */
-
-}
+#if HAVE_INOTIFY_INIT
/*
-** If the current connection is marked as broken, try to reconnect.
+** Poll the inotify file descriptor. Returns 0 on timeout, non-0 if
+** the inotify file descriptor becomes ready before the timeout expires.
*/
-static void maildirwatch_famunbreak(struct maildirwatch *w)
+static int poll_inotify(struct maildirwatch *w)
{
- struct maildirwatch *cpy;
-
- if (!w->fam->broken)
- return;
+ time_t now2;
- if ((cpy=maildirwatch_alloc(w->maildir)) == NULL)
- return;
+ int rc;
- /*
- ** maildirwatch_alloc succeeds only with a good connection.
- ** If this is the last reference to the broken connection, close it.
- */
+ struct pollfd pfd;
- if (--w->fam->refcnt == 0)
+ while (w->now < w->timeout)
{
- FAMClose(&w->fam->fc);
- free(w->fam);
- }
+ pfd.fd=w->inotify_fd;
+ pfd.events=POLLIN;
- w->fam=cpy->fam;
- ++w->fam->refcnt;
+ rc=poll(&pfd, 1, (w->timeout - w->now)*1000);
- maildirwatch_free(cpy);
-}
+ now2=time(NULL);
-static int waitEvent(struct maildirwatch *w)
-{
- int fd;
- fd_set r;
- struct timeval tv;
- time_t now2;
+ if (now2 < w->now)
+ return 1; /* System clock changed */
- int rc;
+ w->now=now2;
- while ((rc=FAMPending(&w->fam->fc)) == 0)
- {
- if (w->now >= w->timeout)
- return 0;
+ if (rc && pfd.revents & POLLIN)
+ return 1;
+ }
- fd=FAMCONNECTION_GETFD(&w->fam->fc);
+ return 0;
+}
- FD_ZERO(&r);
- FD_SET(fd, &r);
+/*
+** read() inotify_events from the inotify handler.
+*/
- tv.tv_sec= w->timeout - w->now;
- tv.tv_usec=0;
+static int read_inotify_events(int fd,
+ void (*callback)(struct inotify_event *ie,
+ void *arg),
+ void *arg)
+{
+ char inotify_buffer[sizeof(struct inotify_event)+NAME_MAX+1];
+ int l;
+ char *iecp;
- select(fd+1, &r, NULL, NULL, &tv);
- now2=time(NULL);
+ l=read(fd, inotify_buffer, sizeof(inotify_buffer));
- if (now2 < w->now)
- return 0; /* System clock changed */
+ if (l < 0 &&
+ (errno == EAGAIN || errno == EWOULDBLOCK))
+ l=0; /* Non-blocking socket timeout */
- w->now=now2;
+ if (l < 0)
+ {
+ fprintf(stderr, "ERR:inotify read: %s\n", strerror(errno));
+ return -1;
}
- return rc;
+ iecp=inotify_buffer;
+
+ while (iecp < inotify_buffer+l)
+ {
+ struct inotify_event *ie=
+ (struct inotify_event *)iecp;
+
+ iecp += sizeof(struct inotify_event)+ie->len;
+
+ (*callback)(ie, arg);
+ }
+ return 0;
}
-#endif
+struct unlock_info {
+ int handle;
+ int removed;
+ int deleted;
+};
-int maildirwatch_unlock(struct maildirwatch *w, int nseconds)
+static void unlock_handler(struct inotify_event *ie,
+ void *arg)
{
-#if HAVE_FAM
- FAMRequest fr;
- FAMEvent fe;
- int cancelled=0;
- char *p;
+ struct unlock_info *ui=(struct unlock_info *)arg;
- if (w->fam->broken)
+ if (ie->wd == ui->handle)
{
- errno=EIO;
- return -1;
+ if (ie->mask & IN_DELETE_SELF)
+ ui->removed=1;
+
+ if (ie->mask & IN_IGNORED)
+ ui->deleted=1;
}
+}
+#endif
- p=malloc(strlen(w->maildir)+ sizeof("/" WATCHDOTLOCK));
+static int do_maildirwatch_unlock(struct maildirwatch *w, int nseconds,
+ const char *p)
+{
+#if HAVE_INOTIFY_INIT
+ int cancelled=0;
- if (!p)
- return -1;
+ struct unlock_info ui;
- strcat(strcpy(p, w->maildir), "/" WATCHDOTLOCK);
+ ui.handle=inotify_add_watch(w->inotify_fd, p, IN_DELETE_SELF);
+ ui.removed=0;
+ ui.deleted=0;
- errno=EIO;
- if (FAMMonitorFile(&w->fam->fc, p, &fr, NULL) < 0)
+ if (ui.handle < 0)
{
- free(p);
- fprintf(stderr, "ERR:FAMMonitorFile: %s\n",
- strerror(errno));
+ if (errno == ENOENT)
+ {
+ /* Doesn't exist anymore, that's ok */
+ return 0;
+ }
+
+ fprintf(stderr, "ERR: %s: %s\n", p, strerror(errno));
return -1;
}
- free(p);
if (nseconds < 0)
nseconds=0;
@@ -254,69 +231,74 @@ int maildirwatch_unlock(struct maildirwatch *w, int nseconds)
w->timeout=w->now + nseconds;
- for (;;)
+ do
{
- if (waitEvent(w) != 1)
- {
- errno=EIO;
+ errno=ETIMEDOUT;
- if (!cancelled && FAMCancelMonitor(&w->fam->fc, &fr) == 0)
+ if (!poll_inotify(w))
+ {
+ if (!cancelled)
{
+ /*
+ ** Timeout on the lock, cancel the inotify.
+ */
w->timeout=w->now+15;
cancelled=1;
+ inotify_rm_watch(w->inotify_fd, ui.handle);
continue;
}
- if (!cancelled)
- fprintf(stderr, "ERR:FAMCancelMonitor: %s\n",
- strerror(errno));
+ fprintf(stderr, "ERR:inotify timeout: %s\n",
+ strerror(errno));
- maildirwatch_fambroken(w);
break;
}
- errno=EIO;
+ read_inotify_events(w->inotify_fd, unlock_handler, &ui);
- if (FAMNextEvent(&w->fam->fc, &fe) != 1)
+ if (ui.removed && !cancelled)
{
- fprintf(stderr, "ERR:FAMNextEvent: %s\n",
- strerror(errno));
- maildirwatch_fambroken(w);
- break;
+ w->timeout=w->now+15;
+ cancelled=1;
+ inotify_rm_watch(w->inotify_fd, ui.handle);
}
- if (fe.fr.reqnum != fr.reqnum)
- continue;
+ /* We don't terminate the loop until we get IN_IGNORED */
- if (fe.code == FAMDeleted && !cancelled)
- {
- errno=EIO;
- if (FAMCancelMonitor(&w->fam->fc, &fr) == 0)
- {
- w->timeout=w->now+15;
- cancelled=1;
- continue;
- }
- fprintf(stderr, "ERR:FAMCancelMonitor: %s\n",
- strerror(errno));
- maildirwatch_fambroken(w);
- break;
- }
+ } while (!ui.deleted);
- if (fe.code == FAMAcknowledge)
- break;
- }
+ return ui.removed;
+#else
- if (w->fam->broken)
- return -1;
+ int n;
+ for (n=0; n<nseconds; ++n)
+ {
+ if (access(p, 0))
+ return 1;
+ sleep(1);
+ }
return 0;
-#else
- return -1;
#endif
}
-#define DIRCNT 3
+int maildirwatch_unlock(struct maildirwatch *w, int nseconds)
+{
+ char *p;
+ int rc;
+
+ p=malloc(strlen(w->maildir)+ sizeof("/" WATCHDOTLOCK));
+
+ if (!p)
+ return -1;
+
+ strcat(strcpy(p, w->maildir), "/" WATCHDOTLOCK);
+
+ rc=do_maildirwatch_unlock(w, nseconds, p);
+
+ free(p);
+ return rc;
+}
int maildirwatch_start(struct maildirwatch *w,
struct maildirwatch_contents *mc)
@@ -326,15 +308,7 @@ int maildirwatch_start(struct maildirwatch *w,
time(&w->now);
w->timeout = w->now + 60;
-#if HAVE_FAM
-
- maildirwatch_famunbreak(w);
-
- if (w->fam->broken)
- {
- errno=EIO;
- return (1);
- }
+#if HAVE_INOTIFY_INIT
{
char *s=malloc(strlen(w->maildir)
@@ -345,153 +319,58 @@ int maildirwatch_start(struct maildirwatch *w,
strcat(strcpy(s, w->maildir), "/new");
- mc->endexists_received=0;
- mc->ack_received=0;
- mc->cancelled=0;
-
- errno=EIO;
+ mc->handles[0]=inotify_add_watch(w->inotify_fd, s,
+ IN_CREATE |
+ IN_DELETE |
+ IN_MOVED_FROM |
+ IN_MOVED_TO);
- if (FAMMonitorDirectory(&w->fam->fc, s, &mc->new_req, NULL) < 0)
- {
- fprintf(stderr, "ERR:"
- "FAMMonitorDirectory(%s) failed: %s\n",
- s, strerror(errno));
- free(s);
- errno=EIO;
- return (-1);
- }
strcat(strcpy(s, w->maildir), "/cur");
- errno=EIO;
-
- if (FAMMonitorDirectory(&w->fam->fc, s, &mc->cur_req, NULL) < 0)
- {
- fprintf(stderr, "ERR:"
- "FAMMonitorDirectory(%s) failed: %s\n",
- s, strerror(errno));
- errno=EIO;
-
- if (FAMCancelMonitor(&mc->w->fam->fc, &mc->new_req) < 0)
- {
- free(s);
- maildirwatch_fambroken(w);
- fprintf(stderr, "ERR:FAMCancelMonitor: %s\n",
- strerror(errno));
- errno=EIO;
- return (-1);
- }
- mc->cancelled=1;
- mc->ack_received=2;
- }
+ mc->handles[1]=inotify_add_watch(w->inotify_fd, s,
+ IN_CREATE |
+ IN_DELETE |
+ IN_MOVED_FROM |
+ IN_MOVED_TO);
strcat(strcpy(s, w->maildir), "/" KEYWORDDIR);
- errno=EIO;
-
- if (FAMMonitorDirectory(&w->fam->fc, s,
- &mc->courierimapkeywords_req, NULL)<0)
- {
- fprintf(stderr, "ERR:"
- "FAMMonitorDirectory(%s) failed: %s\n",
- s, strerror(errno));
-
- errno=EIO;
-
- if (FAMCancelMonitor(&mc->w->fam->fc, &mc->new_req)<0)
- {
- free(s);
- maildirwatch_fambroken(w);
- fprintf(stderr, "ERR:FAMCancelMonitor: %s\n",
- strerror(errno));
- errno=EIO;
- return (-1);
- }
-
- errno=EIO;
-
- if (FAMCancelMonitor(&mc->w->fam->fc, &mc->cur_req)<0)
- {
- free(s);
- maildirwatch_fambroken(w);
- fprintf(stderr, "ERR:FAMCancelMonitor: %s\n",
- strerror(errno));
- errno=EIO;
- return (-1);
- }
-
- mc->cancelled=1;
- mc->ack_received=1;
- }
+ mc->handles[2]=inotify_add_watch(w->inotify_fd, s,
+ IN_CREATE |
+ IN_DELETE |
+ IN_MOVED_FROM |
+ IN_MOVED_TO);
free(s);
}
+
return 0;
#else
return 1;
#endif
}
-#define CANCEL(ww) \
- errno=EIO; if (FAMCancelMonitor(&w->fam->fc, \
- &ww->new_req) || \
- FAMCancelMonitor(&w->fam->fc, \
- &ww->cur_req) || \
- FAMCancelMonitor(&w->fam->fc, \
- &ww->courierimapkeywords_req)) \
- {\
- maildirwatch_fambroken(w); \
- fprintf(stderr, \
- "ERR:FAMCancelMonitor: %s\n", \
- strerror(errno)); \
- return (-1); \
- }
-
int maildirwatch_started(struct maildirwatch_contents *mc,
int *fdret)
{
-#if HAVE_FAM
- struct maildirwatch *w=mc->w;
+#if HAVE_INOTIFY_INIT
+ int n;
+#endif
- if (w->fam->broken)
- return (1);
+ *fdret= -1;
- *fdret=FAMCONNECTION_GETFD(&w->fam->fc);
+#if HAVE_INOTIFY_INIT
- while (FAMPending(&w->fam->fc))
+ for (n=0; n<sizeof(mc->handles)/sizeof(mc->handles[0]); ++n)
{
- FAMEvent fe;
-
- errno=EIO;
+ if (mc->handles[n] < 0)
+ return -1;
+ }
- if (FAMNextEvent(&w->fam->fc, &fe) != 1)
- {
- fprintf(stderr, "ERR:FAMNextEvent: %s\n",
- strerror(errno));
- maildirwatch_fambroken(w);
- return (-1);
- }
+ *fdret=mc->w->inotify_fd;
- switch (fe.code) {
- case FAMDeleted:
- if (!mc->cancelled)
- {
- mc->cancelled=1;
- CANCEL(mc);
- }
- break;
- case FAMAcknowledge:
- if (++mc->ack_received >= DIRCNT)
- return -1;
- break;
- case FAMEndExist:
- ++mc->endexists_received;
- break;
- default:
- break;
- }
- }
+ return 1;
- return (mc->endexists_received >= DIRCNT && mc->ack_received == 0);
#else
*fdret= -1;
@@ -499,6 +378,31 @@ int maildirwatch_started(struct maildirwatch_contents *mc,
#endif
}
+#if HAVE_INOTIFY_INIT
+
+struct check_info {
+ struct maildirwatch_contents *mc;
+ int *changed;
+ int handled;
+};
+
+static void check_handler(struct inotify_event *ie,
+ void *arg)
+{
+ struct check_info *ci=(struct check_info *)arg;
+ int n;
+
+ ci->handled=1;
+
+ for (n=0; n<sizeof(ci->mc->handles)/sizeof(ci->mc->handles[0]); ++n)
+ {
+ if (ie->wd == ci->mc->handles[n])
+ *ci->changed=1;
+ }
+
+}
+#endif
+
int maildirwatch_check(struct maildirwatch_contents *mc,
int *changed,
int *fdret,
@@ -506,6 +410,12 @@ int maildirwatch_check(struct maildirwatch_contents *mc,
{
struct maildirwatch *w=mc->w;
time_t curTime;
+#if HAVE_INOTIFY_INIT
+ struct check_info ci;
+
+ ci.mc=mc;
+ ci.changed=changed;
+#endif
*changed=0;
*fdret=-1;
@@ -516,100 +426,104 @@ int maildirwatch_check(struct maildirwatch_contents *mc,
w->timeout=curTime; /* System clock changed */
w->now=curTime;
-#if HAVE_FAM
+ *timeout=60;
- if (!w->fam->broken)
+#if HAVE_INOTIFY_INIT
+ if (maildirwatch_started(mc, fdret) > 0)
{
- *fdret=FAMCONNECTION_GETFD(&w->fam->fc);
-
- while (FAMPending(&w->fam->fc))
- {
- FAMEvent fe;
+ *timeout=60 * 60;
- errno=EIO;
+ *fdret=w->inotify_fd;
- if (FAMNextEvent(&w->fam->fc, &fe) != 1)
- {
- fprintf(stderr, "ERR:FAMNextEvent: %s\n",
- strerror(errno));
- maildirwatch_fambroken(w);
- return (-1);
- }
+ ci.handled=1;
- switch (fe.code) {
- case FAMDeleted:
- case FAMCreated:
- case FAMMoved:
- if (!mc->cancelled)
- {
- mc->cancelled=1;
- CANCEL(mc);
- }
- break;
- case FAMAcknowledge:
- ++mc->ack_received;
- default:
- break;
- }
+ while (ci.handled)
+ {
+ ci.handled=0;
+ read_inotify_events(w->inotify_fd, check_handler, &ci);
}
-
- *changed=mc->ack_received >= DIRCNT;
- *timeout=60 * 60;
- return 0;
}
#endif
- *timeout=60;
-
- if ( (*changed= w->now >= w->timeout) != 0)
- w->timeout = w->now + 60;
+ if (w->now >= w->timeout)
+ {
+ w->timeout = w->now + *timeout;
+ *changed=1;
+ }
return 0;
}
-void maildirwatch_end(struct maildirwatch_contents *mc)
+#if HAVE_INOTIFY_INIT
+
+struct end_info {
+ struct maildirwatch_contents *mc;
+ int unwatched;
+};
+
+static void end_handler(struct inotify_event *ie,
+ void *arg)
{
-#if HAVE_FAM
- struct maildirwatch *w=mc->w;
+ struct end_info *ei=(struct end_info *)arg;
+ int n;
- if (!w->fam->broken)
+ for (n=0; n<sizeof(ei->mc->handles)/sizeof(ei->mc->handles[0]); ++n)
{
- if (!mc->cancelled)
+ if (ie->wd == ei->mc->handles[n] &&
+ ie->mask & IN_IGNORED)
{
- mc->cancelled=1;
-
-#define return(x)
- CANCEL(mc);
-#undef return
+ ei->mc->handles[n]=-1;
+ ei->unwatched=1;
}
}
+}
+
+static int maildir_end_unwatch(struct maildirwatch_contents *mc)
+{
+ int n;
- while (!w->fam->broken && mc->ack_received < DIRCNT)
+ for (n=0; n<sizeof(mc->handles)/sizeof(mc->handles[0]); ++n)
{
- FAMEvent fe;
+ if (mc->handles[n] >= 0)
+ {
+ inotify_rm_watch(mc->w->inotify_fd,
+ mc->handles[n]);
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif
- time(&w->now);
- w->timeout=w->now + 15;
+void maildirwatch_end(struct maildirwatch_contents *mc)
+{
+#if HAVE_INOTIFY_INIT
+ struct end_info ei;
- errno=EIO;
+ time(&mc->w->now);
+ mc->w->timeout=mc->w->now + 15;
- if (waitEvent(w) != 1)
+ if (maildir_end_unwatch(mc)) /* Send the first inotify_rm_watch */
+ {
+ while (1)
{
- fprintf(stderr, "ERR:FAMPending: timeout\n");
- maildirwatch_fambroken(w);
- break;
- }
+ if (poll_inotify(mc->w) != 1)
+ {
+ fprintf(stderr, "ERR:inotify timeout: %s\n",
+ strerror(errno));
+ break;
+ }
- errno=EIO;
+ ei.mc=mc;
+ ei.unwatched=0;
+ read_inotify_events(mc->w->inotify_fd,
+ end_handler, &ei);
- if (FAMNextEvent(&w->fam->fc, &fe) != 1)
- {
- fprintf(stderr, "ERR:FAMNextEvent: %s\n",
- strerror(errno));
- maildirwatch_fambroken(w);
- break;
+ if (ei.unwatched)
+ {
+ /* Send the next inotify_rm_watch? */
+ if (!maildir_end_unwatch(mc))
+ break; /* Nope, all done! */
+ }
}
-
- if (fe.code == FAMAcknowledge)
- ++mc->ack_received;
}
#endif
}
diff --git a/maildir/maildirwatch.h b/maildir/maildirwatch.h
index 330baac..5f49623 100644
--- a/maildir/maildirwatch.h
+++ b/maildir/maildirwatch.h
@@ -1,7 +1,7 @@
#ifndef maildirwatch_h
#define maildirwatch_h
/*
-** Copyright 2002 Double Precision, Inc.
+** Copyright 2002-2021 Double Precision, Inc.
** See COPYING for distribution information.
*/
@@ -15,16 +15,9 @@ extern "C" {
#endif
/*
-** These function leverage libfam.a to watch for maildir changes.
-**
-** If libfam.a is not available, these functions are compiled to no-ops
+** These function use inotify to watch for maildir changes.
*/
-#if HAVE_FAM
-#include <fam.h>
-#endif
-
-
#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
@@ -36,20 +29,11 @@ extern "C" {
#endif
#endif
-#if HAVE_FAM
-struct maildirwatch_fam {
- FAMConnection fc;
- int broken;
- unsigned refcnt;
-};
-
-#endif
-
struct maildirwatch {
char *maildir;
-#if HAVE_FAM
- struct maildirwatch_fam *fam;
+#if HAVE_INOTIFY_INIT
+ int inotify_fd;
#endif
time_t now;
time_t timeout;
@@ -79,27 +63,16 @@ int maildirwatch_unlock(struct maildirwatch *w, int nseconds);
struct maildirwatch_contents {
struct maildirwatch *w;
-#if HAVE_FAM
- FAMRequest new_req;
- FAMRequest cur_req;
- FAMRequest courierimapkeywords_req;
-
- unsigned short endexists_received;
- unsigned short ack_received;
-
- unsigned short cancelled;
-
+#if HAVE_INOTIFY_INIT
+ int handles[3];
#endif
-
};
/*
** maildirwatch_start() initiates the process of monitoring the maildir.
-** The monitoring process does not get started right away, since FAM needs
-** to acknowledge th monitoring requests first.
**
-** Returns: 0 - monitoring request sent.
-** 1 - FAM is not available, will fall back to 60 second polls.
+** Returns: 0 - monitoring started.
+** 1 - inotify not available, will fall back to 60 second polls.
** -1 - Fatal error.
*/
@@ -107,10 +80,11 @@ int maildirwatch_start(struct maildirwatch *p,
struct maildirwatch_contents *w);
/*
-** Check if FAM started monitoring yet.
+** Check the status of inotify monitoring.
**
** Returns: 1 - Monitoring has started, or we're in fallback mode.
** 0 - Not yet, *fdret is initialized to file descriptor to wait on.
+** (not used at this time).
** -1 - A fatal error occured, fall back to polling mode.
**
** maildirwatch_started() returns right away, without blocking.
@@ -127,7 +101,9 @@ int maildirwatch_started(struct maildirwatch_contents *w,
** -1 - Fatal error.
**
** *fdret and *timeout get initialized to the file descriptor to wait on,
-** and the requested timeout. *fdret may be negative in polling mode.
+** and the requested timeout. *fdret may be negative in polling mode, this
+** should be interpreted as: if *changed is not set, sleep for this period of
+** time.
*/
int maildirwatch_check(struct maildirwatch_contents *w,
diff --git a/maildir/maildirwatch.sgml b/maildir/maildirwatch.sgml
new file mode 100644
index 0000000..3787434
--- /dev/null
+++ b/maildir/maildirwatch.sgml
@@ -0,0 +1,114 @@
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<!-- Copyright 2021 Double Precision, Inc. See COPYING for -->
+<!-- distribution information. -->
+<refentry id="maildirwatch">
+ <info><author><firstname>Sam</firstname><surname>Varshavchik</surname><contrib>Author</contrib></author><productname>Courier Mail Server</productname></info>
+
+ <refmeta>
+ <refentrytitle>maildirwatch</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="manual">Double Precision, Inc.</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>maildirwatch</refname>
+ <refpurpose>wait for changes to a maildir</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis sepchar=" ">
+ <command>maildirwatch</command>
+ <arg choice="req" rep="norepeat"><replaceable>maildir</replaceable></arg>
+ <arg choice="req" rep="norepeat"><replaceable>command</replaceable></arg>
+
+ <arg choice="opt" rep="repeat"><replaceable>argument</replaceable></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="maildirwatch_description">
+ <title>DESCRIPTION</title>
+
+ <para>
+ <command>maildirwatch</command> repeatedly invokes the
+ <command><replaceable>command</replaceable></command>, with any optional
+ <command><replaceable>argument</replaceable></command>s, an external
+ command, as follows:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ The first time the
+ <command><replaceable>command</replaceable></command> gets executed
+ is immediately after
+ <command>maildirwatch</command> starts.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Subsequent invocatons of
+ <command><replaceable>command</replaceable></command> occur whenever
+ there <emphasis>may</emphasis> be some kind of a change
+ to the contents of the
+ <filename><replaceable>maildir</replaceable></filename>.
+ </para>
+
+ <para>
+ <command>maildirwatch</command> does not determine what, if anything,
+ changed in the
+ <filename><replaceable>maildir</replaceable></filename>.
+ <command>maildirwatch</command>
+ might
+ occasionally raise a false alarm and run the
+ <command><replaceable>command</replaceable></command> even though
+ nothing changed, but that's rare. It's also possible that by
+ the time <command><replaceable>command</replaceable></command>
+ actually runs, then whatever was changed in the
+ <filename><replaceable>maildir</replaceable></filename> is no longer
+ changed; it became what it was before it was changed (a mystery
+ that will remain unsolved forever).
+ It's up to the
+ <command><replaceable>command</replaceable></command> to
+ intelligently figure out
+ if it needs to do something about whatever it finds in the
+ <filename><replaceable>maildir</replaceable></filename>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <command>maildirwatch</command> is a rudimentary mechanism for writing
+ shell scripts that deal with newly-delivered mail, in
+ some form or fashion.
+ </para>
+ </refsect1>
+
+ <refsect1 id="maildirwatch_exit">
+ <title>EXIT CODE</title>
+
+ <para>
+ A non-zero exit code indicates that the specified
+ <filename><replaceable>maildir</replaceable></filename> does not
+ exist or is corrupted.
+ </para>
+
+ <para>
+ Otherwise
+ <command>maildirwatch</command> runs until the
+ <command><replaceable>command</replaceable></command> exits with a
+ non-zero exit code.
+ <command>maildirwatch</command> prints
+ <command><replaceable>command</replaceable></command>'s exit code on
+ standard output, and terminates with an exit code of 0.
+ </para>
+ </refsect1>
+
+ <refsect1 id="maildirwatch_see_also">
+ <title>SEE ALSO</title>
+
+ <para>
+<ulink url="maildirmake.html"><citerefentry><refentrytitle>maildirmake</refentrytitle><manvolnum>1</manvolnum></citerefentry></ulink>.</para>
+ </refsect1>
+
+</refentry>
diff --git a/maildir/maildirwatchprog.c b/maildir/maildirwatchprog.c
new file mode 100644
index 0000000..f169da8
--- /dev/null
+++ b/maildir/maildirwatchprog.c
@@ -0,0 +1,201 @@
+/*
+** Copyright 2021 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <poll.h>
+#include "maildirwatch.h"
+
+static void usage()
+{
+ printf("Usage: maildirwatch maildir program arguments...\n");
+ exit(1);
+}
+
+static int forkexec(int argc, char **argv)
+{
+ pid_t p=fork();
+ int s;
+
+ if (p < 0)
+ {
+ perror("fork");
+ return 0;
+ }
+
+ if (p == 0)
+ {
+ char **argvptr=malloc(sizeof(char *)*(argc+1));
+ int n;
+
+ if (!argvptr)
+ {
+ perror("malloc");
+ exit(1);
+ }
+
+ for (n=0; n<argc; ++n)
+ argvptr[n]=argv[n];
+ argvptr[n]=0;
+ execvp(argvptr[0], argvptr);
+ perror(argv[0]);
+ exit(1);
+ }
+
+ if (waitpid(p, &s, 0) != p)
+ return 0;
+
+ if (!WIFEXITED(s))
+ {
+ if (WIFSIGNALED(s))
+ printf("-%d\n", (int)WTERMSIG(s));
+ else
+ printf("-0\n");
+ return 0;
+ }
+
+ if (WEXITSTATUS(s))
+ {
+ printf("%d\n", (int)WEXITSTATUS(s));
+ return 0;
+ }
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct maildirwatch *watch;
+ struct maildirwatch_contents mc;
+ int fdret;
+ int rc;
+
+ if (argc < 3)
+ usage();
+
+ /*
+ ** Call maildirwatch_alloc, then maildirwatch_start.
+ */
+
+ watch=maildirwatch_alloc(argv[1]);
+
+ if (maildirwatch_start(watch, &mc) < 0)
+ {
+ perror(argv[1]);
+ exit(1);
+ }
+
+ /*
+ ** poll() the returned file descriptor as long as
+ ** maildirwatch_started returns 0.
+ */
+
+ while ((rc=maildirwatch_started(&mc, &fdret)) == 0)
+ {
+ struct pollfd pfd;
+
+ pfd.fd=fdret;
+ pfd.events=POLLIN;
+
+ if (poll(&pfd, 1, -1) < 0)
+ {
+ perror(argv[1]);
+ exit(1);
+ }
+ }
+
+ if (rc < 0)
+ {
+ perror(argv[1]);
+ exit(1);
+ }
+
+ /*
+ ** The maildir is now being monitored.
+ */
+
+ while (forkexec(argc-2, argv+2))
+ {
+ int was_changed=0;
+
+ while (1)
+ {
+ int changed;
+ int timeout;
+
+ /*
+ ** maildirwatch_check() checks if the maildir has
+ ** been changed.
+ */
+ int rc=maildirwatch_check(&mc, &changed, &fdret,
+ &timeout);
+
+
+ if (rc < 0)
+ {
+ perror(argv[1]);
+ exit(1);
+ }
+
+ /*
+ ** If it's changed, you can call it again.
+ ** maildirwatch_check() does not block.
+ */
+ if (changed)
+ {
+ was_changed=1;
+ continue;
+ }
+
+ if (was_changed)
+ break;
+
+ /*
+ ** If it's not changed, poll() for the number of
+ ** seconds specified by the timeout.
+ **
+ ** In polling mode fdret is 0, so sleep, then
+ ** check the maildir manually.
+ */
+
+ if (fdret < 0)
+ {
+ sleep(timeout);
+ continue;
+ }
+
+ struct pollfd pfd;
+
+ pfd.fd=fdret;
+ pfd.events=POLLIN;
+
+ if (poll(&pfd, 1, timeout * 1000) < 0)
+ {
+ perror("poll");
+ exit(1);
+ }
+ }
+ }
+
+ /*
+ ** Cleanup.
+ */
+
+ maildirwatch_end(&mc);
+ maildirwatch_free(watch);
+ return (0);
+}