From 67a64a6a7809c183516740696e15ab88f82d7ef0 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Sat, 22 Dec 2012 21:39:23 +0200 Subject: 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) : 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) : New method. (spool) : 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. --- src/Makefile.am | 8 +- src/diskio.c | 6 ++ src/job.c | 18 ++++- src/net.c | 87 ++++++++++++-------- src/process.c | 162 ++++++++++++++++++++++++------------- src/triplet.c | 104 ++++++++++++++++++++++-- src/verify.c | 175 ++++++++++++++++++++-------------------- src/vtab.c | 18 +++-- src/watcher.c | 243 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/wydawca.c | 14 ++++ src/wydawca.h | 49 +++++++++--- 11 files changed, 683 insertions(+), 201 deletions(-) create mode 100644 src/watcher.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 73182c4..828d573 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,6 +15,11 @@ # along with Wydawca. If not, see . sbin_PROGRAMS=wydawca + +if COND_INOTIFY + WATCHER_C=watcher.c +endif + wydawca_SOURCES=\ backup.c\ builtin.c\ @@ -48,7 +53,8 @@ wydawca_SOURCES=\ null.c\ timer.c\ txtacc.c\ - report.c + report.c\ + $(WATCHER_C) BUILT_SOURCES=cmdline.h EXTRA_DIST=cmdline.opt pp-setup update-2.0.awk diff --git a/src/diskio.c b/src/diskio.c index bab5cb8..751d684 100644 --- a/src/diskio.c +++ b/src/diskio.c @@ -411,6 +411,12 @@ replace_allowed_p (struct file_triplet *trp) return strcmp (val, "true") == 0; } +const char * +dir_get_path (struct spool *sp) +{ + return sp->source_dir; +} + /* Move the part FILE_ID of the triplet TRP between the directories in TRP->SPOOL. TRP->RELATIVE_DIR gives relative directory (i.e. the directory part of the file name) for backup purposes. diff --git a/src/job.c b/src/job.c index 4a97f88..3fae432 100644 --- a/src/job.c +++ b/src/job.c @@ -25,7 +25,7 @@ struct job { struct job *next, *prev; int state; - const struct spool *spool; + struct spool *spool; uid_t uid; pid_t pid; time_t timestamp; @@ -36,7 +36,8 @@ struct job *queue; size_t jobmax; size_t jobcnt; -static struct spool fake_spool = { "all spools" }; +struct spool fake_spool = { "all spools" }, + inotify_spool = { "inotify" } ; static int wakeup; @@ -76,13 +77,22 @@ job_active_count () return count; } +static int +procspool (struct spool *spool, void *data) +{ + spool_commit_triplets (spool); + return 0; +} + int wydawca_scanner (struct job *job) { int rc; initstats(); timer_start ("wydawca"); - if (job->spool == &fake_spool) + if (job->spool == &inotify_spool) + rc = for_each_spool (procspool, NULL); + else if (job->spool == &fake_spool) rc = scan_all_spools (1, &job->uid); else { @@ -191,7 +201,7 @@ job_insert (struct job *job, struct job *elt) } void -schedule_job (const struct spool *spool, uid_t uid) +schedule_job (struct spool *spool, uid_t uid) { struct job *job; diff --git a/src/net.c b/src/net.c index 4857fa2..3157fe8 100644 --- a/src/net.c +++ b/src/net.c @@ -22,10 +22,7 @@ open_listener () int fd; if (listen_sockaddr.sa == NULL) - { - logmsg (LOG_CRIT, _("listener address is not configured")); - exit (EX_CONFIG); - } + return -1; fd = socket (listen_sockaddr.sa->sa_family, SOCK_STREAM, 0); if (fd == -1) @@ -99,7 +96,7 @@ handle_connection (FILE *in, FILE *out) { char *buf = NULL; size_t buflen = 0; - const struct spool *spool; + struct spool *spool; char *p; struct passwd *pw; @@ -174,7 +171,22 @@ void wydawca_listener () { int ctlfd = open_listener (); + int wfd = watcher_init (); + int maxfd = 0; + + if (ctlfd != -1) + maxfd = ctlfd; + + if (wfd != -1 && wfd > maxfd) + maxfd = wfd; + if (maxfd == 0) + { + logmsg (LOG_CRIT, + _("listener address is not configured and inotify is not available")); + exit (EX_CONFIG); + } + job_init (); signal (SIGHUP, sig_hup); signal (SIGTERM, sig_term); @@ -182,21 +194,16 @@ wydawca_listener () signal (SIGINT, sig_term); while (!terminate) { - int fd; - FILE *in, *out; int rc; fd_set rset; struct timeval to, *pto; - union { - struct sockaddr sa; - struct sockaddr_in s_in; - struct sockaddr_un s_un; - } addr; - socklen_t len; job_queue_runner (); FD_ZERO (&rset); - FD_SET (ctlfd, &rset); + if (ctlfd != -1) + FD_SET (ctlfd, &rset); + if (wfd != -1) + FD_SET (wfd, &rset); if (wakeup_interval) { @@ -207,7 +214,7 @@ wydawca_listener () else pto = NULL; - rc = select (ctlfd + 1, &rset, NULL, NULL, pto); + rc = select (maxfd + 1, &rset, NULL, NULL, pto); if (rc == 0) continue; else if (rc < 0) @@ -218,26 +225,44 @@ wydawca_listener () break; } - len = sizeof (addr); - fd = accept (ctlfd, (struct sockaddr*) &addr, &len); - if (fd == -1) - continue; - /* FIXME: Use Mailutils ACLs? */ -#ifdef WITH_LIBWRAP - if (!tcpwrap_access(fd)) + if (wfd != -1 && FD_ISSET (wfd, &rset)) { - close(fd); - continue; + watcher_run (wfd); } + + if (ctlfd != -1 && FD_ISSET (ctlfd, &rset)) + { + int fd; + FILE *in, *out; + union + { + struct sockaddr sa; + struct sockaddr_in s_in; + struct sockaddr_un s_un; + } addr; + socklen_t len; + + len = sizeof (addr); + fd = accept (ctlfd, (struct sockaddr*) &addr, &len); + if (fd == -1) + continue; + /* FIXME: Use Mailutils ACLs? */ +#ifdef WITH_LIBWRAP + if (!tcpwrap_access(fd)) + { + close(fd); + continue; + } #endif - in = fdopen (fd, "r"); - setlinebuf (in); - out = fdopen (fd, "w"); - setlinebuf (out); - handle_connection (in, out); - fclose (in); - fclose (out); + in = fdopen (fd, "r"); + setlinebuf (in); + out = fdopen (fd, "w"); + setlinebuf (out); + handle_connection (in, out); + fclose (in); + fclose (out); + } } } diff --git a/src/process.c b/src/process.c index c5eb321..ec64589 100644 --- a/src/process.c +++ b/src/process.c @@ -24,6 +24,20 @@ struct spool_list static struct spool_list *spool_list; +int +for_each_spool (int (*fun) (struct spool *, void *), void *data) +{ + struct spool_list *sp; + + for (sp = spool_list; sp; sp = sp->next) + { + int rc = fun (&sp->spool, data); + if (rc) + return rc; + } + return 0; +} + void register_spool (struct spool *spool) { @@ -122,7 +136,14 @@ parse_file_name (const char *name, struct file_info *finfo) abort (); /* should not happen */ } -int +void +file_info_cleanup (struct file_info *finfo) +{ + free (finfo->name); + memset (finfo, 0, sizeof (*finfo)); +} + +static int match_uid_p (uid_t uid, int uc, uid_t *uv) { int i; @@ -134,10 +155,67 @@ match_uid_p (uid_t uid, int uc, uid_t *uv) return 0; } +int +spool_cwd_add_new_file (const struct spool *spool, const char *name, + int uc, uid_t *uv) +{ + struct stat st; + struct file_info finfo; + + if (stat (name, &st)) + { + logmsg (LOG_ERR, _("cannot stat file %s/%s: %s"), + spool->source_dir, name, strerror (errno)); + return -1; + } + + if (!S_ISREG (st.st_mode)) + { + logmsg (LOG_NOTICE, _("not a regular file: %s/%s"), + spool->source_dir, name); + return -1; + } + + if (!match_uid_p (st.st_uid, uc, uv)) + { + if (debug_level) + logmsg (LOG_DEBUG, _("ignoring file: %s/%s"), + spool->source_dir, name); + return -1; + } + + finfo.sb = st; + parse_file_name (name, &finfo); + + if (debug_level) + logmsg (LOG_DEBUG, _("found file %s: %s, stem: %.*s"), name, + file_type_str (finfo.type), finfo.root_len, finfo.name); + + register_file (&finfo, spool); + return 0; +} + +int +spool_add_new_file (const struct spool *spool, const char *name, + int uc, uid_t *uv) +{ + if (debug_level) + logmsg (LOG_DEBUG, "%s -> %s, adding %s", spool->source_dir, + mu_url_to_string (spool->dest_url), name); + + if (chdir (spool->source_dir)) + { + logmsg (LOG_ERR, _("cannot chdir to %s: %s"), spool->source_dir, + strerror (errno)); + return -1; + } + return spool_cwd_add_new_file (spool, name, uc, uv); +} + /* Scan upload directory from the DPAIR and register all files found there, forming triplets when possible */ void -scan_spool_unlocked (const struct spool *spool, int uc, uid_t *uv) +scan_spool_unlocked (struct spool *spool, int uc, uid_t *uv) { DIR *dir; struct dirent *ent; @@ -166,67 +244,22 @@ scan_spool_unlocked (const struct spool *spool, int uc, uid_t *uv) timer_start (spool->tag); while ((ent = readdir (dir))) { - struct stat st; - struct file_info finfo; - if (strcmp (ent->d_name, ".") == 0 || strcmp (ent->d_name, "..") == 0) continue; - - if (stat (ent->d_name, &st)) - { - logmsg (LOG_ERR, _("cannot stat file %s/%s: %s"), - spool->source_dir, ent->d_name, - strerror (errno)); - continue; - } - - if (!S_ISREG (st.st_mode)) - { - logmsg (LOG_NOTICE, _("not a regular file: %s/%s"), - spool->source_dir, ent->d_name); - continue; - } - - if (!match_uid_p (st.st_uid, uc, uv)) - { - if (debug_level) - logmsg (LOG_DEBUG, _("ignoring file: %s/%s"), - spool->source_dir, ent->d_name); - continue; - } - - finfo.sb = st; - parse_file_name (ent->d_name, &finfo); - - if (debug_level) - logmsg (LOG_DEBUG, _("found file %s: %s, stem: %.*s"), ent->d_name, - file_type_str (finfo.type), finfo.root_len, finfo.name); - - register_file (&finfo, spool); + spool_cwd_add_new_file (spool, ent->d_name, uc, uv); } closedir (dir); if (count_collected_triplets () > 0) - { - int i; - - for (i = 0; i < dictionary_count; i++) - { - if (dictionary_init (spool->dictionary[i])) - { - logmsg (LOG_ERR, _("failed to initialize dictionary %d"), i); - return; - } - } - enumerate_triplets (spool); - } + spool_commit_triplets (spool); + timer_stop (spool->tag); timer_stop ("spool"); } int -scan_spool (const struct spool *spool, int uc, uid_t *uv) +scan_spool (struct spool *spool, int uc, uid_t *uv) { char *lockfile = wydawca_lockname (spool->tag); int rc = wydawca_lock (lockfile); @@ -249,12 +282,33 @@ scan_spool (const struct spool *spool, int uc, uid_t *uv) return rc; } -static void -close_dictionaries (struct spool *spool) +int +spool_open_dictionaries (struct spool *spool) +{ + if (!spool->dict_inited) + { + int i; + + for (i = 0; i < dictionary_count; i++) + { + if (dictionary_init (spool->dictionary[i])) + { + logmsg (LOG_ERR, _("failed to initialize dictionary %d"), i); + return -1; + } + } + spool->dict_inited = 1; + } + return 0; +} + +void +spool_close_dictionaries (struct spool *spool) { int i; for (i = 0; i < NITEMS (spool->dictionary); i++) dictionary_done (spool->dictionary[i]); + spool->dict_inited = 0; } /* Scan all configured update directories */ @@ -271,7 +325,7 @@ scan_all_spools (int uidc, uid_t *uidv) rc++; for (sp = spool_list; sp; sp = sp->next) - close_dictionaries (&sp->spool); + spool_close_dictionaries (&sp->spool); timer_stop ("wydawca"); return rc; } diff --git a/src/triplet.c b/src/triplet.c index 79ca8c2..05b7536 100644 --- a/src/triplet.c +++ b/src/triplet.c @@ -32,7 +32,7 @@ hash_triplet_compare (void const *data1, void const *data2) { struct file_triplet const *t1 = data1; struct file_triplet const *t2 = data2; - return strcmp (t1->name, t2->name); + return t1->spool == t2->spool && strcmp (t1->name, t2->name); } /* Reclaim memory storage associated with a table entry */ @@ -98,6 +98,7 @@ register_file (struct file_info *finfo, const struct spool *spool) key.name = grecs_malloc (finfo->root_len + 1); memcpy (key.name, finfo->name, finfo->root_len); key.name[finfo->root_len] = 0; + key.spool = spool; ret = grecs_symtab_lookup_or_install (triplet_table, &key, &install); if (!ret) @@ -111,6 +112,28 @@ register_file (struct file_info *finfo, const struct spool *spool) ret->file[finfo->type] = *finfo; } +struct file_triplet * +triplet_lookup (struct spool *spool, const char *name) +{ + struct file_triplet key, *ret; + struct file_info finfo; + + if (!triplet_table) + return NULL; + + parse_file_name (name, &finfo); + + key.name = grecs_malloc (finfo.root_len + 1); + memcpy (key.name, finfo.name, finfo.root_len); + key.name[finfo.root_len] = 0; + key.spool = spool; + + ret = grecs_symtab_lookup_or_install (triplet_table, &key, NULL); + file_info_cleanup (&finfo); + + return ret; +} + /* Return true if any part of the triplet TRP was modified more than TTL seconds ago */ static int @@ -147,11 +170,11 @@ enum triplet_state static enum triplet_state -check_triplet_state (struct file_triplet *trp) +check_triplet_state (struct file_triplet *trp, int noauth) { if (trp->file[file_directive].name) { - if (verify_directive_file (trp)) + if (verify_directive_file (trp, noauth)) return triplet_bad; if (trp->file[file_dist].name == 0 @@ -160,8 +183,7 @@ check_triplet_state (struct file_triplet *trp) if (directive_get_value (trp, "filename", NULL)) return triplet_directive; } - else if (trp->file[file_dist].name - && trp->file[file_signature].name) + else if (trp->file[file_dist].name && trp->file[file_signature].name) { if (trp->file[file_dist].sb.st_uid == trp->file[file_signature].sb.st_uid @@ -212,7 +234,7 @@ triplet_processor (void *data, void *proc_data) SP (trp->file[file_signature].name), SP (trp->file[file_directive].name)); - switch (check_triplet_state (trp)) + switch (check_triplet_state (trp, 0)) { case triplet_directive: case triplet_complete: @@ -246,11 +268,13 @@ triplet_processor (void *data, void *proc_data) /* Process all triplets from the table according to the SPOOL */ void -enumerate_triplets (const struct spool *spool) +spool_commit_triplets (struct spool *spool) { if (debug_level) logmsg (LOG_DEBUG, _("processing spool %s (%s)"), spool->tag, mu_url_to_string (spool->dest_url)); + if (spool_open_dictionaries (spool)) + return; if (triplet_table) { grecs_symtab_enumerate (triplet_table, triplet_processor, NULL); @@ -264,6 +288,72 @@ count_collected_triplets () return triplet_table ? grecs_symtab_count_entries (triplet_table) : 0; } +static int +triplet_counter (void *data, void *proc_data) +{ + struct file_triplet *trp = data; + size_t *cp = proc_data; + + if (debug_level) + logmsg (LOG_DEBUG, "FILE %s, DIST=%s, SIG=%s, DIRECTIVE=%s", + trp->name, + SP (trp->file[file_dist].name), + SP (trp->file[file_signature].name), + SP (trp->file[file_directive].name)); + + switch (check_triplet_state (trp, 1)) + { + case triplet_directive: + case triplet_complete: + case triplet_bad: + ++*cp; + case triplet_incomplete: + return 0; + } + + if (triplet_expired_p (trp, trp->spool->file_sweep_time)) + ++*cp;//FIXME + + return 0; +} + +size_t +count_processable_triplets () +{ + size_t count = 0; + if (triplet_table) + grecs_symtab_enumerate (triplet_table, triplet_counter, &count); + return count; +} + +void +triplet_remove_file (struct spool *spool, const char *name) +{ + struct file_triplet *tp = triplet_lookup (spool, name); + int i, n = 0; + + if (!tp) + return; + + for (i = 0; i < FILE_TYPE_COUNT; i++) + { + if (!tp->file[i].name) + /* nothing */; + else if (strcmp (tp->file[i].name, name) == 0) + file_info_cleanup (&tp->file[i]); + else + n++; + } + + if (!n) + { + if (debug_level > 0) + logmsg (LOG_DEBUG, "deleting empty triplet (%s/%s)", + spool->source_dir, name); + grecs_symtab_remove (triplet_table, tp); + } +} + static const char * expand_project_base (struct metadef *def, void *data) diff --git a/src/verify.c b/src/verify.c index 1409889..4a108bc 100644 --- a/src/verify.c +++ b/src/verify.c @@ -202,7 +202,7 @@ uploader_find_frp (struct uploader_info *list, const char *fpr) } int -verify_directive_file (struct file_triplet *trp) +verify_directive_file (struct file_triplet *trp, int noath) { char *command; int rc; @@ -220,105 +220,108 @@ verify_directive_file (struct file_triplet *trp) if (fill_project_name (trp)) return 1; - - md = dictionary_open (dict); - if (!md) - return 1; - - command = triplet_expand_dictionary_query (dict, md, trp); - - rc = dictionary_lookup (dict, md, command); - free (command); - if (rc) - { - logmsg (LOG_ERR, _("cannot get uploaders for %s"), trp->name); - dictionary_close (dict, md); - return 1; - } - - nrows = dictionary_num_rows (dict); - if (nrows == 0) - { - logmsg (LOG_ERR, _("found no uploaders for %s"), trp->name); - dictionary_close (dict, md); - return 1; - } - ncols = dictionary_num_cols (dict); - if (ncols < 4) + if (!noath) { - logmsg (LOG_ERR, - _("project-uploader dictionary error: too few columns (%lu)"), - (unsigned long) ncols); - dictionary_close (dict, md); - return 1; - } + md = dictionary_open (dict); + if (!md) + return 1; - head = tail = NULL; - for (i = 0; i < nrows; i++) - { - const char *p; - struct uploader_info info, *ptr; + command = triplet_expand_dictionary_query (dict, md, trp); - memset (&info, 0, sizeof (info)); - p = dictionary_result (dict, md, i, 0); - if (p) - info.name = triplet_strdup (trp, p); - p = dictionary_result (dict, md, i, 1); - if (p) - info.realname = triplet_strdup (trp, p); - p = dictionary_result (dict, md, i, 2); - if (p) - info.email = triplet_strdup (trp, p); - p = dictionary_result (dict, md, i, 3); - if (p) - info.gpg_key = triplet_strdup (trp, p); + rc = dictionary_lookup (dict, md, command); + free (command); + if (rc) + { + logmsg (LOG_ERR, _("cannot get uploaders for %s"), trp->name); + dictionary_close (dict, md); + return 1; + } - if (debug_level > 3) + nrows = dictionary_num_rows (dict); + if (nrows == 0) { - logmsg (LOG_DEBUG, _("name: %s"), SP (info.name)); - logmsg (LOG_DEBUG, _("realname: %s"), SP (info.realname)); - logmsg (LOG_DEBUG, _("gpg-key: %s"), SP (info.gpg_key)); - logmsg (LOG_DEBUG, _("email: %s"), SP (info.email)); + logmsg (LOG_ERR, _("found no uploaders for %s"), trp->name); + dictionary_close (dict, md); + return 1; } - if (!info.name || !info.realname || !info.gpg_key || !info.email) + ncols = dictionary_num_cols (dict); + if (ncols < 4) { logmsg (LOG_ERR, - _("project-uploader dictionary error: malformed row %lu"), - (unsigned long) i); - /* FIXME: Memory not reclaimed */ - continue; + _("project-uploader dictionary error: too few columns (%lu)"), + (unsigned long) ncols); + dictionary_close (dict, md); + return 1; } - - ptr = new_uploader_info (&info); - if (tail) - tail->next = ptr; - else - head = ptr; - tail = ptr; - } + + head = tail = NULL; + for (i = 0; i < nrows; i++) + { + const char *p; + struct uploader_info info, *ptr; - dictionary_close (dict, md); - - if (!head) - { - logmsg (LOG_ERR, _("no valid uploaders found for %s"), trp->name); - return 1; - } - - trp->uploader_list = head; - trp->uploader = NULL; + memset (&info, 0, sizeof (info)); + p = dictionary_result (dict, md, i, 0); + if (p) + info.name = triplet_strdup (trp, p); + p = dictionary_result (dict, md, i, 1); + if (p) + info.realname = triplet_strdup (trp, p); + p = dictionary_result (dict, md, i, 2); + if (p) + info.email = triplet_strdup (trp, p); + p = dictionary_result (dict, md, i, 3); + if (p) + info.gpg_key = triplet_strdup (trp, p); + + if (debug_level > 3) + { + logmsg (LOG_DEBUG, _("name: %s"), SP (info.name)); + logmsg (LOG_DEBUG, _("realname: %s"), SP (info.realname)); + logmsg (LOG_DEBUG, _("gpg-key: %s"), SP (info.gpg_key)); + logmsg (LOG_DEBUG, _("email: %s"), SP (info.email)); + } + + if (!info.name || !info.realname || !info.gpg_key || !info.email) + { + logmsg (LOG_ERR, + _("project-uploader dictionary error: malformed row %lu"), + (unsigned long) i); + /* FIXME: Memory not reclaimed */ + continue; + } + + ptr = new_uploader_info (&info); + if (tail) + tail->next = ptr; + else + head = ptr; + tail = ptr; + } + + dictionary_close (dict, md); - if (verify_directive_signature (trp)) - { - /*FIXME: Update stats */ - logmsg (LOG_ERR, _("invalid signature for %s"), - trp->name ? trp->name : "[unknown]"); - return 1; + if (!head) + { + logmsg (LOG_ERR, _("no valid uploaders found for %s"), trp->name); + return 1; + } + + trp->uploader_list = head; + trp->uploader = NULL; + + if (verify_directive_signature (trp)) + { + /*FIXME: Update stats */ + logmsg (LOG_ERR, _("invalid signature for %s"), + trp->name ? trp->name : "[unknown]"); + return 1; + } + else if (debug_level) + logmsg (LOG_DEBUG, _("%s: directive file signature OK"), trp->name); } - else if (debug_level) - logmsg (LOG_DEBUG, _("%s: directive file signature OK"), trp->name); if (debug_level > 1) { diff --git a/src/vtab.c b/src/vtab.c index 2f8a85e..cec6fc0 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -24,13 +24,13 @@ struct virt_tab_reg static struct virt_tab_reg reg[] = { { "file", - { dir_test_url, dir_move_file, dir_archive_file, dir_symlink_file, - dir_rmsymlink_file } }, + { dir_get_path, dir_test_url, dir_move_file, dir_archive_file, + dir_symlink_file, dir_rmsymlink_file } }, { "dir", - { dir_test_url, dir_move_file, dir_archive_file, dir_symlink_file, - dir_rmsymlink_file } }, + { dir_get_path, dir_test_url, dir_move_file, dir_archive_file, + dir_symlink_file, dir_rmsymlink_file } }, { "null", - { NULL, null_move_file, null_archive_file, null_symlink_file, + { NULL, NULL, null_move_file, null_archive_file, null_symlink_file, null_rmsymlink_file } }, { NULL } }; @@ -53,6 +53,14 @@ url_to_vtab (mu_url_t url, struct virt_tab *vtab) } +const char * +get_path (struct spool *sp) +{ + if (!sp->vtab.get_path) + return NULL; + return sp->vtab.get_path (sp); +} + int move_file (struct file_triplet *trp, enum file_type file_id) { 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 . */ + +#include "wydawca.h" +#include +#include + +/* 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; +} diff --git a/src/wydawca.c b/src/wydawca.c index 69fb898..521d796 100644 --- a/src/wydawca.c +++ b/src/wydawca.c @@ -328,6 +328,19 @@ wydawca_daemon () #include "cmdline.h" +void +version_hook (FILE *stream) +{ + printf ("Compiled with:"); +#ifdef WITH_LIBWRAP + printf (" libwrap"); +#endif +#ifdef WITH_INOTIFY + printf (" inotify"); +#endif + putchar ('\n'); +} + int main (int argc, char **argv) { @@ -335,6 +348,7 @@ main (int argc, char **argv) const char *p; program_name = argv[0]; + print_version_hook = version_hook; mu_register_all_mailer_formats (); mu_stdstream_setup (MU_STDSTREAM_RESET_NONE); config_init (); diff --git a/src/wydawca.h b/src/wydawca.h index 5de1f3e..722fc9b 100644 --- a/src/wydawca.h +++ b/src/wydawca.h @@ -205,6 +205,7 @@ struct file_triplet struct virt_tab { + const char *(*get_path) (struct spool *sp); int (*test_url) (mu_url_t url, grecs_locus_t *loc); int (*move_file) (struct file_triplet *trp, enum file_type file_id); int (*archive_file) (struct file_triplet *trp, const char *file_name); @@ -229,6 +230,7 @@ struct spool time_t file_sweep_time; /* Remove invalid/unprocessed files after this amount of time */ struct dictionary *dictionary[dictionary_count]; + int dict_inited; struct archive_descr archive; /* Archivation data */ struct notification *notification; char *check_script; @@ -372,6 +374,9 @@ extern char *temp_homedir; extern unsigned min_directive_version; extern unsigned max_directive_version; +extern struct spool fake_spool; +extern struct spool inotify_spool; + #define UPDATE_STATS(what) \ do \ { \ @@ -409,19 +414,31 @@ enum exec_result wydawca_exec (int argc, const char **argv, int *retcode); /* Directory scanning and registering */ -int scan_spool (const struct spool *spool, int uc, uid_t *uv); +int scan_spool (struct spool *spool, int uc, uid_t *uv); int scan_all_spools (int, uid_t *); void spool_create_timers (void); +int spool_add_new_file (const struct spool *spool, const char *name, + int uc, uid_t *uv); +int spool_cwd_add_new_file (const struct spool *spool, const char *name, + int uc, uid_t *uv); +int spool_open_dictionaries (struct spool *spool); +void spool_close_dictionaries (struct spool *spool); + +int for_each_spool (int (*fun) (struct spool *, void *), void *data); void register_spool (struct spool *spool); struct spool *wydawca_find_spool (const char *name); void register_file (struct file_info *finfo, const struct spool *spool); -void enumerate_triplets (const struct spool *); +void spool_commit_triplets (struct spool *); size_t count_collected_triplets (void); +size_t count_processable_triplets (void); + char *triplet_expand_param (const char *tmpl, struct file_triplet *trp); char *triplet_expand_dictionary_query (struct dictionary *dict, void *handle, struct file_triplet *trp); +void triplet_remove_file (struct spool *spool, const char *name); + /* General-purpose dictionary support */ struct dictionary *dictionary_new (enum dictionary_id id, enum dictionary_type type); @@ -442,7 +459,7 @@ unsigned dictionary_num_rows (struct dictionary *dict); unsigned dictionary_num_cols (struct dictionary *dict); /* Verification functions */ -int verify_directive_file (struct file_triplet *trp); +int verify_directive_file (struct file_triplet *trp, int noath); int verify_directive_signature (struct file_triplet *trp); int verify_detached_signature (struct file_triplet *trp); int fill_project_name (struct file_triplet *trp); @@ -485,21 +502,19 @@ int assert_string_arg (grecs_locus_t *, enum grecs_callback_command, /* vtab.c */ int url_to_vtab (mu_url_t url, struct virt_tab *vtab); -int -move_file (struct file_triplet *trp, enum file_type file_id); -int -archive_file (struct file_triplet *trp, const char *file_name); -int -symlink_file (struct file_triplet *trp, - const char *wanted_src, const char *wanted_dst); -int -rmsymlink_file (struct file_triplet *trp, const char *file_name); +const char *get_path (struct spool *sp); +int move_file (struct file_triplet *trp, enum file_type file_id); +int archive_file (struct file_triplet *trp, const char *file_name); +int symlink_file (struct file_triplet *trp, + const char *wanted_src, const char *wanted_dst); +int rmsymlink_file (struct file_triplet *trp, const char *file_name); /* diskio.c */ char *concat_dir (const char *base, const char *name, size_t *pbaselen); int copy_file (const char *file, const char *dst_file); +const char *dir_get_path (struct spool *sp); int dir_test_url (mu_url_t url, grecs_locus_t *locus); int dir_move_file (struct file_triplet *trp, enum file_type file_id); int dir_archive_file (struct file_triplet *trp, const char *reldir); @@ -538,7 +553,7 @@ extern char *report_string; /* job.c */ -void schedule_job (const struct spool *spool, uid_t uid); +void schedule_job (struct spool *spool, uid_t uid); void job_init (void); void job_queue_runner (void); @@ -588,3 +603,11 @@ void txtacc_grow (struct txtacc *acc, const char *buf, size_t size); } \ while (0) char *txtacc_finish (struct txtacc *acc, int steal); + +#ifdef WITH_INOTIFY +int watcher_init (void); +int watcher_run (int); +#else +# define watcher_init() -1 +# define watcher_run(c) +#endif -- cgit v1.2.1