|
|
@@ -0,0 +1,313 @@
|
|
|
+/**
|
|
|
+ * @file load_module.c
|
|
|
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
|
|
|
+ *
|
|
|
+ * @section LICENSE
|
|
|
+ *
|
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
|
+ * modification, are permitted provided that the following conditions are met:
|
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
|
+ * 3. Neither the name of the author nor the
|
|
|
+ * names of its contributors may be used to endorse or promote products
|
|
|
+ * derived from this software without specific prior written permission.
|
|
|
+ *
|
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
|
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
+ *
|
|
|
+ * @section DESCRIPTION
|
|
|
+ *
|
|
|
+ * Synopsis:
|
|
|
+ * load_module(string name)
|
|
|
+ */
|
|
|
+
|
|
|
+#include <stddef.h>
|
|
|
+#include <limits.h>
|
|
|
+#include <string.h>
|
|
|
+#include <errno.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <dlfcn.h>
|
|
|
+
|
|
|
+#include <misc/balloc.h>
|
|
|
+#include <misc/concat_strings.h>
|
|
|
+#include <misc/strdup.h>
|
|
|
+#include <misc/offset.h>
|
|
|
+#include <misc/debug.h>
|
|
|
+#include <structure/LinkedList0.h>
|
|
|
+#include <ncd/NCDModule.h>
|
|
|
+
|
|
|
+#include <generated/blog_channel_ncd_load_module.h>
|
|
|
+
|
|
|
+#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
|
|
|
+#define ModuleGlobal(i) ((i)->m->group->group_state)
|
|
|
+
|
|
|
+struct global {
|
|
|
+ LinkedList0 modules_list;
|
|
|
+};
|
|
|
+
|
|
|
+struct module {
|
|
|
+ char *name;
|
|
|
+ void *lib_handle;
|
|
|
+ int ncdmodule_loaded;
|
|
|
+ LinkedList0Node modules_list_node;
|
|
|
+};
|
|
|
+
|
|
|
+static struct module * find_module (const char *name, struct global *g)
|
|
|
+{
|
|
|
+ for (LinkedList0Node *ln = LinkedList0_GetFirst(&g->modules_list); ln; ln = LinkedList0Node_Next(ln)) {
|
|
|
+ struct module *mod = UPPER_OBJECT(ln, struct module, modules_list_node);
|
|
|
+ if (!strcmp(mod->name, name)) {
|
|
|
+ return mod;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static struct module * module_init (const char *name, NCDModuleInst *i)
|
|
|
+{
|
|
|
+ struct global *g = ModuleGlobal(i);
|
|
|
+ ASSERT(!find_module(name, g))
|
|
|
+
|
|
|
+ struct module *mod = BAlloc(sizeof(*mod));
|
|
|
+ if (!mod) {
|
|
|
+ ModuleLog(i, BLOG_ERROR, "BAlloc failed");
|
|
|
+ goto fail0;
|
|
|
+ }
|
|
|
+
|
|
|
+ mod->name = b_strdup(name);
|
|
|
+ if (!mod->name) {
|
|
|
+ ModuleLog(i, BLOG_ERROR, "b_strdup failed");
|
|
|
+ goto fail1;
|
|
|
+ }
|
|
|
+
|
|
|
+ mod->lib_handle = NULL;
|
|
|
+ mod->ncdmodule_loaded = 0;
|
|
|
+ LinkedList0_Prepend(&g->modules_list, &mod->modules_list_node);
|
|
|
+
|
|
|
+ return mod;
|
|
|
+
|
|
|
+fail1:
|
|
|
+ BFree(mod);
|
|
|
+fail0:
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static void module_free (struct module *mod, struct global *g)
|
|
|
+{
|
|
|
+ LinkedList0_Remove(&g->modules_list, &mod->modules_list_node);
|
|
|
+ if (mod->lib_handle) {
|
|
|
+ if (dlclose(mod->lib_handle) != 0) {
|
|
|
+ BLog(BLOG_ERROR, "dlclose failed");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ BFree(mod->name);
|
|
|
+ BFree(mod);
|
|
|
+}
|
|
|
+
|
|
|
+static char * x_read_link (const char *path)
|
|
|
+{
|
|
|
+ size_t size = 32;
|
|
|
+ char *buf = BAlloc(size + 1);
|
|
|
+ if (!buf) {
|
|
|
+ goto fail0;
|
|
|
+ }
|
|
|
+
|
|
|
+ ssize_t link_size;
|
|
|
+ while (1) {
|
|
|
+ link_size = readlink(path, buf, size);
|
|
|
+ if (link_size < 0) {
|
|
|
+ goto fail1;
|
|
|
+ }
|
|
|
+ if (link_size >= 0 && link_size < size) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (size > SIZE_MAX / 2 || 2 * size > SIZE_MAX - 1) {
|
|
|
+ goto fail1;
|
|
|
+ }
|
|
|
+ size *= 2;
|
|
|
+ char *new_buf = BRealloc(buf, size + 1);
|
|
|
+ if (!new_buf) {
|
|
|
+ goto fail1;
|
|
|
+ }
|
|
|
+ buf = new_buf;
|
|
|
+ }
|
|
|
+
|
|
|
+ buf[link_size] = '\0';
|
|
|
+ return buf;
|
|
|
+
|
|
|
+fail1:
|
|
|
+ BFree(buf);
|
|
|
+fail0:
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static char * find_module_library (NCDModuleInst *i, const char *module_name)
|
|
|
+{
|
|
|
+ char *ret = NULL;
|
|
|
+
|
|
|
+ char *self = x_read_link("/proc/self/exe");
|
|
|
+ if (!self) {
|
|
|
+ ModuleLog(i, BLOG_ERROR, "failed to read /proc/self/exe");
|
|
|
+ goto fail0;
|
|
|
+ }
|
|
|
+
|
|
|
+ char *slash = strrchr(self, '/');
|
|
|
+ if (!slash) {
|
|
|
+ ModuleLog(i, BLOG_ERROR, "contents of /proc/self/exe do not have a slash");
|
|
|
+ goto fail1;
|
|
|
+ }
|
|
|
+ *slash = '\0';
|
|
|
+
|
|
|
+ const char *paths[] = {"../lib/badvpn-ncd", "../mcvpn", NULL};
|
|
|
+
|
|
|
+ size_t j;
|
|
|
+ for (j = 0; paths[j]; j++) {
|
|
|
+ char *module_path = concat_strings(6, self, "/", paths[j], "/libncdmodule_", module_name, ".so");
|
|
|
+ if (!module_path) {
|
|
|
+ ModuleLog(i, BLOG_ERROR, "concat_strings failed");
|
|
|
+ goto fail1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (access(module_path, F_OK) == 0) {
|
|
|
+ ret = module_path;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ BFree(module_path);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!paths[j]) {
|
|
|
+ ModuleLog(i, BLOG_ERROR, "failed to find module");
|
|
|
+ }
|
|
|
+
|
|
|
+fail1:
|
|
|
+ BFree(self);
|
|
|
+fail0:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params)
|
|
|
+{
|
|
|
+ struct global *g = BAlloc(sizeof(*g));
|
|
|
+ if (!g) {
|
|
|
+ BLog(BLOG_ERROR, "BAlloc failed");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ group->group_state = g;
|
|
|
+ LinkedList0_Init(&g->modules_list);
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static void func_globalfree (struct NCDInterpModuleGroup *group)
|
|
|
+{
|
|
|
+ struct global *g = group->group_state;
|
|
|
+
|
|
|
+ LinkedList0Node *ln;
|
|
|
+ while ((ln = LinkedList0_GetFirst(&g->modules_list))) {
|
|
|
+ struct module *mod = UPPER_OBJECT(ln, struct module, modules_list_node);
|
|
|
+ module_free(mod, g);
|
|
|
+ }
|
|
|
+
|
|
|
+ BFree(g);
|
|
|
+}
|
|
|
+
|
|
|
+static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
|
|
|
+{
|
|
|
+ // check arguments
|
|
|
+ NCDValRef name_arg;
|
|
|
+ if (!NCDVal_ListRead(params->args, 1, &name_arg)) {
|
|
|
+ ModuleLog(i, BLOG_ERROR, "wrong arity");
|
|
|
+ goto fail0;
|
|
|
+ }
|
|
|
+ if (!NCDVal_IsStringNoNulls(name_arg)) {
|
|
|
+ ModuleLog(i, BLOG_ERROR, "wrong type");
|
|
|
+ goto fail0;
|
|
|
+ }
|
|
|
+
|
|
|
+ struct module *mod = find_module(NCDVal_StringData(name_arg), ModuleGlobal(i));
|
|
|
+ ASSERT(!mod || mod->lib_handle)
|
|
|
+
|
|
|
+ if (!mod) {
|
|
|
+ mod = module_init(NCDVal_StringData(name_arg), i);
|
|
|
+ if (!mod) {
|
|
|
+ ModuleLog(i, BLOG_ERROR, "module_init failed");
|
|
|
+ goto fail0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // find module library
|
|
|
+ char *module_path = find_module_library(i, NCDVal_StringData(name_arg));
|
|
|
+ if (!module_path) {
|
|
|
+ module_free(mod, ModuleGlobal(i));
|
|
|
+ goto fail0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // load it as a dynamic library
|
|
|
+ mod->lib_handle = dlopen(module_path, RTLD_NOW);
|
|
|
+ BFree(module_path);
|
|
|
+ if (!mod->lib_handle) {
|
|
|
+ ModuleLog(i, BLOG_ERROR, "dlopen failed");
|
|
|
+ module_free(mod, ModuleGlobal(i));
|
|
|
+ goto fail0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!mod->ncdmodule_loaded) {
|
|
|
+ // build name of NCDModuleGroup structure symbol
|
|
|
+ char *group_symbol = concat_strings(2, "ncdmodule_", NCDVal_StringData(name_arg));
|
|
|
+ if (!group_symbol) {
|
|
|
+ ModuleLog(i, BLOG_ERROR, "concat_strings failed");
|
|
|
+ goto fail0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // resolve NCDModuleGroup structure symbol
|
|
|
+ void *group = dlsym(mod->lib_handle, group_symbol);
|
|
|
+ BFree(group_symbol);
|
|
|
+ if (!group) {
|
|
|
+ ModuleLog(i, BLOG_ERROR, "dlsym failed");
|
|
|
+ goto fail0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // load module group
|
|
|
+ if (!NCDModuleInst_Backend_InterpLoadGroup(i, (struct NCDModuleGroup *)group)) {
|
|
|
+ ModuleLog(i, BLOG_ERROR, "NCDModuleInst_Backend_InterpLoadGroup failed");
|
|
|
+ goto fail0;
|
|
|
+ }
|
|
|
+
|
|
|
+ mod->ncdmodule_loaded = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // signal up
|
|
|
+ NCDModuleInst_Backend_Up(i);
|
|
|
+ return;
|
|
|
+
|
|
|
+fail0:
|
|
|
+ NCDModuleInst_Backend_DeadError(i);
|
|
|
+}
|
|
|
+
|
|
|
+static struct NCDModule modules[] = {
|
|
|
+ {
|
|
|
+ .type = "load_module",
|
|
|
+ .func_new2 = func_new
|
|
|
+ }, {
|
|
|
+ .type = NULL
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const struct NCDModuleGroup ncdmodule_load_module = {
|
|
|
+ .func_globalinit = func_globalinit,
|
|
|
+ .func_globalfree = func_globalfree,
|
|
|
+ .modules = modules
|
|
|
+};
|