The driver URI:
"qemu:///embed?root=/some/path"
enables a new way to use the QEMU driver by embedding it directly in the
calling process. To use this the process must have a thread running the
libvirt event loop. This URI will then cause libvirt to dynamically load
the QEMU driver module and call its global initialization function.
The application can now make normal libvirt API calls which are all
serviced in-process with no RPC layer involved.
To avoid conflicting with the existing libvirtd daemon it is important
to specify an alternative root directory. All the paths the QEMU driver
normal creates will be placed under this root. For example using the
path /home/berrange/tmp/embed, as an unprivileged process cause the
creation of the following directories
/home/berrange/tmp/embed/home/berrange/.config/libvirt/qemu/lib
/home/berrange/tmp/embed/home/berrange/.config/libvirt/qemu/snapshot
/home/berrange/tmp/embed/home/berrange/.config/libvirt/qemu/save
/home/berrange/tmp/embed/home/berrange/.config/libvirt/qemu/channel/target
/home/berrange/tmp/embed/home/berrange/.config/libvirt/qemu/ram/libvirt
/home/berrange/tmp/embed/home/berrange/.config/libvirt/qemu/ram/libvirt/qemu
/home/berrange/tmp/embed/home/berrange/.config/libvirt/qemu/nvram
/home/berrange/tmp/embed/home/berrange/.config/libvirt/qemu/dump
/home/berrange/tmp/embed/home/berrange/.cache/libvirt/qemu/cache/capabilities
/home/berrange/tmp/embed/home/berrange/.cache/libvirt/qemu/log
/home/berrange/tmp/embed/run/user/501/libvirt/qemu/run
The application is responsible for purging everything underneath this
root directory when no longer required.
Note that QEMU is still daemonized right now and so the application
embedding the QEMU driver can quit & restart and still have its VMs
present, just as libvirtd would.
A future patch will hook up VIR_DOMAIN_CREATE_AUTO_DESTROY flag such
that the VM are not daemonized and still retain the parent/child
relationship forcing them to die the the application exits.
Thanks to previous refactoring, it is still possible to use
functionality that calls out to other secondary drivers. For example it
can open the network, secret or storage drivers. The network driver
stuff is not fully functional though until the pending refactoring is
done to introduce the virNetworkPort object concept.
A key thing to note is that for this to work, the application that links
to libvirt *MUST* be built with -Wl,--export-dynamic to ensure that
symbols from libvirt.so are exported & thus available to the dynamically
loaded QEMU driver module. If libvirt.so itself was dynamically loaded
then RTLD_GLOBAL must be passed to dlopen().
Signed-off-by: Daniel P. Berrangé <berrange(a)redhat.com>
---
src/libvirt.c | 39 +++++++++++++++++++++++++++++++++++++-
src/qemu/qemu_driver.c | 6 ++++--
src/remote/remote_driver.c | 7 +++++++
3 files changed, 49 insertions(+), 3 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c
index 677f1cef5f..e34fd5c96d 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -87,6 +87,7 @@
#ifdef WITH_BHYVE
# include "bhyve/bhyve_driver.h"
#endif
+#include "access/viraccessmanager.h"
#define VIR_FROM_THIS VIR_FROM_NONE
@@ -841,6 +842,7 @@ virConnectOpenInternal(const char *name,
virConnectPtr ret;
virConfPtr conf = NULL;
char *uristr = NULL;
+ bool embed = false;
ret = virGetConnect();
if (ret == NULL)
@@ -939,6 +941,36 @@ virConnectOpenInternal(const char *name,
ret->uri) < 0) {
goto failed;
}
+
+ if (STREQ(ret->uri->scheme, "qemu") &&
+ STREQ(ret->uri->path, "/embed")) {
+ const char *root = NULL;
+ for (i = 0; i < ret->uri->paramsCount; i++) {
+ virURIParamPtr var = &ret->uri->params[i];
+ if (STREQ(var->name, "root"))
+ root = var->value;
+ }
+
+ if (!root) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("root parameter required for embedded
driver"));
+ goto failed;
+ }
+
+ if (virDriverLoadModule("qemu", "qemuRegister", false)
< 0)
+ goto failed;
+
+ if (virAccessManagerGetDefault() == NULL) {
+ virAccessManagerPtr acl;
+ acl = virAccessManagerNew("none");
+ virAccessManagerSetDefault(acl);
+ }
+
+ if (virStateInitialize(geteuid() == 0, root, NULL, NULL) < 0)
+ goto failed;
+
+ embed = true;
+ }
} else {
VIR_DEBUG("no name, allowing driver auto-select");
}
@@ -1011,7 +1043,12 @@ virConnectOpenInternal(const char *name,
continue;
}
} else {
- VIR_DEBUG("Matching any URI scheme for '%s'", ret->uri ?
ret->uri->scheme : "");
+ if (embed) {
+ VIR_DEBUG("Skipping wildcard for embedded URI");
+ continue;
+ } else {
+ VIR_DEBUG("Matching any URI scheme for '%s'",
ret->uri ? ret->uri->scheme : "");
+ }
}
/* before starting the new connection, check if the driver only works
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 019f6b2bf3..cdd483a338 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -1105,14 +1105,16 @@ static virDrvOpenStatus qemuConnectOpen(virConnectPtr conn,
if (virQEMUDriverIsPrivileged(qemu_driver)) {
if (STRNEQ(conn->uri->path, "/system") &&
- STRNEQ(conn->uri->path, "/session")) {
+ STRNEQ(conn->uri->path, "/session") &&
+ STRNEQ(conn->uri->path, "/embed")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected QEMU URI path '%s', try
qemu:///system"),
conn->uri->path);
return VIR_DRV_OPEN_ERROR;
}
} else {
- if (STRNEQ(conn->uri->path, "/session")) {
+ if (STRNEQ(conn->uri->path, "/session") &&
+ STRNEQ(conn->uri->path, "/embed")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected QEMU URI path '%s', try
qemu:///session"),
conn->uri->path);
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 244e384607..976f311060 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -1343,6 +1343,13 @@ remoteConnectOpen(virConnectPtr conn,
if (flags & VIR_CONNECT_RO)
rflags |= VIR_DRV_OPEN_REMOTE_RO;
+ if (conn->uri &&
+ conn->uri->path &&
+ STREQ(conn->uri->path, "/embed")) {
+ ret = VIR_DRV_OPEN_DECLINED;
+ goto cleanup;
+ }
+
/*
* If no servername is given, and no +XXX
* transport is listed, or transport is unix,
--
2.21.0