aboutsummaryrefslogtreecommitdiff
path: root/src/handler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/handler.c')
-rw-r--r--src/handler.c288
1 files changed, 288 insertions, 0 deletions
diff --git a/src/handler.c b/src/handler.c
new file mode 100644
index 0000000..0e86a44
--- /dev/null
+++ b/src/handler.c
@@ -0,0 +1,288 @@
+/* direvent - directory content watcher daemon
+ Copyright (C) 2012-2016 Sergey Poznyakoff
+
+ Direvent 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.
+
+ Direvent 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 direvent. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "direvent.h"
+#include <grecs.h>
+
+struct handler *
+handler_alloc(enum handler_type type)
+{
+ struct handler *hp = ecalloc(1, sizeof(*hp));
+ hp->type = type;
+ hp->refcnt = 0;
+ return hp;
+}
+
+struct handler *
+handler_copy(struct handler *orig)
+{
+ struct handler *hp = emalloc(sizeof(*hp));
+ *hp = *orig;
+ hp->refcnt = 0;
+ return hp;
+}
+
+/* Reallocate environment of handler HP to accomodate COUNT more
+ entries (not bytes) plus a final NULL entry.
+
+ Return offset of the first unused entry.
+*/
+size_t
+handler_envrealloc(struct handler *hp, size_t count)
+{
+ size_t i;
+
+ if (!hp->prog_env) {
+ hp->prog_env = ecalloc(count + 1, sizeof(hp->prog_env[0]));
+ i = 0;
+ } else {
+ for (i = 0; hp->prog_env[i]; i++)
+ ;
+ 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;
+}
+
+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 void
+handler_ref(struct handler *hp)
+{
+ ++hp->refcnt;
+}
+
+static void
+envfree(char **env)
+{
+ int i;
+
+ if (!env)
+ return;
+ for (i = 0; env[i]; i++)
+ free(env[i]);
+ free(env);
+}
+
+void
+handler_free(struct handler *hp)
+{
+ grecs_list_free(hp->fnames);
+ 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) {
+ handler_free(hp);
+ free(hp);
+ }
+}
+
+/* Handler lists */
+
+static void
+handler_listent_free(void *p)
+{
+ struct handler *hp = p;
+ handler_unref(hp);
+}
+
+struct handler_list {
+ size_t refcnt;
+ grecs_list_ptr_t list;
+ struct handler_iterator *itr_chain;
+};
+
+struct handler_iterator {
+ struct handler_iterator *prev, *next;
+ handler_list_t hlist;
+ struct grecs_list_entry *ent;
+ int advanced;
+};
+
+static struct handler_iterator *itr_avail;
+
+struct handler *
+handler_itr_first(struct dirwatcher *dwp, handler_iterator_t *ret_itr)
+{
+ struct handler_iterator *itr;
+
+ if (!dwp->handler_list)
+ return NULL;
+
+ if (itr_avail) {
+ itr = itr_avail;
+ itr_avail = itr->next;
+ if (itr_avail)
+ itr_avail->prev = NULL;
+ } else
+ itr = emalloc(sizeof *itr);
+
+ itr->prev = NULL;
+ itr->next = dwp->handler_list->itr_chain;
+ itr->hlist = dwp->handler_list;
+ if (dwp->handler_list->itr_chain)
+ dwp->handler_list->itr_chain->prev = itr;
+ dwp->handler_list->itr_chain = itr;
+
+ itr->ent = dwp->handler_list->list->head;
+ itr->advanced = 0;
+ *ret_itr = itr;
+ return handler_itr_current(itr);
+}
+
+struct handler *
+handler_itr_next(handler_iterator_t *pitr)
+{
+ struct handler_iterator *itr;
+
+ if (!pitr || (itr = *pitr) == NULL)
+ return NULL;
+ if (itr->advanced)
+ itr->advanced = 0;
+ else
+ itr->ent = itr->ent->next;
+
+ if (!itr->ent) {
+ /* Remove from iterator chain */
+ struct handler_iterator *p;
+ if ((p = itr->prev) != NULL)
+ p->next = itr->next;
+ else
+ itr->hlist->itr_chain = itr->next;
+ if ((p = itr->next) != NULL)
+ p->prev = itr->prev;
+
+ /* Add to the available chain */
+ if (itr_avail)
+ itr_avail->prev = itr;
+ itr->prev = NULL;
+ itr->next = itr_avail;
+ itr->hlist = NULL;
+ itr_avail = itr;
+ *pitr = NULL;
+ return NULL;
+ }
+ return handler_itr_current(itr);
+}
+
+struct handler *
+handler_itr_current(handler_iterator_t itr)
+{
+ if (!itr)
+ return NULL;
+ return itr->ent ? itr->ent->data : NULL;
+}
+
+handler_list_t
+handler_list_create(void)
+{
+ handler_list_t hlist = emalloc(sizeof(*hlist));
+ hlist->list = grecs_list_create();
+ hlist->list->free_entry = handler_listent_free;
+ hlist->refcnt = 1;
+ hlist->itr_chain = NULL;
+ return hlist;
+}
+
+size_t
+handler_list_size(handler_list_t hlist)
+{
+ return grecs_list_size(hlist->list);
+}
+
+handler_list_t
+handler_list_copy(handler_list_t orig)
+{
+ if (!orig)
+ return handler_list_create();
+ ++orig->refcnt;
+ return orig;
+}
+
+void
+handler_list_unref(handler_list_t hlist)
+{
+ if (hlist) {
+ if (--hlist->refcnt == 0) {
+ grecs_list_free(hlist->list);
+ free(hlist);
+ }
+ }
+}
+
+void
+handler_list_append(handler_list_t hlist, struct handler *hp)
+{
+ handler_ref(hp);
+ grecs_list_append(hlist->list, hp);
+}
+
+void
+handler_list_remove(handler_list_t hlist, struct handler *hp)
+{
+ struct grecs_list_entry *ep;
+ for (ep = hlist->list->head; ep; ep = ep->next)
+ if (ep->data == hp)
+ break;
+ if (!ep)
+ abort();
+
+ if (hlist->itr_chain) {
+ struct handler_iterator *itr;
+
+ for (itr = hlist->itr_chain; itr; itr = itr->next)
+ if (itr->ent == ep) {
+ itr->ent = ep->next;
+ itr->advanced = 1;
+ }
+ }
+
+ grecs_list_remove_entry(hlist->list, ep);
+ if (grecs_list_size(hlist->list) == 0)
+ /* Remove watchers that don't have handlers */
+ dirwatcher_gc();
+}

Return to:

Send suggestions and report system problems to the System administrator.