Signed-off-by: Prerna Saxena <saxenap.ltc(a)gmail.com>
---
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