]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
shared: move machine registration to shared machine-register.{c,h}
authorDaan De Meyer <daan@amutable.com>
Sun, 29 Mar 2026 18:22:40 +0000 (18:22 +0000)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 6 Apr 2026 15:13:40 +0000 (17:13 +0200)
Move register_machine() and unregister_machine() from
vmspawn-register.{c,h} into shared machine-register.{c,h} so both
nspawn and vmspawn can use the same implementation.

The unified register_machine() uses varlink first (for richer
features like SSH support and unit allocation) with a D-Bus
RegisterMachineWithNetwork fallback for older machined. The
interface adds a class parameter ("vm" or "container") and
local_ifindex for nspawn's network interface support.

The unified unregister_machine() similarly tries varlink first
(io.systemd.Machine.Unregister) before falling back to D-Bus.

Both register_machine() and unregister_machine() only log at debug
level internally, leaving error/notice logging to callers.

Add register_machine_with_fallback() which tries system and/or user
scope registration based on a RuntimeScope parameter
(_RUNTIME_SCOPE_INVALID for both), and
unregister_machine_with_fallback() as its counterpart. Both use
RET_GATHER() to collect errors from each scope.

Make --register= a tristate (yes/no/auto) defaulting to auto. When
set to auto, registration failures are logged at notice level and
ignored. When set to yes, failures are fatal.

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
15 files changed:
man/systemd-nspawn.xml
man/systemd-vmspawn.xml
shell-completion/bash/systemd-nspawn
shell-completion/bash/systemd-vmspawn
shell-completion/zsh/_systemd-nspawn
src/nspawn/nspawn-register.c
src/nspawn/nspawn-register.h
src/nspawn/nspawn.c
src/shared/machine-register.c [new file with mode: 0644]
src/shared/machine-register.h [new file with mode: 0644]
src/shared/meson.build
src/vmspawn/meson.build
src/vmspawn/vmspawn-register.c [deleted file]
src/vmspawn/vmspawn-register.h [deleted file]
src/vmspawn/vmspawn.c

index d241ca5c52c5e29315ac363fa3e1625a5f45b8eb..e973b914dd0492228b0a48971d5671d0814b9661 100644 (file)
 
         <listitem><para>Controls whether the container is registered with
         <citerefentry><refentrytitle>systemd-machined</refentrytitle><manvolnum>8</manvolnum></citerefentry>.  Takes a
-        boolean argument, which defaults to <literal>yes</literal>.  This option should be enabled when the container
-        runs a full Operating System (more specifically: a system and service manager as PID 1), and is useful to
-        ensure that the container is accessible via
+        boolean argument or <literal>auto</literal>, and defaults to <literal>auto</literal>.  This option should be
+        enabled when the container runs a full Operating System (more specifically: a system and service manager as
+        PID 1), and is useful to ensure that the container is accessible via
         <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> and shown by
         tools such as <citerefentry
         project='man-pages'><refentrytitle>ps</refentrytitle><manvolnum>1</manvolnum></citerefentry>.  If the container
-        does not run a service manager, it is recommended to set this option to
-        <literal>no</literal>.</para>
+        does not run a service manager, it is recommended to set this option to <literal>no</literal>.  When set to
+        <literal>auto</literal>, registration is attempted but failures are ignored.</para>
 
         <xi:include href="version-info.xml" xpointer="v209"/></listitem>
       </varlistentry>
index cca7496ed0468d8175c127e4df7ce6b01c3f64bd..0a61d781d39d0efc332ee5b416dd7fa157a88daa 100644 (file)
 
           <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>
+          boolean argument or <literal>auto</literal>, and defaults to <literal>auto</literal>.  This ensures that the
+          virtual machine is accessible via
+          <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.  When set to
+          <literal>auto</literal>, registration is attempted but failures are ignored.</para>
 
           <xi:include href="version-info.xml" xpointer="v256"/></listitem>
         </varlistentry>
index 574018b1fff209e794c3c3fdf7991ae6abfc86a0..692020aa62cfb4e8a973e4af927d7d9bb14f549c 100644 (file)
@@ -131,7 +131,7 @@ _systemd_nspawn() {
                 comps=''
                 ;;
             --register)
-                comps='yes no'
+                comps='yes no auto'
                 ;;
             --network-interface)
                 comps=$(__get_interfaces)
