diff options
Diffstat (limited to 'maildir')
| -rw-r--r-- | maildir/.gitignore | 4 | ||||
| -rw-r--r-- | maildir/Makefile.am | 35 | ||||
| -rw-r--r-- | maildir/configure.ac | 21 | ||||
| -rw-r--r-- | maildir/maildirmake.sgml | 1 | ||||
| -rw-r--r-- | maildir/maildirwatch.c | 672 | ||||
| -rw-r--r-- | maildir/maildirwatch.h | 50 | ||||
| -rw-r--r-- | maildir/maildirwatch.sgml | 114 | ||||
| -rw-r--r-- | maildir/maildirwatchprog.c | 201 | 
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); +} | 
