diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml index 7fd19671cb..e51b7e8b69 100644 --- a/man/systemd-vmspawn.xml +++ b/man/systemd-vmspawn.xml @@ -69,6 +69,18 @@ + + + + + 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. + + + + diff --git a/shell-completion/bash/systemd-vmspawn b/shell-completion/bash/systemd-vmspawn index 7043ce2c89..52c3a84e36 100644 --- a/shell-completion/bash/systemd-vmspawn +++ b/shell-completion/bash/systemd-vmspawn @@ -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' diff --git a/src/vmspawn/vmspawn-register.c b/src/vmspawn/vmspawn-register.c index b33f86becf..46f292ce49 100644 --- a/src/vmspawn/vmspawn-register.c +++ b/src/vmspawn/vmspawn-register.c @@ -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))); } diff --git a/src/vmspawn/vmspawn-register.h b/src/vmspawn/vmspawn-register.h index eeee16f701..de118b7492 100644 --- a/src/vmspawn/vmspawn-register.h +++ b/src/vmspawn/vmspawn-register.h @@ -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); diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index 291cac3be1..8951e19704 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -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; - registered = true; + 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; + + 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)