]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
vmspawn: support machined registration
authorSam Leonard <sam.leonard@codethink.co.uk>
Tue, 20 Feb 2024 13:13:16 +0000 (13:13 +0000)
committerSam Leonard <sam.leonard@codethink.co.uk>
Wed, 28 Feb 2024 17:49:00 +0000 (17:49 +0000)
man/systemd-vmspawn.xml
src/vmspawn/meson.build
src/vmspawn/vmspawn-register.c [new file with mode: 0644]
src/vmspawn/vmspawn-register.h [new file with mode: 0644]
src/vmspawn/vmspawn-settings.h
src/vmspawn/vmspawn.c

index d7fee0538ac8c5491a700a305fa07c7c0af6abc2..c9ad82b2f020859eaf2ae358d4f25dc8aa7285a2 100644 (file)
           <xi:include href="version-info.xml" xpointer="v255"/>
           </listitem>
         </varlistentry>
+
+        <varlistentry>
+          <term><option>--uuid=</option></term>
+
+          <listitem><para>Set the specified UUID for the virtual machine. The
+          init system will initialize
+          <filename>/etc/machine-id</filename> from this if this file is
+          not set yet. Note that this option takes effect only if
+          <filename>/etc/machine-id</filename> in the virtual machine is
+          unpopulated.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Property Options</title>
+
+      <variablelist>
+        <varlistentry>
+          <term><option>--register=</option></term>
+
+          <listitem><para>Controls whether the virtual machine is registered with
+          <citerefentry><refentrytitle>systemd-machined</refentrytitle><manvolnum>8</manvolnum></citerefentry>.  Takes a
+          boolean argument, which defaults to <literal>yes</literal> when running as root, and <literal>no</literal> when
+          running as a regular user.  This ensures that the virtual machine is accessible via
+          <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+
+          <para>Note: root privileges are required to use this option as registering with
+          <citerefentry><refentrytitle>systemd-machined</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+          requires privileged D-Bus method calls.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+        </varlistentry>
       </variablelist>
     </refsect2>
 
index 10810afa81d0967e3edab1e16fd7e866318acae1..3cd9a3b69e67b5e0de4cee40d715fdca98686c15 100644 (file)
@@ -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 (file)
index 0000000..d04c3da
--- /dev/null
@@ -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 (file)
index 0000000..7aa82ce
--- /dev/null
@@ -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);
index fe23aa23cf7bc3b47de42b5d072b1ca993e17e24..5446c20911c9f93b310eff9ef73fb4e9f95e3928 100644 (file)
@@ -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,
index ce7f1ef2e3ee8f8ae08d816d62d5358c10fcc70a..e1e4350b843b8f2895b845bd1a268c61ca583765 100644 (file)
@@ -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;