diff --git a/docs/drvqemu.html.in b/docs/drvqemu.html.in index 8beb28655c..f6abb45706 100644 --- a/docs/drvqemu.html.in +++ b/docs/drvqemu.html.in @@ -63,6 +63,105 @@ qemu+tcp://example.com/system (remote access, SASl/Kerberos) qemu+ssh://root@example.com/system (remote access, SSH tunnelled) +
+ 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. +
+ ++ qemu:///embed?root=/some/dir ++ +
+ 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. +
+ ++ 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 +
+ ++ Under the specified root directory the following locations will + be used +
+ ++/some/dir + | + +- log + | | + | +- qemu + | +- swtpm + | + +- etc + | | + | +- qemu + | +- pki + | | + | +- qemu + | + +- run + | | + | +- qemu + | +- swtpm + | + +- cache + | | + | +- qemu + | + +- lib + | + +- qemu + +- swtpm ++ +
+ 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. +
+ ++ To use the QEMU driver in embedded mode the application must + register an event loop with libvirt. Many of the QEMU driver + API calls will rely on the event loop processing data. With this + in mind, applications must NEVER invoke API + calls from the event loop thread itself, only other threads. + Not following this rule will lead to deadlocks in the API. + This restriction is intended to be lifted in a future release + of libvirt, once QMP processing moves to a dedicated thread. +
+diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index e500da1515..00801ef01b 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; @@ -114,7 +115,11 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged) if (!(cfg = virObjectNew(virQEMUDriverConfigClass))) return NULL; - cfg->uri = privileged ? "qemu:///system" : "qemu:///session"; + if (root) { + cfg->uri = g_strdup_printf("qemu:///embed?root=%s", root); + } else { + cfg->uri = g_strdup(privileged ? "qemu:///system" : "qemu:///session"); + } if (privileged) { if (virGetUserID(QEMU_USER, &cfg->user) < 0) @@ -130,7 +135,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", @@ -189,6 +211,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; } @@ -201,7 +233,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); @@ -264,6 +300,7 @@ static void virQEMUDriverConfigDispose(void *obj) virBitmapFree(cfg->namespaces); virStringListFree(cfg->cgroupDeviceACL); + VIR_FREE(cfg->uri); VIR_FREE(cfg->configBaseDir); VIR_FREE(cfg->configDir); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index b9401635d7..4ab1999568 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -75,7 +75,7 @@ typedef virQEMUDriverConfig *virQEMUDriverConfigPtr; struct _virQEMUDriverConfig { virObject parent; - const char *uri; + char *uri; uid_t user; gid_t group; @@ -240,8 +240,9 @@ struct _virQEMUDriver { /* Atomic inc/dec only */ unsigned int nactive; - /* Immutable value */ + /* Immutable values */ bool privileged; + char *embeddedRoot; /* Immutable pointers. Caller must provide locking */ virStateInhibitCallback inhibitCallback; @@ -313,7 +314,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 de857f1903..94703c67c2 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -645,12 +645,6 @@ qemuStateInitialize(bool privileged, const char *defsecmodel = NULL; g_autofree virSecurityManagerPtr *sec_managers = NULL; - 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; @@ -668,6 +662,8 @@ qemuStateInitialize(bool privileged, qemu_driver->privileged = privileged; qemu_driver->hostarch = virArchFromHost(); + if (root != NULL) + qemu_driver->embeddedRoot = g_strdup(root); if (!(qemu_driver->domains = virDomainObjListNew())) goto error; @@ -681,7 +677,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))) @@ -1185,10 +1181,30 @@ 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->embeddedRoot) { + const char *root = virURIGetParam(conn->uri, "root"); + if (!root) + return VIR_DRV_OPEN_ERROR; + + if (STRNEQ(conn->uri->path, "/embed")) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("URI must be qemu:///embed")); + return VIR_DRV_OPEN_ERROR; + } + + if (STRNEQ(root, qemu_driver->embeddedRoot)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot open embedded driver at path '%s', " + "already open with path '%s'"), + root, qemu_driver->embeddedRoot); + 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; @@ -23416,6 +23432,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 1cac0dc886..57a60c568a 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6683,10 +6683,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->embeddedRoot) { + 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 9f5eab3230..fb803eaa47 100644 --- a/tests/domaincapstest.c +++ b/tests/domaincapstest.c @@ -369,7 +369,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 9db0cb44c6..39544c548f 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -380,8 +380,7 @@ int qemuTestDriverInit(virQEMUDriver *driver) return -1; driver->hostarch = virArchFromHost(); - - driver->config = virQEMUDriverConfigNew(false); + driver->config = virQEMUDriverConfigNew(false, ""); if (!driver->config) goto error;