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 --- po/POTFILES.in | 1 + 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 | 3535 ++++++++--------------------------------------- src/qemu/qemu_driver.h | 31 +- src/qemu/qemu_process.c | 2417 ++++++++++++++++++++++++++++++++ src/qemu/qemu_process.h | 52 + 10 files changed, 3121 insertions(+), 3017 deletions(-) create mode 100644 src/qemu/qemu_process.c create mode 100644 src/qemu/qemu_process.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 5f2ed75..343fe5d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -61,6 +61,7 @@ src/qemu/qemu_hotplug.c src/qemu/qemu_monitor.c src/qemu/qemu_monitor_json.c src/qemu/qemu_monitor_text.c +src/qemu/qemu_process.c src/remote/remote_driver.c src/secret/secret_driver.c src/security/security_apparmor.c diff --git a/src/Makefile.am b/src/Makefile.am index 2f94efd..15a4e8c 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 f78ce71..c53a706 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -715,6 +715,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 8135046..52d169a 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, @@ -134,6 +138,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 52ea98e..9cc6e89 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,7 +56,11 @@ #include "qemu_monitor.h" #include "qemu_bridge_filter.h" #include "qemu_audit.h" -#include "c-ctype.h" +#include "qemu_process.h" + +#include "virterror_internal.h" +#include "logging.h" +#include "datatypes.h" #include "event.h" #include "buf.h" #include "util.h" @@ -88,169 +89,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 +163,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,2852 +186,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; - - 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; - } + VIR_INFO("Scanning for snapshots for domain %s in %s", vm->def->name, + snapDir); - if (VIR_ALLOC_N(passphrase, size + 1) < 0) { - memset(data, 0, size); - VIR_FREE(data); - virReportOOMError(); + 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; } - memcpy(passphrase, data, size); - passphrase[size] = '\0'; - memset(data, 0, size); - VIR_FREE(data); + while ((entry = readdir(dir))) { + if (entry->d_name[0] == '.') + continue; - *secretRet = passphrase; - *secretLen = size; + /* NB: ignoring errors, so one malformed config doesn't + kill the whole process */ + VIR_INFO("Loading snapshot file '%s'", entry->d_name); - ret = 0; - -cleanup: - return ret; -} - -static int -findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED, - virConnectPtr conn, - virDomainObjPtr vm, - const char *path, - char **secretRet, - size_t *secretLen) -{ - virDomainDiskDefPtr disk; - int ret = -1; - - virDomainObjLock(vm); - disk = findDomainDiskByPath(vm, path); - - if (!disk) - goto cleanup; - - ret = getVolumeQcowPassphrase(conn, disk, secretRet, secretLen); - -cleanup: - virDomainObjUnlock(vm); - return ret; -} + 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; + } -static int -qemuHandleDomainReset(qemuMonitorPtr mon ATTRIBUTE_UNUSED, - virDomainObjPtr vm) -{ - struct qemud_driver *driver = qemu_driver; - virDomainEventPtr event; + 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; + } - virDomainObjLock(vm); - event = virDomainEventRebootNewFromObj(vm); - virDomainObjUnlock(vm); + virDomainSnapshotAssignDef(&vm->snapshots, def); - if (event) { - qemuDriverLock(driver); - qemuDomainEventQueue(driver, event); - qemuDriverUnlock(driver); + VIR_FREE(fullpath); + VIR_FREE(xmlStr); } - return 0; -} + /* 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(); -static int -qemuHandleDomainShutdown(qemuMonitorPtr mon ATTRIBUTE_UNUSED, - virDomainObjPtr vm) -{ - virDomainObjLock(vm); - ((qemuDomainObjPrivatePtr) vm->privateData)->gotShutdown = true; +cleanup: + if (dir) + closedir(dir); + VIR_FREE(snapDir); virDomainObjUnlock(vm); - - return 0; } - +/** + * qemudStartup: + * + * Initialization function for the QEmu daemon + */ static int -qemuHandleDomainStop(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); +qemudStartup(int privileged) { + char *base = NULL; + char *driverConf = NULL; + int rc; + virConnectPtr conn = NULL; - 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 (VIR_ALLOC(qemu_driver) < 0) + return -1; - if (event) { - qemuDriverLock(driver); - if (event) - 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; -} - + /* Don't have a dom0 so start from 1 */ + qemu_driver->nextvmid = 1; -static int -qemuHandleDomainRTCChange(qemuMonitorPtr mon ATTRIBUTE_UNUSED, - virDomainObjPtr vm, - long long offset) -{ - struct qemud_driver *driver = qemu_driver; - virDomainEventPtr event; + if (virDomainObjListInit(&qemu_driver->domains) < 0) + goto out_of_memory; - virDomainObjLock(vm); - event = virDomainEventRTCChangeNewFromObj(vm, offset); + /* Init callback list */ + if (VIR_ALLOC(qemu_driver->domainEventCallbacks) < 0) + goto out_of_memory; + if (!(qemu_driver->domainEventQueue = virDomainEventQueueNew())) + goto out_of_memory; - if (vm->def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_VARIABLE) - vm->def->clock.data.adjustment = offset; + if ((qemu_driver->domainEventTimer = + virEventAddTimeout(-1, qemuDomainEventFlush, qemu_driver, NULL)) < 0) + goto error; - if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) - VIR_WARN0("unable to save domain status with RTC change"); + /* Allocate bitmap for vnc port reservation */ + if ((qemu_driver->reservedVNCPorts = + virBitmapAlloc(QEMU_VNC_PORT_MAX - QEMU_VNC_PORT_MIN)) == NULL) + goto out_of_memory; - virDomainObjUnlock(vm); + /* read the host sysinfo */ + if (privileged) + qemu_driver->hostsysinfo = virSysinfoRead(); - if (event) { - qemuDriverLock(driver); - qemuDomainEventQueue(driver, event); - qemuDriverUnlock(driver); - } + if (privileged) { + if (virAsprintf(&qemu_driver->logDir, + "%s/log/libvirt/qemu", LOCALSTATEDIR) == -1) + goto out_of_memory; - return 0; -} + 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; -static int -qemuHandleDomainWatchdog(qemuMonitorPtr mon ATTRIBUTE_UNUSED, - virDomainObjPtr vm, - int action) -{ - struct qemud_driver *driver = qemu_driver; - virDomainEventPtr watchdogEvent = NULL; - virDomainEventPtr lifecycleEvent = NULL; + if (virAsprintf(&qemu_driver->libDir, + "%s/lib/libvirt/qemu", LOCALSTATEDIR) == -1) + goto out_of_memory; - virDomainObjLock(vm); - watchdogEvent = virDomainEventWatchdogNewFromObj(vm, action); + 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 (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); + if (virAsprintf(&qemu_driver->logDir, + "%s/.libvirt/qemu/log", userdir) == -1) { + VIR_FREE(userdir); + goto out_of_memory; + } - vm->state = VIR_DOMAIN_PAUSED; - lifecycleEvent = virDomainEventNewFromObj(vm, - VIR_DOMAIN_EVENT_SUSPENDED, - VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG); + if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) { + VIR_FREE(userdir); + goto out_of_memory; + } + VIR_FREE(userdir); - if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) - VIR_WARN("Unable to save status on vm %s after IO error", vm->def->name); + 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 (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->stateDir) != 0) { + char ebuf[1024]; + VIR_ERROR(_("Failed to create state dir '%s': %s"), + qemu_driver->stateDir, 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->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; } - return 0; -} - + /* 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; -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; + VIR_FREE(base); - virDomainObjLock(vm); - disk = findDomainDiskByAlias(vm, diskAlias); + 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 (disk) { - srcPath = disk->src; - devAlias = disk->info.alias; - } else { - srcPath = ""; - devAlias = ""; + if (qemudLoadDriverConfig(qemu_driver, driverConf) < 0) { + goto error; } + VIR_FREE(driverConf); - ioErrorEvent = virDomainEventIOErrorNewFromObj(vm, srcPath, devAlias, action); - ioErrorEvent2 = virDomainEventIOErrorReasonNewFromObj(vm, srcPath, devAlias, action, reason); + if (qemuSecurityInit(qemu_driver) < 0) + goto error; - 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); + if ((qemu_driver->caps = qemuCreateCapabilities(NULL, + qemu_driver)) == NULL) + goto error; - 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 -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) - 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; - } - - 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 ((qemu_driver->activePciHostdevs = pciDeviceListNew()) == NULL) + goto error; - if (errno == EADDRINUSE) { - /* In use, try next */ - continue; + 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; } - /* 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->cacheDir, 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->cacheDir, 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->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 (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 (!virDomainObjIsActive(vm)) { - VIR_DEBUG("VM '%s' not active", vm->def->name); - return; + if (newVM) { + virDomainEventPtr event = + virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_DEFINED, + VIR_DOMAIN_EVENT_DEFINED_ADDED); + if (event) + qemuDomainEventQueue(driver, event); } +} - 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)); - } - - 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) { @@ -3603,7 +1238,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, @@ -3616,9 +1251,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, @@ -3693,7 +1328,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, @@ -3746,7 +1381,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")); @@ -3841,7 +1476,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); @@ -4065,7 +1700,7 @@ qemuDomainMigrateOffline(struct qemud_driver *driver, { int ret; - ret = doStopCPUs(driver, vm); + ret = qemuProcessStopCPUs(driver, vm); if (ret == 0) { virDomainEventPtr event; @@ -4336,7 +1971,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)) { @@ -4546,7 +2181,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, @@ -4562,7 +2197,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"); } @@ -4916,7 +2551,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; @@ -4935,7 +2570,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, @@ -4946,7 +2581,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")); @@ -4973,7 +2608,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) { @@ -5007,7 +2642,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, @@ -5797,8 +3432,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) { @@ -5857,7 +3492,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")); @@ -6157,7 +3792,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, @@ -6223,8 +3858,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 = @@ -6411,7 +4046,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, @@ -7915,60 +5550,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) @@ -8100,12 +5681,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) { @@ -8119,7 +5700,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); @@ -8346,10 +5927,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) { @@ -8931,7 +6512,7 @@ qemudDomainMigratePerform (virDomainPtr dom, } /* Clean up the source domain. */ - qemudShutdownVMDaemon(driver, vm, 1); + qemuProcessStop(driver, vm, 1); qemuDomainStopAudit(vm, "migrated"); resume = 0; @@ -8949,7 +6530,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 @@ -9113,7 +6694,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")); @@ -9135,7 +6716,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, @@ -9964,8 +7545,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; @@ -9977,7 +7558,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; } @@ -9998,7 +7579,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, @@ -10499,56 +8080,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@xxxxxxxxxx> */ - -#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..7061fc2 --- /dev/null +++ b/src/qemu/qemu_process.c @@ -0,0 +1,2417 @@ +/* + * 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 <sys/stat.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 */ + qemuProcessStop(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 (!virDomainObjIsActive(vm)) { + VIR_DEBUG("VM '%s' not active", vm->def->name); + return; + } + + 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..f1ab599 --- /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.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list