diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-12-27 11:09:23 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-12-27 14:12:45 +0200 |
commit | ee6ce9f5da1d780a9efe5a6126884d1f0ae1b5a2 (patch) | |
tree | 6395ce4d873cc317293e9d1929b12d4de89342fe /src | |
parent | e37d338c901a12d8b39481da8e5f437555705e16 (diff) | |
download | direvent-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.am | 1 | ||||
-rw-r--r-- | src/config.c | 107 | ||||
-rw-r--r-- | src/dircond.h | 24 | ||||
-rw-r--r-- | src/ev_inotify.c | 2 | ||||
-rw-r--r-- | src/ev_kqueue.c | 2 | ||||
-rw-r--r-- | src/fnpat.c | 56 | ||||
-rw-r--r-- | src/watcher.c | 4 |
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; } } - - |