From: Rodrigo Dias Correa Date: Fri, 26 Sep 2025 21:40:11 +0000 (+0200) Subject: qga: Support guest shutdown of BusyBox-based systems X-Git-Tag: v10.2.0-rc1~30^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c5b4afd4d56e9c2251e6674d8c9ae530a923ecb9;p=thirdparty%2Fqemu.git qga: Support guest shutdown of BusyBox-based systems On POSIX systems, the QEMU Guest Agent uses /sbin/shutdown to implement the command guest-shutdown. Systems based on BusyBox, such as Alpine Linux, don't have /sbin/shutdown. They have instead three separate commands: poweroff, reboot, and halt. Change the QEMU Guest Agent to, depending on the mode argument, use /sbin/{poweroff,halt,reboot} when they exist, falling back to /sbin/shutdown when they don't. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2589 Signed-off-by: Rodrigo Dias Correa Reviewed-by: Kostiantyn Kostiuk Link: https://lore.kernel.org/qemu-devel/20250926214015.120338-1-r@drigo.nl Signed-off-by: Kostiantyn Kostiuk --- diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 5070f27d75..c7059857e4 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -213,9 +213,20 @@ out: return retcode; } +static bool file_exists(const char *path) +{ + struct stat st; + return stat(path, &st) == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)); +} + +#define POWEROFF_CMD_PATH "/sbin/poweroff" +#define HALT_CMD_PATH "/sbin/halt" +#define REBOOT_CMD_PATH "/sbin/reboot" + void qmp_guest_shutdown(const char *mode, Error **errp) { const char *shutdown_flag; + const char *shutdown_cmd = NULL; Error *local_err = NULL; #ifdef CONFIG_SOLARIS @@ -234,10 +245,19 @@ void qmp_guest_shutdown(const char *mode, Error **errp) slog("guest-shutdown called, mode: %s", mode); if (!mode || strcmp(mode, "powerdown") == 0) { + if (file_exists(POWEROFF_CMD_PATH)) { + shutdown_cmd = POWEROFF_CMD_PATH; + } shutdown_flag = powerdown_flag; } else if (strcmp(mode, "halt") == 0) { + if (file_exists(HALT_CMD_PATH)) { + shutdown_cmd = HALT_CMD_PATH; + } shutdown_flag = halt_flag; } else if (strcmp(mode, "reboot") == 0) { + if (file_exists(REBOOT_CMD_PATH)) { + shutdown_cmd = REBOOT_CMD_PATH; + } shutdown_flag = reboot_flag; } else { error_setg(errp, @@ -255,6 +275,15 @@ void qmp_guest_shutdown(const char *mode, Error **errp) #endif "hypervisor initiated shutdown", (char *) NULL}; + /* + * If the specific command exists (poweroff, halt or reboot), use it instead + * of /sbin/shutdown. + */ + if (shutdown_cmd != NULL) { + argv[0] = shutdown_cmd; + argv[1] = NULL; + } + ga_run_command(argv, NULL, "shutdown", &local_err); if (local_err) { error_propagate(errp, local_err);