libvirt/src/qemu/qemu_conf.c
Michal Privoznik 40a162f83e qemu: Don't cache NUMA caps
In v6.0.0-rc1~439 (and friends) we tried to cache NUMA
capabilities because we assumed they are immutable. And to some
extent they are (NUMA hotplug is not a thing, is it). However,
our capabilities contain also some runtime info that can change,
e.g. hugepages pool allocation sizes or total amount of memory
per node (host side memory hotplug might change the value).

Because of the caching we might not be reporting the correct
runtime info in 'virsh capabilities'.

The NUMA caps are used in three places:

  1) 'virsh capabilities'
  2) domain startup, when parsing numad reply
  3) parsing domain private data XML

In cases 2) and 3) we need NUMA caps to construct list of
physical CPUs that belong to NUMA nodes from numad reply. And
while this may seem static, it's not really because of possible
CPU hotplug on physical host.

There are two possible approaches:

  1) build a validation mechanism that would invalidate the
     cached NUMA caps, or
  2) drop the caching and construct NUMA caps from scratch on
     each use.

In this commit, the latter approach is implemented, because it's
easier.

Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1819058
Fixes: 1a1d848694
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
2020-12-07 11:32:40 +01:00

2036 lines
63 KiB
C

/*
* qemu_conf.c: QEMU configuration management
*
* Copyright (C) 2006-2014 Red Hat, Inc.
* Copyright (C) 2006 Daniel P. Berrange
*
* 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
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include "virerror.h"
#include "qemu_conf.h"
#include "qemu_capabilities.h"
#include "qemu_domain.h"
#include "qemu_firmware.h"
#include "qemu_namespace.h"
#include "qemu_security.h"
#include "viruuid.h"
#include "virbuffer.h"
#include "virconf.h"
#include "viralloc.h"
#include "datatypes.h"
#include "virxml.h"
#include "virlog.h"
#include "cpu/cpu.h"
#include "domain_driver.h"
#include "domain_nwfilter.h"
#include "virfile.h"
#include "virsocket.h"
#include "virstring.h"
#include "storage_conf.h"
#include "virutil.h"
#include "configmake.h"
#include "security/security_util.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
VIR_LOG_INIT("qemu.qemu_conf");
/* These are only defaults, they can be changed now in qemu.conf and
* explicitly specified port is checked against these two (makes
* sense to limit the values).
*
* This limitation is mentioned in qemu.conf, so bear in mind that the
* configuration file should reflect any changes made to these values.
*/
#define QEMU_REMOTE_PORT_MIN 5900
#define QEMU_REMOTE_PORT_MAX 65535
#define QEMU_WEBSOCKET_PORT_MIN 5700
#define QEMU_WEBSOCKET_PORT_MAX 65535
#define QEMU_MIGRATION_PORT_MIN 49152
#define QEMU_MIGRATION_PORT_MAX 49215
static virClassPtr virQEMUDriverConfigClass;
static void virQEMUDriverConfigDispose(void *obj);
static int virQEMUConfigOnceInit(void)
{
if (!VIR_CLASS_NEW(virQEMUDriverConfig, virClassForObject()))
return -1;
return 0;
}
VIR_ONCE_GLOBAL_INIT(virQEMUConfig);
static void
qemuDriverLock(virQEMUDriverPtr driver)
{
virMutexLock(&driver->lock);
}
static void
qemuDriverUnlock(virQEMUDriverPtr driver)
{
virMutexUnlock(&driver->lock);
}
#ifndef DEFAULT_LOADER_NVRAM
# define DEFAULT_LOADER_NVRAM \
"/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd:" \
"/usr/share/OVMF/OVMF_CODE.secboot.fd:/usr/share/OVMF/OVMF_VARS.fd:" \
"/usr/share/AAVMF/AAVMF_CODE.fd:/usr/share/AAVMF/AAVMF_VARS.fd:" \
"/usr/share/AAVMF/AAVMF32_CODE.fd:/usr/share/AAVMF/AAVMF32_VARS.fd"
#endif
virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged,
const char *root)
{
g_autoptr(virQEMUDriverConfig) cfg = NULL;
if (virQEMUConfigInitialize() < 0)
return NULL;
if (!(cfg = virObjectNew(virQEMUDriverConfigClass)))
return NULL;
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)
return NULL;
if (virGetGroupID(QEMU_GROUP, &cfg->group) < 0)
return NULL;
} else {
cfg->user = (uid_t)-1;
cfg->group = (gid_t)-1;
}
cfg->dynamicOwnership = privileged;
if (privileged)
cfg->rememberOwner = virSecurityXATTRNamespaceDefined();
else
cfg->rememberOwner = false;
cfg->cgroupControllers = -1; /* -1 == auto-detect */
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",
LOCALSTATEDIR);
cfg->configBaseDir = g_strdup(SYSCONFDIR "/libvirt");
cfg->stateDir = g_strdup_printf("%s/libvirt/qemu", RUNSTATEDIR);
cfg->swtpmStateDir = g_strdup_printf("%s/libvirt/qemu/swtpm", RUNSTATEDIR);
cfg->cacheDir = g_strdup_printf("%s/cache/libvirt/qemu", LOCALSTATEDIR);
cfg->libDir = g_strdup_printf("%s/lib/libvirt/qemu", LOCALSTATEDIR);
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);
cfg->swtpmStorageDir = g_strdup_printf("%s/lib/libvirt/swtpm",
LOCALSTATEDIR);
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 {
g_autofree char *rundir = NULL;
g_autofree char *cachedir = NULL;
cachedir = virGetUserCacheDirectory();
cfg->logDir = g_strdup_printf("%s/qemu/log", cachedir);
cfg->swtpmLogDir = g_strdup_printf("%s/qemu/log", cachedir);
cfg->cacheDir = g_strdup_printf("%s/qemu/cache", cachedir);
rundir = virGetUserRuntimeDirectory();
cfg->stateDir = g_strdup_printf("%s/qemu/run", rundir);
cfg->swtpmStateDir = g_strdup_printf("%s/swtpm", cfg->stateDir);
cfg->configBaseDir = virGetUserConfigDirectory();
cfg->libDir = g_strdup_printf("%s/qemu/lib", cfg->configBaseDir);
cfg->saveDir = g_strdup_printf("%s/qemu/save", cfg->configBaseDir);
cfg->snapshotDir = g_strdup_printf("%s/qemu/snapshot", cfg->configBaseDir);
cfg->checkpointDir = g_strdup_printf("%s/qemu/checkpoint",
cfg->configBaseDir);
cfg->autoDumpPath = g_strdup_printf("%s/qemu/dump", cfg->configBaseDir);
cfg->channelTargetDir = g_strdup_printf("%s/qemu/channel/target",
cfg->configBaseDir);
cfg->nvramDir = g_strdup_printf("%s/qemu/nvram", cfg->configBaseDir);
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;
}
cfg->configDir = g_strdup_printf("%s/qemu", cfg->configBaseDir);
cfg->autostartDir = g_strdup_printf("%s/qemu/autostart", cfg->configBaseDir);
cfg->slirpStateDir = g_strdup_printf("%s/slirp", cfg->stateDir);
cfg->dbusStateDir = g_strdup_printf("%s/dbus", cfg->stateDir);
/* Set the default directory to find TLS X.509 certificates.
* This will then be used as a fallback if the service specific
* directory doesn't exist (although we don't check if this exists).
*/
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);
cfg->remotePortMin = QEMU_REMOTE_PORT_MIN;
cfg->remotePortMax = QEMU_REMOTE_PORT_MAX;
cfg->webSocketPortMin = QEMU_WEBSOCKET_PORT_MIN;
cfg->webSocketPortMax = QEMU_WEBSOCKET_PORT_MAX;
cfg->migrationPortMin = QEMU_MIGRATION_PORT_MIN;
cfg->migrationPortMax = QEMU_MIGRATION_PORT_MAX;
/* For privileged driver, try and find hugetlbfs mounts automatically.
* Non-privileged driver requires admin to create a dir for the
* user, chown it, and then let user configure it manually. */
if (privileged &&
virFileFindHugeTLBFS(&cfg->hugetlbfs, &cfg->nhugetlbfs) < 0) {
/* This however is not implemented on all platforms. */
if (virGetLastErrorCode() != VIR_ERR_NO_SUPPORT)
return NULL;
}
cfg->bridgeHelperName = g_strdup(QEMU_BRIDGE_HELPER);
cfg->prHelperName = g_strdup(QEMU_PR_HELPER);
cfg->slirpHelperName = g_strdup(QEMU_SLIRP_HELPER);
cfg->dbusDaemonName = g_strdup(QEMU_DBUS_DAEMON);
cfg->securityDefaultConfined = true;
cfg->securityRequireConfined = false;
cfg->keepAliveInterval = 5;
cfg->keepAliveCount = 5;
cfg->seccompSandbox = -1;
cfg->logTimestamp = true;
cfg->glusterDebugLevel = 4;
cfg->stdioLogD = true;
cfg->namespaces = virBitmapNew(QEMU_DOMAIN_NS_LAST);
if (privileged &&
qemuDomainNamespaceAvailable(QEMU_DOMAIN_NS_MOUNT) &&
virBitmapSetBit(cfg->namespaces, QEMU_DOMAIN_NS_MOUNT) < 0)
return NULL;
if (virFirmwareParseList(DEFAULT_LOADER_NVRAM,
&cfg->firmwares,
&cfg->nfirmwares) < 0)
return NULL;
return g_steal_pointer(&cfg);
}
static void virQEMUDriverConfigDispose(void *obj)
{
virQEMUDriverConfigPtr cfg = obj;
virBitmapFree(cfg->namespaces);
g_strfreev(cfg->cgroupDeviceACL);
VIR_FREE(cfg->uri);
VIR_FREE(cfg->configBaseDir);
VIR_FREE(cfg->configDir);
VIR_FREE(cfg->autostartDir);
VIR_FREE(cfg->logDir);
VIR_FREE(cfg->swtpmLogDir);
VIR_FREE(cfg->stateDir);
VIR_FREE(cfg->swtpmStateDir);
VIR_FREE(cfg->slirpStateDir);
VIR_FREE(cfg->dbusStateDir);
VIR_FREE(cfg->libDir);
VIR_FREE(cfg->cacheDir);
VIR_FREE(cfg->saveDir);
VIR_FREE(cfg->snapshotDir);
VIR_FREE(cfg->checkpointDir);
VIR_FREE(cfg->channelTargetDir);
VIR_FREE(cfg->nvramDir);
VIR_FREE(cfg->defaultTLSx509certdir);
VIR_FREE(cfg->defaultTLSx509secretUUID);
VIR_FREE(cfg->vncTLSx509certdir);
VIR_FREE(cfg->vncTLSx509secretUUID);
VIR_FREE(cfg->vncListen);
VIR_FREE(cfg->vncPassword);
VIR_FREE(cfg->vncSASLdir);
VIR_FREE(cfg->spiceTLSx509certdir);
VIR_FREE(cfg->spiceListen);
VIR_FREE(cfg->spicePassword);
VIR_FREE(cfg->spiceSASLdir);
VIR_FREE(cfg->chardevTLSx509certdir);
VIR_FREE(cfg->chardevTLSx509secretUUID);
VIR_FREE(cfg->vxhsTLSx509certdir);
VIR_FREE(cfg->vxhsTLSx509secretUUID);
VIR_FREE(cfg->nbdTLSx509certdir);
VIR_FREE(cfg->nbdTLSx509secretUUID);
VIR_FREE(cfg->migrateTLSx509certdir);
VIR_FREE(cfg->migrateTLSx509secretUUID);
VIR_FREE(cfg->backupTLSx509certdir);
VIR_FREE(cfg->backupTLSx509secretUUID);
while (cfg->nhugetlbfs) {
cfg->nhugetlbfs--;
VIR_FREE(cfg->hugetlbfs[cfg->nhugetlbfs].mnt_dir);
}
VIR_FREE(cfg->hugetlbfs);
VIR_FREE(cfg->bridgeHelperName);
VIR_FREE(cfg->prHelperName);
VIR_FREE(cfg->slirpHelperName);
VIR_FREE(cfg->dbusDaemonName);
VIR_FREE(cfg->saveImageFormat);
VIR_FREE(cfg->dumpImageFormat);
VIR_FREE(cfg->snapshotImageFormat);
VIR_FREE(cfg->autoDumpPath);
g_strfreev(cfg->securityDriverNames);
VIR_FREE(cfg->lockManagerName);
virFirmwareFreeList(cfg->firmwares, cfg->nfirmwares);
VIR_FREE(cfg->memoryBackingDir);
VIR_FREE(cfg->swtpmStorageDir);
g_strfreev(cfg->capabilityfilters);
}
static int
virQEMUDriverConfigHugeTLBFSInit(virHugeTLBFSPtr hugetlbfs,
const char *path,
bool deflt)
{
hugetlbfs->mnt_dir = g_strdup(path);
if (virFileGetHugepageSize(path, &hugetlbfs->size) < 0)
return -1;
hugetlbfs->deflt = deflt;
return 0;
}
static int
virQEMUDriverConfigLoadDefaultTLSEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
int rv;
if ((rv = virConfGetValueString(conf, "default_tls_x509_cert_dir", &cfg->defaultTLSx509certdir)) < 0)
return -1;
cfg->defaultTLSx509certdirPresent = (rv == 1);
if ((rv = virConfGetValueBool(conf, "default_tls_x509_verify", &cfg->defaultTLSx509verify)) < 0)
return -1;
if (rv == 1)
cfg->defaultTLSx509verifyPresent = true;
if (virConfGetValueString(conf, "default_tls_x509_secret_uuid",
&cfg->defaultTLSx509secretUUID) < 0)
return -1;
return 0;
}
static int
virQEMUDriverConfigLoadVNCEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
int rv;
if (virConfGetValueBool(conf, "vnc_auto_unix_socket", &cfg->vncAutoUnixSocket) < 0)
return -1;
if (virConfGetValueBool(conf, "vnc_tls", &cfg->vncTLS) < 0)
return -1;
if ((rv = virConfGetValueBool(conf, "vnc_tls_x509_verify", &cfg->vncTLSx509verify)) < 0)
return -1;
if (rv == 1)
cfg->vncTLSx509verifyPresent = true;
if (virConfGetValueString(conf, "vnc_tls_x509_cert_dir", &cfg->vncTLSx509certdir) < 0)
return -1;
if (virConfGetValueString(conf, "vnc_tls_x509_secret_uuid", &cfg->vncTLSx509secretUUID) < 0)
return -1;
if (virConfGetValueString(conf, "vnc_listen", &cfg->vncListen) < 0)
return -1;
if (virConfGetValueString(conf, "vnc_password", &cfg->vncPassword) < 0)
return -1;
if (virConfGetValueBool(conf, "vnc_sasl", &cfg->vncSASL) < 0)
return -1;
if (virConfGetValueString(conf, "vnc_sasl_dir", &cfg->vncSASLdir) < 0)
return -1;
if (virConfGetValueBool(conf, "vnc_allow_host_audio", &cfg->vncAllowHostAudio) < 0)
return -1;
return 0;
}
static int
virQEMUDriverConfigLoadNographicsEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
return virConfGetValueBool(conf, "nographics_allow_host_audio", &cfg->nogfxAllowHostAudio);
}
static int
virQEMUDriverConfigLoadSPICEEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
if (virConfGetValueBool(conf, "spice_tls", &cfg->spiceTLS) < 0)
return -1;
if (virConfGetValueString(conf, "spice_tls_x509_cert_dir", &cfg->spiceTLSx509certdir) < 0)
return -1;
if (virConfGetValueBool(conf, "spice_sasl", &cfg->spiceSASL) < 0)
return -1;
if (virConfGetValueString(conf, "spice_sasl_dir", &cfg->spiceSASLdir) < 0)
return -1;
if (virConfGetValueString(conf, "spice_listen", &cfg->spiceListen) < 0)
return -1;
if (virConfGetValueString(conf, "spice_password", &cfg->spicePassword) < 0)
return -1;
if (virConfGetValueBool(conf, "spice_auto_unix_socket", &cfg->spiceAutoUnixSocket) < 0)
return -1;
return 0;
}
static int
virQEMUDriverConfigLoadSpecificTLSEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
int rv;
if (virConfGetValueBool(conf, "vxhs_tls", &cfg->vxhsTLS) < 0)
return -1;
if (virConfGetValueBool(conf, "nbd_tls", &cfg->nbdTLS) < 0)
return -1;
if (virConfGetValueBool(conf, "chardev_tls", &cfg->chardevTLS) < 0)
return -1;
if (virConfGetValueBool(conf, "migrate_tls_force", &cfg->migrateTLSForce) < 0)
return -1;
#define GET_CONFIG_TLS_CERTINFO_COMMON(val) \
do { \
if (virConfGetValueString(conf, #val "_tls_x509_cert_dir", \
&cfg->val## TLSx509certdir) < 0) \
return -1; \
if (virConfGetValueString(conf, \
#val "_tls_x509_secret_uuid", \
&cfg->val## TLSx509secretUUID) < 0) \
return -1; \
} while (0)
#define GET_CONFIG_TLS_CERTINFO_SERVER(val) \
do { \
if ((rv = virConfGetValueBool(conf, #val "_tls_x509_verify", \
&cfg->val## TLSx509verify)) < 0) \
return -1; \
if (rv == 1) \
cfg->val## TLSx509verifyPresent = true; \
} while (0)
GET_CONFIG_TLS_CERTINFO_COMMON(chardev);
GET_CONFIG_TLS_CERTINFO_SERVER(chardev);
GET_CONFIG_TLS_CERTINFO_COMMON(migrate);
GET_CONFIG_TLS_CERTINFO_SERVER(migrate);
GET_CONFIG_TLS_CERTINFO_COMMON(backup);
GET_CONFIG_TLS_CERTINFO_SERVER(backup);
GET_CONFIG_TLS_CERTINFO_COMMON(vxhs);
GET_CONFIG_TLS_CERTINFO_COMMON(nbd);
#undef GET_CONFIG_TLS_CERTINFO_COMMON
#undef GET_CONFIG_TLS_CERTINFO_SERVER
return 0;
}
static int
virQEMUDriverConfigLoadRemoteDisplayEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf,
const char *filename)
{
if (virConfGetValueUInt(conf, "remote_websocket_port_min", &cfg->webSocketPortMin) < 0)
return -1;
if (cfg->webSocketPortMin < QEMU_WEBSOCKET_PORT_MIN) {
/* if the port is too low, we can't get the display name
* to tell to vnc (usually subtract 5700, e.g. localhost:1
* for port 5701) */
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s: remote_websocket_port_min: port must be greater "
"than or equal to %d"),
filename, QEMU_WEBSOCKET_PORT_MIN);
return -1;
}
if (virConfGetValueUInt(conf, "remote_websocket_port_max", &cfg->webSocketPortMax) < 0)
return -1;
if (cfg->webSocketPortMax > QEMU_WEBSOCKET_PORT_MAX ||
cfg->webSocketPortMax < cfg->webSocketPortMin) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s: remote_websocket_port_max: port must be between "
"the minimal port and %d"),
filename, QEMU_WEBSOCKET_PORT_MAX);
return -1;
}
if (cfg->webSocketPortMin > cfg->webSocketPortMax) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s: remote_websocket_port_min: min port must not be "
"greater than max port"), filename);
return -1;
}
if (virConfGetValueUInt(conf, "remote_display_port_min", &cfg->remotePortMin) < 0)
return -1;
if (cfg->remotePortMin < QEMU_REMOTE_PORT_MIN) {
/* if the port is too low, we can't get the display name
* to tell to vnc (usually subtract 5900, e.g. localhost:1
* for port 5901) */
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s: remote_display_port_min: port must be greater "
"than or equal to %d"),
filename, QEMU_REMOTE_PORT_MIN);
return -1;
}
if (virConfGetValueUInt(conf, "remote_display_port_max", &cfg->remotePortMax) < 0)
return -1;
if (cfg->remotePortMax > QEMU_REMOTE_PORT_MAX ||
cfg->remotePortMax < cfg->remotePortMin) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s: remote_display_port_max: port must be between "
"the minimal port and %d"),
filename, QEMU_REMOTE_PORT_MAX);
return -1;
}
if (cfg->remotePortMin > cfg->remotePortMax) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s: remote_display_port_min: min port must not be "
"greater than max port"), filename);
return -1;
}
return 0;
}
static int
virQEMUDriverConfigLoadSaveEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
if (virConfGetValueString(conf, "save_image_format", &cfg->saveImageFormat) < 0)
return -1;
if (virConfGetValueString(conf, "dump_image_format", &cfg->dumpImageFormat) < 0)
return -1;
if (virConfGetValueString(conf, "snapshot_image_format", &cfg->snapshotImageFormat) < 0)
return -1;
if (virConfGetValueString(conf, "auto_dump_path", &cfg->autoDumpPath) < 0)
return -1;
if (virConfGetValueBool(conf, "auto_dump_bypass_cache", &cfg->autoDumpBypassCache) < 0)
return -1;
if (virConfGetValueBool(conf, "auto_start_bypass_cache", &cfg->autoStartBypassCache) < 0)
return -1;
return 0;
}
static int
virQEMUDriverConfigLoadProcessEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
g_auto(GStrv) hugetlbfs = NULL;
g_autofree char *stdioHandler = NULL;
g_autofree char *corestr = NULL;
size_t i;
if (virConfGetValueStringList(conf, "hugetlbfs_mount", true,
&hugetlbfs) < 0)
return -1;
if (hugetlbfs) {
/* There already might be something autodetected. Avoid leaking it. */
while (cfg->nhugetlbfs) {
cfg->nhugetlbfs--;
VIR_FREE(cfg->hugetlbfs[cfg->nhugetlbfs].mnt_dir);
}
VIR_FREE(cfg->hugetlbfs);
cfg->nhugetlbfs = virStringListLength((const char *const *)hugetlbfs);
if (hugetlbfs[0])
cfg->hugetlbfs = g_new0(virHugeTLBFS, cfg->nhugetlbfs);
for (i = 0; hugetlbfs[i] != NULL; i++) {
if (virQEMUDriverConfigHugeTLBFSInit(&cfg->hugetlbfs[i],
hugetlbfs[i], i != 0) < 0)
return -1;
}
}
if (virConfGetValueString(conf, "bridge_helper", &cfg->bridgeHelperName) < 0)
return -1;
if (virConfGetValueString(conf, "pr_helper", &cfg->prHelperName) < 0)
return -1;
if (virConfGetValueString(conf, "slirp_helper", &cfg->slirpHelperName) < 0)
return -1;
if (virConfGetValueString(conf, "dbus_daemon", &cfg->dbusDaemonName) < 0)
return -1;
if (virConfGetValueBool(conf, "set_process_name", &cfg->setProcessName) < 0)
return -1;
if (virConfGetValueUInt(conf, "max_processes", &cfg->maxProcesses) < 0)
return -1;
if (virConfGetValueUInt(conf, "max_files", &cfg->maxFiles) < 0)
return -1;
if (virConfGetValueUInt(conf, "max_threads_per_process", &cfg->maxThreadsPerProc) < 0)
return -1;
if (virConfGetValueType(conf, "max_core") == VIR_CONF_STRING) {
if (virConfGetValueString(conf, "max_core", &corestr) < 0)
return -1;
if (STREQ(corestr, "unlimited")) {
cfg->maxCore = ULLONG_MAX;
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unknown core size '%s'"),
corestr);
return -1;
}
} else if (virConfGetValueULLong(conf, "max_core", &cfg->maxCore) < 0) {
return -1;
}
if (virConfGetValueBool(conf, "dump_guest_core", &cfg->dumpGuestCore) < 0)
return -1;
if (virConfGetValueString(conf, "stdio_handler", &stdioHandler) < 0)
return -1;
if (stdioHandler) {
if (STREQ(stdioHandler, "logd")) {
cfg->stdioLogD = true;
} else if (STREQ(stdioHandler, "file")) {
cfg->stdioLogD = false;
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unknown stdio handler %s"),
stdioHandler);
return -1;
}
}
return 0;
}
static int
virQEMUDriverConfigLoadDeviceEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
bool tmp;
int rv;
if (virConfGetValueBool(conf, "mac_filter", &cfg->macFilter) < 0)
return -1;
if (virConfGetValueBool(conf, "relaxed_acs_check", &cfg->relaxedACS) < 0)
return -1;
if (virConfGetValueString(conf, "lock_manager", &cfg->lockManagerName) < 0)
return -1;
if ((rv = virConfGetValueBool(conf, "allow_disk_format_probing", &tmp)) < 0)
return -1;
if (rv == 1 && tmp) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("allow_disk_format_probing is no longer supported"));
return -1;
}
return 0;
}
static int
virQEMUDriverConfigLoadRPCEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
if (virConfGetValueUInt(conf, "max_queued", &cfg->maxQueuedJobs) < 0)
return -1;
if (virConfGetValueInt(conf, "keepalive_interval", &cfg->keepAliveInterval) < 0)
return -1;
if (virConfGetValueUInt(conf, "keepalive_count", &cfg->keepAliveCount) < 0)
return -1;
return 0;
}
static int
virQEMUDriverConfigLoadNetworkEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf,
const char *filename)
{
if (virConfGetValueUInt(conf, "migration_port_min", &cfg->migrationPortMin) < 0)
return -1;
if (cfg->migrationPortMin <= 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s: migration_port_min: port must be greater than 0"),
filename);
return -1;
}
if (virConfGetValueUInt(conf, "migration_port_max", &cfg->migrationPortMax) < 0)
return -1;
if (cfg->migrationPortMax > 65535 ||
cfg->migrationPortMax < cfg->migrationPortMin) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s: migration_port_max: port must be between "
"the minimal port %d and 65535"),
filename, cfg->migrationPortMin);
return -1;
}
if (virConfGetValueString(conf, "migration_host", &cfg->migrateHost) < 0)
return -1;
virStringStripIPv6Brackets(cfg->migrateHost);
if (cfg->migrateHost &&
(STRPREFIX(cfg->migrateHost, "localhost") ||
virSocketAddrIsNumericLocalhost(cfg->migrateHost))) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("migration_host must not be the address of"
" the local machine: %s"),
cfg->migrateHost);
return -1;
}
if (virConfGetValueString(conf, "migration_address", &cfg->migrationAddress) < 0)
return -1;
virStringStripIPv6Brackets(cfg->migrationAddress);
if (cfg->migrationAddress &&
(STRPREFIX(cfg->migrationAddress, "localhost") ||
virSocketAddrIsNumericLocalhost(cfg->migrationAddress))) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("migration_address must not be the address of"
" the local machine: %s"),
cfg->migrationAddress);
return -1;
}
return 0;
}
static int
virQEMUDriverConfigLoadLogEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
return virConfGetValueBool(conf, "log_timestamp", &cfg->logTimestamp);
}
static int
virQEMUDriverConfigLoadNVRAMEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf,
bool privileged)
{
g_auto(GStrv) nvram = NULL;
size_t i;
if (virConfGetValueStringList(conf, "nvram", false, &nvram) < 0)
return -1;
if (nvram) {
g_auto(GStrv) fwList = NULL;
virFirmwareFreeList(cfg->firmwares, cfg->nfirmwares);
cfg->firmwares = NULL;
cfg->nfirmwares = 0;
if (qemuFirmwareFetchConfigs(&fwList, privileged) < 0)
return -1;
if (fwList) {
VIR_WARN("Obsolete nvram variable is set while firmware metadata "
"files found. Note that the nvram config file variable is "
"going to be ignored.");
return 0;
}
cfg->nfirmwares = virStringListLength((const char *const *)nvram);
cfg->firmwares = g_new0(virFirmwarePtr, cfg->nfirmwares);
for (i = 0; nvram[i] != NULL; i++) {
cfg->firmwares[i] = g_new0(virFirmware, 1);
if (virFirmwareParse(nvram[i], cfg->firmwares[i]) < 0)
return -1;
}
}
return 0;
}
static int
virQEMUDriverConfigLoadDebugEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
if (virConfGetValueUInt(conf, "gluster_debug_level", &cfg->glusterDebugLevel) < 0)
return -1;
if (virConfGetValueBool(conf, "virtiofsd_debug", &cfg->virtiofsdDebug) < 0)
return -1;
return 0;
}
static int
virQEMUDriverConfigLoadSecurityEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf,
bool privileged)
{
g_auto(GStrv) controllers = NULL;
g_auto(GStrv) namespaces = NULL;
g_autofree char *user = NULL;
g_autofree char *group = NULL;
size_t i, j;
if (virConfGetValueStringList(conf, "security_driver", true, &cfg->securityDriverNames) < 0)
return -1;
for (i = 0; cfg->securityDriverNames && cfg->securityDriverNames[i] != NULL; i++) {
for (j = i + 1; cfg->securityDriverNames[j] != NULL; j++) {
if (STREQ(cfg->securityDriverNames[i],
cfg->securityDriverNames[j])) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("Duplicate security driver %s"),
cfg->securityDriverNames[i]);
return -1;
}
}
}
if (virConfGetValueBool(conf, "security_default_confined", &cfg->securityDefaultConfined) < 0)
return -1;
if (virConfGetValueBool(conf, "security_require_confined", &cfg->securityRequireConfined) < 0)
return -1;
if (virConfGetValueString(conf, "user", &user) < 0)
return -1;
if (user && virGetUserID(user, &cfg->user) < 0)
return -1;
if (virConfGetValueString(conf, "group", &group) < 0)
return -1;
if (group && virGetGroupID(group, &cfg->group) < 0)
return -1;
if (virConfGetValueBool(conf, "dynamic_ownership", &cfg->dynamicOwnership) < 0)
return -1;
if (virConfGetValueBool(conf, "remember_owner", &cfg->rememberOwner) < 0)
return -1;
if (virConfGetValueStringList(conf, "cgroup_controllers", false,
&controllers) < 0)
return -1;
if (controllers) {
cfg->cgroupControllers = 0;
for (i = 0; controllers[i] != NULL; i++) {
int ctl;
if ((ctl = virCgroupControllerTypeFromString(controllers[i])) < 0) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("Unknown cgroup controller '%s'"),
controllers[i]);
return -1;
}
cfg->cgroupControllers |= (1 << ctl);
}
}
if (virConfGetValueStringList(conf, "cgroup_device_acl", false,
&cfg->cgroupDeviceACL) < 0)
return -1;
if (virConfGetValueInt(conf, "seccomp_sandbox", &cfg->seccompSandbox) < 0)
return -1;
if (virConfGetValueStringList(conf, "namespaces", false, &namespaces) < 0)
return -1;
if (namespaces) {
virBitmapClearAll(cfg->namespaces);
for (i = 0; namespaces[i]; i++) {
int ns = qemuDomainNamespaceTypeFromString(namespaces[i]);
if (ns < 0) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("Unknown namespace: %s"),
namespaces[i]);
return -1;
}
if (!privileged) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("cannot use namespaces in session mode"));
return -1;
}
if (!qemuDomainNamespaceAvailable(ns)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("%s namespace is not available"),
namespaces[i]);
return -1;
}
if (virBitmapSetBit(cfg->namespaces, ns) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to enable namespace: %s"),
namespaces[i]);
return -1;
}
}
}
return 0;
}
static int
virQEMUDriverConfigLoadMemoryEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
g_autofree char *dir = NULL;
int rc;
if ((rc = virConfGetValueString(conf, "memory_backing_dir", &dir)) < 0) {
return -1;
} else if (rc > 0) {
VIR_FREE(cfg->memoryBackingDir);
cfg->memoryBackingDir = g_strdup_printf("%s/libvirt/qemu", dir);
return 0;
}
return 0;
}
static int
virQEMUDriverConfigLoadSWTPMEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
g_autofree char *swtpm_user = NULL;
g_autofree char *swtpm_group = NULL;
if (virConfGetValueString(conf, "swtpm_user", &swtpm_user) < 0)
return -1;
if (swtpm_user && virGetUserID(swtpm_user, &cfg->swtpm_user) < 0)
return -1;
if (virConfGetValueString(conf, "swtpm_group", &swtpm_group) < 0)
return -1;
if (swtpm_group && virGetGroupID(swtpm_group, &cfg->swtpm_group) < 0)
return -1;
return 0;
}
static int
virQEMUDriverConfigLoadCapsFiltersEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
if (virConfGetValueStringList(conf, "capability_filters", false,
&cfg->capabilityfilters) < 0)
return -1;
return 0;
}
int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg,
const char *filename,
bool privileged)
{
g_autoptr(virConf) conf = NULL;
/* Just check the file is readable before opening it, otherwise
* libvirt emits an error.
*/
if (access(filename, R_OK) == -1) {
VIR_INFO("Could not read qemu config file %s", filename);
return 0;
}
if (!(conf = virConfReadFile(filename, 0)))
return -1;
if (virQEMUDriverConfigLoadDefaultTLSEntry(cfg, conf) < 0)
return -1;
if (virQEMUDriverConfigLoadVNCEntry(cfg, conf) < 0)
return -1;
if (virQEMUDriverConfigLoadNographicsEntry(cfg, conf) < 0)
return -1;
if (virQEMUDriverConfigLoadSPICEEntry(cfg, conf) < 0)
return -1;
if (virQEMUDriverConfigLoadSpecificTLSEntry(cfg, conf) < 0)
return -1;
if (virQEMUDriverConfigLoadRemoteDisplayEntry(cfg, conf, filename) < 0)
return -1;
if (virQEMUDriverConfigLoadSaveEntry(cfg, conf) < 0)
return -1;
if (virQEMUDriverConfigLoadProcessEntry(cfg, conf) < 0)
return -1;
if (virQEMUDriverConfigLoadDeviceEntry(cfg, conf) < 0)
return -1;
if (virQEMUDriverConfigLoadRPCEntry(cfg, conf) < 0)
return -1;
if (virQEMUDriverConfigLoadNetworkEntry(cfg, conf, filename) < 0)
return -1;
if (virQEMUDriverConfigLoadLogEntry(cfg, conf) < 0)
return -1;
if (virQEMUDriverConfigLoadNVRAMEntry(cfg, conf, privileged) < 0)
return -1;
if (virQEMUDriverConfigLoadDebugEntry(cfg, conf) < 0)
return -1;
if (virQEMUDriverConfigLoadSecurityEntry(cfg, conf, privileged) < 0)
return -1;
if (virQEMUDriverConfigLoadMemoryEntry(cfg, conf) < 0)
return -1;
if (virQEMUDriverConfigLoadSWTPMEntry(cfg, conf) < 0)
return -1;
if (virQEMUDriverConfigLoadCapsFiltersEntry(cfg, conf) < 0)
return -1;
return 0;
}
/**
* @cfg: Recently read config values
*
* Validate the recently read configuration values.
*
* Returns 0 on success, -1 on failure
*/
int
virQEMUDriverConfigValidate(virQEMUDriverConfigPtr cfg)
{
if (cfg->defaultTLSx509certdirPresent) {
if (!virFileExists(cfg->defaultTLSx509certdir)) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("default_tls_x509_cert_dir directory '%s' "
"does not exist"),
cfg->defaultTLSx509certdir);
return -1;
}
}
if (cfg->vncTLSx509certdir &&
!virFileExists(cfg->vncTLSx509certdir)) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("vnc_tls_x509_cert_dir directory '%s' does not exist"),
cfg->vncTLSx509certdir);
return -1;
}
if (cfg->spiceTLSx509certdir &&
!virFileExists(cfg->spiceTLSx509certdir)) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("spice_tls_x509_cert_dir directory '%s' does not exist"),
cfg->spiceTLSx509certdir);
return -1;
}
if (cfg->chardevTLSx509certdir &&
!virFileExists(cfg->chardevTLSx509certdir)) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("chardev_tls_x509_cert_dir directory '%s' does not exist"),
cfg->chardevTLSx509certdir);
return -1;
}
if (cfg->migrateTLSx509certdir &&
!virFileExists(cfg->migrateTLSx509certdir)) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("migrate_tls_x509_cert_dir directory '%s' does not exist"),
cfg->migrateTLSx509certdir);
return -1;
}
if (cfg->backupTLSx509certdir &&
!virFileExists(cfg->backupTLSx509certdir)) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("backup_tls_x509_cert_dir directory '%s' does not exist"),
cfg->backupTLSx509certdir);
return -1;
}
if (cfg->vxhsTLSx509certdir &&
!virFileExists(cfg->vxhsTLSx509certdir)) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("vxhs_tls_x509_cert_dir directory '%s' does not exist"),
cfg->vxhsTLSx509certdir);
return -1;
}
if (cfg->nbdTLSx509certdir &&
!virFileExists(cfg->nbdTLSx509certdir)) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("nbd_tls_x509_cert_dir directory '%s' does not exist"),
cfg->nbdTLSx509certdir);
return -1;
}
return 0;
}
int
virQEMUDriverConfigSetDefaults(virQEMUDriverConfigPtr cfg)
{
#define SET_TLS_SECRET_UUID_DEFAULT(val) \
do { \
if (!cfg->val## TLSx509certdir && \
!cfg->val## TLSx509secretUUID && \
cfg->defaultTLSx509secretUUID) { \
cfg->val## TLSx509secretUUID = g_strdup(cfg->defaultTLSx509secretUUID); \
} \
} while (0)
SET_TLS_SECRET_UUID_DEFAULT(vnc);
SET_TLS_SECRET_UUID_DEFAULT(chardev);
SET_TLS_SECRET_UUID_DEFAULT(migrate);
SET_TLS_SECRET_UUID_DEFAULT(backup);
SET_TLS_SECRET_UUID_DEFAULT(vxhs);
SET_TLS_SECRET_UUID_DEFAULT(nbd);
#undef SET_TLS_SECRET_UUID_DEFAULT
/*
* If a "SYSCONFDIR" + "pki/libvirt-<val>" exists, then assume someone
* has created a val specific area to place service specific certificates.
*
* If the service specific directory doesn't exist, 'assume' that the
* user has created and populated the "SYSCONFDIR" + "pki/libvirt-default".
*/
#define SET_TLS_X509_CERT_DEFAULT(val) \
do { \
if (cfg->val ## TLSx509certdir) \
break; \
if (virFileExists(SYSCONFDIR "/pki/libvirt-"#val)) { \
cfg->val ## TLSx509certdir = g_strdup(SYSCONFDIR "/pki/libvirt-"#val); \
} else { \
cfg->val ## TLSx509certdir = g_strdup(cfg->defaultTLSx509certdir); \
} \
} while (0)
SET_TLS_X509_CERT_DEFAULT(vnc);
SET_TLS_X509_CERT_DEFAULT(spice);
SET_TLS_X509_CERT_DEFAULT(chardev);
SET_TLS_X509_CERT_DEFAULT(migrate);
SET_TLS_X509_CERT_DEFAULT(backup);
SET_TLS_X509_CERT_DEFAULT(vxhs);
SET_TLS_X509_CERT_DEFAULT(nbd);
#undef SET_TLS_X509_CERT_DEFAULT
#define SET_TLS_VERIFY_DEFAULT(val, defaultverify) \
do { \
if (!cfg->val## TLSx509verifyPresent) {\
if (cfg->defaultTLSx509verifyPresent) \
cfg->val## TLSx509verify = cfg->defaultTLSx509verify; \
else \
cfg->val## TLSx509verify = defaultverify;\
}\
} while (0)
SET_TLS_VERIFY_DEFAULT(vnc, false);
SET_TLS_VERIFY_DEFAULT(chardev, true);
SET_TLS_VERIFY_DEFAULT(migrate, true);
SET_TLS_VERIFY_DEFAULT(backup, true);
#undef SET_TLS_VERIFY_DEFAULT
return 0;
}
virQEMUDriverConfigPtr virQEMUDriverGetConfig(virQEMUDriverPtr driver)
{
virQEMUDriverConfigPtr conf;
qemuDriverLock(driver);
conf = virObjectRef(driver->config);
qemuDriverUnlock(driver);
return conf;
}
virDomainXMLOptionPtr
virQEMUDriverCreateXMLConf(virQEMUDriverPtr driver,
const char *defsecmodel)
{
virQEMUDriverDomainDefParserConfig.priv = driver;
virQEMUDriverDomainDefParserConfig.defSecModel = defsecmodel;
return virDomainXMLOptionNew(&virQEMUDriverDomainDefParserConfig,
&virQEMUDriverPrivateDataCallbacks,
&virQEMUDriverDomainXMLNamespace,
&virQEMUDriverDomainABIStability,
&virQEMUDriverDomainSaveCookie);
}
virCPUDefPtr
virQEMUDriverGetHostCPU(virQEMUDriverPtr driver)
{
virCPUDefPtr hostcpu;
qemuDriverLock(driver);
if (!driver->hostcpu)
driver->hostcpu = virCPUProbeHost(virArchFromHost());
hostcpu = driver->hostcpu;
qemuDriverUnlock(driver);
if (hostcpu)
virCPUDefRef(hostcpu);
return hostcpu;
}
virCapsPtr virQEMUDriverCreateCapabilities(virQEMUDriverPtr driver)
{
size_t i, j;
g_autoptr(virCaps) caps = NULL;
g_autofree virSecurityManagerPtr *sec_managers = NULL;
/* Security driver data */
const char *doi, *model, *lbl, *type;
const int virtTypes[] = {VIR_DOMAIN_VIRT_KVM,
VIR_DOMAIN_VIRT_QEMU,};
/* Basic host arch / guest machine capabilities */
if (!(caps = virQEMUCapsInit(driver->qemuCapsCache)))
return NULL;
if (virGetHostUUID(caps->host.host_uuid)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot get the host uuid"));
return NULL;
}
/* access sec drivers and create a sec model for each one */
if (!(sec_managers = qemuSecurityGetNested(driver->securityManager)))
return NULL;
/* calculate length */
for (i = 0; sec_managers[i]; i++)
;
caps->host.nsecModels = i;
caps->host.secModels = g_new0(virCapsHostSecModel, caps->host.nsecModels);
for (i = 0; sec_managers[i]; i++) {
virCapsHostSecModelPtr sm = &caps->host.secModels[i];
doi = qemuSecurityGetDOI(sec_managers[i]);
model = qemuSecurityGetModel(sec_managers[i]);
sm->model = g_strdup(model);
sm->doi = g_strdup(doi);
for (j = 0; j < G_N_ELEMENTS(virtTypes); j++) {
lbl = qemuSecurityGetBaseLabel(sec_managers[i], virtTypes[j]);
type = virDomainVirtTypeToString(virtTypes[j]);
if (lbl &&
virCapabilitiesHostSecModelAddBaseLabel(sm, type, lbl) < 0)
return NULL;
}
VIR_DEBUG("Initialized caps for security driver \"%s\" with "
"DOI \"%s\"", model, doi);
}
caps->host.numa = virCapabilitiesHostNUMANewHost();
caps->host.cpu = virQEMUDriverGetHostCPU(driver);
return g_steal_pointer(&caps);
}
/**
* virQEMUDriverGetCapabilities:
*
* Get a reference to the virCapsPtr instance for the
* driver. If @refresh is true, the capabilities will be
* rebuilt first
*
* The caller must release the reference with virObjectUnref
*
* Returns: a reference to a virCapsPtr instance or NULL
*/
virCapsPtr virQEMUDriverGetCapabilities(virQEMUDriverPtr driver,
bool refresh)
{
virCapsPtr ret = NULL;
if (refresh) {
virCapsPtr caps = NULL;
if ((caps = virQEMUDriverCreateCapabilities(driver)) == NULL)
return NULL;
qemuDriverLock(driver);
virObjectUnref(driver->caps);
driver->caps = caps;
} else {
qemuDriverLock(driver);
if (driver->caps == NULL ||
driver->caps->nguests == 0) {
VIR_DEBUG("Capabilities didn't detect any guests. Forcing a "
"refresh.");
qemuDriverUnlock(driver);
return virQEMUDriverGetCapabilities(driver, true);
}
}
ret = virObjectRef(driver->caps);
qemuDriverUnlock(driver);
return ret;
}
/**
* virQEMUDriverGetDomainCapabilities:
*
* Get a reference to the virDomainCapsPtr instance. The caller
* must release the reference with virObjetUnref().
*
* Returns: a reference to a virDomainCapsPtr instance or NULL
*/
virDomainCapsPtr
virQEMUDriverGetDomainCapabilities(virQEMUDriverPtr driver,
virQEMUCapsPtr qemuCaps,
const char *machine,
virArch arch,
virDomainVirtType virttype)
{
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
g_autoptr(virDomainCaps) domCaps = NULL;
const char *path = virQEMUCapsGetBinary(qemuCaps);
if (!(domCaps = virDomainCapsNew(path, machine, arch, virttype)))
return NULL;
if (virQEMUCapsFillDomainCaps(qemuCaps, driver->hostarch,
domCaps, driver->privileged,
cfg->firmwares,
cfg->nfirmwares) < 0)
return NULL;
return g_steal_pointer(&domCaps);
}
struct _qemuSharedDeviceEntry {
size_t ref;
char **domains; /* array of domain names */
};
/* Construct the hash key for sharedDevices as "major:minor" */
char *
qemuGetSharedDeviceKey(const char *device_path)
{
int maj, min;
int rc;
if ((rc = virGetDeviceID(device_path, &maj, &min)) < 0) {
virReportSystemError(-rc,
_("Unable to get minor number of device '%s'"),
device_path);
return NULL;
}
return g_strdup_printf("%d:%d", maj, min);
}
/*
* Make necessary checks for the need to check and for the current setting
* of the 'unpriv_sgio' value for the device_path passed.
*
* Returns:
* 0 - Success
* -1 - Some failure which would already have been messaged
* -2 - Mismatch with the "shared" sgio setting - needs to be messaged
* by caller since it has context of which type of disk resource is
* being used and in the future the hostdev information.
*/
static int
qemuCheckUnprivSGIO(GHashTable *sharedDevices,
const char *device_path,
int sgio)
{
g_autofree char *sysfs_path = NULL;
g_autofree char *key = NULL;
int val;
if (!(sysfs_path = virGetUnprivSGIOSysfsPath(device_path, NULL)))
return -1;
/* It can't be conflict if unpriv_sgio is not supported by kernel. */
if (!virFileExists(sysfs_path))
return 0;
if (!(key = qemuGetSharedDeviceKey(device_path)))
return -1;
/* It can't be conflict if no other domain is sharing it. */
if (!(virHashLookup(sharedDevices, key)))
return 0;
if (virGetDeviceUnprivSGIO(device_path, NULL, &val) < 0)
return -1;
/* Error message on failure needs to be handled in caller
* since there is more specific knowledge of device
*/
if (!((val == 0 &&
(sgio == VIR_DOMAIN_DEVICE_SGIO_FILTERED ||
sgio == VIR_DOMAIN_DEVICE_SGIO_DEFAULT)) ||
(val == 1 &&
sgio == VIR_DOMAIN_DEVICE_SGIO_UNFILTERED))) {
return -2;
}
return 0;
}
/* Check if a shared device's setting conflicts with the conf
* used by other domain(s). Currently only checks the sgio
* setting. Note that this should only be called for disk with
* block source if the device type is disk.
*
* Returns 0 if no conflicts, otherwise returns -1.
*/
static int
qemuCheckSharedDisk(GHashTable *sharedDevices,
virDomainDiskDefPtr disk)
{
int ret;
if (disk->device != VIR_DOMAIN_DISK_DEVICE_LUN)
return 0;
if ((ret = qemuCheckUnprivSGIO(sharedDevices, disk->src->path,
disk->sgio)) < 0) {
if (ret == -2) {
if (virDomainDiskGetType(disk) == VIR_STORAGE_TYPE_VOLUME) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("sgio of shared disk 'pool=%s' 'volume=%s' "
"conflicts with other active domains"),
disk->src->srcpool->pool,
disk->src->srcpool->volume);
} else {
virReportError(VIR_ERR_OPERATION_INVALID,
_("sgio of shared disk '%s' conflicts with "
"other active domains"),
disk->src->path);
}
}
return -1;
}
return 0;
}
bool
qemuSharedDeviceEntryDomainExists(qemuSharedDeviceEntryPtr entry,
const char *name,
int *idx)
{
size_t i;
for (i = 0; i < entry->ref; i++) {
if (STREQ(entry->domains[i], name)) {
if (idx)
*idx = i;
return true;
}
}
return false;
}
void
qemuSharedDeviceEntryFree(void *payload)
{
qemuSharedDeviceEntryPtr entry = payload;
size_t i;
if (!entry)
return;
for (i = 0; i < entry->ref; i++)
VIR_FREE(entry->domains[i]);
VIR_FREE(entry->domains);
VIR_FREE(entry);
}
static int
qemuSharedDeviceEntryInsert(virQEMUDriverPtr driver,
const char *key,
const char *name)
{
qemuSharedDeviceEntry *entry = NULL;
if ((entry = virHashLookup(driver->sharedDevices, key))) {
/* Nothing to do if the shared scsi host device is already
* recorded in the table.
*/
if (!qemuSharedDeviceEntryDomainExists(entry, name, NULL)) {
if (VIR_EXPAND_N(entry->domains, entry->ref, 1) < 0) {
/* entry is owned by the hash table here */
entry = NULL;
goto error;
}
entry->domains[entry->ref - 1] = g_strdup(name);
}
} else {
entry = g_new0(qemuSharedDeviceEntry, 1);
entry->domains = g_new0(char *, 1);
entry->domains[0] = g_strdup(name);
entry->ref = 1;
if (virHashAddEntry(driver->sharedDevices, key, entry) < 0)
goto error;
}
return 0;
error:
qemuSharedDeviceEntryFree(entry);
return -1;
}
static int
qemuSharedDeviceEntryRemove(virQEMUDriverPtr driver,
const char *key,
const char *name)
{
qemuSharedDeviceEntryPtr entry = NULL;
int idx;
if (!(entry = virHashLookup(driver->sharedDevices, key)))
return -1;
/* Nothing to do if the shared disk is not recorded in the table. */
if (!qemuSharedDeviceEntryDomainExists(entry, name, &idx))
return 0;
if (entry->ref != 1) {
VIR_FREE(entry->domains[idx]);
VIR_DELETE_ELEMENT(entry->domains, idx, entry->ref);
} else {
ignore_value(virHashRemoveEntry(driver->sharedDevices, key));
}
return 0;
}
static int
qemuSharedDiskAddRemoveInternal(virQEMUDriverPtr driver,
virDomainDiskDefPtr disk,
const char *name,
bool addDisk)
{
g_autofree char *key = NULL;
int ret = -1;
if (virStorageSourceIsEmpty(disk->src) ||
!disk->src->shared ||
!virStorageSourceIsBlockLocal(disk->src))
return 0;
qemuDriverLock(driver);
if (!(key = qemuGetSharedDeviceKey(virDomainDiskGetSource(disk))))
goto cleanup;
if (addDisk) {
if (qemuCheckSharedDisk(driver->sharedDevices, disk) < 0)
goto cleanup;
if (qemuSharedDeviceEntryInsert(driver, key, name) < 0)
goto cleanup;
} else {
if (qemuSharedDeviceEntryRemove(driver, key, name) < 0)
goto cleanup;
}
ret = 0;
cleanup:
qemuDriverUnlock(driver);
return ret;
}
/* qemuAddSharedDisk:
* @driver: Pointer to qemu driver struct
* @src: disk source
* @name: The domain name
*
* Increase ref count and add the domain name into the list which
* records all the domains that use the shared device if the entry
* already exists, otherwise add a new entry.
*/
int
qemuAddSharedDisk(virQEMUDriverPtr driver,
virDomainDiskDefPtr disk,
const char *name)
{
return qemuSharedDiskAddRemoveInternal(driver, disk, name, true);
}
static bool
qemuIsSharedHostdev(virDomainHostdevDefPtr hostdev)
{
return (hostdev->shareable &&
(virHostdevIsSCSIDevice(hostdev) &&
hostdev->source.subsys.u.scsi.protocol !=
VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI));
}
static char *
qemuGetHostdevPath(virDomainHostdevDefPtr hostdev)
{
virDomainHostdevSubsysSCSIPtr scsisrc = &hostdev->source.subsys.u.scsi;
virDomainHostdevSubsysSCSIHostPtr scsihostsrc = &scsisrc->u.host;
g_autofree char *dev_name = NULL;
if (!(dev_name = virSCSIDeviceGetDevName(NULL,
scsihostsrc->adapter,
scsihostsrc->bus,
scsihostsrc->target,
scsihostsrc->unit)))
return NULL;
return g_strdup_printf("/dev/%s", dev_name);
}
static int
qemuSharedHostdevAddRemoveInternal(virQEMUDriverPtr driver,
virDomainHostdevDefPtr hostdev,
const char *name,
bool addDevice)
{
g_autofree char *dev_path = NULL;
g_autofree char *key = NULL;
int ret = -1;
if (!qemuIsSharedHostdev(hostdev))
return 0;
if (!(dev_path = qemuGetHostdevPath(hostdev)) ||
!(key = qemuGetSharedDeviceKey(dev_path)))
return -1;
qemuDriverLock(driver);
if (addDevice)
ret = qemuSharedDeviceEntryInsert(driver, key, name);
else
ret = qemuSharedDeviceEntryRemove(driver, key, name);
qemuDriverUnlock(driver);
return ret;
}
static int
qemuSharedDeviceAddRemoveInternal(virQEMUDriverPtr driver,
virDomainDeviceDefPtr dev,
const char *name,
bool addDevice)
{
/* Currently the only conflicts we have to care about for
* the shared disk and shared host device is "sgio" setting,
* which is only valid for block disk and scsi host device.
*/
if (dev->type == VIR_DOMAIN_DEVICE_DISK)
return qemuSharedDiskAddRemoveInternal(driver, dev->data.disk,
name, addDevice);
else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV)
return qemuSharedHostdevAddRemoveInternal(driver, dev->data.hostdev,
name, addDevice);
else
return 0;
}
/* qemuAddSharedDevice:
* @driver: Pointer to qemu driver struct
* @dev: The device def
* @name: The domain name
*
* Increase ref count and add the domain name into the list which
* records all the domains that use the shared device if the entry
* already exists, otherwise add a new entry.
*/
int
qemuAddSharedDevice(virQEMUDriverPtr driver,
virDomainDeviceDefPtr dev,
const char *name)
{
return qemuSharedDeviceAddRemoveInternal(driver, dev, name, true);
}
int
qemuRemoveSharedDisk(virQEMUDriverPtr driver,
virDomainDiskDefPtr disk,
const char *name)
{
return qemuSharedDiskAddRemoveInternal(driver, disk, name, false);
}
/* qemuRemoveSharedDevice:
* @driver: Pointer to qemu driver struct
* @device: The device def
* @name: The domain name
*
* Decrease ref count and remove the domain name from the list which
* records all the domains that use the shared device if ref is not
* 1, otherwise remove the entry.
*/
int
qemuRemoveSharedDevice(virQEMUDriverPtr driver,
virDomainDeviceDefPtr dev,
const char *name)
{
return qemuSharedDeviceAddRemoveInternal(driver, dev, name, false);
}
int
qemuSetUnprivSGIO(virDomainDeviceDefPtr dev)
{
virDomainDiskDefPtr disk = NULL;
virDomainHostdevDefPtr hostdev = NULL;
g_autofree char *sysfs_path = NULL;
const char *path = NULL;
int val = -1;
/* "sgio" is only valid for block disk; cdrom
* and floopy disk can have empty source.
*/
if (dev->type == VIR_DOMAIN_DEVICE_DISK) {
disk = dev->data.disk;
if (disk->device != VIR_DOMAIN_DISK_DEVICE_LUN ||
!virStorageSourceIsBlockLocal(disk->src))
return 0;
path = virDomainDiskGetSource(disk);
} else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) {
hostdev = dev->data.hostdev;
if (!qemuIsSharedHostdev(hostdev))
return 0;
if (hostdev->source.subsys.u.scsi.sgio) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("'sgio' is not supported for SCSI "
"generic device yet "));
return -1;
}
return 0;
} else {
return 0;
}
if (!(sysfs_path = virGetUnprivSGIOSysfsPath(path, NULL)))
return -1;
/* By default, filter the SG_IO commands, i.e. set unpriv_sgio to 0. */
val = (disk->sgio == VIR_DOMAIN_DEVICE_SGIO_UNFILTERED);
/* Do not do anything if unpriv_sgio is not supported by the kernel and the
* whitelist is enabled. But if requesting unfiltered access, always call
* virSetDeviceUnprivSGIO, to report an error for unsupported unpriv_sgio.
*/
if ((virFileExists(sysfs_path) || val == 1) &&
virSetDeviceUnprivSGIO(path, NULL, val) < 0)
return -1;
return 0;
}
int qemuDriverAllocateID(virQEMUDriverPtr driver)
{
return g_atomic_int_add(&driver->lastvmid, 1) + 1;
}
int
qemuTranslateSnapshotDiskSourcePool(virDomainSnapshotDiskDefPtr def)
{
if (def->src->type != VIR_STORAGE_TYPE_VOLUME)
return 0;
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Snapshots are not yet supported with 'pool' volumes"));
return -1;
}
char *
qemuGetBaseHugepagePath(virQEMUDriverPtr driver,
virHugeTLBFSPtr hugepage)
{
const char *root = driver->embeddedRoot;
char *ret;
if (root && !STRPREFIX(hugepage->mnt_dir, root)) {
g_autofree char * hash = virDomainDriverGenerateRootHash("qemu", root);
ret = g_strdup_printf("%s/libvirt/%s", hugepage->mnt_dir, hash);
} else {
ret = g_strdup_printf("%s/libvirt/qemu", hugepage->mnt_dir);
}
return ret;
}
char *
qemuGetDomainHugepagePath(virQEMUDriverPtr driver,
const virDomainDef *def,
virHugeTLBFSPtr hugepage)
{
g_autofree char *base = qemuGetBaseHugepagePath(driver, hugepage);
g_autofree char *domPath = virDomainDefGetShortName(def);
char *ret = NULL;
if (base && domPath)
ret = g_strdup_printf("%s/%s", base, domPath);
return ret;
}
/**
* qemuGetDomainHupageMemPath: Construct HP enabled memory backend path
*
* The resulting path is stored at @memPath.
*
* Returns 0 on success,
* -1 otherwise.
*/
int
qemuGetDomainHupageMemPath(virQEMUDriverPtr driver,
const virDomainDef *def,
unsigned long long pagesize,
char **memPath)
{
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
size_t i = 0;
if (!cfg->nhugetlbfs) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("hugetlbfs filesystem is not mounted "
"or disabled by administrator config"));
return -1;
}
for (i = 0; i < cfg->nhugetlbfs; i++) {
if (cfg->hugetlbfs[i].size == pagesize)
break;
}
if (i == cfg->nhugetlbfs) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to find any usable hugetlbfs "
"mount for %llu KiB"),
pagesize);
return -1;
}
if (!(*memPath = qemuGetDomainHugepagePath(driver, def, &cfg->hugetlbfs[i])))
return -1;
return 0;
}
int
qemuGetMemoryBackingDomainPath(virQEMUDriverPtr driver,
const virDomainDef *def,
char **path)
{
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
const char *root = driver->embeddedRoot;
g_autofree char *shortName = NULL;
if (!(shortName = virDomainDefGetShortName(def)))
return -1;
if (root && !STRPREFIX(cfg->memoryBackingDir, root)) {
g_autofree char * hash = virDomainDriverGenerateRootHash("qemu", root);
*path = g_strdup_printf("%s/%s-%s", cfg->memoryBackingDir, hash, shortName);
} else {
*path = g_strdup_printf("%s/%s", cfg->memoryBackingDir, shortName);
}
return 0;
}
/**
* qemuGetMemoryBackingPath:
* @driver: the qemu driver
* @def: domain definition
* @alias: memory object alias
* @memPath: constructed path
*
* Constructs path to memory backing dir and stores it at @memPath.
*
* Returns: 0 on success,
* -1 otherwise (with error reported).
*/
int
qemuGetMemoryBackingPath(virQEMUDriverPtr driver,
const virDomainDef *def,
const char *alias,
char **memPath)
{
g_autofree char *domainPath = NULL;
if (!alias) {
/* This should never happen (TM) */
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("memory device alias is not assigned"));
return -1;
}
if (qemuGetMemoryBackingDomainPath(driver, def, &domainPath) < 0)
return -1;
*memPath = g_strdup_printf("%s/%s", domainPath, alias);
return 0;
}