/* wydawca - automatic release submission daemon Copyright (C) 2007-2013 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 . */ #include "wydawca.h" #include static struct module *mod_head, *mod_tail; struct grecs_list *module_load_path, *module_prepend_load_path; static struct module * modlookup(const char *name) { struct module *p; for (p = mod_head; p; p = p->next) if (strcmp(p->name, name) == 0) return p;; return NULL; } static struct module * modinstall(const char *name, const char *path, grecs_locus_t *loc) { struct module *p; p = modlookup(name); if (p) { grecs_error(loc, 0, _("module %s already declared"), name); grecs_error(&p->locus, 0, _("previously declared here")); return NULL; } p = grecs_zalloc(sizeof(*p)); p->name = grecs_strdup(name); p->path = grecs_strdup(path); p->locus.beg.file = grecs_strdup(loc->beg.file); p->locus.beg.line = loc->beg.line; p->locus.beg.col = loc->beg.col; p->locus.end.file = grecs_strdup(loc->end.file); p->locus.end.line = loc->end.line; p->locus.end.col = loc->end.col; if (mod_tail) mod_tail->next = p; else mod_head = p; mod_tail = p; return p; } 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, int mustbe) { void *sym = lt_dlsym(mod->handle, name); if (!sym && mustbe) { grecs_error(&mod->locus, 0, _("module \"%s\" does not define symbol \"%s\""), mod->name, name); } return sym; } static int modload(struct module *mod, lt_dladvise advise) { lt_dlhandle handle = NULL; 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->open = resolve_sym(mod, "wy_open", 0); mod->close = resolve_sym(mod, "wy_close", 0); mod->config = resolve_sym(mod, "wy_config", 1); mod->notify = resolve_sym(mod, "wy_notify", 1); mod->flush = resolve_sym(mod, "wy_flush", 0); if (mod->open) { if (mod->open(mod->modinit)) { grecs_error(mod->modinit ? &mod->modinit->locus : NULL, 0, _("failed to initialize module %s"), mod->name); return 1; } } return 0; } static int conf_notification_modules(struct notification *np) { for (; np; np = np->next) { if (np->modname) { struct module *mod = modlookup(np->modname); if (!mod) { wy_log(LOG_ERR, "%s: no such module", np->modname); return 1; } if (!np->modcfg && mod->config && np->modnode) { np->modcfg = mod->config(np->modnode); if (!np->modcfg) { wy_log(LOG_ERR, "%s: failed to configure", np->modname); return 1; } } } } return 0; } static int spoolmodcfg(struct spool *spool, void *unused) { return conf_notification_modules(spool->notification); } void modules_load() { lt_dladvise advise = NULL; struct grecs_list_entry *ep; struct module *mod; if (lt_dlinit()) { wy_log(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)) wy_log(LOG_ERR, "lt_dladvise_init: %s", lt_dlerror()); else { if (lt_dladvise_ext(&advise)) wy_log(LOG_ERR, "lt_dladvise_ext: %s", lt_dlerror()); if (lt_dladvise_global(&advise)) wy_log(LOG_ERR, "lt_dladvise_global: %s", lt_dlerror()); } for (mod = mod_head; mod; mod = mod->next) { if (modload(mod, advise)) exit(EX_UNAVAILABLE); } lt_dladvise_destroy(&advise); if (for_each_spool(spoolmodcfg, NULL)) { wy_log(LOG_CRIT, _("some modules failed to configure, exiting")); exit(EX_UNAVAILABLE); } conf_notification_modules(default_notification); } void modules_close() { struct module *mod; for (mod = mod_head; mod; mod = mod->next) { if (mod->close) mod->close(); lt_dlclose(mod->handle); } } int module_set_init(const char *name, grecs_node_t *node) { struct module *mod = modlookup(name); if (!mod) return 1; mod->modinit = node; return 0; } void module_notify(const char *name, void *modcfg, enum wy_event ev, struct wy_triplet *trp) { struct module *mod = modlookup(name); if (!mod) { wy_log(LOG_ERR, "no such module: %s", name); return; } if (mod->notify) mod->notify(modcfg, ev, trp); } void module_flush(const char *name, void *modcfg) { struct module *mod = modlookup(name); if (!mod) { wy_log(LOG_ERR, "no such module: %s", name); return; } if (mod->flush) mod->flush(modcfg); } void module_help(const char *modname) { lt_dladvise advise = NULL; struct module mod; void (*help)(void); if (lt_dlinit()) { wy_log(LOG_ERR, _("failed to initialize libtool")); exit(EX_UNAVAILABLE); } lt_dladdsearchdir(WYDAWCA_MODDIR); if (lt_dladvise_init(&advise)) wy_log(LOG_ERR, "lt_dladvise_init: %s", lt_dlerror()); else { if (lt_dladvise_ext(&advise)) wy_log(LOG_ERR, "lt_dladvise_ext: %s", lt_dlerror()); if (lt_dladvise_global(&advise)) wy_log(LOG_ERR, "lt_dladvise_global: %s", lt_dlerror()); } memset(&mod, 0, sizeof(mod)); mod.path = modname; if (modload(&mod, advise)) exit(EX_UNAVAILABLE); lt_dladvise_destroy(&advise); help = resolve_sym(&mod, "wy_help", 0); if (!help) wy_log(LOG_NOTICE, "no help for %s", modname); else help(); lt_dlclose(mod.handle); }