aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2013-12-27 11:09:23 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2013-12-27 14:12:45 +0200
commitee6ce9f5da1d780a9efe5a6126884d1f0ae1b5a2 (patch)
tree6395ce4d873cc317293e9d1929b12d4de89342fe /src
parente37d338c901a12d8b39481da8e5f437555705e16 (diff)
downloaddirevent-ee6ce9f5da1d780a9efe5a6126884d1f0ae1b5a2.tar.gz
direvent-ee6ce9f5da1d780a9efe5a6126884d1f0ae1b5a2.tar.bz2
Implement filename selection
* NEWS: Update. * doc/dircond.texi: Document the file statement. * src/fnpat.c: New file. * src/Makefile.am (dircond_SOURCES): Add fnpat.c * src/config.c (eventconf) <fnames>: New member. (eventconf_free): Free fnames. (eventconf_flush): Set fnames. (watcher_kw) <file>: New statement. * src/dircond.h (filename_pattern): New struct. (handler) <fnames>: New member. (handler_matches_event): New macro. (filename_pattern_free) (filename_pattern_match): New proto. * src/ev_inotify.c (process_event): Use handler_matches_event. * src/ev_kqueue.c: Likewise. * src/watcher.c (deliver_ev_create): Likewise. * tests/Makefile.am: Add new tests. * tests/testsuite.at: Likewise. * tests/create.at: Add a distinctive keyword. * tests/glob01.at: New testcase. * tests/re01.at: New testcase. * tests/re02.at: Likewise. * tests/re03.at: Likewise. * tests/re04.at: Likewise.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/config.c107
-rw-r--r--src/dircond.h24
-rw-r--r--src/ev_inotify.c2
-rw-r--r--src/ev_kqueue.c2
-rw-r--r--src/fnpat.c56
-rw-r--r--src/watcher.c4
7 files changed, 190 insertions, 6 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index fbccb85..5cc3101 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,6 +6,7 @@ dircond_SOURCES=\
config.c\
environ.c\
event.c\
+ fnpat.c\
hashtab.c\
watcher.c\
progman.c\
diff --git a/src/config.c b/src/config.c
index 7e0ae4b..84d7b54 100644
--- a/src/config.c
+++ b/src/config.c
@@ -149,6 +149,7 @@ static struct grecs_keyword syslog_kw[] = {
struct eventconf {
struct grecs_list *pathlist;
event_mask eventmask;
+ struct grecs_list *fnames;
char *command;
uid_t uid; /* Run as this user (unless 0) */
gid_t *gidv; /* Run with these groups' privileges */
@@ -183,6 +184,7 @@ static void
eventconf_free()
{
grecs_list_free(eventconf.pathlist);
+ grecs_list_free(eventconf.fnames);
free(eventconf.command);
free(eventconf.gidv);
envfree(eventconf.env);
@@ -213,6 +215,7 @@ eventconf_flush(grecs_locus_t *loc)
hp = emalloc(sizeof(*hp));
hp->next = NULL;
hp->ev_mask = eventconf.eventmask;
+ hp->fnames = eventconf.fnames;
hp->flags = eventconf.flags;
hp->timeout = eventconf.timeout;
hp->prog = eventconf.command;
@@ -578,8 +581,109 @@ cb_environ(enum grecs_callback_command cmd, grecs_node_t *node,
}
return 0;
}
+
+static int
+file_name_pattern(struct grecs_list *lp, grecs_value_t *val)
+{
+ char *arg;
+ int rc;
+ int flags = REG_EXTENDED|REG_NOSUB;
+ struct filename_pattern *pat;
+
+ if (assert_grecs_value_type(&val->locus, val, GRECS_TYPE_STRING))
+ return 1;
+ arg = val->v.string;
+
+ pat = emalloc(sizeof(*pat));
+
+ if (arg[0] == '/') {
+ char *q, *p;
+
+ pat->type = PAT_REGEX;
+ p = strchr(arg+1, '/');
+ if (!p) {
+ grecs_error(&val->locus, 0, "Unterminated regexp");
+ free(pat);
+ return 1;
+ }
+ for (q = p + 1; *q; q++) {
+ switch (*q) {
+ case 'b':
+ flags &= ~REG_EXTENDED;
+ break;
+ case 'i':
+ flags |= REG_ICASE;
+ break;
+ default:
+ grecs_error(&val->locus, 0,
+ "Unrecognized flag: %c", *q);
+ free(pat);
+ return 1;
+ }
+ }
+
+ *p = 0;
+ rc = regcomp(&pat->v.re, arg + 1, flags);
+ *p = '/';
+
+ if (rc) {
+ char errbuf[128];
+ regerror(rc, &pat->v.re, errbuf, sizeof(errbuf));
+ grecs_error(&val->locus, 0, "%s", errbuf);
+ filename_pattern_free(pat);
+ return 1;
+ }
+ } else {
+ pat->type = PAT_GLOB;
+ pat->v.glob = estrdup(arg);
+ }
+
+ grecs_list_append(lp, pat);
+
+ return 0;
+}
+
+static int
+cb_file_pattern(enum grecs_callback_command cmd, grecs_node_t *node,
+ void *varptr, void *cb_data)
+{
+ grecs_value_t *val = node->v.value;
+ struct grecs_list_entry *ep;
+ struct grecs_list *lp, **lpp = varptr;
+ int i;
+
+ ASSERT_SCALAR(cmd, &node->locus);
+
+ if (!*lpp) {
+ lp = grecs_list_create();
+ lp->free_entry = filename_pattern_free;
+ *lpp = lp;
+ } else
+ lp = *lpp;
+ switch (val->type) {
+ case GRECS_TYPE_STRING:
+ file_name_pattern(lp, val);
+ break;
+
+ case GRECS_TYPE_ARRAY:
+ for (i = 0; i < val->v.arg.c; i++)
+ if (file_name_pattern(lp, val->v.arg.v[i]))
+ break;
+ break;
+
+ case GRECS_TYPE_LIST:
+ for (ep = val->v.list->head; ep; ep = ep->next)
+ if (file_name_pattern(lp,
+ (grecs_value_t *) ep->data))
+ break;
+ break;
+ }
+
+ return 0;
+}
+
static struct grecs_keyword watcher_kw[] = {
{ "path", NULL, "Pathname to watch",
grecs_type_string, GRECS_DFLT, &eventconf.pathlist, 0,
@@ -587,6 +691,9 @@ static struct grecs_keyword watcher_kw[] = {
{ "event", NULL, "Events to watch for",
grecs_type_string, GRECS_LIST, &eventconf.eventmask, 0,
cb_eventlist },
+ { "file", "regexp", "Files to watch for",
+ grecs_type_string, GRECS_LIST, &eventconf.fnames, 0,
+ cb_file_pattern },
{ "command", NULL, "Command to execute on event",
grecs_type_string, GRECS_DFLT, &eventconf.command },
{ "user", "name", "Run command as this user",
diff --git a/src/dircond.h b/src/dircond.h
index 6b4da6b..30f520b 100644
--- a/src/dircond.h
+++ b/src/dircond.h
@@ -23,6 +23,7 @@
#include <string.h>
#include <unistd.h>
#include <signal.h>
+#include <regex.h>
/* Generic (system-independent) event codes */
#define GENEV_CREATE 0x01
@@ -50,12 +51,24 @@ struct transtab {
int tok;
};
+#define PAT_GLOB 0
+#define PAT_REGEX 1
+
+struct filename_pattern {
+ int type;
+ union {
+ regex_t re;
+ char *glob;
+ } v;
+};
+
/* Handler structure */
struct handler {
struct handler *next;
event_mask ev_mask; /* Event mask */
+ struct grecs_list *fnames; /* File name patterns */
int flags; /* Handler flags */
- const char *prog; /* Handler program (no arguments allowed) */
+ const char *prog; /* Handler program (with eventual arguments) */
uid_t uid; /* Run as this user (unless 0) */
gid_t *gidv; /* Run with these groups' privileges */
size_t gidc; /* Number of elements in gidv */
@@ -79,6 +92,12 @@ struct dirwatcher {
#endif
};
+#define __cat2__(a,b) a ## b
+#define handler_matches_event(h,m,f,n) \
+ (((h)->ev_mask.__cat2__(m,_mask) & (f)) && \
+ filename_pattern_match((h)->fnames, n) == 0)
+
+
extern int foreground;
extern int debug_level;
extern int facility;
@@ -202,4 +221,7 @@ int sigv_set_all(void (*handler)(int), int sigc, int *sigv,
struct sigaction *retsa);
int sigv_set_tab(int sigc, struct sigtab *sigtab, struct sigaction *retsa);
int sigv_set_action_tab(int sigc, struct sigtab *sigtab, struct sigaction *sa);
+
+void filename_pattern_free(void *p);
+int filename_pattern_match(struct grecs_list *lp, const char *name);
diff --git a/src/ev_inotify.c b/src/ev_inotify.c
index 54eec28..953bfeb 100644
--- a/src/ev_inotify.c
+++ b/src/ev_inotify.c
@@ -140,7 +140,7 @@ process_event(struct inotify_event *ep)
filename = ep->name;
}
for (h = dp->handler_list; h; h = h->next) {
- if (h->ev_mask.sys_mask & ep->mask)
+ if (handler_matches_event(h, sys, ep->mask, filename))
run_handler(h, event_mask_init(&m,
ep->mask,
&h->ev_mask),
diff --git a/src/ev_kqueue.c b/src/ev_kqueue.c
index e9bf38a..1c8a063 100644
--- a/src/ev_kqueue.c
+++ b/src/ev_kqueue.c
@@ -202,7 +202,7 @@ process_event(struct kevent *ep)
filename = split_pathname(dp, &dirname);
for (h = dp->handler_list; h; h = h->next) {
- if (h->ev_mask.sys_mask & ep->fflags) {
+ if (handler_matches_event(h, sys, ep->fflags, filename)) {
run_handler(h,
event_mask_init(&m, ep->fflags, &h->ev_mask),
dirname, filename);
diff --git a/src/fnpat.c b/src/fnpat.c
new file mode 100644
index 0000000..8191705
--- /dev/null
+++ b/src/fnpat.c
@@ -0,0 +1,56 @@
+/* dircond - directory content watcher daemon
+ Copyright (C) 2012, 2013 Sergey Poznyakoff
+
+ Dircond 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.
+
+ Dircond 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 dircond. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "dircond.h"
+#include <fnmatch.h>
+#include <grecs.h>
+
+void
+filename_pattern_free(void *p)
+{
+ struct filename_pattern *pat = p;
+ switch (pat->type) {
+ case PAT_GLOB:
+ free(pat->v.glob);
+ break;
+ case PAT_REGEX:
+ regfree(&pat->v.re);
+ }
+ free(pat);
+}
+
+int
+filename_pattern_match(struct grecs_list *lp, const char *name)
+{
+ struct grecs_list_entry *ep;
+
+ if (!lp)
+ return 0;
+ for (ep = lp->head; ep; ep = ep->next) {
+ struct filename_pattern *pat = ep->data;
+
+ switch (pat->type) {
+ case PAT_GLOB:
+ if (fnmatch(pat->v.glob, name, FNM_PATHNAME) == 0)
+ return 0;
+ break;
+ case PAT_REGEX:
+ if (regexec(&pat->v.re, name, 0, NULL, 0) == 0)
+ return 0;
+ }
+ }
+ return 1;
+}
diff --git a/src/watcher.c b/src/watcher.c
index b1e9f8f..2c80a3a 100644
--- a/src/watcher.c
+++ b/src/watcher.c
@@ -287,7 +287,7 @@ deliver_ev_create(struct dirwatcher *dp, const char *name)
struct handler *h;
for (h = dp->handler_list; h; h = h->next) {
- if (h->ev_mask.gen_mask & GENEV_CREATE)
+ if (handler_matches_event(h, gen, GENEV_CREATE, name))
run_handler(h, &m, dp->dirname, name);
}
}
@@ -450,5 +450,3 @@ unsplit_pathname(struct dirwatcher *dp)
dp->split_p = NULL;
}
}
-
-

Return to:

Send suggestions and report system problems to the System administrator.