diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2016-08-20 12:24:41 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2016-08-25 22:29:47 +0300 |
commit | 46ae038f21a0bb8b8cfe5361df6439cf572ad2cc (patch) | |
tree | eaf78744a9e023fc8e1baadd8d5fff7f4f664cc3 | |
parent | 4e4bacb23013880c5247ed53e81366f15d2a64ef (diff) | |
download | direvent-46ae038f21a0bb8b8cfe5361df6439cf572ad2cc.tar.gz direvent-46ae038f21a0bb8b8cfe5361df6439cf572ad2cc.tar.bz2 |
Initial preparation for having distinct handler types.
* src/direvent.h (pattern_type): New enum, instead of
PAT_* defines.
(filename_pattern): use enum pattern_type.
(handler_type): New enum.
(handler): Can be of two types: HANDLER_EXTERN, which handles
external programs and is equivalent to the prior content of
the structure, and HANDLER_SENTINEL, which is planned to be
a built-in handler for configuring watchpoints for newly created
directories. All uses changed.
(dirwatcher) <refcnt>: Change type to size_t.
(handler_alloc,handler_add_pattern)
(handler_add_exact_filename): New protos.
* src/hashtab.c: Provisions for safe adding and removing
from the hashtable when iterating over it.
(hashtab) <flags>: Removed.
<itr_level, list_new, list_del>: New members.
(hashtab_remove): When iterating, place pointer to the removed
entry in list_del instead of actually removing it.
(hashtab_lookup_or_install): When iterating, add a pointer to
the newly created entry to the list_new list, instead of adding
it immediately.
(hashtab_foreach): Flush list_del and list_new at the end of
the topmost iteration.
* src/config.c (handler_alloc): Rename to handler_copy
(handler_alloc): New function.
(handler_free): Two types of handlers.
(handler_add_pattern)
(handler_add_exact_filename): New functions.
(file_name_pattern): Takes struct handler * as its
first argument.
(cb_file_pattern): Change accordingly.
* src/fnpat.c (filename_pattern_free): Handle PAT_EXACT.
(filename_pattern_match): Likewise.
* src/progman.c (run_handler): Two types of handlers.
* src/watcher.c (dirwatcher_install): Restore missing gettext
marker.
Allocate handler_list.
(dirwatcher_init): Convert non-directory watchpoints to directory
ones.
-rw-r--r-- | src/config.c | 127 | ||||
-rw-r--r-- | src/direvent.h | 58 | ||||
-rw-r--r-- | src/fnpat.c | 4 | ||||
-rw-r--r-- | src/hashtab.c | 124 | ||||
-rw-r--r-- | src/progman.c | 74 | ||||
-rw-r--r-- | src/watcher.c | 89 |
6 files changed, 378 insertions, 98 deletions
diff --git a/src/config.c b/src/config.c index e98dc0f..3a4878c 100644 --- a/src/config.c +++ b/src/config.c @@ -143,14 +143,23 @@ static struct grecs_keyword syslog_kw[] = { N_("Prefix each message with its priority"), grecs_type_bool, GRECS_DFLT, &syslog_include_prio }, { NULL }, }; +struct handler * +handler_alloc(enum handler_type type) +{ + struct handler *hp = ecalloc(1, sizeof(*hp)); + hp->type = type; + hp->refcnt = 0; + return hp; +} + static struct handler * -handler_alloc(struct handler *orig) +handler_copy(struct handler *orig) { struct handler *hp = emalloc(sizeof(*hp)); *hp = *orig; hp->refcnt = 0; return hp; } @@ -180,32 +189,38 @@ envfree(char **env) */ static size_t handler_envrealloc(struct handler *hp, size_t count) { size_t i; - if (!hp->env) { - hp->env = ecalloc(count + 1, sizeof(hp->env[0])); + if (!hp->prog_env) { + hp->prog_env = ecalloc(count + 1, sizeof(hp->prog_env[0])); i = 0; } else { - for (i = 0; hp->env[i]; i++) + for (i = 0; hp->prog_env[i]; i++) ; - hp->env = erealloc(hp->env, - (i + count + 1) * sizeof(hp->env[0])); - memset(hp->env + i, 0, (count + 1) * sizeof(hp->env[0])); + hp->prog_env = erealloc(hp->prog_env, + (i + count + 1) * sizeof(hp->prog_env[0])); + memset(hp->prog_env + i, 0, (count + 1) * sizeof(hp->prog_env[0])); } return i; } static void handler_free(struct handler *hp) { grecs_list_free(hp->fnames); - free(hp->prog); - free(hp->gidv); - envfree(hp->env); + switch (hp->type) { + case HANDLER_EXTERN: + free(hp->prog_command); + free(hp->prog_gidv); + envfree(hp->prog_env); + break; + case HANDLER_SENTINEL: + dirwatcher_unref(hp->sentinel_watcher); + } } static void handler_unref(struct handler *hp) { if (hp && --hp->refcnt) { @@ -287,25 +302,25 @@ void direvent_handler_list_append(direvent_handler_list_t hlist, struct handler *hp) { handler_ref(hp); grecs_list_append(hlist->list, hp); } - struct eventconf { struct grecs_list *pathlist; struct handler handler; }; static struct eventconf eventconf; static void eventconf_init(void) { - memset(&eventconf, 0, sizeof eventconf); - eventconf.handler.timeout = DEFAULT_TIMEOUT; + memset(&eventconf, 0, sizeof eventconf); + eventconf.handler.type = HANDLER_EXTERN; + eventconf.handler.prog_timeout = DEFAULT_TIMEOUT; } static void eventconf_free(void) { grecs_list_free(eventconf.pathlist); @@ -313,13 +328,13 @@ eventconf_free(void) } void eventconf_flush(grecs_locus_t *loc) { struct grecs_list_entry *ep; - struct handler *hp = handler_alloc(&eventconf.handler); + struct handler *hp = handler_copy(&eventconf.handler); for (ep = eventconf.pathlist->head; ep; ep = ep->next) { struct pathent *pe = ep->data; struct dirwatcher *dwp; int isnew; @@ -328,14 +343,12 @@ eventconf_flush(grecs_locus_t *loc) abort(); if (!isnew && dwp->depth != pe->depth) grecs_error(loc, 0, _("%s: recursion depth does not match previous definition"), pe->path); dwp->depth = pe->depth; - if (!dwp->handler_list) - dwp->handler_list = direvent_handler_list_create(); direvent_handler_list_append(dwp->handler_list, hp); } grecs_list_free(eventconf.pathlist); eventconf_init(); } @@ -351,13 +364,13 @@ cb_watcher(enum grecs_callback_command cmd, grecs_node_t *node, break; case grecs_callback_section_end: if (!eventconf.pathlist) { grecs_error(&node->locus, 0, _("no paths configured")); ++err; } - if (!eventconf.handler.prog) { + if (!eventconf.handler.prog_command) { grecs_error(&node->locus, 0, _("no command configured")); ++err; } if (evtnullp(&eventconf.handler.ev_mask)) evtsetall(&eventconf.handler.ev_mask); @@ -602,15 +615,15 @@ cb_user(enum grecs_callback_command cmd, grecs_node_t *node, return 1; } gid = gr->gr_gid; } else gid = pw->pw_gid; - eventconf.handler.uid = pw->pw_uid; + eventconf.handler.prog_uid = pw->pw_uid; get_user_groups(uv->v.string, gid, - &eventconf.handler.gidc, &eventconf.handler.gidv); + &eventconf.handler.prog_gidc, &eventconf.handler.prog_gidv); return 0; } static int cb_option(enum grecs_callback_command cmd, grecs_node_t *node, @@ -627,21 +640,21 @@ cb_option(enum grecs_callback_command cmd, grecs_node_t *node, for (ep = val->v.list->head; ep; ep = ep->next) { grecs_value_t *vp = ep->data; if (assert_grecs_value_type(&vp->locus, vp, GRECS_TYPE_STRING)) return 1; if (strcmp(vp->v.string, "nowait") == 0) - eventconf.handler.flags |= HF_NOWAIT; + eventconf.handler.prog_flags |= HF_NOWAIT; else if (strcmp(vp->v.string, "wait") == 0) - eventconf.handler.flags &= ~HF_NOWAIT; + eventconf.handler.prog_flags &= ~HF_NOWAIT; else if (strcmp(vp->v.string, "stdout") == 0) - eventconf.handler.flags |= HF_STDOUT; + eventconf.handler.prog_flags |= HF_STDOUT; else if (strcmp(vp->v.string, "stderr") == 0) - eventconf.handler.flags |= HF_STDERR; + eventconf.handler.prog_flags |= HF_STDERR; else if (strcmp(vp->v.string, "shell") == 0) - eventconf.handler.flags |= HF_SHELL; + eventconf.handler.prog_flags |= HF_SHELL; else grecs_error(&vp->locus, 0, _("unrecognized option")); } return 0; } @@ -658,44 +671,71 @@ cb_environ(enum grecs_callback_command cmd, grecs_node_t *node, switch (val->type) { case GRECS_TYPE_STRING: if (assert_grecs_value_type(&val->locus, val, GRECS_TYPE_STRING)) return 1; i = handler_envrealloc(&eventconf.handler, 1); - eventconf.handler.env[i] = estrdup(val->v.string); - eventconf.handler.env[i+1] = NULL; + eventconf.handler.prog_env[i] = estrdup(val->v.string); + eventconf.handler.prog_env[i+1] = NULL; break; case GRECS_TYPE_ARRAY: j = handler_envrealloc(&eventconf.handler, val->v.arg.c); for (i = 0; i < val->v.arg.c; i++, j++) { if (assert_grecs_value_type(&val->v.arg.v[i]->locus, val->v.arg.v[i], GRECS_TYPE_STRING)) return 1; - eventconf.handler.env[j] = estrdup(val->v.arg.v[i]->v.string); + eventconf.handler.prog_env[j] = estrdup(val->v.arg.v[i]->v.string); } - eventconf.handler.env[j] = NULL; + eventconf.handler.prog_env[j] = NULL; break; case GRECS_TYPE_LIST: j = handler_envrealloc(&eventconf.handler, val->v.list->count); for (ep = val->v.list->head; ep; ep = ep->next, j++) { grecs_value_t *vp = ep->data; if (assert_grecs_value_type(&vp->locus, vp, GRECS_TYPE_STRING)) return 1; - eventconf.handler.env[j] = estrdup(vp->v.string); + eventconf.handler.prog_env[j] = estrdup(vp->v.string); } - eventconf.handler.env[j] = NULL; + eventconf.handler.prog_env[j] = NULL; } return 0; } +void +handler_add_pattern(struct handler *hp, struct filename_pattern *pat) +{ + if (!hp->fnames) { + hp->fnames = grecs_list_create(); + hp->fnames->free_entry = filename_pattern_free; + } + grecs_list_append(hp->fnames, pat); +} + +void +handler_add_exact_filename(struct handler *hp, const char *filename) +{ + struct filename_pattern *pat; + pat = emalloc(sizeof(*pat)); + pat->neg = 0; + pat->type = PAT_EXACT; + pat->v.glob = estrdup(filename); + handler_add_pattern(hp, pat); +} + +static int +is_glob(char const *str) +{ + return strcspn(str, "[]*?") < strlen(str); +} + static int -file_name_pattern(struct grecs_list *lp, grecs_value_t *val) +file_name_pattern(struct handler *handler, grecs_value_t *val) { char *arg; int rc; int flags = REG_EXTENDED|REG_NOSUB; struct filename_pattern *pat; @@ -745,53 +785,46 @@ file_name_pattern(struct grecs_list *lp, grecs_value_t *val) 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->type = is_glob(arg) ? PAT_GLOB : PAT_EXACT; pat->v.glob = estrdup(arg); } - grecs_list_append(lp, pat); + handler_add_pattern(handler, 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 handler *handler = varptr; 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); + file_name_pattern(handler, 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])) + if (file_name_pattern(handler, 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, + if (file_name_pattern(handler, (grecs_value_t *) ep->data)) break; break; } return 0; @@ -802,21 +835,21 @@ static struct grecs_keyword watcher_kw[] = { grecs_type_string, GRECS_DFLT, &eventconf.pathlist, 0, cb_path }, { "event", NULL, N_("Events to watch for"), grecs_type_string, GRECS_LIST, &eventconf.handler.ev_mask, 0, cb_eventlist }, { "file", N_("regexp"), N_("Files to watch for"), - grecs_type_string, GRECS_LIST, &eventconf.handler.fnames, 0, + grecs_type_string, GRECS_LIST, &eventconf.handler, 0, cb_file_pattern }, { "command", NULL, N_("Command to execute on event"), - grecs_type_string, GRECS_DFLT, &eventconf.handler.prog }, + grecs_type_string, GRECS_DFLT, &eventconf.handler.prog_command }, { "user", N_("name"), N_("Run command as this user"), grecs_type_string, GRECS_DFLT, NULL, 0, cb_user }, { "timeout", N_("seconds"), N_("Timeout for the command"), - grecs_type_uint, GRECS_DFLT, &eventconf.handler.timeout }, + grecs_type_uint, GRECS_DFLT, &eventconf.handler.prog_timeout }, { "option", NULL, N_("List of additional options"), grecs_type_string, GRECS_LIST, NULL, 0, cb_option }, { "environ", N_("<arg: string> <arg: string>..."), N_("Modify environment"), grecs_type_string, GRECS_DFLT, NULL, 0, diff --git a/src/direvent.h b/src/direvent.h index de73740..75f3708 100644 --- a/src/direvent.h +++ b/src/direvent.h @@ -34,16 +34,16 @@ #define GENEV_CREATE 0x01 #define GENEV_WRITE 0x02 #define GENEV_ATTRIB 0x04 #define GENEV_DELETE 0x08 /* Handler flags. */ -#define HF_NOWAIT 0x01 /* Don't wait for termination */ -#define HF_STDOUT 0x02 /* Capture stdout */ -#define HF_STDERR 0x04 /* Capture stderr */ -#define HF_SHELL 0x08 /* Call program via /bin/sh -c */ +#define HF_NOWAIT 0x01 /* Don't wait for termination */ +#define HF_STDOUT 0x02 /* Capture stdout */ +#define HF_STDERR 0x04 /* Capture stderr */ +#define HF_SHELL 0x08 /* Call program via /bin/sh -c */ #ifndef DEFAULT_TIMEOUT # define DEFAULT_TIMEOUT 5 #endif typedef struct { @@ -54,44 +54,69 @@ typedef struct { /* Event description */ struct transtab { char *name; int tok; }; -#define PAT_GLOB 0 -#define PAT_REGEX 1 +enum pattern_type { + PAT_EXACT, + PAT_GLOB, + PAT_REGEX +}; struct filename_pattern { - int type; + enum pattern_type type; int neg; union { regex_t re; char *glob; } v; }; +enum handler_type { + HANDLER_EXTERN, + HANDLER_SENTINEL +}; + /* Handler structure */ struct handler { size_t refcnt; /* Reference counter */ event_mask ev_mask; /* Event mask */ struct grecs_list *fnames; /* File name patterns */ - int flags; /* Handler flags */ - 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 */ - unsigned timeout; /* Handler timeout */ - char **env; /* Environment */ + enum handler_type type; + union { + struct { + int flags; /* Handler flags */ + char *command; /* Handler command (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 */ + unsigned timeout; /* Handler timeout */ + char **env; /* Environment */ + } prog; + struct { + struct dirwatcher *watcher; + } sentinel; + } v; +#define prog_flags v.prog.flags +#define prog_command v.prog.command +#define prog_uid v.prog.uid +#define prog_gidv v.prog.gidv +#define prog_gidc v.prog.gidc +#define prog_timeout v.prog.timeout +#define prog_env v.prog.env +#define sentinel_watcher v.sentinel.watcher }; typedef struct direvent_handler_list *direvent_handler_list_t; typedef struct grecs_list_entry *direvent_handler_iterator_t; /* A directory watcher is described by the following structure */ struct dirwatcher { - int refcnt; + size_t refcnt; int wd; /* Watch descriptor */ struct dirwatcher *parent; /* Points to the parent watcher. NULL for top-level watchers */ char *dirname; /* Pathname being watched */ direvent_handler_list_t handler_list;/* List of handlers */ int depth; /* Recursion depth */ @@ -107,12 +132,15 @@ struct dirwatcher { #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) +struct handler *handler_alloc(enum handler_type type); +void handler_add_pattern(struct handler *hp, struct filename_pattern *pat); +void handler_add_exact_filename(struct handler *hp, const char *filename); extern int foreground; extern int debug_level; extern int facility; extern char *tag; extern int syslog_include_prio; diff --git a/src/fnpat.c b/src/fnpat.c index 2ac4d11..775a558 100644 --- a/src/fnpat.c +++ b/src/fnpat.c @@ -20,12 +20,13 @@ void filename_pattern_free(void *p) { struct filename_pattern *pat = p; switch (pat->type) { + case PAT_EXACT: case PAT_GLOB: free(pat->v.glob); break; case PAT_REGEX: regfree(&pat->v.re); } @@ -41,12 +42,15 @@ filename_pattern_match(struct grecs_list *lp, const char *name) return 0; for (ep = lp->head; ep; ep = ep->next) { struct filename_pattern *pat = ep->data; int rc; switch (pat->type) { + case PAT_EXACT: + rc = strcmp(pat->v.glob, name); + break; case PAT_GLOB: rc = fnmatch(pat->v.glob, name, FNM_PATHNAME); break; case PAT_REGEX: rc = regexec(&pat->v.re, name, 0, NULL, 0); break; diff --git a/src/hashtab.c b/src/hashtab.c index 1c7b453..600b345 100644 --- a/src/hashtab.c +++ b/src/hashtab.c @@ -24,26 +24,88 @@ static unsigned int hash_size[] = { 32831, 65647, 131231, 262469, 524921, 1049849, 2099707 }; /* |max_rehash| keeps the number of entries in |hash_size| table. */ static unsigned int max_rehash = sizeof(hash_size) / sizeof(hash_size[0]); +struct hashent_list_entry { + struct hashent_list_entry *prev, *next; + struct hashent *ent; +}; + +struct hashent_list { + struct hashent_list_entry *head, *tail; +}; + struct hashtab { - int flags; unsigned int hash_num; /* Index to hash_size table */ size_t elsize; /* Size of an element */ size_t elcount; /* Number of elements in use */ struct hashent **tab; unsigned (*hash_fun)(void *, unsigned long hash_num); int (*cmp_fun)(const void *, const void *); int (*copy_fun)(void *, void *); void *(*hashent_alloc_fun)(size_t size); void (*hashent_free_fun) (void *); + + unsigned int itr_level; + struct hashent_list list_new, list_del; }; + +static void +hashent_list_init(struct hashent_list *list) +{ + list->head = NULL; + list->tail = NULL; +} + +static int +hashent_list_append(struct hashent_list *list, struct hashent *ent) +{ + struct hashent_list_entry *hent = malloc(sizeof(*ent)); + if (!hent) + return -1; + hent->ent = ent; + hent->next = NULL; + hent->prev = list->tail; + if (list->tail) + list->tail->next = hent; + else + list->head = hent; + list->tail = hent; + return 0; +} static void +hashent_list_remove(struct hashent_list *list, struct hashent_list_entry *hent) +{ + struct hashent_list_entry *p; + if ((p = hent->prev)) + p->next = hent->next; + else + list->head = hent->next; + if ((p = hent->next)) + p->prev = hent->prev; + else + list->tail = hent->prev; + free(hent); +} + +static struct hashent_list_entry * +hashent_list_lookup(struct hashtab *st, struct hashent_list *list, + struct hashent *ent) +{ + struct hashent_list_entry *p; + for (p = list->head; p; p = p->next) { + if (st->cmp_fun(p->ent, ent) == 0) + return p; + } + return NULL; +} + +static void hashent_free(struct hashtab *st, void *ptr) { if (st->hashent_free_fun) st->hashent_free_fun(ptr); else free(ptr); @@ -149,12 +211,23 @@ hashtab_strerror(int rc) int hashtab_remove(struct hashtab *st, void *elt) { unsigned int pos, i, j, r; struct hashent *entry; + + if (st->itr_level) { + struct hashent_list_entry *hent; + hent = hashent_list_lookup(st, &st->list_new, elt); + if (hent) { + entry = hent->ent; + hashent_list_remove(&st->list_new, hent); + hashent_free(st, entry); + return 0; + } + } pos = st->hash_fun(elt, hash_size[st->hash_num]); for (i = pos; entry = st->tab[i];) { if (st->cmp_fun(entry, elt) == 0) break; if (++i >= hash_size[st->hash_num]) @@ -166,13 +239,19 @@ hashtab_remove(struct hashtab *st, void *elt) if (!entry) #if 0 return ENOENT; #else abort(); #endif - + + if (st->itr_level) { + if (hashent_list_append(&st->list_del, entry)) + return ENOMEM; + entry->used = 0; + } + hashent_free(st, entry); st->elcount--; for (;;) { st->tab[i] = NULL; j = i; @@ -247,17 +326,32 @@ hashtab_lookup_or_install(struct hashtab *st, void *key, int *install) if (install && *install == 1) { struct hashent *ent = hashent_alloc(st, key); if (!ent) { errno = ENOMEM; return NULL; } + if (st->itr_level) { + if (hashent_list_append(&st->list_new, ent)) { + int ec = errno; + hashent_free(st, ent); + errno = ec; + return NULL; + } + return ent; + } st->tab[i] = ent; st->elcount++; return ent; } else return st->tab[i]; + } else if (rc == ENOENT && st->itr_level) { + struct hashent_list_entry *hent; + hent = hashent_list_lookup(st, &st->list_new, key); + if (hent) + return hent->ent; + rc = ENOENT; } errno = rc; return NULL; } void @@ -319,20 +413,46 @@ int hashtab_foreach(struct hashtab *st, hashtab_enumerator_t fun, void *data) { unsigned i; if (!st) return 0; + + if (st->itr_level++ == 0) { + hashent_list_init(&st->list_new); + hashent_list_init(&st->list_del); + } + for (i = 0; i < hash_size[st->hash_num]; i++) { struct hashent *ep = st->tab[i]; if (ep) { int rc = fun(ep, data); if (rc) return rc; } } + + if (--st->itr_level == 0) { + while (st->list_del.head) { + struct hashent *ent = st->list_del.head->ent; + hashent_list_remove(&st->list_del, st->list_del.head); + hashtab_remove(st, ent); + } + + while (st->list_new.head) { + struct hashent *ent = st->list_new.head->ent; + unsigned i; + int install = 1; + if (hashtab_get_index(&i, st, ent, &install) == 0) { + st->tab[i] = ent; + st->elcount++; + } + hashent_list_remove(&st->list_new, st->list_new.head); + } + } + return 0; } size_t hashtab_count(struct hashtab *st) { diff --git a/src/progman.c b/src/progman.c index 4fff8bb..3b78874 100644 --- a/src/progman.c +++ b/src/progman.c @@ -245,31 +245,31 @@ process_timeouts() debug(2, ("end scanning process list")); } int switchpriv(struct handler *hp) { - if (hp->uid == 0 || hp->uid == getuid()) + if (hp->prog_uid == 0 || hp->prog_uid == getuid()) return 0; - if (setgroups(hp->gidc, hp->gidv) < 0) { + if (setgroups(hp->prog_gidc, hp->prog_gidv) < 0) { diag(LOG_CRIT, "setgroups: %s", strerror(errno)); return 1; } - if (setregid(hp->gidv[0], hp->gidv[0]) < 0) { + if (setregid(hp->prog_gidv[0], hp->prog_gidv[0]) < 0) { diag(LOG_CRIT, "setregid(%lu,%lu): %s", - (unsigned long) hp->gidv[0], - (unsigned long) hp->gidv[0], + (unsigned long) hp->prog_gidv[0], + (unsigned long) hp->prog_gidv[0], strerror(errno)); return 1; } - if (setreuid(hp->uid, hp->uid) < 0) { + if (setreuid(hp->prog_uid, hp->prog_uid) < 0) { diag(LOG_CRIT, "setreuid(%lu,%lu): %s", - (unsigned long) hp->uid, - (unsigned long) hp->uid, + (unsigned long) hp->prog_uid, + (unsigned long) hp->prog_uid, strerror(errno)); return 1; } return 0; } @@ -436,30 +436,30 @@ runcmd(const char *cmd, char **envhint, event_mask *event, const char *file, execve(argv[0], argv, environ_setup(envhint, kve)); diag(LOG_ERR, "execve: %s \"%s\": %s", argv[0], cmd, strerror(errno)); _exit(127); } -int -run_handler(struct handler *hp, event_mask *event, - const char *dirname, const char *file) +static int +run_handler_prog(struct handler *hp, event_mask *event, + const char *dirname, const char *file) { pid_t pid; int redir_fd[2] = { -1, -1 }; struct process *redir_proc[2] = { NULL, NULL }; struct process *p; - if (!hp->prog) + if (!hp->prog_command) return 0; - debug(1, (_("starting %s, dir=%s, file=%s"), hp->prog, dirname, file)); - if (hp->flags & HF_STDERR) - redir_fd[REDIR_ERR] = open_redirector(hp->prog, LOG_ERR, + debug(1, (_("starting %s, dir=%s, file=%s"), hp->prog_command, dirname, file)); + if (hp->prog_flags & HF_STDERR) + redir_fd[REDIR_ERR] = open_redirector(hp->prog_command, LOG_ERR, &redir_proc[REDIR_ERR]); - if (hp->flags & HF_STDOUT) - redir_fd[REDIR_OUT] = open_redirector(hp->prog, LOG_INFO, + if (hp->prog_flags & HF_STDOUT) + redir_fd[REDIR_OUT] = open_redirector(hp->prog_command, LOG_INFO, &redir_proc[REDIR_OUT]); pid = fork(); if (pid == -1) { diag(LOG_ERR, "fork: %s", strerror(errno)); close(redir_fd[REDIR_OUT]); @@ -500,42 +500,68 @@ run_handler(struct handler *hp, event_mask *event, } BIGFD_SET(2, fdset); } close_fds(fdset); alarm(0); signal_setup(SIG_DFL); - runcmd(hp->prog, hp->env, event, file, hp->flags & HF_SHELL); + runcmd(hp->prog_command, hp->prog_env, event, file, hp->prog_flags & HF_SHELL); } /* master */ debug(1, (_("%s running; dir=%s, file=%s, pid=%lu"), - hp->prog, dirname, file, (unsigned long)pid)); + hp->prog_command, dirname, file, (unsigned long)pid)); - p = register_process(PROC_HANDLER, pid, time(NULL), hp->timeout); + p = register_process(PROC_HANDLER, pid, time(NULL), hp->prog_timeout); if (redir_proc[REDIR_OUT]) { redir_proc[REDIR_OUT]->v.master = p; - redir_proc[REDIR_OUT]->timeout = hp->timeout; + redir_proc[REDIR_OUT]->timeout = hp->prog_timeout; } if (redir_proc[REDIR_ERR]) { redir_proc[REDIR_ERR]->v.master = p; - redir_proc[REDIR_ERR]->timeout = hp->timeout; + redir_proc[REDIR_ERR]->timeout = hp->prog_timeout; } memcpy(p->v.redir, redir_proc, sizeof(p->v.redir)); close(redir_fd[REDIR_OUT]); close(redir_fd[REDIR_ERR]); - if (hp->flags & HF_NOWAIT) { + if (hp->prog_flags & HF_NOWAIT) { return 0; } debug(1, (_("waiting for %s (%lu) to terminate"), - hp->prog, (unsigned long)pid)); + hp->prog_command, (unsigned long)pid)); while (time(NULL) - p->start < 2 * p->timeout) { sleep(1); process_cleanup(1); if (p->pid == 0) break; } return 0; } + +static int +run_sentinel(struct handler *hp) +{ + return dirwatcher_init(hp->sentinel_watcher); + //FIXME: Remove watcher +} + +int +run_handler(struct handler *hp, event_mask *event, + const char *dirname, const char *file) +{ + int rc; + switch (hp->type) { + case HANDLER_EXTERN: + rc = run_handler_prog(hp, event, dirname, file); + break; + case HANDLER_SENTINEL: + rc = run_sentinel(hp); + break; + default: + abort(); + } + return rc; +} + diff --git a/src/watcher.c b/src/watcher.c index d0ec081..8f79562 100644 --- a/src/watcher.c +++ b/src/watcher.c @@ -87,24 +87,25 @@ dirwatcher_install(const char *path, int *pnew) if (!nametab) { nametab = hashtab_create(sizeof(struct dwref), dwname_hash, dwname_cmp, dwname_copy, NULL, dwref_free); if (!nametab) { - diag(LOG_CRIT, N_("not enough memory")); + diag(LOG_CRIT, _("not enough memory")); exit(1); } } dwkey.dirname = (char*) path; key.dw = &dwkey; ent = hashtab_lookup_or_install(nametab, &key, &install); if (install) { dw = ecalloc(1, sizeof(*dw)); dw->dirname = estrdup(path); dw->wd = -1; + dw->handler_list = direvent_handler_list_create(); dw->refcnt++; ent->dw = dw; } if (!ent) abort(); /* FIXME */ if (pnew) @@ -151,23 +152,88 @@ dirwatcher_destroy(struct dirwatcher *dwp) if (hashtab_count(nametab) == 0) { diag(LOG_CRIT, _("no watchers left; exiting now")); stop = 1; } } +static int +convert_watcher(struct dirwatcher *dwp) +{ + char *dirname; + char *filename; + char *new_dirname; + struct handler *hp; + direvent_handler_iterator_t itr; + + for_each_handler(dwp, itr, hp) { + if (hp->fnames) { + /* FIXME: Error message */ + return 1; + } + } + + filename = split_pathname(dwp, &dirname); + for_each_handler(dwp, itr, hp) + handler_add_exact_filename(hp, filename); + + new_dirname = estrdup(dirname); + unsplit_pathname(dwp); + diag(LOG_NOTICE, _("watcher %s converted to %s"), + dwp->dirname, new_dirname); + + free(dwp->dirname); + dwp->dirname = new_dirname; + return 0; +} + +struct dirwatcher * +dirwatcher_install_sentinel(struct dirwatcher *dwp) +{ + struct dirwatcher *sent; + char *dirname; + char *filename; + struct handler *hp; + + filename = split_pathname(dwp, &dirname); + sent = dirwatcher_install(dirname, NULL); + hp = handler_alloc(HANDLER_SENTINEL); + getevt("CREATE", &hp->ev_mask); + hp->sentinel_watcher = dwp; + dirwatcher_ref(dwp); + handler_add_exact_filename(hp, filename); + direvent_handler_list_append(sent->handler_list, hp); + + unsplit_pathname(dwp); + diag(LOG_NOTICE, _("installing CREATE sentinel for %s"), dwp->dirname); + return sent; +} + int dirwatcher_init(struct dirwatcher *dwp) { + struct stat st; event_mask mask = { 0, 0 }; struct handler *hp; - direvent_handler_iterator_t itr; - + direvent_handler_iterator_t itr; int wd; debug(1, (_("creating watcher %s"), dwp->dirname)); + if (stat(dwp->dirname, &st)) { + if (errno == ENOENT) { + dwp = dirwatcher_install_sentinel(dwp); + } else { + diag(LOG_ERR, _("cannot set watcher on %s: %s"), + dwp->dirname, strerror(errno)); + return 1; + } + } else if (!S_ISDIR(st.st_mode)) { + diag(LOG_NOTICE, _("%s is a regular file"), dwp->dirname); + convert_watcher(dwp); + } + for_each_handler(dwp, itr, hp) { mask.sys_mask |= hp->ev_mask.sys_mask; mask.gen_mask |= hp->ev_mask.gen_mask; } wd = sysev_add_watch(dwp, mask); @@ -347,33 +413,36 @@ watch_subdirs(struct dirwatcher *parent, int notify) static int setwatcher(struct hashent *ent, void *data) { struct dwref *dwref = (struct dwref *) ent; struct dirwatcher *dwp = dwref->dw; - int *success = data; if (dwp->wd == -1 && dirwatcher_init(dwp) == 0) watch_subdirs(dwp, 0); - if (dwp->wd >= 0) - *success = 1; return 0; } +static int +checkwatcher(struct hashent *ent, void *data) +{ + struct dwref *dwref = (struct dwref *) ent; + struct dirwatcher *dwp = dwref->dw; + return dwp->wd >= 0; +} + void setup_watchers(void) { - int success = 0; - sysev_init(); if (hashtab_count(nametab) == 0) { diag(LOG_CRIT, _("no event handlers configured")); exit(1); } - hashtab_foreach(nametab, setwatcher, &success); - if (!success) { + hashtab_foreach(nametab, setwatcher, NULL); + if (!hashtab_foreach(nametab, checkwatcher, NULL)) { diag(LOG_CRIT, _("no event handlers installed")); exit(2); } } static int |