summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2016-08-20 09:24:41 (GMT)
committer Sergey Poznyakoff <gray@gnu.org>2016-08-25 19:29:47 (GMT)
commit46ae038f21a0bb8b8cfe5361df6439cf572ad2cc (patch) (side-by-side diff)
treeeaf78744a9e023fc8e1baadd8d5fff7f4f664cc3
parent4e4bacb23013880c5247ed53e81366f15d2a64ef (diff)
downloaddirevent-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.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--src/config.c127
-rw-r--r--src/direvent.h58
-rw-r--r--src/fnpat.c4
-rw-r--r--src/hashtab.c124
-rw-r--r--src/progman.c74
-rw-r--r--src/watcher.c89
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);
}

Return to:

Send suggestions and report system problems to the System administrator.