summaryrefslogtreecommitdiffstats
path: root/maildir/maildirwatch.c
diff options
context:
space:
mode:
Diffstat (limited to 'maildir/maildirwatch.c')
-rw-r--r--maildir/maildirwatch.c672
1 files changed, 293 insertions, 379 deletions
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
}