index 995aeb12712987c4e59edb6e8171e8c9f6cb735b..1ca45091a7ef4fb64c02f708d7a003f7a6f17376 100644 (file)
@@ -31,8 +31,8 @@ _systemd_vmspawn() {
     local -A OPTS=(
         [STANDALONE]='-h --help --version -q --quiet --no-pager -n --network-tap --network-user-mode --user --system -x --ephemeral'
         [PATH]='-D --directory -i --image --linux --initrd --extra-drive --forward-journal --efi-nvram-template'
-        [BOOL]='--kvm --cxl --vsock --tpm --discard-disk --register --pass-ssh-key'
-        [SECURE_BOOT]='--secure-boot'
+        [BOOL]='--kvm --cxl --vsock --tpm --discard-disk --pass-ssh-key'
+        [TRISTATE]='--register --secure-boot'
         [FIRMWARE]='--firmware'
         [FIRMWARE_FEATURES]='--firmware-features'
         [BIND]='--bind --bind-ro'
@@ -47,7 +47,7 @@ _systemd_vmspawn() {
 
     if __contains_word "$prev" ${OPTS[BOOL]}; then
         comps='yes no'
-    elif __contains_word "$prev" ${OPTS[SECURE_BOOT]}; then
+    elif __contains_word "$prev" ${OPTS[TRISTATE]}; then
         comps='yes no auto'
     elif __contains_word "$prev" ${OPTS[PATH]}; then
         compopt -o nospace -o filenames
index 5f081700644c6735d20ca8fde98462dc0b58c1ff..f613db908e30eb6f5ecfab63580c7ff735a56fde 100644 (file)
@@ -45,7 +45,7 @@ _arguments \
     '--tmpfs=[Mount an empty tmpfs to the specified directory.]: : _files' \
     '--setenv=[Specifies an environment variable assignment to pass to the init process in the container, in the format "NAME=VALUE".]: : _message "environment variables"' \
     '--share-system[Allows the container to share certain system facilities with the host.]' \
-    '--register=[Controls whether the container is registered with systemd-machined(8).]:systemd-machined registration:( yes no )' \
+    '--register=[Controls whether the container is registered with systemd-machined(8).]:systemd-machined registration:( yes no auto )' \
     '--keep-unit[Instead of creating a transient scope unit to run the container in, simply register the service or scope unit systemd-nspawn has been invoked in with systemd-machined(8).]' \
     '--personality=[Control the architecture ("personality") reported by uname(2) in the container.]:architecture:(x86 x86-64)' \
     '--volatile=[Run the system in volatile mode.]:volatile:(no yes state)' \
index 04031adcc5ab576ec5247b44407a9faf853f8590..ace0f6637a5450698f162e0539c8961f6260540e 100644 (file)
@@ -131,150 +131,6 @@ static int can_set_coredump_receive(sd_bus *bus) {
         return r >= 0;
 }
 
-static int register_machine_ex(
-                sd_bus *bus,
-                const char *machine_name,
-                const PidRef *pid,
-                const char *directory,
-                sd_id128_t uuid,
-                int local_ifindex,
-                const char *service,
-                sd_bus_error *error) {
-
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
-        int r;
-
-        assert(bus);
-        assert(machine_name);
-        assert(service);
-        assert(error);
-
-        r = bus_message_new_method_call(bus, &m,  bus_machine_mgr, "RegisterMachineEx");
-        if (r < 0)
-                return bus_log_create_error(r);
-
-        r = sd_bus_message_append(m, "s", machine_name);
-        if (r < 0)
-                return bus_log_create_error(r);
-
-        r = sd_bus_message_open_container(m, 'a', "(sv)");
-        if (r < 0)
-                return bus_log_create_error(r);
-
-        r = sd_bus_message_append(
-                        m,
-                        "(sv)(sv)(sv)",
-                        "Id", "ay", SD_BUS_MESSAGE_APPEND_ID128(uuid),
-                        "Service", "s", service,
-                        "Class", "s", "container");
-        if (r < 0)
-                return bus_log_create_error(r);
-
-        if (pidref_is_set(pid)) {
-                if (pid->fd >= 0) {
-                        r = sd_bus_message_append(m, "(sv)", "LeaderPIDFD", "h", pid->fd);
-                        if (r < 0)
-                                return bus_log_create_error(r);
-                }
-
-                if (pid->fd_id > 0) {
-                        r = sd_bus_message_append(m, "(sv)", "LeaderPIDFDID", "t", pid->fd_id);
-                        if (r < 0)
-                                return bus_log_create_error(r);
-
-                        r = sd_bus_message_append(m, "(sv)", "LeaderPID", "u", pid->pid);
-                        if (r < 0)
-                                return bus_log_create_error(r);
-                }
-        }
-
-        if (!isempty(directory)) {
-                r = sd_bus_message_append(m, "(sv)", "RootDirectory", "s", directory);
-                if (r < 0)
-                        return bus_log_create_error(r);
-        }
-
-        if (local_ifindex > 0) {
-                r = sd_bus_message_append(m, "(sv)", "NetworkInterfaces", "ai", 1, local_ifindex);
-                if (r < 0)
-                        return bus_log_create_error(r);
-        }
-
-        r = sd_bus_message_close_container(m);
-        if (r < 0)
-                return bus_log_create_error(r);
-
-        return sd_bus_call(bus, m, 0, error, NULL);
-}
-
-int register_machine(
-                sd_bus *bus,
-                const char *machine_name,
-                const PidRef *pid,
-                const char *directory,
-                sd_id128_t uuid,
-                int local_ifindex,
-                const char *service) {
-
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        int r;
-
-        assert(bus);
-        assert(machine_name);
-        assert(service);
-
-        r = register_machine_ex(
-                        bus,
-                        machine_name,
-                        pid,
-                        directory,
-                        uuid,
-                        local_ifindex,
-                        service,
-                        &error);
-        if (r >= 0)
-                return 0;
-        if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
-                return log_error_errno(r, "Failed to register machine: %s", bus_error_message(&error, r));
-
-        sd_bus_error_free(&error);
-
-        r = bus_call_method(
-                        bus,
-                        bus_machine_mgr,
-                        "RegisterMachineWithNetwork",
-                        &error,
-                        NULL,
-                        "sayssusai",
-                        machine_name,
-                        SD_BUS_MESSAGE_APPEND_ID128(uuid),
-                        service,
-                        "container",
-                        pidref_is_set(pid) ? (uint32_t) pid->pid : 0,
-                        strempty(directory),
-                        local_ifindex > 0 ? 1 : 0, local_ifindex);
-        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;
-}
-
 int allocate_scope(
                 sd_bus *bus,
                 const char *machine_name,
index c4b8048606251c52cb4e2a669d3d7423f77156ac..d82c780181c6d4d681ad8d1e1d483d67e9bfd027 100644 (file)
@@ -4,16 +4,6 @@
 #include "shared-forward.h"
 #include "nspawn-settings.h"
 
-int register_machine(
-                sd_bus *bus,
-                const char *machine_name,
-                const PidRef *pid,
-                const char *directory,
-                sd_id128_t uuid,
-                int local_ifindex,
-                const char *service);
-int unregister_machine(sd_bus *bus, const char *machine_name);
-
 typedef enum AllocateScopeFlags {
         ALLOCATE_SCOPE_ALLOW_PIDFD = 1 << 0,
 } AllocateScopeFlags;
index 98e2de271105682068b71800655ca5f573e71207..62676e935f002c79a2986804c3a06de8f88f3f3d 100644 (file)
@@ -66,6 +66,7 @@
 #include "loopback-setup.h"
 #include "machine-bind-user.h"
 #include "machine-credential.h"
+#include "machine-register.h"
 #include "main-func.h"
 #include "mkdir.h"
 #include "mount-util.h"
@@ -192,7 +193,7 @@ static CustomMount *arg_custom_mounts = NULL;
 static size_t arg_n_custom_mounts = 0;
 static char **arg_setenv = NULL;
 static bool arg_quiet = false;
-static bool arg_register = true;
+static int arg_register = -1;
 static bool arg_keep_unit = false;
 static char **arg_network_interfaces = NULL;
 static char **arg_network_macvlan = NULL;
@@ -1163,7 +1164,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_REGISTER:
-                        r = parse_boolean_argument("--register=", optarg, &arg_register);
+                        r = parse_tristate_argument_with_auto("--register=", optarg, &arg_register);
                         if (r < 0)
                                 return r;
 
@@ -5613,7 +5614,7 @@ static int run_container(
 
         /* Registration always happens on the system bus */
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *system_bus = NULL;
-        if (arg_register || (arg_privileged && !arg_keep_unit)) {
+        if (arg_register != 0 || (arg_privileged && !arg_keep_unit)) {
                 r = sd_bus_default_system(&system_bus);
                 if (r < 0)
                         return log_error_errno(r, "Failed to open system bus: %m");
@@ -5628,7 +5629,7 @@ static int run_container(
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *user_bus = NULL;
         _cleanup_(sd_bus_unrefp) sd_bus *runtime_bus = NULL;
 
-        if (arg_register || !arg_keep_unit) {
+        if (arg_register != 0 || !arg_keep_unit) {
                 if (arg_privileged)
                         runtime_bus = sd_bus_ref(system_bus);
                 else {
@@ -5697,40 +5698,27 @@ static int run_container(
         }
 
         bool registered_system = false, registered_runtime = false;
-        if (arg_register) {
-                r = register_machine(
+        if (arg_register != 0) {
+                r = register_machine_with_fallback_and_log(
+                                arg_privileged ? RUNTIME_SCOPE_SYSTEM : _RUNTIME_SCOPE_INVALID,
                                 system_bus,
+                                runtime_bus,
                                 arg_machine,
+                                arg_uuid,
+                                arg_container_service_name,
+                                "container",
                                 pid,
                                 arg_directory,
-                                arg_uuid,
+                                /* cid= */ 0,
                                 ifi,
-                                arg_container_service_name);
-                if (r < 0) {
-                        if (arg_privileged) /* if privileged the request to register definitely failed */
-                                return r;
-
-                        log_notice_errno(r, "Failed to register machine in system context, will try in user context.");
-                } else
-                        registered_system = true;
-
-                if (!arg_privileged) {
-                        r = register_machine(
-                                        runtime_bus,
-                                        arg_machine,
-                                        pid,
-                                        arg_directory,
-                                        arg_uuid,
-                                        ifi,
-                                        arg_container_service_name);
-                        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;
-                }
+                                /* address= */ NULL,
+                                /* key_path= */ NULL,
+                                /* allocate_unit= */ false,
+                                /* graceful= */ arg_register < 0,
+                                &registered_system,
+                                &registered_runtime);
+                if (r < 0)
+                        return r;
         }
 
         if (arg_keep_unit && (arg_slice || arg_property))
@@ -5942,10 +5930,7 @@ static int run_container(
         r = wait_for_container(pid, &container_status);
 
         /* Tell machined that we are gone. */
-        if (registered_system)
-                (void) unregister_machine(system_bus, arg_machine);
-        if (registered_runtime)
-                (void) unregister_machine(runtime_bus, arg_machine);
+        (void) unregister_machine_with_fallback_and_log(system_bus, runtime_bus, arg_machine, registered_system, registered_runtime);
 
         if (r < 0)
                 /* We failed to wait for the container, or the container exited abnormally. */
diff --git a/src/shared/machine-register.c b/src/shared/machine-register.c
new file mode 100644 (file)
index 0000000..d6e76db
--- /dev/null
@@ -0,0 +1,393 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <unistd.h>
+
+#include "sd-bus.h"
+#include "sd-id128.h"
+#include "sd-json.h"
+#include "sd-varlink.h"
+
+#include "bus-error.h"
+#include "bus-locator.h"
+#include "bus-util.h"
+#include "errno-util.h"
+#include "json-util.h"
+#include "log.h"
+#include "machine-register.h"
+#include "path-lookup.h"
+#include "pidref.h"
+#include "runtime-scope.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "terminal-util.h"
+
+static int register_machine_dbus_ex(
+                sd_bus *bus,
+                const char *machine_name,
+                sd_id128_t uuid,
+                const char *service,
+                const char *class,
+                const PidRef *pidref,
+                const char *directory,
+                int local_ifindex,
+                sd_bus_error *error) {
+
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+        int r;
+
+        assert(bus);
+        assert(machine_name);
+        assert(service);
+        assert(class);
+
+        r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "RegisterMachineEx");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "s", machine_name);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_open_container(m, 'a', "(sv)");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(
+                        m,
+                        "(sv)(sv)(sv)",
+                        "Id", "ay", SD_BUS_MESSAGE_APPEND_ID128(uuid),
+                        "Service", "s", service,
+                        "Class", "s", class);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        if (pidref_is_set(pidref)) {
+                if (pidref->fd >= 0) {
+                        r = sd_bus_message_append(m, "(sv)", "LeaderPIDFD", "h", pidref->fd);
+                        if (r < 0)
+                                return bus_log_create_error(r);
+                }
+
+                if (pidref->fd_id > 0) {
+                        r = sd_bus_message_append(m, "(sv)", "LeaderPIDFDID", "t", pidref->fd_id);
+                        if (r < 0)
+                                return bus_log_create_error(r);
+
+                        r = sd_bus_message_append(m, "(sv)", "LeaderPID", "u", pidref->pid);
+                        if (r < 0)
+                                return bus_log_create_error(r);
+                }
+        }
+
+        if (!isempty(directory)) {
+                r = sd_bus_message_append(m, "(sv)", "RootDirectory", "s", directory);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        if (local_ifindex > 0) {
+                r = sd_bus_message_append(m, "(sv)", "NetworkInterfaces", "ai", 1, local_ifindex);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        return sd_bus_call(bus, m, 0, error, NULL);
+}
+
+static int register_machine_dbus(
+                sd_bus *bus,
+                const char *machine_name,
+                sd_id128_t uuid,
+                const char *service,
+                const char *class,
+                const PidRef *pidref,
+                const char *directory,
+                int local_ifindex) {
+
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(bus);
+        assert(machine_name);
+        assert(service);
+        assert(class);
+
+        /* First try RegisterMachineEx which supports PIDFD-based leader tracking. */
+        r = register_machine_dbus_ex(bus, machine_name, uuid, service, class, pidref, directory, local_ifindex, &error);
+        if (r >= 0)
+                return 0;
+        if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
+                return log_debug_errno(r, "Failed to register machine via D-Bus: %s", bus_error_message(&error, r));
+
+        sd_bus_error_free(&error);
+
+        r = bus_call_method(
+                        bus,
+                        bus_machine_mgr,
+                        "RegisterMachineWithNetwork",
+                        &error,
+                        NULL,
+                        "sayssusai",
+                        machine_name,
+                        SD_BUS_MESSAGE_APPEND_ID128(uuid),
+                        service,
+                        class,
+                        pidref_is_set(pidref) ? (uint32_t) pidref->pid : 0,
+                        strempty(directory),
+                        local_ifindex > 0 ? 1 : 0, local_ifindex);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to register machine via D-Bus: %s", bus_error_message(&error, r));
+
+        return 0;
+}
+
+int register_machine(
+                sd_bus *bus,
+                const char *machine_name,
+                sd_id128_t uuid,
+                const char *service,
+                const char *class,
+                const PidRef *pidref,
+                const char *directory,
+                unsigned cid,
+                int local_ifindex,
+                const char *address,
+                const char *key_path,
+                bool allocate_unit,
+                RuntimeScope scope) {
+
+        _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
+        int r;
+
+        assert(machine_name);
+        assert(service);
+        assert(class);
+
+        /* First try to use varlink, as it provides more features (such as SSH support). */
+        _cleanup_free_ char *p = NULL;
+        r = runtime_directory_generic(scope, "systemd/machine/io.systemd.Machine", &p);
+        if (r >= 0)
+                r = sd_varlink_connect_address(&vl, p);
+        if (r == -ENOENT || ERRNO_IS_DISCONNECT(r)) {
+                log_debug_errno(r, "Failed to connect to machined via varlink%s%s, falling back to D-Bus: %m",
+                                p ? " on " : "", strempty(p));
+
+                /* In case we are running with an older machined, fall back to D-Bus. */
+                if (!bus)
+                        return log_debug_errno(SYNTHETIC_ERRNO(ESRCH), "Varlink connection to machined not available and no bus provided.");
+
+                return register_machine_dbus(bus, machine_name, uuid, service, class, pidref, directory, local_ifindex);
+        }
+        if (r < 0)
+                return log_debug_errno(r, "Failed to connect to machined on %s: %m", strna(p));
+
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *reply = NULL;
+        const char *error_id = NULL;
+        r = sd_varlink_callbo(
+                        vl,
+                        "io.systemd.Machine.Register",
+                        &reply,
+                        &error_id,
+                        SD_JSON_BUILD_PAIR_STRING("name", machine_name),
+                        SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(uuid), "id", SD_JSON_BUILD_ID128(uuid)),
+                        SD_JSON_BUILD_PAIR_STRING("service", service),
+                        SD_JSON_BUILD_PAIR_STRING("class", class),
+                        SD_JSON_BUILD_PAIR_CONDITION(VSOCK_CID_IS_REGULAR(cid), "vSockCid", SD_JSON_BUILD_UNSIGNED(cid)),
+                        SD_JSON_BUILD_PAIR_CONDITION(local_ifindex > 0, "ifIndices", SD_JSON_BUILD_ARRAY(SD_JSON_BUILD_INTEGER(local_ifindex))),
+                        SD_JSON_BUILD_PAIR_CONDITION(!!directory, "rootDirectory", SD_JSON_BUILD_STRING(directory)),
+                        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(allocate_unit, "allocateUnit", SD_JSON_BUILD_BOOLEAN(true)),
+                        SD_JSON_BUILD_PAIR_CONDITION(pidref_is_set(pidref), "leaderProcessId", JSON_BUILD_PIDREF(pidref)));
+        if (r < 0)
+                return log_debug_errno(r, "Failed to register machine via varlink: %m");
+        if (error_id)
+                return log_debug_errno(sd_varlink_error_to_errno(error_id, reply),
+                                       "Failed to register machine via varlink: %s", error_id);
+
+        return 0;
+}
+
+static const char* machine_registration_scope_string(RuntimeScope scope, bool registered_system, bool registered_user) {
+        if (scope == _RUNTIME_SCOPE_INVALID) {
+                if (!registered_system && !registered_user)
+                        return "system and user";
+                if (!registered_system)
+                        return "system";
+                return "user";
+        }
+
+        return runtime_scope_to_string(scope);
+}
+
+int register_machine_with_fallback_and_log(
+                RuntimeScope scope,
+                sd_bus *system_bus,
+                sd_bus *user_bus,
+                const char *machine_name,
+                sd_id128_t uuid,
+                const char *service,
+                const char *class,
+                const PidRef *pidref,
+                const char *directory,
+                unsigned cid,
+                int local_ifindex,
+                const char *address,
+                const char *key_path,
+                bool allocate_unit,
+                bool graceful,
+                bool *reterr_registered_system,
+                bool *reterr_registered_user) {
+
+        bool registered_system = false, registered_user = false;
+        int r = 0;
+
+        assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, _RUNTIME_SCOPE_INVALID));
+        assert(system_bus || !IN_SET(scope, RUNTIME_SCOPE_SYSTEM, _RUNTIME_SCOPE_INVALID));
+        assert(user_bus || !IN_SET(scope, RUNTIME_SCOPE_USER, _RUNTIME_SCOPE_INVALID));
+        assert(machine_name);
+        assert(service);
+        assert(class);
+        assert(reterr_registered_system);
+        assert(reterr_registered_user);
+
+        if (IN_SET(scope, RUNTIME_SCOPE_SYSTEM, _RUNTIME_SCOPE_INVALID)) {
+                int q = register_machine(
+                                system_bus,
+                                machine_name,
+                                uuid,
+                                service,
+                                class,
+                                pidref,
+                                directory,
+                                cid,
+                                local_ifindex,
+                                address,
+                                key_path,
+                                scope == RUNTIME_SCOPE_SYSTEM ? allocate_unit : false,
+                                RUNTIME_SCOPE_SYSTEM);
+                if (q < 0)
+                        RET_GATHER(r, q);
+                else
+                        registered_system = true;
+        }
+
+        if (IN_SET(scope, RUNTIME_SCOPE_USER, _RUNTIME_SCOPE_INVALID)) {
+                int q = register_machine(
+                                user_bus,
+                                machine_name,
+                                uuid,
+                                service,
+                                class,
+                                pidref,
+                                directory,
+                                cid,
+                                local_ifindex,
+                                address,
+                                key_path,
+                                allocate_unit,
+                                RUNTIME_SCOPE_USER);
+                if (q < 0)
+                        RET_GATHER(r, q);
+                else
+                        registered_user = true;
+        }
+
+        if (r < 0) {
+                if (graceful) {
+                        log_notice_errno(r, "Failed to register machine in %s context, ignoring: %m",
+                                         machine_registration_scope_string(scope, registered_system, registered_user));
+                        r = 0;
+                } else
+                        r = log_error_errno(r, "Failed to register machine in %s context: %m",
+                                            machine_registration_scope_string(scope, registered_system, registered_user));
+        }
+
+        if (reterr_registered_system)
+                *reterr_registered_system = registered_system;
+        if (reterr_registered_user)
+                *reterr_registered_user = registered_user;
+
+        return r;
+}
+
+int unregister_machine_with_fallback_and_log(
+                sd_bus *system_bus,
+                sd_bus *user_bus,
+                const char *machine_name,
+                bool registered_system,
+                bool registered_user) {
+
+        int r = 0;
+        bool failed_system = false, failed_user = false;
+
+        if (registered_system) {
+                int q = unregister_machine(system_bus, machine_name, RUNTIME_SCOPE_SYSTEM);
+                if (q < 0) {
+                        RET_GATHER(r, q);
+                        failed_system = true;
+                }
+        }
+
+        if (registered_user) {
+                int q = unregister_machine(user_bus, machine_name, RUNTIME_SCOPE_USER);
+                if (q < 0) {
+                        RET_GATHER(r, q);
+                        failed_user = true;
+                }
+        }
+
+        if (r < 0)
+                log_notice_errno(r, "Failed to unregister machine in %s context, ignoring: %m",
+                                 machine_registration_scope_string(
+                                                 registered_system && registered_user ? _RUNTIME_SCOPE_INVALID :
+                                                 registered_system ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER,
+                                                 !failed_system, !failed_user));
+
+        return 0;
+}
+
+int unregister_machine(sd_bus *bus, const char *machine_name, RuntimeScope scope) {
+        int r;
+
+        assert(machine_name);
+
+        /* First try varlink */
+        _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
+        _cleanup_free_ char *p = NULL;
+        r = runtime_directory_generic(scope, "systemd/machine/io.systemd.Machine", &p);
+        if (r >= 0)
+                r = sd_varlink_connect_address(&vl, p);
+        if (r >= 0) {
+                _cleanup_(sd_json_variant_unrefp) sd_json_variant *reply = NULL;
+                const char *error_id = NULL;
+                r = sd_varlink_callbo(
+                                vl,
+                                "io.systemd.Machine.Unregister",
+                                &reply,
+                                &error_id,
+                                SD_JSON_BUILD_PAIR_STRING("name", machine_name));
+                if (r >= 0 && !error_id)
+                        return 0;
+                if (r >= 0)
+                        r = sd_varlink_error_to_errno(error_id, reply);
+        }
+
+        log_debug_errno(r, "Failed to unregister machine via varlink, falling back to D-Bus: %m");
+
+        /* Fall back to D-Bus */
+        if (!bus)
+                return log_debug_errno(SYNTHETIC_ERRNO(ESRCH), "Varlink connection to machined not available and no bus provided.");
+
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        r = bus_call_method(bus, bus_machine_mgr, "UnregisterMachine", &error, NULL, "s", machine_name);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to unregister machine via D-Bus: %s", bus_error_message(&error, r));
+
+        return 0;
+}
diff --git a/src/shared/machine-register.h b/src/shared/machine-register.h
new file mode 100644 (file)
index 0000000..e405873
--- /dev/null
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "shared-forward.h"
+
+int register_machine(
+                sd_bus *bus,
+                const char *machine_name,
+                sd_id128_t uuid,
+                const char *service,
+                const char *class,
+                const PidRef *pidref,
+                const char *directory,
+                unsigned cid,
+                int local_ifindex,
+                const char *address,
+                const char *key_path,
+                bool allocate_unit,
+                RuntimeScope scope);
+int register_machine_with_fallback_and_log(
+                RuntimeScope scope,
+                sd_bus *system_bus,
+                sd_bus *user_bus,
+                const char *machine_name,
+                sd_id128_t uuid,
+                const char *service,
+                const char *class,
+                const PidRef *pidref,
+                const char *directory,
+                unsigned cid,
+                int local_ifindex,
+                const char *address,
+                const char *key_path,
+                bool allocate_unit,
+                bool graceful,
+                bool *reterr_registered_system,
+                bool *reterr_registered_user);
+
+int unregister_machine(sd_bus *bus, const char *machine_name, RuntimeScope scope);
+int unregister_machine_with_fallback_and_log(
+                sd_bus *system_bus,
+                sd_bus *user_bus,
+                const char *machine_name,
+                bool registered_system,
+                bool registered_user);
index e2c1501adb86b9c90013468edef04fd14355f293..2c6207d494454b561fcf57971c78ce766b1da624 100644 (file)
@@ -125,6 +125,7 @@ shared_sources = files(
         'machine-bind-user.c',
         'machine-credential.c',
         'machine-id-setup.c',
+        'machine-register.c',
         'macvlan-util.c',
         'main-func.c',
         'metrics.c',
index 722e6a52cc7f274be40ddb67e0a5195da3623152..99bad2d61897351a83bff993927ca99529f93391 100644 (file)
@@ -10,7 +10,6 @@ vmspawn_sources = files(
         'vmspawn-settings.c',
         'vmspawn-scope.c',
         'vmspawn-mount.c',
-        'vmspawn-register.c',
 )
 vmspawn_extract_sources = files(
         'vmspawn-util.c',
diff --git a/src/vmspawn/vmspawn-register.c b/src/vmspawn/vmspawn-register.c
deleted file mode 100644 (file)
index 46f292c..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <unistd.h>
-
-#include "sd-bus.h"
-#include "sd-id128.h"
-#include "sd-json.h"
-#include "sd-varlink.h"
-
-#include "bus-error.h"
-#include "bus-locator.h"
-#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"
-#include "terminal-util.h"
-#include "varlink-util.h"
-#include "vmspawn-register.h"
-
-int register_machine(
-                sd_bus *bus,
-                const char *machine_name,
-                sd_id128_t uuid,
-                const char *service,
-                const PidRef *pidref,
-                const char *directory,
-                unsigned cid,
-                const char *address,
-                const char *key_path,
-                bool allocate_unit,
-                RuntimeScope scope) {
-
-        _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
-        int r;
-
-        assert(machine_name);
-        assert(service);
-
-        /* First try to use varlink, as it provides more features (such as SSH support). */
-        _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;
-
-                assert(bus);
-
-                /* In case we are running with an older machined, fallback to the existing D-Bus method. */
-                r = bus_call_method(
-                                bus,
-                                bus_machine_mgr,
-                                "RegisterMachine",
-                                &error,
-                                NULL,
-                                "sayssus",
-                                machine_name,
-                                SD_BUS_MESSAGE_APPEND_ID128(uuid),
-                                service,
-                                "vm",
-                                (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 0;
-        }
-        if (r < 0)
-                return log_error_errno(r, "Failed to connect to machined on %p: %m", p);
-
-        return varlink_callbo_and_log(
-                        vl,
-                        "io.systemd.Machine.Register",
-                        /* ret_reply= */ NULL,
-                        SD_JSON_BUILD_PAIR_STRING("name", machine_name),
-                        SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(uuid), "id", SD_JSON_BUILD_ID128(uuid)),
-                        SD_JSON_BUILD_PAIR_STRING("service", service),
-                        SD_JSON_BUILD_PAIR_STRING("class", "vm"),
-                        SD_JSON_BUILD_PAIR_CONDITION(VSOCK_CID_IS_REGULAR(cid), "vSockCid", SD_JSON_BUILD_UNSIGNED(cid)),
-                        SD_JSON_BUILD_PAIR_CONDITION(!!directory, "rootDirectory", SD_JSON_BUILD_STRING(directory)),
-                        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(allocate_unit, "allocateUnit", SD_JSON_BUILD_BOOLEAN(true)),
-                        SD_JSON_BUILD_PAIR_CONDITION(pidref_is_set(pidref), "leaderProcessId", JSON_BUILD_PIDREF(pidref)));
-}
-
-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
deleted file mode 100644 (file)
index de118b7..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include "shared-forward.h"
-
-int register_machine(
-                sd_bus *bus,
-                const char *machine_name,
-                sd_id128_t uuid,
-                const char *service,
-                const PidRef *pidref,
-                const char *directory,
-                unsigned cid,
-                const char *address,
-                const char *key_path,
-                bool allocate_unit,
-                RuntimeScope scope);
-
-int unregister_machine(sd_bus *bus, const char *machine_name);
index ee2c49b778fb7c4edcbf82716f98ac54f1e0a5d0..f91c36193dd01f6f20082c11388428d7673f4b0f 100644 (file)
@@ -51,6 +51,7 @@
 #include "log.h"
 #include "machine-bind-user.h"
 #include "machine-credential.h"
+#include "machine-register.h"
 #include "main-func.h"
 #include "mkdir.h"
 #include "namespace-util.h"
@@ -89,7 +90,6 @@
 #include "utf8.h"
 #include "vmspawn-mount.h"
 #include "vmspawn-qemu-config.h"
-#include "vmspawn-register.h"
 #include "vmspawn-scope.h"
 #include "vmspawn-settings.h"
 #include "vmspawn-util.h"
@@ -150,7 +150,7 @@ static bool arg_firmware_describe = false;
 static Set *arg_firmware_features_include = NULL;
 static Set *arg_firmware_features_exclude = NULL;
 static char *arg_forward_journal = NULL;
-static bool arg_register = true;
+static int arg_register = -1;
 static bool arg_keep_unit = false;
 static sd_id128_t arg_uuid = {};
 static char **arg_kernel_cmdline_extra = NULL;
@@ -665,7 +665,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_REGISTER:
-                        r = parse_boolean_argument("--register=", optarg, &arg_register);
+                        r = parse_tristate_argument_with_auto("--register=", optarg, &arg_register);
                         if (r < 0)
                                 return r;
 
@@ -2234,7 +2234,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_runtime_scope == RUNTIME_SCOPE_SYSTEM) {
+        if (arg_register != 0 || 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");
@@ -3571,7 +3571,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_runtime_scope != RUNTIME_SCOPE_SYSTEM)) {
+        if (!arg_keep_unit && (arg_register == 0 || arg_runtime_scope != RUNTIME_SCOPE_SYSTEM)) {
                 r = allocate_scope(
                                 runtime_bus,
                                 arg_machine,
@@ -3596,51 +3596,29 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
         }
 
         bool registered_system = false, registered_runtime = false;
-        if (arg_register) {
+        if (arg_register != 0) {
                 char vm_address[STRLEN("vsock/") + DECIMAL_STR_MAX(unsigned)];
                 xsprintf(vm_address, "vsock/%u", child_cid);
-                r = register_machine(
+                r = register_machine_with_fallback_and_log(
+                                arg_runtime_scope == RUNTIME_SCOPE_USER ? _RUNTIME_SCOPE_INVALID : RUNTIME_SCOPE_SYSTEM,
                                 system_bus,
+                                runtime_bus,
                                 arg_machine,
                                 arg_uuid,
                                 "systemd-vmspawn",
+                                "vm",
                                 &child_pidref,
                                 arg_directory,
                                 child_cid,
+                                /* local_ifindex= */ 0,
                                 child_cid != VMADDR_CID_ANY ? vm_address : NULL,
                                 ssh_private_key_path,
-                                !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;
-
-                                log_notice_errno(r, "Failed to register machine in user context, but succeeded in system context, will proceed.");
-                        } else
-                                registered_runtime = true;
-                }
+                                !arg_keep_unit,
+                                /* graceful= */ arg_register < 0,
+                                &registered_system,
+                                &registered_runtime);
+                if (r < 0)
+                        return r;
         }
 
         /* Report that the VM is now set up */
@@ -3743,10 +3721,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
         if (scope_allocated)
                 terminate_scope(runtime_bus, arg_machine);
 
-        if (registered_system)
-                (void) unregister_machine(system_bus, arg_machine);
-        if (registered_runtime)
-                (void) unregister_machine(runtime_bus, arg_machine);
+        (void) unregister_machine_with_fallback_and_log(system_bus, runtime_bus, arg_machine, registered_system, registered_runtime);
 
         if (use_vsock) {
                 if (exit_status == INT_MAX) {