QMP quit, host signals and UI shutdown requests force QEMU to exit
by setting shutdown_action to SHUTDOWN_ACTION_POWEROFF.

This can race with guest shutdown processing. With -no-shutdown,
consider the following sequence:

1. Management sends system_powerdown, then sends SIGTERM after
   waiting for the guest to shut down.
2. QEMU starts processing a guest shutdown request.
3. SIGTERM arrives after qemu_kill_report(), while QEMU is sending
   the guest SHUTDOWN event.
4. The signal handler changes shutdown_action to POWEROFF, so QEMU
   exits instead of pausing.

Management receives SHUTDOWN with guest=true, but QEMU exits despite
-no-shutdown, without reporting a host SHUTDOWN event or printing
the signal diagnostic.

Store forced and regular shutdown requests separately, and decide
whether to exit based on the request being processed. Leave
shutdown_action under user control. A forced request arriving while
a regular request is being processed remains pending for the next
main loop iteration.

Keep qemu_shutdown_requested() checking both kinds of requests for
its Xen caller.

There is no need to clear shutdown_force_requested with qatomic_xchg():
processing a forced shutdown request always exits the main loop.

Signed-off-by: Vladimir Sementsov-Ogievskiy <[email protected]>
---
 monitor/qmp-cmds.c |  1 -
 system/runstate.c  | 24 ++++++++++++++++--------
 ui/cocoa.m         |  1 -
 ui/sdl2.c          |  2 --
 4 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c
index 7525a88e049..6d0ba75ff16 100644
--- a/monitor/qmp-cmds.c
+++ b/monitor/qmp-cmds.c
@@ -44,7 +44,6 @@ NameInfo *qmp_query_name(Error **errp)
 
 void qmp_quit(Error **errp)
 {
-    shutdown_action = SHUTDOWN_ACTION_POWEROFF;
     qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_QMP_QUIT);
 }
 
diff --git a/system/runstate.c b/system/runstate.c
index 2b28e2a30e2..5d10ff0df99 100644
--- a/system/runstate.c
+++ b/system/runstate.c
@@ -564,9 +564,9 @@ int vm_stop_force_state(RunState state)
 
 static ShutdownCause reset_requested;
 static ShutdownCause shutdown_requested;
+static ShutdownCause shutdown_force_requested;
 static int shutdown_exit_code = EXIT_SUCCESS;
 static int shutdown_signal;
-static bool force_shutdown;
 static pid_t shutdown_pid;
 static int powerdown_requested;
 static int debug_requested;
@@ -584,12 +584,12 @@ static uint32_t wakeup_reason_mask = ~(1 << 
QEMU_WAKEUP_REASON_NONE);
 
 bool qemu_shutdown_requested(void)
 {
-    return shutdown_requested;
+    return shutdown_requested || shutdown_force_requested;
 }
 
 bool qemu_force_shutdown_requested(void)
 {
-    return force_shutdown;
+    return shutdown_force_requested;
 }
 
 ShutdownCause qemu_reset_requested_get(void)
@@ -972,11 +972,12 @@ bool qemu_wakeup_suspend_enabled(void)
  */
 static void qemu_system_shutdown_request_safe(ShutdownCause reason)
 {
-    shutdown_requested = reason;
     if (reason == SHUTDOWN_CAUSE_HOST_QMP_QUIT ||
         reason == SHUTDOWN_CAUSE_HOST_SIGNAL ||
         reason == SHUTDOWN_CAUSE_HOST_UI) {
-        force_shutdown = true;
+        shutdown_force_requested = reason;
+    } else {
+        shutdown_requested = reason;
     }
     qemu_notify_event();
 }
@@ -992,7 +993,6 @@ void qemu_system_killed(int signal, pid_t pid)
 {
     shutdown_signal = signal;
     shutdown_pid = pid;
-    shutdown_action = SHUTDOWN_ACTION_POWEROFF;
     qemu_system_shutdown_request_safe(SHUTDOWN_CAUSE_HOST_SIGNAL);
 }
 
@@ -1042,6 +1042,7 @@ static bool main_loop_should_exit(int *status)
 {
     RunState r;
     ShutdownCause request;
+    bool force = false;
 
     if (qemu_debug_requested()) {
         vm_stop(RUN_STATE_DEBUG);
@@ -1049,11 +1050,18 @@ static bool main_loop_should_exit(int *status)
     if (qemu_suspend_requested()) {
         qemu_system_suspend();
     }
-    request = qatomic_xchg(&shutdown_requested, SHUTDOWN_CAUSE_NONE);
+
+    if (shutdown_force_requested) {
+        force = true;
+        request = shutdown_force_requested;
+    } else {
+        request = qatomic_xchg(&shutdown_requested, SHUTDOWN_CAUSE_NONE);
+    }
+
     if (request) {
         qemu_kill_report();
         qemu_system_shutdown(request);
-        if (shutdown_action == SHUTDOWN_ACTION_PAUSE) {
+        if (!force && shutdown_action == SHUTDOWN_ACTION_PAUSE) {
             vm_stop(RUN_STATE_SHUTDOWN);
         } else {
             if (shutdown_exit_code != EXIT_SUCCESS) {
diff --git a/ui/cocoa.m b/ui/cocoa.m
index 3751be37922..5447fb637ad 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -1221,7 +1221,6 @@ - (void)applicationWillTerminate:(NSNotification 
*)aNotification
     COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n");
 
     with_bql(^{
-        shutdown_action = SHUTDOWN_ACTION_POWEROFF;
         qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI);
     });
 
diff --git a/ui/sdl2.c b/ui/sdl2.c
index 1c97d23a47c..d647905a379 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -645,7 +645,6 @@ static void handle_windowevent(SDL_Event *ev)
                 allow_close = false;
             }
             if (allow_close) {
-                shutdown_action = SHUTDOWN_ACTION_POWEROFF;
                 qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI);
             }
         } else {
@@ -692,7 +691,6 @@ void sdl2_poll_events(struct sdl2_console *scon)
                 allow_close = false;
             }
             if (allow_close) {
-                shutdown_action = SHUTDOWN_ACTION_POWEROFF;
                 qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI);
             }
             break;
-- 
2.43.0


Reply via email to