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

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.9)
cmake_minimum_required(VERSION 3.13)
project(f00bar C)
set(CMAKE_C_STANDARD_REQUIRED ON)
@ -19,11 +19,6 @@ pkg_check_modules(FONTCONFIG REQUIRED fontconfig) # Core
pkg_check_modules(CAIRO REQUIRED cairo cairo-xcb cairo-ft) # Core
pkg_check_modules(YAML REQUIRED yaml-0.1) # Core (configuration)
pkg_check_modules(XCB_XKB REQUIRED xcb-xkb) # Module/xkb
pkg_check_modules(JSON REQUIRED json-c) # Module/i3
pkg_check_modules(UDEV REQUIRED libudev) # Module/battery
pkg_check_modules(MPD REQUIRED libmpdclient) # Module/mpd
pkg_check_modules(ALSA REQUIRED alsa) # Module/alsa
add_executable(f00bar
bar.c bar.h
@ -50,32 +45,19 @@ add_executable(f00bar
particles/progress-bar.c particles/progress-bar.h
particles/ramp.c particles/ramp.h
particles/string.c particles/string.h
modules/alsa/alsa.c modules/alsa/alsa.h
modules/backlight/backlight.c modules/backlight/backlight.h
modules/battery/battery.c modules/battery/battery.h
modules/clock/clock.c modules/clock/clock.h
modules/i3/i3.c modules/i3/i3.h
modules/label/label.c modules/label/label.h
modules/mpd/mpd.c modules/mpd/mpd.h
modules/network/network.c modules/network/network.h
modules/removables/removables.c modules/removables/removables.h
modules/xkb/xkb.c modules/xkb/xkb.h
modules/xwindow/xwindow.c modules/xwindow/xwindow.h
)
# TODO: directory global
target_compile_definitions(f00bar PRIVATE _GNU_SOURCE)
# Make global symbols in f00bar visible to dlopen:ed plugins
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rdynamic")
target_compile_options(f00bar PRIVATE
${XCB_CFLAGS_OTHER}
${FONTCONFIG_CFLAGS_OTHER}
${CAIRO_CFLAGS_OTHER}
${YAML_CFLAGS_OTHER}
${XCB_XKB_CFLAGS_OTHER}
${JSON_CFLAGS_OTHER}
${UDEV_CFLAGS_OTHER}
${MPD_CFLAGS_OTHER}
${ALSA_CFLAGS_OTHER}
)
target_include_directories(f00bar PRIVATE
@ -83,22 +65,22 @@ target_include_directories(f00bar PRIVATE
${FONTCONFIG_INCLUDE_DIRS}
${CAIRO_INCLUDE_DIRS}
${YAML_INCLUDE_DIRS}
${XCB_XKB_INCLUDE_DIRS}
${JSON_INCLUDE_DIRS}
${UDEV_INCLUDE_DIRS}
${MPD_INCLUDE_DIRS}
${ALSA_INCLUDE_DIRS}
)
target_link_libraries(f00bar
${CMAKE_THREAD_LIBS_INIT}
${CMAKE_DL_LIBS}
${XCB_LIBRARIES}
${FONTCONFIG_LIBRARIES}
${CAIRO_LIBRARIES}
${YAML_LIBRARIES}
${XCB_XKB_LIBRARIES}
${JSON_LIBRARIES}
${UDEV_LIBRARIES}
${MPD_LIBRARIES}
${ALSA_LIBRARIES}
)
add_library(module-sdk INTERFACE)
target_compile_definitions(module-sdk INTERFACE _GNU_SOURCE)
target_compile_options(module-sdk INTERFACE ${CAIRO_CFLAGS_OTHER})
target_include_directories(module-sdk INTERFACE ${CAIRO_INCLUDE_DIRS})
target_link_libraries(module-sdk INTERFACE ${CMAKE_THREAD_LIBS_INIT})
add_subdirectory(modules)

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");

View file

@ -5,6 +5,10 @@
#include <string.h>
#include <assert.h>
#include <dlfcn.h>
#include "color.h"
#include "decoration.h"
@ -21,18 +25,6 @@
#include "particles/string.h"
#include "module.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/mpd/mpd.h"
#include "modules/network/network.h"
#include "modules/removables/removables.h"
#include "modules/xkb/xkb.h"
#include "modules/xwindow/xwindow.h"
#include "config-verify.h"
static uint8_t
@ -452,30 +444,19 @@ 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);
if (strcmp(mod_name, "alsa") == 0)
mods[idx] = module_alsa.from_conf(m.value, font);
else if (strcmp(mod_name, "backlight") == 0)
mods[idx] = module_backlight.from_conf(m.value, font);
else if (strcmp(mod_name, "battery") == 0)
mods[idx] = module_battery.from_conf(m.value, font);
else if (strcmp(mod_name, "clock") == 0)
mods[idx] = module_clock.from_conf(m.value, font);
else if (strcmp(mod_name, "i3") == 0)
mods[idx] = module_i3.from_conf(m.value, font);
else if (strcmp(mod_name, "label") == 0)
mods[idx] = module_label.from_conf(m.value, font);
else if (strcmp(mod_name, "mpd") == 0)
mods[idx] = module_mpd.from_conf(m.value, font);
else if (strcmp(mod_name, "network") == 0)
mods[idx] = module_network.from_conf(m.value, font);
else if (strcmp(mod_name, "removables") == 0)
mods[idx] = module_removables.from_conf(m.value, font);
else if (strcmp(mod_name, "xkb") == 0)
mods[idx] = module_xkb.from_conf(m.value, font);
else if (strcmp(mod_name, "xwindow") == 0)
mods[idx] = module_xwindow.from_conf(m.value, font);
else
assert(false);
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);
mods[idx] = info->from_conf(m.value, font);
}
if (i == 0) {

54
modules/CMakeLists.txt Normal file
View file

@ -0,0 +1,54 @@
cmake_minimum_required(VERSION 3.13)
pkg_check_modules(ALSA REQUIRED alsa)
add_library(alsa MODULE alsa/alsa.c alsa/alsa.h)
target_compile_options(alsa PRIVATE ${ALSA_CFLAGS_OTHER})
target_include_directories(alsa PRIVATE ${ALSA_INCLUDE_DIRS})
target_link_libraries(alsa module-sdk ${ALSA_LIBRARIES})
pkg_check_modules(UDEV REQUIRED libudev)
add_library(backlight MODULE backlight/backlight.c backlight/backlight.h)
target_compile_options(backlight PRIVATE ${UDEV_CFLAGS_OTHER})
target_include_directories(backlight PRIVATE ${UDEV_INCLUDE_DIRS})
target_link_libraries(backlight module-sdk ${UDEV_LIBRARIES})
add_library(battery MODULE battery/battery.c battery/battery.h)
target_compile_options(battery PRIVATE ${UDEV_CFLAGS_OTHER})
target_include_directories(battery PRIVATE ${UDEV_INCLUDE_DIRS})
target_link_libraries(battery module-sdk ${UDEV_LIBRARIES})
add_library(clock MODULE clock/clock.c clock/clock.h)
target_link_libraries(clock module-sdk)
pkg_check_modules(JSON REQUIRED json-c)
add_library(i3 MODULE i3/i3.c i3/i3.h)
target_compile_options(i3 PRIVATE ${JSON_CFLAGS_OTHER})
target_include_directories(i3 PRIVATE ${JSON_INCLUDE_DIRS})
target_link_libraries(i3 module-sdk ${JSON_LIBRARIES})
add_library(label MODULE label/label.c label/label.h)
target_link_libraries(label module-sdk)
pkg_check_modules(MPD REQUIRED libmpdclient)
add_library(mpd MODULE mpd/mpd.c mpd/mpd.h)
target_compile_options(mpd PRIVATE ${MPD_CFLAGS_OTHER})
target_include_directories(mpd PRIVATE ${MPD_INCLUDE_DIRS})
target_link_libraries(mpd module-sdk ${MPD_LIBRARIES})
add_library(network MODULE network/network.c network/network.h)
target_link_libraries(network module-sdk)
add_library(removables MODULE removables/removables.c removables/removables.h)
target_compile_options(removables PRIVATE ${UDEV_CFLAGS_OTHER})
target_include_directories(removables PRIVATE ${UDEV_INCLUDE_DIRS})
target_link_libraries(removables module-sdk ${UDEV_LIBRARIES})
pkg_check_modules(XCB_XKB REQUIRED xcb-xkb)
add_library(xkb MODULE xkb/xkb.c xkb/xkb.h)
target_compile_options(xkb PRIVATE ${XCB_XKB_CFLAGS_OTHER})
target_include_directories(xkb PRIVATE ${XCB_XKB_INCLUDE_DIRS})
target_link_libraries(xkb module-sdk ${XCB_XKB_LIBRARIES})
add_library(xwindow MODULE xwindow/xwindow.c xwindow/xwindow.h)
target_link_libraries(xwindow module-sdk)