]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
vmspawn: Add --user/--system and support user session machined registration
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 22 Dec 2025 14:11:18 +0000 (15:11 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 1 Jan 2026 23:49:36 +0000 (08:49 +0900)
The UX of registering with the user session machined
instance is much better as there won't be an authorization
prompt. To make that available for users, let's add --user
and --system switches for vmspawn. For backwards compat, we'll
still try to register with the system machined instance if the
user machined instance is not available.

man/systemd-vmspawn.xml
shell-completion/bash/systemd-vmspawn
src/vmspawn/vmspawn-register.c
src/vmspawn/vmspawn-register.h
src/vmspawn/vmspawn.c

index 7fd19671cbcc42dfc6b801c41fd50cf07cd1caa5..e51b7e8b6934b4c4e827cd3f753ac8ac3960c745 100644 (file)
         <xi:include href="version-info.xml" xpointer="v256"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--system</option></term>
+        <term><option>--user</option></term>
+
+        <listitem><para>Specify whether to interact with the user manager or the system manager and whether
+        to register with the user machined instance or the system machined instance. If
+        unspecified, the system manager and machined instance will be used when running as root, otherwise
+        the user manager and machined instance will be used.</para>
+
+        <xi:include href="version-info.xml" xpointer="v260"/></listitem>
+      </varlistentry>
+
     </variablelist>
 
     <refsect2>
index 7043ce2c89a47a853ebc0b50323b2798c88d9c15..52c3a84e36c0759c62d7d04b3d55b0781f02531f 100644 (file)
@@ -29,7 +29,7 @@ _systemd_vmspawn() {
     local i verb comps
 
     local -A OPTS=(
-        [STANDALONE]='-h --help --version -q --quiet --no-pager -n --network-tap --network-user-mode'
+        [STANDALONE]='-h --help --version -q --quiet --no-pager -n --network-tap --network-user-mode --user --system'
         [PATH]='-D --directory -i --image --linux --initrd --extra-drive --forward-journal'
         [BOOL]='--kvm --vsock --tpm --secure-boot --discard-disk --register --pass-ssh-key'
         [FIRMWARE]='--firmware'
index b33f86becf09434d18f7dfe6612bfc62511ed8c4..46f292ce49525019a8b5a3f0954edb448eb8f696 100644 (file)
@@ -12,6 +12,7 @@
 #include "errno-util.h"
 #include "json-util.h"
 #include "log.h"
+#include "path-lookup.h"
 #include "pidref.h"
 #include "socket-util.h"
 #include "string-util.h"
@@ -29,7 +30,8 @@ int register_machine(
                 unsigned cid,
                 const char *address,
                 const char *key_path,
-                bool keep_unit) {
+                bool allocate_unit,
+                RuntimeScope scope) {
 
         _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
         int r;
@@ -38,7 +40,12 @@ int register_machine(
         assert(service);
 
         /* First try to use varlink, as it provides more features (such as SSH support). */
-        r = sd_varlink_connect_address(&vl, "/run/systemd/machine/io.systemd.Machine");
+        _cleanup_free_ char *p = NULL;
+        r = runtime_directory_generic(scope, "systemd/machine/io.systemd.Machine", &p);
+        if (r < 0)
+                return r;
+
+        r = sd_varlink_connect_address(&vl, p);
         if (r == -ENOENT || ERRNO_IS_DISCONNECT(r)) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
 
@@ -59,12 +66,12 @@ int register_machine(
                                 (uint32_t) (pidref_is_set(pidref) ? pidref->pid : 0),
                                 strempty(directory));
                 if (r < 0)
-                       return log_error_errno(r, "Failed to register machine: %s", bus_error_message(&error, r));
+                        return log_error_errno(r, "Failed to register machine: %s", bus_error_message(&error, r));
 
                 return 0;
         }
         if (r < 0)
-                return log_error_errno(r, "Failed to connect to machined on /run/systemd/machine/io.systemd.Machine: %m");
+                return log_error_errno(r, "Failed to connect to machined on %p: %m", p);
 
         return varlink_callbo_and_log(
                         vl,
@@ -79,7 +86,7 @@ int register_machine(
                         SD_JSON_BUILD_PAIR_CONDITION(!!address, "sshAddress", SD_JSON_BUILD_STRING(address)),
                         SD_JSON_BUILD_PAIR_CONDITION(!!key_path, "sshPrivateKeyPath", SD_JSON_BUILD_STRING(key_path)),
                         SD_JSON_BUILD_PAIR_CONDITION(isatty_safe(STDIN_FILENO), "allowInteractiveAuthentication", SD_JSON_BUILD_BOOLEAN(true)),
-                        SD_JSON_BUILD_PAIR_CONDITION(!keep_unit, "allocateUnit", SD_JSON_BUILD_BOOLEAN(true)),
+                        SD_JSON_BUILD_PAIR_CONDITION(allocate_unit, "allocateUnit", SD_JSON_BUILD_BOOLEAN(true)),
                         SD_JSON_BUILD_PAIR_CONDITION(pidref_is_set(pidref), "leaderProcessId", JSON_BUILD_PIDREF(pidref)));
 }
 
index eeee16f701156b447079266e6f100c4047f4c646..de118b7492fa2926ead5fe289604f4cf1c7140aa 100644 (file)
@@ -13,6 +13,7 @@ int register_machine(
                 unsigned cid,
                 const char *address,
                 const char *key_path,
-                bool keep_unit);
+                bool allocate_unit,
+                RuntimeScope scope);
 
 int unregister_machine(sd_bus *bus, const char *machine_name);
index 291cac3be15256ba81fbc9572bed1e311427b39d..8951e19704a77528a4f7c4df4cae6464d625bdd9 100644 (file)
@@ -123,7 +123,6 @@ static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U;
 static RuntimeMountContext arg_runtime_mounts = {};
 static char *arg_firmware = NULL;
 static char *arg_forward_journal = NULL;
-static bool arg_privileged = false;
 static bool arg_register = true;
 static bool arg_keep_unit = false;
 static sd_id128_t arg_uuid = {};
@@ -144,6 +143,7 @@ static char **arg_bind_user = NULL;
 static char *arg_bind_user_shell = NULL;
 static bool arg_bind_user_shell_copy = false;
 static char **arg_bind_user_groups = NULL;
+static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
 
 STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
@@ -184,6 +184,8 @@ static int help(void) {
                "  -q --quiet               Do not show status information\n"
                "     --no-pager            Do not pipe output into a pager\n"
                "     --no-ask-password     Do not prompt for password\n"
+               "     --user                Interact with user manager\n"
+               "     --system              Interact with system manager\n"
                "\n%3$sImage:%4$s\n"
                "  -D --directory=PATH      Root directory for the VM\n"
                "  -i --image=FILE|DEVICE   Root file system disk image or device for the VM\n"
@@ -308,6 +310,8 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_BIND_USER,
                 ARG_BIND_USER_SHELL,
                 ARG_BIND_USER_GROUP,
+                ARG_SYSTEM,
+                ARG_USER,
         };
 
         static const struct option options[] = {
@@ -360,6 +364,8 @@ static int parse_argv(int argc, char *argv[]) {
                 { "bind-user",         required_argument, NULL, ARG_BIND_USER         },
                 { "bind-user-shell",   required_argument, NULL, ARG_BIND_USER_SHELL   },
                 { "bind-user-group",   required_argument, NULL, ARG_BIND_USER_GROUP   },
+                { "system",            no_argument,       NULL, ARG_SYSTEM            },
+                { "user",              no_argument,       NULL, ARG_USER              },
                 {}
         };
 
@@ -730,6 +736,14 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_SYSTEM:
+                        arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
+                        break;
+
+                case ARG_USER:
+                        arg_runtime_scope = RUNTIME_SCOPE_USER;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -1808,7 +1822,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
 
         /* Registration always happens on the system bus */
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *system_bus = NULL;
-        if (arg_register || arg_privileged) {
+        if (arg_register || arg_runtime_scope == RUNTIME_SCOPE_SYSTEM) {
                 r = sd_bus_default_system(&system_bus);
                 if (r < 0)
                         return log_error_errno(r, "Failed to open system bus: %m");
@@ -1823,7 +1837,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
         /* Scope allocation happens on the user bus if we are unpriv, otherwise system bus. */
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *user_bus = NULL;
         _cleanup_(sd_bus_unrefp) sd_bus *runtime_bus = NULL;
-        if (arg_privileged)
+        if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM)
                 runtime_bus = sd_bus_ref(system_bus);
         else {
                 r = sd_bus_default_user(&user_bus);
@@ -1949,10 +1963,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                 if (asprintf(&subdir, "systemd/vmspawn.%" PRIx64, random_u64()) < 0)
                         return log_oom();
 
-                r = runtime_directory(
-                                arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER,
-                                subdir,
-                                &runtime_dir);
+                r = runtime_directory(arg_runtime_scope, subdir, &runtime_dir);
                 if (r < 0)
                         return log_error_errno(r, "Failed to lookup runtime directory: %m");
                 if (r > 0) { /* We need to create our own runtime dir */
@@ -2762,7 +2773,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
         }
 
         bool scope_allocated = false;
-        if (!arg_keep_unit && (!arg_register || !arg_privileged)) {
+        if (!arg_keep_unit && (!arg_register || arg_runtime_scope != RUNTIME_SCOPE_SYSTEM)) {
                 r = allocate_scope(
                                 runtime_bus,
                                 arg_machine,
@@ -2778,7 +2789,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
 
                 scope_allocated = true;
         } else {
-                if (arg_privileged)
+                if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM)
                         r = cg_pid_get_unit(0, &unit);
                 else
                         r = cg_pid_get_user_unit(0, &unit);
@@ -2786,7 +2797,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                         return log_error_errno(r, "Failed to get our own unit: %m");
         }
 
-        bool registered = false;
+        bool registered_system = false, registered_runtime = false;
         if (arg_register) {
                 char vm_address[STRLEN("vsock/") + DECIMAL_STR_MAX(unsigned)];
                 xsprintf(vm_address, "vsock/%u", child_cid);
@@ -2800,11 +2811,38 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                                 child_cid,
                                 child_cid != VMADDR_CID_ANY ? vm_address : NULL,
                                 ssh_private_key_path,
-                                arg_keep_unit || !arg_privileged);
-                if (r < 0)
-                        return r;
+                                !arg_keep_unit && arg_runtime_scope == RUNTIME_SCOPE_SYSTEM,
+                                RUNTIME_SCOPE_SYSTEM);
+                if (r < 0) {
+                        /* if privileged the request to register definitely failed */
+                        if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM)
+                                return r;
+
+                        log_notice_errno(r, "Failed to register machine in system context, will try in user context.");
+                } else
+                        registered_system = true;
+
+                if (arg_runtime_scope == RUNTIME_SCOPE_USER) {
+                        r = register_machine(
+                                        runtime_bus,
+                                        arg_machine,
+                                        arg_uuid,
+                                        "systemd-vmspawn",
+                                        &child_pidref,
+                                        arg_directory,
+                                        child_cid,
+                                        child_cid != VMADDR_CID_ANY ? vm_address : NULL,
+                                        ssh_private_key_path,
+                                        !arg_keep_unit,
+                                        RUNTIME_SCOPE_USER);
+                        if (r < 0) {
+                                if (!registered_system) /* neither registration worked: fail */
+                                        return r;
 
-                registered = true;
+                                log_notice_errno(r, "Failed to register machine in user context, but succeeded in system context, will proceed.");
+                        } else
+                                registered_runtime = true;
+                }
         }
 
         /* Report that the VM is now set up */
@@ -2905,8 +2943,10 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
         if (scope_allocated)
                 terminate_scope(runtime_bus, arg_machine);
 
-        if (registered)
+        if (registered_system)
                 (void) unregister_machine(system_bus, arg_machine);
+        if (registered_runtime)
+                (void) unregister_machine(runtime_bus, arg_machine);
 
         if (use_vsock) {
                 if (exit_status == INT_MAX) {
@@ -2928,8 +2968,12 @@ static int determine_names(void) {
                 if (arg_machine) {
                         _cleanup_(image_unrefp) Image *i = NULL;
 
-                        r = image_find(arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER,
-                                       IMAGE_MACHINE, arg_machine, NULL, &i);
+                        /* Use both user and system images in user mode, use only system images in system mode. */
+                        r = image_find(arg_runtime_scope == RUNTIME_SCOPE_USER ? _RUNTIME_SCOPE_INVALID : arg_runtime_scope,
+                                       IMAGE_MACHINE,
+                                       arg_machine,
+                                       /* root= */ NULL,
+                                       &i);
                         if (r == -ENOENT)
                                 return log_error_errno(r, "No image for machine '%s'.", arg_machine);
                         if (r < 0)
@@ -2993,7 +3037,7 @@ static int run(int argc, char *argv[]) {
 
         log_setup();
 
-        arg_privileged = getuid() == 0;
+        arg_runtime_scope = getuid() == 0 ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
 
         r = parse_environment();
         if (r < 0)