diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index d241ca5c52..e973b914dd 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -781,14 +781,14 @@ Controls whether the container is registered with systemd-machined8. Takes a - boolean argument, which defaults to yes. 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 auto, and defaults to auto. 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 machinectl1 and shown by tools such as ps1. If the container - does not run a service manager, it is recommended to set this option to - no. + does not run a service manager, it is recommended to set this option to no. When set to + auto, registration is attempted but failures are ignored. diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml index cca7496ed0..0a61d781d3 100644 --- a/man/systemd-vmspawn.xml +++ b/man/systemd-vmspawn.xml @@ -501,13 +501,10 @@ 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. + boolean argument or auto, and defaults to auto. This ensures that the + virtual machine is accessible via + machinectl1. When set to + auto, registration is attempted but failures are ignored. diff --git a/shell-completion/bash/systemd-nspawn b/shell-completion/bash/systemd-nspawn index 574018b1ff..692020aa62 100644 --- a/shell-completion/bash/systemd-nspawn +++ b/shell-completion/bash/systemd-nspawn @@ -131,7 +131,7 @@ _systemd_nspawn() { comps='' ;; --register) - comps='yes no' + comps='yes no auto' ;; --network-interface) comps=$(__get_interfaces) diff --git a/shell-completion/bash/systemd-vmspawn b/shell-completion/bash/systemd-vmspawn index 995aeb1271..1ca45091a7 100644 --- a/shell-completion/bash/systemd-vmspawn +++ b/shell-completion/bash/systemd-vmspawn @@ -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 diff --git a/shell-completion/zsh/_systemd-nspawn b/shell-completion/zsh/_systemd-nspawn index 5f08170064..f613db908e 100644 --- a/shell-completion/zsh/_systemd-nspawn +++ b/shell-completion/zsh/_systemd-nspawn @@ -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)' \ diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index 04031adcc5..ace0f6637a 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -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, diff --git a/src/nspawn/nspawn-register.h b/src/nspawn/nspawn-register.h index c4b8048606..d82c780181 100644 --- a/src/nspawn/nspawn-register.h +++ b/src/nspawn/nspawn-register.h @@ -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; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 98e2de2711..62676e935f 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -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, + ®istered_system, + ®istered_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 index 0000000000..d6e76db85c --- /dev/null +++ b/src/shared/machine-register.c @@ -0,0 +1,393 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#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 index 0000000000..e405873b5b --- /dev/null +++ b/src/shared/machine-register.h @@ -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); diff --git a/src/shared/meson.build b/src/shared/meson.build index e2c1501adb..2c6207d494 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -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', diff --git a/src/vmspawn/meson.build b/src/vmspawn/meson.build index 722e6a52cc..99bad2d618 100644 --- a/src/vmspawn/meson.build +++ b/src/vmspawn/meson.build @@ -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 index 46f292ce49..0000000000 --- a/src/vmspawn/vmspawn-register.c +++ /dev/null @@ -1,104 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include - -#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 index de118b7492..0000000000 --- a/src/vmspawn/vmspawn-register.h +++ /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); diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index ee2c49b778..f91c36193d 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -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, + ®istered_system, + ®istered_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) {