util: create new virmodule.{c,h} files for dlopen support code

The driver.{c,h} files are primarily targetted at loading hypervisor
drivers and some helper functions in that area. It also, however,
contains a generically useful function for loading extension modules
that is called by the storage driver. Split that functionality off
into a new virmodule.{c,h} file to isolate it.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2018-04-25 15:35:27 +01:00
parent 1f2f055bc3
commit 8b0f721f2e
8 changed files with 201 additions and 136 deletions

View File

@ -28,6 +28,7 @@
#include "viralloc.h"
#include "virfile.h"
#include "virlog.h"
#include "virmodule.h"
#include "virthread.h"
#include "configmake.h"
@ -38,136 +39,6 @@ VIR_LOG_INIT("driver");
/* XXX re-implement this for other OS, or use libtools helper lib ? */
#define DEFAULT_DRIVER_DIR LIBDIR "/libvirt/connection-driver"
#ifdef HAVE_DLFCN_H
# include <dlfcn.h>
static void *
virDriverLoadModuleFile(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 *
virDriverLoadModuleFunc(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;
}
/**
* virDriverLoadModuleFull:
* @path: filename of module to load
* @regfunc: name of the function that registers the module
*
* 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
virDriverLoadModuleFull(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 = virDriverLoadModuleFile(path)))
goto cleanup;
if (!(regsym = virDriverLoadModuleFunc(rethandle, path, regfunc)))
goto cleanup;
if ((*regsym)() < 0) {
/* regsym() should report an error itself, but lets
* just make sure */
virErrorPtr err = virGetLastError();
if (err == NULL) {
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
virDriverLoadModuleFull(const char *path ATTRIBUTE_UNUSED,
const char *regfunc ATTRIBUTE_UNUSED,
bool required)
{
VIR_DEBUG("dlopen not available on this platform");
if (required) {
virReportSystemError(ENOSYS,
_("Failed to find module '%s': %s"), path);
return -1;
} else {
/* Since we have no dlopen(), but definition we have no
* loadable modules on disk, so we can resaonably
* return '1' instead of an error.
*/
return 1;
}
}
#endif /* ! HAVE_DLFCN_H */
int
@ -188,7 +59,7 @@ virDriverLoadModule(const char *name,
"LIBVIRT_DRIVER_DIR")))
return -1;
ret = virDriverLoadModuleFull(modfile, regfunc, required);
ret = virModuleLoad(modfile, regfunc, required);
VIR_FREE(modfile);

View File

@ -110,9 +110,6 @@ int virSetSharedStorageDriver(virStorageDriverPtr driver) ATTRIBUTE_RETURN_CHECK
int virDriverLoadModule(const char *name,
const char *regfunc,
bool required);
int virDriverLoadModuleFull(const char *path,
const char *regfunc,
bool required);
virConnectPtr virGetConnectInterface(void);
virConnectPtr virGetConnectNetwork(void);

View File

@ -4,7 +4,6 @@
# driver.h
virDriverLoadModule;
virDriverLoadModuleFull;
# Let emacs know we want case-insensitive sorting
# Local Variables:

View File

@ -2228,6 +2228,9 @@ virMediatedDeviceTypeFree;
virMediatedDeviceTypeReadAttrs;
# util/virmodule.h
virModuleLoad;
# util/virnetdev.h
virNetDevAddMulti;

View File

@ -33,6 +33,7 @@
#include "virstoragefile.h"
#include "storage_backend.h"
#include "virlog.h"
#include "virmodule.h"
#include "virfile.h"
#include "configmake.h"
@ -97,7 +98,7 @@ virStorageDriverLoadBackendModule(const char *name,
"LIBVIRT_STORAGE_BACKEND_DIR")))
return -1;
ret = virDriverLoadModuleFull(modfile, regfunc, forceload);
ret = virModuleLoad(modfile, regfunc, forceload);
VIR_FREE(modfile);

View File

@ -100,6 +100,8 @@ UTIL_SOURCES = \
util/virmacaddr.h \
util/virmacmap.c \
util/virmacmap.h \
util/virmodule.c \
util/virmodule.h \
util/virnetdev.c \
util/virnetdev.h \
util/virnetdevbandwidth.c \

163
src/util/virmodule.c Normal file
View File

@ -0,0 +1,163 @@
/*
* 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
* <http://www.gnu.org/licenses/>.
*
*/
#include <config.h>
#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 <dlfcn.h>
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 */
virErrorPtr err = virGetLastError();
if (err == NULL) {
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 ATTRIBUTE_UNUSED,
const char *regfunc ATTRIBUTE_UNUSED,
bool required)
{
VIR_DEBUG("dlopen not available on this platform");
if (required) {
virReportSystemError(ENOSYS,
_("Failed to find module '%s': %s"), path);
return -1;
} else {
/* Since we have no dlopen(), but definition we have no
* loadable modules on disk, so we can resaonably
* return '1' instead of an error.
*/
return 1;
}
}
#endif /* ! HAVE_DLFCN_H */

29
src/util/virmodule.h Normal file
View File

@ -0,0 +1,29 @@
/*
* virmodule.h: 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
* <http://www.gnu.org/licenses/>.
*
*/
#ifndef __VIR_MODULE_H__
# define __VIR_MODULE_H__
int virModuleLoad(const char *path,
const char *regfunc,
bool required);
#endif /* __VIR_MODULE_H__ */