1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <stdlib.h>
#include <dlfcn.h>
#include <contrib/cleanup.h>
#include "kresconfig.h"
#include "lib/defines.h"
#include "lib/utils.h"
#include "lib/module.h"
/* List of embedded modules. These aren't (un)loaded. */
int iterate_init(struct kr_module *self);
int validate_init(struct kr_module *self);
int cache_init(struct kr_module *self);
kr_module_init_cb kr_module_get_embedded(const char *name)
{
if (strcmp(name, "iterate") == 0)
return iterate_init;
if (strcmp(name, "validate") == 0)
return validate_init;
if (strcmp(name, "cache") == 0)
return cache_init;
return NULL;
}
/** 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)
{
if (kr_fails_assert(module && name && path))
return kr_error(EINVAL);
/* 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);
}
/** Load C module symbols. */
static int load_sym_c(struct kr_module *module, uint32_t api_required)
{
module->init = kr_module_get_embedded(module->name);
if (module->init) {
return kr_ok();
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic" /* casts after load_symbol() */
/* Check if it's embedded first */
/* 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);
#undef ML
if (load_symbol(module->lib, m_prefix, "layer")
|| load_symbol(module->lib, m_prefix, "props")) {
/* In case someone re-compiled against new kresd
* but haven't actually changed the symbols. */
kr_log_error(SYSTEM, "module %s requires upgrade. Please refer to "
"https://knot-resolver.readthedocs.io/en/stable/upgrading.html",
module->name);
return kr_error(ENOTSUP);
}
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));
}
|