From 64b77a0efc0cdb769042d5111698d16a777b1c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 12 Jan 2019 19:03:49 +0100 Subject: [PATCH] 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() --- CMakeLists.txt | 48 +++++----------- config-verify.c | 128 +++++++++++++++++++++++++++-------------- config.c | 53 ++++++----------- modules/CMakeLists.txt | 54 +++++++++++++++++ 4 files changed, 170 insertions(+), 113 deletions(-) create mode 100644 modules/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 140cb2a..17fc928 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/config-verify.c b/config-verify.c index 267b757..9002ca1 100644 --- a/config-verify.c +++ b/config-verify.c @@ -3,24 +3,17 @@ #include #include +#include +#include +#include +#include +#include + #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"); diff --git a/config.c b/config.c index 3d65197..a9ba245 100644 --- a/config.c +++ b/config.c @@ -5,6 +5,10 @@ #include #include + +#include + + #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) { diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt new file mode 100644 index 0000000..ffceaec --- /dev/null +++ b/modules/CMakeLists.txt @@ -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)