[PULL 3/3] qga: Support guest shutdown of BusyBox-based systems

Kostiantyn Kostiuk posted 3 patches 2 weeks, 1 day ago
Maintainers: Michael Roth <michael.roth@amd.com>, Kostiantyn Kostiuk <kkostiuk@redhat.com>
[PULL 3/3] qga: Support guest shutdown of BusyBox-based systems
Posted by Kostiantyn Kostiuk 2 weeks, 1 day ago
From: Rodrigo Dias Correa <r@drigo.nl>

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>
Reviewed-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20250926214015.120338-1-r@drigo.nl
Signed-off-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
---
 qga/commands-posix.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

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);
-- 
2.51.1.dirty
Re: [PULL 3/3] qga: Support guest shutdown of BusyBox-based systems
Posted by Michael Tokarev 1 week, 6 days ago
On 10/30/25 16:12, Kostiantyn Kostiuk wrote:
> From: Rodrigo Dias Correa <r@drigo.nl>
> 
> 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.

FWIW, I think sbin/poweroff is universal.  But this will do it too.


> +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));
> +}

stat(2) will never return info about a symlink, so S_ISLINK here is
always 0.  It is lstat(2) wihch might return symlink info.

Not that this is a bug, just a confusing expression which will be
copy-pasted by other new users and the confusion will spread ;)

I think I'll send a fix for this, since this patch is landed in
master.

Thanks,

/mjt
Re: [PULL 3/3] qga: Support guest shutdown of BusyBox-based systems
Posted by Rodrigo Dias Correa 1 week, 5 days ago
On Sat Nov 1, 2025 at 11:04 AM CET, Michael Tokarev wrote:

> On 10/30/25 16:12, Kostiantyn Kostiuk wrote:

>> From: Rodrigo Dias Correa <r@drigo.nl>

>> 

>> +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));

>> +}

>

> stat(2) will never return info about a symlink, so S_ISLINK here is

> always 0.  It is lstat(2) wihch might return symlink info.

>
You are right, I overlooked this in stat(2) documentation.
>

> Not that this is a bug, just a confusing expression which will be

> copy-pasted by other new users and the confusion will spread ;)

>

> I think I'll send a fix for this, since this patch is landed in

> master.

I appreciate that.

Thanks,
Rodrigo

>

> Thanks,

>

> /mjt