diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml index d7fee0538ac..c9ad82b2f02 100644 --- a/man/systemd-vmspawn.xml +++ b/man/systemd-vmspawn.xml @@ -274,6 +274,41 @@ + + + + + Set the specified UUID for the virtual machine. The + init system will initialize + /etc/machine-id from this if this file is + not set yet. Note that this option takes effect only if + /etc/machine-id in the virtual machine is + unpopulated. + + + + + + + + Property Options + + + + + + Controls whether the virtual machine is registered with + systemd-machined8. Takes a + boolean argument, which defaults to yes when running as root, and no when + running as a regular user. This ensures that the virtual machine is accessible via + machinectl1. + + Note: root privileges are required to use this option as registering with + systemd-machined8 + requires privileged D-Bus method calls. + + + diff --git a/src/vmspawn/meson.build b/src/vmspawn/meson.build index 10810afa81d..3cd9a3b69e6 100644 --- a/src/vmspawn/meson.build +++ b/src/vmspawn/meson.build @@ -5,6 +5,7 @@ libvmspawn_core_sources = files( 'vmspawn-util.c', 'vmspawn-scope.c', 'vmspawn-mount.c', + 'vmspawn-register.c', ) libvmspawn_core = static_library( 'vmspawn-core', diff --git a/src/vmspawn/vmspawn-register.c b/src/vmspawn/vmspawn-register.c new file mode 100644 index 00000000000..d04c3dafad9 --- /dev/null +++ b/src/vmspawn/vmspawn-register.c @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-bus.h" +#include "sd-id128.h" + +#include "bus-error.h" +#include "bus-locator.h" +#include "macro.h" +#include "process-util.h" +#include "string-util.h" +#include "vmspawn-register.h" + +int register_machine(sd_bus *bus, const char *machine_name, sd_id128_t uuid, const char *service, const char *directory) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(bus); + assert(machine_name); + assert(service); + + r = bus_call_method( + bus, + bus_machine_mgr, + "RegisterMachine", + &error, + NULL, + "sayssus", + machine_name, + SD_BUS_MESSAGE_APPEND_ID128(uuid), + service, + "vm", + (uint32_t) getpid_cached(), + strempty(directory)); + if (r < 0) + return log_error_errno(r, "Failed to register machine: %s", bus_error_message(&error, r)); + + return 0; +} + +int unregister_machine(sd_bus *bus, const char *machine_name) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(bus); + + r = bus_call_method(bus, bus_machine_mgr, "UnregisterMachine", &error, NULL, "s", machine_name); + if (r < 0) + log_debug("Failed to unregister machine: %s", bus_error_message(&error, r)); + + return 0; +} diff --git a/src/vmspawn/vmspawn-register.h b/src/vmspawn/vmspawn-register.h new file mode 100644 index 00000000000..7aa82ce849a --- /dev/null +++ b/src/vmspawn/vmspawn-register.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-bus.h" +#include "sd-id128.h" + +int register_machine(sd_bus *bus, const char *machine_name, sd_id128_t uuid, const char *service, const char *directory); +int unregister_machine(sd_bus *bus, const char *machine_name); diff --git a/src/vmspawn/vmspawn-settings.h b/src/vmspawn/vmspawn-settings.h index fe23aa23cf7..5446c20911c 100644 --- a/src/vmspawn/vmspawn-settings.h +++ b/src/vmspawn/vmspawn-settings.h @@ -17,6 +17,7 @@ typedef enum ConsoleMode { typedef enum SettingsMask { SETTING_START_MODE = UINT64_C(1) << 0, + SETTING_MACHINE_ID = UINT64_C(1) << 6, SETTING_BIND_MOUNTS = UINT64_C(1) << 11, SETTING_DIRECTORY = UINT64_C(1) << 26, SETTING_CREDENTIALS = UINT64_C(1) << 30, diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index ce7f1ef2e3e..e1e4350b843 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -57,6 +57,7 @@ #include "tmpfile-util.h" #include "unit-name.h" #include "vmspawn-mount.h" +#include "vmspawn-register.h" #include "vmspawn-scope.h" #include "vmspawn-settings.h" #include "vmspawn-util.h" @@ -86,6 +87,8 @@ static char *arg_runtime_directory = NULL; static char *arg_forward_journal = NULL; static bool arg_runtime_directory_created = false; static bool arg_privileged = false; +static bool arg_register = false; +static sd_id128_t arg_uuid = {}; static char **arg_kernel_cmdline_extra = NULL; static char **arg_extra_drives = NULL; static char *arg_background = NULL; @@ -139,6 +142,9 @@ static int help(void) { " --firmware=PATH|list Select firmware definition file (or list available)\n" "\n%3$sSystem Identity:%4$s\n" " -M --machine=NAME Set the machine name for the VM\n" + " --uuid=UUID Set a specific machine UUID for the VM\n" + "\n%3$sProperties:%4$s\n" + " --register=BOOLEAN Register VM with systemd-machined\n" "\n%3$sUser Namespacing:%4$s\n" " --private-users=UIDBASE[:NUIDS]\n" " Configure the UID/GID range to map into the\n" @@ -186,6 +192,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_INITRD, ARG_QEMU_GUI, ARG_NETWORK_USER_MODE, + ARG_UUID, + ARG_REGISTER, ARG_BIND, ARG_BIND_RO, ARG_EXTRA_DRIVE, @@ -223,6 +231,8 @@ static int parse_argv(int argc, char *argv[]) { { "qemu-gui", no_argument, NULL, ARG_QEMU_GUI }, /* compat option */ { "network-tap", no_argument, NULL, 'n' }, { "network-user-mode", no_argument, NULL, ARG_NETWORK_USER_MODE }, + { "uuid", required_argument, NULL, ARG_UUID }, + { "register", required_argument, NULL, ARG_REGISTER }, { "bind", required_argument, NULL, ARG_BIND }, { "bind-ro", required_argument, NULL, ARG_BIND_RO }, { "extra-drive", required_argument, NULL, ARG_EXTRA_DRIVE }, @@ -372,6 +382,26 @@ static int parse_argv(int argc, char *argv[]) { arg_network_stack = NETWORK_STACK_USER; break; + case ARG_UUID: + r = id128_from_string_nonzero(optarg, &arg_uuid); + if (r == -ENXIO) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Machine UUID may not be all zeroes."); + if (r < 0) + return log_error_errno(r, "Invalid UUID: %s", optarg); + + arg_settings_mask |= SETTING_MACHINE_ID; + break; + + case ARG_REGISTER: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --register= argument: %s", optarg); + return r; + } + + arg_register = r; + break; + case ARG_BIND: case ARG_BIND_RO: r = runtime_mount_parse(&arg_runtime_mounts, optarg, c == ARG_BIND_RO); @@ -1093,6 +1123,12 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { if (r < 0) return r; + if (arg_register) { + r = register_machine(bus, arg_machine, arg_uuid, trans_scope, arg_directory); + if (r < 0) + return r; + } + bool use_kvm = arg_kvm > 0; if (arg_kvm < 0) { r = qemu_check_kvm_support(); @@ -1747,6 +1783,9 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { if (r < 0) return log_error_errno(r, "Failed to run event loop: %m"); + if (arg_register) + (void) unregister_machine(bus, arg_machine); + if (use_vsock) { if (exit_status == INT_MAX) { log_debug("Couldn't retrieve inner EXIT_STATUS from VSOCK"); @@ -1825,6 +1864,9 @@ static int verify_arguments(void) { if (!strv_isempty(arg_initrds) && !arg_linux) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --initrd= cannot be used without --linux=."); + if (arg_register && !arg_privileged) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), "--register= requires root privileges, refusing."); + return 0; } @@ -1836,6 +1878,9 @@ static int run(int argc, char *argv[]) { arg_privileged = getuid() == 0; + /* don't attempt to register as a machine when running as a user */ + arg_register = arg_privileged; + r = parse_argv(argc, argv); if (r <= 0) return r;