Move the qemudStartVMDaemon and qemudShutdownVMDaemon
methods into a separate file, renaming them to
qemuProcessStart, qemuProcessStop. All helper methods
called by these are also moved & renamed to match
* src/Makefile.am: Add qemu_process.c/.h
* src/qemu/qemu_command.c: Add emuDomainAssignPCIAddresses
* src/qemu/qemu_command.h: Add VNC port min/max
* src/qemu/qemu_domain.c, src/qemu/qemu_domain.h: Add
domain event queue helpers
* src/qemu/qemu_driver.c, src/qemu/qemu_driver.h: Remove
all QEMU process startup/shutdown functions
* src/qemu/qemu_process.c, src/qemu/qemu_process.h: Add
all QEMU process startup/shutdown functions
---
src/Makefile.am | 1 +
src/qemu/qemu_command.c | 29 +
src/qemu/qemu_command.h | 5 +
src/qemu/qemu_domain.c | 56 +
src/qemu/qemu_domain.h | 11 +
src/qemu/qemu_driver.c | 3519 ++++++++---------------------------------------
src/qemu/qemu_driver.h | 31 +-
src/qemu/qemu_process.c | 2412 ++++++++++++++++++++++++++++++++
src/qemu/qemu_process.h | 52 +
9 files changed, 3110 insertions(+), 3006 deletions(-)
create mode 100644 src/qemu/qemu_process.c
create mode 100644 src/qemu/qemu_process.h
diff --git a/src/Makefile.am b/src/Makefile.am
index f8b8434..9ef1d36 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -281,6 +281,7 @@ QEMU_DRIVER_SOURCES = \
qemu/qemu_hostdev.c qemu/qemu_hostdev.h \
qemu/qemu_hotplug.c qemu/qemu_hotplug.h \
qemu/qemu_conf.c qemu/qemu_conf.h \
+ qemu/qemu_process.c qemu/qemu_process.h \
qemu/qemu_monitor.c qemu/qemu_monitor.h \
qemu/qemu_monitor_text.c \
qemu/qemu_monitor_text.h \
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 1ce8941..4f4658b 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -711,6 +711,35 @@ static int qemuCollectPCIAddress(virDomainDefPtr def
ATTRIBUTE_UNUSED,
}
+int
+qemuDomainAssignPCIAddresses(virDomainDefPtr def)
+{
+ int ret = -1;
+ unsigned long long qemuCmdFlags = 0;
+ qemuDomainPCIAddressSetPtr addrs = NULL;
+
+ if (qemuCapsExtractVersionInfo(def->emulator,
+ NULL,
+ &qemuCmdFlags) < 0)
+ goto cleanup;
+
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (!(addrs = qemuDomainPCIAddressSetCreate(def)))
+ goto cleanup;
+
+ if (qemuAssignDevicePCISlots(def, addrs) < 0)
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ qemuDomainPCIAddressSetFree(addrs);
+
+ return ret;
+}
+
+
qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def)
{
qemuDomainPCIAddressSetPtr addrs;
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index e410259..b562919 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -37,6 +37,10 @@
# define QEMU_VIRTIO_SERIAL_PREFIX "virtio-serial"
# define QEMU_FSDEV_HOST_PREFIX "fsdev-"
+#define QEMU_VNC_PORT_MIN 5900
+#define QEMU_VNC_PORT_MAX 65535
+
+
virCommandPtr qemuBuildCommandLine(virConnectPtr conn,
struct qemud_driver *driver,
virDomainDefPtr def,
@@ -130,6 +134,7 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps,
virDomainDefPtr qemuParseCommandLineString(virCapsPtr caps,
const char *args);
+int qemuDomainAssignPCIAddresses(virDomainDefPtr def);
qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def);
int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs,
int slot);
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index fa7c8bd..e3163ab 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -29,6 +29,7 @@
#include "logging.h"
#include "virterror_internal.h"
#include "c-ctype.h"
+#include "event.h"
#include <sys/time.h>
@@ -41,6 +42,61 @@
#define timeval_to_ms(tv) (((tv).tv_sec * 1000ull) + ((tv).tv_usec / 1000))
+static void qemuDomainEventDispatchFunc(virConnectPtr conn,
+ virDomainEventPtr event,
+ virConnectDomainEventGenericCallback cb,
+ void *cbopaque,
+ void *opaque)
+{
+ struct qemud_driver *driver = opaque;
+
+ /* Drop the lock whle dispatching, for sake of re-entrancy */
+ qemuDriverUnlock(driver);
+ virDomainEventDispatchDefaultFunc(conn, event, cb, cbopaque, NULL);
+ qemuDriverLock(driver);
+}
+
+void qemuDomainEventFlush(int timer ATTRIBUTE_UNUSED, void *opaque)
+{
+ struct qemud_driver *driver = opaque;
+ virDomainEventQueue tempQueue;
+
+ qemuDriverLock(driver);
+
+ driver->domainEventDispatching = 1;
+
+ /* Copy the queue, so we're reentrant safe */
+ tempQueue.count = driver->domainEventQueue->count;
+ tempQueue.events = driver->domainEventQueue->events;
+ driver->domainEventQueue->count = 0;
+ driver->domainEventQueue->events = NULL;
+
+ virEventUpdateTimeout(driver->domainEventTimer, -1);
+ virDomainEventQueueDispatch(&tempQueue,
+ driver->domainEventCallbacks,
+ qemuDomainEventDispatchFunc,
+ driver);
+
+ /* Purge any deleted callbacks */
+ virDomainEventCallbackListPurgeMarked(driver->domainEventCallbacks);
+
+ driver->domainEventDispatching = 0;
+ qemuDriverUnlock(driver);
+}
+
+
+/* driver must be locked before calling */
+void qemuDomainEventQueue(struct qemud_driver *driver,
+ virDomainEventPtr event)
+{
+ if (virDomainEventQueuePush(driver->domainEventQueue,
+ event) < 0)
+ virDomainEventFree(event);
+ if (driver->domainEventQueue->count == 1)
+ virEventUpdateTimeout(driver->domainEventTimer, 0);
+}
+
+
static void *qemuDomainObjPrivateAlloc(void)
{
qemuDomainObjPrivatePtr priv;
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index f14fb79..4333a78 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -77,6 +77,17 @@ struct _qemuDomainObjPrivate {
int persistentAddrs;
};
+struct qemuDomainWatchdogEvent
+{
+ virDomainObjPtr vm;
+ int action;
+};
+
+void qemuDomainEventFlush(int timer ATTRIBUTE_UNUSED, void *opaque);
+
+/* driver must be locked before calling */
+void qemuDomainEventQueue(struct qemud_driver *driver,
+ virDomainEventPtr event);
void qemuDomainSetPrivateDataHooks(virCapsPtr caps);
void qemuDomainSetNamespaceHooks(virCapsPtr caps);
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 84d339b..dcc1050 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -46,9 +46,6 @@
#include <sys/un.h>
-#include "virterror_internal.h"
-#include "logging.h"
-#include "datatypes.h"
#include "qemu_driver.h"
#include "qemu_conf.h"
#include "qemu_capabilities.h"
@@ -59,6 +56,11 @@
#include "qemu_monitor.h"
#include "qemu_bridge_filter.h"
#include "qemu_audit.h"
+#include "qemu_process.h"
+
+#include "virterror_internal.h"
+#include "logging.h"
+#include "datatypes.h"
#include "c-ctype.h"
#include "event.h"
#include "buf.h"
@@ -88,169 +90,48 @@
#define VIR_FROM_THIS VIR_FROM_QEMU
-#define QEMU_VNC_PORT_MIN 5900
-#define QEMU_VNC_PORT_MAX 65535
-
#define QEMU_NB_MEM_PARAM 3
+# if HAVE_LINUX_KVM_H
+# include <linux/kvm.h>
+# endif
-#define timeval_to_ms(tv) (((tv).tv_sec * 1000ull) + ((tv).tv_usec / 1000))
+/* device for kvm ioctls */
+# define KVM_DEVICE "/dev/kvm"
-struct watchdogEvent
-{
- virDomainObjPtr vm;
- int action;
-};
+/* add definitions missing in older linux/kvm.h */
+# ifndef KVMIO
+# define KVMIO 0xAE
+# endif
+# ifndef KVM_CHECK_EXTENSION
+# define KVM_CHECK_EXTENSION _IO(KVMIO, 0x03)
+# endif
+# ifndef KVM_CAP_NR_VCPUS
+# define KVM_CAP_NR_VCPUS 9 /* returns max vcpus per vm */
+# endif
+
+
+#define timeval_to_ms(tv) (((tv).tv_sec * 1000ull) + ((tv).tv_usec / 1000))
static void processWatchdogEvent(void *data, void *opaque);
static int qemudShutdown(void);
-static void qemuDomainEventFlush(int timer, void *opaque);
-static void qemuDomainEventQueue(struct qemud_driver *driver,
- virDomainEventPtr event);
-
static int qemudDomainObjStart(virConnectPtr conn,
struct qemud_driver *driver,
virDomainObjPtr vm,
bool start_paused);
-static int qemudStartVMDaemon(virConnectPtr conn,
- struct qemud_driver *driver,
- virDomainObjPtr vm,
- const char *migrateFrom,
- bool start_paused,
- int stdin_fd,
- const char *stdin_path,
- enum virVMOperationType vmop);
-
-static void qemudShutdownVMDaemon(struct qemud_driver *driver,
- virDomainObjPtr vm,
- int migrated);
-
static int qemudDomainGetMaxVcpus(virDomainPtr dom);
-static int qemuDetectVcpuPIDs(struct qemud_driver *driver,
- virDomainObjPtr vm);
-
-static int qemudVMFiltersInstantiate(virConnectPtr conn,
- virDomainDefPtr def);
-
-static struct qemud_driver *qemu_driver = NULL;
-
-
-static int doStartCPUs(struct qemud_driver *driver, virDomainObjPtr vm, virConnectPtr
conn)
-{
- int ret;
- qemuDomainObjPrivatePtr priv = vm->privateData;
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- ret = qemuMonitorStartCPUs(priv->mon, conn);
- if (ret == 0) {
- vm->state = VIR_DOMAIN_RUNNING;
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- return ret;
-}
-
-static int doStopCPUs(struct qemud_driver *driver, virDomainObjPtr vm)
-{
- int ret;
- int oldState = vm->state;
- qemuDomainObjPrivatePtr priv = vm->privateData;
-
- vm->state = VIR_DOMAIN_PAUSED;
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- ret = qemuMonitorStopCPUs(priv->mon);
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- if (ret < 0) {
- vm->state = oldState;
- }
- return ret;
-}
-
-
-static int
-qemudLogFD(struct qemud_driver *driver, const char* name, bool append)
-{
- char *logfile;
- mode_t logmode;
- int fd = -1;
-
- if (virAsprintf(&logfile, "%s/%s.log", driver->logDir, name) < 0)
{
- virReportOOMError();
- return -1;
- }
-
- logmode = O_CREAT | O_WRONLY;
- /* Only logrotate files in /var/log, so only append if running privileged */
- if (driver->privileged || append)
- logmode |= O_APPEND;
- else
- logmode |= O_TRUNC;
-
- if ((fd = open(logfile, logmode, S_IRUSR | S_IWUSR)) < 0) {
- virReportSystemError(errno,
- _("failed to create logfile %s"),
- logfile);
- VIR_FREE(logfile);
- return -1;
- }
- VIR_FREE(logfile);
- if (virSetCloseExec(fd) < 0) {
- virReportSystemError(errno, "%s",
- _("Unable to set VM logfile close-on-exec
flag"));
- VIR_FORCE_CLOSE(fd);
- return -1;
- }
- return fd;
-}
-
-
-static int
-qemudLogReadFD(const char* logDir, const char* name, off_t pos)
-{
- char *logfile;
- mode_t logmode = O_RDONLY;
- int fd = -1;
-
- if (virAsprintf(&logfile, "%s/%s.log", logDir, name) < 0) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("failed to build logfile name %s/%s.log"),
- logDir, name);
- return -1;
- }
-
- if ((fd = open(logfile, logmode)) < 0) {
- virReportSystemError(errno,
- _("failed to create logfile %s"),
- logfile);
- VIR_FREE(logfile);
- return -1;
- }
- if (virSetCloseExec(fd) < 0) {
- virReportSystemError(errno, "%s",
- _("Unable to set VM logfile close-on-exec
flag"));
- VIR_FORCE_CLOSE(fd);
- VIR_FREE(logfile);
- return -1;
- }
- if (pos < 0 || lseek(fd, pos, SEEK_SET) < 0) {
- virReportSystemError(pos < 0 ? 0 : errno,
- _("Unable to seek to %lld in %s"),
- (long long) pos, logfile);
- VIR_FORCE_CLOSE(fd);
- }
- VIR_FREE(logfile);
- return fd;
-}
+struct qemud_driver *qemu_driver = NULL;
struct qemuAutostartData {
struct qemud_driver *driver;
virConnectPtr conn;
};
+
static void
qemuAutostartDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque)
{
@@ -283,8 +164,10 @@ qemuAutostartDomain(void *payload, const char *name ATTRIBUTE_UNUSED,
void *opaq
virDomainObjUnlock(vm);
}
+
static void
-qemudAutostartConfigs(struct qemud_driver *driver) {
+qemuAutostartDomains(struct qemud_driver *driver)
+{
/* XXX: Figure out a better way todo this. The domain
* startup code needs a connection handle in order
* to lookup the bridge associated with a virtual
@@ -304,2847 +187,605 @@ qemudAutostartConfigs(struct qemud_driver *driver) {
virConnectClose(conn);
}
-
-/**
- * qemudRemoveDomainStatus
- *
- * remove all state files of a domain from statedir
- *
- * Returns 0 on success
- */
static int
-qemudRemoveDomainStatus(struct qemud_driver *driver,
- virDomainObjPtr vm)
+qemuSecurityInit(struct qemud_driver *driver)
{
- char ebuf[1024];
- char *file = NULL;
-
- if (virAsprintf(&file, "%s/%s.xml", driver->stateDir,
vm->def->name) < 0) {
- virReportOOMError();
- return(-1);
- }
-
- if (unlink(file) < 0 && errno != ENOENT && errno != ENOTDIR)
- VIR_WARN("Failed to remove domain XML for %s: %s",
- vm->def->name, virStrerror(errno, ebuf, sizeof(ebuf)));
- VIR_FREE(file);
+ virSecurityManagerPtr mgr = virSecurityManagerNew(driver->securityDriverName,
+
driver->allowDiskFormatProbing);
+ if (!mgr)
+ goto error;
- if (virFileDeletePid(driver->stateDir, vm->def->name) != 0)
- VIR_WARN("Failed to remove PID file for %s: %s",
- vm->def->name, virStrerror(errno, ebuf, sizeof(ebuf)));
+ if (driver->privileged) {
+ virSecurityManagerPtr dac = virSecurityManagerNewDAC(driver->user,
+ driver->group,
+
driver->allowDiskFormatProbing,
+
driver->dynamicOwnership);
+ if (!dac)
+ goto error;
+ if (!(driver->securityManager = virSecurityManagerNewStack(mgr,
+ dac)))
+ goto error;
+ } else {
+ driver->securityManager = mgr;
+ }
return 0;
-}
-
-/*
- * This is a callback registered with a qemuMonitorPtr instance,
- * and to be invoked when the monitor console hits an end of file
- * condition, or error, thus indicating VM shutdown should be
- * performed
- */
-static void
-qemuHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
- virDomainObjPtr vm,
- int hasError) {
- struct qemud_driver *driver = qemu_driver;
- virDomainEventPtr event = NULL;
- qemuDomainObjPrivatePtr priv;
+error:
+ VIR_ERROR0(_("Failed to initialize security drivers"));
+ virSecurityManagerFree(mgr);
+ return -1;
+}
- VIR_DEBUG("Received EOF on %p '%s'", vm, vm->def->name);
- virDomainObjLock(vm);
+static virCapsPtr
+qemuCreateCapabilities(virCapsPtr oldcaps,
+ struct qemud_driver *driver)
+{
+ virCapsPtr caps;
- if (!virDomainObjIsActive(vm)) {
- VIR_DEBUG("Domain %p is not active, ignoring EOF", vm);
- virDomainObjUnlock(vm);
- return;
+ /* Basic host arch / guest machine capabilities */
+ if (!(caps = qemuCapsInit(oldcaps))) {
+ virReportOOMError();
+ return NULL;
}
- priv = vm->privateData;
- if (!hasError && priv->monJSON && !priv->gotShutdown) {
- VIR_DEBUG("Monitor connection to '%s' closed without SHUTDOWN event;
"
- "assuming the domain crashed", vm->def->name);
- hasError = 1;
+ if (driver->allowDiskFormatProbing) {
+ caps->defaultDiskDriverName = NULL;
+ caps->defaultDiskDriverType = NULL;
+ } else {
+ caps->defaultDiskDriverName = "qemu";
+ caps->defaultDiskDriverType = "raw";
}
- event = virDomainEventNewFromObj(vm,
- VIR_DOMAIN_EVENT_STOPPED,
- hasError ?
- VIR_DOMAIN_EVENT_STOPPED_FAILED :
- VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
-
- qemudShutdownVMDaemon(driver, vm, 0);
- qemuDomainStopAudit(vm, hasError ? "failed" : "shutdown");
-
- if (!vm->persistent)
- virDomainRemoveInactive(&driver->domains, vm);
- else
- virDomainObjUnlock(vm);
+ qemuDomainSetPrivateDataHooks(caps);
+ qemuDomainSetNamespaceHooks(caps);
- if (event) {
- qemuDriverLock(driver);
- qemuDomainEventQueue(driver, event);
- qemuDriverUnlock(driver);
+ if (virGetHostUUID(caps->host.host_uuid)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("cannot get the host uuid"));
+ goto err_exit;
}
-}
-
-
-static virDomainDiskDefPtr
-findDomainDiskByPath(virDomainObjPtr vm,
- const char *path)
-{
- int i;
- for (i = 0; i < vm->def->ndisks; i++) {
- virDomainDiskDefPtr disk;
+ /* Security driver data */
+ const char *doi, *model;
- disk = vm->def->disks[i];
- if (disk->src != NULL && STREQ(disk->src, path))
- return disk;
+ doi = virSecurityManagerGetDOI(driver->securityManager);
+ model = virSecurityManagerGetModel(driver->securityManager);
+ if (STRNEQ(model, "none")) {
+ if (!(caps->host.secModel.model = strdup(model)))
+ goto no_memory;
+ if (!(caps->host.secModel.doi = strdup(doi)))
+ goto no_memory;
}
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("no disk found with path %s"),
- path);
- return NULL;
-}
-
-static virDomainDiskDefPtr
-findDomainDiskByAlias(virDomainObjPtr vm,
- const char *alias)
-{
- int i;
-
- if (STRPREFIX(alias, QEMU_DRIVE_HOST_PREFIX))
- alias += strlen(QEMU_DRIVE_HOST_PREFIX);
-
- for (i = 0; i < vm->def->ndisks; i++) {
- virDomainDiskDefPtr disk;
+ VIR_DEBUG("Initialized caps for security driver \"%s\" with "
+ "DOI \"%s\"", model, doi);
- disk = vm->def->disks[i];
- if (disk->info.alias != NULL && STREQ(disk->info.alias, alias))
- return disk;
- }
+ return caps;
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("no disk found with alias %s"),
- alias);
+no_memory:
+ virReportOOMError();
+err_exit:
+ virCapabilitiesFree(caps);
return NULL;
}
-static int
-getVolumeQcowPassphrase(virConnectPtr conn,
- virDomainDiskDefPtr disk,
- char **secretRet,
- size_t *secretLen)
+static void qemuDomainSnapshotLoad(void *payload,
+ const char *name ATTRIBUTE_UNUSED,
+ void *data)
{
- virSecretPtr secret;
- char *passphrase;
- unsigned char *data;
- size_t size;
- int ret = -1;
- virStorageEncryptionPtr enc;
-
- if (!disk->encryption) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("disk %s does not have any encryption information"),
- disk->src);
- return -1;
- }
- enc = disk->encryption;
-
- if (!conn) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("cannot find secrets without a
connection"));
- goto cleanup;
- }
-
- if (conn->secretDriver == NULL ||
- conn->secretDriver->lookupByUUID == NULL ||
- conn->secretDriver->getValue == NULL) {
- qemuReportError(VIR_ERR_NO_SUPPORT, "%s",
- _("secret storage not supported"));
- goto cleanup;
- }
+ virDomainObjPtr vm = (virDomainObjPtr)payload;
+ char *baseDir = (char *)data;
+ char *snapDir = NULL;
+ DIR *dir = NULL;
+ struct dirent *entry;
+ char *xmlStr;
+ int ret;
+ char *fullpath;
+ virDomainSnapshotDefPtr def = NULL;
+ char ebuf[1024];
- if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW ||
- enc->nsecrets != 1 ||
- enc->secrets[0]->type !=
- VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) {
- qemuReportError(VIR_ERR_XML_ERROR,
- _("invalid <encryption> for volume %s"),
disk->src);
+ virDomainObjLock(vm);
+ if (virAsprintf(&snapDir, "%s/%s", baseDir, vm->def->name) <
0) {
+ VIR_ERROR(_("Failed to allocate memory for snapshot directory for domain
%s"),
+ vm->def->name);
goto cleanup;
}
- secret = conn->secretDriver->lookupByUUID(conn,
- enc->secrets[0]->uuid);
- if (secret == NULL)
- goto cleanup;
- data = conn->secretDriver->getValue(secret, &size,
- VIR_SECRET_GET_VALUE_INTERNAL_CALL);
- virUnrefSecret(secret);
- if (data == NULL)
- goto cleanup;
+ VIR_INFO("Scanning for snapshots for domain %s in %s",
vm->def->name,
+ snapDir);
- if (memchr(data, '\0', size) != NULL) {
- memset(data, 0, size);
- VIR_FREE(data);
- qemuReportError(VIR_ERR_XML_ERROR,
- _("format='qcow' passphrase for %s must not contain
a "
- "'\\0'"), disk->src);
+ if (!(dir = opendir(snapDir))) {
+ if (errno != ENOENT)
+ VIR_ERROR(_("Failed to open snapshot directory %s for domain %s:
%s"),
+ snapDir, vm->def->name,
+ virStrerror(errno, ebuf, sizeof(ebuf)));
goto cleanup;
}
- if (VIR_ALLOC_N(passphrase, size + 1) < 0) {
- memset(data, 0, size);
- VIR_FREE(data);
- virReportOOMError();
- goto cleanup;
- }
- memcpy(passphrase, data, size);
- passphrase[size] = '\0';
+ while ((entry = readdir(dir))) {
+ if (entry->d_name[0] == '.')
+ continue;
- memset(data, 0, size);
- VIR_FREE(data);
+ /* NB: ignoring errors, so one malformed config doesn't
+ kill the whole process */
+ VIR_INFO("Loading snapshot file '%s'", entry->d_name);
- *secretRet = passphrase;
- *secretLen = size;
+ if (virAsprintf(&fullpath, "%s/%s", snapDir, entry->d_name) <
0) {
+ VIR_ERROR0(_("Failed to allocate memory for path"));
+ continue;
+ }
- ret = 0;
+ ret = virFileReadAll(fullpath, 1024*1024*1, &xmlStr);
+ if (ret < 0) {
+ /* Nothing we can do here, skip this one */
+ VIR_ERROR(_("Failed to read snapshot file %s: %s"), fullpath,
+ virStrerror(errno, ebuf, sizeof(ebuf)));
+ VIR_FREE(fullpath);
+ continue;
+ }
-cleanup:
- return ret;
-}
+ def = virDomainSnapshotDefParseString(xmlStr, 0);
+ if (def == NULL) {
+ /* Nothing we can do here, skip this one */
+ VIR_ERROR(_("Failed to parse snapshot XML from file '%s'"),
fullpath);
+ VIR_FREE(fullpath);
+ VIR_FREE(xmlStr);
+ continue;
+ }
-static int
-findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
- virConnectPtr conn,
- virDomainObjPtr vm,
- const char *path,
- char **secretRet,
- size_t *secretLen)
-{
- virDomainDiskDefPtr disk;
- int ret = -1;
+ virDomainSnapshotAssignDef(&vm->snapshots, def);
- virDomainObjLock(vm);
- disk = findDomainDiskByPath(vm, path);
+ VIR_FREE(fullpath);
+ VIR_FREE(xmlStr);
+ }
- if (!disk)
- goto cleanup;
+ /* FIXME: qemu keeps internal track of snapshots. We can get access
+ * to this info via the "info snapshots" monitor command for running
+ * domains, or via "qemu-img snapshot -l" for shutoff domains. It would
+ * be nice to update our internal state based on that, but there is a
+ * a problem. qemu doesn't track all of the same metadata that we do.
+ * In particular we wouldn't be able to fill in the <parent>, which is
+ * pretty important in our metadata.
+ */
- ret = getVolumeQcowPassphrase(conn, disk, secretRet, secretLen);
+ virResetLastError();
cleanup:
+ if (dir)
+ closedir(dir);
+ VIR_FREE(snapDir);
virDomainObjUnlock(vm);
- return ret;
}
-
+/**
+ * qemudStartup:
+ *
+ * Initialization function for the QEmu daemon
+ */
static int
-qemuHandleDomainReset(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
- virDomainObjPtr vm)
-{
- struct qemud_driver *driver = qemu_driver;
- virDomainEventPtr event;
+qemudStartup(int privileged) {
+ char *base = NULL;
+ char *driverConf = NULL;
+ int rc;
+ virConnectPtr conn = NULL;
- virDomainObjLock(vm);
- event = virDomainEventRebootNewFromObj(vm);
- virDomainObjUnlock(vm);
+ if (VIR_ALLOC(qemu_driver) < 0)
+ return -1;
- if (event) {
- qemuDriverLock(driver);
- qemuDomainEventQueue(driver, event);
- qemuDriverUnlock(driver);
+ if (virMutexInit(&qemu_driver->lock) < 0) {
+ VIR_ERROR0(_("cannot initialize mutex"));
+ VIR_FREE(qemu_driver);
+ return -1;
}
+ qemuDriverLock(qemu_driver);
+ qemu_driver->privileged = privileged;
- return 0;
-}
-
-
-static int
-qemuHandleDomainShutdown(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
- virDomainObjPtr vm)
-{
- virDomainObjLock(vm);
- ((qemuDomainObjPrivatePtr) vm->privateData)->gotShutdown = true;
- virDomainObjUnlock(vm);
-
- return 0;
-}
-
+ /* Don't have a dom0 so start from 1 */
+ qemu_driver->nextvmid = 1;
-static int
-qemuHandleDomainStop(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
- virDomainObjPtr vm)
-{
- struct qemud_driver *driver = qemu_driver;
- virDomainEventPtr event = NULL;
+ if (virDomainObjListInit(&qemu_driver->domains) < 0)
+ goto out_of_memory;
- virDomainObjLock(vm);
- if (vm->state == VIR_DOMAIN_RUNNING) {
- VIR_DEBUG("Transitioned guest %s to paused state due to unknown event",
vm->def->name);
+ /* Init callback list */
+ if (VIR_ALLOC(qemu_driver->domainEventCallbacks) < 0)
+ goto out_of_memory;
+ if (!(qemu_driver->domainEventQueue = virDomainEventQueueNew()))
+ goto out_of_memory;
- vm->state = VIR_DOMAIN_PAUSED;
- event = virDomainEventNewFromObj(vm,
- VIR_DOMAIN_EVENT_SUSPENDED,
- VIR_DOMAIN_EVENT_SUSPENDED_PAUSED);
+ if ((qemu_driver->domainEventTimer =
+ virEventAddTimeout(-1, qemuDomainEventFlush, qemu_driver, NULL)) < 0)
+ goto error;
- if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
- VIR_WARN("Unable to save status on vm %s after IO error",
vm->def->name);
- }
- virDomainObjUnlock(vm);
+ /* Allocate bitmap for vnc port reservation */
+ if ((qemu_driver->reservedVNCPorts =
+ virBitmapAlloc(QEMU_VNC_PORT_MAX - QEMU_VNC_PORT_MIN)) == NULL)
+ goto out_of_memory;
- if (event) {
- qemuDriverLock(driver);
- if (event)
- qemuDomainEventQueue(driver, event);
- qemuDriverUnlock(driver);
- }
+ /* read the host sysinfo */
+ if (privileged)
+ qemu_driver->hostsysinfo = virSysinfoRead();
- return 0;
-}
+ if (privileged) {
+ if (virAsprintf(&qemu_driver->logDir,
+ "%s/log/libvirt/qemu", LOCALSTATEDIR) == -1)
+ goto out_of_memory;
+ if ((base = strdup (SYSCONFDIR "/libvirt")) == NULL)
+ goto out_of_memory;
-static int
-qemuHandleDomainRTCChange(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
- virDomainObjPtr vm,
- long long offset)
-{
- struct qemud_driver *driver = qemu_driver;
- virDomainEventPtr event;
+ if (virAsprintf(&qemu_driver->stateDir,
+ "%s/run/libvirt/qemu", LOCALSTATEDIR) == -1)
+ goto out_of_memory;
- virDomainObjLock(vm);
- event = virDomainEventRTCChangeNewFromObj(vm, offset);
+ if (virAsprintf(&qemu_driver->libDir,
+ "%s/lib/libvirt/qemu", LOCALSTATEDIR) == -1)
+ goto out_of_memory;
- if (vm->def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_VARIABLE)
- vm->def->clock.data.adjustment = offset;
+ if (virAsprintf(&qemu_driver->cacheDir,
+ "%s/cache/libvirt/qemu", LOCALSTATEDIR) == -1)
+ goto out_of_memory;
+ if (virAsprintf(&qemu_driver->saveDir,
+ "%s/lib/libvirt/qemu/save", LOCALSTATEDIR) == -1)
+ goto out_of_memory;
+ if (virAsprintf(&qemu_driver->snapshotDir,
+ "%s/lib/libvirt/qemu/snapshot", LOCALSTATEDIR) == -1)
+ goto out_of_memory;
+ if (virAsprintf(&qemu_driver->autoDumpPath,
+ "%s/lib/libvirt/qemu/dump", LOCALSTATEDIR) == -1)
+ goto out_of_memory;
+ } else {
+ uid_t uid = geteuid();
+ char *userdir = virGetUserDirectory(uid);
+ if (!userdir)
+ goto error;
- if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
- VIR_WARN0("unable to save domain status with RTC change");
+ if (virAsprintf(&qemu_driver->logDir,
+ "%s/.libvirt/qemu/log", userdir) == -1) {
+ VIR_FREE(userdir);
+ goto out_of_memory;
+ }
- virDomainObjUnlock(vm);
+ if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) {
+ VIR_FREE(userdir);
+ goto out_of_memory;
+ }
+ VIR_FREE(userdir);
- if (event) {
- qemuDriverLock(driver);
- qemuDomainEventQueue(driver, event);
- qemuDriverUnlock(driver);
+ if (virAsprintf(&qemu_driver->stateDir, "%s/qemu/run", base) ==
-1)
+ goto out_of_memory;
+ if (virAsprintf(&qemu_driver->libDir, "%s/qemu/lib", base) ==
-1)
+ goto out_of_memory;
+ if (virAsprintf(&qemu_driver->cacheDir, "%s/qemu/cache", base)
== -1)
+ goto out_of_memory;
+ if (virAsprintf(&qemu_driver->saveDir, "%s/qemu/save", base) ==
-1)
+ goto out_of_memory;
+ if (virAsprintf(&qemu_driver->snapshotDir, "%s/qemu/snapshot",
base) == -1)
+ goto out_of_memory;
+ if (virAsprintf(&qemu_driver->autoDumpPath, "%s/qemu/dump",
base) == -1)
+ goto out_of_memory;
}
- return 0;
-}
-
-
-static int
-qemuHandleDomainWatchdog(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
- virDomainObjPtr vm,
- int action)
-{
- struct qemud_driver *driver = qemu_driver;
- virDomainEventPtr watchdogEvent = NULL;
- virDomainEventPtr lifecycleEvent = NULL;
-
- virDomainObjLock(vm);
- watchdogEvent = virDomainEventWatchdogNewFromObj(vm, action);
-
- if (action == VIR_DOMAIN_EVENT_WATCHDOG_PAUSE &&
- vm->state == VIR_DOMAIN_RUNNING) {
- VIR_DEBUG("Transitioned guest %s to paused state due to watchdog",
vm->def->name);
-
- vm->state = VIR_DOMAIN_PAUSED;
- lifecycleEvent = virDomainEventNewFromObj(vm,
- VIR_DOMAIN_EVENT_SUSPENDED,
- VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG);
-
- if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
- VIR_WARN("Unable to save status on vm %s after IO error",
vm->def->name);
+ if (virFileMakePath(qemu_driver->stateDir) != 0) {
+ char ebuf[1024];
+ VIR_ERROR(_("Failed to create state dir '%s': %s"),
+ qemu_driver->stateDir, virStrerror(errno, ebuf, sizeof ebuf));
+ goto error;
}
-
- if (vm->def->watchdog->action == VIR_DOMAIN_WATCHDOG_ACTION_DUMP) {
- struct watchdogEvent *wdEvent;
- if (VIR_ALLOC(wdEvent) == 0) {
- wdEvent->action = VIR_DOMAIN_WATCHDOG_ACTION_DUMP;
- wdEvent->vm = vm;
- ignore_value(virThreadPoolSendJob(driver->workerPool, wdEvent));
- } else
- virReportOOMError();
+ if (virFileMakePath(qemu_driver->libDir) != 0) {
+ char ebuf[1024];
+ VIR_ERROR(_("Failed to create lib dir '%s': %s"),
+ qemu_driver->libDir, virStrerror(errno, ebuf, sizeof ebuf));
+ goto error;
}
-
- virDomainObjUnlock(vm);
-
- if (watchdogEvent || lifecycleEvent) {
- qemuDriverLock(driver);
- if (watchdogEvent)
- qemuDomainEventQueue(driver, watchdogEvent);
- if (lifecycleEvent)
- qemuDomainEventQueue(driver, lifecycleEvent);
- qemuDriverUnlock(driver);
+ if (virFileMakePath(qemu_driver->cacheDir) != 0) {
+ char ebuf[1024];
+ VIR_ERROR(_("Failed to create cache dir '%s': %s"),
+ qemu_driver->cacheDir, virStrerror(errno, ebuf, sizeof ebuf));
+ goto error;
}
-
- return 0;
-}
-
-
-static int
-qemuHandleDomainIOError(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
- virDomainObjPtr vm,
- const char *diskAlias,
- int action,
- const char *reason)
-{
- struct qemud_driver *driver = qemu_driver;
- virDomainEventPtr ioErrorEvent = NULL;
- virDomainEventPtr ioErrorEvent2 = NULL;
- virDomainEventPtr lifecycleEvent = NULL;
- const char *srcPath;
- const char *devAlias;
- virDomainDiskDefPtr disk;
-
- virDomainObjLock(vm);
- disk = findDomainDiskByAlias(vm, diskAlias);
-
- if (disk) {
- srcPath = disk->src;
- devAlias = disk->info.alias;
- } else {
- srcPath = "";
- devAlias = "";
+ if (virFileMakePath(qemu_driver->saveDir) != 0) {
+ char ebuf[1024];
+ VIR_ERROR(_("Failed to create save dir '%s': %s"),
+ qemu_driver->saveDir, virStrerror(errno, ebuf, sizeof ebuf));
+ goto error;
+ }
+ if (virFileMakePath(qemu_driver->snapshotDir) != 0) {
+ char ebuf[1024];
+ VIR_ERROR(_("Failed to create save dir '%s': %s"),
+ qemu_driver->snapshotDir, virStrerror(errno, ebuf, sizeof ebuf));
+ goto error;
+ }
+ if (virFileMakePath(qemu_driver->autoDumpPath) != 0) {
+ char ebuf[1024];
+ VIR_ERROR(_("Failed to create dump dir '%s': %s"),
+ qemu_driver->autoDumpPath, virStrerror(errno, ebuf, sizeof ebuf));
+ goto error;
}
- ioErrorEvent = virDomainEventIOErrorNewFromObj(vm, srcPath, devAlias, action);
- ioErrorEvent2 = virDomainEventIOErrorReasonNewFromObj(vm, srcPath, devAlias, action,
reason);
-
- if (action == VIR_DOMAIN_EVENT_IO_ERROR_PAUSE &&
- vm->state == VIR_DOMAIN_RUNNING) {
- VIR_DEBUG("Transitioned guest %s to paused state due to IO error",
vm->def->name);
+ /* Configuration paths are either ~/.libvirt/qemu/... (session) or
+ * /etc/libvirt/qemu/... (system).
+ */
+ if (virAsprintf(&driverConf, "%s/qemu.conf", base) < 0 ||
+ virAsprintf(&qemu_driver->configDir, "%s/qemu", base) < 0 ||
+ virAsprintf(&qemu_driver->autostartDir, "%s/qemu/autostart",
base) < 0)
+ goto out_of_memory;
- vm->state = VIR_DOMAIN_PAUSED;
- lifecycleEvent = virDomainEventNewFromObj(vm,
- VIR_DOMAIN_EVENT_SUSPENDED,
- VIR_DOMAIN_EVENT_SUSPENDED_IOERROR);
+ VIR_FREE(base);
- if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
- VIR_WARN("Unable to save status on vm %s after IO error",
vm->def->name);
+ rc = virCgroupForDriver("qemu", &qemu_driver->cgroup, privileged,
1);
+ if (rc < 0) {
+ char buf[1024];
+ VIR_WARN("Unable to create cgroup for driver: %s",
+ virStrerror(-rc, buf, sizeof(buf)));
}
- virDomainObjUnlock(vm);
- if (ioErrorEvent || ioErrorEvent2 || lifecycleEvent) {
- qemuDriverLock(driver);
- if (ioErrorEvent)
- qemuDomainEventQueue(driver, ioErrorEvent);
- if (ioErrorEvent2)
- qemuDomainEventQueue(driver, ioErrorEvent2);
- if (lifecycleEvent)
- qemuDomainEventQueue(driver, lifecycleEvent);
- qemuDriverUnlock(driver);
+ if (qemudLoadDriverConfig(qemu_driver, driverConf) < 0) {
+ goto error;
}
+ VIR_FREE(driverConf);
- return 0;
-}
+ if (qemuSecurityInit(qemu_driver) < 0)
+ goto error;
-
-static int
-qemuHandleDomainGraphics(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
- virDomainObjPtr vm,
- int phase,
- int localFamily,
- const char *localNode,
- const char *localService,
- int remoteFamily,
- const char *remoteNode,
- const char *remoteService,
- const char *authScheme,
- const char *x509dname,
- const char *saslUsername)
-{
- struct qemud_driver *driver = qemu_driver;
- virDomainEventPtr event;
- virDomainEventGraphicsAddressPtr localAddr = NULL;
- virDomainEventGraphicsAddressPtr remoteAddr = NULL;
- virDomainEventGraphicsSubjectPtr subject = NULL;
- int i;
-
- virDomainObjLock(vm);
-
- if (VIR_ALLOC(localAddr) < 0)
- goto no_memory;
- localAddr->family = localFamily;
- if (!(localAddr->service = strdup(localService)) ||
- !(localAddr->node = strdup(localNode)))
- goto no_memory;
-
- if (VIR_ALLOC(remoteAddr) < 0)
- goto no_memory;
- remoteAddr->family = remoteFamily;
- if (!(remoteAddr->service = strdup(remoteService)) ||
- !(remoteAddr->node = strdup(remoteNode)))
- goto no_memory;
-
- if (VIR_ALLOC(subject) < 0)
- goto no_memory;
- if (x509dname) {
- if (VIR_REALLOC_N(subject->identities, subject->nidentity+1) < 0)
- goto no_memory;
- if (!(subject->identities[subject->nidentity].type =
strdup("x509dname")) ||
- !(subject->identities[subject->nidentity].name = strdup(x509dname)))
- goto no_memory;
- subject->nidentity++;
- }
- if (saslUsername) {
- if (VIR_REALLOC_N(subject->identities, subject->nidentity+1) < 0)
- goto no_memory;
- if (!(subject->identities[subject->nidentity].type =
strdup("saslUsername")) ||
- !(subject->identities[subject->nidentity].name =
strdup(saslUsername)))
- goto no_memory;
- subject->nidentity++;
- }
-
- event = virDomainEventGraphicsNewFromObj(vm, phase, localAddr, remoteAddr,
authScheme, subject);
- virDomainObjUnlock(vm);
-
- if (event) {
- qemuDriverLock(driver);
- qemuDomainEventQueue(driver, event);
- qemuDriverUnlock(driver);
- }
-
- return 0;
-
-no_memory:
- virReportOOMError();
- if (localAddr) {
- VIR_FREE(localAddr->service);
- VIR_FREE(localAddr->node);
- VIR_FREE(localAddr);
- }
- if (remoteAddr) {
- VIR_FREE(remoteAddr->service);
- VIR_FREE(remoteAddr->node);
- VIR_FREE(remoteAddr);
- }
- if (subject) {
- for (i = 0 ; i < subject->nidentity ; i++) {
- VIR_FREE(subject->identities[i].type);
- VIR_FREE(subject->identities[i].name);
- }
- VIR_FREE(subject->identities);
- VIR_FREE(subject);
- }
-
- return -1;
-}
-
-
-static void qemuHandleMonitorDestroy(qemuMonitorPtr mon,
- virDomainObjPtr vm)
-{
- qemuDomainObjPrivatePtr priv = vm->privateData;
- if (priv->mon == mon)
- priv->mon = NULL;
- virDomainObjUnref(vm);
-}
-
-static qemuMonitorCallbacks monitorCallbacks = {
- .destroy = qemuHandleMonitorDestroy,
- .eofNotify = qemuHandleMonitorEOF,
- .diskSecretLookup = findVolumeQcowPassphrase,
- .domainShutdown = qemuHandleDomainShutdown,
- .domainStop = qemuHandleDomainStop,
- .domainReset = qemuHandleDomainReset,
- .domainRTCChange = qemuHandleDomainRTCChange,
- .domainWatchdog = qemuHandleDomainWatchdog,
- .domainIOError = qemuHandleDomainIOError,
- .domainGraphics = qemuHandleDomainGraphics,
-};
-
-static int
-qemuConnectMonitor(struct qemud_driver *driver, virDomainObjPtr vm)
-{
- qemuDomainObjPrivatePtr priv = vm->privateData;
- int ret = -1;
-
- if (virSecurityManagerSetSocketLabel(driver->securityManager, vm) < 0) {
- VIR_ERROR(_("Failed to set security context for monitor for %s"),
- vm->def->name);
- goto error;
- }
-
- /* Hold an extra reference because we can't allow 'vm' to be
- * deleted while the monitor is active */
- virDomainObjRef(vm);
-
- priv->mon = qemuMonitorOpen(vm,
- priv->monConfig,
- priv->monJSON,
- &monitorCallbacks);
-
- if (priv->mon == NULL)
- virDomainObjUnref(vm);
-
- if (virSecurityManagerClearSocketLabel(driver->securityManager, vm) < 0) {
- VIR_ERROR(_("Failed to clear security context for monitor for %s"),
- vm->def->name);
- goto error;
- }
-
- if (priv->mon == NULL) {
- VIR_INFO("Failed to connect monitor for %s", vm->def->name);
- goto error;
- }
-
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- ret = qemuMonitorSetCapabilities(priv->mon);
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
-error:
-
- return ret;
-}
-
-struct virReconnectDomainData {
- virConnectPtr conn;
- struct qemud_driver *driver;
-};
-/*
- * Open an existing VM's monitor, re-detect VCPU threads
- * and re-reserve the security labels in use
- */
-static void
-qemuReconnectDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque)
-{
- virDomainObjPtr obj = payload;
- struct virReconnectDomainData *data = opaque;
- struct qemud_driver *driver = data->driver;
- qemuDomainObjPrivatePtr priv;
- unsigned long long qemuCmdFlags;
- virConnectPtr conn = data->conn;
-
- virDomainObjLock(obj);
-
- VIR_DEBUG("Reconnect monitor to %p '%s'", obj,
obj->def->name);
-
- priv = obj->privateData;
-
- /* Hold an extra reference because we can't allow 'vm' to be
- * deleted if qemuConnectMonitor() failed */
- virDomainObjRef(obj);
-
- /* XXX check PID liveliness & EXE path */
- if (qemuConnectMonitor(driver, obj) < 0)
- goto error;
-
- if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) {
- goto error;
- }
-
- /* XXX we should be persisting the original flags in the XML
- * not re-detecting them, since the binary may have changed
- * since launch time */
- if (qemuCapsExtractVersionInfo(obj->def->emulator,
- NULL,
- &qemuCmdFlags) >= 0 &&
- (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
- priv->persistentAddrs = 1;
-
- if (!(priv->pciaddrs = qemuDomainPCIAddressSetCreate(obj->def)) ||
- qemuAssignDevicePCISlots(obj->def, priv->pciaddrs) < 0)
- goto error;
- }
-
- if (virSecurityManagerReserveLabel(driver->securityManager, obj) < 0)
- goto error;
-
- if (qemudVMFiltersInstantiate(conn, obj->def))
- goto error;
-
- if (obj->def->id >= driver->nextvmid)
- driver->nextvmid = obj->def->id + 1;
-
- if (virDomainObjUnref(obj) > 0)
- virDomainObjUnlock(obj);
- return;
-
-error:
- if (!virDomainObjIsActive(obj)) {
- if (virDomainObjUnref(obj) > 0)
- virDomainObjUnlock(obj);
- return;
- }
-
- if (virDomainObjUnref(obj) > 0) {
- /* We can't get the monitor back, so must kill the VM
- * to remove danger of it ending up running twice if
- * user tries to start it again later */
- qemudShutdownVMDaemon(driver, obj, 0);
- if (!obj->persistent)
- virDomainRemoveInactive(&driver->domains, obj);
- else
- virDomainObjUnlock(obj);
- }
-}
-
-/**
- * qemudReconnectDomains
- *
- * Try to re-open the resources for live VMs that we care
- * about.
- */
-static void
-qemuReconnectDomains(virConnectPtr conn, struct qemud_driver *driver)
-{
- struct virReconnectDomainData data = {conn, driver};
- virHashForEach(driver->domains.objs, qemuReconnectDomain, &data);
-}
-
-
-static int
-qemuSecurityInit(struct qemud_driver *driver)
-{
- virSecurityManagerPtr mgr = virSecurityManagerNew(driver->securityDriverName,
-
driver->allowDiskFormatProbing);
- if (!mgr)
- goto error;
-
- if (driver->privileged) {
- virSecurityManagerPtr dac = virSecurityManagerNewDAC(driver->user,
- driver->group,
-
driver->allowDiskFormatProbing,
-
driver->dynamicOwnership);
- if (!dac)
- goto error;
-
- if (!(driver->securityManager = virSecurityManagerNewStack(mgr,
- dac)))
- goto error;
- } else {
- driver->securityManager = mgr;
- }
-
- return 0;
-
-error:
- VIR_ERROR0(_("Failed to initialize security drivers"));
- virSecurityManagerFree(mgr);
- return -1;
-}
-
-
-static virCapsPtr
-qemuCreateCapabilities(virCapsPtr oldcaps,
- struct qemud_driver *driver)
-{
- virCapsPtr caps;
-
- /* Basic host arch / guest machine capabilities */
- if (!(caps = qemuCapsInit(oldcaps))) {
- virReportOOMError();
- return NULL;
- }
-
- if (driver->allowDiskFormatProbing) {
- caps->defaultDiskDriverName = NULL;
- caps->defaultDiskDriverType = NULL;
- } else {
- caps->defaultDiskDriverName = "qemu";
- caps->defaultDiskDriverType = "raw";
- }
-
- qemuDomainSetPrivateDataHooks(caps);
- qemuDomainSetNamespaceHooks(caps);
-
- if (virGetHostUUID(caps->host.host_uuid)) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("cannot get the host uuid"));
- goto err_exit;
- }
-
- /* Security driver data */
- const char *doi, *model;
-
- doi = virSecurityManagerGetDOI(driver->securityManager);
- model = virSecurityManagerGetModel(driver->securityManager);
- if (STRNEQ(model, "none")) {
- if (!(caps->host.secModel.model = strdup(model)))
- goto no_memory;
- if (!(caps->host.secModel.doi = strdup(doi)))
- goto no_memory;
- }
-
- VIR_DEBUG("Initialized caps for security driver \"%s\" with "
- "DOI \"%s\"", model, doi);
-
- return caps;
-
-no_memory:
- virReportOOMError();
-err_exit:
- virCapabilitiesFree(caps);
- return NULL;
-}
-
-static void qemuDomainSnapshotLoad(void *payload,
- const char *name ATTRIBUTE_UNUSED,
- void *data)
-{
- virDomainObjPtr vm = (virDomainObjPtr)payload;
- char *baseDir = (char *)data;
- char *snapDir = NULL;
- DIR *dir = NULL;
- struct dirent *entry;
- char *xmlStr;
- int ret;
- char *fullpath;
- virDomainSnapshotDefPtr def = NULL;
- char ebuf[1024];
-
- virDomainObjLock(vm);
- if (virAsprintf(&snapDir, "%s/%s", baseDir, vm->def->name) <
0) {
- VIR_ERROR(_("Failed to allocate memory for snapshot directory for domain
%s"),
- vm->def->name);
- goto cleanup;
- }
-
- VIR_INFO("Scanning for snapshots for domain %s in %s",
vm->def->name,
- snapDir);
-
- if (!(dir = opendir(snapDir))) {
- if (errno != ENOENT)
- VIR_ERROR(_("Failed to open snapshot directory %s for domain %s:
%s"),
- snapDir, vm->def->name,
- virStrerror(errno, ebuf, sizeof(ebuf)));
- goto cleanup;
- }
-
- while ((entry = readdir(dir))) {
- if (entry->d_name[0] == '.')
- continue;
-
- /* NB: ignoring errors, so one malformed config doesn't
- kill the whole process */
- VIR_INFO("Loading snapshot file '%s'", entry->d_name);
-
- if (virAsprintf(&fullpath, "%s/%s", snapDir, entry->d_name) <
0) {
- VIR_ERROR0(_("Failed to allocate memory for path"));
- continue;
- }
-
- ret = virFileReadAll(fullpath, 1024*1024*1, &xmlStr);
- if (ret < 0) {
- /* Nothing we can do here, skip this one */
- VIR_ERROR(_("Failed to read snapshot file %s: %s"), fullpath,
- virStrerror(errno, ebuf, sizeof(ebuf)));
- VIR_FREE(fullpath);
- continue;
- }
-
- def = virDomainSnapshotDefParseString(xmlStr, 0);
- if (def == NULL) {
- /* Nothing we can do here, skip this one */
- VIR_ERROR(_("Failed to parse snapshot XML from file '%s'"),
fullpath);
- VIR_FREE(fullpath);
- VIR_FREE(xmlStr);
- continue;
- }
-
- virDomainSnapshotAssignDef(&vm->snapshots, def);
-
- VIR_FREE(fullpath);
- VIR_FREE(xmlStr);
- }
-
- /* FIXME: qemu keeps internal track of snapshots. We can get access
- * to this info via the "info snapshots" monitor command for running
- * domains, or via "qemu-img snapshot -l" for shutoff domains. It would
- * be nice to update our internal state based on that, but there is a
- * a problem. qemu doesn't track all of the same metadata that we do.
- * In particular we wouldn't be able to fill in the <parent>, which is
- * pretty important in our metadata.
- */
-
- virResetLastError();
-
-cleanup:
- if (dir)
- closedir(dir);
- VIR_FREE(snapDir);
- virDomainObjUnlock(vm);
-}
-
-/**
- * qemudStartup:
- *
- * Initialization function for the QEmu daemon
- */
-static int
-qemudStartup(int privileged) {
- char *base = NULL;
- char *driverConf = NULL;
- int rc;
- virConnectPtr conn = NULL;
-
- if (VIR_ALLOC(qemu_driver) < 0)
- return -1;
-
- if (virMutexInit(&qemu_driver->lock) < 0) {
- VIR_ERROR0(_("cannot initialize mutex"));
- VIR_FREE(qemu_driver);
- return -1;
- }
- qemuDriverLock(qemu_driver);
- qemu_driver->privileged = privileged;
-
- /* Don't have a dom0 so start from 1 */
- qemu_driver->nextvmid = 1;
-
- if (virDomainObjListInit(&qemu_driver->domains) < 0)
- goto out_of_memory;
-
- /* Init callback list */
- if (VIR_ALLOC(qemu_driver->domainEventCallbacks) < 0)
- goto out_of_memory;
- if (!(qemu_driver->domainEventQueue = virDomainEventQueueNew()))
- goto out_of_memory;
-
- if ((qemu_driver->domainEventTimer =
- virEventAddTimeout(-1, qemuDomainEventFlush, qemu_driver, NULL)) < 0)
- goto error;
-
- /* Allocate bitmap for vnc port reservation */
- if ((qemu_driver->reservedVNCPorts =
- virBitmapAlloc(QEMU_VNC_PORT_MAX - QEMU_VNC_PORT_MIN)) == NULL)
- goto out_of_memory;
-
- /* read the host sysinfo */
- if (privileged)
- qemu_driver->hostsysinfo = virSysinfoRead();
-
- if (privileged) {
- if (virAsprintf(&qemu_driver->logDir,
- "%s/log/libvirt/qemu", LOCALSTATEDIR) == -1)
- goto out_of_memory;
-
- if ((base = strdup (SYSCONFDIR "/libvirt")) == NULL)
- goto out_of_memory;
-
- if (virAsprintf(&qemu_driver->stateDir,
- "%s/run/libvirt/qemu", LOCALSTATEDIR) == -1)
- goto out_of_memory;
-
- if (virAsprintf(&qemu_driver->libDir,
- "%s/lib/libvirt/qemu", LOCALSTATEDIR) == -1)
- goto out_of_memory;
-
- if (virAsprintf(&qemu_driver->cacheDir,
- "%s/cache/libvirt/qemu", LOCALSTATEDIR) == -1)
- goto out_of_memory;
- if (virAsprintf(&qemu_driver->saveDir,
- "%s/lib/libvirt/qemu/save", LOCALSTATEDIR) == -1)
- goto out_of_memory;
- if (virAsprintf(&qemu_driver->snapshotDir,
- "%s/lib/libvirt/qemu/snapshot", LOCALSTATEDIR) == -1)
- goto out_of_memory;
- if (virAsprintf(&qemu_driver->autoDumpPath,
- "%s/lib/libvirt/qemu/dump", LOCALSTATEDIR) == -1)
- goto out_of_memory;
- } else {
- uid_t uid = geteuid();
- char *userdir = virGetUserDirectory(uid);
- if (!userdir)
- goto error;
-
- if (virAsprintf(&qemu_driver->logDir,
- "%s/.libvirt/qemu/log", userdir) == -1) {
- VIR_FREE(userdir);
- goto out_of_memory;
- }
-
- if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) {
- VIR_FREE(userdir);
- goto out_of_memory;
- }
- VIR_FREE(userdir);
-
- if (virAsprintf(&qemu_driver->stateDir, "%s/qemu/run", base) ==
-1)
- goto out_of_memory;
- if (virAsprintf(&qemu_driver->libDir, "%s/qemu/lib", base) ==
-1)
- goto out_of_memory;
- if (virAsprintf(&qemu_driver->cacheDir, "%s/qemu/cache", base)
== -1)
- goto out_of_memory;
- if (virAsprintf(&qemu_driver->saveDir, "%s/qemu/save", base) ==
-1)
- goto out_of_memory;
- if (virAsprintf(&qemu_driver->snapshotDir, "%s/qemu/snapshot",
base) == -1)
- goto out_of_memory;
- if (virAsprintf(&qemu_driver->autoDumpPath, "%s/qemu/dump",
base) == -1)
- goto out_of_memory;
- }
-
- if (virFileMakePath(qemu_driver->stateDir) != 0) {
- char ebuf[1024];
- VIR_ERROR(_("Failed to create state dir '%s': %s"),
- qemu_driver->stateDir, virStrerror(errno, ebuf, sizeof ebuf));
- goto error;
- }
- if (virFileMakePath(qemu_driver->libDir) != 0) {
- char ebuf[1024];
- VIR_ERROR(_("Failed to create lib dir '%s': %s"),
- qemu_driver->libDir, virStrerror(errno, ebuf, sizeof ebuf));
- goto error;
- }
- if (virFileMakePath(qemu_driver->cacheDir) != 0) {
- char ebuf[1024];
- VIR_ERROR(_("Failed to create cache dir '%s': %s"),
- qemu_driver->cacheDir, virStrerror(errno, ebuf, sizeof ebuf));
- goto error;
- }
- if (virFileMakePath(qemu_driver->saveDir) != 0) {
- char ebuf[1024];
- VIR_ERROR(_("Failed to create save dir '%s': %s"),
- qemu_driver->saveDir, virStrerror(errno, ebuf, sizeof ebuf));
- goto error;
- }
- if (virFileMakePath(qemu_driver->snapshotDir) != 0) {
- char ebuf[1024];
- VIR_ERROR(_("Failed to create save dir '%s': %s"),
- qemu_driver->snapshotDir, virStrerror(errno, ebuf, sizeof ebuf));
- goto error;
- }
- if (virFileMakePath(qemu_driver->autoDumpPath) != 0) {
- char ebuf[1024];
- VIR_ERROR(_("Failed to create dump dir '%s': %s"),
- qemu_driver->autoDumpPath, virStrerror(errno, ebuf, sizeof ebuf));
- goto error;
- }
-
- /* Configuration paths are either ~/.libvirt/qemu/... (session) or
- * /etc/libvirt/qemu/... (system).
- */
- if (virAsprintf(&driverConf, "%s/qemu.conf", base) < 0 ||
- virAsprintf(&qemu_driver->configDir, "%s/qemu", base) < 0 ||
- virAsprintf(&qemu_driver->autostartDir, "%s/qemu/autostart",
base) < 0)
- goto out_of_memory;
-
- VIR_FREE(base);
-
- rc = virCgroupForDriver("qemu", &qemu_driver->cgroup, privileged,
1);
- if (rc < 0) {
- char buf[1024];
- VIR_WARN("Unable to create cgroup for driver: %s",
- virStrerror(-rc, buf, sizeof(buf)));
- }
-
- if (qemudLoadDriverConfig(qemu_driver, driverConf) < 0) {
- goto error;
- }
- VIR_FREE(driverConf);
-
- if (qemuSecurityInit(qemu_driver) < 0)
- goto error;
-
- if ((qemu_driver->caps = qemuCreateCapabilities(NULL,
- qemu_driver)) == NULL)
+ if ((qemu_driver->caps = qemuCreateCapabilities(NULL,
+ qemu_driver)) == NULL)
goto error;
if ((qemu_driver->activePciHostdevs = pciDeviceListNew()) == NULL)
- goto error;
-
- if (privileged) {
- if (chown(qemu_driver->libDir, qemu_driver->user, qemu_driver->group)
< 0) {
- virReportSystemError(errno,
- _("unable to set ownership of '%s' to user
%d:%d"),
- qemu_driver->libDir, qemu_driver->user,
qemu_driver->group);
- goto error;
- }
- if (chown(qemu_driver->cacheDir, qemu_driver->user, qemu_driver->group)
< 0) {
- virReportSystemError(errno,
- _("unable to set ownership of '%s' to
%d:%d"),
- qemu_driver->cacheDir, qemu_driver->user,
qemu_driver->group);
- goto error;
- }
- if (chown(qemu_driver->saveDir, qemu_driver->user, qemu_driver->group)
< 0) {
- virReportSystemError(errno,
- _("unable to set ownership of '%s' to
%d:%d"),
- qemu_driver->saveDir, qemu_driver->user,
qemu_driver->group);
- goto error;
- }
- if (chown(qemu_driver->snapshotDir, qemu_driver->user,
qemu_driver->group) < 0) {
- virReportSystemError(errno,
- _("unable to set ownership of '%s' to
%d:%d"),
- qemu_driver->snapshotDir, qemu_driver->user,
qemu_driver->group);
- goto error;
- }
- }
-
- /* If hugetlbfs is present, then we need to create a sub-directory within
- * it, since we can't assume the root mount point has permissions that
- * will let our spawned QEMU instances use it.
- *
- * NB the check for '/', since user may config "" to disable
hugepages
- * even when mounted
- */
- if (qemu_driver->hugetlbfs_mount &&
- qemu_driver->hugetlbfs_mount[0] == '/') {
- char *mempath = NULL;
- if (virAsprintf(&mempath, "%s/libvirt/qemu",
qemu_driver->hugetlbfs_mount) < 0)
- goto out_of_memory;
-
- if ((rc = virFileMakePath(mempath)) != 0) {
- virReportSystemError(rc,
- _("unable to create hugepage path %s"),
mempath);
- VIR_FREE(mempath);
- goto error;
- }
- if (qemu_driver->privileged &&
- chown(mempath, qemu_driver->user, qemu_driver->group) < 0) {
- virReportSystemError(errno,
- _("unable to set ownership on %s to %d:%d"),
- mempath, qemu_driver->user, qemu_driver->group);
- VIR_FREE(mempath);
- goto error;
- }
-
- qemu_driver->hugepage_path = mempath;
- }
-
- /* Get all the running persistent or transient configs first */
- if (virDomainLoadAllConfigs(qemu_driver->caps,
- &qemu_driver->domains,
- qemu_driver->stateDir,
- NULL,
- 1, NULL, NULL) < 0)
- goto error;
-
- conn = virConnectOpen(qemu_driver->privileged ?
- "qemu:///system" :
- "qemu:///session");
-
- qemuReconnectDomains(conn, qemu_driver);
-
- /* Then inactive persistent configs */
- if (virDomainLoadAllConfigs(qemu_driver->caps,
- &qemu_driver->domains,
- qemu_driver->configDir,
- qemu_driver->autostartDir,
- 0, NULL, NULL) < 0)
- goto error;
-
-
- virHashForEach(qemu_driver->domains.objs, qemuDomainSnapshotLoad,
- qemu_driver->snapshotDir);
-
- qemuDriverUnlock(qemu_driver);
-
- qemudAutostartConfigs(qemu_driver);
-
- qemu_driver->workerPool = virThreadPoolNew(0, 1, processWatchdogEvent,
qemu_driver);
- if (!qemu_driver->workerPool)
- goto error;
-
- if (conn)
- virConnectClose(conn);
-
- return 0;
-
-out_of_memory:
- virReportOOMError();
-error:
- if (qemu_driver)
- qemuDriverUnlock(qemu_driver);
- if (conn)
- virConnectClose(conn);
- VIR_FREE(base);
- VIR_FREE(driverConf);
- qemudShutdown();
- return -1;
-}
-
-static void qemudNotifyLoadDomain(virDomainObjPtr vm, int newVM, void *opaque)
-{
- struct qemud_driver *driver = opaque;
-
- if (newVM) {
- virDomainEventPtr event =
- virDomainEventNewFromObj(vm,
- VIR_DOMAIN_EVENT_DEFINED,
- VIR_DOMAIN_EVENT_DEFINED_ADDED);
- if (event)
- qemuDomainEventQueue(driver, event);
- }
-}
-
-/**
- * qemudReload:
- *
- * Function to restart the QEmu daemon, it will recheck the configuration
- * files and update its state and the networking
- */
-static int
-qemudReload(void) {
- if (!qemu_driver)
- return 0;
-
- qemuDriverLock(qemu_driver);
- virDomainLoadAllConfigs(qemu_driver->caps,
- &qemu_driver->domains,
- qemu_driver->configDir,
- qemu_driver->autostartDir,
- 0, qemudNotifyLoadDomain, qemu_driver);
- qemuDriverUnlock(qemu_driver);
-
- qemudAutostartConfigs(qemu_driver);
-
- return 0;
-}
-
-/**
- * qemudActive:
- *
- * Checks if the QEmu daemon is active, i.e. has an active domain or
- * an active network
- *
- * Returns 1 if active, 0 otherwise
- */
-static int
-qemudActive(void) {
- int active = 0;
-
- if (!qemu_driver)
- return 0;
-
- /* XXX having to iterate here is not great because it requires many locks */
- qemuDriverLock(qemu_driver);
- active = virDomainObjListNumOfDomains(&qemu_driver->domains, 1);
- qemuDriverUnlock(qemu_driver);
- return active;
-}
-
-/**
- * qemudShutdown:
- *
- * Shutdown the QEmu daemon, it will stop all active domains and networks
- */
-static int
-qemudShutdown(void) {
- int i;
-
- if (!qemu_driver)
- return -1;
-
- qemuDriverLock(qemu_driver);
- pciDeviceListFree(qemu_driver->activePciHostdevs);
- virCapabilitiesFree(qemu_driver->caps);
-
- virDomainObjListDeinit(&qemu_driver->domains);
- virBitmapFree(qemu_driver->reservedVNCPorts);
-
- virSysinfoDefFree(qemu_driver->hostsysinfo);
-
- VIR_FREE(qemu_driver->configDir);
- VIR_FREE(qemu_driver->autostartDir);
- VIR_FREE(qemu_driver->logDir);
- VIR_FREE(qemu_driver->stateDir);
- VIR_FREE(qemu_driver->libDir);
- VIR_FREE(qemu_driver->cacheDir);
- VIR_FREE(qemu_driver->saveDir);
- VIR_FREE(qemu_driver->snapshotDir);
- VIR_FREE(qemu_driver->autoDumpPath);
- VIR_FREE(qemu_driver->vncTLSx509certdir);
- VIR_FREE(qemu_driver->vncListen);
- VIR_FREE(qemu_driver->vncPassword);
- VIR_FREE(qemu_driver->vncSASLdir);
- VIR_FREE(qemu_driver->spiceTLSx509certdir);
- VIR_FREE(qemu_driver->spiceListen);
- VIR_FREE(qemu_driver->spicePassword);
- VIR_FREE(qemu_driver->hugetlbfs_mount);
- VIR_FREE(qemu_driver->hugepage_path);
- VIR_FREE(qemu_driver->saveImageFormat);
- VIR_FREE(qemu_driver->dumpImageFormat);
-
- virSecurityManagerFree(qemu_driver->securityManager);
-
- ebtablesContextFree(qemu_driver->ebtables);
-
- if (qemu_driver->cgroupDeviceACL) {
- for (i = 0 ; qemu_driver->cgroupDeviceACL[i] != NULL ; i++)
- VIR_FREE(qemu_driver->cgroupDeviceACL[i]);
- VIR_FREE(qemu_driver->cgroupDeviceACL);
- }
-
- /* Free domain callback list */
- virDomainEventCallbackListFree(qemu_driver->domainEventCallbacks);
- virDomainEventQueueFree(qemu_driver->domainEventQueue);
-
- if (qemu_driver->domainEventTimer != -1)
- virEventRemoveTimeout(qemu_driver->domainEventTimer);
-
- if (qemu_driver->brctl)
- brShutdown(qemu_driver->brctl);
-
- virCgroupFree(&qemu_driver->cgroup);
-
- qemuDriverUnlock(qemu_driver);
- virMutexDestroy(&qemu_driver->lock);
- virThreadPoolFree(qemu_driver->workerPool);
- VIR_FREE(qemu_driver);
-
- return 0;
-}
-
-typedef int qemuLogHandleOutput(virDomainObjPtr vm,
- const char *output,
- int fd);
-
-/*
- * Returns -1 for error, 0 on success
- */
-static int
-qemudReadLogOutput(virDomainObjPtr vm,
- int fd,
- char *buf,
- size_t buflen,
- qemuLogHandleOutput func,
- const char *what,
- int timeout)
-{
- int retries = (timeout*10);
- int got = 0;
- buf[0] = '\0';
-
- while (retries) {
- ssize_t func_ret, ret;
- int isdead = 0;
-
- func_ret = func(vm, buf, fd);
-
- if (kill(vm->pid, 0) == -1 && errno == ESRCH)
- isdead = 1;
-
- /* Any failures should be detected before we read the log, so we
- * always have something useful to report on failure. */
- ret = saferead(fd, buf+got, buflen-got-1);
- if (ret < 0) {
- virReportSystemError(errno,
- _("Failure while reading %s log output"),
- what);
- return -1;
- }
-
- got += ret;
- buf[got] = '\0';
- if (got == buflen-1) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("Out of space while reading %s log output: %s"),
- what, buf);
- return -1;
- }
-
- if (isdead) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("Process exited while reading %s log output:
%s"),
- what, buf);
- return -1;
- }
-
- if (func_ret <= 0)
- return func_ret;
-
- usleep(100*1000);
- retries--;
- }
-
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("Timed out while reading %s log output: %s"),
- what, buf);
- return -1;
-}
-
-
-/*
- * Look at a chunk of data from the QEMU stdout logs and try to
- * find a TTY device, as indicated by a line like
- *
- * char device redirected to /dev/pts/3
- *
- * Returns -1 for error, 0 success, 1 continue reading
- */
-static int
-qemudExtractTTYPath(const char *haystack,
- size_t *offset,
- char **path)
-{
- static const char needle[] = "char device redirected to";
- char *tmp, *dev;
-
- VIR_FREE(*path);
- /* First look for our magic string */
- if (!(tmp = strstr(haystack + *offset, needle))) {
- return 1;
- }
- tmp += sizeof(needle);
- dev = tmp;
-
- /*
- * And look for first whitespace character and nul terminate
- * to mark end of the pty path
- */
- while (*tmp) {
- if (c_isspace(*tmp)) {
- *path = strndup(dev, tmp-dev);
- if (*path == NULL) {
- virReportOOMError();
- return -1;
- }
-
- /* ... now further update offset till we get EOL */
- *offset = tmp - haystack;
- return 0;
- }
- tmp++;
- }
-
- /*
- * We found a path, but didn't find any whitespace,
- * so it must be still incomplete - we should at
- * least see a \n - indicate that we want to carry
- * on trying again
- */
- return 1;
-}
-
-static int
-qemudFindCharDevicePTYsMonitor(virDomainObjPtr vm,
- virHashTablePtr paths)
-{
- int i;
-
-#define LOOKUP_PTYS(array, arraylen, idprefix) \
- for (i = 0 ; i < (arraylen) ; i++) { \
- virDomainChrDefPtr chr = (array)[i]; \
- if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) { \
- char id[16]; \
- \
- if (snprintf(id, sizeof(id), idprefix "%i", i) >= sizeof(id)) \
- return -1; \
- \
- const char *path = (const char *) virHashLookup(paths, id); \
- if (path == NULL) { \
- if (chr->source.data.file.path == NULL) { \
- /* neither the log output nor 'info chardev' had a */ \
- /* pty path for this chardev, report an error */ \
- qemuReportError(VIR_ERR_INTERNAL_ERROR, \
- _("no assigned pty for device %s"), id); \
- return -1; \
- } else { \
- /* 'info chardev' had no pty path for this chardev, */\
- /* but the log output had, so we're fine */ \
- continue; \
- } \
- } \
- \
- VIR_FREE(chr->source.data.file.path); \
- chr->source.data.file.path = strdup(path); \
- \
- if (chr->source.data.file.path == NULL) { \
- virReportOOMError(); \
- return -1; \
- } \
- } \
- }
-
- LOOKUP_PTYS(vm->def->serials, vm->def->nserials,
"serial");
- LOOKUP_PTYS(vm->def->parallels, vm->def->nparallels,
"parallel");
- LOOKUP_PTYS(vm->def->channels, vm->def->nchannels,
"channel");
- if (vm->def->console)
- LOOKUP_PTYS(&vm->def->console, 1, "console");
-#undef LOOKUP_PTYS
-
- return 0;
-}
-
-static int
-qemudFindCharDevicePTYs(virDomainObjPtr vm,
- const char *output,
- int fd ATTRIBUTE_UNUSED)
-{
- size_t offset = 0;
- int ret, i;
-
- /* The order in which QEMU prints out the PTY paths is
- the order in which it procsses its serial and parallel
- device args. This code must match that ordering.... */
-
- /* first comes the serial devices */
- for (i = 0 ; i < vm->def->nserials ; i++) {
- virDomainChrDefPtr chr = vm->def->serials[i];
- if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) {
- if ((ret = qemudExtractTTYPath(output, &offset,
- &chr->source.data.file.path)) != 0)
- return ret;
- }
- }
-
- /* then the parallel devices */
- for (i = 0 ; i < vm->def->nparallels ; i++) {
- virDomainChrDefPtr chr = vm->def->parallels[i];
- if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) {
- if ((ret = qemudExtractTTYPath(output, &offset,
- &chr->source.data.file.path)) != 0)
- return ret;
- }
- }
-
- /* then the channel devices */
- for (i = 0 ; i < vm->def->nchannels ; i++) {
- virDomainChrDefPtr chr = vm->def->channels[i];
- if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) {
- if ((ret = qemudExtractTTYPath(output, &offset,
- &chr->source.data.file.path)) != 0)
- return ret;
- }
- }
-
- return 0;
-}
-
-static void qemudFreePtyPath(void *payload, const char *name ATTRIBUTE_UNUSED)
-{
- VIR_FREE(payload);
-}
-
-static void
-qemuReadLogFD(int logfd, char *buf, int maxlen, int off)
-{
- int ret;
- char *tmpbuf = buf + off;
-
- ret = saferead(logfd, tmpbuf, maxlen - off - 1);
- if (ret < 0) {
- ret = 0;
- }
-
- tmpbuf[ret] = '\0';
-}
-
-static int
-qemudWaitForMonitor(struct qemud_driver* driver,
- virDomainObjPtr vm, off_t pos)
-{
- char buf[4096] = ""; /* Plenty of space to get startup greeting */
- int logfd;
- int ret = -1;
- virHashTablePtr paths = NULL;
-
- if ((logfd = qemudLogReadFD(driver->logDir, vm->def->name, pos)) < 0)
- return -1;
-
- if (qemudReadLogOutput(vm, logfd, buf, sizeof(buf),
- qemudFindCharDevicePTYs,
- "console", 30) < 0)
- goto closelog;
-
- VIR_DEBUG("Connect monitor to %p '%s'", vm, vm->def->name);
- if (qemuConnectMonitor(driver, vm) < 0) {
- goto cleanup;
- }
-
- /* Try to get the pty path mappings again via the monitor. This is much more
- * reliable if it's available.
- * Note that the monitor itself can be on a pty, so we still need to try the
- * log output method. */
- paths = virHashCreate(0);
- if (paths == NULL) {
- virReportOOMError();
- goto cleanup;
- }
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- qemuDomainObjPrivatePtr priv = vm->privateData;
- ret = qemuMonitorGetPtyPaths(priv->mon, paths);
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- VIR_DEBUG("qemuMonitorGetPtyPaths returned %i", ret);
- if (ret == 0) {
- ret = qemudFindCharDevicePTYsMonitor(vm, paths);
- }
-
-cleanup:
- if (paths) {
- virHashFree(paths, qemudFreePtyPath);
- }
-
- if (kill(vm->pid, 0) == -1 && errno == ESRCH) {
- /* VM is dead, any other error raised in the interim is probably
- * not as important as the qemu cmdline output */
- qemuReadLogFD(logfd, buf, sizeof(buf), strlen(buf));
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("process exited while connecting to monitor: %s"),
- buf);
- ret = -1;
- }
-
-closelog:
- if (VIR_CLOSE(logfd) < 0) {
- char ebuf[4096];
- VIR_WARN("Unable to close logfile: %s",
- virStrerror(errno, ebuf, sizeof ebuf));
- }
-
- return ret;
-}
-
-static int
-qemuDetectVcpuPIDs(struct qemud_driver *driver,
- virDomainObjPtr vm) {
- pid_t *cpupids = NULL;
- int ncpupids;
- qemuDomainObjPrivatePtr priv = vm->privateData;
-
- if (vm->def->virtType != VIR_DOMAIN_VIRT_KVM) {
- priv->nvcpupids = 1;
- if (VIR_ALLOC_N(priv->vcpupids, priv->nvcpupids) < 0) {
- virReportOOMError();
- return -1;
- }
- priv->vcpupids[0] = vm->pid;
- return 0;
- }
-
- /* What follows is now all KVM specific */
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if ((ncpupids = qemuMonitorGetCPUInfo(priv->mon, &cpupids)) < 0) {
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- return -1;
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- /* Treat failure to get VCPU<->PID mapping as non-fatal */
- if (ncpupids == 0)
- return 0;
-
- if (ncpupids != vm->def->vcpus) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("got wrong number of vCPU pids from QEMU monitor. "
- "got %d, wanted %d"),
- ncpupids, vm->def->vcpus);
- VIR_FREE(cpupids);
- return -1;
- }
-
- priv->nvcpupids = ncpupids;
- priv->vcpupids = cpupids;
- return 0;
-}
-
-/*
- * To be run between fork/exec of QEMU only
- */
-static int
-qemudInitCpuAffinity(virDomainObjPtr vm)
-{
- int i, hostcpus, maxcpu = QEMUD_CPUMASK_LEN;
- virNodeInfo nodeinfo;
- unsigned char *cpumap;
- int cpumaplen;
-
- DEBUG0("Setting CPU affinity");
-
- if (nodeGetInfo(NULL, &nodeinfo) < 0)
- return -1;
-
- /* setaffinity fails if you set bits for CPUs which
- * aren't present, so we have to limit ourselves */
- hostcpus = VIR_NODEINFO_MAXCPUS(nodeinfo);
- if (maxcpu > hostcpus)
- maxcpu = hostcpus;
-
- cpumaplen = VIR_CPU_MAPLEN(maxcpu);
- if (VIR_ALLOC_N(cpumap, cpumaplen) < 0) {
- virReportOOMError();
- return -1;
- }
-
- if (vm->def->cpumask) {
- /* XXX why don't we keep 'cpumask' in the libvirt cpumap
- * format to start with ?!?! */
- for (i = 0 ; i < maxcpu && i < vm->def->cpumasklen ; i++)
- if (vm->def->cpumask[i])
- VIR_USE_CPU(cpumap, i);
- } else {
- /* You may think this is redundant, but we can't assume libvirtd
- * itself is running on all pCPUs, so we need to explicitly set
- * the spawned QEMU instance to all pCPUs if no map is given in
- * its config file */
- for (i = 0 ; i < maxcpu ; i++)
- VIR_USE_CPU(cpumap, i);
- }
-
- /* We are pressuming we are running between fork/exec of QEMU
- * so use '0' to indicate our own process ID. No threads are
- * running at this point
- */
- if (virProcessInfoSetAffinity(0, /* Self */
- cpumap, cpumaplen, maxcpu) < 0) {
- VIR_FREE(cpumap);
- return -1;
- }
- VIR_FREE(cpumap);
-
- return 0;
-}
-
-
-static int
-qemuInitPasswords(virConnectPtr conn,
- struct qemud_driver *driver,
- virDomainObjPtr vm,
- unsigned long long qemuCmdFlags) {
- int ret = 0;
- qemuDomainObjPrivatePtr priv = vm->privateData;
-
- if (vm->def->ngraphics == 1) {
- if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
- ret = qemuDomainChangeGraphicsPasswords(driver, vm,
- VIR_DOMAIN_GRAPHICS_TYPE_VNC,
-
&vm->def->graphics[0]->data.vnc.auth,
- driver->vncPassword);
- } else if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE)
{
- ret = qemuDomainChangeGraphicsPasswords(driver, vm,
- VIR_DOMAIN_GRAPHICS_TYPE_SPICE,
-
&vm->def->graphics[0]->data.spice.auth,
- driver->spicePassword);
- }
- }
-
- if (ret < 0)
- goto cleanup;
-
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- int i;
-
- for (i = 0 ; i < vm->def->ndisks ; i++) {
- char *secret;
- size_t secretLen;
-
- if (!vm->def->disks[i]->encryption ||
- !vm->def->disks[i]->src)
- continue;
-
- if (getVolumeQcowPassphrase(conn,
- vm->def->disks[i],
- &secret, &secretLen) < 0)
- goto cleanup;
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- ret = qemuMonitorSetDrivePassphrase(priv->mon,
- vm->def->disks[i]->info.alias,
- secret);
- VIR_FREE(secret);
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- if (ret < 0)
- goto cleanup;
- }
- }
-
-cleanup:
- return ret;
-}
-
-
-#define QEMU_PCI_VENDOR_INTEL 0x8086
-#define QEMU_PCI_VENDOR_LSI_LOGIC 0x1000
-#define QEMU_PCI_VENDOR_REDHAT 0x1af4
-#define QEMU_PCI_VENDOR_CIRRUS 0x1013
-#define QEMU_PCI_VENDOR_REALTEK 0x10ec
-#define QEMU_PCI_VENDOR_AMD 0x1022
-#define QEMU_PCI_VENDOR_ENSONIQ 0x1274
-#define QEMU_PCI_VENDOR_VMWARE 0x15ad
-#define QEMU_PCI_VENDOR_QEMU 0x1234
-
-#define QEMU_PCI_PRODUCT_DISK_VIRTIO 0x1001
-
-#define QEMU_PCI_PRODUCT_BALLOON_VIRTIO 0x1002
-
-#define QEMU_PCI_PRODUCT_NIC_NE2K 0x8029
-#define QEMU_PCI_PRODUCT_NIC_PCNET 0x2000
-#define QEMU_PCI_PRODUCT_NIC_RTL8139 0x8139
-#define QEMU_PCI_PRODUCT_NIC_E1000 0x100E
-#define QEMU_PCI_PRODUCT_NIC_VIRTIO 0x1000
-
-#define QEMU_PCI_PRODUCT_VGA_CIRRUS 0x00b8
-#define QEMU_PCI_PRODUCT_VGA_VMWARE 0x0405
-#define QEMU_PCI_PRODUCT_VGA_STDVGA 0x1111
-
-#define QEMU_PCI_PRODUCT_AUDIO_AC97 0x2415
-#define QEMU_PCI_PRODUCT_AUDIO_ES1370 0x5000
-
-#define QEMU_PCI_PRODUCT_CONTROLLER_PIIX 0x7010
-#define QEMU_PCI_PRODUCT_CONTROLLER_LSI 0x0012
-
-#define QEMU_PCI_PRODUCT_WATCHDOG_I63000ESB 0x25ab
-
-static int
-qemuAssignNextPCIAddress(virDomainDeviceInfo *info,
- int vendor,
- int product,
- qemuMonitorPCIAddress *addrs,
- int naddrs)
-{
- int found = 0;
- int i;
-
- VIR_DEBUG("Look for %x:%x out of %d", vendor, product, naddrs);
-
- for (i = 0 ; (i < naddrs) && !found; i++) {
- VIR_DEBUG("Maybe %x:%x", addrs[i].vendor, addrs[i].product);
- if (addrs[i].vendor == vendor &&
- addrs[i].product == product) {
- VIR_DEBUG("Match %d", i);
- found = 1;
- break;
- }
- }
- if (!found) {
- return -1;
- }
-
- /* Blank it out so this device isn't matched again */
- addrs[i].vendor = 0;
- addrs[i].product = 0;
-
- if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
- info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
-
- if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
- info->addr.pci.domain = addrs[i].addr.domain;
- info->addr.pci.bus = addrs[i].addr.bus;
- info->addr.pci.slot = addrs[i].addr.slot;
- info->addr.pci.function = addrs[i].addr.function;
- }
-
- return 0;
-}
-
-static int
-qemuGetPCIDiskVendorProduct(virDomainDiskDefPtr def,
- unsigned *vendor,
- unsigned *product)
-{
- switch (def->bus) {
- case VIR_DOMAIN_DISK_BUS_VIRTIO:
- *vendor = QEMU_PCI_VENDOR_REDHAT;
- *product = QEMU_PCI_PRODUCT_DISK_VIRTIO;
- break;
-
- default:
- return -1;
- }
-
- return 0;
-}
-
-static int
-qemuGetPCINetVendorProduct(virDomainNetDefPtr def,
- unsigned *vendor,
- unsigned *product)
-{
- if (!def->model)
- return -1;
-
- if (STREQ(def->model, "ne2k_pci")) {
- *vendor = QEMU_PCI_VENDOR_REALTEK;
- *product = QEMU_PCI_PRODUCT_NIC_NE2K;
- } else if (STREQ(def->model, "pcnet")) {
- *vendor = QEMU_PCI_VENDOR_AMD;
- *product = QEMU_PCI_PRODUCT_NIC_PCNET;
- } else if (STREQ(def->model, "rtl8139")) {
- *vendor = QEMU_PCI_VENDOR_REALTEK;
- *product = QEMU_PCI_PRODUCT_NIC_RTL8139;
- } else if (STREQ(def->model, "e1000")) {
- *vendor = QEMU_PCI_VENDOR_INTEL;
- *product = QEMU_PCI_PRODUCT_NIC_E1000;
- } else if (STREQ(def->model, "virtio")) {
- *vendor = QEMU_PCI_VENDOR_REDHAT;
- *product = QEMU_PCI_PRODUCT_NIC_VIRTIO;
- } else {
- VIR_INFO("Unexpected NIC model %s, cannot get PCI address",
- def->model);
- return -1;
- }
- return 0;
-}
-
-static int
-qemuGetPCIControllerVendorProduct(virDomainControllerDefPtr def,
- unsigned *vendor,
- unsigned *product)
-{
- switch (def->type) {
- case VIR_DOMAIN_CONTROLLER_TYPE_SCSI:
- *vendor = QEMU_PCI_VENDOR_LSI_LOGIC;
- *product = QEMU_PCI_PRODUCT_CONTROLLER_LSI;
- break;
-
- case VIR_DOMAIN_CONTROLLER_TYPE_FDC:
- /* XXX we could put in the ISA bridge address, but
- that's not technically the FDC's address */
- return -1;
-
- case VIR_DOMAIN_CONTROLLER_TYPE_IDE:
- *vendor = QEMU_PCI_VENDOR_INTEL;
- *product = QEMU_PCI_PRODUCT_CONTROLLER_PIIX;
- break;
-
- default:
- VIR_INFO("Unexpected controller type %s, cannot get PCI address",
- virDomainControllerTypeToString(def->type));
- return -1;
- }
-
- return 0;
-}
-
-static int
-qemuGetPCIVideoVendorProduct(virDomainVideoDefPtr def,
- unsigned *vendor,
- unsigned *product)
-{
- switch (def->type) {
- case VIR_DOMAIN_VIDEO_TYPE_CIRRUS:
- *vendor = QEMU_PCI_VENDOR_CIRRUS;
- *product = QEMU_PCI_PRODUCT_VGA_CIRRUS;
- break;
-
- case VIR_DOMAIN_VIDEO_TYPE_VGA:
- *vendor = QEMU_PCI_VENDOR_QEMU;
- *product = QEMU_PCI_PRODUCT_VGA_STDVGA;
- break;
-
- case VIR_DOMAIN_VIDEO_TYPE_VMVGA:
- *vendor = QEMU_PCI_VENDOR_VMWARE;
- *product = QEMU_PCI_PRODUCT_VGA_VMWARE;
- break;
-
- default:
- return -1;
- }
- return 0;
-}
-
-static int
-qemuGetPCISoundVendorProduct(virDomainSoundDefPtr def,
- unsigned *vendor,
- unsigned *product)
-{
- switch (def->model) {
- case VIR_DOMAIN_SOUND_MODEL_ES1370:
- *vendor = QEMU_PCI_VENDOR_ENSONIQ;
- *product = QEMU_PCI_PRODUCT_AUDIO_ES1370;
- break;
-
- case VIR_DOMAIN_SOUND_MODEL_AC97:
- *vendor = QEMU_PCI_VENDOR_INTEL;
- *product = QEMU_PCI_PRODUCT_AUDIO_AC97;
- break;
-
- default:
- return -1;
- }
-
- return 0;
-}
-
-static int
-qemuGetPCIWatchdogVendorProduct(virDomainWatchdogDefPtr def,
- unsigned *vendor,
- unsigned *product)
-{
- switch (def->model) {
- case VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB:
- *vendor = QEMU_PCI_VENDOR_INTEL;
- *product = QEMU_PCI_PRODUCT_WATCHDOG_I63000ESB;
- break;
-
- default:
- return -1;
- }
-
- return 0;
-}
-
-
-static int
-qemuGetPCIMemballoonVendorProduct(virDomainMemballoonDefPtr def,
- unsigned *vendor,
- unsigned *product)
-{
- switch (def->model) {
- case VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO:
- *vendor = QEMU_PCI_VENDOR_REDHAT;
- *product = QEMU_PCI_PRODUCT_BALLOON_VIRTIO;
- break;
-
- default:
- return -1;
- }
-
- return 0;
-}
-
-
-/*
- * This entire method assumes that PCI devices in 'info pci'
- * match ordering of devices specified on the command line
- * wrt to devices of matching vendor+product
- *
- * XXXX this might not be a valid assumption if we assign
- * some static addrs on CLI. Have to check that...
- */
-static int
-qemuDetectPCIAddresses(virDomainObjPtr vm,
- qemuMonitorPCIAddress *addrs,
- int naddrs)
-{
- unsigned int vendor = 0, product = 0;
- int i;
-
- /* XXX should all these vendor/product IDs be kept in the
- * actual device data structure instead ?
- */
-
- for (i = 0 ; i < vm->def->ndisks ; i++) {
- if (qemuGetPCIDiskVendorProduct(vm->def->disks[i], &vendor,
&product) < 0)
- continue;
-
- if (qemuAssignNextPCIAddress(&(vm->def->disks[i]->info),
- vendor, product,
- addrs, naddrs) < 0) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("cannot find PCI address for VirtIO disk %s"),
- vm->def->disks[i]->dst);
- return -1;
- }
- }
-
- for (i = 0 ; i < vm->def->nnets ; i++) {
- if (qemuGetPCINetVendorProduct(vm->def->nets[i], &vendor, &product)
< 0)
- continue;
-
- if (qemuAssignNextPCIAddress(&(vm->def->nets[i]->info),
- vendor, product,
- addrs, naddrs) < 0) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("cannot find PCI address for %s NIC"),
- vm->def->nets[i]->model);
- return -1;
- }
- }
-
- for (i = 0 ; i < vm->def->ncontrollers ; i++) {
- if (qemuGetPCIControllerVendorProduct(vm->def->controllers[i], &vendor,
&product) < 0)
- continue;
-
- if (qemuAssignNextPCIAddress(&(vm->def->controllers[i]->info),
- vendor, product,
- addrs, naddrs) < 0) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("cannot find PCI address for controller %s"),
-
virDomainControllerTypeToString(vm->def->controllers[i]->type));
- return -1;
- }
- }
-
- for (i = 0 ; i < vm->def->nvideos ; i++) {
- if (qemuGetPCIVideoVendorProduct(vm->def->videos[i], &vendor,
&product) < 0)
- continue;
-
- if (qemuAssignNextPCIAddress(&(vm->def->videos[i]->info),
- vendor, product,
- addrs, naddrs) < 0) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("cannot find PCI address for video adapter %s"),
-
virDomainVideoTypeToString(vm->def->videos[i]->type));
- return -1;
- }
- }
-
- for (i = 0 ; i < vm->def->nsounds ; i++) {
- if (qemuGetPCISoundVendorProduct(vm->def->sounds[i], &vendor,
&product) < 0)
- continue;
-
- if (qemuAssignNextPCIAddress(&(vm->def->sounds[i]->info),
- vendor, product,
- addrs, naddrs) < 0) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("cannot find PCI address for sound adapter %s"),
-
virDomainSoundModelTypeToString(vm->def->sounds[i]->model));
- return -1;
- }
- }
-
-
- if (vm->def->watchdog &&
- qemuGetPCIWatchdogVendorProduct(vm->def->watchdog, &vendor,
&product) == 0) {
- if (qemuAssignNextPCIAddress(&(vm->def->watchdog->info),
- vendor, product,
- addrs, naddrs) < 0) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("cannot find PCI address for watchdog %s"),
-
virDomainWatchdogModelTypeToString(vm->def->watchdog->model));
- return -1;
- }
- }
-
- if (vm->def->memballoon &&
- qemuGetPCIMemballoonVendorProduct(vm->def->memballoon, &vendor,
&product) == 0) {
- if (qemuAssignNextPCIAddress(&(vm->def->memballoon->info),
- vendor, product,
- addrs, naddrs) < 0) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("cannot find PCI address for balloon %s"),
-
virDomainMemballoonModelTypeToString(vm->def->memballoon->model));
- return -1;
- }
- }
-
- /* XXX console (virtio) */
-
-
- /* ... and now things we don't have in our xml */
-
- /* XXX USB controller ? */
-
- /* XXX what about other PCI devices (ie bridges) */
-
- return 0;
-}
-
-static int
-qemuInitPCIAddresses(struct qemud_driver *driver,
- virDomainObjPtr vm)
-{
- qemuDomainObjPrivatePtr priv = vm->privateData;
- int naddrs;
- int ret;
- qemuMonitorPCIAddress *addrs = NULL;
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- naddrs = qemuMonitorGetAllPCIAddresses(priv->mon,
- &addrs);
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- ret = qemuDetectPCIAddresses(vm, addrs, naddrs);
-
- VIR_FREE(addrs);
-
- return ret;
-}
-
-
-static int qemudNextFreePort(struct qemud_driver *driver,
- int startPort) {
- int i;
-
- for (i = startPort ; i < QEMU_VNC_PORT_MAX; i++) {
- int fd;
- int reuse = 1;
- struct sockaddr_in addr;
- bool used = false;
-
- if (virBitmapGetBit(driver->reservedVNCPorts,
- i - QEMU_VNC_PORT_MIN, &used) < 0)
- VIR_DEBUG("virBitmapGetBit failed on bit %d", i -
QEMU_VNC_PORT_MIN);
-
- if (used)
- continue;
-
- addr.sin_family = AF_INET;
- addr.sin_port = htons(i);
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
- fd = socket(PF_INET, SOCK_STREAM, 0);
- if (fd < 0)
- return -1;
-
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&reuse, sizeof(reuse))
< 0) {
- VIR_FORCE_CLOSE(fd);
- break;
- }
+ goto error;
- if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
- /* Not in use, lets grab it */
- VIR_FORCE_CLOSE(fd);
- /* Add port to bitmap of reserved ports */
- if (virBitmapSetBit(driver->reservedVNCPorts,
- i - QEMU_VNC_PORT_MIN) < 0) {
- VIR_DEBUG("virBitmapSetBit failed on bit %d",
- i - QEMU_VNC_PORT_MIN);
- }
- return i;
+ if (privileged) {
+ if (chown(qemu_driver->libDir, qemu_driver->user, qemu_driver->group)
< 0) {
+ virReportSystemError(errno,
+ _("unable to set ownership of '%s' to user
%d:%d"),
+ qemu_driver->libDir, qemu_driver->user,
qemu_driver->group);
+ goto error;
}
- VIR_FORCE_CLOSE(fd);
-
- if (errno == EADDRINUSE) {
- /* In use, try next */
- continue;
+ if (chown(qemu_driver->cacheDir, qemu_driver->user, qemu_driver->group)
< 0) {
+ virReportSystemError(errno,
+ _("unable to set ownership of '%s' to
%d:%d"),
+ qemu_driver->cacheDir, qemu_driver->user,
qemu_driver->group);
+ goto error;
}
- /* Some other bad failure, get out.. */
- break;
- }
- return -1;
-}
-
-
-static void
-qemuReturnPort(struct qemud_driver *driver,
- int port)
-{
- if (port < QEMU_VNC_PORT_MIN)
- return;
-
- if (virBitmapClearBit(driver->reservedVNCPorts,
- port - QEMU_VNC_PORT_MIN) < 0)
- VIR_DEBUG("Could not mark port %d as unused", port);
-}
-
-
-static int
-qemuAssignPCIAddresses(virDomainDefPtr def)
-{
- int ret = -1;
- unsigned long long qemuCmdFlags = 0;
- qemuDomainPCIAddressSetPtr addrs = NULL;
-
- if (qemuCapsExtractVersionInfo(def->emulator,
- NULL,
- &qemuCmdFlags) < 0)
- goto cleanup;
-
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (!(addrs = qemuDomainPCIAddressSetCreate(def)))
- goto cleanup;
-
- if (qemuAssignDevicePCISlots(def, addrs) < 0)
- goto cleanup;
- }
-
- ret = 0;
-
-cleanup:
- qemuDomainPCIAddressSetFree(addrs);
-
- return ret;
-}
-
-
-static int
-qemuPrepareChardevDevice(virDomainDefPtr def ATTRIBUTE_UNUSED,
- virDomainChrDefPtr dev,
- void *opaque ATTRIBUTE_UNUSED)
-{
- int fd;
- if (dev->source.type != VIR_DOMAIN_CHR_TYPE_FILE)
- return 0;
-
- if ((fd = open(dev->source.data.file.path,
- O_CREAT | O_APPEND, S_IRUSR|S_IWUSR)) < 0) {
- virReportSystemError(errno,
- _("Unable to pre-create chardev file
'%s'"),
- dev->source.data.file.path);
- return -1;
- }
-
- VIR_FORCE_CLOSE(fd);
-
- return 0;
-}
-
-
-struct qemudHookData {
- virConnectPtr conn;
- virDomainObjPtr vm;
- struct qemud_driver *driver;
-};
-
-static int qemudSecurityHook(void *data) {
- struct qemudHookData *h = data;
-
- /* This must take place before exec(), so that all QEMU
- * memory allocation is on the correct NUMA node
- */
- if (qemuAddToCgroup(h->driver, h->vm->def) < 0)
- return -1;
-
- /* This must be done after cgroup placement to avoid resetting CPU
- * affinity */
- if (qemudInitCpuAffinity(h->vm) < 0)
- return -1;
-
- if (virSecurityManagerSetProcessLabel(h->driver->securityManager, h->vm)
< 0)
- return -1;
-
- return 0;
-}
-
-static int
-qemuPrepareMonitorChr(struct qemud_driver *driver,
- virDomainChrSourceDefPtr monConfig,
- const char *vm)
-{
- monConfig->type = VIR_DOMAIN_CHR_TYPE_UNIX;
- monConfig->data.nix.listen = true;
-
- if (virAsprintf(&monConfig->data.nix.path, "%s/%s.monitor",
- driver->libDir, vm) < 0) {
- virReportOOMError();
- return -1;
- }
-
- return 0;
-}
-
-static int qemuDomainSnapshotSetCurrentActive(virDomainObjPtr vm,
- char *snapshotDir);
-static int qemuDomainSnapshotSetCurrentInactive(virDomainObjPtr vm,
- char *snapshotDir);
-
-
-#define START_POSTFIX ": starting up\n"
-#define SHUTDOWN_POSTFIX ": shutting down\n"
-
-static int qemudStartVMDaemon(virConnectPtr conn,
- struct qemud_driver *driver,
- virDomainObjPtr vm,
- const char *migrateFrom,
- bool start_paused,
- int stdin_fd,
- const char *stdin_path,
- enum virVMOperationType vmop) {
- int ret;
- unsigned long long qemuCmdFlags;
- off_t pos = -1;
- char ebuf[1024];
- char *pidfile = NULL;
- int logfile = -1;
- char *timestamp;
- qemuDomainObjPrivatePtr priv = vm->privateData;
- virCommandPtr cmd = NULL;
-
- struct qemudHookData hookData;
- hookData.conn = conn;
- hookData.vm = vm;
- hookData.driver = driver;
-
- DEBUG0("Beginning VM startup process");
-
- if (virDomainObjIsActive(vm)) {
- qemuReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("VM is already active"));
- return -1;
- }
-
- /* Do this upfront, so any part of the startup process can add
- * runtime state to vm->def that won't be persisted. This let's us
- * report implicit runtime defaults in the XML, like vnc listen/socket
- */
- DEBUG0("Setting current domain def as transient");
- if (virDomainObjSetDefTransient(driver->caps, vm, true) < 0)
- goto cleanup;
-
- /* Must be run before security labelling */
- DEBUG0("Preparing host devices");
- if (qemuPrepareHostDevices(driver, vm->def) < 0)
- goto cleanup;
-
- DEBUG0("Preparing chr devices");
- if (virDomainChrDefForeach(vm->def,
- true,
- qemuPrepareChardevDevice,
- NULL) < 0)
- goto cleanup;
-
- /* If you are using a SecurityDriver with dynamic labelling,
- then generate a security label for isolation */
- DEBUG0("Generating domain security label (if required)");
- if (virSecurityManagerGenLabel(driver->securityManager, vm) < 0) {
- qemuDomainSecurityLabelAudit(vm, false);
- goto cleanup;
- }
- qemuDomainSecurityLabelAudit(vm, true);
-
- DEBUG0("Generating setting domain security labels (if required)");
- if (virSecurityManagerSetAllLabel(driver->securityManager,
- vm, stdin_path) < 0)
- goto cleanup;
-
- if (stdin_fd != -1) {
- /* if there's an fd to migrate from, and it's a pipe, put the
- * proper security label on it
- */
- struct stat stdin_sb;
-
- DEBUG0("setting security label on pipe used for migration");
-
- if (fstat(stdin_fd, &stdin_sb) < 0) {
+ if (chown(qemu_driver->saveDir, qemu_driver->user, qemu_driver->group)
< 0) {
virReportSystemError(errno,
- _("cannot stat fd %d"), stdin_fd);
- goto cleanup;
+ _("unable to set ownership of '%s' to
%d:%d"),
+ qemu_driver->saveDir, qemu_driver->user,
qemu_driver->group);
+ goto error;
}
- if (S_ISFIFO(stdin_sb.st_mode) &&
- virSecurityManagerSetFDLabel(driver->securityManager, vm, stdin_fd) <
0)
- goto cleanup;
- }
-
- /* Ensure no historical cgroup for this VM is lying around bogus
- * settings */
- DEBUG0("Ensuring no historical cgroup is lying around");
- qemuRemoveCgroup(driver, vm, 1);
-
- if (vm->def->ngraphics == 1) {
- if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC
&&
- !vm->def->graphics[0]->data.vnc.socket &&
- vm->def->graphics[0]->data.vnc.autoport) {
- int port = qemudNextFreePort(driver, QEMU_VNC_PORT_MIN);
- if (port < 0) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("Unable to find an unused VNC
port"));
- goto cleanup;
- }
- vm->def->graphics[0]->data.vnc.port = port;
- } else if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE
&&
- vm->def->graphics[0]->data.spice.autoport) {
- int port = qemudNextFreePort(driver, QEMU_VNC_PORT_MIN);
- int tlsPort = -1;
- if (port < 0) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("Unable to find an unused SPICE
port"));
- goto cleanup;
- }
-
- if (driver->spiceTLS) {
- tlsPort = qemudNextFreePort(driver, port + 1);
- if (tlsPort < 0) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("Unable to find an unused
SPICE TLS port"));
- qemuReturnPort(driver, port);
- goto cleanup;
- }
- }
-
- vm->def->graphics[0]->data.spice.port = port;
- vm->def->graphics[0]->data.spice.tlsPort = tlsPort;
+ if (chown(qemu_driver->snapshotDir, qemu_driver->user,
qemu_driver->group) < 0) {
+ virReportSystemError(errno,
+ _("unable to set ownership of '%s' to
%d:%d"),
+ qemu_driver->snapshotDir, qemu_driver->user,
qemu_driver->group);
+ goto error;
}
}
- if (virFileMakePath(driver->logDir) != 0) {
- virReportSystemError(errno,
- _("cannot create log directory %s"),
- driver->logDir);
- goto cleanup;
- }
-
- DEBUG0("Creating domain log file");
- if ((logfile = qemudLogFD(driver, vm->def->name, false)) < 0)
- goto cleanup;
-
- DEBUG0("Determining emulator version");
- if (qemuCapsExtractVersionInfo(vm->def->emulator,
- NULL,
- &qemuCmdFlags) < 0)
- goto cleanup;
-
- DEBUG0("Setting up domain cgroup (if required)");
- if (qemuSetupCgroup(driver, vm) < 0)
- goto cleanup;
-
- if (VIR_ALLOC(priv->monConfig) < 0) {
- virReportOOMError();
- goto cleanup;
- }
-
- DEBUG0("Preparing monitor state");
- if (qemuPrepareMonitorChr(driver, priv->monConfig, vm->def->name) < 0)
- goto cleanup;
-
-#if HAVE_YAJL
- if (qemuCmdFlags & QEMUD_CMD_FLAG_MONITOR_JSON)
- priv->monJSON = 1;
- else
-#endif
- priv->monJSON = 0;
-
- priv->monitor_warned = 0;
- priv->gotShutdown = false;
-
- if ((ret = virFileDeletePid(driver->stateDir, vm->def->name)) != 0) {
- virReportSystemError(ret,
- _("Cannot remove stale PID file for %s"),
- vm->def->name);
- goto cleanup;
- }
-
- if (!(pidfile = virFilePid(driver->stateDir, vm->def->name))) {
- virReportSystemError(errno,
- "%s", _("Failed to build pidfile
path."));
- goto cleanup;
- }
-
- /*
- * Normally PCI addresses are assigned in the virDomainCreate
- * or virDomainDefine methods. We might still need to assign
- * some here to cope with the question of upgrades. Regardless
- * we also need to populate the PCi address set cache for later
- * use in hotplug
+ /* If hugetlbfs is present, then we need to create a sub-directory within
+ * it, since we can't assume the root mount point has permissions that
+ * will let our spawned QEMU instances use it.
+ *
+ * NB the check for '/', since user may config "" to disable
hugepages
+ * even when mounted
*/
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- DEBUG0("Assigning domain PCI addresses");
- /* Populate cache with current addresses */
- if (priv->pciaddrs) {
- qemuDomainPCIAddressSetFree(priv->pciaddrs);
- priv->pciaddrs = NULL;
- }
- if (!(priv->pciaddrs = qemuDomainPCIAddressSetCreate(vm->def)))
- goto cleanup;
-
-
- /* Assign any remaining addresses */
- if (qemuAssignDevicePCISlots(vm->def, priv->pciaddrs) < 0)
- goto cleanup;
-
- priv->persistentAddrs = 1;
- } else {
- priv->persistentAddrs = 0;
- }
-
- DEBUG0("Building emulator command line");
- vm->def->id = driver->nextvmid++;
- if (!(cmd = qemuBuildCommandLine(conn, driver, vm->def, priv->monConfig,
- priv->monJSON != 0, qemuCmdFlags,
- migrateFrom, stdin_fd,
- vm->current_snapshot, vmop)))
- goto cleanup;
-
- if (qemuDomainSnapshotSetCurrentInactive(vm, driver->snapshotDir) < 0)
- goto cleanup;
-
- /* now that we know it is about to start call the hook if present */
- if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) {
- char *xml = virDomainDefFormat(vm->def, 0);
- int hookret;
-
- hookret = virHookCall(VIR_HOOK_DRIVER_QEMU, vm->def->name,
- VIR_HOOK_QEMU_OP_START, VIR_HOOK_SUBOP_BEGIN, NULL, xml);
- VIR_FREE(xml);
-
- /*
- * If the script raised an error abort the launch
- */
- if (hookret < 0)
- goto cleanup;
- }
+ if (qemu_driver->hugetlbfs_mount &&
+ qemu_driver->hugetlbfs_mount[0] == '/') {
+ char *mempath = NULL;
+ if (virAsprintf(&mempath, "%s/libvirt/qemu",
qemu_driver->hugetlbfs_mount) < 0)
+ goto out_of_memory;
- if ((timestamp = virTimestamp()) == NULL) {
- virReportOOMError();
- goto cleanup;
- } else {
- if (safewrite(logfile, timestamp, strlen(timestamp)) < 0 ||
- safewrite(logfile, START_POSTFIX, strlen(START_POSTFIX)) < 0) {
- VIR_WARN("Unable to write timestamp to logfile: %s",
- virStrerror(errno, ebuf, sizeof ebuf));
+ if ((rc = virFileMakePath(mempath)) != 0) {
+ virReportSystemError(rc,
+ _("unable to create hugepage path %s"),
mempath);
+ VIR_FREE(mempath);
+ goto error;
}
-
- VIR_FREE(timestamp);
- }
-
- virCommandWriteArgLog(cmd, logfile);
-
- if ((pos = lseek(logfile, 0, SEEK_END)) < 0)
- VIR_WARN("Unable to seek to end of logfile: %s",
- virStrerror(errno, ebuf, sizeof ebuf));
-
- VIR_DEBUG("Clear emulator capabilities: %d",
- driver->clearEmulatorCapabilities);
- if (driver->clearEmulatorCapabilities)
- virCommandClearCaps(cmd);
-
- virCommandSetPreExecHook(cmd, qemudSecurityHook, &hookData);
-
- virCommandSetOutputFD(cmd, &logfile);
- virCommandSetErrorFD(cmd, &logfile);
- virCommandNonblockingFDs(cmd);
- virCommandSetPidFile(cmd, pidfile);
- virCommandDaemonize(cmd);
-
- ret = virCommandRun(cmd, NULL);
- VIR_FREE(pidfile);
-
- /* wait for qemu process to to show up */
- if (ret == 0) {
- if (virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("Domain %s didn't show up"),
vm->def->name);
- ret = -1;
+ if (qemu_driver->privileged &&
+ chown(mempath, qemu_driver->user, qemu_driver->group) < 0) {
+ virReportSystemError(errno,
+ _("unable to set ownership on %s to %d:%d"),
+ mempath, qemu_driver->user, qemu_driver->group);
+ VIR_FREE(mempath);
+ goto error;
}
-#if 0
- } else if (ret == -2) {
- /*
- * XXX this is bogus. It isn't safe to set vm->pid = child
- * because the child no longer exists.
- */
- /* The virExec process that launches the daemon failed. Pending on
- * when it failed (we can't determine for sure), there may be
- * extra info in the domain log (if the hook failed for example).
- *
- * Pretend like things succeeded, and let 'WaitForMonitor' report
- * the log contents for us.
- */
- vm->pid = child;
- ret = 0;
-#endif
+ qemu_driver->hugepage_path = mempath;
}
- if (migrateFrom)
- start_paused = true;
- vm->state = start_paused ? VIR_DOMAIN_PAUSED : VIR_DOMAIN_RUNNING;
-
- if (ret == -1) /* The VM failed to start; tear filters before taps */
- virDomainConfVMNWFilterTeardown(vm);
-
- if (ret == -1) /* The VM failed to start */
- goto cleanup;
+ /* Get all the running persistent or transient configs first */
+ if (virDomainLoadAllConfigs(qemu_driver->caps,
+ &qemu_driver->domains,
+ qemu_driver->stateDir,
+ NULL,
+ 1, NULL, NULL) < 0)
+ goto error;
- DEBUG0("Waiting for monitor to show up");
- if (qemudWaitForMonitor(driver, vm, pos) < 0)
- goto cleanup;
+ conn = virConnectOpen(qemu_driver->privileged ?
+ "qemu:///system" :
+ "qemu:///session");
- DEBUG0("Detecting VCPU PIDs");
- if (qemuDetectVcpuPIDs(driver, vm) < 0)
- goto cleanup;
+ qemuProcessReconnectAll(conn, qemu_driver);
- DEBUG0("Setting any required VM passwords");
- if (qemuInitPasswords(conn, driver, vm, qemuCmdFlags) < 0)
- goto cleanup;
+ /* Then inactive persistent configs */
+ if (virDomainLoadAllConfigs(qemu_driver->caps,
+ &qemu_driver->domains,
+ qemu_driver->configDir,
+ qemu_driver->autostartDir,
+ 0, NULL, NULL) < 0)
+ goto error;
- /* If we have -device, then addresses are assigned explicitly.
- * If not, then we have to detect dynamic ones here */
- if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
- DEBUG0("Determining domain device PCI addresses");
- if (qemuInitPCIAddresses(driver, vm) < 0)
- goto cleanup;
- }
- DEBUG0("Setting initial memory amount");
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuMonitorSetBalloon(priv->mon, vm->def->mem.cur_balloon) < 0) {
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- goto cleanup;
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
+ virHashForEach(qemu_driver->domains.objs, qemuDomainSnapshotLoad,
+ qemu_driver->snapshotDir);
- if (!start_paused) {
- DEBUG0("Starting domain CPUs");
- /* Allow the CPUS to start executing */
- if (doStartCPUs(driver, vm, conn) < 0) {
- if (virGetLastError() == NULL)
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("resume operation failed"));
- goto cleanup;
- }
- }
+ qemuDriverUnlock(qemu_driver);
+ qemuAutostartDomains(qemu_driver);
- DEBUG0("Writing domain status to disk");
- if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
- goto cleanup;
+ qemu_driver->workerPool = virThreadPoolNew(0, 1, processWatchdogEvent,
qemu_driver);
+ if (!qemu_driver->workerPool)
+ goto error;
- virCommandFree(cmd);
- VIR_FORCE_CLOSE(logfile);
+ if (conn)
+ virConnectClose(conn);
return 0;
-cleanup:
- /* We jump here if we failed to start the VM for any reason, or
- * if we failed to initialize the now running VM. kill it off and
- * pretend we never started it */
- virCommandFree(cmd);
- VIR_FORCE_CLOSE(logfile);
- qemudShutdownVMDaemon(driver, vm, 0);
-
+out_of_memory:
+ virReportOOMError();
+error:
+ if (qemu_driver)
+ qemuDriverUnlock(qemu_driver);
+ if (conn)
+ virConnectClose(conn);
+ VIR_FREE(base);
+ VIR_FREE(driverConf);
+ qemudShutdown();
return -1;
}
-static void qemudShutdownVMDaemon(struct qemud_driver *driver,
- virDomainObjPtr vm,
- int migrated) {
- int ret;
- int retries = 0;
- qemuDomainObjPrivatePtr priv = vm->privateData;
- virErrorPtr orig_err;
- virDomainDefPtr def;
- int i;
- int logfile = -1;
- char *timestamp;
- char ebuf[1024];
-
- VIR_DEBUG("Shutting down VM '%s' pid=%d migrated=%d",
- vm->def->name, vm->pid, migrated);
+static void qemudNotifyLoadDomain(virDomainObjPtr vm, int newVM, void *opaque)
+{
+ struct qemud_driver *driver = opaque;
- if ((logfile = qemudLogFD(driver, vm->def->name, true)) < 0) {
- /* To not break the normal domain shutdown process, skip the
- * timestamp log writing if failed on opening log file. */
- VIR_WARN("Unable to open logfile: %s",
- virStrerror(errno, ebuf, sizeof ebuf));
- } else {
- if ((timestamp = virTimestamp()) == NULL) {
- virReportOOMError();
- } else {
- if (safewrite(logfile, timestamp, strlen(timestamp)) < 0 ||
- safewrite(logfile, SHUTDOWN_POSTFIX,
- strlen(SHUTDOWN_POSTFIX)) < 0) {
- VIR_WARN("Unable to write timestamp to logfile: %s",
- virStrerror(errno, ebuf, sizeof ebuf));
- }
+ if (newVM) {
+ virDomainEventPtr event =
+ virDomainEventNewFromObj(vm,
+ VIR_DOMAIN_EVENT_DEFINED,
+ VIR_DOMAIN_EVENT_DEFINED_ADDED);
+ if (event)
+ qemuDomainEventQueue(driver, event);
+ }
+}
- VIR_FREE(timestamp);
- }
+/**
+ * qemudReload:
+ *
+ * Function to restart the QEmu daemon, it will recheck the configuration
+ * files and update its state and the networking
+ */
+static int
+qemudReload(void) {
+ if (!qemu_driver)
+ return 0;
- if (VIR_CLOSE(logfile) < 0)
- VIR_WARN("Unable to close logfile: %s",
- virStrerror(errno, ebuf, sizeof ebuf));
- }
+ qemuDriverLock(qemu_driver);
+ virDomainLoadAllConfigs(qemu_driver->caps,
+ &qemu_driver->domains,
+ qemu_driver->configDir,
+ qemu_driver->autostartDir,
+ 0, qemudNotifyLoadDomain, qemu_driver);
+ qemuDriverUnlock(qemu_driver);
- /* This method is routinely used in clean up paths. Disable error
- * reporting so we don't squash a legit error. */
- orig_err = virSaveLastError();
+ qemuAutostartDomains(qemu_driver);
- virDomainConfVMNWFilterTeardown(vm);
+ return 0;
+}
- if (driver->macFilter) {
- def = vm->def;
- for (i = 0 ; i < def->nnets ; i++) {
- virDomainNetDefPtr net = def->nets[i];
- if (net->ifname == NULL)
- continue;
- if ((errno = networkDisallowMacOnPort(driver, net->ifname,
- net->mac))) {
- virReportSystemError(errno,
- _("failed to remove ebtables rule to allow MAC address on
'%s'"),
- net->ifname);
- }
- }
- }
+/**
+ * qemudActive:
+ *
+ * Checks if the QEmu daemon is active, i.e. has an active domain or
+ * an active network
+ *
+ * Returns 1 if active, 0 otherwise
+ */
+static int
+qemudActive(void) {
+ int active = 0;
- /* This will safely handle a non-running guest with pid=0 or pid=-1*/
- if (virKillProcess(vm->pid, 0) == 0 &&
- virKillProcess(vm->pid, SIGTERM) < 0)
- virReportSystemError(errno,
- _("Failed to send SIGTERM to %s (%d)"),
- vm->def->name, vm->pid);
+ if (!qemu_driver)
+ return 0;
- if (priv->mon)
- qemuMonitorClose(priv->mon);
+ /* XXX having to iterate here is not great because it requires many locks */
+ qemuDriverLock(qemu_driver);
+ active = virDomainObjListNumOfDomains(&qemu_driver->domains, 1);
+ qemuDriverUnlock(qemu_driver);
+ return active;
+}
- if (priv->monConfig) {
- if (priv->monConfig->type == VIR_DOMAIN_CHR_TYPE_UNIX)
- unlink(priv->monConfig->data.nix.path);
- virDomainChrSourceDefFree(priv->monConfig);
- priv->monConfig = NULL;
- }
+/**
+ * qemudShutdown:
+ *
+ * Shutdown the QEmu daemon, it will stop all active domains and networks
+ */
+static int
+qemudShutdown(void) {
+ int i;
- /* shut it off for sure */
- virKillProcess(vm->pid, SIGKILL);
+ if (!qemu_driver)
+ return -1;
- /* now that we know it's stopped call the hook if present */
- if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) {
- char *xml = virDomainDefFormat(vm->def, 0);
+ qemuDriverLock(qemu_driver);
+ pciDeviceListFree(qemu_driver->activePciHostdevs);
+ virCapabilitiesFree(qemu_driver->caps);
- /* we can't stop the operation even if the script raised an error */
- virHookCall(VIR_HOOK_DRIVER_QEMU, vm->def->name,
- VIR_HOOK_QEMU_OP_STOPPED, VIR_HOOK_SUBOP_END, NULL, xml);
- VIR_FREE(xml);
- }
+ virDomainObjListDeinit(&qemu_driver->domains);
+ virBitmapFree(qemu_driver->reservedVNCPorts);
- /* Reset Security Labels */
- virSecurityManagerRestoreAllLabel(driver->securityManager,
- vm, migrated);
- virSecurityManagerReleaseLabel(driver->securityManager, vm);
+ virSysinfoDefFree(qemu_driver->hostsysinfo);
- /* Clear out dynamically assigned labels */
- if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
- VIR_FREE(vm->def->seclabel.model);
- VIR_FREE(vm->def->seclabel.label);
- VIR_FREE(vm->def->seclabel.imagelabel);
- }
+ VIR_FREE(qemu_driver->configDir);
+ VIR_FREE(qemu_driver->autostartDir);
+ VIR_FREE(qemu_driver->logDir);
+ VIR_FREE(qemu_driver->stateDir);
+ VIR_FREE(qemu_driver->libDir);
+ VIR_FREE(qemu_driver->cacheDir);
+ VIR_FREE(qemu_driver->saveDir);
+ VIR_FREE(qemu_driver->snapshotDir);
+ VIR_FREE(qemu_driver->autoDumpPath);
+ VIR_FREE(qemu_driver->vncTLSx509certdir);
+ VIR_FREE(qemu_driver->vncListen);
+ VIR_FREE(qemu_driver->vncPassword);
+ VIR_FREE(qemu_driver->vncSASLdir);
+ VIR_FREE(qemu_driver->spiceTLSx509certdir);
+ VIR_FREE(qemu_driver->spiceListen);
+ VIR_FREE(qemu_driver->spicePassword);
+ VIR_FREE(qemu_driver->hugetlbfs_mount);
+ VIR_FREE(qemu_driver->hugepage_path);
+ VIR_FREE(qemu_driver->saveImageFormat);
+ VIR_FREE(qemu_driver->dumpImageFormat);
- virDomainDefClearDeviceAliases(vm->def);
- if (!priv->persistentAddrs) {
- virDomainDefClearPCIAddresses(vm->def);
- qemuDomainPCIAddressSetFree(priv->pciaddrs);
- priv->pciaddrs = NULL;
- }
+ virSecurityManagerFree(qemu_driver->securityManager);
- qemuDomainReAttachHostDevices(driver, vm->def);
+ ebtablesContextFree(qemu_driver->ebtables);
-#if WITH_MACVTAP
- def = vm->def;
- for (i = 0; i < def->nnets; i++) {
- virDomainNetDefPtr net = def->nets[i];
- if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
- delMacvtap(net->ifname, net->mac, net->data.direct.linkdev,
- &net->data.direct.virtPortProfile);
- VIR_FREE(net->ifname);
- }
+ if (qemu_driver->cgroupDeviceACL) {
+ for (i = 0 ; qemu_driver->cgroupDeviceACL[i] != NULL ; i++)
+ VIR_FREE(qemu_driver->cgroupDeviceACL[i]);
+ VIR_FREE(qemu_driver->cgroupDeviceACL);
}
-#endif
-retry:
- if ((ret = qemuRemoveCgroup(driver, vm, 0)) < 0) {
- if (ret == -EBUSY && (retries++ < 5)) {
- usleep(200*1000);
- goto retry;
- }
- VIR_WARN("Failed to remove cgroup for %s",
- vm->def->name);
- }
+ /* Free domain callback list */
+ virDomainEventCallbackListFree(qemu_driver->domainEventCallbacks);
+ virDomainEventQueueFree(qemu_driver->domainEventQueue);
- qemudRemoveDomainStatus(driver, vm);
+ if (qemu_driver->domainEventTimer != -1)
+ virEventRemoveTimeout(qemu_driver->domainEventTimer);
- /* Remove VNC port from port reservation bitmap, but only if it was
- reserved by the driver (autoport=yes)
- */
- if ((vm->def->ngraphics == 1) &&
- vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
- vm->def->graphics[0]->data.vnc.autoport) {
- qemuReturnPort(driver, vm->def->graphics[0]->data.vnc.port);
- }
- if ((vm->def->ngraphics == 1) &&
- vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE &&
- vm->def->graphics[0]->data.spice.autoport) {
- qemuReturnPort(driver, vm->def->graphics[0]->data.spice.port);
- qemuReturnPort(driver, vm->def->graphics[0]->data.spice.tlsPort);
- }
+ if (qemu_driver->brctl)
+ brShutdown(qemu_driver->brctl);
- vm->pid = -1;
- vm->def->id = -1;
- vm->state = VIR_DOMAIN_SHUTOFF;
- VIR_FREE(priv->vcpupids);
- priv->nvcpupids = 0;
+ virCgroupFree(&qemu_driver->cgroup);
- if (vm->newDef) {
- virDomainDefFree(vm->def);
- vm->def = vm->newDef;
- vm->def->id = -1;
- vm->newDef = NULL;
- }
+ qemuDriverUnlock(qemu_driver);
+ virMutexDestroy(&qemu_driver->lock);
+ virThreadPoolFree(qemu_driver->workerPool);
+ VIR_FREE(qemu_driver);
- if (orig_err) {
- virSetError(orig_err);
- virFreeError(orig_err);
- }
+ return 0;
}
+
+static int qemuDomainSnapshotSetCurrentActive(virDomainObjPtr vm,
+ char *snapshotDir);
+static int qemuDomainSnapshotSetCurrentInactive(virDomainObjPtr vm,
+ char *snapshotDir);
+
+
static virDrvOpenStatus qemudOpen(virConnectPtr conn,
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED) {
@@ -3582,7 +1223,7 @@ static virDomainPtr qemudDomainCreate(virConnectPtr conn, const char
*xml,
if (qemudCanonicalizeMachine(driver, def) < 0)
goto cleanup;
- if (qemuAssignPCIAddresses(def) < 0)
+ if (qemuDomainAssignPCIAddresses(def) < 0)
goto cleanup;
if (!(vm = virDomainAssignDef(driver->caps,
@@ -3595,9 +1236,9 @@ static virDomainPtr qemudDomainCreate(virConnectPtr conn, const char
*xml,
if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
goto cleanup; /* XXXX free the 'vm' we created ? */
- if (qemudStartVMDaemon(conn, driver, vm, NULL,
- (flags & VIR_DOMAIN_START_PAUSED) != 0,
- -1, NULL, VIR_VM_OP_CREATE) < 0) {
+ if (qemuProcessStart(conn, driver, vm, NULL,
+ (flags & VIR_DOMAIN_START_PAUSED) != 0,
+ -1, NULL, VIR_VM_OP_CREATE) < 0) {
qemuDomainStartAudit(vm, "booted", false);
if (qemuDomainObjEndJob(vm) > 0)
virDomainRemoveInactive(&driver->domains,
@@ -3672,7 +1313,7 @@ static int qemudDomainSuspend(virDomainPtr dom) {
goto endjob;
}
if (vm->state != VIR_DOMAIN_PAUSED) {
- if (doStopCPUs(driver, vm) < 0) {
+ if (qemuProcessStopCPUs(driver, vm) < 0) {
goto endjob;
}
event = virDomainEventNewFromObj(vm,
@@ -3725,7 +1366,7 @@ static int qemudDomainResume(virDomainPtr dom) {
goto endjob;
}
if (vm->state == VIR_DOMAIN_PAUSED) {
- if (doStartCPUs(driver, vm, dom->conn) < 0) {
+ if (qemuProcessStartCPUs(driver, vm, dom->conn) < 0) {
if (virGetLastError() == NULL)
qemuReportError(VIR_ERR_OPERATION_FAILED,
"%s", _("resume operation failed"));
@@ -3820,7 +1461,7 @@ static int qemudDomainDestroy(virDomainPtr dom) {
goto endjob;
}
- qemudShutdownVMDaemon(driver, vm, 0);
+ qemuProcessStop(driver, vm, 0);
event = virDomainEventNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
@@ -4044,7 +1685,7 @@ qemuDomainMigrateOffline(struct qemud_driver *driver,
{
int ret;
- ret = doStopCPUs(driver, vm);
+ ret = qemuProcessStopCPUs(driver, vm);
if (ret == 0) {
virDomainEventPtr event;
@@ -4315,7 +1956,7 @@ static int qemudDomainSaveFlag(struct qemud_driver *driver,
virDomainPtr dom,
/* Pause */
if (vm->state == VIR_DOMAIN_RUNNING) {
header.was_running = 1;
- if (doStopCPUs(driver, vm) < 0)
+ if (qemuProcessStopCPUs(driver, vm) < 0)
goto endjob;
if (!virDomainObjIsActive(vm)) {
@@ -4525,7 +2166,7 @@ static int qemudDomainSaveFlag(struct qemud_driver *driver,
virDomainPtr dom,
ret = 0;
/* Shut it down */
- qemudShutdownVMDaemon(driver, vm, 0);
+ qemuProcessStop(driver, vm, 0);
qemuDomainStopAudit(vm, "saved");
event = virDomainEventNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
@@ -4541,7 +2182,7 @@ endjob:
if (vm) {
if (ret != 0) {
if (header.was_running && virDomainObjIsActive(vm)) {
- rc = doStartCPUs(driver, vm, dom->conn);
+ rc = qemuProcessStartCPUs(driver, vm, dom->conn);
if (rc < 0)
VIR_WARN0("Unable to resume guest CPUs after save
failure");
}
@@ -4895,7 +2536,7 @@ static int qemudDomainCoreDump(virDomainPtr dom,
/* Pause domain for non-live dump */
if (!(flags & VIR_DUMP_LIVE) && vm->state == VIR_DOMAIN_RUNNING) {
- if (doStopCPUs(driver, vm) < 0)
+ if (qemuProcessStopCPUs(driver, vm) < 0)
goto endjob;
paused = 1;
@@ -4914,7 +2555,7 @@ static int qemudDomainCoreDump(virDomainPtr dom,
endjob:
if ((ret == 0) && (flags & VIR_DUMP_CRASH)) {
- qemudShutdownVMDaemon(driver, vm, 0);
+ qemuProcessStop(driver, vm, 0);
qemuDomainStopAudit(vm, "crashed");
event = virDomainEventNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
@@ -4925,7 +2566,7 @@ endjob:
will support synchronous operations so we always get here after
the migration is complete. */
else if (resume && paused && virDomainObjIsActive(vm)) {
- if (doStartCPUs(driver, vm, dom->conn) < 0) {
+ if (qemuProcessStartCPUs(driver, vm, dom->conn) < 0) {
if (virGetLastError() == NULL)
qemuReportError(VIR_ERR_OPERATION_FAILED,
"%s", _("resuming after dump
failed"));
@@ -4952,7 +2593,7 @@ cleanup:
static void processWatchdogEvent(void *data, void *opaque)
{
int ret;
- struct watchdogEvent *wdEvent = data;
+ struct qemuDomainWatchdogEvent *wdEvent = data;
struct qemud_driver *driver = opaque;
switch (wdEvent->action) {
@@ -4986,7 +2627,7 @@ static void processWatchdogEvent(void *data, void *opaque)
qemuReportError(VIR_ERR_OPERATION_FAILED,
"%s", _("Dump failed"));
- ret = doStartCPUs(driver, wdEvent->vm, NULL);
+ ret = qemuProcessStartCPUs(driver, wdEvent->vm, NULL);
if (ret < 0)
qemuReportError(VIR_ERR_OPERATION_FAILED,
@@ -5776,8 +3417,8 @@ qemudDomainSaveImageStartVM(virConnectPtr conn,
}
/* Set the migration source and start it up. */
- ret = qemudStartVMDaemon(conn, driver, vm, "stdio", true, fd, path,
- VIR_VM_OP_RESTORE);
+ ret = qemuProcessStart(conn, driver, vm, "stdio", true, fd, path,
+ VIR_VM_OP_RESTORE);
if (intermediate_pid != -1) {
if (ret < 0) {
@@ -5836,7 +3477,7 @@ qemudDomainSaveImageStartVM(virConnectPtr conn,
/* If it was running before, resume it now. */
if (header->was_running) {
- if (doStartCPUs(driver, vm, conn) < 0) {
+ if (qemuProcessStartCPUs(driver, vm, conn) < 0) {
if (virGetLastError() == NULL)
qemuReportError(VIR_ERR_OPERATION_FAILED,
"%s", _("failed to resume domain"));
@@ -6136,7 +3777,7 @@ static char *qemuDomainXMLToNative(virConnectPtr conn,
&qemuCmdFlags) < 0)
goto cleanup;
- if (qemuPrepareMonitorChr(driver, &monConfig, def->name) < 0)
+ if (qemuProcessPrepareMonitorChr(driver, &monConfig, def->name) < 0)
goto cleanup;
if (!(cmd = qemuBuildCommandLine(conn, driver, def,
@@ -6202,8 +3843,8 @@ static int qemudDomainObjStart(virConnectPtr conn,
goto cleanup;
}
- ret = qemudStartVMDaemon(conn, driver, vm, NULL, start_paused, -1, NULL,
- VIR_VM_OP_CREATE);
+ ret = qemuProcessStart(conn, driver, vm, NULL, start_paused, -1, NULL,
+ VIR_VM_OP_CREATE);
qemuDomainStartAudit(vm, "booted", ret >= 0);
if (ret >= 0) {
virDomainEventPtr event =
@@ -6390,7 +4031,7 @@ static virDomainPtr qemudDomainDefine(virConnectPtr conn, const char
*xml) {
if (qemudCanonicalizeMachine(driver, def) < 0)
goto cleanup;
- if (qemuAssignPCIAddresses(def) < 0)
+ if (qemuDomainAssignPCIAddresses(def) < 0)
goto cleanup;
if (!(vm = virDomainAssignDef(driver->caps,
@@ -7894,60 +5535,6 @@ qemuDomainEventDeregisterAny(virConnectPtr conn,
}
-static void qemuDomainEventDispatchFunc(virConnectPtr conn,
- virDomainEventPtr event,
- virConnectDomainEventGenericCallback cb,
- void *cbopaque,
- void *opaque)
-{
- struct qemud_driver *driver = opaque;
-
- /* Drop the lock whle dispatching, for sake of re-entrancy */
- qemuDriverUnlock(driver);
- virDomainEventDispatchDefaultFunc(conn, event, cb, cbopaque, NULL);
- qemuDriverLock(driver);
-}
-
-static void qemuDomainEventFlush(int timer ATTRIBUTE_UNUSED, void *opaque)
-{
- struct qemud_driver *driver = opaque;
- virDomainEventQueue tempQueue;
-
- qemuDriverLock(driver);
-
- driver->domainEventDispatching = 1;
-
- /* Copy the queue, so we're reentrant safe */
- tempQueue.count = driver->domainEventQueue->count;
- tempQueue.events = driver->domainEventQueue->events;
- driver->domainEventQueue->count = 0;
- driver->domainEventQueue->events = NULL;
-
- virEventUpdateTimeout(driver->domainEventTimer, -1);
- virDomainEventQueueDispatch(&tempQueue,
- driver->domainEventCallbacks,
- qemuDomainEventDispatchFunc,
- driver);
-
- /* Purge any deleted callbacks */
- virDomainEventCallbackListPurgeMarked(driver->domainEventCallbacks);
-
- driver->domainEventDispatching = 0;
- qemuDriverUnlock(driver);
-}
-
-
-/* driver must be locked before calling */
-static void qemuDomainEventQueue(struct qemud_driver *driver,
- virDomainEventPtr event)
-{
- if (virDomainEventQueuePush(driver->domainEventQueue,
- event) < 0)
- virDomainEventFree(event);
- if (qemu_driver->domainEventQueue->count == 1)
- virEventUpdateTimeout(driver->domainEventTimer, 0);
-}
-
/* Migration support. */
static bool ATTRIBUTE_NONNULL(1)
@@ -8078,12 +5665,12 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn,
/* Start the QEMU daemon, with the same command-line arguments plus
* -incoming unix:/path/to/file or exec:nc -U /path/to/file
*/
- internalret = qemudStartVMDaemon(dconn, driver, vm, migrateFrom, true,
- -1, NULL, VIR_VM_OP_MIGRATE_IN_START);
+ internalret = qemuProcessStart(dconn, driver, vm, migrateFrom, true,
+ -1, NULL, VIR_VM_OP_MIGRATE_IN_START);
VIR_FREE(migrateFrom);
if (internalret < 0) {
qemuDomainStartAudit(vm, "migrated", false);
- /* Note that we don't set an error here because qemudStartVMDaemon
+ /* Note that we don't set an error here because qemuProcessStart
* should have already done that.
*/
if (!vm->persistent) {
@@ -8097,7 +5684,7 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn,
unixfile,
false) < 0) {
qemuDomainStartAudit(vm, "migrated", false);
- qemudShutdownVMDaemon(driver, vm, 0);
+ qemuProcessStop(driver, vm, 0);
if (!vm->persistent) {
if (qemuDomainObjEndJob(vm) > 0)
virDomainRemoveInactive(&driver->domains, vm);
@@ -8324,10 +5911,10 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
* -incoming tcp:0.0.0.0:port
*/
snprintf (migrateFrom, sizeof (migrateFrom), "tcp:0.0.0.0:%d", this_port);
- if (qemudStartVMDaemon (dconn, driver, vm, migrateFrom, true,
- -1, NULL, VIR_VM_OP_MIGRATE_IN_START) < 0) {
+ if (qemuProcessStart(dconn, driver, vm, migrateFrom, true,
+ -1, NULL, VIR_VM_OP_MIGRATE_IN_START) < 0) {
qemuDomainStartAudit(vm, "migrated", false);
- /* Note that we don't set an error here because qemudStartVMDaemon
+ /* Note that we don't set an error here because qemuProcessStart
* should have already done that.
*/
if (!vm->persistent) {
@@ -8909,7 +6496,7 @@ qemudDomainMigratePerform (virDomainPtr dom,
}
/* Clean up the source domain. */
- qemudShutdownVMDaemon(driver, vm, 1);
+ qemuProcessStop(driver, vm, 1);
qemuDomainStopAudit(vm, "migrated");
resume = 0;
@@ -8927,7 +6514,7 @@ qemudDomainMigratePerform (virDomainPtr dom,
endjob:
if (resume && vm->state == VIR_DOMAIN_PAUSED) {
/* we got here through some sort of failure; start the domain again */
- if (doStartCPUs(driver, vm, dom->conn) < 0) {
+ if (qemuProcessStartCPUs(driver, vm, dom->conn) < 0) {
/* Hm, we already know we are in error here. We don't want to
* overwrite the previous error, though, so we just throw something
* to the logs and hope for the best
@@ -9091,7 +6678,7 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn,
* >= 0.10.6 to work properly. This isn't strictly necessary on
* older qemu's, but it also doesn't hurt anything there
*/
- if (doStartCPUs(driver, vm, dconn) < 0) {
+ if (qemuProcessStartCPUs(driver, vm, dconn) < 0) {
if (virGetLastError() == NULL)
qemuReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("resume operation
failed"));
@@ -9113,7 +6700,7 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn,
goto endjob;
}
} else {
- qemudShutdownVMDaemon(driver, vm, 1);
+ qemuProcessStop(driver, vm, 1);
qemuDomainStopAudit(vm, "failed");
event = virDomainEventNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
@@ -9942,8 +7529,8 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr
snapshot,
if (qemuDomainSnapshotSetCurrentActive(vm, driver->snapshotDir) < 0)
goto endjob;
- rc = qemudStartVMDaemon(snapshot->domain->conn, driver, vm, NULL,
- false, -1, NULL, VIR_VM_OP_CREATE);
+ rc = qemuProcessStart(snapshot->domain->conn, driver, vm, NULL,
+ false, -1, NULL, VIR_VM_OP_CREATE);
qemuDomainStartAudit(vm, "from-snapshot", rc >= 0);
if (qemuDomainSnapshotSetCurrentInactive(vm, driver->snapshotDir) < 0)
goto endjob;
@@ -9955,7 +7542,7 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr
snapshot,
/* qemu unconditionally starts the domain running again after
* loadvm, so let's pause it to keep consistency
*/
- rc = doStopCPUs(driver, vm);
+ rc = qemuProcessStopCPUs(driver, vm);
if (rc < 0)
goto endjob;
}
@@ -9976,7 +7563,7 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr
snapshot,
*/
if (virDomainObjIsActive(vm)) {
- qemudShutdownVMDaemon(driver, vm, 0);
+ qemuProcessStop(driver, vm, 0);
qemuDomainStopAudit(vm, "from-snapshot");
event = virDomainEventNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
@@ -10473,56 +8060,32 @@ static virStateDriver qemuStateDriver = {
.active = qemudActive,
};
-static int
-qemudVMFilterRebuild(virConnectPtr conn ATTRIBUTE_UNUSED,
- virHashIterator iter, void *data)
-{
- virHashForEach(qemu_driver->domains.objs, iter, data);
-
- return 0;
-}
-
-static int
-qemudVMFiltersInstantiate(virConnectPtr conn,
- virDomainDefPtr def)
-{
- int err = 0;
- int i;
-
- if (!conn)
- return 1;
-
- for (i = 0 ; i < def->nnets ; i++) {
- virDomainNetDefPtr net = def->nets[i];
- if ((net->filter) && (net->ifname)) {
- if (virDomainConfNWFilterInstantiate(conn, net)) {
- err = 1;
- break;
- }
- }
- }
-
- return err;
-}
-
-
static void
-qemudVMDriverLock(void) {
+qemuVMDriverLock(void) {
qemuDriverLock(qemu_driver);
};
static void
-qemudVMDriverUnlock(void) {
+qemuVMDriverUnlock(void) {
qemuDriverUnlock(qemu_driver);
};
+static int
+qemuVMFilterRebuild(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virHashIterator iter, void *data)
+{
+ virHashForEach(qemu_driver->domains.objs, iter, data);
+
+ return 0;
+}
+
static virNWFilterCallbackDriver qemuCallbackDriver = {
.name = "QEMU",
- .vmFilterRebuild = qemudVMFilterRebuild,
- .vmDriverLock = qemudVMDriverLock,
- .vmDriverUnlock = qemudVMDriverUnlock,
+ .vmFilterRebuild = qemuVMFilterRebuild,
+ .vmDriverLock = qemuVMDriverLock,
+ .vmDriverUnlock = qemuVMDriverUnlock,
};
int qemuRegister(void) {
diff --git a/src/qemu/qemu_driver.h b/src/qemu/qemu_driver.h
index dac0935..73da9e4 100644
--- a/src/qemu/qemu_driver.h
+++ b/src/qemu/qemu_driver.h
@@ -21,34 +21,9 @@
* Author: Daniel P. Berrange <berrange(a)redhat.com>
*/
-
-#ifndef QEMUD_DRIVER_H
-# define QEMUD_DRIVER_H
-
-# include <config.h>
-
-# include <libxml/xpath.h>
-
-# include "internal.h"
-
-# if HAVE_LINUX_KVM_H
-# include <linux/kvm.h>
-# endif
-
-/* device for kvm ioctls */
-# define KVM_DEVICE "/dev/kvm"
-
-/* add definitions missing in older linux/kvm.h */
-# ifndef KVMIO
-# define KVMIO 0xAE
-# endif
-# ifndef KVM_CHECK_EXTENSION
-# define KVM_CHECK_EXTENSION _IO(KVMIO, 0x03)
-# endif
-# ifndef KVM_CAP_NR_VCPUS
-# define KVM_CAP_NR_VCPUS 9 /* returns max vcpus per vm */
-# endif
+#ifndef __QEMU_DRIVER_H__
+# define __QEMU_DRIVER_H__
int qemuRegister(void);
-#endif /* QEMUD_DRIVER_H */
+#endif /* __QEMU_DRIVER_H__ */
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
new file mode 100644
index 0000000..1e3dea2
--- /dev/null
+++ b/src/qemu/qemu_process.c
@@ -0,0 +1,2412 @@
+/*
+ * qemu_process.h: QEMU process management
+ *
+ * Copyright (C) 2006-2011 Red Hat, Inc.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <config.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include "qemu_process.h"
+#include "qemu_domain.h"
+#include "qemu_cgroup.h"
+#include "qemu_capabilities.h"
+#include "qemu_monitor.h"
+#include "qemu_command.h"
+#include "qemu_audit.h"
+#include "qemu_hostdev.h"
+#include "qemu_hotplug.h"
+#include "qemu_bridge_filter.h"
+
+#include "datatypes.h"
+#include "logging.h"
+#include "virterror_internal.h"
+#include "memory.h"
+#include "hooks.h"
+#include "files.h"
+#include "util.h"
+#include "c-ctype.h"
+#include "nodeinfo.h"
+#include "processinfo.h"
+#include "domain_nwfilter.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+#define START_POSTFIX ": starting up\n"
+#define SHUTDOWN_POSTFIX ": shutting down\n"
+
+/**
+ * qemudRemoveDomainStatus
+ *
+ * remove all state files of a domain from statedir
+ *
+ * Returns 0 on success
+ */
+static int
+qemuProcessRemoveDomainStatus(struct qemud_driver *driver,
+ virDomainObjPtr vm)
+{
+ char ebuf[1024];
+ char *file = NULL;
+
+ if (virAsprintf(&file, "%s/%s.xml", driver->stateDir,
vm->def->name) < 0) {
+ virReportOOMError();
+ return(-1);
+ }
+
+ if (unlink(file) < 0 && errno != ENOENT && errno != ENOTDIR)
+ VIR_WARN("Failed to remove domain XML for %s: %s",
+ vm->def->name, virStrerror(errno, ebuf, sizeof(ebuf)));
+ VIR_FREE(file);
+
+ if (virFileDeletePid(driver->stateDir, vm->def->name) != 0)
+ VIR_WARN("Failed to remove PID file for %s: %s",
+ vm->def->name, virStrerror(errno, ebuf, sizeof(ebuf)));
+
+
+ return 0;
+}
+
+
+/* XXX figure out how to remove this */
+extern struct qemud_driver *qemu_driver;
+
+/*
+ * This is a callback registered with a qemuMonitorPtr instance,
+ * and to be invoked when the monitor console hits an end of file
+ * condition, or error, thus indicating VM shutdown should be
+ * performed
+ */
+static void
+qemuProcessHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm,
+ int hasError)
+{
+ struct qemud_driver *driver = qemu_driver;
+ virDomainEventPtr event = NULL;
+ qemuDomainObjPrivatePtr priv;
+
+ VIR_DEBUG("Received EOF on %p '%s'", vm, vm->def->name);
+
+ virDomainObjLock(vm);
+
+ if (!virDomainObjIsActive(vm)) {
+ VIR_DEBUG("Domain %p is not active, ignoring EOF", vm);
+ virDomainObjUnlock(vm);
+ return;
+ }
+
+ priv = vm->privateData;
+ if (!hasError && priv->monJSON && !priv->gotShutdown) {
+ VIR_DEBUG("Monitor connection to '%s' closed without SHUTDOWN event;
"
+ "assuming the domain crashed", vm->def->name);
+ hasError = 1;
+ }
+
+ event = virDomainEventNewFromObj(vm,
+ VIR_DOMAIN_EVENT_STOPPED,
+ hasError ?
+ VIR_DOMAIN_EVENT_STOPPED_FAILED :
+ VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
+
+ qemuProcessStop(driver, vm, 0);
+ qemuDomainStopAudit(vm, hasError ? "failed" : "shutdown");
+
+ if (!vm->persistent)
+ virDomainRemoveInactive(&driver->domains, vm);
+ else
+ virDomainObjUnlock(vm);
+
+ if (event) {
+ qemuDriverLock(driver);
+ qemuDomainEventQueue(driver, event);
+ qemuDriverUnlock(driver);
+ }
+}
+
+
+static virDomainDiskDefPtr
+qemuProcessFindDomainDiskByPath(virDomainObjPtr vm,
+ const char *path)
+{
+ int i;
+
+ for (i = 0; i < vm->def->ndisks; i++) {
+ virDomainDiskDefPtr disk;
+
+ disk = vm->def->disks[i];
+ if (disk->src != NULL && STREQ(disk->src, path))
+ return disk;
+ }
+
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("no disk found with path %s"),
+ path);
+ return NULL;
+}
+
+static virDomainDiskDefPtr
+qemuProcessFindDomainDiskByAlias(virDomainObjPtr vm,
+ const char *alias)
+{
+ int i;
+
+ if (STRPREFIX(alias, QEMU_DRIVE_HOST_PREFIX))
+ alias += strlen(QEMU_DRIVE_HOST_PREFIX);
+
+ for (i = 0; i < vm->def->ndisks; i++) {
+ virDomainDiskDefPtr disk;
+
+ disk = vm->def->disks[i];
+ if (disk->info.alias != NULL && STREQ(disk->info.alias, alias))
+ return disk;
+ }
+
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("no disk found with alias %s"),
+ alias);
+ return NULL;
+}
+
+static int
+qemuProcessGetVolumeQcowPassphrase(virConnectPtr conn,
+ virDomainDiskDefPtr disk,
+ char **secretRet,
+ size_t *secretLen)
+{
+ virSecretPtr secret;
+ char *passphrase;
+ unsigned char *data;
+ size_t size;
+ int ret = -1;
+ virStorageEncryptionPtr enc;
+
+ if (!disk->encryption) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("disk %s does not have any encryption information"),
+ disk->src);
+ return -1;
+ }
+ enc = disk->encryption;
+
+ if (!conn) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("cannot find secrets without a
connection"));
+ goto cleanup;
+ }
+
+ if (conn->secretDriver == NULL ||
+ conn->secretDriver->lookupByUUID == NULL ||
+ conn->secretDriver->getValue == NULL) {
+ qemuReportError(VIR_ERR_NO_SUPPORT, "%s",
+ _("secret storage not supported"));
+ goto cleanup;
+ }
+
+ if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW ||
+ enc->nsecrets != 1 ||
+ enc->secrets[0]->type !=
+ VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) {
+ qemuReportError(VIR_ERR_XML_ERROR,
+ _("invalid <encryption> for volume %s"),
disk->src);
+ goto cleanup;
+ }
+
+ secret = conn->secretDriver->lookupByUUID(conn,
+ enc->secrets[0]->uuid);
+ if (secret == NULL)
+ goto cleanup;
+ data = conn->secretDriver->getValue(secret, &size,
+ VIR_SECRET_GET_VALUE_INTERNAL_CALL);
+ virUnrefSecret(secret);
+ if (data == NULL)
+ goto cleanup;
+
+ if (memchr(data, '\0', size) != NULL) {
+ memset(data, 0, size);
+ VIR_FREE(data);
+ qemuReportError(VIR_ERR_XML_ERROR,
+ _("format='qcow' passphrase for %s must not contain
a "
+ "'\\0'"), disk->src);
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC_N(passphrase, size + 1) < 0) {
+ memset(data, 0, size);
+ VIR_FREE(data);
+ virReportOOMError();
+ goto cleanup;
+ }
+ memcpy(passphrase, data, size);
+ passphrase[size] = '\0';
+
+ memset(data, 0, size);
+ VIR_FREE(data);
+
+ *secretRet = passphrase;
+ *secretLen = size;
+
+ ret = 0;
+
+cleanup:
+ return ret;
+}
+
+static int
+qemuProcessFindVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virConnectPtr conn,
+ virDomainObjPtr vm,
+ const char *path,
+ char **secretRet,
+ size_t *secretLen)
+{
+ virDomainDiskDefPtr disk;
+ int ret = -1;
+
+ virDomainObjLock(vm);
+ disk = qemuProcessFindDomainDiskByPath(vm, path);
+
+ if (!disk)
+ goto cleanup;
+
+ ret = qemuProcessGetVolumeQcowPassphrase(conn, disk, secretRet, secretLen);
+
+cleanup:
+ virDomainObjUnlock(vm);
+ return ret;
+}
+
+
+static int
+qemuProcessHandleReset(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm)
+{
+ struct qemud_driver *driver = qemu_driver;
+ virDomainEventPtr event;
+
+ virDomainObjLock(vm);
+ event = virDomainEventRebootNewFromObj(vm);
+ virDomainObjUnlock(vm);
+
+ if (event) {
+ qemuDriverLock(driver);
+ qemuDomainEventQueue(driver, event);
+ qemuDriverUnlock(driver);
+ }
+
+ return 0;
+}
+
+
+static int
+qemuProcessHandleShutdown(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm)
+{
+ virDomainObjLock(vm);
+ ((qemuDomainObjPrivatePtr) vm->privateData)->gotShutdown = true;
+ virDomainObjUnlock(vm);
+
+ return 0;
+}
+
+
+static int
+qemuProcessHandleStop(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm)
+{
+ struct qemud_driver *driver = qemu_driver;
+ virDomainEventPtr event = NULL;
+
+ virDomainObjLock(vm);
+ if (vm->state == VIR_DOMAIN_RUNNING) {
+ VIR_DEBUG("Transitioned guest %s to paused state due to unknown event",
vm->def->name);
+
+ vm->state = VIR_DOMAIN_PAUSED;
+ event = virDomainEventNewFromObj(vm,
+ VIR_DOMAIN_EVENT_SUSPENDED,
+ VIR_DOMAIN_EVENT_SUSPENDED_PAUSED);
+
+ if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
+ VIR_WARN("Unable to save status on vm %s after IO error",
vm->def->name);
+ }
+ virDomainObjUnlock(vm);
+
+ if (event) {
+ qemuDriverLock(driver);
+ if (event)
+ qemuDomainEventQueue(driver, event);
+ qemuDriverUnlock(driver);
+ }
+
+ return 0;
+}
+
+
+static int
+qemuProcessHandleRTCChange(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm,
+ long long offset)
+{
+ struct qemud_driver *driver = qemu_driver;
+ virDomainEventPtr event;
+
+ virDomainObjLock(vm);
+ event = virDomainEventRTCChangeNewFromObj(vm, offset);
+
+ if (vm->def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_VARIABLE)
+ vm->def->clock.data.adjustment = offset;
+
+ if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
+ VIR_WARN0("unable to save domain status with RTC change");
+
+ virDomainObjUnlock(vm);
+
+ if (event) {
+ qemuDriverLock(driver);
+ qemuDomainEventQueue(driver, event);
+ qemuDriverUnlock(driver);
+ }
+
+ return 0;
+}
+
+
+static int
+qemuProcessHandleWatchdog(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm,
+ int action)
+{
+ struct qemud_driver *driver = qemu_driver;
+ virDomainEventPtr watchdogEvent = NULL;
+ virDomainEventPtr lifecycleEvent = NULL;
+
+ virDomainObjLock(vm);
+ watchdogEvent = virDomainEventWatchdogNewFromObj(vm, action);
+
+ if (action == VIR_DOMAIN_EVENT_WATCHDOG_PAUSE &&
+ vm->state == VIR_DOMAIN_RUNNING) {
+ VIR_DEBUG("Transitioned guest %s to paused state due to watchdog",
vm->def->name);
+
+ vm->state = VIR_DOMAIN_PAUSED;
+ lifecycleEvent = virDomainEventNewFromObj(vm,
+ VIR_DOMAIN_EVENT_SUSPENDED,
+ VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG);
+
+ if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
+ VIR_WARN("Unable to save status on vm %s after IO error",
vm->def->name);
+ }
+
+ if (vm->def->watchdog->action == VIR_DOMAIN_WATCHDOG_ACTION_DUMP) {
+ struct qemuDomainWatchdogEvent *wdEvent;
+ if (VIR_ALLOC(wdEvent) == 0) {
+ wdEvent->action = VIR_DOMAIN_WATCHDOG_ACTION_DUMP;
+ wdEvent->vm = vm;
+ ignore_value(virThreadPoolSendJob(driver->workerPool, wdEvent));
+ } else
+ virReportOOMError();
+ }
+
+ virDomainObjUnlock(vm);
+
+ if (watchdogEvent || lifecycleEvent) {
+ qemuDriverLock(driver);
+ if (watchdogEvent)
+ qemuDomainEventQueue(driver, watchdogEvent);
+ if (lifecycleEvent)
+ qemuDomainEventQueue(driver, lifecycleEvent);
+ qemuDriverUnlock(driver);
+ }
+
+ return 0;
+}
+
+
+static int
+qemuProcessHandleIOError(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm,
+ const char *diskAlias,
+ int action,
+ const char *reason)
+{
+ struct qemud_driver *driver = qemu_driver;
+ virDomainEventPtr ioErrorEvent = NULL;
+ virDomainEventPtr ioErrorEvent2 = NULL;
+ virDomainEventPtr lifecycleEvent = NULL;
+ const char *srcPath;
+ const char *devAlias;
+ virDomainDiskDefPtr disk;
+
+ virDomainObjLock(vm);
+ disk = qemuProcessFindDomainDiskByAlias(vm, diskAlias);
+
+ if (disk) {
+ srcPath = disk->src;
+ devAlias = disk->info.alias;
+ } else {
+ srcPath = "";
+ devAlias = "";
+ }
+
+ ioErrorEvent = virDomainEventIOErrorNewFromObj(vm, srcPath, devAlias, action);
+ ioErrorEvent2 = virDomainEventIOErrorReasonNewFromObj(vm, srcPath, devAlias, action,
reason);
+
+ if (action == VIR_DOMAIN_EVENT_IO_ERROR_PAUSE &&
+ vm->state == VIR_DOMAIN_RUNNING) {
+ VIR_DEBUG("Transitioned guest %s to paused state due to IO error",
vm->def->name);
+
+ vm->state = VIR_DOMAIN_PAUSED;
+ lifecycleEvent = virDomainEventNewFromObj(vm,
+ VIR_DOMAIN_EVENT_SUSPENDED,
+ VIR_DOMAIN_EVENT_SUSPENDED_IOERROR);
+
+ if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
+ VIR_WARN("Unable to save status on vm %s after IO error",
vm->def->name);
+ }
+ virDomainObjUnlock(vm);
+
+ if (ioErrorEvent || ioErrorEvent2 || lifecycleEvent) {
+ qemuDriverLock(driver);
+ if (ioErrorEvent)
+ qemuDomainEventQueue(driver, ioErrorEvent);
+ if (ioErrorEvent2)
+ qemuDomainEventQueue(driver, ioErrorEvent2);
+ if (lifecycleEvent)
+ qemuDomainEventQueue(driver, lifecycleEvent);
+ qemuDriverUnlock(driver);
+ }
+
+ return 0;
+}
+
+
+static int
+qemuProcessHandleGraphics(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm,
+ int phase,
+ int localFamily,
+ const char *localNode,
+ const char *localService,
+ int remoteFamily,
+ const char *remoteNode,
+ const char *remoteService,
+ const char *authScheme,
+ const char *x509dname,
+ const char *saslUsername)
+{
+ struct qemud_driver *driver = qemu_driver;
+ virDomainEventPtr event;
+ virDomainEventGraphicsAddressPtr localAddr = NULL;
+ virDomainEventGraphicsAddressPtr remoteAddr = NULL;
+ virDomainEventGraphicsSubjectPtr subject = NULL;
+ int i;
+
+ virDomainObjLock(vm);
+
+ if (VIR_ALLOC(localAddr) < 0)
+ goto no_memory;
+ localAddr->family = localFamily;
+ if (!(localAddr->service = strdup(localService)) ||
+ !(localAddr->node = strdup(localNode)))
+ goto no_memory;
+
+ if (VIR_ALLOC(remoteAddr) < 0)
+ goto no_memory;
+ remoteAddr->family = remoteFamily;
+ if (!(remoteAddr->service = strdup(remoteService)) ||
+ !(remoteAddr->node = strdup(remoteNode)))
+ goto no_memory;
+
+ if (VIR_ALLOC(subject) < 0)
+ goto no_memory;
+ if (x509dname) {
+ if (VIR_REALLOC_N(subject->identities, subject->nidentity+1) < 0)
+ goto no_memory;
+ if (!(subject->identities[subject->nidentity].type =
strdup("x509dname")) ||
+ !(subject->identities[subject->nidentity].name = strdup(x509dname)))
+ goto no_memory;
+ subject->nidentity++;
+ }
+ if (saslUsername) {
+ if (VIR_REALLOC_N(subject->identities, subject->nidentity+1) < 0)
+ goto no_memory;
+ if (!(subject->identities[subject->nidentity].type =
strdup("saslUsername")) ||
+ !(subject->identities[subject->nidentity].name =
strdup(saslUsername)))
+ goto no_memory;
+ subject->nidentity++;
+ }
+
+ event = virDomainEventGraphicsNewFromObj(vm, phase, localAddr, remoteAddr,
authScheme, subject);
+ virDomainObjUnlock(vm);
+
+ if (event) {
+ qemuDriverLock(driver);
+ qemuDomainEventQueue(driver, event);
+ qemuDriverUnlock(driver);
+ }
+
+ return 0;
+
+no_memory:
+ virReportOOMError();
+ if (localAddr) {
+ VIR_FREE(localAddr->service);
+ VIR_FREE(localAddr->node);
+ VIR_FREE(localAddr);
+ }
+ if (remoteAddr) {
+ VIR_FREE(remoteAddr->service);
+ VIR_FREE(remoteAddr->node);
+ VIR_FREE(remoteAddr);
+ }
+ if (subject) {
+ for (i = 0 ; i < subject->nidentity ; i++) {
+ VIR_FREE(subject->identities[i].type);
+ VIR_FREE(subject->identities[i].name);
+ }
+ VIR_FREE(subject->identities);
+ VIR_FREE(subject);
+ }
+
+ return -1;
+}
+
+
+static void qemuProcessHandleMonitorDestroy(qemuMonitorPtr mon,
+ virDomainObjPtr vm)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ if (priv->mon == mon)
+ priv->mon = NULL;
+ virDomainObjUnref(vm);
+}
+
+static qemuMonitorCallbacks monitorCallbacks = {
+ .destroy = qemuProcessHandleMonitorDestroy,
+ .eofNotify = qemuProcessHandleMonitorEOF,
+ .diskSecretLookup = qemuProcessFindVolumeQcowPassphrase,
+ .domainShutdown = qemuProcessHandleShutdown,
+ .domainStop = qemuProcessHandleStop,
+ .domainReset = qemuProcessHandleReset,
+ .domainRTCChange = qemuProcessHandleRTCChange,
+ .domainWatchdog = qemuProcessHandleWatchdog,
+ .domainIOError = qemuProcessHandleIOError,
+ .domainGraphics = qemuProcessHandleGraphics,
+};
+
+static int
+qemuConnectMonitor(struct qemud_driver *driver, virDomainObjPtr vm)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ int ret = -1;
+
+ if (virSecurityManagerSetSocketLabel(driver->securityManager, vm) < 0) {
+ VIR_ERROR(_("Failed to set security context for monitor for %s"),
+ vm->def->name);
+ goto error;
+ }
+
+ /* Hold an extra reference because we can't allow 'vm' to be
+ * deleted while the monitor is active */
+ virDomainObjRef(vm);
+
+ priv->mon = qemuMonitorOpen(vm,
+ priv->monConfig,
+ priv->monJSON,
+ &monitorCallbacks);
+
+ if (priv->mon == NULL)
+ virDomainObjUnref(vm);
+
+ if (virSecurityManagerClearSocketLabel(driver->securityManager, vm) < 0) {
+ VIR_ERROR(_("Failed to clear security context for monitor for %s"),
+ vm->def->name);
+ goto error;
+ }
+
+ if (priv->mon == NULL) {
+ VIR_INFO("Failed to connect monitor for %s", vm->def->name);
+ goto error;
+ }
+
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ ret = qemuMonitorSetCapabilities(priv->mon);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+error:
+
+ return ret;
+}
+
+static int
+qemuProcessLogFD(struct qemud_driver *driver, const char* name, bool append)
+{
+ char *logfile;
+ mode_t logmode;
+ int fd = -1;
+
+ if (virAsprintf(&logfile, "%s/%s.log", driver->logDir, name) < 0)
{
+ virReportOOMError();
+ return -1;
+ }
+
+ logmode = O_CREAT | O_WRONLY;
+ /* Only logrotate files in /var/log, so only append if running privileged */
+ if (driver->privileged || append)
+ logmode |= O_APPEND;
+ else
+ logmode |= O_TRUNC;
+
+ if ((fd = open(logfile, logmode, S_IRUSR | S_IWUSR)) < 0) {
+ virReportSystemError(errno,
+ _("failed to create logfile %s"),
+ logfile);
+ VIR_FREE(logfile);
+ return -1;
+ }
+ VIR_FREE(logfile);
+ if (virSetCloseExec(fd) < 0) {
+ virReportSystemError(errno, "%s",
+ _("Unable to set VM logfile close-on-exec
flag"));
+ VIR_FORCE_CLOSE(fd);
+ return -1;
+ }
+ return fd;
+}
+
+
+static int
+qemuProcessLogReadFD(const char* logDir, const char* name, off_t pos)
+{
+ char *logfile;
+ mode_t logmode = O_RDONLY;
+ int fd = -1;
+
+ if (virAsprintf(&logfile, "%s/%s.log", logDir, name) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to build logfile name %s/%s.log"),
+ logDir, name);
+ return -1;
+ }
+
+ if ((fd = open(logfile, logmode)) < 0) {
+ virReportSystemError(errno,
+ _("failed to create logfile %s"),
+ logfile);
+ VIR_FREE(logfile);
+ return -1;
+ }
+ if (virSetCloseExec(fd) < 0) {
+ virReportSystemError(errno, "%s",
+ _("Unable to set VM logfile close-on-exec
flag"));
+ VIR_FORCE_CLOSE(fd);
+ VIR_FREE(logfile);
+ return -1;
+ }
+ if (pos < 0 || lseek(fd, pos, SEEK_SET) < 0) {
+ virReportSystemError(pos < 0 ? 0 : errno,
+ _("Unable to seek to %lld in %s"),
+ (long long) pos, logfile);
+ VIR_FORCE_CLOSE(fd);
+ }
+ VIR_FREE(logfile);
+ return fd;
+}
+
+
+typedef int qemuProcessLogHandleOutput(virDomainObjPtr vm,
+ const char *output,
+ int fd);
+
+/*
+ * Returns -1 for error, 0 on success
+ */
+static int
+qemuProcessReadLogOutput(virDomainObjPtr vm,
+ int fd,
+ char *buf,
+ size_t buflen,
+ qemuProcessLogHandleOutput func,
+ const char *what,
+ int timeout)
+{
+ int retries = (timeout*10);
+ int got = 0;
+ buf[0] = '\0';
+
+ while (retries) {
+ ssize_t func_ret, ret;
+ int isdead = 0;
+
+ func_ret = func(vm, buf, fd);
+
+ if (kill(vm->pid, 0) == -1 && errno == ESRCH)
+ isdead = 1;
+
+ /* Any failures should be detected before we read the log, so we
+ * always have something useful to report on failure. */
+ ret = saferead(fd, buf+got, buflen-got-1);
+ if (ret < 0) {
+ virReportSystemError(errno,
+ _("Failure while reading %s log output"),
+ what);
+ return -1;
+ }
+
+ got += ret;
+ buf[got] = '\0';
+ if (got == buflen-1) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Out of space while reading %s log output: %s"),
+ what, buf);
+ return -1;
+ }
+
+ if (isdead) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Process exited while reading %s log output:
%s"),
+ what, buf);
+ return -1;
+ }
+
+ if (func_ret <= 0)
+ return func_ret;
+
+ usleep(100*1000);
+ retries--;
+ }
+
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Timed out while reading %s log output: %s"),
+ what, buf);
+ return -1;
+}
+
+
+/*
+ * Look at a chunk of data from the QEMU stdout logs and try to
+ * find a TTY device, as indicated by a line like
+ *
+ * char device redirected to /dev/pts/3
+ *
+ * Returns -1 for error, 0 success, 1 continue reading
+ */
+static int
+qemuProcessExtractTTYPath(const char *haystack,
+ size_t *offset,
+ char **path)
+{
+ static const char needle[] = "char device redirected to";
+ char *tmp, *dev;
+
+ VIR_FREE(*path);
+ /* First look for our magic string */
+ if (!(tmp = strstr(haystack + *offset, needle))) {
+ return 1;
+ }
+ tmp += sizeof(needle);
+ dev = tmp;
+
+ /*
+ * And look for first whitespace character and nul terminate
+ * to mark end of the pty path
+ */
+ while (*tmp) {
+ if (c_isspace(*tmp)) {
+ *path = strndup(dev, tmp-dev);
+ if (*path == NULL) {
+ virReportOOMError();
+ return -1;
+ }
+
+ /* ... now further update offset till we get EOL */
+ *offset = tmp - haystack;
+ return 0;
+ }
+ tmp++;
+ }
+
+ /*
+ * We found a path, but didn't find any whitespace,
+ * so it must be still incomplete - we should at
+ * least see a \n - indicate that we want to carry
+ * on trying again
+ */
+ return 1;
+}
+
+static int
+qemuProcessFindCharDevicePTYsMonitor(virDomainObjPtr vm,
+ virHashTablePtr paths)
+{
+ int i;
+
+#define LOOKUP_PTYS(array, arraylen, idprefix) \
+ for (i = 0 ; i < (arraylen) ; i++) { \
+ virDomainChrDefPtr chr = (array)[i]; \
+ if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) { \
+ char id[16]; \
+ \
+ if (snprintf(id, sizeof(id), idprefix "%i", i) >= sizeof(id)) \
+ return -1; \
+ \
+ const char *path = (const char *) virHashLookup(paths, id); \
+ if (path == NULL) { \
+ if (chr->source.data.file.path == NULL) { \
+ /* neither the log output nor 'info chardev' had a */ \
+ /* pty path for this chardev, report an error */ \
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, \
+ _("no assigned pty for device %s"), id); \
+ return -1; \
+ } else { \
+ /* 'info chardev' had no pty path for this chardev, */\
+ /* but the log output had, so we're fine */ \
+ continue; \
+ } \
+ } \
+ \
+ VIR_FREE(chr->source.data.file.path); \
+ chr->source.data.file.path = strdup(path); \
+ \
+ if (chr->source.data.file.path == NULL) { \
+ virReportOOMError(); \
+ return -1; \
+ } \
+ } \
+ }
+
+ LOOKUP_PTYS(vm->def->serials, vm->def->nserials,
"serial");
+ LOOKUP_PTYS(vm->def->parallels, vm->def->nparallels,
"parallel");
+ LOOKUP_PTYS(vm->def->channels, vm->def->nchannels,
"channel");
+ if (vm->def->console)
+ LOOKUP_PTYS(&vm->def->console, 1, "console");
+#undef LOOKUP_PTYS
+
+ return 0;
+}
+
+static int
+qemuProcessFindCharDevicePTYs(virDomainObjPtr vm,
+ const char *output,
+ int fd ATTRIBUTE_UNUSED)
+{
+ size_t offset = 0;
+ int ret, i;
+
+ /* The order in which QEMU prints out the PTY paths is
+ the order in which it procsses its serial and parallel
+ device args. This code must match that ordering.... */
+
+ /* first comes the serial devices */
+ for (i = 0 ; i < vm->def->nserials ; i++) {
+ virDomainChrDefPtr chr = vm->def->serials[i];
+ if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) {
+ if ((ret = qemuProcessExtractTTYPath(output, &offset,
+ &chr->source.data.file.path)) !=
0)
+ return ret;
+ }
+ }
+
+ /* then the parallel devices */
+ for (i = 0 ; i < vm->def->nparallels ; i++) {
+ virDomainChrDefPtr chr = vm->def->parallels[i];
+ if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) {
+ if ((ret = qemuProcessExtractTTYPath(output, &offset,
+ &chr->source.data.file.path)) !=
0)
+ return ret;
+ }
+ }
+
+ /* then the channel devices */
+ for (i = 0 ; i < vm->def->nchannels ; i++) {
+ virDomainChrDefPtr chr = vm->def->channels[i];
+ if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) {
+ if ((ret = qemuProcessExtractTTYPath(output, &offset,
+ &chr->source.data.file.path)) !=
0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void qemuProcessFreePtyPath(void *payload, const char *name ATTRIBUTE_UNUSED)
+{
+ VIR_FREE(payload);
+}
+
+static void
+qemuProcessReadLogFD(int logfd, char *buf, int maxlen, int off)
+{
+ int ret;
+ char *tmpbuf = buf + off;
+
+ ret = saferead(logfd, tmpbuf, maxlen - off - 1);
+ if (ret < 0) {
+ ret = 0;
+ }
+
+ tmpbuf[ret] = '\0';
+}
+
+static int
+qemuProcessWaitForMonitor(struct qemud_driver* driver,
+ virDomainObjPtr vm, off_t pos)
+{
+ char buf[4096] = ""; /* Plenty of space to get startup greeting */
+ int logfd;
+ int ret = -1;
+ virHashTablePtr paths = NULL;
+
+ if ((logfd = qemuProcessLogReadFD(driver->logDir, vm->def->name, pos)) <
0)
+ return -1;
+
+ if (qemuProcessReadLogOutput(vm, logfd, buf, sizeof(buf),
+ qemuProcessFindCharDevicePTYs,
+ "console", 30) < 0)
+ goto closelog;
+
+ VIR_DEBUG("Connect monitor to %p '%s'", vm, vm->def->name);
+ if (qemuConnectMonitor(driver, vm) < 0) {
+ goto cleanup;
+ }
+
+ /* Try to get the pty path mappings again via the monitor. This is much more
+ * reliable if it's available.
+ * Note that the monitor itself can be on a pty, so we still need to try the
+ * log output method. */
+ paths = virHashCreate(0);
+ if (paths == NULL) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ ret = qemuMonitorGetPtyPaths(priv->mon, paths);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ VIR_DEBUG("qemuMonitorGetPtyPaths returned %i", ret);
+ if (ret == 0)
+ ret = qemuProcessFindCharDevicePTYsMonitor(vm, paths);
+
+cleanup:
+ if (paths) {
+ virHashFree(paths, qemuProcessFreePtyPath);
+ }
+
+ if (kill(vm->pid, 0) == -1 && errno == ESRCH) {
+ /* VM is dead, any other error raised in the interim is probably
+ * not as important as the qemu cmdline output */
+ qemuProcessReadLogFD(logfd, buf, sizeof(buf), strlen(buf));
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("process exited while connecting to monitor: %s"),
+ buf);
+ ret = -1;
+ }
+
+closelog:
+ if (VIR_CLOSE(logfd) < 0) {
+ char ebuf[4096];
+ VIR_WARN("Unable to close logfile: %s",
+ virStrerror(errno, ebuf, sizeof ebuf));
+ }
+
+ return ret;
+}
+
+static int
+qemuProcessDetectVcpuPIDs(struct qemud_driver *driver,
+ virDomainObjPtr vm)
+{
+ pid_t *cpupids = NULL;
+ int ncpupids;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+
+ if (vm->def->virtType != VIR_DOMAIN_VIRT_KVM) {
+ priv->nvcpupids = 1;
+ if (VIR_ALLOC_N(priv->vcpupids, priv->nvcpupids) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+ priv->vcpupids[0] = vm->pid;
+ return 0;
+ }
+
+ /* What follows is now all KVM specific */
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if ((ncpupids = qemuMonitorGetCPUInfo(priv->mon, &cpupids)) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ return -1;
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ /* Treat failure to get VCPU<->PID mapping as non-fatal */
+ if (ncpupids == 0)
+ return 0;
+
+ if (ncpupids != vm->def->vcpus) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("got wrong number of vCPU pids from QEMU monitor. "
+ "got %d, wanted %d"),
+ ncpupids, vm->def->vcpus);
+ VIR_FREE(cpupids);
+ return -1;
+ }
+
+ priv->nvcpupids = ncpupids;
+ priv->vcpupids = cpupids;
+ return 0;
+}
+
+/*
+ * To be run between fork/exec of QEMU only
+ */
+static int
+qemuProcessInitCpuAffinity(virDomainObjPtr vm)
+{
+ int i, hostcpus, maxcpu = QEMUD_CPUMASK_LEN;
+ virNodeInfo nodeinfo;
+ unsigned char *cpumap;
+ int cpumaplen;
+
+ DEBUG0("Setting CPU affinity");
+
+ if (nodeGetInfo(NULL, &nodeinfo) < 0)
+ return -1;
+
+ /* setaffinity fails if you set bits for CPUs which
+ * aren't present, so we have to limit ourselves */
+ hostcpus = VIR_NODEINFO_MAXCPUS(nodeinfo);
+ if (maxcpu > hostcpus)
+ maxcpu = hostcpus;
+
+ cpumaplen = VIR_CPU_MAPLEN(maxcpu);
+ if (VIR_ALLOC_N(cpumap, cpumaplen) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+
+ if (vm->def->cpumask) {
+ /* XXX why don't we keep 'cpumask' in the libvirt cpumap
+ * format to start with ?!?! */
+ for (i = 0 ; i < maxcpu && i < vm->def->cpumasklen ; i++)
+ if (vm->def->cpumask[i])
+ VIR_USE_CPU(cpumap, i);
+ } else {
+ /* You may think this is redundant, but we can't assume libvirtd
+ * itself is running on all pCPUs, so we need to explicitly set
+ * the spawned QEMU instance to all pCPUs if no map is given in
+ * its config file */
+ for (i = 0 ; i < maxcpu ; i++)
+ VIR_USE_CPU(cpumap, i);
+ }
+
+ /* We are pressuming we are running between fork/exec of QEMU
+ * so use '0' to indicate our own process ID. No threads are
+ * running at this point
+ */
+ if (virProcessInfoSetAffinity(0, /* Self */
+ cpumap, cpumaplen, maxcpu) < 0) {
+ VIR_FREE(cpumap);
+ return -1;
+ }
+ VIR_FREE(cpumap);
+
+ return 0;
+}
+
+
+static int
+qemuProcessInitPasswords(virConnectPtr conn,
+ struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ unsigned long long qemuCmdFlags)
+{
+ int ret = 0;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+
+ if (vm->def->ngraphics == 1) {
+ if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
+ ret = qemuDomainChangeGraphicsPasswords(driver, vm,
+ VIR_DOMAIN_GRAPHICS_TYPE_VNC,
+
&vm->def->graphics[0]->data.vnc.auth,
+ driver->vncPassword);
+ } else if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE)
{
+ ret = qemuDomainChangeGraphicsPasswords(driver, vm,
+ VIR_DOMAIN_GRAPHICS_TYPE_SPICE,
+
&vm->def->graphics[0]->data.spice.auth,
+ driver->spicePassword);
+ }
+ }
+
+ if (ret < 0)
+ goto cleanup;
+
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ int i;
+
+ for (i = 0 ; i < vm->def->ndisks ; i++) {
+ char *secret;
+ size_t secretLen;
+
+ if (!vm->def->disks[i]->encryption ||
+ !vm->def->disks[i]->src)
+ continue;
+
+ if (qemuProcessGetVolumeQcowPassphrase(conn,
+ vm->def->disks[i],
+ &secret, &secretLen) < 0)
+ goto cleanup;
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ ret = qemuMonitorSetDrivePassphrase(priv->mon,
+ vm->def->disks[i]->info.alias,
+ secret);
+ VIR_FREE(secret);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ if (ret < 0)
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ return ret;
+}
+
+
+#define QEMU_PCI_VENDOR_INTEL 0x8086
+#define QEMU_PCI_VENDOR_LSI_LOGIC 0x1000
+#define QEMU_PCI_VENDOR_REDHAT 0x1af4
+#define QEMU_PCI_VENDOR_CIRRUS 0x1013
+#define QEMU_PCI_VENDOR_REALTEK 0x10ec
+#define QEMU_PCI_VENDOR_AMD 0x1022
+#define QEMU_PCI_VENDOR_ENSONIQ 0x1274
+#define QEMU_PCI_VENDOR_VMWARE 0x15ad
+#define QEMU_PCI_VENDOR_QEMU 0x1234
+
+#define QEMU_PCI_PRODUCT_DISK_VIRTIO 0x1001
+
+#define QEMU_PCI_PRODUCT_BALLOON_VIRTIO 0x1002
+
+#define QEMU_PCI_PRODUCT_NIC_NE2K 0x8029
+#define QEMU_PCI_PRODUCT_NIC_PCNET 0x2000
+#define QEMU_PCI_PRODUCT_NIC_RTL8139 0x8139
+#define QEMU_PCI_PRODUCT_NIC_E1000 0x100E
+#define QEMU_PCI_PRODUCT_NIC_VIRTIO 0x1000
+
+#define QEMU_PCI_PRODUCT_VGA_CIRRUS 0x00b8
+#define QEMU_PCI_PRODUCT_VGA_VMWARE 0x0405
+#define QEMU_PCI_PRODUCT_VGA_STDVGA 0x1111
+
+#define QEMU_PCI_PRODUCT_AUDIO_AC97 0x2415
+#define QEMU_PCI_PRODUCT_AUDIO_ES1370 0x5000
+
+#define QEMU_PCI_PRODUCT_CONTROLLER_PIIX 0x7010
+#define QEMU_PCI_PRODUCT_CONTROLLER_LSI 0x0012
+
+#define QEMU_PCI_PRODUCT_WATCHDOG_I63000ESB 0x25ab
+
+static int
+qemuProcessAssignNextPCIAddress(virDomainDeviceInfo *info,
+ int vendor,
+ int product,
+ qemuMonitorPCIAddress *addrs,
+ int naddrs)
+{
+ int found = 0;
+ int i;
+
+ VIR_DEBUG("Look for %x:%x out of %d", vendor, product, naddrs);
+
+ for (i = 0 ; (i < naddrs) && !found; i++) {
+ VIR_DEBUG("Maybe %x:%x", addrs[i].vendor, addrs[i].product);
+ if (addrs[i].vendor == vendor &&
+ addrs[i].product == product) {
+ VIR_DEBUG("Match %d", i);
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ return -1;
+ }
+
+ /* Blank it out so this device isn't matched again */
+ addrs[i].vendor = 0;
+ addrs[i].product = 0;
+
+ if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
+ info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+
+ if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
+ info->addr.pci.domain = addrs[i].addr.domain;
+ info->addr.pci.bus = addrs[i].addr.bus;
+ info->addr.pci.slot = addrs[i].addr.slot;
+ info->addr.pci.function = addrs[i].addr.function;
+ }
+
+ return 0;
+}
+
+static int
+qemuProcessGetPCIDiskVendorProduct(virDomainDiskDefPtr def,
+ unsigned *vendor,
+ unsigned *product)
+{
+ switch (def->bus) {
+ case VIR_DOMAIN_DISK_BUS_VIRTIO:
+ *vendor = QEMU_PCI_VENDOR_REDHAT;
+ *product = QEMU_PCI_PRODUCT_DISK_VIRTIO;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+qemuProcessGetPCINetVendorProduct(virDomainNetDefPtr def,
+ unsigned *vendor,
+ unsigned *product)
+{
+ if (!def->model)
+ return -1;
+
+ if (STREQ(def->model, "ne2k_pci")) {
+ *vendor = QEMU_PCI_VENDOR_REALTEK;
+ *product = QEMU_PCI_PRODUCT_NIC_NE2K;
+ } else if (STREQ(def->model, "pcnet")) {
+ *vendor = QEMU_PCI_VENDOR_AMD;
+ *product = QEMU_PCI_PRODUCT_NIC_PCNET;
+ } else if (STREQ(def->model, "rtl8139")) {
+ *vendor = QEMU_PCI_VENDOR_REALTEK;
+ *product = QEMU_PCI_PRODUCT_NIC_RTL8139;
+ } else if (STREQ(def->model, "e1000")) {
+ *vendor = QEMU_PCI_VENDOR_INTEL;
+ *product = QEMU_PCI_PRODUCT_NIC_E1000;
+ } else if (STREQ(def->model, "virtio")) {
+ *vendor = QEMU_PCI_VENDOR_REDHAT;
+ *product = QEMU_PCI_PRODUCT_NIC_VIRTIO;
+ } else {
+ VIR_INFO("Unexpected NIC model %s, cannot get PCI address",
+ def->model);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+qemuProcessGetPCIControllerVendorProduct(virDomainControllerDefPtr def,
+ unsigned *vendor,
+ unsigned *product)
+{
+ switch (def->type) {
+ case VIR_DOMAIN_CONTROLLER_TYPE_SCSI:
+ *vendor = QEMU_PCI_VENDOR_LSI_LOGIC;
+ *product = QEMU_PCI_PRODUCT_CONTROLLER_LSI;
+ break;
+
+ case VIR_DOMAIN_CONTROLLER_TYPE_FDC:
+ /* XXX we could put in the ISA bridge address, but
+ that's not technically the FDC's address */
+ return -1;
+
+ case VIR_DOMAIN_CONTROLLER_TYPE_IDE:
+ *vendor = QEMU_PCI_VENDOR_INTEL;
+ *product = QEMU_PCI_PRODUCT_CONTROLLER_PIIX;
+ break;
+
+ default:
+ VIR_INFO("Unexpected controller type %s, cannot get PCI address",
+ virDomainControllerTypeToString(def->type));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+qemuProcessGetPCIVideoVendorProduct(virDomainVideoDefPtr def,
+ unsigned *vendor,
+ unsigned *product)
+{
+ switch (def->type) {
+ case VIR_DOMAIN_VIDEO_TYPE_CIRRUS:
+ *vendor = QEMU_PCI_VENDOR_CIRRUS;
+ *product = QEMU_PCI_PRODUCT_VGA_CIRRUS;
+ break;
+
+ case VIR_DOMAIN_VIDEO_TYPE_VGA:
+ *vendor = QEMU_PCI_VENDOR_QEMU;
+ *product = QEMU_PCI_PRODUCT_VGA_STDVGA;
+ break;
+
+ case VIR_DOMAIN_VIDEO_TYPE_VMVGA:
+ *vendor = QEMU_PCI_VENDOR_VMWARE;
+ *product = QEMU_PCI_PRODUCT_VGA_VMWARE;
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static int
+qemuProcessGetPCISoundVendorProduct(virDomainSoundDefPtr def,
+ unsigned *vendor,
+ unsigned *product)
+{
+ switch (def->model) {
+ case VIR_DOMAIN_SOUND_MODEL_ES1370:
+ *vendor = QEMU_PCI_VENDOR_ENSONIQ;
+ *product = QEMU_PCI_PRODUCT_AUDIO_ES1370;
+ break;
+
+ case VIR_DOMAIN_SOUND_MODEL_AC97:
+ *vendor = QEMU_PCI_VENDOR_INTEL;
+ *product = QEMU_PCI_PRODUCT_AUDIO_AC97;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+qemuProcessGetPCIWatchdogVendorProduct(virDomainWatchdogDefPtr def,
+ unsigned *vendor,
+ unsigned *product)
+{
+ switch (def->model) {
+ case VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB:
+ *vendor = QEMU_PCI_VENDOR_INTEL;
+ *product = QEMU_PCI_PRODUCT_WATCHDOG_I63000ESB;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+qemuProcessGetPCIMemballoonVendorProduct(virDomainMemballoonDefPtr def,
+ unsigned *vendor,
+ unsigned *product)
+{
+ switch (def->model) {
+ case VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO:
+ *vendor = QEMU_PCI_VENDOR_REDHAT;
+ *product = QEMU_PCI_PRODUCT_BALLOON_VIRTIO;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * This entire method assumes that PCI devices in 'info pci'
+ * match ordering of devices specified on the command line
+ * wrt to devices of matching vendor+product
+ *
+ * XXXX this might not be a valid assumption if we assign
+ * some static addrs on CLI. Have to check that...
+ */
+static int
+qemuProcessDetectPCIAddresses(virDomainObjPtr vm,
+ qemuMonitorPCIAddress *addrs,
+ int naddrs)
+{
+ unsigned int vendor = 0, product = 0;
+ int i;
+
+ /* XXX should all these vendor/product IDs be kept in the
+ * actual device data structure instead ?
+ */
+
+ for (i = 0 ; i < vm->def->ndisks ; i++) {
+ if (qemuProcessGetPCIDiskVendorProduct(vm->def->disks[i], &vendor,
&product) < 0)
+ continue;
+
+ if (qemuProcessAssignNextPCIAddress(&(vm->def->disks[i]->info),
+ vendor, product,
+ addrs, naddrs) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot find PCI address for VirtIO disk %s"),
+ vm->def->disks[i]->dst);
+ return -1;
+ }
+ }
+
+ for (i = 0 ; i < vm->def->nnets ; i++) {
+ if (qemuProcessGetPCINetVendorProduct(vm->def->nets[i], &vendor,
&product) < 0)
+ continue;
+
+ if (qemuProcessAssignNextPCIAddress(&(vm->def->nets[i]->info),
+ vendor, product,
+ addrs, naddrs) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot find PCI address for %s NIC"),
+ vm->def->nets[i]->model);
+ return -1;
+ }
+ }
+
+ for (i = 0 ; i < vm->def->ncontrollers ; i++) {
+ if (qemuProcessGetPCIControllerVendorProduct(vm->def->controllers[i],
&vendor, &product) < 0)
+ continue;
+
+ if
(qemuProcessAssignNextPCIAddress(&(vm->def->controllers[i]->info),
+ vendor, product,
+ addrs, naddrs) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot find PCI address for controller %s"),
+
virDomainControllerTypeToString(vm->def->controllers[i]->type));
+ return -1;
+ }
+ }
+
+ for (i = 0 ; i < vm->def->nvideos ; i++) {
+ if (qemuProcessGetPCIVideoVendorProduct(vm->def->videos[i], &vendor,
&product) < 0)
+ continue;
+
+ if (qemuProcessAssignNextPCIAddress(&(vm->def->videos[i]->info),
+ vendor, product,
+ addrs, naddrs) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot find PCI address for video adapter %s"),
+
virDomainVideoTypeToString(vm->def->videos[i]->type));
+ return -1;
+ }
+ }
+
+ for (i = 0 ; i < vm->def->nsounds ; i++) {
+ if (qemuProcessGetPCISoundVendorProduct(vm->def->sounds[i], &vendor,
&product) < 0)
+ continue;
+
+ if (qemuProcessAssignNextPCIAddress(&(vm->def->sounds[i]->info),
+ vendor, product,
+ addrs, naddrs) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot find PCI address for sound adapter %s"),
+
virDomainSoundModelTypeToString(vm->def->sounds[i]->model));
+ return -1;
+ }
+ }
+
+
+ if (vm->def->watchdog &&
+ qemuProcessGetPCIWatchdogVendorProduct(vm->def->watchdog, &vendor,
&product) == 0) {
+ if (qemuProcessAssignNextPCIAddress(&(vm->def->watchdog->info),
+ vendor, product,
+ addrs, naddrs) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot find PCI address for watchdog %s"),
+
virDomainWatchdogModelTypeToString(vm->def->watchdog->model));
+ return -1;
+ }
+ }
+
+ if (vm->def->memballoon &&
+ qemuProcessGetPCIMemballoonVendorProduct(vm->def->memballoon, &vendor,
&product) == 0) {
+ if (qemuProcessAssignNextPCIAddress(&(vm->def->memballoon->info),
+ vendor, product,
+ addrs, naddrs) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot find PCI address for balloon %s"),
+
virDomainMemballoonModelTypeToString(vm->def->memballoon->model));
+ return -1;
+ }
+ }
+
+ /* XXX console (virtio) */
+
+
+ /* ... and now things we don't have in our xml */
+
+ /* XXX USB controller ? */
+
+ /* XXX what about other PCI devices (ie bridges) */
+
+ return 0;
+}
+
+static int
+qemuProcessInitPCIAddresses(struct qemud_driver *driver,
+ virDomainObjPtr vm)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ int naddrs;
+ int ret;
+ qemuMonitorPCIAddress *addrs = NULL;
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ naddrs = qemuMonitorGetAllPCIAddresses(priv->mon,
+ &addrs);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ ret = qemuProcessDetectPCIAddresses(vm, addrs, naddrs);
+
+ VIR_FREE(addrs);
+
+ return ret;
+}
+
+
+static int qemuProcessNextFreePort(struct qemud_driver *driver,
+ int startPort)
+{
+ int i;
+
+ for (i = startPort ; i < QEMU_VNC_PORT_MAX; i++) {
+ int fd;
+ int reuse = 1;
+ struct sockaddr_in addr;
+ bool used = false;
+
+ if (virBitmapGetBit(driver->reservedVNCPorts,
+ i - QEMU_VNC_PORT_MIN, &used) < 0)
+ VIR_DEBUG("virBitmapGetBit failed on bit %d", i -
QEMU_VNC_PORT_MIN);
+
+ if (used)
+ continue;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(i);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&reuse, sizeof(reuse))
< 0) {
+ VIR_FORCE_CLOSE(fd);
+ break;
+ }
+
+ if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
+ /* Not in use, lets grab it */
+ VIR_FORCE_CLOSE(fd);
+ /* Add port to bitmap of reserved ports */
+ if (virBitmapSetBit(driver->reservedVNCPorts,
+ i - QEMU_VNC_PORT_MIN) < 0) {
+ VIR_DEBUG("virBitmapSetBit failed on bit %d",
+ i - QEMU_VNC_PORT_MIN);
+ }
+ return i;
+ }
+ VIR_FORCE_CLOSE(fd);
+
+ if (errno == EADDRINUSE) {
+ /* In use, try next */
+ continue;
+ }
+ /* Some other bad failure, get out.. */
+ break;
+ }
+ return -1;
+}
+
+
+static void
+qemuProcessReturnPort(struct qemud_driver *driver,
+ int port)
+{
+ if (port < QEMU_VNC_PORT_MIN)
+ return;
+
+ if (virBitmapClearBit(driver->reservedVNCPorts,
+ port - QEMU_VNC_PORT_MIN) < 0)
+ VIR_DEBUG("Could not mark port %d as unused", port);
+}
+
+
+static int
+qemuProcessPrepareChardevDevice(virDomainDefPtr def ATTRIBUTE_UNUSED,
+ virDomainChrDefPtr dev,
+ void *opaque ATTRIBUTE_UNUSED)
+{
+ int fd;
+ if (dev->source.type != VIR_DOMAIN_CHR_TYPE_FILE)
+ return 0;
+
+ if ((fd = open(dev->source.data.file.path,
+ O_CREAT | O_APPEND, S_IRUSR|S_IWUSR)) < 0) {
+ virReportSystemError(errno,
+ _("Unable to pre-create chardev file
'%s'"),
+ dev->source.data.file.path);
+ return -1;
+ }
+
+ VIR_FORCE_CLOSE(fd);
+
+ return 0;
+}
+
+
+struct qemuProcessHookData {
+ virConnectPtr conn;
+ virDomainObjPtr vm;
+ struct qemud_driver *driver;
+};
+
+static int qemuProcessHook(void *data)
+{
+ struct qemuProcessHookData *h = data;
+
+ /* This must take place before exec(), so that all QEMU
+ * memory allocation is on the correct NUMA node
+ */
+ if (qemuAddToCgroup(h->driver, h->vm->def) < 0)
+ return -1;
+
+ /* This must be done after cgroup placement to avoid resetting CPU
+ * affinity */
+ if (qemuProcessInitCpuAffinity(h->vm) < 0)
+ return -1;
+
+ if (virSecurityManagerSetProcessLabel(h->driver->securityManager, h->vm)
< 0)
+ return -1;
+
+ return 0;
+}
+
+
+int
+qemuProcessPrepareMonitorChr(struct qemud_driver *driver,
+ virDomainChrSourceDefPtr monConfig,
+ const char *vm)
+{
+ monConfig->type = VIR_DOMAIN_CHR_TYPE_UNIX;
+ monConfig->data.nix.listen = true;
+
+ if (virAsprintf(&monConfig->data.nix.path, "%s/%s.monitor",
+ driver->libDir, vm) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int qemuProcessStartCPUs(struct qemud_driver *driver, virDomainObjPtr vm, virConnectPtr
conn)
+{
+ int ret;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ ret = qemuMonitorStartCPUs(priv->mon, conn);
+ if (ret == 0) {
+ vm->state = VIR_DOMAIN_RUNNING;
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ return ret;
+}
+
+
+int qemuProcessStopCPUs(struct qemud_driver *driver, virDomainObjPtr vm)
+{
+ int ret;
+ int oldState = vm->state;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+
+ vm->state = VIR_DOMAIN_PAUSED;
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ ret = qemuMonitorStopCPUs(priv->mon);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ if (ret < 0) {
+ vm->state = oldState;
+ }
+ return ret;
+}
+
+
+
+static int
+qemuProcessFiltersInstantiate(virConnectPtr conn,
+ virDomainDefPtr def)
+{
+ int err = 0;
+ int i;
+
+ if (!conn)
+ return 1;
+
+ for (i = 0 ; i < def->nnets ; i++) {
+ virDomainNetDefPtr net = def->nets[i];
+ if ((net->filter) && (net->ifname)) {
+ if (virDomainConfNWFilterInstantiate(conn, net)) {
+ err = 1;
+ break;
+ }
+ }
+ }
+
+ return err;
+}
+
+struct qemuProcessReconnectData {
+ virConnectPtr conn;
+ struct qemud_driver *driver;
+};
+/*
+ * Open an existing VM's monitor, re-detect VCPU threads
+ * and re-reserve the security labels in use
+ */
+static void
+qemuProcessReconnect(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque)
+{
+ virDomainObjPtr obj = payload;
+ struct qemuProcessReconnectData *data = opaque;
+ struct qemud_driver *driver = data->driver;
+ qemuDomainObjPrivatePtr priv;
+ unsigned long long qemuCmdFlags;
+ virConnectPtr conn = data->conn;
+
+ virDomainObjLock(obj);
+
+ VIR_DEBUG("Reconnect monitor to %p '%s'", obj,
obj->def->name);
+
+ priv = obj->privateData;
+
+ /* Hold an extra reference because we can't allow 'vm' to be
+ * deleted if qemuConnectMonitor() failed */
+ virDomainObjRef(obj);
+
+ /* XXX check PID liveliness & EXE path */
+ if (qemuConnectMonitor(driver, obj) < 0)
+ goto error;
+
+ if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) {
+ goto error;
+ }
+
+ /* XXX we should be persisting the original flags in the XML
+ * not re-detecting them, since the binary may have changed
+ * since launch time */
+ if (qemuCapsExtractVersionInfo(obj->def->emulator,
+ NULL,
+ &qemuCmdFlags) >= 0 &&
+ (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+ priv->persistentAddrs = 1;
+
+ if (!(priv->pciaddrs = qemuDomainPCIAddressSetCreate(obj->def)) ||
+ qemuAssignDevicePCISlots(obj->def, priv->pciaddrs) < 0)
+ goto error;
+ }
+
+ if (virSecurityManagerReserveLabel(driver->securityManager, obj) < 0)
+ goto error;
+
+ if (qemuProcessFiltersInstantiate(conn, obj->def))
+ goto error;
+
+ if (obj->def->id >= driver->nextvmid)
+ driver->nextvmid = obj->def->id + 1;
+
+ if (virDomainObjUnref(obj) > 0)
+ virDomainObjUnlock(obj);
+ return;
+
+error:
+ if (!virDomainObjIsActive(obj)) {
+ if (virDomainObjUnref(obj) > 0)
+ virDomainObjUnlock(obj);
+ return;
+ }
+
+ if (virDomainObjUnref(obj) > 0) {
+ /* We can't get the monitor back, so must kill the VM
+ * to remove danger of it ending up running twice if
+ * user tries to start it again later */
+ qemudShutdownVMDaemon(driver, obj, 0);
+ if (!obj->persistent)
+ virDomainRemoveInactive(&driver->domains, obj);
+ else
+ virDomainObjUnlock(obj);
+ }
+}
+
+/**
+ * qemuProcessReconnectAll
+ *
+ * Try to re-open the resources for live VMs that we care
+ * about.
+ */
+void
+qemuProcessReconnectAll(virConnectPtr conn, struct qemud_driver *driver)
+{
+ struct qemuProcessReconnectData data = {conn, driver};
+ virHashForEach(driver->domains.objs, qemuProcessReconnect, &data);
+}
+
+int qemuProcessStart(virConnectPtr conn,
+ struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ const char *migrateFrom,
+ bool start_paused,
+ int stdin_fd,
+ const char *stdin_path,
+ enum virVMOperationType vmop)
+{
+ int ret;
+ unsigned long long qemuCmdFlags;
+ off_t pos = -1;
+ char ebuf[1024];
+ char *pidfile = NULL;
+ int logfile = -1;
+ char *timestamp;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ virCommandPtr cmd = NULL;
+ struct qemuProcessHookData hookData;
+
+ hookData.conn = conn;
+ hookData.vm = vm;
+ hookData.driver = driver;
+
+ DEBUG0("Beginning VM startup process");
+
+ if (virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("VM is already active"));
+ return -1;
+ }
+
+ /* Do this upfront, so any part of the startup process can add
+ * runtime state to vm->def that won't be persisted. This let's us
+ * report implicit runtime defaults in the XML, like vnc listen/socket
+ */
+ DEBUG0("Setting current domain def as transient");
+ if (virDomainObjSetDefTransient(driver->caps, vm, true) < 0)
+ goto cleanup;
+
+ /* Must be run before security labelling */
+ DEBUG0("Preparing host devices");
+ if (qemuPrepareHostDevices(driver, vm->def) < 0)
+ goto cleanup;
+
+ DEBUG0("Preparing chr devices");
+ if (virDomainChrDefForeach(vm->def,
+ true,
+ qemuProcessPrepareChardevDevice,
+ NULL) < 0)
+ goto cleanup;
+
+ /* If you are using a SecurityDriver with dynamic labelling,
+ then generate a security label for isolation */
+ DEBUG0("Generating domain security label (if required)");
+ if (virSecurityManagerGenLabel(driver->securityManager, vm) < 0) {
+ qemuDomainSecurityLabelAudit(vm, false);
+ goto cleanup;
+ }
+ qemuDomainSecurityLabelAudit(vm, true);
+
+ DEBUG0("Generating setting domain security labels (if required)");
+ if (virSecurityManagerSetAllLabel(driver->securityManager,
+ vm, stdin_path) < 0)
+ goto cleanup;
+
+ if (stdin_fd != -1) {
+ /* if there's an fd to migrate from, and it's a pipe, put the
+ * proper security label on it
+ */
+ struct stat stdin_sb;
+
+ DEBUG0("setting security label on pipe used for migration");
+
+ if (fstat(stdin_fd, &stdin_sb) < 0) {
+ virReportSystemError(errno,
+ _("cannot stat fd %d"), stdin_fd);
+ goto cleanup;
+ }
+ if (S_ISFIFO(stdin_sb.st_mode) &&
+ virSecurityManagerSetFDLabel(driver->securityManager, vm, stdin_fd) <
0)
+ goto cleanup;
+ }
+
+ /* Ensure no historical cgroup for this VM is lying around bogus
+ * settings */
+ DEBUG0("Ensuring no historical cgroup is lying around");
+ qemuRemoveCgroup(driver, vm, 1);
+
+ if (vm->def->ngraphics == 1) {
+ if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC
&&
+ !vm->def->graphics[0]->data.vnc.socket &&
+ vm->def->graphics[0]->data.vnc.autoport) {
+ int port = qemuProcessNextFreePort(driver, QEMU_VNC_PORT_MIN);
+ if (port < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Unable to find an unused VNC
port"));
+ goto cleanup;
+ }
+ vm->def->graphics[0]->data.vnc.port = port;
+ } else if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE
&&
+ vm->def->graphics[0]->data.spice.autoport) {
+ int port = qemuProcessNextFreePort(driver, QEMU_VNC_PORT_MIN);
+ int tlsPort = -1;
+ if (port < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Unable to find an unused SPICE
port"));
+ goto cleanup;
+ }
+
+ if (driver->spiceTLS) {
+ tlsPort = qemuProcessNextFreePort(driver, port + 1);
+ if (tlsPort < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Unable to find an unused
SPICE TLS port"));
+ qemuProcessReturnPort(driver, port);
+ goto cleanup;
+ }
+ }
+
+ vm->def->graphics[0]->data.spice.port = port;
+ vm->def->graphics[0]->data.spice.tlsPort = tlsPort;
+ }
+ }
+
+ if (virFileMakePath(driver->logDir) != 0) {
+ virReportSystemError(errno,
+ _("cannot create log directory %s"),
+ driver->logDir);
+ goto cleanup;
+ }
+
+ DEBUG0("Creating domain log file");
+ if ((logfile = qemuProcessLogFD(driver, vm->def->name, false)) < 0)
+ goto cleanup;
+
+ DEBUG0("Determining emulator version");
+ if (qemuCapsExtractVersionInfo(vm->def->emulator,
+ NULL,
+ &qemuCmdFlags) < 0)
+ goto cleanup;
+
+ DEBUG0("Setting up domain cgroup (if required)");
+ if (qemuSetupCgroup(driver, vm) < 0)
+ goto cleanup;
+
+ if (VIR_ALLOC(priv->monConfig) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ DEBUG0("Preparing monitor state");
+ if (qemuProcessPrepareMonitorChr(driver, priv->monConfig, vm->def->name)
< 0)
+ goto cleanup;
+
+#if HAVE_YAJL
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_MONITOR_JSON)
+ priv->monJSON = 1;
+ else
+#endif
+ priv->monJSON = 0;
+
+ priv->monitor_warned = 0;
+ priv->gotShutdown = false;
+
+ if ((ret = virFileDeletePid(driver->stateDir, vm->def->name)) != 0) {
+ virReportSystemError(ret,
+ _("Cannot remove stale PID file for %s"),
+ vm->def->name);
+ goto cleanup;
+ }
+
+ if (!(pidfile = virFilePid(driver->stateDir, vm->def->name))) {
+ virReportSystemError(errno,
+ "%s", _("Failed to build pidfile
path."));
+ goto cleanup;
+ }
+
+ /*
+ * Normally PCI addresses are assigned in the virDomainCreate
+ * or virDomainDefine methods. We might still need to assign
+ * some here to cope with the question of upgrades. Regardless
+ * we also need to populate the PCi address set cache for later
+ * use in hotplug
+ */
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ DEBUG0("Assigning domain PCI addresses");
+ /* Populate cache with current addresses */
+ if (priv->pciaddrs) {
+ qemuDomainPCIAddressSetFree(priv->pciaddrs);
+ priv->pciaddrs = NULL;
+ }
+ if (!(priv->pciaddrs = qemuDomainPCIAddressSetCreate(vm->def)))
+ goto cleanup;
+
+
+ /* Assign any remaining addresses */
+ if (qemuAssignDevicePCISlots(vm->def, priv->pciaddrs) < 0)
+ goto cleanup;
+
+ priv->persistentAddrs = 1;
+ } else {
+ priv->persistentAddrs = 0;
+ }
+
+ DEBUG0("Building emulator command line");
+ vm->def->id = driver->nextvmid++;
+ if (!(cmd = qemuBuildCommandLine(conn, driver, vm->def, priv->monConfig,
+ priv->monJSON != 0, qemuCmdFlags,
+ migrateFrom, stdin_fd,
+ vm->current_snapshot, vmop)))
+ goto cleanup;
+
+#if 0
+ /* XXX */
+ if (qemuDomainSnapshotSetCurrentInactive(vm, driver->snapshotDir) < 0)
+ goto cleanup;
+#endif
+
+ /* now that we know it is about to start call the hook if present */
+ if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) {
+ char *xml = virDomainDefFormat(vm->def, 0);
+ int hookret;
+
+ hookret = virHookCall(VIR_HOOK_DRIVER_QEMU, vm->def->name,
+ VIR_HOOK_QEMU_OP_START, VIR_HOOK_SUBOP_BEGIN, NULL, xml);
+ VIR_FREE(xml);
+
+ /*
+ * If the script raised an error abort the launch
+ */
+ if (hookret < 0)
+ goto cleanup;
+ }
+
+ if ((timestamp = virTimestamp()) == NULL) {
+ virReportOOMError();
+ goto cleanup;
+ } else {
+ if (safewrite(logfile, timestamp, strlen(timestamp)) < 0 ||
+ safewrite(logfile, START_POSTFIX, strlen(START_POSTFIX)) < 0) {
+ VIR_WARN("Unable to write timestamp to logfile: %s",
+ virStrerror(errno, ebuf, sizeof ebuf));
+ }
+
+ VIR_FREE(timestamp);
+ }
+
+ virCommandWriteArgLog(cmd, logfile);
+
+ if ((pos = lseek(logfile, 0, SEEK_END)) < 0)
+ VIR_WARN("Unable to seek to end of logfile: %s",
+ virStrerror(errno, ebuf, sizeof ebuf));
+
+ VIR_DEBUG("Clear emulator capabilities: %d",
+ driver->clearEmulatorCapabilities);
+ if (driver->clearEmulatorCapabilities)
+ virCommandClearCaps(cmd);
+
+ virCommandSetPreExecHook(cmd, qemuProcessHook, &hookData);
+
+ virCommandSetOutputFD(cmd, &logfile);
+ virCommandSetErrorFD(cmd, &logfile);
+ virCommandNonblockingFDs(cmd);
+ virCommandSetPidFile(cmd, pidfile);
+ virCommandDaemonize(cmd);
+
+ ret = virCommandRun(cmd, NULL);
+ VIR_FREE(pidfile);
+
+ /* wait for qemu process to to show up */
+ if (ret == 0) {
+ if (virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Domain %s didn't show up"),
vm->def->name);
+ ret = -1;
+ }
+#if 0
+ } else if (ret == -2) {
+ /*
+ * XXX this is bogus. It isn't safe to set vm->pid = child
+ * because the child no longer exists.
+ */
+
+ /* The virExec process that launches the daemon failed. Pending on
+ * when it failed (we can't determine for sure), there may be
+ * extra info in the domain log (if the hook failed for example).
+ *
+ * Pretend like things succeeded, and let 'WaitForMonitor' report
+ * the log contents for us.
+ */
+ vm->pid = child;
+ ret = 0;
+#endif
+ }
+
+ if (migrateFrom)
+ start_paused = true;
+ vm->state = start_paused ? VIR_DOMAIN_PAUSED : VIR_DOMAIN_RUNNING;
+
+ if (ret == -1) /* The VM failed to start; tear filters before taps */
+ virDomainConfVMNWFilterTeardown(vm);
+
+ if (ret == -1) /* The VM failed to start */
+ goto cleanup;
+
+ DEBUG0("Waiting for monitor to show up");
+ if (qemuProcessWaitForMonitor(driver, vm, pos) < 0)
+ goto cleanup;
+
+ DEBUG0("Detecting VCPU PIDs");
+ if (qemuProcessDetectVcpuPIDs(driver, vm) < 0)
+ goto cleanup;
+
+ DEBUG0("Setting any required VM passwords");
+ if (qemuProcessInitPasswords(conn, driver, vm, qemuCmdFlags) < 0)
+ goto cleanup;
+
+ /* If we have -device, then addresses are assigned explicitly.
+ * If not, then we have to detect dynamic ones here */
+ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+ DEBUG0("Determining domain device PCI addresses");
+ if (qemuProcessInitPCIAddresses(driver, vm) < 0)
+ goto cleanup;
+ }
+
+ DEBUG0("Setting initial memory amount");
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuMonitorSetBalloon(priv->mon, vm->def->mem.cur_balloon) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ goto cleanup;
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ if (!start_paused) {
+ DEBUG0("Starting domain CPUs");
+ /* Allow the CPUS to start executing */
+ if (qemuProcessStartCPUs(driver, vm, conn) < 0) {
+ if (virGetLastError() == NULL)
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("resume operation failed"));
+ goto cleanup;
+ }
+ }
+
+
+ DEBUG0("Writing domain status to disk");
+ if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
+ goto cleanup;
+
+ virCommandFree(cmd);
+ VIR_FORCE_CLOSE(logfile);
+
+ return 0;
+
+cleanup:
+ /* We jump here if we failed to start the VM for any reason, or
+ * if we failed to initialize the now running VM. kill it off and
+ * pretend we never started it */
+ virCommandFree(cmd);
+ VIR_FORCE_CLOSE(logfile);
+ qemuProcessStop(driver, vm, 0);
+
+ return -1;
+}
+
+
+void qemuProcessStop(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ int migrated)
+{
+ int ret;
+ int retries = 0;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ virErrorPtr orig_err;
+ virDomainDefPtr def;
+ int i;
+ int logfile = -1;
+ char *timestamp;
+ char ebuf[1024];
+
+ VIR_DEBUG("Shutting down VM '%s' pid=%d migrated=%d",
+ vm->def->name, vm->pid, migrated);
+
+ if ((logfile = qemuProcessLogFD(driver, vm->def->name, true)) < 0) {
+ /* To not break the normal domain shutdown process, skip the
+ * timestamp log writing if failed on opening log file. */
+ VIR_WARN("Unable to open logfile: %s",
+ virStrerror(errno, ebuf, sizeof ebuf));
+ } else {
+ if ((timestamp = virTimestamp()) == NULL) {
+ virReportOOMError();
+ } else {
+ if (safewrite(logfile, timestamp, strlen(timestamp)) < 0 ||
+ safewrite(logfile, SHUTDOWN_POSTFIX,
+ strlen(SHUTDOWN_POSTFIX)) < 0) {
+ VIR_WARN("Unable to write timestamp to logfile: %s",
+ virStrerror(errno, ebuf, sizeof ebuf));
+ }
+
+ VIR_FREE(timestamp);
+ }
+
+ if (VIR_CLOSE(logfile) < 0)
+ VIR_WARN("Unable to close logfile: %s",
+ virStrerror(errno, ebuf, sizeof ebuf));
+ }
+
+ /* This method is routinely used in clean up paths. Disable error
+ * reporting so we don't squash a legit error. */
+ orig_err = virSaveLastError();
+
+ virDomainConfVMNWFilterTeardown(vm);
+
+ if (driver->macFilter) {
+ def = vm->def;
+ for (i = 0 ; i < def->nnets ; i++) {
+ virDomainNetDefPtr net = def->nets[i];
+ if (net->ifname == NULL)
+ continue;
+ if ((errno = networkDisallowMacOnPort(driver, net->ifname,
+ net->mac))) {
+ virReportSystemError(errno,
+ _("failed to remove ebtables rule to allow MAC address on
'%s'"),
+ net->ifname);
+ }
+ }
+ }
+
+ /* This will safely handle a non-running guest with pid=0 or pid=-1*/
+ if (virKillProcess(vm->pid, 0) == 0 &&
+ virKillProcess(vm->pid, SIGTERM) < 0)
+ virReportSystemError(errno,
+ _("Failed to send SIGTERM to %s (%d)"),
+ vm->def->name, vm->pid);
+
+ if (priv->mon)
+ qemuMonitorClose(priv->mon);
+
+ if (priv->monConfig) {
+ if (priv->monConfig->type == VIR_DOMAIN_CHR_TYPE_UNIX)
+ unlink(priv->monConfig->data.nix.path);
+ virDomainChrSourceDefFree(priv->monConfig);
+ priv->monConfig = NULL;
+ }
+
+ /* shut it off for sure */
+ virKillProcess(vm->pid, SIGKILL);
+
+ /* now that we know it's stopped call the hook if present */
+ if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) {
+ char *xml = virDomainDefFormat(vm->def, 0);
+
+ /* we can't stop the operation even if the script raised an error */
+ virHookCall(VIR_HOOK_DRIVER_QEMU, vm->def->name,
+ VIR_HOOK_QEMU_OP_STOPPED, VIR_HOOK_SUBOP_END, NULL, xml);
+ VIR_FREE(xml);
+ }
+
+ /* Reset Security Labels */
+ virSecurityManagerRestoreAllLabel(driver->securityManager,
+ vm, migrated);
+ virSecurityManagerReleaseLabel(driver->securityManager, vm);
+
+ /* Clear out dynamically assigned labels */
+ if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
+ VIR_FREE(vm->def->seclabel.model);
+ VIR_FREE(vm->def->seclabel.label);
+ VIR_FREE(vm->def->seclabel.imagelabel);
+ }
+
+ virDomainDefClearDeviceAliases(vm->def);
+ if (!priv->persistentAddrs) {
+ virDomainDefClearPCIAddresses(vm->def);
+ qemuDomainPCIAddressSetFree(priv->pciaddrs);
+ priv->pciaddrs = NULL;
+ }
+
+ qemuDomainReAttachHostDevices(driver, vm->def);
+
+#if WITH_MACVTAP
+ def = vm->def;
+ for (i = 0; i < def->nnets; i++) {
+ virDomainNetDefPtr net = def->nets[i];
+ if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
+ delMacvtap(net->ifname, net->mac, net->data.direct.linkdev,
+ &net->data.direct.virtPortProfile);
+ VIR_FREE(net->ifname);
+ }
+ }
+#endif
+
+retry:
+ if ((ret = qemuRemoveCgroup(driver, vm, 0)) < 0) {
+ if (ret == -EBUSY && (retries++ < 5)) {
+ usleep(200*1000);
+ goto retry;
+ }
+ VIR_WARN("Failed to remove cgroup for %s",
+ vm->def->name);
+ }
+
+ qemuProcessRemoveDomainStatus(driver, vm);
+
+ /* Remove VNC port from port reservation bitmap, but only if it was
+ reserved by the driver (autoport=yes)
+ */
+ if ((vm->def->ngraphics == 1) &&
+ vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
+ vm->def->graphics[0]->data.vnc.autoport) {
+ qemuProcessReturnPort(driver, vm->def->graphics[0]->data.vnc.port);
+ }
+ if ((vm->def->ngraphics == 1) &&
+ vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE &&
+ vm->def->graphics[0]->data.spice.autoport) {
+ qemuProcessReturnPort(driver, vm->def->graphics[0]->data.spice.port);
+ qemuProcessReturnPort(driver,
vm->def->graphics[0]->data.spice.tlsPort);
+ }
+
+ vm->pid = -1;
+ vm->def->id = -1;
+ vm->state = VIR_DOMAIN_SHUTOFF;
+ VIR_FREE(priv->vcpupids);
+ priv->nvcpupids = 0;
+
+ if (vm->newDef) {
+ virDomainDefFree(vm->def);
+ vm->def = vm->newDef;
+ vm->def->id = -1;
+ vm->newDef = NULL;
+ }
+
+ if (orig_err) {
+ virSetError(orig_err);
+ virFreeError(orig_err);
+ }
+}
+
diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h
new file mode 100644
index 0000000..4d803cc
--- /dev/null
+++ b/src/qemu/qemu_process.h
@@ -0,0 +1,52 @@
+/*
+ * qemu_process.c: QEMU process management
+ *
+ * Copyright (C) 2006-2011 Red Hat, Inc.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __QEMU_PROCESS_H__
+# define __QEMU_PROCESS_H__
+
+#include "qemu_conf.h"
+
+int qemuProcessPrepareMonitorChr(struct qemud_driver *driver,
+ virDomainChrSourceDefPtr monConfig,
+ const char *vm);
+
+int qemuProcessStartCPUs(struct qemud_driver *driver, virDomainObjPtr vm, virConnectPtr
conn);
+int qemuProcessStopCPUs(struct qemud_driver *driver, virDomainObjPtr vm);
+
+void qemuProcessAutostartAll(struct qemud_driver *driver);
+void qemuProcessReconnectAll(virConnectPtr conn, struct qemud_driver *driver);
+
+int qemuProcessAssignPCIAddresses(virDomainDefPtr def);
+
+int qemuProcessStart(virConnectPtr conn,
+ struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ const char *migrateFrom,
+ bool start_paused,
+ int stdin_fd,
+ const char *stdin_path,
+ enum virVMOperationType vmop);
+
+void qemuProcessStop(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ int migrated);
+
+#endif /* __QEMU_PROCESS_H__ */
--
1.7.3.4