/** * @file load_module.c * @author Ambroz Bizjak * * @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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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 };