summaryrefslogtreecommitdiffabout
path: root/src/watcher.c
authorSergey Poznyakoff <gray@gnu.org.ua>2012-12-22 19:39:23 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2012-12-22 19:54:43 (GMT)
commit67a64a6a7809c183516740696e15ab88f82d7ef0 (patch) (side-by-side diff)
treeb58f30a8cee98bac7c9f4bc8236c86eefdadb4ef /src/watcher.c
parent2c28190e77ce38b6437a2be8337680f77f58d37a (diff)
downloadwydawca-67a64a6a7809c183516740696e15ab88f82d7ef0.tar.gz
wydawca-67a64a6a7809c183516740696e15ab88f82d7ef0.tar.bz2
Implement inotify support.
* configure.ac: New option --with-inotify. Bye default, use inotify if it is present. * src/watcher.c: New file. Implements inotify watcher. * src/Makefile.am [COND_INOTIFY] (wydawca_SOURCES): Add watcher.c * src/diskio.c (dir_get_path): New function. * src/job.c (job) <spool>: Remove const qualifier. All uses changed. (inotify_spool): New pseudo-spool. (fake_spool): Remove static qualifier. (wydawca_scanner): Support for inotify spools. * src/net.c (open_listener): Don't exit if the listener address is not set. (wydawca_listener): Listen on the listener socket and on the inotify descriptor. If none is set, bail out. * src/process.c (for_each_spool) (file_info_cleanup) (spool_cwd_add_new_file,spool_add_new_file): New functions. (scan_spool_unlocked): Use spool_cwd_add_new_file. Don't initialize dictionaries here: it will be done in spool_commit_triplets. (spool_open_dictionaries): New function. (close_dictionaries): Rename to spool_close_dictionaries. Clear dict_inited. * src/triplet.c (hash_triplet_compare): Compare spools as well. (register_file): Likewise. (triplet_lookup): New function. (check_triplet_state): New argument: noauth. All uses updated. (enumerate_triplets): Rename to spool_commit_triplets. Call spool_open_dictionaries. (count_processable_triplets,triplet_remove_file): New functions. * src/verify.c (verify_directive_file): New argument: noauth. All uses updated. * src/vtab.c (reg): Initialize get_path member. (get_path): New function. * src/wydawca.c (main): Set print_version_hook. * src/wydawca.h (virt_tab) <get_path>: New method. (spool) <dict_inited>: New member. (fake_spool, inotify_spool): New externs. (spool_add_new_file, spool_cwd_add_new_file) (spool_open_dictionaries, spool_close_dictionaries) (for_each_spool, count_processable_triplets) (triplet_remove_file, get_path): New protos. (enumerate_triplets): Rename to spool_commit_triplets. (verify_directive_file): Take two arguments.
Diffstat (limited to 'src/watcher.c') (more/less context) (ignore whitespace changes)
-rw-r--r--src/watcher.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/src/watcher.c b/src/watcher.c
new file mode 100644
index 0000000..f8761ee
--- a/dev/null
+++ b/src/watcher.c
@@ -0,0 +1,243 @@
+/* wydawca - automatic release submission daemon
+ Copyright (C) 2007, 2009-2012 Sergey Poznyakoff
+
+ Wydawca is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ Wydawca is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with wydawca. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "wydawca.h"
+#include <sys/inotify.h>
+#include <sys/ioctl.h>
+
+/* A directory watcher is described by the following structure */
+struct dirwatcher
+{
+ struct dirwatcher *next, *prev;
+ struct dirwatcher *parent; /* Points to the parent watcher.
+ NULL for top-level watchers */
+ struct spool *spool;
+ int wd; /* Watch descriptor */
+};
+
+static struct dirwatcher *dirwatcher_list;
+
+struct dirwatcher *
+dirwatcher_unlink(struct dirwatcher **root, struct dirwatcher *p)
+{
+ if (p->prev)
+ p->prev->next = p->next;
+ else
+ *root = p->next;
+ if (p->next)
+ p->next->prev = p->prev;
+ p->next = p->prev = NULL;
+ return p;
+}
+
+struct dirwatcher *
+dirwatcher_pop(struct dirwatcher **pp)
+{
+ if (*pp)
+ return dirwatcher_unlink(pp, *pp);
+ return NULL;
+}
+
+static void
+dirwatcher_push(struct dirwatcher **pp, struct dirwatcher *p)
+{
+ p->prev = NULL;
+ p->next = *pp;
+ if (*pp)
+ (*pp)->prev = p;
+ *pp = p;
+}
+
+/* Find a watcher with the given descriptor */
+static struct dirwatcher *
+dirwatcher_find_wd (int wd)
+{
+ struct dirwatcher *dwp;
+
+ for (dwp = dirwatcher_list; dwp; dwp = dwp->next)
+ if (dwp->wd == wd)
+ break;
+ return dwp;
+}
+
+static int
+create_watcher (struct spool *sp, void *data)
+{
+ int ifd = *(int*)data;
+ struct dirwatcher *dwp;
+ int wd;
+ const char *path = get_path (sp);
+
+ if (!sp)
+ return 0;
+
+ if (debug_level > 1)
+ logmsg (LOG_DEBUG, "creating watcher %s", path);
+ dwp = malloc (sizeof(*dwp));
+ if (!dwp)
+ {
+ logmsg (LOG_ERR, "not enough memory");
+ return 1;
+ }
+ dwp->spool = sp;
+ dwp->parent = NULL;
+
+ wd = inotify_add_watch (ifd, path, IN_DELETE|IN_CREATE|IN_CLOSE_WRITE|
+ IN_MOVED_FROM|IN_MOVED_TO);
+ if (wd == -1)
+ {
+ logmsg (LOG_ERR, "cannot set watch on %s: %s", path, strerror (errno));
+ free (dwp);
+ return 1;
+ }
+
+ dwp->wd = wd;
+ dirwatcher_push (&dirwatcher_list, dwp);
+ return 0;
+}
+
+int
+watcher_init ()
+{
+ int ifd, rc;
+
+ if (debug_level > 1)
+ logmsg (LOG_DEBUG, "setting up inotify");
+ ifd = inotify_init ();
+ if (ifd == -1)
+ {
+ logmsg (LOG_ERR, "inotify_init: %s", strerror (errno));
+ return -1;
+ }
+
+ rc = for_each_spool (create_watcher, &ifd);
+ if (rc)
+ exit (EX_OSERR);
+ if (!dirwatcher_list)
+ {
+ if (debug_level > 1)
+ logmsg (LOG_DEBUG, "inotify: nothing to watch");
+ close (ifd);
+ ifd = -1;
+ }
+ else if (debug_level > 1)
+ logmsg (LOG_DEBUG, "inotify initialized successfully");
+
+ return ifd;
+}
+
+static void
+process_event (struct inotify_event *ep)
+{
+ static struct dirwatcher *dwp;
+ dwp = dirwatcher_find_wd (ep->wd);
+
+ if (ep->mask & IN_IGNORED)
+ /* nothing */;
+ else if (ep->mask & IN_Q_OVERFLOW)
+ logmsg (LOG_NOTICE, "event queue overflow");
+ else if (ep->mask & IN_UNMOUNT)
+ /* FIXME: not sure if there's
+ anything to do. Perhaps we should
+ deregister the watched dirs that
+ were located under the mountpoint
+ */;
+ else if (!dwp)
+ {
+ if (ep->name)
+ logmsg (LOG_NOTICE, "unrecognized event %x for %s",
+ ep->mask, ep->name);
+ else
+ logmsg (LOG_NOTICE, "unrecognized event %x", ep->mask);
+ }
+ else if (ep->mask & IN_CREATE)
+ {
+ if (debug_level > 0)
+ logmsg (LOG_DEBUG, "%s/%s created", dwp->spool->source_dir, ep->name);
+ }
+ else if (ep->mask & (IN_DELETE|IN_MOVED_FROM))
+ {
+ if (debug_level > 0)
+ logmsg (LOG_DEBUG, "%s/%s %s", dwp->spool->source_dir, ep->name,
+ ep->mask & IN_DELETE ? "deleted" : "moved out");
+ triplet_remove_file (dwp->spool, ep->name);
+ }
+ else if (ep->mask & (IN_CLOSE_WRITE|IN_MOVED_TO))
+ {
+ if (debug_level > 0)
+ logmsg (LOG_DEBUG, "%s/%s written", dwp->spool->source_dir, ep->name);
+ if (spool_add_new_file (dwp->spool, ep->name, 0, NULL) == 0 &&
+ count_processable_triplets ())
+ schedule_job (&inotify_spool, getuid ());
+ }
+ else
+ logmsg (LOG_NOTICE, "%s/%s: unexpected event %x",
+ dwp->spool->source_dir, ep->name, ep->mask);
+}
+
+static char buffer[4096];
+static int offset;
+
+int
+watcher_run (int ifd)
+{
+ int n;
+ int rdbytes;
+
+ if (ioctl (ifd, FIONREAD, &n))
+ {
+ logmsg (LOG_ERR, "ioctl: %s", strerror (errno));
+ return -1;
+ }
+ if (offset + n > sizeof buffer)
+ n = sizeof buffer - offset;
+ if (n)
+ {
+ rdbytes = read (ifd, buffer + offset, n);
+ if (rdbytes == -1)
+ {
+ if (errno == EINTR)
+ {
+ //FIXME logmsg (LOG_NOTICE, "got signal %d", signo);
+ return 0;
+ }
+
+ logmsg (LOG_NOTICE, "read failed: %s", strerror (errno));
+ return -1;
+ }
+ }
+ offset += n;
+
+ for (n = 0; offset - n >= sizeof (struct inotify_event); )
+ {
+ struct inotify_event *ep;
+ size_t size;
+
+ ep = (struct inotify_event *) (buffer + n);
+ size = sizeof(*ep) + ep->len;
+ if (offset - n < size)
+ break;
+
+ process_event (ep);
+
+ n += size;
+ }
+ if (n > 0 && offset - n > 0)
+ memmove (buffer, buffer + n, offset - n);
+ offset -= n;
+
+ return 0;
+}

Return to:

Send suggestions and report system problems to the System administrator.