summaryrefslogtreecommitdiffabout
path: root/src
Side-by-side diff
Diffstat (limited to 'src') (more/less context) (ignore whitespace changes)
-rw-r--r--src/Makefile.am8
-rw-r--r--src/diskio.c6
-rw-r--r--src/job.c18
-rw-r--r--src/net.c87
-rw-r--r--src/process.c162
-rw-r--r--src/triplet.c104
-rw-r--r--src/verify.c175
-rw-r--r--src/vtab.c18
-rw-r--r--src/watcher.c243
-rw-r--r--src/wydawca.c14
-rw-r--r--src/wydawca.h49
11 files changed, 683 insertions, 201 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 73182c4..828d573 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,2 +17,7 @@
sbin_PROGRAMS=wydawca
+
+if COND_INOTIFY
+ WATCHER_C=watcher.c
+endif
+
wydawca_SOURCES=\
@@ -50,3 +55,4 @@ wydawca_SOURCES=\
txtacc.c\
- report.c
+ report.c\
+ $(WATCHER_C)
diff --git a/src/diskio.c b/src/diskio.c
index bab5cb8..751d684 100644
--- a/src/diskio.c
+++ b/src/diskio.c
@@ -413,2 +413,8 @@ replace_allowed_p (struct file_triplet *trp)
+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
diff --git a/src/job.c b/src/job.c
index 4a97f88..3fae432 100644
--- a/src/job.c
+++ b/src/job.c
@@ -27,3 +27,3 @@ struct job
int state;
- const struct spool *spool;
+ struct spool *spool;
uid_t uid;
@@ -38,3 +38,4 @@ size_t jobcnt;
-static struct spool fake_spool = { "all spools" };
+struct spool fake_spool = { "all spools" },
+ inotify_spool = { "inotify" } ;
@@ -78,2 +79,9 @@ job_active_count ()
+static int
+procspool (struct spool *spool, void *data)
+{
+ spool_commit_triplets (spool);
+ return 0;
+}
+
int
@@ -84,3 +92,5 @@ wydawca_scanner (struct job *job)
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);
@@ -193,3 +203,3 @@ 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)
{
diff --git a/src/net.c b/src/net.c
index 4857fa2..3157fe8 100644
--- a/src/net.c
+++ b/src/net.c
@@ -24,6 +24,3 @@ open_listener ()
if (listen_sockaddr.sa == NULL)
- {
- logmsg (LOG_CRIT, _("listener address is not configured"));
- exit (EX_CONFIG);
- }
+ return -1;
@@ -101,3 +98,3 @@ handle_connection (FILE *in, FILE *out)
size_t buflen = 0;
- const struct spool *spool;
+ struct spool *spool;
char *p;
@@ -176,3 +173,18 @@ 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 ();
@@ -184,4 +196,2 @@ wydawca_listener ()
{
- int fd;
- FILE *in, *out;
int rc;
@@ -189,8 +199,2 @@ wydawca_listener ()
struct timeval to, *pto;
- union {
- struct sockaddr sa;
- struct sockaddr_in s_in;
- struct sockaddr_un s_un;
- } addr;
- socklen_t len;
@@ -198,3 +202,6 @@ wydawca_listener ()
FD_ZERO (&rset);
- FD_SET (ctlfd, &rset);
+ if (ctlfd != -1)
+ FD_SET (ctlfd, &rset);
+ if (wfd != -1)
+ FD_SET (wfd, &rset);
@@ -209,3 +216,3 @@ wydawca_listener ()
- rc = select (ctlfd + 1, &rset, NULL, NULL, pto);
+ rc = select (maxfd + 1, &rset, NULL, NULL, pto);
if (rc == 0)
@@ -220,22 +227,40 @@ wydawca_listener ()
- 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
@@ -26,2 +26,16 @@ 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
@@ -124,3 +138,10 @@ parse_file_name (const char *name, struct file_info *finfo)
-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)
@@ -136,2 +157,59 @@ match_uid_p (uid_t uid, int uc, uid_t *uv)
+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
@@ -139,3 +217,3 @@ match_uid_p (uid_t uid, int uc, uid_t *uv)
void
-scan_spool_unlocked (const struct spool *spool, int uc, uid_t *uv)
+scan_spool_unlocked (struct spool *spool, int uc, uid_t *uv)
{
@@ -168,39 +246,5 @@ scan_spool_unlocked (const struct spool *spool, int uc, uid_t *uv)
{
- 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);
}
@@ -210,15 +254,4 @@ scan_spool_unlocked (const struct spool *spool, int uc, uid_t *uv)
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);
@@ -228,3 +261,3 @@ scan_spool_unlocked (const struct spool *spool, int uc, uid_t *uv)
int
-scan_spool (const struct spool *spool, int uc, uid_t *uv)
+scan_spool (struct spool *spool, int uc, uid_t *uv)
{
@@ -251,4 +284,24 @@ scan_spool (const struct spool *spool, int uc, uid_t *uv)
-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)
{
@@ -257,2 +310,3 @@ close_dictionaries (struct spool *spool)
dictionary_done (spool->dictionary[i]);
+ spool->dict_inited = 0;
}
@@ -273,3 +327,3 @@ scan_all_spools (int uidc, uid_t *uidv)
for (sp = spool_list; sp; sp = sp->next)
- close_dictionaries (&sp->spool);
+ spool_close_dictionaries (&sp->spool);
timer_stop ("wydawca");
diff --git a/src/triplet.c b/src/triplet.c
index 79ca8c2..05b7536 100644
--- a/src/triplet.c
+++ b/src/triplet.c
@@ -34,3 +34,3 @@ hash_triplet_compare (void const *data1, void const *data2)
struct file_triplet const *t2 = data2;
- return strcmp (t1->name, t2->name);
+ return t1->spool == t2->spool && strcmp (t1->name, t2->name);
}
@@ -100,2 +100,3 @@ register_file (struct file_info *finfo, const struct spool *spool)
key.name[finfo->root_len] = 0;
+ key.spool = spool;
@@ -113,2 +114,24 @@ register_file (struct file_info *finfo, const struct spool *spool)
+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
@@ -149,3 +172,3 @@ enum triplet_state
static enum triplet_state
-check_triplet_state (struct file_triplet *trp)
+check_triplet_state (struct file_triplet *trp, int noauth)
{
@@ -153,3 +176,3 @@ check_triplet_state (struct file_triplet *trp)
{
- if (verify_directive_file (trp))
+ if (verify_directive_file (trp, noauth))
return triplet_bad;
@@ -162,4 +185,3 @@ check_triplet_state (struct file_triplet *trp)
}
- else if (trp->file[file_dist].name
- && trp->file[file_signature].name)
+ else if (trp->file[file_dist].name && trp->file[file_signature].name)
{
@@ -214,3 +236,3 @@ triplet_processor (void *data, void *proc_data)
- switch (check_triplet_state (trp))
+ switch (check_triplet_state (trp, 0))
{
@@ -248,3 +270,3 @@ triplet_processor (void *data, void *proc_data)
void
-enumerate_triplets (const struct spool *spool)
+spool_commit_triplets (struct spool *spool)
{
@@ -253,2 +275,4 @@ enumerate_triplets (const struct spool *spool)
spool->tag, mu_url_to_string (spool->dest_url));
+ if (spool_open_dictionaries (spool))
+ return;
if (triplet_table)
@@ -266,2 +290,68 @@ count_collected_triplets ()
+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);
+ }
+}
+
diff --git a/src/verify.c b/src/verify.c
index 1409889..4a108bc 100644
--- a/src/verify.c
+++ b/src/verify.c
@@ -204,3 +204,3 @@ 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)
{
@@ -222,101 +222,104 @@ verify_directive_file (struct file_triplet *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);
diff --git a/src/vtab.c b/src/vtab.c
index 2f8a85e..cec6fc0 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -26,9 +26,9 @@ 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 } },
@@ -55,2 +55,10 @@ 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
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;
+}
diff --git a/src/wydawca.c b/src/wydawca.c
index 69fb898..521d796 100644
--- a/src/wydawca.c
+++ b/src/wydawca.c
@@ -330,2 +330,15 @@ wydawca_daemon ()
+void
+version_hook (FILE *stream)
+{
+ printf ("Compiled with:");
+#ifdef WITH_LIBWRAP
+ printf (" libwrap");
+#endif
+#ifdef WITH_INOTIFY
+ printf (" inotify");
+#endif
+ putchar ('\n');
+}
+
int
@@ -337,2 +350,3 @@ main (int argc, char **argv)
program_name = argv[0];
+ print_version_hook = version_hook;
mu_register_all_mailer_formats ();
diff --git a/src/wydawca.h b/src/wydawca.h
index 5de1f3e..722fc9b 100644
--- a/src/wydawca.h
+++ b/src/wydawca.h
@@ -207,2 +207,3 @@ struct virt_tab
{
+ const char *(*get_path) (struct spool *sp);
int (*test_url) (mu_url_t url, grecs_locus_t *loc);
@@ -231,2 +232,3 @@ struct spool
struct dictionary *dictionary[dictionary_count];
+ int dict_inited;
struct archive_descr archive; /* Archivation data */
@@ -374,2 +376,5 @@ extern unsigned max_directive_version;
+extern struct spool fake_spool;
+extern struct spool inotify_spool;
+
#define UPDATE_STATS(what) \
@@ -411,3 +416,3 @@ 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 *);
@@ -415,2 +420,10 @@ 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);
@@ -418,4 +431,6 @@ 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);
@@ -424,2 +439,4 @@ char *triplet_expand_dictionary_query (struct dictionary *dict, void *handle,
+void triplet_remove_file (struct spool *spool, const char *name);
+
/* General-purpose dictionary support */
@@ -444,3 +461,3 @@ 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);
@@ -487,11 +504,8 @@ 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);
@@ -502,2 +516,3 @@ 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);
@@ -540,3 +555,3 @@ 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);
@@ -590 +605,9 @@ void txtacc_grow (struct txtacc *acc, const char *buf, size_t size);
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

Return to:

Send suggestions and report system problems to the System administrator.