#include "qemu_checkpoint.h"
#include "qemu_command.h"
#include "qemu_security.h"
+#include "qemu_process.h"
#include "storage_source.h"
#include "storage_source_conf.h"
{
qemuDomainObjPrivate *priv = vm->privateData;
g_autoptr(virQEMUDriverConfig) cfg = NULL;
+ /* some flags need to be probed after the private data is freed */
+ unsigned int apiFlags = priv->backup->apiFlags;
size_t i;
for (i = 0; i < priv->backup->ndisks; i++) {
if (vm->job->asyncJob == VIR_ASYNC_JOB_BACKUP)
virDomainObjEndAsyncJob(vm);
+
+ /* Users can request that the VM is preserved after a guest OS shutdown for
+ * the duration of the backup. This is the place where we need to check if
+ * this happened and optionally terminate the VM if the guest OS is still
+ * shut down */
+ if (apiFlags & VIR_DOMAIN_BACKUP_BEGIN_PRESERVE_SHUTDOWN_DOMAIN) {
+ int reason = -1;
+ virDomainState state = virDomainObjGetState(vm, &reason);
+
+ VIR_DEBUG("state: '%u', reason:'%d'", state, reason);
+
+ if (state == VIR_DOMAIN_SHUTDOWN ||
+ (state == VIR_DOMAIN_PAUSED && reason == VIR_DOMAIN_PAUSED_SHUTTING_DOWN)) {
+ VIR_DEBUG("backup job finished terminating the previously shutdown VM");
+ ignore_value(qemuProcessShutdownOrReboot(vm));
+ }
+ }
}
int ret = -1;
g_autoptr(qemuFDPassDirect) fdpass = NULL;
- virCheckFlags(VIR_DOMAIN_BACKUP_BEGIN_REUSE_EXTERNAL, -1);
+ virCheckFlags(VIR_DOMAIN_BACKUP_BEGIN_REUSE_EXTERNAL |
+ VIR_DOMAIN_BACKUP_BEGIN_PRESERVE_SHUTDOWN_DOMAIN, -1);
if (!(def = virDomainBackupDefParseString(backupXML, priv->driver->xmlopt, 0)))
return -1;
}
+static void
+qemuProcessResetPreservedDomain(void *opaque)
+{
+ virDomainObj *vm = opaque;
+ qemuDomainObjPrivate *priv = vm->privateData;
+ virQEMUDriver *driver = priv->driver;
+ virObjectEvent *event = NULL;
+ int rc;
+
+ VIR_DEBUG("vm=%p", vm);
+
+ virObjectLock(vm);
+ if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("guest unexpectedly quit"));
+ goto endjob;
+ }
+
+ qemuDomainObjEnterMonitor(vm);
+ rc = qemuMonitorSystemReset(priv->mon);
+ qemuDomainObjExitMonitor(vm);
+
+ /* A guest-initiated OS shutdown completes qemu pauses the CPUs thus we need
+ * to also update the state */
+ virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_SHUTTING_DOWN);
+ event = virDomainEventLifecycleNewFromObj(vm,
+ VIR_DOMAIN_EVENT_SUSPENDED,
+ VIR_DOMAIN_EVENT_SUSPENDED_GUEST_SHUTDOWN);
+
+ if (rc < 0)
+ goto endjob;
+
+ endjob:
+ virDomainObjEndJob(vm);
+
+ cleanup:
+ qemuDomainSaveStatus(vm);
+ virDomainObjEndAPI(&vm);
+ virObjectEventStateQueue(driver->domainEventState, event);
+}
+
+
/**
* qemuProcessShutdownOrReboot:
* @vm: domain object
virObjectUnref(vm);
}
+ return false;
+ } else if (priv->backup && priv->backup->apiFlags & VIR_DOMAIN_BACKUP_BEGIN_PRESERVE_SHUTDOWN_DOMAIN) {
+ /* The users can request that while the 'backup' job is active (and
+ * possibly also other block jobs in the future) the qemu process will
+ * be kept around even when the guest OS shuts down, evem when the
+ * requested action is to terminate the VM.
+ *
+ * In such case we'll reset the VM and keep it paused with proper state
+ * so that users can re-start it if needed.
+ *
+ * Terminating of the qemu process once the backup job is
+ * completed/terminated (unless the guest was unpaused/restarted) is
+ * then done in qemuBackupJobTerminate by invoking this function once
+ * again.
+ */
+ g_autofree char *name = g_strdup_printf("reset-%s", vm->def->name);
+ virThread th;
+
+ VIR_DEBUG("preserving qemu process while backup job is running");
+
+ virObjectRef(vm);
+ if (virThreadCreateFull(&th,
+ false,
+ qemuProcessResetPreservedDomain,
+ name,
+ false,
+ vm) < 0) {
+ VIR_WARN("Failed to create thread to reset shutdown VM");
+ virObjectUnref(vm);
+ }
+
return false;
} else {
ignore_value(qemuProcessKill(vm, VIR_QEMU_PROCESS_KILL_NOWAIT));