2008-11-21 12:16:08 +00:00
|
|
|
/*
|
|
|
|
* driver.c: Helpers for loading drivers
|
|
|
|
*
|
2011-04-11 22:25:25 +00:00
|
|
|
* Copyright (C) 2006-2011 Red Hat, Inc.
|
2008-11-21 12:16:08 +00:00
|
|
|
*
|
|
|
|
* 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
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2008-11-21 12:16:08 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "driver.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2014-04-24 15:45:49 +00:00
|
|
|
#include "virfile.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2018-04-25 14:35:27 +00:00
|
|
|
#include "virmodule.h"
|
2021-01-06 11:56:11 +00:00
|
|
|
#include "virobject.h"
|
2019-10-04 14:57:04 +00:00
|
|
|
#include "virstring.h"
|
2018-02-13 10:08:00 +00:00
|
|
|
#include "virthread.h"
|
2020-02-25 10:01:09 +00:00
|
|
|
#include "virutil.h"
|
2010-11-16 14:54:17 +00:00
|
|
|
#include "configmake.h"
|
2008-11-21 12:16:08 +00:00
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("driver");
|
|
|
|
|
2018-04-19 15:21:38 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
2008-11-21 12:16:08 +00:00
|
|
|
|
2016-09-21 19:17:30 +00:00
|
|
|
/* XXX re-implement this for other OS, or use libtools helper lib ? */
|
2017-07-26 19:31:47 +00:00
|
|
|
#define DEFAULT_DRIVER_DIR LIBDIR "/libvirt/connection-driver"
|
2008-11-21 12:16:08 +00:00
|
|
|
|
2017-08-02 10:21:12 +00:00
|
|
|
|
2017-01-18 15:44:20 +00:00
|
|
|
|
2017-01-26 13:57:41 +00:00
|
|
|
int
|
|
|
|
virDriverLoadModule(const char *name,
|
2018-04-19 15:50:56 +00:00
|
|
|
const char *regfunc,
|
|
|
|
bool required)
|
2008-11-21 12:16:08 +00:00
|
|
|
{
|
2020-04-13 12:48:57 +00:00
|
|
|
g_autofree char *modfile = NULL;
|
2017-01-26 13:57:41 +00:00
|
|
|
int ret;
|
2008-11-21 12:16:08 +00:00
|
|
|
|
2011-02-16 23:37:57 +00:00
|
|
|
VIR_DEBUG("Module load %s", name);
|
2008-11-21 12:16:08 +00:00
|
|
|
|
2014-04-24 15:45:49 +00:00
|
|
|
if (!(modfile = virFileFindResourceFull(name,
|
|
|
|
"libvirt_driver_",
|
2020-08-25 23:47:07 +00:00
|
|
|
VIR_FILE_MODULE_EXT,
|
2020-05-28 00:40:50 +00:00
|
|
|
abs_top_builddir "/src",
|
2015-04-01 10:38:42 +00:00
|
|
|
DEFAULT_DRIVER_DIR,
|
2014-04-24 15:45:49 +00:00
|
|
|
"LIBVIRT_DRIVER_DIR")))
|
2018-04-19 14:25:01 +00:00
|
|
|
return -1;
|
2008-11-21 12:16:08 +00:00
|
|
|
|
2018-04-25 14:35:27 +00:00
|
|
|
ret = virModuleLoad(modfile, regfunc, required);
|
2017-01-26 13:57:41 +00:00
|
|
|
return ret;
|
2008-11-21 12:16:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* XXX unload modules, but we can't until we can unregister libvirt drivers */
|
2018-01-31 18:21:52 +00:00
|
|
|
|
2019-10-04 14:57:04 +00:00
|
|
|
/**
|
|
|
|
* virDriverShouldAutostart:
|
|
|
|
* @dir: driver's run state directory (usually /var/run/libvirt/$driver)
|
|
|
|
* @autostart: whether driver should initiate autostart
|
|
|
|
*
|
|
|
|
* Automatic starting of libvirt's objects (e.g. domains, networks, storage
|
|
|
|
* pools, etc.) doesn't play nice with using '--timeout' on daemon's command
|
|
|
|
* line because the objects are attempted to autostart on every start of
|
|
|
|
* corresponding driver/daemon. To resolve this problem, a file is created in
|
|
|
|
* driver's private directory (which doesn't survive host's reboot) and thus
|
|
|
|
* autostart is attempted only once.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virDriverShouldAutostart(const char *dir,
|
|
|
|
bool *autostart)
|
|
|
|
{
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *path = NULL;
|
2019-10-04 14:57:04 +00:00
|
|
|
|
|
|
|
*autostart = false;
|
|
|
|
|
2019-10-22 13:48:08 +00:00
|
|
|
path = g_strdup_printf("%s/autostarted", dir);
|
2019-10-04 14:57:04 +00:00
|
|
|
|
|
|
|
if (virFileExists(path)) {
|
|
|
|
VIR_DEBUG("Autostart file %s exists, skipping autostart", path);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_DEBUG("Autostart file %s does not exist, do autostart", path);
|
|
|
|
*autostart = true;
|
|
|
|
|
|
|
|
if (virFileTouch(path, 0600) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-13 10:08:00 +00:00
|
|
|
virThreadLocal connectInterface;
|
|
|
|
virThreadLocal connectNetwork;
|
|
|
|
virThreadLocal connectNWFilter;
|
|
|
|
virThreadLocal connectNodeDev;
|
|
|
|
virThreadLocal connectSecret;
|
|
|
|
virThreadLocal connectStorage;
|
|
|
|
|
|
|
|
static int
|
|
|
|
virConnectCacheOnceInit(void)
|
|
|
|
{
|
log error if virConnectCacheOnceInit() fails
virGetConnectNetwork() calls
virGetConnectGeneric(), which calls
virConnecCacheInitialize(), which is actually a call (only once) to
virConnectCacheOnceInit() which calls
virThreadLocalInit() several times, which calls
pthread_key_create()
If pthread_key_create() fails, it (of course) doesn't log an error
(because it's not a part of libvirt), nor does any other function on
the call chain all the way up to virGetConnectNetwork(). But none of
the callers of virGetConnectNetwork() log an error either, so it is
possible that an API could fail due to virGetConnectNetwork() failing,
but would only log "an error was encountered, but the cause is
unknown. Deal with it." (paraphrasing).
(In all likelyhood, virConnectCacheOnceInit() is going to be called at
some earlier time, and almost certainly pthread_key_create() will
never fail (and if it does, the user will have *much* bigger problems
than an obtuse error message from libvirt)).
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2021-01-26 04:54:57 +00:00
|
|
|
if (virThreadLocalInit(&connectInterface, NULL) < 0 ||
|
|
|
|
virThreadLocalInit(&connectNetwork, NULL) < 0 ||
|
|
|
|
virThreadLocalInit(&connectNWFilter, NULL) < 0 ||
|
|
|
|
virThreadLocalInit(&connectNodeDev, NULL) < 0 ||
|
|
|
|
virThreadLocalInit(&connectSecret, NULL) < 0 ||
|
|
|
|
virThreadLocalInit(&connectStorage, NULL) < 0) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("Unable to initialize thread local variable"));
|
2018-02-13 10:08:00 +00:00
|
|
|
return -1;
|
log error if virConnectCacheOnceInit() fails
virGetConnectNetwork() calls
virGetConnectGeneric(), which calls
virConnecCacheInitialize(), which is actually a call (only once) to
virConnectCacheOnceInit() which calls
virThreadLocalInit() several times, which calls
pthread_key_create()
If pthread_key_create() fails, it (of course) doesn't log an error
(because it's not a part of libvirt), nor does any other function on
the call chain all the way up to virGetConnectNetwork(). But none of
the callers of virGetConnectNetwork() log an error either, so it is
possible that an API could fail due to virGetConnectNetwork() failing,
but would only log "an error was encountered, but the cause is
unknown. Deal with it." (paraphrasing).
(In all likelyhood, virConnectCacheOnceInit() is going to be called at
some earlier time, and almost certainly pthread_key_create() will
never fail (and if it does, the user will have *much* bigger problems
than an obtuse error message from libvirt)).
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2021-01-26 04:54:57 +00:00
|
|
|
}
|
|
|
|
|
2018-02-13 10:08:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virConnectCache);
|
|
|
|
|
2019-10-08 21:02:00 +00:00
|
|
|
static virConnectPtr
|
|
|
|
virGetConnectGeneric(virThreadLocalPtr threadPtr, const char *name)
|
2018-01-31 18:21:52 +00:00
|
|
|
{
|
2018-02-13 10:08:00 +00:00
|
|
|
virConnectPtr conn;
|
|
|
|
|
|
|
|
if (virConnectCacheInitialize() < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2019-10-08 21:02:00 +00:00
|
|
|
conn = virThreadLocalGet(threadPtr);
|
|
|
|
|
2018-02-13 10:08:00 +00:00
|
|
|
if (conn) {
|
2019-10-08 21:02:00 +00:00
|
|
|
VIR_DEBUG("Return cached %s connection %p", name, conn);
|
2018-02-13 10:08:00 +00:00
|
|
|
virObjectRef(conn);
|
|
|
|
} else {
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *uri = NULL;
|
2019-10-08 21:02:00 +00:00
|
|
|
const char *uriPath = geteuid() == 0 ? "/system" : "/session";
|
|
|
|
|
2019-10-22 13:48:08 +00:00
|
|
|
uri = g_strdup_printf("%s://%s", name, uriPath);
|
2019-10-08 21:02:00 +00:00
|
|
|
|
|
|
|
conn = virConnectOpen(uri);
|
|
|
|
VIR_DEBUG("Opened new %s connection %p", name, conn);
|
2018-02-13 10:08:00 +00:00
|
|
|
}
|
|
|
|
return conn;
|
2018-01-31 18:21:52 +00:00
|
|
|
}
|
|
|
|
|
2018-02-13 10:08:00 +00:00
|
|
|
|
2019-10-08 21:02:00 +00:00
|
|
|
virConnectPtr virGetConnectInterface(void)
|
|
|
|
{
|
|
|
|
return virGetConnectGeneric(&connectInterface, "interface");
|
|
|
|
}
|
2018-02-13 10:08:00 +00:00
|
|
|
|
2019-10-08 21:02:00 +00:00
|
|
|
virConnectPtr virGetConnectNetwork(void)
|
|
|
|
{
|
|
|
|
return virGetConnectGeneric(&connectNetwork, "network");
|
2018-01-31 18:21:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virConnectPtr virGetConnectNWFilter(void)
|
|
|
|
{
|
2019-10-08 21:02:00 +00:00
|
|
|
return virGetConnectGeneric(&connectNWFilter, "nwfilter");
|
2018-01-31 18:21:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virConnectPtr virGetConnectNodeDev(void)
|
|
|
|
{
|
2019-10-08 21:02:00 +00:00
|
|
|
return virGetConnectGeneric(&connectNodeDev, "nodedev");
|
2018-01-31 18:21:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virConnectPtr virGetConnectSecret(void)
|
|
|
|
{
|
2019-10-08 21:02:00 +00:00
|
|
|
return virGetConnectGeneric(&connectSecret, "secret");
|
2018-01-31 18:21:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virConnectPtr virGetConnectStorage(void)
|
|
|
|
{
|
2019-10-08 21:02:00 +00:00
|
|
|
return virGetConnectGeneric(&connectStorage, "storage");
|
2018-02-13 10:08:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virSetConnectInterface(virConnectPtr conn)
|
|
|
|
{
|
|
|
|
if (virConnectCacheInitialize() < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
VIR_DEBUG("Override interface connection with %p", conn);
|
|
|
|
return virThreadLocalSet(&connectInterface, conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virSetConnectNetwork(virConnectPtr conn)
|
|
|
|
{
|
|
|
|
if (virConnectCacheInitialize() < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
VIR_DEBUG("Override network connection with %p", conn);
|
|
|
|
return virThreadLocalSet(&connectNetwork, conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virSetConnectNWFilter(virConnectPtr conn)
|
|
|
|
{
|
|
|
|
if (virConnectCacheInitialize() < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
VIR_DEBUG("Override nwfilter connection with %p", conn);
|
|
|
|
return virThreadLocalSet(&connectNWFilter, conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virSetConnectNodeDev(virConnectPtr conn)
|
|
|
|
{
|
|
|
|
if (virConnectCacheInitialize() < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
VIR_DEBUG("Override nodedev connection with %p", conn);
|
|
|
|
return virThreadLocalSet(&connectNodeDev, conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virSetConnectSecret(virConnectPtr conn)
|
|
|
|
{
|
|
|
|
if (virConnectCacheInitialize() < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
VIR_DEBUG("Override secret connection with %p", conn);
|
|
|
|
return virThreadLocalSet(&connectSecret, conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virSetConnectStorage(virConnectPtr conn)
|
|
|
|
{
|
|
|
|
if (virConnectCacheInitialize() < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
VIR_DEBUG("Override storage connection with %p", conn);
|
|
|
|
return virThreadLocalSet(&connectStorage, conn);
|
2018-01-31 18:21:52 +00:00
|
|
|
}
|
2019-09-26 14:56:36 +00:00
|
|
|
|
|
|
|
bool
|
|
|
|
virConnectValidateURIPath(const char *uriPath,
|
|
|
|
const char *entityName,
|
|
|
|
bool privileged)
|
|
|
|
{
|
|
|
|
if (privileged) {
|
2019-09-26 14:56:43 +00:00
|
|
|
/* TODO: qemu and vbox drivers allow '/session'
|
|
|
|
* connections as root. This is not ideal, but changing
|
|
|
|
* these drivers to refuse privileged '/session'
|
|
|
|
* connections, like everyone else is already doing, can
|
|
|
|
* break existing applications. Until we decide what to do,
|
|
|
|
* for now we can handle them as exception in this validate
|
|
|
|
* function.
|
|
|
|
*/
|
|
|
|
bool compatSessionRoot = (STREQ(entityName, "qemu") ||
|
|
|
|
STREQ(entityName, "vbox")) &&
|
|
|
|
STREQ(uriPath, "/session");
|
|
|
|
|
|
|
|
if (STRNEQ(uriPath, "/system") && !compatSessionRoot) {
|
2019-09-26 14:56:36 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected %s URI path '%s', try "
|
|
|
|
"%s:///system"),
|
|
|
|
entityName, uriPath, entityName);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (STRNEQ(uriPath, "/session")) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected %s URI path '%s', try "
|
|
|
|
"%s:///session"),
|
|
|
|
entityName, uriPath, entityName);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|