diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2012-12-22 21:39:23 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2012-12-22 21:54:43 +0200 |
commit | 67a64a6a7809c183516740696e15ab88f82d7ef0 (patch) | |
tree | b58f30a8cee98bac7c9f4bc8236c86eefdadb4ef /src/watcher.c | |
parent | 2c28190e77ce38b6437a2be8337680f77f58d37a (diff) | |
download | wydawca-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')
-rw-r--r-- | src/watcher.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/src/watcher.c b/src/watcher.c new file mode 100644 index 0000000..f8761ee --- /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; +} |