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 @@ -146,8 +146,17 @@ static struct grecs_keyword syslog_kw[] = { { 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; @@ -183,15 +192,15 @@ 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; } @@ -200,9 +209,15 @@ 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 @@ -290,7 +305,6 @@ direvent_handler_list_append(direvent_handler_list_t hlist, struct handler *hp) grecs_list_append(hlist->list, hp); } - struct eventconf { struct grecs_list *pathlist; struct handler handler; @@ -301,8 +315,9 @@ 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 @@ -316,7 +331,7 @@ 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; @@ -331,8 +346,6 @@ eventconf_flush(grecs_locus_t *loc) _("%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); @@ -354,7 +367,7 @@ cb_watcher(enum grecs_callback_command cmd, grecs_node_t *node, 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; @@ -605,9 +618,9 @@ cb_user(enum grecs_callback_command cmd, grecs_node_t *node, } 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; } @@ -630,15 +643,15 @@ cb_option(enum grecs_callback_command cmd, grecs_node_t *node, 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")); } @@ -661,8 +674,8 @@ cb_environ(enum grecs_callback_command cmd, grecs_node_t *node, 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: @@ -672,9 +685,9 @@ cb_environ(enum grecs_callback_command cmd, grecs_node_t *node, 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: @@ -684,15 +697,42 @@ cb_environ(enum grecs_callback_command cmd, grecs_node_t *node, 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; @@ -748,11 +788,11 @@ file_name_pattern(struct grecs_list *lp, grecs_value_t *val) 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; } @@ -762,33 +802,26 @@ 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; @@ -805,15 +838,15 @@ static struct grecs_keyword watcher_kw[] = { 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 }, diff --git a/src/direvent.h b/src/direvent.h index de73740..75f3708 100644 --- a/src/direvent.h +++ b/src/direvent.h @@ -37,10 +37,10 @@ #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 @@ -57,11 +57,14 @@ struct transtab { 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; @@ -69,18 +72,40 @@ struct filename_pattern { } 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; @@ -88,7 +113,7 @@ 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 */ @@ -110,6 +135,9 @@ struct dirwatcher { (((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; diff --git a/src/fnpat.c b/src/fnpat.c index 2ac4d11..775a558 100644 --- a/src/fnpat.c +++ b/src/fnpat.c @@ -23,6 +23,7 @@ filename_pattern_free(void *p) { struct filename_pattern *pat = p; switch (pat->type) { + case PAT_EXACT: case PAT_GLOB: free(pat->v.glob); break; @@ -44,6 +45,9 @@ filename_pattern_match(struct grecs_list *lp, const char *name) 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; diff --git a/src/hashtab.c b/src/hashtab.c index 1c7b453..600b345 100644 --- a/src/hashtab.c +++ b/src/hashtab.c @@ -27,8 +27,16 @@ static unsigned int hash_size[] = { /* |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 */ @@ -38,9 +46,63 @@ struct hashtab { 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) @@ -152,6 +214,17 @@ 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];) { @@ -169,7 +242,13 @@ hashtab_remove(struct hashtab *st, void *elt) #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--; @@ -250,11 +329,26 @@ hashtab_lookup_or_install(struct hashtab *st, void *key, int *install) 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; @@ -322,6 +416,12 @@ hashtab_foreach(struct hashtab *st, hashtab_enumerator_t fun, void *data) 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) { @@ -330,6 +430,26 @@ hashtab_foreach(struct hashtab *st, hashtab_enumerator_t fun, void *data) 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; } diff --git a/src/progman.c b/src/progman.c index 4fff8bb..3b78874 100644 --- a/src/progman.c +++ b/src/progman.c @@ -248,25 +248,25 @@ process_timeouts() 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; } @@ -439,24 +439,24 @@ runcmd(const char *cmd, char **envhint, event_mask *event, const char *file, _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(); @@ -503,34 +503,34 @@ run_handler(struct handler *hp, event_mask *event, 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); @@ -539,3 +539,29 @@ run_handler(struct handler *hp, event_mask *event, } 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 @@ -90,7 +90,7 @@ dirwatcher_install(const char *path, int *pnew) 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); } } @@ -102,6 +102,7 @@ dirwatcher_install(const char *path, int *pnew) dw = ecalloc(1, sizeof(*dw)); dw->dirname = estrdup(path); dw->wd = -1; + dw->handler_list = direvent_handler_list_create(); dw->refcnt++; ent->dw = dw; } @@ -154,17 +155,82 @@ dirwatcher_destroy(struct dirwatcher *dwp) } } +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; @@ -350,27 +416,30 @@ 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); } |