/* 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);
}