diff --git a/examples/object-events/event-test.c b/examples/object-events/event-test.c index 12690cac09..78d2008066 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -240,6 +240,12 @@ eventDetailToString(int event, case VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED: return "Finished"; + case VIR_DOMAIN_EVENT_SHUTDOWN_GUEST: + return "Guest request"; + + case VIR_DOMAIN_EVENT_SHUTDOWN_HOST: + return "Host request"; + case VIR_DOMAIN_EVENT_SHUTDOWN_LAST: break; } diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index c9e96a6c90..720db32f76 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -2983,7 +2983,16 @@ typedef enum { * Details on the cause of a 'shutdown' lifecycle event */ typedef enum { - VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED = 0, /* Guest finished shutdown sequence */ + /* Guest finished shutdown sequence */ + VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED = 0, + + /* Domain finished shutting down after request from the guest itself + * (e.g. hardware-specific action) */ + VIR_DOMAIN_EVENT_SHUTDOWN_GUEST = 1, + + /* Domain finished shutting down after request from the host (e.g. killed by + * a signal) */ + VIR_DOMAIN_EVENT_SHUTDOWN_HOST = 2, # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_SHUTDOWN_LAST diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 2148d483ed..a2de1a6c5b 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1326,13 +1326,13 @@ qemuMonitorEmitEvent(qemuMonitorPtr mon, const char *event, int -qemuMonitorEmitShutdown(qemuMonitorPtr mon) +qemuMonitorEmitShutdown(qemuMonitorPtr mon, virTristateBool guest) { int ret = -1; - VIR_DEBUG("mon=%p", mon); + VIR_DEBUG("mon=%p guest=%u", mon, guest); mon->willhangup = 1; - QEMU_MONITOR_CALLBACK(mon, ret, domainShutdown, mon->vm); + QEMU_MONITOR_CALLBACK(mon, ret, domainShutdown, mon->vm, guest); return ret; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 12f98beba7..8956bf929a 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -130,6 +130,7 @@ typedef int (*qemuMonitorDomainEventCallback)(qemuMonitorPtr mon, void *opaque); typedef int (*qemuMonitorDomainShutdownCallback)(qemuMonitorPtr mon, virDomainObjPtr vm, + virTristateBool guest, void *opaque); typedef int (*qemuMonitorDomainResetCallback)(qemuMonitorPtr mon, virDomainObjPtr vm, @@ -344,7 +345,7 @@ int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, int qemuMonitorEmitEvent(qemuMonitorPtr mon, const char *event, long long seconds, unsigned int micros, const char *details); -int qemuMonitorEmitShutdown(qemuMonitorPtr mon); +int qemuMonitorEmitShutdown(qemuMonitorPtr mon, virTristateBool guest); int qemuMonitorEmitReset(qemuMonitorPtr mon); int qemuMonitorEmitPowerdown(qemuMonitorPtr mon); int qemuMonitorEmitStop(qemuMonitorPtr mon); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 083729003b..757595dd74 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -523,9 +523,15 @@ qemuMonitorJSONKeywordStringToJSON(const char *str, const char *firstkeyword) } -static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED) +static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data) { - qemuMonitorEmitShutdown(mon); + bool guest = false; + virTristateBool guest_initiated = VIR_TRISTATE_BOOL_ABSENT; + + if (virJSONValueObjectGetBoolean(data, "guest", &guest) == 0) + guest_initiated = guest ? VIR_TRISTATE_BOOL_YES : VIR_TRISTATE_BOOL_NO; + + qemuMonitorEmitShutdown(mon, guest_initiated); } static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index ac17da6686..be031b56b9 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -634,12 +634,14 @@ qemuProcessHandleEvent(qemuMonitorPtr mon ATTRIBUTE_UNUSED, static int qemuProcessHandleShutdown(qemuMonitorPtr mon ATTRIBUTE_UNUSED, virDomainObjPtr vm, + virTristateBool guest_initiated, void *opaque) { virQEMUDriverPtr driver = opaque; qemuDomainObjPrivatePtr priv; virObjectEventPtr event = NULL; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + int detail = 0; VIR_DEBUG("vm=%p", vm); @@ -662,9 +664,24 @@ qemuProcessHandleShutdown(qemuMonitorPtr mon ATTRIBUTE_UNUSED, virDomainObjSetState(vm, VIR_DOMAIN_SHUTDOWN, VIR_DOMAIN_SHUTDOWN_UNKNOWN); + + switch (guest_initiated) { + case VIR_TRISTATE_BOOL_YES: + detail = VIR_DOMAIN_EVENT_SHUTDOWN_GUEST; + break; + + case VIR_TRISTATE_BOOL_NO: + detail = VIR_DOMAIN_EVENT_SHUTDOWN_HOST; + break; + + default: + detail = VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED; + break; + } + event = virDomainEventLifecycleNewFromObj(vm, - VIR_DOMAIN_EVENT_SHUTDOWN, - VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED); + VIR_DOMAIN_EVENT_SHUTDOWN, + detail); if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) { VIR_WARN("Unable to save status on vm %s after state change", diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 5c420213cf..ccb514ef9a 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -12257,7 +12257,9 @@ VIR_ENUM_IMPL(virshDomainEventStopped, VIR_ENUM_DECL(virshDomainEventShutdown) VIR_ENUM_IMPL(virshDomainEventShutdown, VIR_DOMAIN_EVENT_SHUTDOWN_LAST, - N_("Finished")) + N_("Finished"), + N_("Finished after guest request"), + N_("Finished after host request")) VIR_ENUM_DECL(virshDomainEventPMSuspended) VIR_ENUM_IMPL(virshDomainEventPMSuspended,