diff --git a/CMakeLists.txt b/CMakeLists.txt index 17fc928..4f07ee9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ add_executable(f00bar main.c module.c module.h particle.c particle.h + plugin.c plugin.h tag.c tag.h xcb.c xcb.h yml.c yml.h diff --git a/config-verify.c b/config-verify.c index 9002ca1..6597b39 100644 --- a/config-verify.c +++ b/config-verify.c @@ -3,15 +3,10 @@ #include #include -#include -#include -#include -#include -#include - #define LOG_MODULE "config:verify" #define LOG_ENABLE_DBG 0 #include "log.h" +#include "plugin.h" #include "tllist.h" const char * @@ -434,40 +429,13 @@ verify_module(keychain_t *chain, const struct yml_node *node) return false; } - char path[1024]; - snprintf(path, sizeof(path), "./modules/lib%s.so", mod_name); - - void *lib = dlopen(path, RTLD_LOCAL | RTLD_NOW | RTLD_NOLOAD); - - if (lib == NULL) - lib = dlopen(path, RTLD_LOCAL | RTLD_NOW); - - 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); - + const struct module_info *info = plugin_load_module(mod_name); + if (info == NULL) { + LOG_ERR( + "%s: invalid module name: %s", conf_err_prefix(chain, node), 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; @@ -512,45 +480,6 @@ 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) { @@ -559,10 +488,6 @@ 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"); diff --git a/config.c b/config.c index a9ba245..e18660c 100644 --- a/config.c +++ b/config.c @@ -5,10 +5,6 @@ #include #include - -#include - - #include "color.h" #include "decoration.h" @@ -26,6 +22,7 @@ #include "module.h" #include "config-verify.h" +#include "plugin.h" static uint8_t hex_nibble(char hex) @@ -444,18 +441,7 @@ conf_to_bar(const struct yml_node *bar) struct yml_dict_iter m = yml_dict_iter(it.node); const char *mod_name = yml_value_as_string(m.key); - char path[1024]; - snprintf(path, sizeof(path), "./modules/lib%s.so", mod_name); - - void *lib = dlopen(path, RTLD_LOCAL | RTLD_NOW | RTLD_NOLOAD); - assert(lib != NULL); - - char sym[1024]; - snprintf(sym, sizeof(sym), "module_%s", mod_name); - - const struct module_info *info = dlsym(lib, sym); - assert(info != NULL); - + const struct module_info *info = plugin_load_module(mod_name); mods[idx] = info->from_conf(m.value, font); } diff --git a/plugin.c b/plugin.c new file mode 100644 index 0000000..439ec91 --- /dev/null +++ b/plugin.c @@ -0,0 +1,82 @@ +#include "plugin.h" + +#include +#include + +#define LOG_MODULE "plugin" +#define LOG_ENABLE_DBG 0 +#include "log.h" +#include "config.h" +#include "tllist.h" + +struct plugin { + char *name; + void *lib; +}; + +static tll(struct plugin) libs = tll_init(); + +static void __attribute__((destructor)) +fini(void) +{ + tll_foreach(libs, plug) { + dlerror(); + dlclose(plug->item.lib); + + const char *dl_error = dlerror(); + if (dl_error != NULL) + LOG_ERR("%s: dlclose(): %s", plug->item.name, dl_error); + + free(plug->item.name); + } + + tll_free(libs); +} + +const struct module_info * +plugin_load_module(const char *name) +{ + char path[128]; + snprintf(path, sizeof(path), "./modules/lib%s.so", name); + + void *lib = NULL; + + /* Have we already loaded it? */ + tll_foreach(libs, plug) { + if (strcmp(plug->item.name, name) == 0) { + lib = plug->item.lib; + LOG_DBG("%s already loaded: %p", name, lib); + break; + } + } + + if (lib == NULL) { + /* Not loaded - do it now */ + lib = dlopen(path, RTLD_LOCAL | RTLD_NOW); + LOG_DBG("%s: dlopened to %p", name, lib); + + if (lib == NULL) { + LOG_ERR("%s: dlopen: %s", name, dlerror()); + return NULL; + } + + tll_push_back(libs, ((struct plugin){strdup(name), lib})); + } + + /* TODO: use same name in all modules */ + char sym[128]; + snprintf(sym, sizeof(sym), "module_%s", name); + + /* TODO: cache symbol */ + 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", name, dlsym_error); + return NULL; + } + + assert(info != NULL); + return info; +} diff --git a/plugin.h b/plugin.h new file mode 100644 index 0000000..6d829e2 --- /dev/null +++ b/plugin.h @@ -0,0 +1,5 @@ +#pragma once + +#include "module.h" + +const struct module_info *plugin_load_module(const char *name);