/*
* 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
* .
*/
#include
#include
#include
#include
#include
#include
#include
#include "virerror.h"
#include "qemu_conf.h"
#include "qemu_capabilities.h"
#include "qemu_domain.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_nwfilter.h"
#include "virfile.h"
#include "virsocketaddr.h"
#include "virstring.h"
#include "viratomic.h"
#include "storage_conf.h"
#include "configmake.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);
}
void qemuDomainCmdlineDefFree(qemuDomainCmdlineDefPtr def)
{
size_t i;
if (!def)
return;
for (i = 0; i < def->num_args; i++)
VIR_FREE(def->args[i]);
for (i = 0; i < def->num_env; i++) {
VIR_FREE(def->env_name[i]);
VIR_FREE(def->env_value[i]);
}
VIR_FREE(def->args);
VIR_FREE(def->env_name);
VIR_FREE(def->env_value);
VIR_FREE(def);
}
#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)
{
virQEMUDriverConfigPtr cfg;
if (virQEMUConfigInitialize() < 0)
return NULL;
if (!(cfg = virObjectNew(virQEMUDriverConfigClass)))
return NULL;
cfg->uri = privileged ? "qemu:///system" : "qemu:///session";
if (privileged) {
if (virGetUserID(QEMU_USER, &cfg->user) < 0)
goto error;
if (virGetGroupID(QEMU_GROUP, &cfg->group) < 0)
goto error;
} else {
cfg->user = (uid_t)-1;
cfg->group = (gid_t)-1;
}
cfg->dynamicOwnership = privileged;
cfg->cgroupControllers = -1; /* -1 == auto-detect */
if (privileged) {
if (virAsprintf(&cfg->logDir,
"%s/log/libvirt/qemu", LOCALSTATEDIR) < 0)
goto error;
if (virAsprintf(&cfg->swtpmLogDir,
"%s/log/swtpm/libvirt/qemu", LOCALSTATEDIR) < 0)
goto error;
if (VIR_STRDUP(cfg->configBaseDir, SYSCONFDIR "/libvirt") < 0)
goto error;
if (virAsprintf(&cfg->stateDir,
"%s/run/libvirt/qemu", LOCALSTATEDIR) < 0)
goto error;
if (virAsprintf(&cfg->swtpmStateDir,
"%s/run/libvirt/qemu/swtpm", LOCALSTATEDIR) < 0)
goto error;
if (virAsprintf(&cfg->cacheDir,
"%s/cache/libvirt/qemu", LOCALSTATEDIR) < 0)
goto error;
if (virAsprintf(&cfg->libDir,
"%s/lib/libvirt/qemu", LOCALSTATEDIR) < 0)
goto error;
if (virAsprintf(&cfg->saveDir, "%s/save", cfg->libDir) < 0)
goto error;
if (virAsprintf(&cfg->snapshotDir, "%s/snapshot", cfg->libDir) < 0)
goto error;
if (virAsprintf(&cfg->autoDumpPath, "%s/dump", cfg->libDir) < 0)
goto error;
if (virAsprintf(&cfg->channelTargetDir,
"%s/channel/target", cfg->libDir) < 0)
goto error;
if (virAsprintf(&cfg->nvramDir, "%s/nvram", cfg->libDir) < 0)
goto error;
if (virAsprintf(&cfg->memoryBackingDir, "%s/ram", cfg->libDir) < 0)
goto error;
if (virAsprintf(&cfg->swtpmStorageDir, "%s/lib/libvirt/swtpm",
LOCALSTATEDIR) < 0)
goto error;
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 {
char *rundir;
char *cachedir;
cachedir = virGetUserCacheDirectory();
if (!cachedir)
goto error;
if (virAsprintf(&cfg->logDir,
"%s/qemu/log", cachedir) < 0) {
VIR_FREE(cachedir);
goto error;
}
if (virAsprintf(&cfg->swtpmLogDir,
"%s/qemu/log", cachedir) < 0) {
VIR_FREE(cachedir);
goto error;
}
if (virAsprintf(&cfg->cacheDir, "%s/qemu/cache", cachedir) < 0) {
VIR_FREE(cachedir);
goto error;
}
VIR_FREE(cachedir);
rundir = virGetUserRuntimeDirectory();
if (!rundir)
goto error;
if (virAsprintf(&cfg->stateDir, "%s/qemu/run", rundir) < 0) {
VIR_FREE(rundir);
goto error;
}
VIR_FREE(rundir);
if (virAsprintf(&cfg->swtpmStateDir, "%s/swtpm", cfg->stateDir) < 0)
goto error;
if (!(cfg->configBaseDir = virGetUserConfigDirectory()))
goto error;
if (virAsprintf(&cfg->libDir, "%s/qemu/lib", cfg->configBaseDir) < 0)
goto error;
if (virAsprintf(&cfg->saveDir, "%s/qemu/save", cfg->configBaseDir) < 0)
goto error;
if (virAsprintf(&cfg->snapshotDir, "%s/qemu/snapshot", cfg->configBaseDir) < 0)
goto error;
if (virAsprintf(&cfg->autoDumpPath, "%s/qemu/dump", cfg->configBaseDir) < 0)
goto error;
if (virAsprintf(&cfg->channelTargetDir,
"%s/qemu/channel/target", cfg->configBaseDir) < 0)
goto error;
if (virAsprintf(&cfg->nvramDir,
"%s/qemu/nvram", cfg->configBaseDir) < 0)
goto error;
if (virAsprintf(&cfg->memoryBackingDir, "%s/qemu/ram", cfg->configBaseDir) < 0)
goto error;
if (virAsprintf(&cfg->swtpmStorageDir, "%s/qemu/swtpm", cfg->configBaseDir) < 0)
goto error;
cfg->swtpm_user = (uid_t)-1;
cfg->swtpm_group = (gid_t)-1;
}
if (virAsprintf(&cfg->configDir, "%s/qemu", cfg->configBaseDir) < 0)
goto error;
if (virAsprintf(&cfg->autostartDir, "%s/qemu/autostart", cfg->configBaseDir) < 0)
goto error;
/* 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 (VIR_STRDUP(cfg->defaultTLSx509certdir,
SYSCONFDIR "/pki/qemu") < 0)
goto error;
if (VIR_STRDUP(cfg->vncListen, VIR_LOOPBACK_IPV4_ADDR) < 0)
goto error;
if (VIR_STRDUP(cfg->spiceListen, VIR_LOOPBACK_IPV4_ADDR) < 0)
goto error;
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)
goto error;
}
if (VIR_STRDUP(cfg->bridgeHelperName, QEMU_BRIDGE_HELPER) < 0 ||
VIR_STRDUP(cfg->prHelperName, QEMU_PR_HELPER) < 0)
goto error;
cfg->clearEmulatorCapabilities = true;
cfg->securityDefaultConfined = true;
cfg->securityRequireConfined = false;
cfg->keepAliveInterval = 5;
cfg->keepAliveCount = 5;
cfg->seccompSandbox = -1;
cfg->logTimestamp = true;
cfg->glusterDebugLevel = 4;
cfg->stdioLogD = true;
if (!(cfg->namespaces = virBitmapNew(QEMU_DOMAIN_NS_LAST)))
goto error;
if (privileged &&
qemuDomainNamespaceAvailable(QEMU_DOMAIN_NS_MOUNT) &&
virBitmapSetBit(cfg->namespaces, QEMU_DOMAIN_NS_MOUNT) < 0)
goto error;
if (virFirmwareParseList(DEFAULT_LOADER_NVRAM,
&cfg->firmwares,
&cfg->nfirmwares) < 0)
goto error;
return cfg;
error:
virObjectUnref(cfg);
return NULL;
}
static void virQEMUDriverConfigDispose(void *obj)
{
virQEMUDriverConfigPtr cfg = obj;
virBitmapFree(cfg->namespaces);
virStringListFree(cfg->cgroupDeviceACL);
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->libDir);
VIR_FREE(cfg->cacheDir);
VIR_FREE(cfg->saveDir);
VIR_FREE(cfg->snapshotDir);
VIR_FREE(cfg->channelTargetDir);
VIR_FREE(cfg->nvramDir);
VIR_FREE(cfg->defaultTLSx509certdir);
VIR_FREE(cfg->defaultTLSx509secretUUID);
VIR_FREE(cfg->vncTLSx509certdir);
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->nbdTLSx509certdir);
VIR_FREE(cfg->migrateTLSx509certdir);
VIR_FREE(cfg->migrateTLSx509secretUUID);
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->saveImageFormat);
VIR_FREE(cfg->dumpImageFormat);
VIR_FREE(cfg->snapshotImageFormat);
VIR_FREE(cfg->autoDumpPath);
virStringListFree(cfg->securityDriverNames);
VIR_FREE(cfg->lockManagerName);
virFirmwareFreeList(cfg->firmwares, cfg->nfirmwares);
VIR_FREE(cfg->memoryBackingDir);
VIR_FREE(cfg->swtpmStorageDir);
}
static int
virQEMUDriverConfigHugeTLBFSInit(virHugeTLBFSPtr hugetlbfs,
const char *path,
bool deflt)
{
int ret = -1;
if (VIR_STRDUP(hugetlbfs->mnt_dir, path) < 0)
goto cleanup;
if (virFileGetHugepageSize(path, &hugetlbfs->size) < 0)
goto cleanup;
hugetlbfs->deflt = deflt;
ret = 0;
cleanup:
return ret;
}
static int
virQEMUDriverConfigLoadSpecificTLSEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
int rv;
if (virConfGetValueBool(conf, "vxhs_tls", &cfg->vxhsTLS) < 0)
return -1;
if (virConfGetValueString(conf, "vxhs_tls_x509_cert_dir", &cfg->vxhsTLSx509certdir) < 0)
return -1;
if (virConfGetValueBool(conf, "nbd_tls", &cfg->nbdTLS) < 0)
return -1;
if (virConfGetValueString(conf, "nbd_tls_x509_cert_dir", &cfg->nbdTLSx509certdir) < 0)
return -1;
#define GET_CONFIG_TLS_CERTINFO(val) \
do { \
if ((rv = virConfGetValueBool(conf, #val "_tls_x509_verify", \
&cfg->val## TLSx509verify)) < 0) \
return -1; \
if (rv == 1) \
cfg->val## TLSx509verifyPresent = true; \
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)
if (virConfGetValueBool(conf, "chardev_tls", &cfg->chardevTLS) < 0)
return -1;
GET_CONFIG_TLS_CERTINFO(chardev);
GET_CONFIG_TLS_CERTINFO(migrate);
#undef GET_CONFIG_TLS_CERTINFO
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)
{
char *stdioHandler = NULL;
char **hugetlbfs = NULL;
char *corestr = NULL;
int ret = -1;
size_t i;
if (virConfGetValueStringList(conf, "hugetlbfs_mount", true,
&hugetlbfs) < 0)
goto cleanup;
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] &&
VIR_ALLOC_N(cfg->hugetlbfs, cfg->nhugetlbfs) < 0)
goto cleanup;
for (i = 0; hugetlbfs[i] != NULL; i++) {
if (virQEMUDriverConfigHugeTLBFSInit(&cfg->hugetlbfs[i],
hugetlbfs[i], i != 0) < 0)
goto cleanup;
}
}
if (virConfGetValueBool(conf, "clear_emulator_capabilities", &cfg->clearEmulatorCapabilities) < 0)
goto cleanup;
if (virConfGetValueString(conf, "bridge_helper", &cfg->bridgeHelperName) < 0)
goto cleanup;
if (virConfGetValueString(conf, "pr_helper", &cfg->prHelperName) < 0)
goto cleanup;
if (virConfGetValueBool(conf, "set_process_name", &cfg->setProcessName) < 0)
goto cleanup;
if (virConfGetValueUInt(conf, "max_processes", &cfg->maxProcesses) < 0)
goto cleanup;
if (virConfGetValueUInt(conf, "max_files", &cfg->maxFiles) < 0)
goto cleanup;
if (virConfGetValueType(conf, "max_core") == VIR_CONF_STRING) {
if (virConfGetValueString(conf, "max_core", &corestr) < 0)
goto cleanup;
if (STREQ(corestr, "unlimited")) {
cfg->maxCore = ULLONG_MAX;
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unknown core size '%s'"),
corestr);
goto cleanup;
}
} else if (virConfGetValueULLong(conf, "max_core", &cfg->maxCore) < 0) {
goto cleanup;
}
if (virConfGetValueBool(conf, "dump_guest_core", &cfg->dumpGuestCore) < 0)
goto cleanup;
if (virConfGetValueString(conf, "stdio_handler", &stdioHandler) < 0)
goto cleanup;
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);
VIR_FREE(stdioHandler);
goto cleanup;
}
VIR_FREE(stdioHandler);
}
ret = 0;
cleanup:
virStringListFree(hugetlbfs);
VIR_FREE(corestr);
return ret;
}
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)
{
char **nvram = NULL;
int ret = -1;
size_t i;
if (virConfGetValueStringList(conf, "nvram", false, &nvram) < 0)
goto cleanup;
if (nvram) {
virFirmwareFreeList(cfg->firmwares, cfg->nfirmwares);
cfg->nfirmwares = virStringListLength((const char *const *)nvram);
if (nvram[0] && VIR_ALLOC_N(cfg->firmwares, cfg->nfirmwares) < 0)
goto cleanup;
for (i = 0; nvram[i] != NULL; i++) {
if (VIR_ALLOC(cfg->firmwares[i]) < 0)
goto cleanup;
if (virFirmwareParse(nvram[i], cfg->firmwares[i]) < 0)
goto cleanup;
}
}
ret = 0;
cleanup:
virStringListFree(nvram);
return ret;
}
static int
virQEMUDriverConfigLoadGlusterDebugEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
return virConfGetValueUInt(conf, "gluster_debug_level", &cfg->glusterDebugLevel);
}
static int
virQEMUDriverConfigLoadSecurityEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf,
bool privileged)
{
char *user = NULL, *group = NULL;
char **controllers = NULL;
char **namespaces = NULL;
int ret = -1;
size_t i, j;
if (virConfGetValueStringList(conf, "security_driver", true, &cfg->securityDriverNames) < 0)
goto cleanup;
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]);
goto cleanup;
}
}
}
if (virConfGetValueBool(conf, "security_default_confined", &cfg->securityDefaultConfined) < 0)
goto cleanup;
if (virConfGetValueBool(conf, "security_require_confined", &cfg->securityRequireConfined) < 0)
goto cleanup;
if (virConfGetValueString(conf, "user", &user) < 0)
goto cleanup;
if (user && virGetUserID(user, &cfg->user) < 0)
goto cleanup;
if (virConfGetValueString(conf, "group", &group) < 0)
goto cleanup;
if (group && virGetGroupID(group, &cfg->group) < 0)
goto cleanup;
if (virConfGetValueBool(conf, "dynamic_ownership", &cfg->dynamicOwnership) < 0)
goto cleanup;
if (virConfGetValueStringList(conf, "cgroup_controllers", false,
&controllers) < 0)
goto cleanup;
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]);
goto cleanup;
}
cfg->cgroupControllers |= (1 << ctl);
}
}
if (virConfGetValueStringList(conf, "cgroup_device_acl", false,
&cfg->cgroupDeviceACL) < 0)
goto cleanup;
if (virConfGetValueInt(conf, "seccomp_sandbox", &cfg->seccompSandbox) < 0)
goto cleanup;
if (virConfGetValueStringList(conf, "namespaces", false, &namespaces) < 0)
goto cleanup;
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]);
goto cleanup;
}
if (!privileged) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("cannot use namespaces in session mode"));
goto cleanup;
}
if (!qemuDomainNamespaceAvailable(ns)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("%s namespace is not available"),
namespaces[i]);
goto cleanup;
}
if (virBitmapSetBit(cfg->namespaces, ns) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to enable namespace: %s"),
namespaces[i]);
goto cleanup;
}
}
}
ret = 0;
cleanup:
virStringListFree(controllers);
virStringListFree(namespaces);
VIR_FREE(user);
VIR_FREE(group);
return ret;
}
static int
virQEMUDriverConfigLoadMemoryEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
return virConfGetValueString(conf, "memory_backing_dir", &cfg->memoryBackingDir);
}
static int
virQEMUDriverConfigLoadSWTPMEntry(virQEMUDriverConfigPtr cfg,
virConfPtr conf)
{
char *swtpm_user = NULL, *swtpm_group = NULL;
int ret = -1;
if (virConfGetValueString(conf, "swtpm_user", &swtpm_user) < 0)
goto cleanup;
if (swtpm_user && virGetUserID(swtpm_user, &cfg->swtpm_user) < 0)
goto cleanup;
if (virConfGetValueString(conf, "swtpm_group", &swtpm_group) < 0)
goto cleanup;
if (swtpm_group && virGetGroupID(swtpm_group, &cfg->swtpm_group) < 0)
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(swtpm_user);
VIR_FREE(swtpm_group);
return ret;
}
int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg,
const char *filename,
bool privileged)
{
virConfPtr conf = NULL;
int ret = -1;
int rv;
char *corestr = 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)))
goto cleanup;
if ((rv = virConfGetValueString(conf, "default_tls_x509_cert_dir", &cfg->defaultTLSx509certdir)) < 0)
goto cleanup;
cfg->checkdefaultTLSx509certdir = (rv == 1);
if (virConfGetValueBool(conf, "default_tls_x509_verify", &cfg->defaultTLSx509verify) < 0)
goto cleanup;
if (virConfGetValueString(conf, "default_tls_x509_secret_uuid",
&cfg->defaultTLSx509secretUUID) < 0)
goto cleanup;
if (virConfGetValueBool(conf, "vnc_auto_unix_socket", &cfg->vncAutoUnixSocket) < 0)
goto cleanup;
if (virConfGetValueBool(conf, "vnc_tls", &cfg->vncTLS) < 0)
goto cleanup;
if ((rv = virConfGetValueBool(conf, "vnc_tls_x509_verify", &cfg->vncTLSx509verify)) < 0)
goto cleanup;
if (rv == 1)
cfg->vncTLSx509verifyPresent = true;
if (virConfGetValueString(conf, "vnc_tls_x509_cert_dir", &cfg->vncTLSx509certdir) < 0)
goto cleanup;
if (virConfGetValueString(conf, "vnc_listen", &cfg->vncListen) < 0)
goto cleanup;
if (virConfGetValueString(conf, "vnc_password", &cfg->vncPassword) < 0)
goto cleanup;
if (virConfGetValueBool(conf, "vnc_sasl", &cfg->vncSASL) < 0)
goto cleanup;
if (virConfGetValueString(conf, "vnc_sasl_dir", &cfg->vncSASLdir) < 0)
goto cleanup;
if (virConfGetValueBool(conf, "vnc_allow_host_audio", &cfg->vncAllowHostAudio) < 0)
goto cleanup;
if (virConfGetValueBool(conf, "nographics_allow_host_audio", &cfg->nogfxAllowHostAudio) < 0)
goto cleanup;
if (virConfGetValueBool(conf, "spice_tls", &cfg->spiceTLS) < 0)
goto cleanup;
if (virConfGetValueString(conf, "spice_tls_x509_cert_dir", &cfg->spiceTLSx509certdir) < 0)
goto cleanup;
if (virConfGetValueBool(conf, "spice_sasl", &cfg->spiceSASL) < 0)
goto cleanup;
if (virConfGetValueString(conf, "spice_sasl_dir", &cfg->spiceSASLdir) < 0)
goto cleanup;
if (virConfGetValueString(conf, "spice_listen", &cfg->spiceListen) < 0)
goto cleanup;
if (virConfGetValueString(conf, "spice_password", &cfg->spicePassword) < 0)
goto cleanup;
if (virConfGetValueBool(conf, "spice_auto_unix_socket", &cfg->spiceAutoUnixSocket) < 0)
goto cleanup;
if (virQEMUDriverConfigLoadSpecificTLSEntry(cfg, conf) < 0)
goto cleanup;
if (virQEMUDriverConfigLoadRemoteDisplayEntry(cfg, conf, filename) < 0)
goto cleanup;
if (virQEMUDriverConfigLoadSaveEntry(cfg, conf) < 0)
goto cleanup;
if (virQEMUDriverConfigLoadProcessEntry(cfg, conf) < 0)
goto cleanup;
if (virQEMUDriverConfigLoadDeviceEntry(cfg, conf) < 0)
goto cleanup;
if (virQEMUDriverConfigLoadRPCEntry(cfg, conf) < 0)
goto cleanup;
if (virQEMUDriverConfigLoadNetworkEntry(cfg, conf, filename) < 0)
goto cleanup;
if (virQEMUDriverConfigLoadLogEntry(cfg, conf) < 0)
goto cleanup;
if (virQEMUDriverConfigLoadNVRAMEntry(cfg, conf) < 0)
goto cleanup;
if (virQEMUDriverConfigLoadGlusterDebugEntry(cfg, conf) < 0)
goto cleanup;
if (virQEMUDriverConfigLoadSecurityEntry(cfg, conf, privileged) < 0)
goto cleanup;
if (virQEMUDriverConfigLoadMemoryEntry(cfg, conf) < 0)
goto cleanup;
if (virQEMUDriverConfigLoadSWTPMEntry(cfg, conf) < 0)
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(corestr);
virConfFree(conf);
return ret;
}
/**
* @cfg: Recently read config values
*
* Validate the recently read configuration values.
*
* Returns 0 on success, -1 on failure
*/
int
virQEMUDriverConfigValidate(virQEMUDriverConfigPtr cfg)
{
if (cfg->checkdefaultTLSx509certdir) {
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->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)
{
int ret = -1;
#define SET_TLS_SECRET_UUID_DEFAULT(val) \
do { \
if (!cfg->val## TLSx509certdir && \
!cfg->val## TLSx509secretUUID && \
cfg->defaultTLSx509secretUUID) { \
if (VIR_STRDUP(cfg->val## TLSx509secretUUID, \
cfg->defaultTLSx509secretUUID) < 0) \
goto cleanup; \
} \
} while (0)
SET_TLS_SECRET_UUID_DEFAULT(chardev);
SET_TLS_SECRET_UUID_DEFAULT(migrate);
#undef SET_TLS_SECRET_UUID_DEFAULT
/*
* If a "SYSCONFDIR" + "pki/libvirt-" 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)) { \
if (VIR_STRDUP(cfg->val ## TLSx509certdir, \
SYSCONFDIR "/pki/libvirt-"#val) < 0) \
goto cleanup; \
} else { \
if (VIR_STRDUP(cfg->val ## TLSx509certdir, \
cfg->defaultTLSx509certdir) < 0) \
goto cleanup; \
} \
} 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(vxhs);
SET_TLS_X509_CERT_DEFAULT(nbd);
#undef SET_TLS_X509_CERT_DEFAULT
#define SET_TLS_VERIFY_DEFAULT(val) \
do { \
if (!cfg->val## TLSx509verifyPresent) \
cfg->val## TLSx509verify = cfg->defaultTLSx509verify; \
} while (0)
SET_TLS_VERIFY_DEFAULT(vnc);
SET_TLS_VERIFY_DEFAULT(chardev);
SET_TLS_VERIFY_DEFAULT(migrate);
#undef SET_TLS_VERIFY_DEFAULT
ret = 0;
cleanup:
return ret;
}
virQEMUDriverConfigPtr virQEMUDriverGetConfig(virQEMUDriverPtr driver)
{
virQEMUDriverConfigPtr conf;
qemuDriverLock(driver);
conf = virObjectRef(driver->config);
qemuDriverUnlock(driver);
return conf;
}
bool
virQEMUDriverIsPrivileged(virQEMUDriverPtr driver)
{
return driver->privileged;
}
virDomainXMLOptionPtr
virQEMUDriverCreateXMLConf(virQEMUDriverPtr driver)
{
virQEMUDriverDomainDefParserConfig.priv = driver;
return virDomainXMLOptionNew(&virQEMUDriverDomainDefParserConfig,
&virQEMUDriverPrivateDataCallbacks,
&virQEMUDriverDomainXMLNamespace,
&virQEMUDriverDomainABIStability,
&virQEMUDriverDomainSaveCookie);
}
virCapsPtr virQEMUDriverCreateCapabilities(virQEMUDriverPtr driver)
{
size_t i, j;
virCapsPtr caps;
virSecurityManagerPtr *sec_managers = NULL;
/* Security driver data */
const char *doi, *model, *lbl, *type;
virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
const int virtTypes[] = {VIR_DOMAIN_VIRT_KVM,
VIR_DOMAIN_VIRT_QEMU,};
/* Basic host arch / guest machine capabilities */
if (!(caps = virQEMUCapsInit(driver->qemuCapsCache)))
goto error;
if (virGetHostUUID(caps->host.host_uuid)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot get the host uuid"));
goto error;
}
/* access sec drivers and create a sec model for each one */
if (!(sec_managers = qemuSecurityGetNested(driver->securityManager)))
goto error;
/* calculate length */
for (i = 0; sec_managers[i]; i++)
;
caps->host.nsecModels = i;
if (VIR_ALLOC_N(caps->host.secModels, caps->host.nsecModels) < 0)
goto error;
for (i = 0; sec_managers[i]; i++) {
virCapsHostSecModelPtr sm = &caps->host.secModels[i];
doi = qemuSecurityGetDOI(sec_managers[i]);
model = qemuSecurityGetModel(sec_managers[i]);
if (VIR_STRDUP(sm->model, model) < 0 ||
VIR_STRDUP(sm->doi, doi) < 0)
goto error;
for (j = 0; j < ARRAY_CARDINALITY(virtTypes); j++) {
lbl = qemuSecurityGetBaseLabel(sec_managers[i], virtTypes[j]);
type = virDomainVirtTypeToString(virtTypes[j]);
if (lbl &&
virCapabilitiesHostSecModelAddBaseLabel(sm, type, lbl) < 0)
goto error;
}
VIR_DEBUG("Initialized caps for security driver \"%s\" with "
"DOI \"%s\"", model, doi);
}
VIR_FREE(sec_managers);
virObjectUnref(cfg);
return caps;
error:
VIR_FREE(sec_managers);
virObjectUnref(caps);
virObjectUnref(cfg);
return NULL;
}
/**
* 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 virObjetUnref
*
* 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->nguests == 0 && !refresh) {
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;
}
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;
char *key = NULL;
int rc;
if ((rc = virGetDeviceID(device_path, &maj, &min)) < 0) {
virReportSystemError(-rc,
_("Unable to get minor number of device '%s'"),
device_path);
return NULL;
}
if (virAsprintf(&key, "%d:%d", maj, min) < 0)
return NULL;
return key;
}
/*
* 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(virHashTablePtr sharedDevices,
const char *device_path,
int sgio)
{
char *sysfs_path = NULL;
char *key = NULL;
int val;
int ret = -1;
if (!(sysfs_path = virGetUnprivSGIOSysfsPath(device_path, NULL)))
goto cleanup;
/* It can't be conflict if unpriv_sgio is not supported by kernel. */
if (!virFileExists(sysfs_path)) {
ret = 0;
goto cleanup;
}
if (!(key = qemuGetSharedDeviceKey(device_path)))
goto cleanup;
/* It can't be conflict if no other domain is sharing it. */
if (!(virHashLookup(sharedDevices, key))) {
ret = 0;
goto cleanup;
}
if (virGetDeviceUnprivSGIO(device_path, NULL, &val) < 0)
goto cleanup;
/* 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))) {
ret = -2;
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(sysfs_path);
VIR_FREE(key);
return ret;
}
/* 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(virHashTablePtr 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, const void *name ATTRIBUTE_UNUSED)
{
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 ||
VIR_STRDUP(entry->domains[entry->ref - 1], name) < 0) {
/* entry is owned by the hash table here */
entry = NULL;
goto error;
}
}
} else {
if (VIR_ALLOC(entry) < 0 ||
VIR_ALLOC_N(entry->domains, 1) < 0 ||
VIR_STRDUP(entry->domains[0], name) < 0)
goto error;
entry->ref = 1;
if (virHashAddEntry(driver->sharedDevices, key, entry) < 0)
goto error;
}
return 0;
error:
qemuSharedDeviceEntryFree(entry, NULL);
return -1;
}
/* 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)
{
char *key = NULL;
int ret = -1;
if (virStorageSourceIsEmpty(disk->src) ||
!disk->src->shared ||
!virStorageSourceIsBlockLocal(disk->src))
return 0;
qemuDriverLock(driver);
if (qemuCheckSharedDisk(driver->sharedDevices, disk) < 0)
goto cleanup;
if (!(key = qemuGetSharedDeviceKey(virDomainDiskGetSource(disk))))
goto cleanup;
if (qemuSharedDeviceEntryInsert(driver, key, name) < 0)
goto cleanup;
ret = 0;
cleanup:
qemuDriverUnlock(driver);
VIR_FREE(key);
return ret;
}
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;
char *dev_name = NULL;
char *dev_path = NULL;
if (!(dev_name = virSCSIDeviceGetDevName(NULL,
scsihostsrc->adapter,
scsihostsrc->bus,
scsihostsrc->target,
scsihostsrc->unit)))
goto cleanup;
ignore_value(virAsprintf(&dev_path, "/dev/%s", dev_name));
cleanup:
VIR_FREE(dev_name);
return dev_path;
}
static int
qemuAddSharedHostdev(virQEMUDriverPtr driver,
virDomainHostdevDefPtr hostdev,
const char *name)
{
char *dev_path = NULL;
char *key = NULL;
int ret = -1;
if (!qemuIsSharedHostdev(hostdev))
return 0;
if (!(dev_path = qemuGetHostdevPath(hostdev)))
goto cleanup;
if (!(key = qemuGetSharedDeviceKey(dev_path)))
goto cleanup;
qemuDriverLock(driver);
ret = qemuSharedDeviceEntryInsert(driver, key, name);
qemuDriverUnlock(driver);
cleanup:
VIR_FREE(dev_path);
VIR_FREE(key);
return ret;
}
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 recored in the table. */
if (!qemuSharedDeviceEntryDomainExists(entry, name, &idx))
return 0;
if (entry->ref != 1)
VIR_DELETE_ELEMENT(entry->domains, idx, entry->ref);
else
ignore_value(virHashRemoveEntry(driver->sharedDevices, key));
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)
{
/* 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 qemuAddSharedDisk(driver, dev->data.disk, name);
else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV)
return qemuAddSharedHostdev(driver, dev->data.hostdev, name);
else
return 0;
}
int
qemuRemoveSharedDisk(virQEMUDriverPtr driver,
virDomainDiskDefPtr disk,
const char *name)
{
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 (qemuSharedDeviceEntryRemove(driver, key, name) < 0)
goto cleanup;
ret = 0;
cleanup:
qemuDriverUnlock(driver);
VIR_FREE(key);
return ret;
}
static int
qemuRemoveSharedHostdev(virQEMUDriverPtr driver,
virDomainHostdevDefPtr hostdev,
const char *name)
{
char *dev_path = NULL;
char *key = NULL;
int ret = -1;
if (!qemuIsSharedHostdev(hostdev))
return 0;
if (!(dev_path = qemuGetHostdevPath(hostdev)))
goto cleanup;
if (!(key = qemuGetSharedDeviceKey(dev_path)))
goto cleanup;
qemuDriverLock(driver);
ret = qemuSharedDeviceEntryRemove(driver, key, name);
qemuDriverUnlock(driver);
cleanup:
VIR_FREE(dev_path);
VIR_FREE(key);
return ret;
}
/* 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)
{
if (dev->type == VIR_DOMAIN_DEVICE_DISK)
return qemuRemoveSharedDisk(driver, dev->data.disk, name);
else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV)
return qemuRemoveSharedHostdev(driver, dev->data.hostdev, name);
else
return 0;
}
int
qemuSetUnprivSGIO(virDomainDeviceDefPtr dev)
{
virDomainDiskDefPtr disk = NULL;
virDomainHostdevDefPtr hostdev = NULL;
char *sysfs_path = NULL;
const char *path = NULL;
int val = -1;
int ret = -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 "));
goto cleanup;
}
return 0;
} else {
return 0;
}
if (!(sysfs_path = virGetUnprivSGIOSysfsPath(path, NULL)))
goto cleanup;
/* 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)
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(sysfs_path);
return ret;
}
int qemuDriverAllocateID(virQEMUDriverPtr driver)
{
return virAtomicIntInc(&driver->lastvmid);
}
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(virHugeTLBFSPtr hugepage)
{
char *ret;
if (virAsprintf(&ret, "%s/libvirt/qemu", hugepage->mnt_dir) < 0)
return NULL;
return ret;
}
char *
qemuGetDomainHugepagePath(const virDomainDef *def,
virHugeTLBFSPtr hugepage)
{
char *base = qemuGetBaseHugepagePath(hugepage);
char *domPath = virDomainDefGetShortName(def);
char *ret = NULL;
if (base && domPath)
ignore_value(virAsprintf(&ret, "%s/%s", base, domPath));
VIR_FREE(domPath);
VIR_FREE(base);
return ret;
}
/**
* qemuGetDomainDefaultHugepath:
* @def: domain definition
* @hugetlbfs: array of configured hugepages
* @nhugetlbfs: number of item in the array
*
* Callers must ensure that @hugetlbfs contains at least one entry.
*
* Returns 0 on success, -1 otherwise.
* */
char *
qemuGetDomainDefaultHugepath(const virDomainDef *def,
virHugeTLBFSPtr hugetlbfs,
size_t nhugetlbfs)
{
size_t i;
for (i = 0; i < nhugetlbfs; i++)
if (hugetlbfs[i].deflt)
break;
if (i == nhugetlbfs)
i = 0;
return qemuGetDomainHugepagePath(def, &hugetlbfs[i]);
}
/**
* qemuGetDomainHupageMemPath: Construct HP enabled memory backend path
*
* If no specific hugepage size is requested (@pagesize is zero)
* the default hugepage size is used).
* The resulting path is stored at @memPath.
*
* Returns 0 on success,
* -1 otherwise.
*/
int
qemuGetDomainHupageMemPath(const virDomainDef *def,
virQEMUDriverConfigPtr cfg,
unsigned long long pagesize,
char **memPath)
{
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;
}
if (!pagesize) {
if (!(*memPath = qemuGetDomainDefaultHugepath(def,
cfg->hugetlbfs,
cfg->nhugetlbfs)))
return -1;
} else {
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(def, &cfg->hugetlbfs[i])))
return -1;
}
return 0;
}
int
qemuGetMemoryBackingBasePath(virQEMUDriverConfigPtr cfg,
char **path)
{
return virAsprintf(path, "%s/libvirt/qemu", cfg->memoryBackingDir);
}
int
qemuGetMemoryBackingDomainPath(const virDomainDef *def,
virQEMUDriverConfigPtr cfg,
char **path)
{
char *shortName = NULL;
char *base = NULL;
int ret = -1;
if (!(shortName = virDomainDefGetShortName(def)) ||
qemuGetMemoryBackingBasePath(cfg, &base) < 0 ||
virAsprintf(path, "%s/%s", base, shortName) < 0)
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(base);
VIR_FREE(shortName);
return ret;
}
/**
* qemuGetMemoryBackingPath:
* @def: domain definition
* @cfg: the driver config
* @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(const virDomainDef *def,
virQEMUDriverConfigPtr cfg,
const char *alias,
char **memPath)
{
char *domainPath = NULL;
int ret = -1;
if (!alias) {
/* This should never happen (TM) */
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("memory device alias is not assigned"));
goto cleanup;
}
if (qemuGetMemoryBackingDomainPath(def, cfg, &domainPath) < 0 ||
virAsprintf(memPath, "%s/%s", domainPath, alias) < 0)
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(domainPath);
return ret;
}