/* * virmodule.c: APIs for dlopen'ing extension modules * * Copyright (C) 2012-2018 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see * . * */ #include #include "internal.h" #include "virmodule.h" #include "virerror.h" #include "virfile.h" #include "virlog.h" #define VIR_FROM_THIS VIR_FROM_NONE VIR_LOG_INIT("util.module"); #ifdef HAVE_DLFCN_H # include static void * virModuleLoadFile(const char *file) { void *handle = NULL; int flags = RTLD_NOW | RTLD_GLOBAL; # ifdef RTLD_NODELETE flags |= RTLD_NODELETE; # endif VIR_DEBUG("Load module file '%s'", file); virUpdateSelfLastChanged(file); if (!(handle = dlopen(file, flags))) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to load module '%s': %s"), file, dlerror()); return NULL; } return handle; } static void * virModuleLoadFunc(void *handle, const char *file, const char *funcname) { void *regsym; VIR_DEBUG("Lookup function '%s'", funcname); if (!(regsym = dlsym(handle, funcname))) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to find symbol '%s' in module '%s': %s"), funcname, file, dlerror()); return NULL; } return regsym; } /** * virModuleLoad: * @path: filename of module to load * @regfunc: name of the function that registers the module * @required: true if module must exist on disk, false to silently skip * * Loads a loadable module named @path and calls the * registration function @regfunc. The module will never * be unloaded because unloading is not safe in a multi-threaded * application. * * The module is automatically looked up in the appropriate place (git or * installed directory). * * Returns 0 on success, 1 if the module was not found and -1 on any error. */ int virModuleLoad(const char *path, const char *regfunc, bool required) { void *rethandle = NULL; int (*regsym)(void); int ret = -1; if (!virFileExists(path)) { if (required) { virReportSystemError(errno, _("Failed to find module '%s'"), path); return -1; } else { VIR_INFO("Module '%s' does not exist", path); return 1; } } if (!(rethandle = virModuleLoadFile(path))) goto cleanup; if (!(regsym = virModuleLoadFunc(rethandle, path, regfunc))) goto cleanup; if ((*regsym)() < 0) { /* regsym() should report an error itself, but lets * just make sure */ if (virGetLastErrorCode() == VIR_ERR_OK) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to execute symbol '%s' in module '%s'"), regfunc, path); } goto cleanup; } rethandle = NULL; ret = 0; cleanup: if (rethandle) dlclose(rethandle); return ret; } #else /* ! HAVE_DLFCN_H */ int virModuleLoad(const char *path, const char *regfunc G_GNUC_UNUSED, bool required) { VIR_DEBUG("dlopen not available on this platform"); if (required) { virReportSystemError(ENOSYS, _("Failed to find module '%s'"), path); return -1; } else { /* Since we have no dlopen(), but definition we have no * loadable modules on disk, so we can reasonably * return '1' instead of an error. */ return 1; } } #endif /* ! HAVE_DLFCN_H */