diff options
Diffstat (limited to 'src/module.c')
-rw-r--r-- | src/module.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/src/module.c b/src/module.c new file mode 100644 index 0000000..41bbb39 --- /dev/null +++ b/src/module.c @@ -0,0 +1,236 @@ +/* wydawca - automatic release submission daemon + Copyright (C) 2007-2012 Sergey Poznyakoff + + Wydawca 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. + + Wydawca 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 wydawca. If not, see <http://www.gnu.org/licenses/>. */ + +#include "wydawca.h" +#include <ltdl.h> + +static struct grecs_symtab *modtab; +struct grecs_list *module_load_path, *module_prepend_load_path; + +static void +modfree(void *p) +{ + struct module *mod = p; + free(mod->locus.beg.file); + free(mod->locus.end.file); + free(mod->path); + free(mod->name); + free(mod); +} + +static struct module * +modinstall(const char *name, const char *path, grecs_locus_t *loc) +{ + struct module key; + struct module *ent; + int install = 1; + + if (!modtab) { + modtab = grecs_symtab_create(sizeof(struct module), + NULL, + NULL, + NULL, + NULL, + modfree); + if (!modtab) + grecs_alloc_die(); + } + + key.name = (char*) name; + ent = grecs_symtab_lookup_or_install(modtab, &key, &install); + if (!ent) + grecs_alloc_die(); + if (install == 0) { + grecs_error(loc, 0, _("module %s already declared"), name); + grecs_error(&ent->locus, 0, _("previously declared here")); + return NULL; + } + ent->path = grecs_strdup(path); + ent->locus.beg.file = grecs_strdup(loc->beg.file); + ent->locus.beg.line = loc->beg.line; + ent->locus.beg.col = loc->beg.col; + ent->locus.end.file = grecs_strdup(loc->end.file); + ent->locus.end.line = loc->end.line; + ent->locus.end.col = loc->end.col; + return ent; +} + +static struct module * +modlookup(const char *name) +{ + struct module key; + + if (!modtab) + return NULL; + + key.name = (char*) name; + return grecs_symtab_lookup_or_install(modtab, &key, NULL); +} + +int +cb_module(enum grecs_callback_command cmd, grecs_node_t *node, + void *varptr, void *cb_data) +{ + grecs_value_t *name; + grecs_value_t *path; + grecs_locus_t *locus = &node->locus; + grecs_value_t *value = node->v.value; + + if (cmd != grecs_callback_set_value) { + grecs_error(locus, 0, _("Unexpected block statement")); + return 1; + } + + if (!(name = get_arg(value, 0, GRECS_TYPE_STRING))) + return 1; + if (!(path = get_arg(value, 1, GRECS_TYPE_STRING))) + return 1; + + modinstall(name->v.string, path->v.string, locus); + + return 0; +}; + +static void * +resolve_sym(struct module *mod, const char *name) +{ + void *sym = lt_dlsym(mod->handle, name); + if (!sym) { + grecs_error(&mod->locus, 0, + _("module \"%s\" does not define symbol \"%s\""), + mod->name, name); + } + return sym; +} + +static int +modload(void *sym, void *data) +{ + struct module *mod = sym; + lt_dlhandle handle = NULL; + lt_dladvise advise = data; + + if (mod->handle) { + grecs_error(&mod->locus, 0, _("already loaded")); + return 0; + } + + handle = lt_dlopenadvise(mod->path, advise); + + if (!handle) { + grecs_error(&mod->locus, 0, + _("cannot load module %s: %s"), mod->path, + lt_dlerror()); + return 1; + } + mod->handle = handle; + mod->config = resolve_sym(mod, "config"); + mod->notify = resolve_sym(mod, "notify"); + + return 0; +} + +static int +spoolmodcfg(struct spool *spool, void *unused) +{ + struct notification *np; + + for (np = spool->notification; np; np = np->next) { + if (np->modname) { + struct module *mod = modlookup(np->modname); + if (!mod) { + logmsg(LOG_ERR, "spool %s: no such module: %s", + spool->tag, np->modname); + return 1; + } + if (mod->config) { + np->modcfg = mod->config(np->modnode); + if (!np->modcfg) { + logmsg(LOG_ERR, + "spool %s: failed to configure " + "module \"%s\"", + spool->tag, np->modname); + return 1; + } + } + } + } + return 0; +} + +void +modules_load() +{ + lt_dladvise advise = NULL; + struct grecs_list_entry *ep; + + if (lt_dlinit()) { + logmsg(LOG_ERR, _("failed to initialize libtool")); + return; + } + + /* Prepare load path */ + if (module_prepend_load_path) + for (ep = module_prepend_load_path->head; ep; ep = ep->next) + lt_dladdsearchdir(ep->data); + lt_dladdsearchdir(WYDAWCA_MODDIR); + if (module_load_path) + for (ep = module_load_path->head; ep; ep = ep->next) + lt_dladdsearchdir(ep->data); + + + if (lt_dladvise_init(&advise)) + logmsg(LOG_ERR, "lt_dladvise_init: %s", lt_dlerror()); + else { + if (lt_dladvise_ext(&advise)) + logmsg(LOG_ERR, "lt_dladvise_ext: %s", lt_dlerror()); + if (lt_dladvise_global(&advise)) + logmsg(LOG_ERR, "lt_dladvise_global: %s", + lt_dlerror()); + } + if (grecs_symtab_enumerate(modtab, modload, NULL)) { + logmsg(LOG_CRIT, _("some modules failed to load, exiting")); + exit(EX_UNAVAILABLE); + } + lt_dladvise_destroy(&advise); + + if (for_each_spool(spoolmodcfg, NULL)) { + logmsg(LOG_CRIT, + _("some modules failed to configure, exiting")); + exit(EX_UNAVAILABLE); + } +} + +static char * +getfn(void *trp, const char *fmt) +{ + return triplet_expand_param(fmt, trp); +} + +void +module_notify(const char *name, void *modcfg, + enum notification_event ev, struct file_triplet *trp) +{ + struct module *mod = modlookup(name); + + if (!mod) { + logmsg(LOG_ERR, "no such module: %s", name); + return; + } + if (mod->notify) + mod->notify(modcfg, ev, getfn, trp); +} + |