libvirt/src/qemu/qemu_conf.c
Martin Kletzander 29226beefe qemu: configurable remote display port boundaries
The defines QEMU_REMOTE_PORT_MIN and QEMU_REMOTE_PORT_MAX were used to
find free port when starting domains. As this was hard-coded to the
same ports as default VNC servers, there were races with these other
programs. This patch includes the possibility to change the default
starting port as well as the maximum port (mostly for completeness) in
qemu config file.

Support for two new config options in qemu.conf is added:
 - remote_port_min (defaults to QEMU_REMOTE_PORT_MIN and
   must be >= than this value)
 - remote_port_max (defaults to QEMU_REMOTE_PORT_MAX and
   must be <= than this value)
2012-08-21 11:36:32 +02:00

743 lines
23 KiB
C

/*
* qemu_conf.c: QEMU configuration management
*
* Copyright (C) 2006-2012 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/>.
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <sys/utsname.h>
#include <mntent.h>
#include "virterror_internal.h"
#include "qemu_conf.h"
#include "qemu_command.h"
#include "qemu_capabilities.h"
#include "qemu_bridge_filter.h"
#include "uuid.h"
#include "buf.h"
#include "conf.h"
#include "util.h"
#include "memory.h"
#include "datatypes.h"
#include "xml.h"
#include "nodeinfo.h"
#include "logging.h"
#include "cpu/cpu.h"
#include "domain_nwfilter.h"
#include "virfile.h"
#include "configmake.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
struct _qemuDriverCloseDef {
virConnectPtr conn;
qemuDriverCloseCallback cb;
};
void qemuDriverLock(struct qemud_driver *driver)
{
virMutexLock(&driver->lock);
}
void qemuDriverUnlock(struct qemud_driver *driver)
{
virMutexUnlock(&driver->lock);
}
int qemudLoadDriverConfig(struct qemud_driver *driver,
const char *filename) {
virConfPtr conf;
virConfValuePtr p;
char *user;
char *group;
int i;
/* Setup critical defaults */
driver->securityDefaultConfined = true;
driver->securityRequireConfined = false;
driver->dynamicOwnership = 1;
driver->clearEmulatorCapabilities = 1;
if (!(driver->vncListen = strdup("127.0.0.1"))) {
virReportOOMError();
return -1;
}
driver->remotePortMin = QEMU_REMOTE_PORT_MIN;
driver->remotePortMax = QEMU_REMOTE_PORT_MAX;
if (!(driver->vncTLSx509certdir = strdup(SYSCONFDIR "/pki/libvirt-vnc"))) {
virReportOOMError();
return -1;
}
if (!(driver->spiceListen = strdup("127.0.0.1"))) {
virReportOOMError();
return -1;
}
if (!(driver->spiceTLSx509certdir
= strdup(SYSCONFDIR "/pki/libvirt-spice"))) {
virReportOOMError();
return -1;
}
#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R
/* For privileged driver, try and find hugepage mount automatically.
* Non-privileged driver requires admin to create a dir for the
* user, chown it, and then let user configure it manually */
if (driver->privileged &&
!(driver->hugetlbfs_mount = virFileFindMountPoint("hugetlbfs"))) {
if (errno != ENOENT) {
virReportSystemError(errno, "%s",
_("unable to find hugetlbfs mountpoint"));
return -1;
}
}
#endif
if (!(driver->lockManager =
virLockManagerPluginNew("nop", NULL, 0)))
return -1;
driver->keepAliveInterval = 5;
driver->keepAliveCount = 5;
/* 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;
}
conf = virConfReadFile (filename, 0);
if (!conf) {
return -1;
}
#define CHECK_TYPE(name,typ) if (p && p->type != (typ)) { \
virReportError(VIR_ERR_INTERNAL_ERROR, \
"%s: %s: expected type " #typ, \
filename, (name)); \
virConfFree(conf); \
return -1; \
}
p = virConfGetValue (conf, "vnc_auto_unix_socket");
CHECK_TYPE ("vnc_auto_unix_socket", VIR_CONF_LONG);
if (p) driver->vncAutoUnixSocket = p->l;
p = virConfGetValue (conf, "vnc_tls");
CHECK_TYPE ("vnc_tls", VIR_CONF_LONG);
if (p) driver->vncTLS = p->l;
p = virConfGetValue (conf, "vnc_tls_x509_verify");
CHECK_TYPE ("vnc_tls_x509_verify", VIR_CONF_LONG);
if (p) driver->vncTLSx509verify = p->l;
p = virConfGetValue (conf, "vnc_tls_x509_cert_dir");
CHECK_TYPE ("vnc_tls_x509_cert_dir", VIR_CONF_STRING);
if (p && p->str) {
VIR_FREE(driver->vncTLSx509certdir);
if (!(driver->vncTLSx509certdir = strdup(p->str))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
}
p = virConfGetValue (conf, "vnc_listen");
CHECK_TYPE ("vnc_listen", VIR_CONF_STRING);
if (p && p->str) {
VIR_FREE(driver->vncListen);
if (!(driver->vncListen = strdup(p->str))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
}
p = virConfGetValue (conf, "vnc_password");
CHECK_TYPE ("vnc_password", VIR_CONF_STRING);
if (p && p->str) {
VIR_FREE(driver->vncPassword);
if (!(driver->vncPassword = strdup(p->str))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
}
p = virConfGetValue (conf, "security_driver");
if (p && p->type == VIR_CONF_LIST) {
size_t len;
virConfValuePtr pp;
/* Calc lenght and check items */
for (len = 0, pp = p->list; pp; len++, pp = pp->next) {
if (pp->type != VIR_CONF_STRING) {
VIR_ERROR(_("security_driver be a list of strings"));
virConfFree(conf);
return -1;
}
}
if (VIR_ALLOC_N(driver->securityDriverNames, len + 1) < 0) {
virReportOOMError();
virConfFree(conf);
return -1;
}
for (i = 0, pp = p->list; pp; i++, pp = pp->next) {
driver->securityDriverNames[i] = strdup(pp->str);
if (driver->securityDriverNames == NULL) {
virReportOOMError();
virConfFree(conf);
return -1;
}
}
driver->securityDriverNames[len] = NULL;
} else {
CHECK_TYPE ("security_driver", VIR_CONF_STRING);
if (p && p->str) {
if (VIR_ALLOC_N(driver->securityDriverNames, 2) < 0 ||
!(driver->securityDriverNames[0] = strdup(p->str))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
driver->securityDriverNames[1] = NULL;
}
}
p = virConfGetValue (conf, "security_default_confined");
CHECK_TYPE ("security_default_confined", VIR_CONF_LONG);
if (p) driver->securityDefaultConfined = p->l;
p = virConfGetValue (conf, "security_require_confined");
CHECK_TYPE ("security_require_confined", VIR_CONF_LONG);
if (p) driver->securityRequireConfined = p->l;
p = virConfGetValue (conf, "vnc_sasl");
CHECK_TYPE ("vnc_sasl", VIR_CONF_LONG);
if (p) driver->vncSASL = p->l;
p = virConfGetValue (conf, "vnc_sasl_dir");
CHECK_TYPE ("vnc_sasl_dir", VIR_CONF_STRING);
if (p && p->str) {
VIR_FREE(driver->vncSASLdir);
if (!(driver->vncSASLdir = strdup(p->str))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
}
p = virConfGetValue (conf, "spice_tls");
CHECK_TYPE ("spice_tls", VIR_CONF_LONG);
if (p) driver->spiceTLS = p->l;
p = virConfGetValue (conf, "spice_tls_x509_cert_dir");
CHECK_TYPE ("spice_tls_x509_cert_dir", VIR_CONF_STRING);
if (p && p->str) {
VIR_FREE(driver->spiceTLSx509certdir);
if (!(driver->spiceTLSx509certdir = strdup(p->str))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
}
p = virConfGetValue (conf, "spice_listen");
CHECK_TYPE ("spice_listen", VIR_CONF_STRING);
if (p && p->str) {
VIR_FREE(driver->spiceListen);
if (!(driver->spiceListen = strdup(p->str))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
}
p = virConfGetValue (conf, "spice_password");
CHECK_TYPE ("spice_password", VIR_CONF_STRING);
if (p && p->str) {
VIR_FREE(driver->spicePassword);
if (!(driver->spicePassword = strdup(p->str))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
}
p = virConfGetValue (conf, "remote_display_port_min");
CHECK_TYPE ("remote_display_port_min", VIR_CONF_LONG);
if (p) {
if (p->l < 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);
virConfFree(conf);
return -1;
}
driver->remotePortMin = p->l;
}
p = virConfGetValue (conf, "remote_display_port_max");
CHECK_TYPE ("remote_display_port_max", VIR_CONF_LONG);
if (p) {
if (p->l > QEMU_REMOTE_PORT_MAX ||
p->l < driver->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);
virConfFree(conf);
return -1;
}
/* increasing the value by 1 makes all the loops going through
the bitmap (i = remotePortMin; i < remotePortMax; i++), work as
expected. */
driver->remotePortMax = p->l + 1;
}
if (driver->remotePortMin > driver->remotePortMax) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s: remote_display_port_min: min port must not be greater than max port"),
filename);
virConfFree(conf);
return -1;
}
p = virConfGetValue (conf, "user");
CHECK_TYPE ("user", VIR_CONF_STRING);
if (!(user = strdup(p && p->str ? p->str : QEMU_USER))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
if (virGetUserID(user, &driver->user) < 0) {
VIR_FREE(user);
virConfFree(conf);
return -1;
}
VIR_FREE(user);
p = virConfGetValue (conf, "group");
CHECK_TYPE ("group", VIR_CONF_STRING);
if (!(group = strdup(p && p->str ? p->str : QEMU_GROUP))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
if (virGetGroupID(group, &driver->group) < 0) {
VIR_FREE(group);
virConfFree(conf);
return -1;
}
VIR_FREE(group);
p = virConfGetValue (conf, "dynamic_ownership");
CHECK_TYPE ("dynamic_ownership", VIR_CONF_LONG);
if (p) driver->dynamicOwnership = p->l;
p = virConfGetValue (conf, "cgroup_controllers");
CHECK_TYPE ("cgroup_controllers", VIR_CONF_LIST);
if (p) {
virConfValuePtr pp;
for (i = 0, pp = p->list; pp; ++i, pp = pp->next) {
int ctl;
if (pp->type != VIR_CONF_STRING) {
VIR_ERROR(_("cgroup_controllers must be a list of strings"));
virConfFree(conf);
return -1;
}
ctl = virCgroupControllerTypeFromString(pp->str);
if (ctl < 0) {
VIR_ERROR(_("Unknown cgroup controller '%s'"), pp->str);
virConfFree(conf);
return -1;
}
driver->cgroupControllers |= (1 << ctl);
}
} else {
driver->cgroupControllers =
(1 << VIR_CGROUP_CONTROLLER_CPU) |
(1 << VIR_CGROUP_CONTROLLER_DEVICES) |
(1 << VIR_CGROUP_CONTROLLER_MEMORY) |
(1 << VIR_CGROUP_CONTROLLER_BLKIO) |
(1 << VIR_CGROUP_CONTROLLER_CPUSET) |
(1 << VIR_CGROUP_CONTROLLER_CPUACCT);
}
for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
if (driver->cgroupControllers & (1 << i)) {
VIR_INFO("Configured cgroup controller '%s'",
virCgroupControllerTypeToString(i));
}
}
p = virConfGetValue (conf, "cgroup_device_acl");
CHECK_TYPE ("cgroup_device_acl", VIR_CONF_LIST);
if (p) {
int len = 0;
virConfValuePtr pp;
for (pp = p->list; pp; pp = pp->next)
len++;
if (VIR_ALLOC_N(driver->cgroupDeviceACL, 1+len) < 0) {
virReportOOMError();
virConfFree(conf);
return -1;
}
for (i = 0, pp = p->list; pp; ++i, pp = pp->next) {
if (pp->type != VIR_CONF_STRING) {
VIR_ERROR(_("cgroup_device_acl must be a list of strings"));
virConfFree(conf);
return -1;
}
driver->cgroupDeviceACL[i] = strdup (pp->str);
if (driver->cgroupDeviceACL[i] == NULL) {
virReportOOMError();
virConfFree(conf);
return -1;
}
}
driver->cgroupDeviceACL[i] = NULL;
}
p = virConfGetValue (conf, "save_image_format");
CHECK_TYPE ("save_image_format", VIR_CONF_STRING);
if (p && p->str) {
VIR_FREE(driver->saveImageFormat);
if (!(driver->saveImageFormat = strdup(p->str))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
}
p = virConfGetValue (conf, "dump_image_format");
CHECK_TYPE ("dump_image_format", VIR_CONF_STRING);
if (p && p->str) {
VIR_FREE(driver->dumpImageFormat);
if (!(driver->dumpImageFormat = strdup(p->str))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
}
p = virConfGetValue (conf, "auto_dump_path");
CHECK_TYPE ("auto_dump_path", VIR_CONF_STRING);
if (p && p->str) {
VIR_FREE(driver->autoDumpPath);
if (!(driver->autoDumpPath = strdup(p->str))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
}
p = virConfGetValue (conf, "auto_dump_bypass_cache");
CHECK_TYPE ("auto_dump_bypass_cache", VIR_CONF_LONG);
if (p) driver->autoDumpBypassCache = true;
p = virConfGetValue (conf, "auto_start_bypass_cache");
CHECK_TYPE ("auto_start_bypass_cache", VIR_CONF_LONG);
if (p) driver->autoStartBypassCache = true;
p = virConfGetValue (conf, "hugetlbfs_mount");
CHECK_TYPE ("hugetlbfs_mount", VIR_CONF_STRING);
if (p && p->str) {
VIR_FREE(driver->hugetlbfs_mount);
if (!(driver->hugetlbfs_mount = strdup(p->str))) {
virReportOOMError();
virConfFree(conf);
return -1;
}
}
p = virConfGetValue (conf, "mac_filter");
CHECK_TYPE ("mac_filter", VIR_CONF_LONG);
if (p && p->l) {
driver->macFilter = p->l;
if (!(driver->ebtables = ebtablesContextNew("qemu"))) {
driver->macFilter = 0;
virReportSystemError(errno,
_("failed to enable mac filter in '%s'"),
__FILE__);
virConfFree(conf);
return -1;
}
if ((errno = networkDisableAllFrames(driver))) {
virReportSystemError(errno,
_("failed to add rule to drop all frames in '%s'"),
__FILE__);
virConfFree(conf);
return -1;
}
}
p = virConfGetValue (conf, "relaxed_acs_check");
CHECK_TYPE ("relaxed_acs_check", VIR_CONF_LONG);
if (p) driver->relaxedACS = p->l;
p = virConfGetValue (conf, "vnc_allow_host_audio");
CHECK_TYPE ("vnc_allow_host_audio", VIR_CONF_LONG);
if (p) driver->vncAllowHostAudio = p->l;
p = virConfGetValue (conf, "clear_emulator_capabilities");
CHECK_TYPE ("clear_emulator_capabilities", VIR_CONF_LONG);
if (p) driver->clearEmulatorCapabilities = p->l;
p = virConfGetValue (conf, "allow_disk_format_probing");
CHECK_TYPE ("allow_disk_format_probing", VIR_CONF_LONG);
if (p) driver->allowDiskFormatProbing = p->l;
p = virConfGetValue (conf, "set_process_name");
CHECK_TYPE ("set_process_name", VIR_CONF_LONG);
if (p) driver->setProcessName = p->l;
p = virConfGetValue(conf, "max_processes");
CHECK_TYPE("max_processes", VIR_CONF_LONG);
if (p) driver->maxProcesses = p->l;
p = virConfGetValue(conf, "max_files");
CHECK_TYPE("max_files", VIR_CONF_LONG);
if (p) driver->maxFiles = p->l;
p = virConfGetValue (conf, "lock_manager");
CHECK_TYPE ("lock_manager", VIR_CONF_STRING);
if (p && p->str) {
char *lockConf;
virLockManagerPluginUnref(driver->lockManager);
if (virAsprintf(&lockConf, "%s/libvirt/qemu-%s.conf", SYSCONFDIR, p->str) < 0) {
virReportOOMError();
virConfFree(conf);
return -1;
}
if (!(driver->lockManager =
virLockManagerPluginNew(p->str, lockConf, 0)))
VIR_ERROR(_("Failed to load lock manager %s"), p->str);
VIR_FREE(lockConf);
}
p = virConfGetValue(conf, "max_queued");
CHECK_TYPE("max_queued", VIR_CONF_LONG);
if (p) driver->max_queued = p->l;
p = virConfGetValue(conf, "keepalive_interval");
CHECK_TYPE("keepalive_interval", VIR_CONF_LONG);
if (p) driver->keepAliveInterval = p->l;
p = virConfGetValue(conf, "keepalive_count");
CHECK_TYPE("keepalive_count", VIR_CONF_LONG);
if (p) driver->keepAliveCount = p->l;
virConfFree (conf);
return 0;
}
static void
qemuDriverCloseCallbackFree(void *payload,
const void *name ATTRIBUTE_UNUSED)
{
VIR_FREE(payload);
}
int
qemuDriverCloseCallbackInit(struct qemud_driver *driver)
{
driver->closeCallbacks = virHashCreate(5, qemuDriverCloseCallbackFree);
if (!driver->closeCallbacks)
return -1;
return 0;
}
void
qemuDriverCloseCallbackShutdown(struct qemud_driver *driver)
{
virHashFree(driver->closeCallbacks);
}
int
qemuDriverCloseCallbackSet(struct qemud_driver *driver,
virDomainObjPtr vm,
virConnectPtr conn,
qemuDriverCloseCallback cb)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
qemuDriverCloseDefPtr closeDef;
virUUIDFormat(vm->def->uuid, uuidstr);
VIR_DEBUG("vm=%s, uuid=%s, conn=%p, cb=%p",
vm->def->name, uuidstr, conn, cb);
closeDef = virHashLookup(driver->closeCallbacks, uuidstr);
if (closeDef) {
if (closeDef->conn != conn) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Close callback for domain %s already registered"
" with another connection %p"),
vm->def->name, closeDef->conn);
return -1;
}
if (closeDef->cb && closeDef->cb != cb) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Another close callback is already defined for"
" domain %s"), vm->def->name);
return -1;
}
closeDef->cb = cb;
} else {
if (VIR_ALLOC(closeDef) < 0) {
virReportOOMError();
return -1;
}
closeDef->conn = conn;
closeDef->cb = cb;
if (virHashAddEntry(driver->closeCallbacks, uuidstr, closeDef) < 0) {
VIR_FREE(closeDef);
return -1;
}
}
return 0;
}
int
qemuDriverCloseCallbackUnset(struct qemud_driver *driver,
virDomainObjPtr vm,
qemuDriverCloseCallback cb)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
qemuDriverCloseDefPtr closeDef;
virUUIDFormat(vm->def->uuid, uuidstr);
VIR_DEBUG("vm=%s, uuid=%s, cb=%p",
vm->def->name, uuidstr, cb);
closeDef = virHashLookup(driver->closeCallbacks, uuidstr);
if (!closeDef)
return -1;
if (closeDef->cb && closeDef->cb != cb) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Trying to remove mismatching close callback for"
" domain %s"), vm->def->name);
return -1;
}
return virHashRemoveEntry(driver->closeCallbacks, uuidstr);
}
qemuDriverCloseCallback
qemuDriverCloseCallbackGet(struct qemud_driver *driver,
virDomainObjPtr vm,
virConnectPtr conn)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
qemuDriverCloseDefPtr closeDef;
qemuDriverCloseCallback cb = NULL;
virUUIDFormat(vm->def->uuid, uuidstr);
VIR_DEBUG("vm=%s, uuid=%s, conn=%p",
vm->def->name, uuidstr, conn);
closeDef = virHashLookup(driver->closeCallbacks, uuidstr);
if (closeDef && (!conn || closeDef->conn == conn))
cb = closeDef->cb;
VIR_DEBUG("cb=%p", cb);
return cb;
}
struct qemuDriverCloseCallbackData {
struct qemud_driver *driver;
virConnectPtr conn;
};
static void
qemuDriverCloseCallbackRun(void *payload,
const void *name,
void *opaque)
{
struct qemuDriverCloseCallbackData *data = opaque;
qemuDriverCloseDefPtr closeDef = payload;
const char *uuidstr = name;
unsigned char uuid[VIR_UUID_BUFLEN];
virDomainObjPtr dom;
VIR_DEBUG("conn=%p, thisconn=%p, uuid=%s, cb=%p",
closeDef->conn, data->conn, uuidstr, closeDef->cb);
if (data->conn != closeDef->conn || !closeDef->cb)
return;
if (virUUIDParse(uuidstr, uuid) < 0) {
VIR_WARN("Failed to parse %s", uuidstr);
return;
}
if (!(dom = virDomainFindByUUID(&data->driver->domains, uuid))) {
VIR_DEBUG("No domain object with UUID %s", uuidstr);
return;
}
dom = closeDef->cb(data->driver, dom, data->conn);
if (dom)
virDomainObjUnlock(dom);
virHashRemoveEntry(data->driver->closeCallbacks, uuidstr);
}
void
qemuDriverCloseCallbackRunAll(struct qemud_driver *driver,
virConnectPtr conn)
{
struct qemuDriverCloseCallbackData data = {
driver, conn
};
VIR_DEBUG("conn=%p", conn);
virHashForEach(driver->closeCallbacks, qemuDriverCloseCallbackRun, &data);
}