Signed-off-by: Prerna Saxena <saxenap.ltc@xxxxxxxxx> --- src/qemu/qemu_driver.c | 1161 ----------------------------------------------- src/qemu/qemu_process.c | 1133 +++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_process.h | 86 ++++ 3 files changed, 1219 insertions(+), 1161 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b249347..9d495fb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -151,11 +151,6 @@ static int qemuDomainObjStart(virConnectPtr conn, static int qemuDomainManagedSaveLoad(virDomainObjPtr vm, void *opaque); -static int qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid, - bool dynamicOwnership, - const char *path, int oflags, - bool *needUnlink, bool *bypassSecurityDriver); - static int qemuGetDHCPInterfaces(virDomainPtr dom, virDomainObjPtr vm, virDomainInterfacePtr **ifaces); @@ -2819,38 +2814,6 @@ qemuDomainGetControlInfo(virDomainPtr dom, verify(sizeof(QEMU_SAVE_MAGIC) == sizeof(QEMU_SAVE_PARTIAL)); -typedef enum { - QEMU_SAVE_FORMAT_RAW = 0, - QEMU_SAVE_FORMAT_GZIP = 1, - QEMU_SAVE_FORMAT_BZIP2 = 2, - /* - * Deprecated by xz and never used as part of a release - * QEMU_SAVE_FORMAT_LZMA - */ - QEMU_SAVE_FORMAT_XZ = 3, - QEMU_SAVE_FORMAT_LZOP = 4, - /* Note: add new members only at the end. - These values are used in the on-disk format. - Do not change or re-use numbers. */ - - QEMU_SAVE_FORMAT_LAST -} virQEMUSaveFormat; - -VIR_ENUM_DECL(qemuSaveCompression) -VIR_ENUM_IMPL(qemuSaveCompression, QEMU_SAVE_FORMAT_LAST, - "raw", - "gzip", - "bzip2", - "xz", - "lzop") - -VIR_ENUM_DECL(qemuDumpFormat) -VIR_ENUM_IMPL(qemuDumpFormat, VIR_DOMAIN_CORE_DUMP_FORMAT_LAST, - "elf", - "kdump-zlib", - "kdump-lzo", - "kdump-snappy") - typedef struct _virQEMUSaveHeader virQEMUSaveHeader; typedef virQEMUSaveHeader *virQEMUSaveHeaderPtr; struct _virQEMUSaveHeader { @@ -3062,214 +3025,6 @@ qemuCompressGetCommand(virQEMUSaveFormat compression) return ret; } -/** - * qemuOpenFile: - * @driver: driver object - * @vm: domain object - * @path: path to file to open - * @oflags: flags for opening/creation of the file - * @needUnlink: set to true if file was created by this function - * @bypassSecurityDriver: optional pointer to a boolean that will be set to true - * if security driver operations are pointless (due to - * NFS mount) - * - * Internal function to properly create or open existing files, with - * ownership affected by qemu driver setup and domain DAC label. - * - * Returns the file descriptor on success and negative errno on failure. - * - * This function should not be used on storage sources. Use - * qemuDomainStorageFileInit and storage driver APIs if possible. - **/ -static int -qemuOpenFile(virQEMUDriverPtr driver, - virDomainObjPtr vm, - const char *path, - int oflags, - bool *needUnlink, - bool *bypassSecurityDriver) -{ - int ret = -1; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - uid_t user = cfg->user; - gid_t group = cfg->group; - bool dynamicOwnership = cfg->dynamicOwnership; - virSecurityLabelDefPtr seclabel; - - virObjectUnref(cfg); - - /* TODO: Take imagelabel into account? */ - if (vm && - (seclabel = virDomainDefGetSecurityLabelDef(vm->def, "dac")) != NULL && - seclabel->label != NULL && - (virParseOwnershipIds(seclabel->label, &user, &group) < 0)) - goto cleanup; - - ret = qemuOpenFileAs(user, group, dynamicOwnership, - path, oflags, needUnlink, bypassSecurityDriver); - - cleanup: - return ret; -} - -static int -qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid, - bool dynamicOwnership, - const char *path, int oflags, - bool *needUnlink, bool *bypassSecurityDriver) -{ - struct stat sb; - bool is_reg = true; - bool need_unlink = false; - bool bypass_security = false; - unsigned int vfoflags = 0; - int fd = -1; - int path_shared = virFileIsSharedFS(path); - uid_t uid = geteuid(); - gid_t gid = getegid(); - - /* path might be a pre-existing block dev, in which case - * we need to skip the create step, and also avoid unlink - * in the failure case */ - if (oflags & O_CREAT) { - need_unlink = true; - - /* Don't force chown on network-shared FS - * as it is likely to fail. */ - if (path_shared <= 0 || dynamicOwnership) - vfoflags |= VIR_FILE_OPEN_FORCE_OWNER; - - if (stat(path, &sb) == 0) { - /* It already exists, we don't want to delete it on error */ - need_unlink = false; - - is_reg = !!S_ISREG(sb.st_mode); - /* If the path is regular file which exists - * already and dynamic_ownership is off, we don't - * want to change its ownership, just open it as-is */ - if (is_reg && !dynamicOwnership) { - uid = sb.st_uid; - gid = sb.st_gid; - } - } - } - - /* First try creating the file as root */ - if (!is_reg) { - if ((fd = open(path, oflags & ~O_CREAT)) < 0) { - fd = -errno; - goto error; - } - } else { - if ((fd = virFileOpenAs(path, oflags, S_IRUSR | S_IWUSR, uid, gid, - vfoflags | VIR_FILE_OPEN_NOFORK)) < 0) { - /* If we failed as root, and the error was permission-denied - (EACCES or EPERM), assume it's on a network-connected share - where root access is restricted (eg, root-squashed NFS). If the - qemu user is non-root, just set a flag to - bypass security driver shenanigans, and retry the operation - after doing setuid to qemu user */ - if ((fd != -EACCES && fd != -EPERM) || fallback_uid == geteuid()) - goto error; - - /* On Linux we can also verify the FS-type of the directory. */ - switch (path_shared) { - case 1: - /* it was on a network share, so we'll continue - * as outlined above - */ - break; - - case -1: - virReportSystemError(-fd, oflags & O_CREAT - ? _("Failed to create file " - "'%s': couldn't determine fs type") - : _("Failed to open file " - "'%s': couldn't determine fs type"), - path); - goto cleanup; - - case 0: - default: - /* local file - log the error returned by virFileOpenAs */ - goto error; - } - - /* If we created the file above, then we need to remove it; - * otherwise, the next attempt to create will fail. If the - * file had already existed before we got here, then we also - * don't want to delete it and allow the following to succeed - * or fail based on existing protections - */ - if (need_unlink) - unlink(path); - - /* Retry creating the file as qemu user */ - - /* Since we're passing different modes... */ - vfoflags |= VIR_FILE_OPEN_FORCE_MODE; - - if ((fd = virFileOpenAs(path, oflags, - S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, - fallback_uid, fallback_gid, - vfoflags | VIR_FILE_OPEN_FORK)) < 0) { - virReportSystemError(-fd, oflags & O_CREAT - ? _("Error from child process creating '%s'") - : _("Error from child process opening '%s'"), - path); - goto cleanup; - } - - /* Since we had to setuid to create the file, and the fstype - is NFS, we assume it's a root-squashing NFS share, and that - the security driver stuff would have failed anyway */ - - bypass_security = true; - } - } - cleanup: - if (needUnlink) - *needUnlink = need_unlink; - if (bypassSecurityDriver) - *bypassSecurityDriver = bypass_security; - return fd; - - error: - virReportSystemError(-fd, oflags & O_CREAT - ? _("Failed to create file '%s'") - : _("Failed to open file '%s'"), - path); - goto cleanup; -} - - -static int -qemuFileWrapperFDClose(virDomainObjPtr vm, - virFileWrapperFdPtr fd) -{ - int ret; - - /* virFileWrapperFd uses iohelper to write data onto disk. - * However, iohelper calls fdatasync() which may take ages to - * finish. Therefore, we shouldn't be waiting with the domain - * object locked. */ - - /* XXX Currently, this function is intended for *Save() only - * as restore needs some reworking before it's ready for - * this. */ - - virObjectUnlock(vm); - ret = virFileWrapperFdClose(fd); - virObjectLock(vm); - if (!virDomainObjIsActive(vm)) { - if (!virGetLastError()) - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("domain is no longer running")); - ret = -1; - } - return ret; -} - /* Helper function to execute a migration to file with a correct save header * the caller needs to make sure that the processors are stopped and do all other @@ -3481,82 +3236,6 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom, return ret; } - -/* qemuGetCompressionProgram: - * @imageFormat: String representation from qemu.conf for the compression - * image format being used (dump, save, or snapshot). - * @compresspath: Pointer to a character string to store the fully qualified - * path from virFindFileInPath. - * @styleFormat: String representing the style of format (dump, save, snapshot) - * @use_raw_on_fail: Boolean indicating how to handle the error path. For - * callers that are OK with invalid data or inability to - * find the compression program, just return a raw format - * and let the path remain as NULL. - * - * Returns: - * virQEMUSaveFormat - Integer representation of the compression - * program to be used for particular style - * (e.g. dump, save, or snapshot). - * QEMU_SAVE_FORMAT_RAW - If there is no qemu.conf imageFormat value or - * no there was an error, then just return RAW - * indicating none. - */ -static int ATTRIBUTE_NONNULL(2) -qemuGetCompressionProgram(const char *imageFormat, - char **compresspath, - const char *styleFormat, - bool use_raw_on_fail) -{ - int ret; - - *compresspath = NULL; - - if (!imageFormat) - return QEMU_SAVE_FORMAT_RAW; - - if ((ret = qemuSaveCompressionTypeFromString(imageFormat)) < 0) - goto error; - - if (ret == QEMU_SAVE_FORMAT_RAW) - return QEMU_SAVE_FORMAT_RAW; - - if (!(*compresspath = virFindFileInPath(imageFormat))) - goto error; - - return ret; - - error: - if (ret < 0) { - if (use_raw_on_fail) - VIR_WARN("Invalid %s image format specified in " - "configuration file, using raw", - styleFormat); - else - virReportError(VIR_ERR_OPERATION_FAILED, - _("Invalid %s image format specified " - "in configuration file"), - styleFormat); - } else { - if (use_raw_on_fail) - VIR_WARN("Compression program for %s image format in " - "configuration file isn't available, using raw", - styleFormat); - else - virReportError(VIR_ERR_OPERATION_FAILED, - _("Compression program for %s image format " - "in configuration file isn't available"), - styleFormat); - } - - /* Use "raw" as the format if the specified format is not valid, - * or the compress program is not available. */ - if (use_raw_on_fail) - return QEMU_SAVE_FORMAT_RAW; - - return -1; -} - - static int qemuDomainSaveFlags(virDomainPtr dom, const char *path, const char *dxml, unsigned int flags) @@ -3761,147 +3440,6 @@ qemuDomainManagedSaveRemove(virDomainPtr dom, unsigned int flags) return ret; } -static int qemuDumpToFd(virQEMUDriverPtr driver, virDomainObjPtr vm, - int fd, qemuDomainAsyncJob asyncJob, - const char *dumpformat) -{ - qemuDomainObjPrivatePtr priv = vm->privateData; - int ret = -1; - - if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DUMP_GUEST_MEMORY)) { - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", - _("dump-guest-memory is not supported")); - return -1; - } - - if (qemuSecuritySetImageFDLabel(driver->securityManager, vm->def, fd) < 0) - return -1; - - VIR_FREE(priv->job.current); - priv->job.dump_memory_only = true; - - if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) - return -1; - - if (dumpformat) { - ret = qemuMonitorGetDumpGuestMemoryCapability(priv->mon, dumpformat); - - if (ret <= 0) { - virReportError(VIR_ERR_INVALID_ARG, - _("unsupported dumpformat '%s' " - "for this QEMU binary"), - dumpformat); - ret = -1; - goto cleanup; - } - } - - ret = qemuMonitorDumpToFd(priv->mon, fd, dumpformat); - - cleanup: - ignore_value(qemuDomainObjExitMonitor(driver, vm)); - - return ret; -} - -static int -doCoreDump(virQEMUDriverPtr driver, - virDomainObjPtr vm, - const char *path, - unsigned int dump_flags, - unsigned int dumpformat) -{ - int fd = -1; - int ret = -1; - virFileWrapperFdPtr wrapperFd = NULL; - int directFlag = 0; - unsigned int flags = VIR_FILE_WRAPPER_NON_BLOCKING; - const char *memory_dump_format = NULL; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - char *compressedpath = NULL; - - /* We reuse "save" flag for "dump" here. Then, we can support the same - * format in "save" and "dump". This path doesn't need the compression - * program to exist and can ignore the return value - it only cares to - * get the compressedpath */ - ignore_value(qemuGetCompressionProgram(cfg->dumpImageFormat, - &compressedpath, - "dump", true)); - - /* Create an empty file with appropriate ownership. */ - if (dump_flags & VIR_DUMP_BYPASS_CACHE) { - flags |= VIR_FILE_WRAPPER_BYPASS_CACHE; - directFlag = virFileDirectFdFlag(); - if (directFlag < 0) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("bypass cache unsupported by this system")); - goto cleanup; - } - } - /* Core dumps usually imply last-ditch analysis efforts are - * desired, so we intentionally do not unlink even if a file was - * created. */ - if ((fd = qemuOpenFile(driver, vm, path, - O_CREAT | O_TRUNC | O_WRONLY | directFlag, - NULL, NULL)) < 0) - goto cleanup; - - if (!(wrapperFd = virFileWrapperFdNew(&fd, path, flags))) - goto cleanup; - - if (dump_flags & VIR_DUMP_MEMORY_ONLY) { - if (!(memory_dump_format = qemuDumpFormatTypeToString(dumpformat))) { - virReportError(VIR_ERR_INVALID_ARG, - _("unknown dumpformat '%d'"), dumpformat); - goto cleanup; - } - - /* qemu dumps in "elf" without dumpformat set */ - if (STREQ(memory_dump_format, "elf")) - memory_dump_format = NULL; - - ret = qemuDumpToFd(driver, vm, fd, QEMU_ASYNC_JOB_DUMP, - memory_dump_format); - } else { - if (dumpformat != VIR_DOMAIN_CORE_DUMP_FORMAT_RAW) { - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", - _("kdump-compressed format is only supported with " - "memory-only dump")); - goto cleanup; - } - - if (!qemuMigrationIsAllowed(driver, vm, false, 0)) - goto cleanup; - - ret = qemuMigrationToFile(driver, vm, fd, compressedpath, - QEMU_ASYNC_JOB_DUMP); - } - - if (ret < 0) - goto cleanup; - - if (VIR_CLOSE(fd) < 0) { - virReportSystemError(errno, - _("unable to close file %s"), - path); - goto cleanup; - } - if (qemuFileWrapperFDClose(vm, wrapperFd) < 0) - goto cleanup; - - ret = 0; - - cleanup: - VIR_FORCE_CLOSE(fd); - if (ret != 0) - unlink(path); - virFileWrapperFdFree(wrapperFd); - VIR_FREE(compressedpath); - virObjectUnref(cfg); - return ret; -} - - static int qemuDomainCoreDumpWithFormat(virDomainPtr dom, const char *path, @@ -4106,712 +3644,13 @@ qemuDomainScreenshot(virDomainPtr dom, return ret; } -static char * -getAutoDumpPath(virQEMUDriverPtr driver, - virDomainObjPtr vm) -{ - char *dumpfile = NULL; - char *domname = virDomainObjGetShortName(vm->def); - char timestr[100]; - struct tm time_info; - time_t curtime = time(NULL); - virQEMUDriverConfigPtr cfg = NULL; - - if (!domname) - return NULL; - - cfg = virQEMUDriverGetConfig(driver); - - localtime_r(&curtime, &time_info); - strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info); - - ignore_value(virAsprintf(&dumpfile, "%s/%s-%s", - cfg->autoDumpPath, - domname, - timestr)); - - virObjectUnref(cfg); - VIR_FREE(domname); - return dumpfile; -} - -static void -processWatchdogEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm, - int action) -{ - int ret; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - char *dumpfile = getAutoDumpPath(driver, vm); - unsigned int flags = VIR_DUMP_MEMORY_ONLY; - - if (!dumpfile) - goto cleanup; - - switch (action) { - case VIR_DOMAIN_WATCHDOG_ACTION_DUMP: - if (qemuDomainObjBeginAsyncJob(driver, vm, - QEMU_ASYNC_JOB_DUMP, - VIR_DOMAIN_JOB_OPERATION_DUMP) < 0) { - goto cleanup; - } - - if (!virDomainObjIsActive(vm)) { - virReportError(VIR_ERR_OPERATION_INVALID, - "%s", _("domain is not running")); - goto endjob; - } - - flags |= cfg->autoDumpBypassCache ? VIR_DUMP_BYPASS_CACHE: 0; - if ((ret = doCoreDump(driver, vm, dumpfile, flags, - VIR_DOMAIN_CORE_DUMP_FORMAT_RAW)) < 0) - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("Dump failed")); - - ret = qemuProcessStartCPUs(driver, vm, NULL, - VIR_DOMAIN_RUNNING_UNPAUSED, - QEMU_ASYNC_JOB_DUMP); - - if (ret < 0) - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("Resuming after dump failed")); - break; - default: - goto cleanup; - } - - endjob: - qemuDomainObjEndAsyncJob(driver, vm); - - cleanup: - VIR_FREE(dumpfile); - virObjectUnref(cfg); -} -static int -doCoreDumpToAutoDumpPath(virQEMUDriverPtr driver, - virDomainObjPtr vm, - unsigned int flags) -{ - int ret = -1; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - char *dumpfile = getAutoDumpPath(driver, vm); - if (!dumpfile) - goto cleanup; - flags |= cfg->autoDumpBypassCache ? VIR_DUMP_BYPASS_CACHE: 0; - if ((ret = doCoreDump(driver, vm, dumpfile, flags, - VIR_DOMAIN_CORE_DUMP_FORMAT_RAW)) < 0) - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("Dump failed")); - cleanup: - VIR_FREE(dumpfile); - virObjectUnref(cfg); - return ret; -} -static void -qemuProcessGuestPanicEventInfo(virQEMUDriverPtr driver, - virDomainObjPtr vm, - qemuMonitorEventPanicInfoPtr info) -{ - char *msg = qemuMonitorGuestPanicEventInfoFormatMsg(info); - char *timestamp = virTimeStringNow(); - if (msg && timestamp) - qemuDomainLogAppendMessage(driver, vm, "%s: panic %s\n", timestamp, msg); - VIR_FREE(timestamp); - VIR_FREE(msg); -} - - -static void -processGuestPanicEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm, - int action, - qemuMonitorEventPanicInfoPtr info) -{ - qemuDomainObjPrivatePtr priv = vm->privateData; - virObjectEventPtr event = NULL; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - bool removeInactive = false; - - if (qemuDomainObjBeginAsyncJob(driver, vm, QEMU_ASYNC_JOB_DUMP, - VIR_DOMAIN_JOB_OPERATION_DUMP) < 0) - goto cleanup; - - if (!virDomainObjIsActive(vm)) { - VIR_DEBUG("Ignoring GUEST_PANICKED event from inactive domain %s", - vm->def->name); - goto endjob; - } - - if (info) - qemuProcessGuestPanicEventInfo(driver, vm, info); - - virDomainObjSetState(vm, VIR_DOMAIN_CRASHED, VIR_DOMAIN_CRASHED_PANICKED); - - event = virDomainEventLifecycleNewFromObj(vm, - VIR_DOMAIN_EVENT_CRASHED, - VIR_DOMAIN_EVENT_CRASHED_PANICKED); - - qemuDomainEventQueue(driver, event); - - if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) { - VIR_WARN("Unable to save status on vm %s after state change", - vm->def->name); - } - - if (virDomainLockProcessPause(driver->lockManager, vm, &priv->lockState) < 0) - VIR_WARN("Unable to release lease on %s", vm->def->name); - VIR_DEBUG("Preserving lock state '%s'", NULLSTR(priv->lockState)); - - switch (action) { - case VIR_DOMAIN_LIFECYCLE_CRASH_COREDUMP_DESTROY: - if (doCoreDumpToAutoDumpPath(driver, vm, VIR_DUMP_MEMORY_ONLY) < 0) - goto endjob; - ATTRIBUTE_FALLTHROUGH; - - case VIR_DOMAIN_LIFECYCLE_CRASH_DESTROY: - qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_CRASHED, - QEMU_ASYNC_JOB_DUMP, 0); - event = virDomainEventLifecycleNewFromObj(vm, - VIR_DOMAIN_EVENT_STOPPED, - VIR_DOMAIN_EVENT_STOPPED_CRASHED); - - qemuDomainEventQueue(driver, event); - virDomainAuditStop(vm, "destroyed"); - removeInactive = true; - break; - - case VIR_DOMAIN_LIFECYCLE_CRASH_COREDUMP_RESTART: - if (doCoreDumpToAutoDumpPath(driver, vm, VIR_DUMP_MEMORY_ONLY) < 0) - goto endjob; - ATTRIBUTE_FALLTHROUGH; - - case VIR_DOMAIN_LIFECYCLE_CRASH_RESTART: - qemuDomainSetFakeReboot(driver, vm, true); - qemuProcessShutdownOrReboot(driver, vm); - break; - - case VIR_DOMAIN_LIFECYCLE_CRASH_PRESERVE: - break; - - default: - break; - } - - endjob: - qemuDomainObjEndAsyncJob(driver, vm); - if (removeInactive) - qemuDomainRemoveInactiveJob(driver, vm); - - cleanup: - virObjectUnref(cfg); -} - - -static void -processDeviceDeletedEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm, - char *devAlias) -{ - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - virDomainDeviceDef dev; - - VIR_DEBUG("Removing device %s from domain %p %s", - devAlias, vm, vm->def->name); - - if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) - goto cleanup; - - if (!virDomainObjIsActive(vm)) { - VIR_DEBUG("Domain is not running"); - goto endjob; - } - - if (STRPREFIX(devAlias, "vcpu")) { - qemuDomainRemoveVcpuAlias(driver, vm, devAlias); - } else { - if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) - goto endjob; - - if (qemuDomainRemoveDevice(driver, vm, &dev) < 0) - goto endjob; - } - - if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) - VIR_WARN("unable to save domain status after removing device %s", - devAlias); - - endjob: - qemuDomainObjEndJob(driver, vm); - - cleanup: - VIR_FREE(devAlias); - virObjectUnref(cfg); -} - - -static void -syncNicRxFilterMacAddr(char *ifname, virNetDevRxFilterPtr guestFilter, - virNetDevRxFilterPtr hostFilter) -{ - char newMacStr[VIR_MAC_STRING_BUFLEN]; - - if (virMacAddrCmp(&hostFilter->mac, &guestFilter->mac)) { - virMacAddrFormat(&guestFilter->mac, newMacStr); - - /* set new MAC address from guest to associated macvtap device */ - if (virNetDevSetMAC(ifname, &guestFilter->mac) < 0) { - VIR_WARN("Couldn't set new MAC address %s to device %s " - "while responding to NIC_RX_FILTER_CHANGED", - newMacStr, ifname); - } else { - VIR_DEBUG("device %s MAC address set to %s", ifname, newMacStr); - } - } -} - - -static void -syncNicRxFilterGuestMulticast(char *ifname, virNetDevRxFilterPtr guestFilter, - virNetDevRxFilterPtr hostFilter) -{ - size_t i, j; - bool found; - char macstr[VIR_MAC_STRING_BUFLEN]; - - for (i = 0; i < guestFilter->multicast.nTable; i++) { - found = false; - - for (j = 0; j < hostFilter->multicast.nTable; j++) { - if (virMacAddrCmp(&guestFilter->multicast.table[i], - &hostFilter->multicast.table[j]) == 0) { - found = true; - break; - } - } - - if (!found) { - virMacAddrFormat(&guestFilter->multicast.table[i], macstr); - - if (virNetDevAddMulti(ifname, &guestFilter->multicast.table[i]) < 0) { - VIR_WARN("Couldn't add new multicast MAC address %s to " - "device %s while responding to NIC_RX_FILTER_CHANGED", - macstr, ifname); - } else { - VIR_DEBUG("Added multicast MAC %s to %s interface", - macstr, ifname); - } - } - } -} - - -static void -syncNicRxFilterHostMulticast(char *ifname, virNetDevRxFilterPtr guestFilter, - virNetDevRxFilterPtr hostFilter) -{ - size_t i, j; - bool found; - char macstr[VIR_MAC_STRING_BUFLEN]; - - for (i = 0; i < hostFilter->multicast.nTable; i++) { - found = false; - - for (j = 0; j < guestFilter->multicast.nTable; j++) { - if (virMacAddrCmp(&hostFilter->multicast.table[i], - &guestFilter->multicast.table[j]) == 0) { - found = true; - break; - } - } - - if (!found) { - virMacAddrFormat(&hostFilter->multicast.table[i], macstr); - - if (virNetDevDelMulti(ifname, &hostFilter->multicast.table[i]) < 0) { - VIR_WARN("Couldn't delete multicast MAC address %s from " - "device %s while responding to NIC_RX_FILTER_CHANGED", - macstr, ifname); - } else { - VIR_DEBUG("Deleted multicast MAC %s from %s interface", - macstr, ifname); - } - } - } -} - - -static void -syncNicRxFilterPromiscMode(char *ifname, - virNetDevRxFilterPtr guestFilter, - virNetDevRxFilterPtr hostFilter) -{ - bool promisc; - bool setpromisc = false; - - /* Set macvtap promisc mode to true if the guest has vlans defined */ - /* or synchronize the macvtap promisc mode if different from guest */ - if (guestFilter->vlan.nTable > 0) { - if (!hostFilter->promiscuous) { - setpromisc = true; - promisc = true; - } - } else if (hostFilter->promiscuous != guestFilter->promiscuous) { - setpromisc = true; - promisc = guestFilter->promiscuous; - } - - if (setpromisc) { - if (virNetDevSetPromiscuous(ifname, promisc) < 0) { - VIR_WARN("Couldn't set PROMISC flag to %s for device %s " - "while responding to NIC_RX_FILTER_CHANGED", - promisc ? "true" : "false", ifname); - } - } -} - - -static void -syncNicRxFilterMultiMode(char *ifname, virNetDevRxFilterPtr guestFilter, - virNetDevRxFilterPtr hostFilter) -{ - if (hostFilter->multicast.mode != guestFilter->multicast.mode) { - switch (guestFilter->multicast.mode) { - case VIR_NETDEV_RX_FILTER_MODE_ALL: - if (virNetDevSetRcvAllMulti(ifname, true)) { - - VIR_WARN("Couldn't set allmulticast flag to 'on' for " - "device %s while responding to " - "NIC_RX_FILTER_CHANGED", ifname); - } - break; - - case VIR_NETDEV_RX_FILTER_MODE_NORMAL: - if (virNetDevSetRcvMulti(ifname, true)) { - - VIR_WARN("Couldn't set multicast flag to 'on' for " - "device %s while responding to " - "NIC_RX_FILTER_CHANGED", ifname); - } - - if (virNetDevSetRcvAllMulti(ifname, false)) { - VIR_WARN("Couldn't set allmulticast flag to 'off' for " - "device %s while responding to " - "NIC_RX_FILTER_CHANGED", ifname); - } - break; - - case VIR_NETDEV_RX_FILTER_MODE_NONE: - if (virNetDevSetRcvAllMulti(ifname, false)) { - VIR_WARN("Couldn't set allmulticast flag to 'off' for " - "device %s while responding to " - "NIC_RX_FILTER_CHANGED", ifname); - } - - if (virNetDevSetRcvMulti(ifname, false)) { - VIR_WARN("Couldn't set multicast flag to 'off' for " - "device %s while responding to " - "NIC_RX_FILTER_CHANGED", - ifname); - } - break; - } - } -} - - -static void -syncNicRxFilterDeviceOptions(char *ifname, virNetDevRxFilterPtr guestFilter, - virNetDevRxFilterPtr hostFilter) -{ - syncNicRxFilterPromiscMode(ifname, guestFilter, hostFilter); - syncNicRxFilterMultiMode(ifname, guestFilter, hostFilter); -} - - -static void -syncNicRxFilterMulticast(char *ifname, - virNetDevRxFilterPtr guestFilter, - virNetDevRxFilterPtr hostFilter) -{ - syncNicRxFilterGuestMulticast(ifname, guestFilter, hostFilter); - syncNicRxFilterHostMulticast(ifname, guestFilter, hostFilter); -} - -static void -processNicRxFilterChangedEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm, - char *devAlias) -{ - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - qemuDomainObjPrivatePtr priv = vm->privateData; - virDomainDeviceDef dev; - virDomainNetDefPtr def; - virNetDevRxFilterPtr guestFilter = NULL; - virNetDevRxFilterPtr hostFilter = NULL; - int ret; - - VIR_DEBUG("Received NIC_RX_FILTER_CHANGED event for device %s " - "from domain %p %s", - devAlias, vm, vm->def->name); - - if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) - goto cleanup; - - if (!virDomainObjIsActive(vm)) { - VIR_DEBUG("Domain is not running"); - goto endjob; - } - - if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) { - VIR_WARN("NIC_RX_FILTER_CHANGED event received for " - "non-existent device %s in domain %s", - devAlias, vm->def->name); - goto endjob; - } - if (dev.type != VIR_DOMAIN_DEVICE_NET) { - VIR_WARN("NIC_RX_FILTER_CHANGED event received for " - "non-network device %s in domain %s", - devAlias, vm->def->name); - goto endjob; - } - def = dev.data.net; - - if (!virDomainNetGetActualTrustGuestRxFilters(def)) { - VIR_DEBUG("ignore NIC_RX_FILTER_CHANGED event for network " - "device %s in domain %s", - def->info.alias, vm->def->name); - /* not sending "query-rx-filter" will also suppress any - * further NIC_RX_FILTER_CHANGED events for this device - */ - goto endjob; - } - - /* handle the event - send query-rx-filter and respond to it. */ - - VIR_DEBUG("process NIC_RX_FILTER_CHANGED event for network " - "device %s in domain %s", def->info.alias, vm->def->name); - - qemuDomainObjEnterMonitor(driver, vm); - ret = qemuMonitorQueryRxFilter(priv->mon, devAlias, &guestFilter); - if (qemuDomainObjExitMonitor(driver, vm) < 0) - ret = -1; - if (ret < 0) - goto endjob; - - if (virDomainNetGetActualType(def) == VIR_DOMAIN_NET_TYPE_DIRECT) { - - if (virNetDevGetRxFilter(def->ifname, &hostFilter)) { - VIR_WARN("Couldn't get current RX filter for device %s " - "while responding to NIC_RX_FILTER_CHANGED", - def->ifname); - goto endjob; - } - - /* For macvtap connections, set the following macvtap network device - * attributes to match those of the guest network device: - * - MAC address - * - Multicast MAC address table - * - Device options: - * - PROMISC - * - MULTICAST - * - ALLMULTI - */ - syncNicRxFilterMacAddr(def->ifname, guestFilter, hostFilter); - syncNicRxFilterMulticast(def->ifname, guestFilter, hostFilter); - syncNicRxFilterDeviceOptions(def->ifname, guestFilter, hostFilter); - } - - if (virDomainNetGetActualType(def) == VIR_DOMAIN_NET_TYPE_NETWORK) { - const char *brname = virDomainNetGetActualBridgeName(def); - - /* For libivrt network connections, set the following TUN/TAP network - * device attributes to match those of the guest network device: - * - QoS filters (which are based on MAC address) - */ - if (virDomainNetGetActualBandwidth(def) && - def->data.network.actual && - virNetDevBandwidthUpdateFilter(brname, &guestFilter->mac, - def->data.network.actual->class_id) < 0) - goto endjob; - } - - endjob: - qemuDomainObjEndJob(driver, vm); - - cleanup: - virNetDevRxFilterFree(hostFilter); - virNetDevRxFilterFree(guestFilter); - VIR_FREE(devAlias); - virObjectUnref(cfg); -} - - -static void -processSerialChangedEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm, - char *devAlias, - bool connected) -{ - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - virDomainChrDeviceState newstate; - virObjectEventPtr event = NULL; - virDomainDeviceDef dev; - qemuDomainObjPrivatePtr priv = vm->privateData; - - if (connected) - newstate = VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED; - else - newstate = VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED; - - VIR_DEBUG("Changing serial port state %s in domain %p %s", - devAlias, vm, vm->def->name); - - if (newstate == VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED && - virDomainObjIsActive(vm) && priv->agent) { - /* peek into the domain definition to find the channel */ - if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) == 0 && - dev.type == VIR_DOMAIN_DEVICE_CHR && - dev.data.chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL && - dev.data.chr->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO && - STREQ_NULLABLE(dev.data.chr->target.name, "org.qemu.guest_agent.0")) - /* Close agent monitor early, so that other threads - * waiting for the agent to reply can finish and our - * job we acquire below can succeed. */ - qemuAgentNotifyClose(priv->agent); - - /* now discard the data, since it may possibly change once we unlock - * while entering the job */ - memset(&dev, 0, sizeof(dev)); - } - - if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) - goto cleanup; - - if (!virDomainObjIsActive(vm)) { - VIR_DEBUG("Domain is not running"); - goto endjob; - } - - if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) - goto endjob; - - /* we care only about certain devices */ - if (dev.type != VIR_DOMAIN_DEVICE_CHR || - dev.data.chr->deviceType != VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL || - dev.data.chr->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) - goto endjob; - - dev.data.chr->state = newstate; - - if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) - VIR_WARN("unable to save status of domain %s after updating state of " - "channel %s", vm->def->name, devAlias); - - if (STREQ_NULLABLE(dev.data.chr->target.name, "org.qemu.guest_agent.0")) { - if (newstate == VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED) { - if (qemuConnectAgent(driver, vm) < 0) - goto endjob; - } else { - if (priv->agent) { - qemuAgentClose(priv->agent); - priv->agent = NULL; - } - priv->agentError = false; - } - - event = virDomainEventAgentLifecycleNewFromObj(vm, newstate, - VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL); - qemuDomainEventQueue(driver, event); - } - - endjob: - qemuDomainObjEndJob(driver, vm); - - cleanup: - VIR_FREE(devAlias); - virObjectUnref(cfg); - -} - - -static void -processBlockJobEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm, - char *diskAlias, - int type, - int status) -{ - virDomainDiskDefPtr disk; - - if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) - goto cleanup; - - if (!virDomainObjIsActive(vm)) { - VIR_DEBUG("Domain is not running"); - goto endjob; - } - - if ((disk = qemuProcessFindDomainDiskByAlias(vm, diskAlias))) - qemuBlockJobEventProcess(driver, vm, disk, QEMU_ASYNC_JOB_NONE, type, status); - - endjob: - qemuDomainObjEndJob(driver, vm); - cleanup: - VIR_FREE(diskAlias); -} - - -static void -processMonitorEOFEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm) -{ - qemuDomainObjPrivatePtr priv = vm->privateData; - int eventReason = VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN; - int stopReason = VIR_DOMAIN_SHUTOFF_SHUTDOWN; - const char *auditReason = "shutdown"; - unsigned int stopFlags = 0; - virObjectEventPtr event = NULL; - - if (qemuProcessBeginStopJob(driver, vm, QEMU_JOB_DESTROY, true) < 0) - return; - - if (!virDomainObjIsActive(vm)) { - VIR_DEBUG("Domain %p '%s' is not active, ignoring EOF", - vm, vm->def->name); - goto endjob; - } - - if (priv->monJSON && !priv->gotShutdown) { - VIR_DEBUG("Monitor connection to '%s' closed without SHUTDOWN event; " - "assuming the domain crashed", vm->def->name); - eventReason = VIR_DOMAIN_EVENT_STOPPED_FAILED; - stopReason = VIR_DOMAIN_SHUTOFF_CRASHED; - auditReason = "failed"; - } - - if (priv->job.asyncJob == QEMU_ASYNC_JOB_MIGRATION_IN) { - stopFlags |= VIR_QEMU_PROCESS_STOP_MIGRATED; - qemuMigrationErrorSave(driver, vm->def->name, - qemuMonitorLastError(priv->mon)); - } - - event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED, - eventReason); - qemuProcessStop(driver, vm, stopReason, QEMU_ASYNC_JOB_NONE, stopFlags); - virDomainAuditStop(vm, auditReason); - qemuDomainEventQueue(driver, event); - - endjob: - qemuDomainRemoveInactive(driver, vm); - qemuDomainObjEndJob(driver, vm); -} static void qemuProcessEventHandler(void *data, void *opaque) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index ee8bae5..d2b5fe8 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -36,6 +36,7 @@ #include "qemu_processpriv.h" #include "qemu_alias.h" #include "qemu_block.h" +#include "qemu_blockjob.h" #include "qemu_domain.h" #include "qemu_domain_address.h" #include "qemu_cgroup.h" @@ -87,6 +88,18 @@ typedef struct { void (*handler_func)(qemuEventPtr ev, void *opaque); } qemuEventFuncTable; +VIR_ENUM_IMPL(qemuSaveCompression, QEMU_SAVE_FORMAT_LAST, + "raw", + "gzip", + "bzip2", + "xz", + "lzop") + +VIR_ENUM_IMPL(qemuDumpFormat, VIR_DOMAIN_CORE_DUMP_FORMAT_LAST, + "elf", + "kdump-zlib", + "kdump-lzo", + "kdump-snappy") /** * qemuProcessRemoveDomainStatus @@ -7458,3 +7471,1123 @@ qemuProcessReconnectAll(virConnectPtr conn, virQEMUDriverPtr driver) struct qemuProcessReconnectData data = {.conn = conn, .driver = driver}; virDomainObjListForEach(driver->domains, qemuProcessReconnectHelper, &data); } + +static int +qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid, + bool dynamicOwnership, + const char *path, int oflags, + bool *needUnlink, bool *bypassSecurityDriver) +{ + struct stat sb; + bool is_reg = true; + bool need_unlink = false; + bool bypass_security = false; + unsigned int vfoflags = 0; + int fd = -1; + int path_shared = virFileIsSharedFS(path); + uid_t uid = geteuid(); + gid_t gid = getegid(); + + /* path might be a pre-existing block dev, in which case + * we need to skip the create step, and also avoid unlink + * in the failure case */ + if (oflags & O_CREAT) { + need_unlink = true; + + /* Don't force chown on network-shared FS + * as it is likely to fail. */ + if (path_shared <= 0 || dynamicOwnership) + vfoflags |= VIR_FILE_OPEN_FORCE_OWNER; + + if (stat(path, &sb) == 0) { + /* It already exists, we don't want to delete it on error */ + need_unlink = false; + + is_reg = !!S_ISREG(sb.st_mode); + /* If the path is regular file which exists + * already and dynamic_ownership is off, we don't + * want to change its ownership, just open it as-is */ + if (is_reg && !dynamicOwnership) { + uid = sb.st_uid; + gid = sb.st_gid; + } + } + } + + /* First try creating the file as root */ + if (!is_reg) { + if ((fd = open(path, oflags & ~O_CREAT)) < 0) { + fd = -errno; + goto error; + } + } else { + if ((fd = virFileOpenAs(path, oflags, S_IRUSR | S_IWUSR, uid, gid, + vfoflags | VIR_FILE_OPEN_NOFORK)) < 0) { + /* If we failed as root, and the error was permission-denied + (EACCES or EPERM), assume it's on a network-connected share + where root access is restricted (eg, root-squashed NFS). If the + qemu user is non-root, just set a flag to + bypass security driver shenanigans, and retry the operation + after doing setuid to qemu user */ + if ((fd != -EACCES && fd != -EPERM) || fallback_uid == geteuid()) + goto error; + + /* On Linux we can also verify the FS-type of the directory. */ + switch (path_shared) { + case 1: + /* it was on a network share, so we'll continue + * as outlined above + */ + break; + + case -1: + virReportSystemError(-fd, oflags & O_CREAT + ? _("Failed to create file " + "'%s': couldn't determine fs type") + : _("Failed to open file " + "'%s': couldn't determine fs type"), + path); + goto cleanup; + + case 0: + default: + /* local file - log the error returned by virFileOpenAs */ + goto error; + } + + /* If we created the file above, then we need to remove it; + * otherwise, the next attempt to create will fail. If the + * file had already existed before we got here, then we also + * don't want to delete it and allow the following to succeed + * or fail based on existing protections + */ + if (need_unlink) + unlink(path); + + /* Retry creating the file as qemu user */ + + /* Since we're passing different modes... */ + vfoflags |= VIR_FILE_OPEN_FORCE_MODE; + + if ((fd = virFileOpenAs(path, oflags, + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, + fallback_uid, fallback_gid, + vfoflags | VIR_FILE_OPEN_FORK)) < 0) { + virReportSystemError(-fd, oflags & O_CREAT + ? _("Error from child process creating '%s'") + : _("Error from child process opening '%s'"), + path); + goto cleanup; + } + + /* Since we had to setuid to create the file, and the fstype + is NFS, we assume it's a root-squashing NFS share, and that + the security driver stuff would have failed anyway */ + + bypass_security = true; + } + } + cleanup: + if (needUnlink) + *needUnlink = need_unlink; + if (bypassSecurityDriver) + *bypassSecurityDriver = bypass_security; + return fd; + + error: + virReportSystemError(-fd, oflags & O_CREAT + ? _("Failed to create file '%s'") + : _("Failed to open file '%s'"), + path); + goto cleanup; +} + +/** + * qemuOpenFile: + * @driver: driver object + * @vm: domain object + * @path: path to file to open + * @oflags: flags for opening/creation of the file + * @needUnlink: set to true if file was created by this function + * @bypassSecurityDriver: optional pointer to a boolean that will be set to true + * if security driver operations are pointless (due to + * NFS mount) + * + * Internal function to properly create or open existing files, with + * ownership affected by qemu driver setup and domain DAC label. + * + * Returns the file descriptor on success and negative errno on failure. + * + * This function should not be used on storage sources. Use + * qemuDomainStorageFileInit and storage driver APIs if possible. + **/ +int +qemuOpenFile(virQEMUDriverPtr driver, + virDomainObjPtr vm, + const char *path, + int oflags, + bool *needUnlink, + bool *bypassSecurityDriver) +{ + int ret = -1; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + uid_t user = cfg->user; + gid_t group = cfg->group; + bool dynamicOwnership = cfg->dynamicOwnership; + virSecurityLabelDefPtr seclabel; + + virObjectUnref(cfg); + + /* TODO: Take imagelabel into account? */ + if (vm && + (seclabel = virDomainDefGetSecurityLabelDef(vm->def, "dac")) != NULL && + seclabel->label != NULL && + (virParseOwnershipIds(seclabel->label, &user, &group) < 0)) + goto cleanup; + + ret = qemuOpenFileAs(user, group, dynamicOwnership, + path, oflags, needUnlink, bypassSecurityDriver); + + cleanup: + return ret; +} + +/* qemuGetCompressionProgram: + * @imageFormat: String representation from qemu.conf for the compression + * image format being used (dump, save, or snapshot). + * @compresspath: Pointer to a character string to store the fully qualified + * path from virFindFileInPath. + * @styleFormat: String representing the style of format (dump, save, snapshot) + * @use_raw_on_fail: Boolean indicating how to handle the error path. For + * callers that are OK with invalid data or inability to + * find the compression program, just return a raw format + * and let the path remain as NULL. + * + * Returns: + * virQEMUSaveFormat - Integer representation of the compression + * program to be used for particular style + * (e.g. dump, save, or snapshot). + * QEMU_SAVE_FORMAT_RAW - If there is no qemu.conf imageFormat value or + * no there was an error, then just return RAW + * indicating none. + **/ +int ATTRIBUTE_NONNULL(2) +qemuGetCompressionProgram(const char *imageFormat, + char **compresspath, + const char *styleFormat, + bool use_raw_on_fail) +{ + int ret; + + *compresspath = NULL; + + if (!imageFormat) + return QEMU_SAVE_FORMAT_RAW; + + if ((ret = qemuSaveCompressionTypeFromString(imageFormat)) < 0) + goto error; + + if (ret == QEMU_SAVE_FORMAT_RAW) + return QEMU_SAVE_FORMAT_RAW; + + if (!(*compresspath = virFindFileInPath(imageFormat))) + goto error; + + return ret; + + error: + if (ret < 0) { + if (use_raw_on_fail) + VIR_WARN("Invalid %s image format specified in " + "configuration file, using raw", + styleFormat); + else + virReportError(VIR_ERR_OPERATION_FAILED, + _("Invalid %s image format specified " + "in configuration file"), + styleFormat); + } else { + if (use_raw_on_fail) + VIR_WARN("Compression program for %s image format in " + "configuration file isn't available, using raw", + styleFormat); + else + virReportError(VIR_ERR_OPERATION_FAILED, + _("Compression program for %s image format " + "in configuration file isn't available"), + styleFormat); + } + + /* Use "raw" as the format if the specified format is not valid, + * or the compress program is not available. */ + if (use_raw_on_fail) + return QEMU_SAVE_FORMAT_RAW; + + return -1; +} + + +static int qemuDumpToFd(virQEMUDriverPtr driver, virDomainObjPtr vm, + int fd, qemuDomainAsyncJob asyncJob, + const char *dumpformat) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + int ret = -1; + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DUMP_GUEST_MEMORY)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("dump-guest-memory is not supported")); + return -1; + } + + if (qemuSecuritySetImageFDLabel(driver->securityManager, vm->def, fd) < 0) + return -1; + + VIR_FREE(priv->job.current); + priv->job.dump_memory_only = true; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + return -1; + + if (dumpformat) { + ret = qemuMonitorGetDumpGuestMemoryCapability(priv->mon, dumpformat); + + if (ret <= 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("unsupported dumpformat '%s' " + "for this QEMU binary"), + dumpformat); + ret = -1; + goto cleanup; + } + } + + ret = qemuMonitorDumpToFd(priv->mon, fd, dumpformat); + + cleanup: + ignore_value(qemuDomainObjExitMonitor(driver, vm)); + + return ret; +} + +int +doCoreDump(virQEMUDriverPtr driver, + virDomainObjPtr vm, + const char *path, + unsigned int dump_flags, + unsigned int dumpformat) +{ + int fd = -1; + int ret = -1; + virFileWrapperFdPtr wrapperFd = NULL; + int directFlag = 0; + unsigned int flags = VIR_FILE_WRAPPER_NON_BLOCKING; + const char *memory_dump_format = NULL; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + char *compressedpath = NULL; + + /* We reuse "save" flag for "dump" here. Then, we can support the same + * format in "save" and "dump". This path doesn't need the compression + * program to exist and can ignore the return value - it only cares to + * get the compressedpath */ + ignore_value(qemuGetCompressionProgram(cfg->dumpImageFormat, + &compressedpath, + "dump", true)); + + /* Create an empty file with appropriate ownership. */ + if (dump_flags & VIR_DUMP_BYPASS_CACHE) { + flags |= VIR_FILE_WRAPPER_BYPASS_CACHE; + directFlag = virFileDirectFdFlag(); + if (directFlag < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("bypass cache unsupported by this system")); + goto cleanup; + } + } + /* Core dumps usually imply last-ditch analysis efforts are + * desired, so we intentionally do not unlink even if a file was + * created. */ + if ((fd = qemuOpenFile(driver, vm, path, + O_CREAT | O_TRUNC | O_WRONLY | directFlag, + NULL, NULL)) < 0) + goto cleanup; + + if (!(wrapperFd = virFileWrapperFdNew(&fd, path, flags))) + goto cleanup; + + if (dump_flags & VIR_DUMP_MEMORY_ONLY) { + if (!(memory_dump_format = qemuDumpFormatTypeToString(dumpformat))) { + virReportError(VIR_ERR_INVALID_ARG, + _("unknown dumpformat '%d'"), dumpformat); + goto cleanup; + } + + /* qemu dumps in "elf" without dumpformat set */ + if (STREQ(memory_dump_format, "elf")) + memory_dump_format = NULL; + + ret = qemuDumpToFd(driver, vm, fd, QEMU_ASYNC_JOB_DUMP, + memory_dump_format); + } else { + if (dumpformat != VIR_DOMAIN_CORE_DUMP_FORMAT_RAW) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("kdump-compressed format is only supported with " + "memory-only dump")); + goto cleanup; + } + + if (!qemuMigrationIsAllowed(driver, vm, false, 0)) + goto cleanup; + + ret = qemuMigrationToFile(driver, vm, fd, compressedpath, + QEMU_ASYNC_JOB_DUMP); + } + + if (ret < 0) + goto cleanup; + + if (VIR_CLOSE(fd) < 0) { + virReportSystemError(errno, + _("unable to close file %s"), + path); + goto cleanup; + } + if (qemuFileWrapperFDClose(vm, wrapperFd) < 0) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FORCE_CLOSE(fd); + if (ret != 0) + unlink(path); + virFileWrapperFdFree(wrapperFd); + VIR_FREE(compressedpath); + virObjectUnref(cfg); + return ret; +} + +int +qemuFileWrapperFDClose(virDomainObjPtr vm, + virFileWrapperFdPtr fd) +{ + int ret; + + /* virFileWrapperFd uses iohelper to write data onto disk. + * However, iohelper calls fdatasync() which may take ages to + * finish. Therefore, we shouldn't be waiting with the domain + * object locked. */ + + /* XXX Currently, this function is intended for *Save() only + * as restore needs some reworking before it's ready for + * this. */ + + virObjectUnlock(vm); + ret = virFileWrapperFdClose(fd); + virObjectLock(vm); + if (!virDomainObjIsActive(vm)) { + if (!virGetLastError()) + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("domain is no longer running")); + ret = -1; + } + return ret; +} + +static char * +getAutoDumpPath(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + char *dumpfile = NULL; + char *domname = virDomainObjGetShortName(vm->def); + char timestr[100]; + struct tm time_info; + time_t curtime = time(NULL); + virQEMUDriverConfigPtr cfg = NULL; + + if (!domname) + return NULL; + + cfg = virQEMUDriverGetConfig(driver); + + localtime_r(&curtime, &time_info); + strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info); + + ignore_value(virAsprintf(&dumpfile, "%s/%s-%s", + cfg->autoDumpPath, + domname, + timestr)); + + virObjectUnref(cfg); + VIR_FREE(domname); + return dumpfile; +} +void +processWatchdogEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + int action) +{ + int ret; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + char *dumpfile = getAutoDumpPath(driver, vm); + unsigned int flags = VIR_DUMP_MEMORY_ONLY; + + if (!dumpfile) + goto cleanup; + + switch (action) { + case VIR_DOMAIN_WATCHDOG_ACTION_DUMP: + if (qemuDomainObjBeginAsyncJob(driver, vm, + QEMU_ASYNC_JOB_DUMP, + VIR_DOMAIN_JOB_OPERATION_DUMP) < 0) { + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + flags |= cfg->autoDumpBypassCache ? VIR_DUMP_BYPASS_CACHE: 0; + if ((ret = doCoreDump(driver, vm, dumpfile, flags, + VIR_DOMAIN_CORE_DUMP_FORMAT_RAW)) < 0) + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("Dump failed")); + + ret = qemuProcessStartCPUs(driver, vm, NULL, + VIR_DOMAIN_RUNNING_UNPAUSED, + QEMU_ASYNC_JOB_DUMP); + + if (ret < 0) + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("Resuming after dump failed")); + break; + default: + goto cleanup; + } + + endjob: + qemuDomainObjEndAsyncJob(driver, vm); + + cleanup: + VIR_FREE(dumpfile); + virObjectUnref(cfg); +} + +static int +doCoreDumpToAutoDumpPath(virQEMUDriverPtr driver, + virDomainObjPtr vm, + unsigned int flags) +{ + int ret = -1; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + char *dumpfile = getAutoDumpPath(driver, vm); + + if (!dumpfile) + goto cleanup; + + flags |= cfg->autoDumpBypassCache ? VIR_DUMP_BYPASS_CACHE: 0; + if ((ret = doCoreDump(driver, vm, dumpfile, flags, + VIR_DOMAIN_CORE_DUMP_FORMAT_RAW)) < 0) + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("Dump failed")); + cleanup: + VIR_FREE(dumpfile); + virObjectUnref(cfg); + return ret; +} + +static void +qemuProcessGuestPanicEventInfo(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuMonitorEventPanicInfoPtr info) +{ + char *msg = qemuMonitorGuestPanicEventInfoFormatMsg(info); + char *timestamp = virTimeStringNow(); + + if (msg && timestamp) + qemuDomainLogAppendMessage(driver, vm, "%s: panic %s\n", timestamp, msg); + + VIR_FREE(timestamp); + VIR_FREE(msg); +} + +void +processGuestPanicEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + int action, + qemuMonitorEventPanicInfoPtr info) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virObjectEventPtr event = NULL; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + bool removeInactive = false; + + if (qemuDomainObjBeginAsyncJob(driver, vm, QEMU_ASYNC_JOB_DUMP, + VIR_DOMAIN_JOB_OPERATION_DUMP) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Ignoring GUEST_PANICKED event from inactive domain %s", + vm->def->name); + goto endjob; + } + + if (info) + qemuProcessGuestPanicEventInfo(driver, vm, info); + + virDomainObjSetState(vm, VIR_DOMAIN_CRASHED, VIR_DOMAIN_CRASHED_PANICKED); + + event = virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_CRASHED, + VIR_DOMAIN_EVENT_CRASHED_PANICKED); + + qemuDomainEventQueue(driver, event); + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) { + VIR_WARN("Unable to save status on vm %s after state change", + vm->def->name); + } + + if (virDomainLockProcessPause(driver->lockManager, vm, &priv->lockState) < 0) + VIR_WARN("Unable to release lease on %s", vm->def->name); + VIR_DEBUG("Preserving lock state '%s'", NULLSTR(priv->lockState)); + + switch (action) { + case VIR_DOMAIN_LIFECYCLE_CRASH_COREDUMP_DESTROY: + if (doCoreDumpToAutoDumpPath(driver, vm, VIR_DUMP_MEMORY_ONLY) < 0) + goto endjob; + ATTRIBUTE_FALLTHROUGH; + + case VIR_DOMAIN_LIFECYCLE_CRASH_DESTROY: + qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_CRASHED, + QEMU_ASYNC_JOB_DUMP, 0); + event = virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_CRASHED); + + qemuDomainEventQueue(driver, event); + virDomainAuditStop(vm, "destroyed"); + removeInactive = true; + break; + + case VIR_DOMAIN_LIFECYCLE_CRASH_COREDUMP_RESTART: + if (doCoreDumpToAutoDumpPath(driver, vm, VIR_DUMP_MEMORY_ONLY) < 0) + goto endjob; + ATTRIBUTE_FALLTHROUGH; + + case VIR_DOMAIN_LIFECYCLE_CRASH_RESTART: + qemuDomainSetFakeReboot(driver, vm, true); + qemuProcessShutdownOrReboot(driver, vm); + break; + + case VIR_DOMAIN_LIFECYCLE_CRASH_PRESERVE: + break; + + default: + break; + } + + endjob: + qemuDomainObjEndAsyncJob(driver, vm); + if (removeInactive) + qemuDomainRemoveInactiveJob(driver, vm); + + cleanup: + virObjectUnref(cfg); +} + +void +processDeviceDeletedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virDomainDeviceDef dev; + + VIR_DEBUG("Removing device %s from domain %p %s", + devAlias, vm, vm->def->name); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if (STRPREFIX(devAlias, "vcpu")) { + qemuDomainRemoveVcpuAlias(driver, vm, devAlias); + } else { + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) + goto endjob; + + if (qemuDomainRemoveDevice(driver, vm, &dev) < 0) + goto endjob; + } + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) + VIR_WARN("unable to save domain status after removing device %s", + devAlias); + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + VIR_FREE(devAlias); + virObjectUnref(cfg); +} + + + + +void +syncNicRxFilterMacAddr(char *ifname, virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter) +{ + char newMacStr[VIR_MAC_STRING_BUFLEN]; + + if (virMacAddrCmp(&hostFilter->mac, &guestFilter->mac)) { + virMacAddrFormat(&guestFilter->mac, newMacStr); + + /* set new MAC address from guest to associated macvtap device */ + if (virNetDevSetMAC(ifname, &guestFilter->mac) < 0) { + VIR_WARN("Couldn't set new MAC address %s to device %s " + "while responding to NIC_RX_FILTER_CHANGED", + newMacStr, ifname); + } else { + VIR_DEBUG("device %s MAC address set to %s", ifname, newMacStr); + } + } +} + +static void +syncNicRxFilterGuestMulticast(char *ifname, virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter) +{ + size_t i, j; + bool found; + char macstr[VIR_MAC_STRING_BUFLEN]; + + for (i = 0; i < guestFilter->multicast.nTable; i++) { + found = false; + + for (j = 0; j < hostFilter->multicast.nTable; j++) { + if (virMacAddrCmp(&guestFilter->multicast.table[i], + &hostFilter->multicast.table[j]) == 0) { + found = true; + break; + } + } + + if (!found) { + virMacAddrFormat(&guestFilter->multicast.table[i], macstr); + + if (virNetDevAddMulti(ifname, &guestFilter->multicast.table[i]) < 0) { + VIR_WARN("Couldn't add new multicast MAC address %s to " + "device %s while responding to NIC_RX_FILTER_CHANGED", + macstr, ifname); + } else { + VIR_DEBUG("Added multicast MAC %s to %s interface", + macstr, ifname); + } + } + } +} + +static void +syncNicRxFilterHostMulticast(char *ifname, virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter) +{ + size_t i, j; + bool found; + char macstr[VIR_MAC_STRING_BUFLEN]; + + for (i = 0; i < hostFilter->multicast.nTable; i++) { + found = false; + + for (j = 0; j < guestFilter->multicast.nTable; j++) { + if (virMacAddrCmp(&hostFilter->multicast.table[i], + &guestFilter->multicast.table[j]) == 0) { + found = true; + break; + } + } + + if (!found) { + virMacAddrFormat(&hostFilter->multicast.table[i], macstr); + + if (virNetDevDelMulti(ifname, &hostFilter->multicast.table[i]) < 0) { + VIR_WARN("Couldn't delete multicast MAC address %s from " + "device %s while responding to NIC_RX_FILTER_CHANGED", + macstr, ifname); + } else { + VIR_DEBUG("Deleted multicast MAC %s from %s interface", + macstr, ifname); + } + } + } +} + + +static void +syncNicRxFilterPromiscMode(char *ifname, + virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter) +{ + bool promisc; + bool setpromisc = false; + + /* Set macvtap promisc mode to true if the guest has vlans defined */ + /* or synchronize the macvtap promisc mode if different from guest */ + if (guestFilter->vlan.nTable > 0) { + if (!hostFilter->promiscuous) { + setpromisc = true; + promisc = true; + } + } else if (hostFilter->promiscuous != guestFilter->promiscuous) { + setpromisc = true; + promisc = guestFilter->promiscuous; + } + + if (setpromisc) { + if (virNetDevSetPromiscuous(ifname, promisc) < 0) { + VIR_WARN("Couldn't set PROMISC flag to %s for device %s " + "while responding to NIC_RX_FILTER_CHANGED", + promisc ? "true" : "false", ifname); + } + } +} + +static void +syncNicRxFilterMultiMode(char *ifname, virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter) +{ + if (hostFilter->multicast.mode != guestFilter->multicast.mode) { + switch (guestFilter->multicast.mode) { + case VIR_NETDEV_RX_FILTER_MODE_ALL: + if (virNetDevSetRcvAllMulti(ifname, true)) { + + VIR_WARN("Couldn't set allmulticast flag to 'on' for " + "device %s while responding to " + "NIC_RX_FILTER_CHANGED", ifname); + } + break; + + case VIR_NETDEV_RX_FILTER_MODE_NORMAL: + if (virNetDevSetRcvMulti(ifname, true)) { + + VIR_WARN("Couldn't set multicast flag to 'on' for " + "device %s while responding to " + "NIC_RX_FILTER_CHANGED", ifname); + } + + if (virNetDevSetRcvAllMulti(ifname, false)) { + VIR_WARN("Couldn't set allmulticast flag to 'off' for " + "device %s while responding to " + "NIC_RX_FILTER_CHANGED", ifname); + } + break; + + case VIR_NETDEV_RX_FILTER_MODE_NONE: + if (virNetDevSetRcvAllMulti(ifname, false)) { + VIR_WARN("Couldn't set allmulticast flag to 'off' for " + "device %s while responding to " + "NIC_RX_FILTER_CHANGED", ifname); + } + + if (virNetDevSetRcvMulti(ifname, false)) { + VIR_WARN("Couldn't set multicast flag to 'off' for " + "device %s while responding to " + "NIC_RX_FILTER_CHANGED", + ifname); + } + break; + } + } +} + +void +syncNicRxFilterDeviceOptions(char *ifname, virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter) +{ + syncNicRxFilterPromiscMode(ifname, guestFilter, hostFilter); + syncNicRxFilterMultiMode(ifname, guestFilter, hostFilter); +} + + +void +syncNicRxFilterMulticast(char *ifname, + virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter) +{ + syncNicRxFilterGuestMulticast(ifname, guestFilter, hostFilter); + syncNicRxFilterHostMulticast(ifname, guestFilter, hostFilter); +} + +void +processNicRxFilterChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + qemuDomainObjPrivatePtr priv = vm->privateData; + virDomainDeviceDef dev; + virDomainNetDefPtr def; + virNetDevRxFilterPtr guestFilter = NULL; + virNetDevRxFilterPtr hostFilter = NULL; + int ret; + + VIR_DEBUG("Received NIC_RX_FILTER_CHANGED event for device %s " + "from domain %p %s", + devAlias, vm, vm->def->name); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) { + VIR_WARN("NIC_RX_FILTER_CHANGED event received for " + "non-existent device %s in domain %s", + devAlias, vm->def->name); + goto endjob; + } + if (dev.type != VIR_DOMAIN_DEVICE_NET) { + VIR_WARN("NIC_RX_FILTER_CHANGED event received for " + "non-network device %s in domain %s", + devAlias, vm->def->name); + goto endjob; + } + def = dev.data.net; + + if (!virDomainNetGetActualTrustGuestRxFilters(def)) { + VIR_DEBUG("ignore NIC_RX_FILTER_CHANGED event for network " + "device %s in domain %s", + def->info.alias, vm->def->name); + /* not sending "query-rx-filter" will also suppress any + * further NIC_RX_FILTER_CHANGED events for this device + */ + goto endjob; + } + + /* handle the event - send query-rx-filter and respond to it. */ + + VIR_DEBUG("process NIC_RX_FILTER_CHANGED event for network " + "device %s in domain %s", def->info.alias, vm->def->name); + + qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorQueryRxFilter(priv->mon, devAlias, &guestFilter); + if (qemuDomainObjExitMonitor(driver, vm) < 0) + ret = -1; + if (ret < 0) + goto endjob; + + if (virDomainNetGetActualType(def) == VIR_DOMAIN_NET_TYPE_DIRECT) { + + if (virNetDevGetRxFilter(def->ifname, &hostFilter)) { + VIR_WARN("Couldn't get current RX filter for device %s " + "while responding to NIC_RX_FILTER_CHANGED", + def->ifname); + goto endjob; + } + + /* For macvtap connections, set the following macvtap network device + * attributes to match those of the guest network device: + * - MAC address + * - Multicast MAC address table + * - Device options: + * - PROMISC + * - MULTICAST + * - ALLMULTI + */ + syncNicRxFilterMacAddr(def->ifname, guestFilter, hostFilter); + syncNicRxFilterMulticast(def->ifname, guestFilter, hostFilter); + syncNicRxFilterDeviceOptions(def->ifname, guestFilter, hostFilter); + } + + if (virDomainNetGetActualType(def) == VIR_DOMAIN_NET_TYPE_NETWORK) { + const char *brname = virDomainNetGetActualBridgeName(def); + + /* For libivrt network connections, set the following TUN/TAP network + * device attributes to match those of the guest network device: + * - QoS filters (which are based on MAC address) + */ + if (virDomainNetGetActualBandwidth(def) && + def->data.network.actual && + virNetDevBandwidthUpdateFilter(brname, &guestFilter->mac, + def->data.network.actual->class_id) < 0) + goto endjob; + } + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + virNetDevRxFilterFree(hostFilter); + virNetDevRxFilterFree(guestFilter); + VIR_FREE(devAlias); + virObjectUnref(cfg); +} + +void +processSerialChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias, + bool connected) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virDomainChrDeviceState newstate; + virObjectEventPtr event = NULL; + virDomainDeviceDef dev; + qemuDomainObjPrivatePtr priv = vm->privateData; + + if (connected) + newstate = VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED; + else + newstate = VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED; + + VIR_DEBUG("Changing serial port state %s in domain %p %s", + devAlias, vm, vm->def->name); + + if (newstate == VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED && + virDomainObjIsActive(vm) && priv->agent) { + /* peek into the domain definition to find the channel */ + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) == 0 && + dev.type == VIR_DOMAIN_DEVICE_CHR && + dev.data.chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL && + dev.data.chr->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO && + STREQ_NULLABLE(dev.data.chr->target.name, "org.qemu.guest_agent.0")) + /* Close agent monitor early, so that other threads + * waiting for the agent to reply can finish and our + * job we acquire below can succeed. */ + qemuAgentNotifyClose(priv->agent); + + /* now discard the data, since it may possibly change once we unlock + * while entering the job */ + memset(&dev, 0, sizeof(dev)); + } + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) + goto endjob; + + /* we care only about certain devices */ + if (dev.type != VIR_DOMAIN_DEVICE_CHR || + dev.data.chr->deviceType != VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL || + dev.data.chr->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) + goto endjob; + + dev.data.chr->state = newstate; + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) + VIR_WARN("unable to save status of domain %s after updating state of " + "channel %s", vm->def->name, devAlias); + + if (STREQ_NULLABLE(dev.data.chr->target.name, "org.qemu.guest_agent.0")) { + if (newstate == VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED) { + if (qemuConnectAgent(driver, vm) < 0) + goto endjob; + } else { + if (priv->agent) { + qemuAgentClose(priv->agent); + priv->agent = NULL; + } + priv->agentError = false; + } + + event = virDomainEventAgentLifecycleNewFromObj(vm, newstate, + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL); + qemuDomainEventQueue(driver, event); + } + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + VIR_FREE(devAlias); + virObjectUnref(cfg); + +} + +void +processBlockJobEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *diskAlias, + int type, + int status) +{ + virDomainDiskDefPtr disk; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if ((disk = qemuProcessFindDomainDiskByAlias(vm, diskAlias))) + qemuBlockJobEventProcess(driver, vm, disk, QEMU_ASYNC_JOB_NONE, type, status); + + endjob: + qemuDomainObjEndJob(driver, vm); + cleanup: + VIR_FREE(diskAlias); +} + +void +processMonitorEOFEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + int eventReason = VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN; + int stopReason = VIR_DOMAIN_SHUTOFF_SHUTDOWN; + const char *auditReason = "shutdown"; + unsigned int stopFlags = 0; + virObjectEventPtr event = NULL; + + if (qemuProcessBeginStopJob(driver, vm, QEMU_JOB_DESTROY, true) < 0) + return; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain %p '%s' is not active, ignoring EOF", + vm, vm->def->name); + goto endjob; + } + + if (priv->monJSON && !priv->gotShutdown) { + VIR_DEBUG("Monitor connection to '%s' closed without SHUTDOWN event; " + "assuming the domain crashed", vm->def->name); + eventReason = VIR_DOMAIN_EVENT_STOPPED_FAILED; + stopReason = VIR_DOMAIN_SHUTOFF_CRASHED; + auditReason = "failed"; + } + + if (priv->job.asyncJob == QEMU_ASYNC_JOB_MIGRATION_IN) { + stopFlags |= VIR_QEMU_PROCESS_STOP_MIGRATED; + qemuMigrationErrorSave(driver, vm->def->name, + qemuMonitorLastError(priv->mon)); + } + + event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED, + eventReason); + qemuProcessStop(driver, vm, stopReason, QEMU_ASYNC_JOB_NONE, stopFlags); + virDomainAuditStop(vm, auditReason); + qemuDomainEventQueue(driver, event); + + endjob: + qemuDomainRemoveInactive(driver, vm); + qemuDomainObjEndJob(driver, vm); +} diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h index a2bbc4f..95007b5 100644 --- a/src/qemu/qemu_process.h +++ b/src/qemu/qemu_process.h @@ -25,6 +25,92 @@ # include "qemu_conf.h" # include "qemu_domain.h" +typedef enum { + QEMU_SAVE_FORMAT_RAW = 0, + QEMU_SAVE_FORMAT_GZIP = 1, + QEMU_SAVE_FORMAT_BZIP2 = 2, + /* + * Deprecated by xz and never used as part of a release + * QEMU_SAVE_FORMAT_LZMA + */ + QEMU_SAVE_FORMAT_XZ = 3, + QEMU_SAVE_FORMAT_LZOP = 4, + /* Note: add new members only at the end. + These values are used in the on-disk format. + Do not change or re-use numbers. */ + + QEMU_SAVE_FORMAT_LAST +} virQEMUSaveFormat; + +VIR_ENUM_DECL(qemuSaveCompression) +VIR_ENUM_DECL(qemuDumpFormat) + +int +qemuFileWrapperFDClose(virDomainObjPtr vm, + virFileWrapperFdPtr fd); + +int +qemuGetCompressionProgram(const char *imageFormat, + char **compresspath, + const char *styleFormat, + bool use_raw_on_fail); +int +qemuOpenFile(virQEMUDriverPtr driver, + virDomainObjPtr vm, + const char *path, + int oflags, + bool *needUnlink, + bool *bypassSecurityDriver); +int +doCoreDump(virQEMUDriverPtr driver, + virDomainObjPtr vm, + const char *path, + unsigned int dump_flags, + unsigned int dumpformat); + +void +syncNicRxFilterMacAddr(char *ifname, virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter); + +void +syncNicRxFilterDeviceOptions(char *ifname, virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter); + +void +syncNicRxFilterMulticast(char *ifname, + virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter); + +void +processWatchdogEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + int action); +void +processGuestPanicEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + int action, + qemuMonitorEventPanicInfoPtr info); +void +processNicRxFilterChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias); +void +processSerialChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias, + bool connected); + +void +processBlockJobEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *diskAlias, + int type, + int status); +void processMonitorEOFEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm); +void processDeviceDeletedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias); int qemuProcessPrepareMonitorChr(virDomainChrSourceDefPtr monConfig, const char *domainDir); -- 2.9.5 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list