summaryrefslogtreecommitdiffstats
path: root/lib/module.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/module.c')
-rw-r--r--lib/module.c170
1 files changed, 170 insertions, 0 deletions
diff --git a/lib/module.c b/lib/module.c
new file mode 100644
index 0000000..68abc84
--- /dev/null
+++ b/lib/module.c
@@ -0,0 +1,170 @@
+/* Copyright (C) 2015-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program 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.
+
+ This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <pthread.h>
+#include <contrib/cleanup.h>
+
+#include "lib/defines.h"
+#include "lib/utils.h"
+#include "lib/module.h"
+
+/* List of embedded modules */
+const kr_layer_api_t *iterate_layer(struct kr_module *module);
+const kr_layer_api_t *validate_layer(struct kr_module *module);
+const kr_layer_api_t *cache_layer(struct kr_module *module);
+static const struct kr_module embedded_modules[] = {
+ { "iterate", NULL, NULL, NULL, iterate_layer, NULL, NULL, NULL },
+ { "validate", NULL, NULL, NULL, validate_layer, NULL, NULL, NULL },
+ { "cache", NULL, NULL, NULL, cache_layer, NULL, NULL, NULL },
+};
+
+/** Library extension. */
+#if defined(__APPLE__)
+ #define LIBEXT ".dylib"
+#elif _WIN32
+ #define LIBEXT ".dll"
+#else
+ #define LIBEXT ".so"
+#endif
+
+
+/** Load prefixed symbol. */
+static void *load_symbol(void *lib, const char *prefix, const char *name)
+{
+ auto_free char *symbol = kr_strcatdup(2, prefix, name);
+ return dlsym(lib, symbol);
+}
+
+static int load_library(struct kr_module *module, const char *name, const char *path)
+{
+ assert(module && name && path);
+ /* Absolute or relative path (then only library search path is used). */
+ auto_free char *lib_path = kr_strcatdup(4, path, "/", name, LIBEXT);
+ if (lib_path == NULL) {
+ return kr_error(ENOMEM);
+ }
+
+ /* Workaround for buggy _fini/__attribute__((destructor)) and dlclose(),
+ * this keeps the library mapped until the program finishes though. */
+ module->lib = dlopen(lib_path, RTLD_NOW | RTLD_NODELETE);
+ if (module->lib) {
+ return kr_ok();
+ }
+
+ return kr_error(ENOENT);
+}
+
+const struct kr_module * kr_module_embedded(const char *name)
+{
+ for (unsigned i = 0; i < sizeof(embedded_modules)/sizeof(embedded_modules[0]); ++i) {
+ if (strcmp(name, embedded_modules[i].name) == 0)
+ return embedded_modules + i;
+ }
+ return NULL;
+}
+
+/** Load C module symbols. */
+static int load_sym_c(struct kr_module *module, uint32_t api_required)
+{
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wpedantic" /* casts after load_symbol() */
+ /* Check if it's embedded first */
+ const struct kr_module *embedded = kr_module_embedded(module->name);
+ if (embedded) {
+ module->init = embedded->init;
+ module->deinit = embedded->deinit;
+ module->config = embedded->config;
+ module->layer = embedded->layer;
+ module->props = embedded->props;
+ return kr_ok();
+ }
+ /* Load dynamic library module */
+ auto_free char *m_prefix = kr_strcatdup(2, module->name, "_");
+
+ /* Check ABI version, return error on mismatch. */
+ module_api_cb *api = load_symbol(module->lib, m_prefix, "api");
+ if (api == NULL) {
+ return kr_error(ENOENT);
+ }
+ if (api() != api_required) {
+ return kr_error(ENOTSUP);
+ }
+
+ /* Load ABI by symbol names. */
+ #define ML(symname) module->symname = \
+ load_symbol(module->lib, m_prefix, #symname)
+ ML(init);
+ ML(deinit);
+ ML(config);
+ ML(layer);
+ ML(props);
+ #undef ML
+
+ return kr_ok();
+ #pragma GCC diagnostic pop
+}
+
+int kr_module_load(struct kr_module *module, const char *name, const char *path)
+{
+ if (module == NULL || name == NULL) {
+ return kr_error(EINVAL);
+ }
+
+ /* Initialize, keep userdata */
+ void *data = module->data;
+ memset(module, 0, sizeof(struct kr_module));
+ module->data = data;
+ module->name = strdup(name);
+ if (module->name == NULL) {
+ return kr_error(ENOMEM);
+ }
+
+ /* Search for module library. */
+ if (!path || load_library(module, name, path) != 0) {
+ module->lib = RTLD_DEFAULT;
+ }
+
+ /* Try to load module ABI. */
+ int ret = load_sym_c(module, KR_MODULE_API);
+ if (ret == 0 && module->init) {
+ ret = module->init(module);
+ }
+ if (ret != 0) {
+ kr_module_unload(module);
+ }
+
+ return ret;
+}
+
+void kr_module_unload(struct kr_module *module)
+{
+ if (module == NULL) {
+ return;
+ }
+
+ if (module->deinit) {
+ module->deinit(module);
+ }
+
+ if (module->lib && module->lib != RTLD_DEFAULT) {
+ dlclose(module->lib);
+ }
+
+ free(module->name);
+ memset(module, 0, sizeof(struct kr_module));
+}