]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
vmspawn: implement io.systemd.MachineInstance.AddStorage / RemoveStorage
authorChristian Brauner <brauner@kernel.org>
Fri, 1 May 2026 11:36:21 +0000 (13:36 +0200)
committerChristian Brauner <brauner@kernel.org>
Wed, 6 May 2026 08:30:17 +0000 (10:30 +0200)
Wire up the runtime hotplug Varlink methods on the per-VM control
socket:

  AddStorage  → take fd from the link, look up the DiskType from the
                'config' field, build a DriveInfo flagged
                QMP_DRIVE_REMOVABLE, dispatch to
                vmspawn_qmp_add_block_device(). Reply delivered async
                by on_add_device_add_complete() once the guest sees
                the device.

  RemoveStorage → forward the user-visible name to
                  vmspawn_qmp_remove_block_device(); the existing
                  device_del / DEVICE_DELETED / blockdev-del chain
                  replies on the link.

Add SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT to the server flags so
clients can push storage fds across via sd_varlink_push_fd().

Maps -EEXIST → StorageExists and -EOPNOTSUPP/-EINVAL →
ConfigNotSupported in the AddStorage handler so callers see the
specific MachineInstance errors.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
src/vmspawn/vmspawn-varlink.c

index 9aa6afeae0385151b64b51c50f14a099c1463849..a782a5c9c1a4fc60a19061c779de3dd5862beadb 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "alloc-util.h"
 #include "errno-util.h"
+#include "fd-util.h"
 #include "hashmap.h"
 #include "log.h"
 #include "path-util.h"
@@ -11,6 +12,7 @@
 #include "varlink-io.systemd.MachineInstance.h"
 #include "varlink-io.systemd.VirtualMachineInstance.h"
 #include "varlink-util.h"
+#include "vmspawn-bind-volume.h"
 #include "vmspawn-qmp.h"
 #include "vmspawn-varlink.h"
 
@@ -168,6 +170,77 @@ static int vl_method_describe(sd_varlink *link, sd_json_variant *parameters, sd_
         return qmp_execute_varlink_async(ctx, link, "query-status", /* arguments= */ NULL, on_qmp_describe_complete);
 }
 
+static int vl_method_add_storage(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+        VmspawnVarlinkContext *ctx = ASSERT_PTR(userdata);
+        int r;
+
+        struct {
+                int fd_index;
+                const char *name;
+                const char *config;
+        } p = {
+                .fd_index = -1,
+        };
+
+        static const sd_json_dispatch_field dispatch_table[] = {
+                { "fileDescriptorIndex", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int,          voffsetof(p, fd_index), SD_JSON_MANDATORY },
+                { "name",                SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string, voffsetof(p, name),     SD_JSON_MANDATORY },
+                { "config",              SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string, voffsetof(p, config),   0                 },
+                {}
+        };
+
+        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        if (machine_storage_name_split(p.name, /* ret_provider= */ NULL, /* ret_volume= */ NULL) < 0)
+                return sd_varlink_error_invalid_parameter_name(link, "name");
+
+        if (disk_type_from_bind_volume_config(p.config) < 0)
+                return sd_varlink_error(link, "io.systemd.MachineInstance.BadConfig", NULL);
+
+        if (p.fd_index < 0)
+                return sd_varlink_error_invalid_parameter_name(link, "fileDescriptorIndex");
+
+        _cleanup_close_ int fd = sd_varlink_take_fd(link, p.fd_index);
+        if (fd < 0)
+                return sd_varlink_error_errno(link, fd);
+
+        r = vmspawn_bind_volume_attach_fd(ctx->bridge, link, TAKE_FD(fd), p.name, p.config);
+        if (r == -EEXIST)
+                return sd_varlink_error(link, "io.systemd.MachineInstance.StorageExists", NULL);
+        if (r == -EOPNOTSUPP)
+                return sd_varlink_error(link, "io.systemd.MachineInstance.ConfigNotSupported", NULL);
+        if (r < 0)
+                return sd_varlink_error_errno(link, r);
+
+        /* Async reply via on_add_device_add_complete. */
+        return 0;
+}
+
+static int vl_method_remove_storage(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+        VmspawnVarlinkContext *ctx = ASSERT_PTR(userdata);
+        int r;
+
+        struct {
+                const char *name;
+        } p = {};
+
+        static const sd_json_dispatch_field dispatch_table[] = {
+                { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, name), SD_JSON_MANDATORY },
+                {}
+        };
+
+        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        if (machine_storage_name_split(p.name, /* ret_provider= */ NULL, /* ret_volume= */ NULL) < 0)
+                return sd_varlink_error_invalid_parameter_name(link, "name");
+
+        return vmspawn_qmp_remove_block_device(ctx->bridge, link, p.name);
+}
+
 static int vl_method_subscribe_events(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
         VmspawnVarlinkContext *ctx = ASSERT_PTR(userdata);
         _cleanup_strv_free_ char **filter = NULL;
@@ -380,9 +453,10 @@ int vmspawn_varlink_setup(
         if (!ctx)
                 return log_oom();
 
-        /* Create varlink server for VM control */
+        /* AddStorage receives an fd from the caller. */
         r = varlink_server_new(&ctx->varlink_server,
-                               SD_VARLINK_SERVER_INHERIT_USERDATA,
+                               SD_VARLINK_SERVER_INHERIT_USERDATA |
+                               SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT,
                                ctx);
         if (r < 0)
                 return log_error_errno(r, "Failed to create varlink server: %m");
@@ -402,7 +476,9 @@ int vmspawn_varlink_setup(
                         "io.systemd.MachineInstance.Resume",            vl_method_resume,
                         "io.systemd.MachineInstance.Reboot",            vl_method_reboot,
                         "io.systemd.MachineInstance.Describe",          vl_method_describe,
-                        "io.systemd.MachineInstance.SubscribeEvents",   vl_method_subscribe_events);
+                        "io.systemd.MachineInstance.SubscribeEvents",   vl_method_subscribe_events,
+                        "io.systemd.MachineInstance.AddStorage",        vl_method_add_storage,
+                        "io.systemd.MachineInstance.RemoveStorage",     vl_method_remove_storage);
         if (r < 0)
                 return log_error_errno(r, "Failed to bind varlink methods: %m");