aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2012-12-22 21:39:23 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2012-12-22 21:54:43 +0200
commit67a64a6a7809c183516740696e15ab88f82d7ef0 (patch)
treeb58f30a8cee98bac7c9f4bc8236c86eefdadb4ef
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.
-rw-r--r--configure.ac32
-rw-r--r--src/Makefile.am8
-rw-r--r--src/diskio.c6
-rw-r--r--src/job.c18
-rw-r--r--src/net.c53
-rw-r--r--src/process.c160
-rw-r--r--src/triplet.c104
-rw-r--r--src/verify.c5
-rw-r--r--src/vtab.c18
-rw-r--r--src/watcher.c243
-rw-r--r--src/wydawca.c14
-rw-r--r--src/wydawca.h47
12 files changed, 608 insertions, 100 deletions
diff --git a/configure.ac b/configure.ac
index a503cc7..2683b9b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,7 +15,8 @@
# along with wydawca. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ(2.63)
-AC_INIT([wydawca], 2.1.90, [bug-wydawca@gnu.org.ua])
+AC_INIT([wydawca], 2.1.90, [bug-wydawca@gnu.org.ua], [wydawca],
+ [http://www.gnu.org.ua/software/wydawca])
AC_CONFIG_SRCDIR([src/wydawca.c])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_HEADER([config.h])
@@ -33,7 +34,7 @@ AC_PROG_RANLIB
# Checks for header files.
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS([stdlib.h string.h sys/file.h unistd.h])
+AC_CHECK_HEADERS([stdlib.h string.h sys/file.h unistd.h sys/inotify.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
@@ -50,7 +51,9 @@ AC_FUNC_MALLOC
AC_FUNC_MEMCMP
AC_FUNC_STAT
AC_FUNC_VPRINTF
-AC_CHECK_FUNCS([fchdir memset strchr strdup strerror strrchr setegid setregid setresgid setresuid seteuid setreuid vsyslog sysconf getdtablesize])
+AC_CHECK_FUNCS([fchdir memset strchr strdup strerror strrchr setegid setregid\
+ setresgid setresuid seteuid setreuid vsyslog sysconf getdtablesize\
+ inotify_init])
# **********************
# Mailutils
@@ -107,6 +110,29 @@ if test "$status_tcpwrap" = yes; then
AC_DEFINE_UNQUOTED([WITH_LIBWRAP],1,[Defined if compiling with libwrap])
fi
+# **********************
+# TCP wrappers
+# **********************
+AC_ARG_WITH(inotify,
+ AC_HELP_STRING([--with-inotify],
+ [compile with inotify(7) support (Linux-specific)]),
+ [status_inotify=${withval}],
+ [status_inotify=probe])
+if test $status_inotify != no; then
+ if test "$ac_cv_header_sys_inotify_h" = yes &&
+ test "$ac_cv_func_inotify_init" = yes; then
+ status_inotify=yes
+ elif test $status_inotify = probe; then
+ status_inotify=no
+ else
+ AC_MSG_FAILURE([Requested inotify(7) support is not available])
+ fi
+fi
+if test $status_inotify = yes; then
+ AC_DEFINE([WITH_INOTIFY],1,[Set to 1 if inotify(7) is to be used])
+fi
+AM_CONDITIONAL([COND_INOTIFY],[test $status_inotify = yes])
+
# Grecs subsystem
GRECS_SETUP([grecs],[git2chg getopt tests])
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 <http://www.gnu.org/licenses/>.
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,6 +171,21 @@ 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);
@@ -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);
+ 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,6 +225,23 @@ wydawca_listener ()
break;
}
+ if (wfd != -1 && FD_ISSET (wfd, &rset))
+ {
+ 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)
@@ -240,5 +264,6 @@ wydawca_listener ()
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;
+ spool_commit_triplets (spool);
- 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);
- }
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;
@@ -221,6 +221,8 @@ verify_directive_file (struct file_triplet *trp)
if (fill_project_name (trp))
return 1;
+ if (!noath)
+ {
md = dictionary_open (dict);
if (!md)
return 1;
@@ -319,6 +321,7 @@ verify_directive_file (struct file_triplet *trp)
}
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 <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
@@ -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