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>
+ 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>
+
+ <h4><a id="embedTree">Directory tree</a></h4>
+
+ <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>
+
+ <h4><a id="embedAPI">API usage with event loop></a></h4>
+
+ <p>
+ 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 <strong>NEVER</strong> 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.
+ </p>
+
<h2><a id="security">Driver security architecture</a></h2>
<p>
#endif
-virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged)
+virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged,
+ const char *root)
{
g_autoptr(virQEMUDriverConfig) cfg = NULL;
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)
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",
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;
}
* 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);
virBitmapFree(cfg->namespaces);
virStringListFree(cfg->cgroupDeviceACL);
+ VIR_FREE(cfg->uri);
VIR_FREE(cfg->configBaseDir);
VIR_FREE(cfg->configDir);
struct _virQEMUDriverConfig {
virObject parent;
- const char *uri;
+ char *uri;
uid_t user;
gid_t group;
/* Atomic inc/dec only */
unsigned int nactive;
- /* Immutable value */
+ /* Immutable values */
bool privileged;
+ char *embeddedRoot;
/* Immutable pointers. Caller must provide locking */
virStateInhibitCallback inhibitCallback;
virHashAtomicPtr migrationErrors;
};
-virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged);
+virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged,
+ const char *root);
int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg,
const char *filename,
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;
qemu_driver->privileged = privileged;
qemu_driver->hostarch = virArchFromHost();
+ if (root != NULL)
+ qemu_driver->embeddedRoot = g_strdup(root);
if (!(qemu_driver->domains = virDomainObjListNew()))
goto error;
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)))
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;
static virConnectDriver qemuConnectDriver = {
.localOnly = true,
.uriSchemes = (const char *[]){ "qemu", NULL },
+ .embeddable = true,
.hypervisorDriver = &qemuHypervisorDriver,
};
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;
#endif
#if WITH_QEMU
- virQEMUDriverConfigPtr cfg = virQEMUDriverConfigNew(false);
+ virQEMUDriverConfigPtr cfg = virQEMUDriverConfigNew(false, "");
if (!cfg)
return EXIT_FAILURE;
return -1;
driver->hostarch = virArchFromHost();
-
- driver->config = virQEMUDriverConfigNew(false);
+ driver->config = virQEMUDriverConfigNew(false, "");
if (!driver->config)
goto error;