qga/commands-posix.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
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 <r@drigo.nl>
---
qga/commands-posix.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 5070f27..c705985 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);
--
2.47.3
Reviewed-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Best Regards,
Kostiantyn Kostiuk.
On Sat, Sep 27, 2025 at 12:49 AM Rodrigo Dias Correa <r@drigo.nl> wrote:
> 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 <r@drigo.nl>
> ---
> qga/commands-posix.c | 29 +++++++++++++++++++++++++++++
> 1 file changed, 29 insertions(+)
>
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index 5070f27..c705985 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);
> --
> 2.47.3
>
>
© 2016 - 2026 Red Hat, Inc.