+}
+
+/*========== monitoring of input file ==========*/
+
+/*---------- tailing input file ----------*/
+
+
+
+/*---------- filemon implemented with inotify ----------*/
+
+#if defined(HAVE_INOTIFY) && !defined(HAVE_FILEMON)
+#define HAVE_FILEMON
+
+#include <linux/inotify.h>
+
+static int filemon_inotify_fd;
+static int filemon_inotify_wd= -1;
+
+static void *filemon_inotify_readable(oop_source *lp, int fd,
+ oop_event e, void *u) {
+ struct inotify_event iev;
+ for (;;) {
+ int r= read(filemon_inotify_fd, &iev, sizeof(iev));
+ if (r==-1) {
+ if (errno==EAGAIN) break;
+ sysdie("read from inotify master");
+ } else if (r==sizeof(iev)) {
+ assert(wd == filemon_inotify_wd);
+ } else {
+ die("inotify read %d bytes wanted struct of %d", r, (int)sizeof(iev));
+ }
+ }
+ /* Technically speaking the select might fire early in which case
+ * we'll read no events and then call filemon_callback
+ * unnecessarily. We don't care about that.
+ */
+ filemon_callback();
+ return OOP_CONTINUE;
+}
+
+static int filemon_init(void) {
+ filemon_inotify_fd= inotify_init();
+ if (filemon_inotify_fd<0) {
+ syswarn("could not initialise inotify: inotify_init failed");
+ return 0;
+ }
+ set nonblock;
+ loop->on_fd(loop, filemon_inotify_fd, OOP_READ, filemon_inotify_readable);
+
+ return 1;
+}
+
+static void filemon_setfile(int mainfeed_fd, const char *mainfeed_path) {
+ if (filemon_inotify_wd >= 0) {
+ int r= inotify_rm_watch(filemon_inotify_fd, filemon_inotify_wd);
+ if (r) sysdie("inotify_rm_watch");
+ }
+ filemon_inotify_wd= inotify_add_watch(filemon_inotify_fd, path, IN_MODIFY);
+ if (filemon_inotify_wd < 0) sysdie("inotify_add_watch");
+}
+
+#endif /* HAVE_INOTIFY && !HAVE_FILEMON *//
+
+/*---------- filemon dummy implementation ----------*/
+
+#if !defined(HAVE_FILEMON)
+
+static int filemon_init(void) { return 0; }
+static void filemon_setfile(int mainfeed_fd, const char *mainfeed_path) { }
+
+#endif
+
+/*========== interaction with innd ==========*/
+
+/* see state diagram at top of file */
+
+static char *path_ductlock, *path_duct, *path_ductdefer;
+static int tailing_fd= -1, flushing_fd= -1;
+
+static void statemc_init(void) {
+ path_ductlock= xasprintf("%s_duct.lock", feedfile);
+ path_duct= xasprintf("%s_duct", feedfile);
+ path_ductdefer= xasprintf("%s_duct.defer", feedfile);
+
+ int lockfd= open(path_ductlock, O_CREAT|O_RDWR, 0600);
+ if (lockfd<0) sysdie("open lockfile %s", path_ductlock);
+
+ struct flock fl;
+ memset(&fl,0,sizeof(fl));
+ fl.l_type= F_WRLCK;
+ fl.l_whence= SEEK_SET;
+ r= fcntl(lockfd, F_SETLK, &fl);
+ if (r==-1) {
+ if (errno==EACCES || errno==EAGAIN)
+ die("another duct holds the lockfile");
+ sysdie("fcntl F_SETLK lockfile %s", path_ductlock);
+ }
+}
+
+static void statemc_poll(void) {
+ if (tailing_fd>=0) return;
+
+ int d_fd= open(path_duct, O_RDWR);
+ if (d_fd<0)
+ if (errno!=ENOENT) sysdie("open duct file %s", path_duct);
+
+ int f_fd= open(feedfile, O_RDWR);
+ if (f_fd<0)
+ if (errno!=ENOENT) sysdie("open feed file %s", feedfile);
+
+ if (d_fd<0) {
+ if (f_fd>=0)
+ start_tailing(f_fd);
+ return;
+ }
+
+
+
+/*========== main program ==========*/
+
+#define EVERY(what, interval, body) \
+ static const struct timeval what##_timeout = { 5, 0 }; \
+ static void what##_schedule(void); \
+ static void *what##_timedout(oop_source *lp, struct timeval tv, void *u) { \
+ { body } \
+ what##_schedule(); \
+ } \
+ static void what##_schedule(void) { \
+ loop->on_time(loop, what##_timeout, what##_timedout, 0); \
+ }
+
+EVERY(filepoll, {5,0}, { check_master_queue(); })
+
+EVERY(period, {PERIOD_SECONDS,0}, {
+ if (connect_delay) connect_delay--;
+ statemc_poll();
+ check_master_queue();
+});
+