This enables support for running QEMU embedded to the calling
application process using a URI:
qemu:///embed?root=/some/path
Note that it is important to keep the path reasonably short to
avoid risk of hitting the limit on UNIX socket path names
which is 108 characters.
When using the embedded mode with a root=/var/tmp/embed, the
driver will use the following paths:
logDir: /var/tmp/embed/log/qemu
swtpmLogDir: /var/tmp/embed/log/swtpm
configBaseDir: /var/tmp/embed/etc/qemu
stateDir: /var/tmp/embed/run/qemu
swtpmStateDir: /var/tmp/embed/run/swtpm
cacheDir: /var/tmp/embed/cache/qemu
libDir: /var/tmp/embed/lib/qemu
swtpmStorageDir: /var/tmp/embed/lib/swtpm
defaultTLSx509certdir: /var/tmp/embed/etc/pki/qemu
These are identical whether the embedded driver is privileged
or unprivileged.
This compares with the system instance which uses
logDir: /var/log/libvirt/qemu
swtpmLogDir: /var/log/swtpm/libvirt/qemu
configBaseDir: /etc/libvirt/qemu
stateDir: /run/libvirt/qemu
swtpmStateDir: /run/libvirt/qemu/swtpm
cacheDir: /var/cache/libvirt/qemu
libDir: /var/lib/libvirt/qemu
swtpmStorageDir: /var/lib/libvirt/swtpm
defaultTLSx509certdir: /etc/pki/qemu
At this time all features present in the QEMU driver are available when
running in embedded mode, availability matching whether the embedded
driver is privileged or unprivileged.
Signed-off-by: Daniel P. Berrangé <berrange(a)redhat.com>
---
docs/drvqemu.html.in | 84 +++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_conf.c | 38 +++++++++++++++++--
src/qemu/qemu_conf.h | 6 ++-
src/qemu/qemu_driver.c | 26 +++++++------
src/qemu/qemu_process.c | 15 ++++++--
tests/domaincapstest.c | 2 +-
tests/testutilsqemu.c | 2 +-
7 files changed, 151 insertions(+), 22 deletions(-)
diff --git a/docs/drvqemu.html.in b/docs/drvqemu.html.in
index 294117ee1f..4bcf5ac002 100644
--- a/docs/drvqemu.html.in
+++ b/docs/drvqemu.html.in
@@ -63,6 +63,90 @@
qemu+tcp://example.com/system (remote access, SASl/Kerberos)
qemu+ssh://root@example.com/system (remote access, SSH tunnelled)
</pre>
+ <h3><a id="uriembedded">Embedded driver</a></h3>
+
+ <p>
+ Since 6.0.0 the QEMU driver has experimental support for operating
+ in an embedded mode. In this scenario, rather than connecting to
+ the libvirtd daemon, the QEMU driver runs in the client application
+ process directly. To use this the client application must have
+ registered & be running an instance of the event loop. To open
+ the driver in embedded mode the app use the new URI path and specify
+ a virtual root directory under which the driver will create content.
+ </p>
+
+ <pre>
+ qemu:///embed?root=/some/dir
+ </pre>
+
+ <p>
+ Under the specified root directory the following locations will
+ be used
+ </p>
+
+ <pre>
+/some/dir
+ |
+ +- log
+ | |
+ | +- qemu
+ | +- swtpm
+ |
+ +- etc
+ | |
+ | +- qemu
+ | +- pki
+ | |
+ | +- qemu
+ |
+ +- run
+ | |
+ | +- qemu
+ | +- swtpm
+ |
+ +- cache
+ | |
+ | +- qemu
+ |
+ +- lib
+ |
+ +- qemu
+ +- swtpm
+ </pre>
+
+ <p>
+ Note that UNIX domain sockets used for QEMU virtual machines had
+ a maximum filename length of 108 characters. Bear this in mind
+ when picking a root directory to avoid risk of exhausting the
+ filename space. The application is responsible for recursively
+ purging the contents of this directory tree once they no longer
+ require a connection, though it can also be left intact for reuse
+ when opening a future connection.
+ </p>
+
+ <p>
+ Broadly speaking the range of functionality is intended to be
+ on a par with that seen when using the traditional system or
+ session libvirt connections to QEMU. The features will of course
+ differ depending on whether the application using the embedded
+ driver is running privileged or unprivileged. For example PCI
+ device assignment or TAP based networking are only available
+ when running privileged. While the embedded mode is still classed
+ as experimental some features may change their default settings
+ between releases.
+ </p>
+
+ <p>
+ By default if the application uses any APIs associated with
+ secondary drivers, these will result in a connection being
+ opened to the corresponding driver in libvirtd. For example,
+ this allows a virtual machine from the embedded QEMU to connect
+ its NIC to a virtual network or connect its disk to a storage
+ volume. Some of the secondary drivers will also be able to support
+ running in embedded mode. Currently this is supported by the
+ secrets driver, to allow for use of VMs with encrypted disks
+ </p>
+
<h2><a id="security">Driver security
architecture</a></h2>
<p>
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 30637b21ac..e2b4c26b9b 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -104,7 +104,8 @@ qemuDriverUnlock(virQEMUDriverPtr driver)
#endif
-virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged)
+virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged,
+ const char *root)
{
g_autoptr(virQEMUDriverConfig) cfg = NULL;
@@ -130,7 +131,24 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged)
cfg->cgroupControllers = -1; /* -1 == auto-detect */
- if (privileged) {
+ if (root != NULL) {
+ cfg->logDir = g_strdup_printf("%s/log/qemu", root);
+ cfg->swtpmLogDir = g_strdup_printf("%s/log/swtpm", root);
+ cfg->configBaseDir = g_strdup_printf("%s/etc", root);
+ cfg->stateDir = g_strdup_printf("%s/run/qemu", root);
+ cfg->swtpmStateDir = g_strdup_printf("%s/run/swtpm", root);
+ cfg->cacheDir = g_strdup_printf("%s/cache/qemu", root);
+ cfg->libDir = g_strdup_printf("%s/lib/qemu", root);
+ cfg->swtpmStorageDir = g_strdup_printf("%s/lib/swtpm", root);
+
+ cfg->saveDir = g_strdup_printf("%s/save", cfg->libDir);
+ cfg->snapshotDir = g_strdup_printf("%s/snapshot", cfg->libDir);
+ cfg->checkpointDir = g_strdup_printf("%s/checkpoint",
cfg->libDir);
+ cfg->autoDumpPath = g_strdup_printf("%s/dump", cfg->libDir);
+ cfg->channelTargetDir = g_strdup_printf("%s/channel/target",
cfg->libDir);
+ cfg->nvramDir = g_strdup_printf("%s/nvram", cfg->libDir);
+ cfg->memoryBackingDir = g_strdup_printf("%s/ram", cfg->libDir);
+ } else if (privileged) {
cfg->logDir = g_strdup_printf("%s/log/libvirt/qemu",
LOCALSTATEDIR);
cfg->swtpmLogDir = g_strdup_printf("%s/log/swtpm/libvirt/qemu",
@@ -194,6 +212,16 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged)
cfg->memoryBackingDir = g_strdup_printf("%s/qemu/ram",
cfg->configBaseDir);
cfg->swtpmStorageDir = g_strdup_printf("%s/qemu/swtpm",
cfg->configBaseDir);
+ }
+
+ if (privileged) {
+ if (!virDoesUserExist("tss") ||
+ virGetUserID("tss", &cfg->swtpm_user) < 0)
+ cfg->swtpm_user = 0; /* fall back to root */
+ if (!virDoesGroupExist("tss") ||
+ virGetGroupID("tss", &cfg->swtpm_group) < 0)
+ cfg->swtpm_group = 0; /* fall back to root */
+ } else {
cfg->swtpm_user = (uid_t)-1;
cfg->swtpm_group = (gid_t)-1;
}
@@ -206,7 +234,11 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged)
* This will then be used as a fallback if the service specific
* directory doesn't exist (although we don't check if this exists).
*/
- cfg->defaultTLSx509certdir = g_strdup(SYSCONFDIR "/pki/qemu");
+ if (root == NULL) {
+ cfg->defaultTLSx509certdir = g_strdup(SYSCONFDIR "pki/qemu");
+ } else {
+ cfg->defaultTLSx509certdir = g_strdup_printf("%s/etc/pki/qemu",
root);
+ }
cfg->vncListen = g_strdup(VIR_LOOPBACK_IPV4_ADDR);
cfg->spiceListen = g_strdup(VIR_LOOPBACK_IPV4_ADDR);
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index 95b33a1093..5de9d2f913 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -241,8 +241,9 @@ struct _virQEMUDriver {
/* Atomic inc/dec only */
unsigned int nactive;
- /* Immutable value */
+ /* Immutable values */
bool privileged;
+ bool embedded;
/* Immutable pointers. Caller must provide locking */
virStateInhibitCallback inhibitCallback;
@@ -301,7 +302,8 @@ struct _virQEMUDriver {
virHashAtomicPtr migrationErrors;
};
-virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged);
+virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged,
+ const char *root);
int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg,
const char *filename,
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 31fe921ee3..e51086ea5e 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -658,12 +658,6 @@ qemuStateInitialize(bool privileged,
bool autostart = true;
size_t i;
- if (root != NULL) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("Driver does not support embedded mode"));
- return -1;
- }
-
if (VIR_ALLOC(qemu_driver) < 0)
return VIR_DRV_STATE_INIT_ERROR;
@@ -680,6 +674,7 @@ qemuStateInitialize(bool privileged,
qemu_driver->inhibitOpaque = opaque;
qemu_driver->privileged = privileged;
+ qemu_driver->embedded = root != NULL;
if (!(qemu_driver->domains = virDomainObjListNew()))
goto error;
@@ -693,7 +688,7 @@ qemuStateInitialize(bool privileged,
if (privileged)
qemu_driver->hostsysinfo = virSysinfoRead();
- if (!(qemu_driver->config = cfg = virQEMUDriverConfigNew(privileged)))
+ if (!(qemu_driver->config = cfg = virQEMUDriverConfigNew(privileged, root)))
goto error;
if (!(driverConf = g_strdup_printf("%s/qemu.conf",
cfg->configBaseDir)))
@@ -1208,10 +1203,18 @@ static virDrvOpenStatus qemuConnectOpen(virConnectPtr conn,
return VIR_DRV_OPEN_ERROR;
}
- if (!virConnectValidateURIPath(conn->uri->path,
- "qemu",
- virQEMUDriverIsPrivileged(qemu_driver)))
- return VIR_DRV_OPEN_ERROR;
+ if (qemu_driver->embedded) {
+ if (STRNEQ(conn->uri->path, "/embed")) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("URI must be qemu:///embed"));
+ return VIR_DRV_OPEN_ERROR;
+ }
+ } else {
+ if (!virConnectValidateURIPath(conn->uri->path,
+ "qemu",
+ virQEMUDriverIsPrivileged(qemu_driver)))
+ return VIR_DRV_OPEN_ERROR;
+ }
if (virConnectOpenEnsureACL(conn) < 0)
return VIR_DRV_OPEN_ERROR;
@@ -22950,6 +22953,7 @@ static virHypervisorDriver qemuHypervisorDriver = {
static virConnectDriver qemuConnectDriver = {
.localOnly = true,
.uriSchemes = (const char *[]){ "qemu", NULL },
+ .embeddable = true,
.hypervisorDriver = &qemuHypervisorDriver,
};
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index a588ee25f8..c812fa680c 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -6717,10 +6717,17 @@ qemuProcessLaunch(virConnectPtr conn,
cfg = virQEMUDriverGetConfig(driver);
- if ((flags & VIR_QEMU_PROCESS_START_AUTODESTROY) && !conn) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Domain autodestroy requires a connection handle"));
- return -1;
+ if (flags & VIR_QEMU_PROCESS_START_AUTODESTROY) {
+ if (!conn) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Domain autodestroy requires a connection
handle"));
+ return -1;
+ }
+ if (driver->embedded) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Domain autodestroy not supported for embedded drivers
yet"));
+ return -1;
+ }
}
hookData.vm = vm;
diff --git a/tests/domaincapstest.c b/tests/domaincapstest.c
index c6700f7e78..16a37b37fe 100644
--- a/tests/domaincapstest.c
+++ b/tests/domaincapstest.c
@@ -373,7 +373,7 @@ mymain(void)
#endif
#if WITH_QEMU
- virQEMUDriverConfigPtr cfg = virQEMUDriverConfigNew(false);
+ virQEMUDriverConfigPtr cfg = virQEMUDriverConfigNew(false, "");
if (!cfg)
return EXIT_FAILURE;
diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c
index 7b9e427463..3a21dfac71 100644
--- a/tests/testutilsqemu.c
+++ b/tests/testutilsqemu.c
@@ -694,7 +694,7 @@ int qemuTestDriverInit(virQEMUDriver *driver)
if (virMutexInit(&driver->lock) < 0)
return -1;
- driver->config = virQEMUDriverConfigNew(false);
+ driver->config = virQEMUDriverConfigNew(false, "");
if (!driver->config)
goto error;
--
2.23.0