config: dlopen() modules on-demand

TODO: optimizations and proper cleanup

* We currently reload the shared library for each *instance* of the
  module, and we do it twice; once when verifying, and once when
  instantiating.

* The shared libraries are never dlclosed()
This commit is contained in:
Daniel Eklöf 2019-01-12 19:03:49 +01:00
parent 731ab848e1
commit 64b77a0efc
4 changed files with 170 additions and 113 deletions

View file

@ -3,24 +3,17 @@
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <dlfcn.h>
#define LOG_MODULE "config:verify"
#define LOG_ENABLE_DBG 0
#include "log.h"
#include "tllist.h"
#include "modules/alsa/alsa.h"
#include "modules/backlight/backlight.h"
#include "modules/battery/battery.h"
#include "modules/clock/clock.h"
#include "modules/i3/i3.h"
#include "modules/label/label.h"
#include "modules/label/label.h"
#include "modules/mpd/mpd.h"
#include "modules/network/network.h"
#include "modules/removables/removables.h"
#include "modules/xkb/xkb.h"
#include "modules/xwindow/xwindow.h"
const char *
conf_err_prefix(const keychain_t *chain, const struct yml_node *node)
{
@ -441,42 +434,46 @@ verify_module(keychain_t *chain, const struct yml_node *node)
return false;
}
/* TODO: this will dlopened later */
static const struct {
const char *name;
const struct module_info *info;
} modules[] = {
{"alsa", &module_alsa},
{"backlight", &module_backlight},
{"battery", &module_battery},
{"clock", &module_clock},
{"i3", &module_i3},
{"label", &module_label},
{"mpd", &module_mpd},
{"network", &module_network},
{"removables", &module_removables},
{"xkb", &module_xkb},
{"xwindow", &module_xwindow},
};
char path[1024];
snprintf(path, sizeof(path), "./modules/lib%s.so", mod_name);
for (size_t i = 0; i < sizeof(modules) / sizeof(modules[0]); i++) {
if (strcmp(modules[i].name, mod_name) != 0)
continue;
void *lib = dlopen(path, RTLD_LOCAL | RTLD_NOW | RTLD_NOLOAD);
if (!conf_verify_dict(chain_push(chain, mod_name),
values,
modules[i].info->attrs,
modules[i].info->attr_count))
{
return false;
}
if (lib == NULL)
lib = dlopen(path, RTLD_LOCAL | RTLD_NOW);
chain_pop(chain);
return true;
if (lib == NULL) {
const char *dl_error = dlerror();
if (dl_error != NULL)
LOG_ERR("%s: dlopen: %s", mod_name, dlerror());
else
LOG_ERR("%s: invalid module name: %s",
conf_err_prefix(chain, module), mod_name);
return false;
}
LOG_ERR("%s: invalid module name: %s", conf_err_prefix(chain, module), mod_name);
return false;
char sym[1024];
snprintf(sym, sizeof(sym), "module_%s", mod_name);
dlerror(); /* Clear previous error */
const struct module_info *info = dlsym(lib, sym);
const char *dlsym_error = dlerror();
if (dlsym_error != NULL) {
LOG_ERR("%s: dlsym: %s", mod_name, dlsym_error);
dlclose(lib);
return false;
}
assert(info != NULL);
if (!conf_verify_dict(chain_push(chain, mod_name), values,
info->attrs, info->attr_count))
return false;
chain_pop(chain);
return true;
}
static bool
@ -515,6 +512,45 @@ verify_bar_location(keychain_t *chain, const struct yml_node *node)
return conf_verify_enum(chain, node, (const char *[]){"top", "bottom"}, 2);
}
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
static void
find_modules(void)
{
int cwd = open(".", O_RDONLY);
assert(cwd != -1);
int modules = openat(cwd, "modules", O_RDONLY);
assert(modules != -1);
DIR *dir = fdopendir(modules);
assert(dir != NULL);
for (struct dirent *e = readdir(dir); e != NULL; e = readdir(dir)) {
const size_t len = strlen(e->d_name);
if (len <= 6) {
/* Need at least "libX.so" */
continue;
}
if (strncmp(e->d_name, "lib", 3) != 0)
continue;
if (strcmp(&e->d_name[len - 3], ".so") != 0)
continue;
char *name = malloc(len - 6 + 1);
memcpy(name, &e->d_name[3], len - 6);
name[len - 6] = '\0';
LOG_DBG("%s", name);
free(name);
}
closedir(dir);
}
#endif
bool
conf_verify_bar(const struct yml_node *bar)
{
@ -523,6 +559,10 @@ conf_verify_bar(const struct yml_node *bar)
return false;
}
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
find_modules();
#endif
keychain_t chain = tll_init();
chain_push(&chain, "bar");