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)