From cc8f398202e3455a93fd79be8e454e6beedd7989 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Sun, 29 Mar 2026 11:15:35 +0000 Subject: [PATCH] nspawn: add --forward-journal= and --forward-journal-*= options Add --forward-journal=FILE|DIR to forward the container's journal entries to the host via systemd-journal-remote. When specified, nspawn starts systemd-journal-remote listening on a Unix socket, bind-mounts it into the container at /run/host/journal/socket, and passes a journal.forward_to_socket credential pointing to it. Add --forward-journal-max-use=, --forward-journal-keep-free=, --forward-journal-max-file-size=, and --forward-journal-max-files= to configure disk usage limits for the forwarded journal. Consolidate nspawn's per-machine on-disk state under a single runtime directory at /run/systemd/nspawn//. The container rootdir mount point moves from /tmp/nspawn-root-XXXXXX to /root, the unix-export directory moves from /run/systemd/nspawn/unix-export/ to /unix-export, and the journal-remote socket lives at /journal-remote-socket. Update ssh-generator and ssh-proxy to follow the new unix-export path layout. Extract fork_journal_remote() into fork-notify.{c,h} as a shared helper used by both nspawn and vmspawn, replacing vmspawn's start_systemd_journal_remote(). Extract runtime_directory_make() into path-lookup.{c,h} as a shared helper used by both nspawn and vmspawn, replacing vmspawn's inline runtime directory creation logic. Co-developed-by: Claude Opus 4.6 --- man/systemd-nspawn.xml | 30 +++++ shell-completion/bash/systemd-nspawn | 4 +- shell-completion/zsh/_systemd-nspawn | 5 + src/libsystemd/sd-path/path-lookup.c | 39 +++++++ src/libsystemd/sd-path/path-lookup.h | 1 + src/nspawn/nspawn.c | 169 +++++++++++++++++++++++---- src/shared/fork-notify.c | 94 +++++++++++++++ src/shared/fork-notify.h | 9 ++ src/ssh-generator/ssh-generator.c | 2 +- src/ssh-generator/ssh-proxy.c | 4 +- src/vmspawn/vmspawn.c | 141 +++------------------- 11 files changed, 346 insertions(+), 152 deletions(-) diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 045aa60db81..54d6f83915c 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -1590,6 +1590,36 @@ After=sys-subsystem-net-devices-ens1.device + + + + Forward the container's journal to the host by starting + systemd-journal-remote8 + listening on a Unix socket that is bind-mounted into the container. The container's + systemd-journald8 + connects to the socket via the journal.forward_to_socket credential and streams + journal entries to the host in real-time. Takes a path to a journal file or directory where the received + entries will be stored. If the path ends in .journal, entries are written to a single + file; otherwise, entries are split per host into the specified directory. + + + + + + + + + + + These options configure the corresponding settings of + systemd-journal-remote8 + when forwarding journal entries from the container. See + journal-remote.conf5 + for the descriptions of these settings. + + + + diff --git a/shell-completion/bash/systemd-nspawn b/shell-completion/bash/systemd-nspawn index b39d3cbd6d8..85f3023083c 100644 --- a/shell-completion/bash/systemd-nspawn +++ b/shell-completion/bash/systemd-nspawn @@ -78,7 +78,9 @@ _systemd_nspawn() { --network-ipvlan --network-veth-extra --network-zone -p --port --system-call-filter --overlay --overlay-ro --settings --rlimit --hostname --no-new-privileges --oom-score-adjust --cpu-affinity --resolv-conf --timezone --root-hash-sig --background --oci-bundle --verity-data - --restrict-address-families' + --restrict-address-families + --forward-journal --forward-journal-max-use --forward-journal-keep-free + --forward-journal-max-file-size --forward-journal-max-files' ) _init_completion || return diff --git a/shell-completion/zsh/_systemd-nspawn b/shell-completion/zsh/_systemd-nspawn index ee28fa74759..10dc527236c 100644 --- a/shell-completion/zsh/_systemd-nspawn +++ b/shell-completion/zsh/_systemd-nspawn @@ -54,4 +54,9 @@ _arguments \ "--notify-ready=[Control when the ready notification is sent]:options:(yes no)" \ "--suppress-sync=[Control whether to suppress disk synchronization for the container payload]:options:(yes no)" \ '--restrict-address-families=[Restrict socket address families accessible in the container.]: : _message "address families"' \ + '--forward-journal=[Forward the container journal to the host via systemd-journal-remote.]: : _files' \ + '--forward-journal-max-use=[Maximum disk space used by forwarded journal files.]: : _message bytes' \ + '--forward-journal-keep-free=[Disk space to keep free for forwarded journal files.]: : _message bytes' \ + '--forward-journal-max-file-size=[Maximum size of individual forwarded journal files.]: : _message bytes' \ + '--forward-journal-max-files=[Maximum number of forwarded journal files.]: : _message number' \ '*:: : _normal' diff --git a/src/libsystemd/sd-path/path-lookup.c b/src/libsystemd/sd-path/path-lookup.c index 32c14fb14a7..4e4abaebf8f 100644 --- a/src/libsystemd/sd-path/path-lookup.c +++ b/src/libsystemd/sd-path/path-lookup.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "fs-util.h" #include "log.h" +#include "mkdir.h" #include "path-lookup.h" #include "path-util.h" #include "stat-util.h" @@ -101,6 +102,44 @@ int runtime_directory(RuntimeScope scope, const char *fallback_suffix, char **re return 1; } +int runtime_directory_make(RuntimeScope scope, const char *subdir, char **ret_dir, char **ret_dir_destroy) { + _cleanup_free_ char *dir = NULL, *destroy = NULL; + int r; + + assert(subdir); + assert(ret_dir); + assert(ret_dir_destroy); + + /* Use runtime_directory() (not _generic()) so that when we run in a systemd service + * with RuntimeDirectory= set, we pick up $RUNTIME_DIRECTORY and place our stuff into the + * directory the service manager prepared for us. When the env var is unset, we fall back + * to the provided subdirectory under /run (or the $XDG_RUNTIME_DIR equivalent in user scope) + * and take care of creation and destruction ourselves. */ + r = runtime_directory(scope, subdir, &dir); + if (r < 0) + return r; + + if (r > 0) { + /* $RUNTIME_DIRECTORY was not set, so we got the fallback path and need to create and + * clean up the directory ourselves. */ + destroy = strdup(dir); + if (!destroy) + return -ENOMEM; + + r = mkdir_p(dir, 0755); + if (r < 0) + return r; + } + + /* When $RUNTIME_DIRECTORY is set the service manager created the directory for us and + * will destroy it (or preserve it, per RuntimeDirectoryPreserve=) when the service stops. */ + + *ret_dir = TAKE_PTR(dir); + *ret_dir_destroy = TAKE_PTR(destroy); + + return 0; +} + static const char* const user_data_unit_paths[] = { "/usr/local/lib/systemd/user", "/usr/local/share/systemd/user", diff --git a/src/libsystemd/sd-path/path-lookup.h b/src/libsystemd/sd-path/path-lookup.h index 67a4f5d69cf..8dcbf766e6a 100644 --- a/src/libsystemd/sd-path/path-lookup.h +++ b/src/libsystemd/sd-path/path-lookup.h @@ -60,6 +60,7 @@ void lookup_paths_done(LookupPaths *p); int config_directory_generic(RuntimeScope scope, const char *suffix, char **ret); int runtime_directory_generic(RuntimeScope scope, const char *suffix, char **ret); int runtime_directory(RuntimeScope scope, const char *fallback_suffix, char **ret); +int runtime_directory_make(RuntimeScope scope, const char *subdir, char **ret_dir, char **ret_dir_destroy); /* We don't treat /etc/xdg/systemd/ in these functions as the xdg base dir spec suggests because we assume * that is a link to /etc/systemd/ anyway. */ diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index c7ccfd49963..b1c8defb6c4 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -49,6 +49,7 @@ #include "fd-util.h" #include "fdset.h" #include "fileio.h" +#include "fork-notify.h" #include "format-table.h" #include "format-util.h" #include "fs-util.h" @@ -95,6 +96,7 @@ #include "pager.h" #include "parse-argument.h" #include "parse-util.h" +#include "path-lookup.h" #include "path-util.h" #include "pidref.h" #include "polkit-agent.h" @@ -133,6 +135,7 @@ /* The notify socket inside the container it can use to talk to nspawn using the sd_notify(3) protocol */ #define NSPAWN_NOTIFY_SOCKET_PATH "/run/host/notify" #define NSPAWN_MOUNT_TUNNEL "/run/host/incoming" +#define NSPAWN_JOURNAL_SOCKET_PATH "/run/host/journal/socket" #define EXIT_FORCE_RESTART 133 @@ -262,6 +265,11 @@ static char *arg_background = NULL; static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID; static bool arg_cleanup = false; static bool arg_ask_password = true; +static char *arg_forward_journal = NULL; +static uint64_t arg_forward_journal_max_use = UINT64_MAX; +static uint64_t arg_forward_journal_keep_free = UINT64_MAX; +static uint64_t arg_forward_journal_max_file_size = UINT64_MAX; +static uint64_t arg_forward_journal_max_files = UINT64_MAX; STATIC_DESTRUCTOR_REGISTER(arg_directory, freep); STATIC_DESTRUCTOR_REGISTER(arg_template, freep); @@ -303,6 +311,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_restrict_address_families, set_freep); STATIC_DESTRUCTOR_REGISTER(arg_settings_filename, freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); STATIC_DESTRUCTOR_REGISTER(arg_background, freep); +STATIC_DESTRUCTOR_REGISTER(arg_forward_journal, freep); static int parse_private_users( const char *s, @@ -1250,6 +1259,36 @@ static int parse_argv(int argc, char *argv[]) { arg_settings_mask |= SETTING_LINK_JOURNAL; break; + OPTION_LONG("forward-journal", "FILE|DIR", "Forward the container's journal to the host"): + r = parse_path_argument(arg, /* suppress_root= */ false, &arg_forward_journal); + if (r < 0) + return r; + break; + + OPTION_LONG("forward-journal-max-use", "BYTES", "Maximum disk space for forwarded journal"): + r = parse_size(arg, 1024, &arg_forward_journal_max_use); + if (r < 0) + return log_error_errno(r, "Failed to parse --forward-journal-max-use= value: %s", optarg); + break; + + OPTION_LONG("forward-journal-keep-free", "BYTES", "Minimum disk space to keep free"): + r = parse_size(arg, 1024, &arg_forward_journal_keep_free); + if (r < 0) + return log_error_errno(r, "Failed to parse --forward-journal-keep-free= value: %s", optarg); + break; + + OPTION_LONG("forward-journal-max-file-size", "BYTES", "Maximum size of individual journal files"): + r = parse_size(arg, 1024, &arg_forward_journal_max_file_size); + if (r < 0) + return log_error_errno(r, "Failed to parse --forward-journal-max-file-size= value: %s", optarg); + break; + + OPTION_LONG("forward-journal-max-files", "N", "Maximum number of journal files to keep"): + r = safe_atou64(arg, &arg_forward_journal_max_files); + if (r < 0) + return log_error_errno(r, "Failed to parse --forward-journal-max-files= value: %s", optarg); + break; + OPTION_GROUP("Mounts"): {} OPTION_LONG("bind", "PATH[:PATH[:OPTIONS]]", @@ -1446,6 +1485,12 @@ static int parse_argv(int argc, char *argv[]) { arg_caps_retain |= arg_private_network ? UINT64_C(1) << CAP_NET_ADMIN : 0; arg_caps_retain &= ~minus; + if ((arg_forward_journal_max_use != UINT64_MAX || + arg_forward_journal_keep_free != UINT64_MAX || + arg_forward_journal_max_file_size != UINT64_MAX || + arg_forward_journal_max_files != UINT64_MAX) && !arg_forward_journal) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--forward-journal-max-use=/--forward-journal-keep-free=/--forward-journal-max-file-size=/--forward-journal-max-files= require --forward-journal=."); + /* Make sure to parse environment before we reset the settings mask below */ r = parse_environment(); if (r < 0) @@ -3708,9 +3753,10 @@ static int setup_notify_child(const void *directory) { return TAKE_FD(fd); } -static int setup_unix_export_dir_outside(char **ret) { +static int setup_unix_export_dir_outside(const char *runtime_dir, char **ret) { int r; + assert(runtime_dir); assert(ret); if (arg_userns_mode == USER_NAMESPACE_MANAGED) { @@ -3719,7 +3765,7 @@ static int setup_unix_export_dir_outside(char **ret) { } _cleanup_free_ char *p = NULL; - p = path_join("/run/systemd/nspawn/unix-export", arg_machine); + p = path_join(runtime_dir, "unix-export"); if (!p) return log_oom(); @@ -5103,6 +5149,7 @@ static int load_oci_bundle(void) { } static int run_container( + const char *runtime_dir, const char *directory, int mount_fd, DissectedImage *dissected_image, @@ -5141,7 +5188,7 @@ static int run_container( assert_se(sigaddset(&mask_chld, SIGCHLD) == 0); /* Set up the unix export host directory on the host first */ - r = setup_unix_export_dir_outside(&unix_export_host_dir); + r = setup_unix_export_dir_outside(runtime_dir, &unix_export_host_dir); if (r < 0) return r; @@ -5907,18 +5954,22 @@ static int cant_be_in_netns(void) { return 0; } -static void cleanup_propagation_and_export_directories(void) { - const char *p; +static void cleanup_propagation_and_export_directories(const char *runtime_dir) { + _cleanup_free_ char *p = NULL; - if (!arg_machine || arg_runtime_scope != RUNTIME_SCOPE_SYSTEM) + if (!runtime_dir || arg_userns_mode == USER_NAMESPACE_MANAGED) return; - p = strjoina("/run/systemd/nspawn/propagate/", arg_machine); - (void) rm_rf(p, REMOVE_ROOT); + p = path_join("/run/systemd/nspawn/propagate", arg_machine); + if (p) + (void) rm_rf(p, REMOVE_ROOT); - p = strjoina("/run/systemd/nspawn/unix-export/", arg_machine); - (void) umount2(p, MNT_DETACH|UMOUNT_NOFOLLOW); - (void) rmdir(p); + free(p); + p = path_join(runtime_dir, "unix-export"); + if (p) { + (void) umount2(p, MNT_DETACH|UMOUNT_NOFOLLOW); + (void) rmdir(p); + } } static int do_cleanup(void) { @@ -5931,7 +5982,16 @@ static int do_cleanup(void) { if (r < 0) return r; - cleanup_propagation_and_export_directories(); + _cleanup_free_ char *subdir = path_join("systemd/nspawn", arg_machine); + if (!subdir) + return log_oom(); + + _cleanup_free_ char *runtime_dir = NULL; + r = runtime_directory(arg_runtime_scope, subdir, &runtime_dir); + if (r < 0) + return r; + + cleanup_propagation_and_export_directories(runtime_dir); return 0; } @@ -5951,6 +6011,9 @@ static int run(int argc, char *argv[]) { _cleanup_(sd_netlink_unrefp) sd_netlink *nfnl = NULL; _cleanup_(pidref_done) PidRef pid = PIDREF_NULL; _cleanup_(sd_varlink_unrefp) sd_varlink *nsresource_link = NULL, *mountfsd_link = NULL; + _cleanup_free_ char *runtime_dir = NULL, *subdir = NULL; + _cleanup_(rm_rf_physical_and_freep) char *runtime_dir_destroy = NULL; + _cleanup_(fork_notify_terminate) PidRef journal_remote_pidref = PIDREF_NULL; log_setup(); @@ -6440,6 +6503,22 @@ static int run(int argc, char *argv[]) { } else assert_not_reached(); + subdir = path_join("systemd/nspawn", arg_machine); + if (!subdir) { + r = log_oom(); + goto finish; + } + + r = runtime_directory_make( + arg_runtime_scope, + subdir, + &runtime_dir, + &runtime_dir_destroy); + if (r < 0) { + log_error_errno(r, "Failed to create runtime directory: %m"); + goto finish; + } + /* Create a temporary place to mount stuff. */ r = mkdtemp_malloc("/tmp/nspawn-root-XXXXXX", &rootdir); if (r < 0) { @@ -6486,8 +6565,61 @@ static int run(int argc, char *argv[]) { expose_args.nfnl = nfnl; } + if (arg_forward_journal) { + _cleanup_free_ char *socket_path = path_join(runtime_dir, "journal-remote-socket"); + if (!socket_path) { + r = log_oom(); + goto finish; + } + + union sockaddr_union sa; + r = sockaddr_un_set_path(&sa.un, socket_path); + if (r < 0) { + log_error_errno(r, "Failed to set AF_UNIX path to '%s': %m", socket_path); + goto finish; + } + + (void) sockaddr_un_unlink(&sa.un); + + r = fork_journal_remote( + socket_path, + arg_forward_journal, + arg_forward_journal_max_use, + arg_forward_journal_keep_free, + arg_forward_journal_max_file_size, + arg_forward_journal_max_files, + &journal_remote_pidref); + if (r < 0) + goto finish; + + CustomMount *cm = custom_mount_add(&arg_custom_mounts, &arg_n_custom_mounts, CUSTOM_MOUNT_BIND); + if (!cm) { + r = log_oom(); + goto finish; + } + + cm->source = TAKE_PTR(socket_path); + cm->read_only = true; + cm->destination = strdup(NSPAWN_JOURNAL_SOCKET_PATH); + if (!cm->destination) { + r = log_oom(); + goto finish; + } + + r = machine_credential_add(&arg_credentials, "journal.forward_to_socket", NSPAWN_JOURNAL_SOCKET_PATH, SIZE_MAX); + if (r == -EEXIST) { + log_error_errno(r, "Credential 'journal.forward_to_socket' already set via --set-credential=, refusing --forward-journal=."); + goto finish; + } + if (r < 0) { + log_error_errno(r, "Failed to add 'journal.forward_to_socket' credential: %m"); + goto finish; + } + } + for (;;) { r = run_container( + runtime_dir, rootdir, mount_fd, dissected_image, @@ -6528,18 +6660,7 @@ finish: log_warning_errno(errno, "Can't remove image file '%s', ignoring: %m", arg_image); } - if (arg_machine && arg_userns_mode != USER_NAMESPACE_MANAGED) { - const char *p; - - p = strjoina("/run/systemd/nspawn/propagate/", arg_machine); - (void) rm_rf(p, REMOVE_ROOT); - - p = strjoina("/run/systemd/nspawn/unix-export/", arg_machine); - (void) umount2(p, MNT_DETACH|UMOUNT_NOFOLLOW); - (void) rmdir(p); - } - - cleanup_propagation_and_export_directories(); + cleanup_propagation_and_export_directories(runtime_dir); expose_port_flush(nfnl, arg_expose_ports, AF_INET, &expose_args.address4); expose_port_flush(nfnl, arg_expose_ports, AF_INET6, &expose_args.address6); diff --git a/src/shared/fork-notify.c b/src/shared/fork-notify.c index e9686786fe7..066ee29115f 100644 --- a/src/shared/fork-notify.c +++ b/src/shared/fork-notify.c @@ -3,14 +3,19 @@ #include #include +#include "alloc-util.h" #include "build-path.h" +#include "chase.h" +#include "chattr-util.h" #include "escape.h" #include "event-util.h" #include "exit-status.h" +#include "fd-util.h" #include "fork-notify.h" #include "log.h" #include "notify-recv.h" #include "parse-util.h" +#include "path-util.h" #include "pidref.h" #include "process-util.h" #include "runtime-scope.h" @@ -238,3 +243,92 @@ int journal_fork(RuntimeScope scope, char * const* units, OutputMode output, Pid return fork_notify(argv, ret_pidref); } + +int fork_journal_remote( + const char *listen_address, + const char *output, + uint64_t max_use, + uint64_t keep_free, + uint64_t max_file_size, + uint64_t max_files, + PidRef *ret_pidref) { + + int r; + + assert(listen_address); + assert(output); + assert(ret_pidref); + + ChaseFlags chase_flags = CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY; + if (endswith(output, ".journal")) + chase_flags |= CHASE_PARENT; + + _cleanup_close_ int fd = -EBADF; + r = chase(output, /* root= */ NULL, chase_flags, /* ret_path= */ NULL, &fd); + if (r < 0) + return log_error_errno(r, "Failed to create journal directory for '%s': %m", output); + + r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL); + if (r < 0) + log_debug_errno(r, "Failed to set NOCOW flag on journal directory for '%s', ignoring: %m", output); + + _cleanup_free_ char *sd_socket_activate = NULL; + r = find_executable("systemd-socket-activate", &sd_socket_activate); + if (r < 0) + return log_error_errno(r, "Failed to find systemd-socket-activate binary: %m"); + + _cleanup_free_ char *sd_journal_remote = NULL; + r = find_executable_full( + "systemd-journal-remote", + /* root= */ NULL, + STRV_MAKE(LIBEXECDIR), + /* use_path_envvar= */ true, + &sd_journal_remote, + /* ret_fd= */ NULL); + if (r < 0) + return log_error_errno(r, "Failed to find systemd-journal-remote binary: %m"); + + _cleanup_strv_free_ char **argv = strv_new( + sd_socket_activate, + "--listen", listen_address, + sd_journal_remote, + "--output", output, + "--split-mode", endswith(output, ".journal") ? "none" : "host"); + if (!argv) + return log_oom(); + + if (max_use != UINT64_MAX && + strv_extendf(&argv, "--max-use=%" PRIu64, max_use) < 0) + return log_oom(); + + if (keep_free != UINT64_MAX && + strv_extendf(&argv, "--keep-free=%" PRIu64, keep_free) < 0) + return log_oom(); + + if (max_file_size != UINT64_MAX && + strv_extendf(&argv, "--max-file-size=%" PRIu64, max_file_size) < 0) + return log_oom(); + + if (max_files != UINT64_MAX && + strv_extendf(&argv, "--max-files=%" PRIu64, max_files) < 0) + return log_oom(); + + r = fork_notify(/* argv= */ NULL, ret_pidref); + if (r < 0) + return r; + if (r == 0) { + /* In the child */ + if (setenv("SYSTEMD_JOURNAL_REMOTE_CONFIG_FILE", + "/dev/null", + /* overwrite= */ true) < 0) { + log_debug_errno(errno, "Failed to set $SYSTEMD_JOURNAL_REMOTE_CONFIG_FILE: %m"); + _exit(EXIT_MEMORY); + } + + r = invoke_callout_binary(argv[0], argv); + log_debug_errno(r, "Failed to invoke %s: %m", argv[0]); + _exit(EXIT_EXEC); + } + + return 0; +} diff --git a/src/shared/fork-notify.h b/src/shared/fork-notify.h index 2dbfe368a46..cc241beff93 100644 --- a/src/shared/fork-notify.h +++ b/src/shared/fork-notify.h @@ -11,3 +11,12 @@ void fork_notify_terminate(PidRef *pidref); void fork_notify_terminate_many(sd_event_source **array, size_t n); int journal_fork(RuntimeScope scope, char * const *units, OutputMode output, PidRef *ret_pidref); + +int fork_journal_remote( + const char *listen_address, + const char *output, + uint64_t max_use, + uint64_t keep_free, + uint64_t max_file_size, + uint64_t max_files, + PidRef *ret_pidref); diff --git a/src/ssh-generator/ssh-generator.c b/src/ssh-generator/ssh-generator.c index bc01250a55b..ae062fc58da 100644 --- a/src/ssh-generator/ssh-generator.c +++ b/src/ssh-generator/ssh-generator.c @@ -338,7 +338,7 @@ static int add_export_unix_socket( return r; log_debug("Binding SSH to AF_UNIX socket /run/host/unix-export/ssh\n" - "→ connect via 'ssh unix/run/systemd/nspawn/unix-export/\?\?\?/ssh' from host"); + "→ connect via 'ssh unix/run/systemd/nspawn/\?\?\?/unix-export/ssh' from host"); return 0; } diff --git a/src/ssh-generator/ssh-proxy.c b/src/ssh-generator/ssh-proxy.c index fa42c5bee80..79d8ee05656 100644 --- a/src/ssh-generator/ssh-proxy.c +++ b/src/ssh-generator/ssh-proxy.c @@ -349,11 +349,11 @@ static int process_machine(const char *machine, const char *port) { if (!streq_ptr(p.service, "systemd-nspawn")) return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Don't know how to SSH into '%s' container %s.", p.service, machine); - r = runtime_directory_generic(scope, "systemd/nspawn/unix-export", &path); + r = runtime_directory_generic(scope, "systemd/nspawn", &path); if (r < 0) return log_error_errno(r, "Failed to determine runtime directory: %m"); - if (!path_extend(&path, machine, "ssh")) + if (!path_extend(&path, machine, "unix-export", "ssh")) return log_oom(); r = is_socket(path); diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index b7f03501f76..8ccd631762c 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -25,8 +25,6 @@ #include "bus-locator.h" #include "bus-util.h" #include "capability-util.h" -#include "chase.h" -#include "chattr-util.h" #include "common-signal.h" #include "copy.h" #include "discover-image.h" @@ -34,7 +32,6 @@ #include "escape.h" #include "ether-addr-util.h" #include "event-util.h" -#include "exit-status.h" #include "extract-word.h" #include "fd-util.h" #include "fileio.h" @@ -1621,87 +1618,6 @@ static int start_tpm( return 0; } -static int start_systemd_journal_remote( - const char *scope, - unsigned port, - const char *sd_socket_activate, - char **ret_listen_address, - PidRef *ret_pidref) { - - int r; - - assert(scope); - assert(sd_socket_activate); - - _cleanup_free_ char *scope_prefix = NULL; - r = unit_name_to_prefix(scope, &scope_prefix); - if (r < 0) - return log_error_errno(r, "Failed to strip .scope suffix from scope: %m"); - - _cleanup_free_ char *listen_address = NULL; - if (asprintf(&listen_address, "vsock:2:%u", port) < 0) - return log_oom(); - - _cleanup_free_ char *sd_journal_remote = NULL; - r = find_executable_full( - "systemd-journal-remote", - /* root= */ NULL, - STRV_MAKE(LIBEXECDIR), - /* use_path_envvar= */ true, /* systemd-journal-remote should be installed in - * LIBEXECDIR, but for supporting fancy setups. */ - &sd_journal_remote, - /* ret_fd= */ NULL); - if (r < 0) - return log_error_errno(r, "Failed to find systemd-journal-remote binary: %m"); - - _cleanup_strv_free_ char **argv = strv_new( - sd_socket_activate, - "--listen", listen_address, - sd_journal_remote, - "--output", arg_forward_journal, - "--split-mode", endswith(arg_forward_journal, ".journal") ? "none" : "host"); - if (!argv) - return log_oom(); - - if (arg_forward_journal_max_use != UINT64_MAX && - strv_extendf(&argv, "--max-use=%" PRIu64, arg_forward_journal_max_use) < 0) - return log_oom(); - - if (arg_forward_journal_keep_free != UINT64_MAX && - strv_extendf(&argv, "--keep-free=%" PRIu64, arg_forward_journal_keep_free) < 0) - return log_oom(); - - if (arg_forward_journal_max_file_size != UINT64_MAX && - strv_extendf(&argv, "--max-file-size=%" PRIu64, arg_forward_journal_max_file_size) < 0) - return log_oom(); - - if (arg_forward_journal_max_files != UINT64_MAX && - strv_extendf(&argv, "--max-files=%" PRIu64, arg_forward_journal_max_files) < 0) - return log_oom(); - - r = fork_notify(/* argv= */ NULL, ret_pidref); - if (r < 0) - return r; - if (r == 0) { - /* In the child */ - if (setenv("SYSTEMD_JOURNAL_REMOTE_CONFIG_FILE", - "/dev/null", - /* overwrite= */ true) < 0) { - log_debug_errno(errno, "Failed to set $SYSTEMD_JOURNAL_REMOTE_CONFIG_FILE: %m"); - _exit(EXIT_MEMORY); - } - - r = invoke_callout_binary(argv[0], argv); - log_error_errno(r, "Failed to invoke %s: %m", argv[0]); - _exit(EXIT_EXEC); - } - - if (ret_listen_address) - *ret_listen_address = TAKE_PTR(listen_address); - - return 0; -} - static int discover_root(char **ret) { int r; _cleanup_(dissected_image_unrefp) DissectedImage *image = NULL; @@ -2725,13 +2641,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { return log_oom(); /* Create our runtime directory. We need this for the QMP varlink control socket, the QEMU - * config file, TPM state, virtiofsd sockets, runtime mounts, and SSH key material. - * - * Use runtime_directory() (not _generic()) so that when vmspawn runs in a systemd service - * with RuntimeDirectory= set, we pick up $RUNTIME_DIRECTORY and place our stuff into the - * directory the service manager prepared for us. When the env var is unset, we fall back - * to /run/systemd/vmspawn// (or the $XDG_RUNTIME_DIR equivalent in user scope) - * and take care of creation and destruction ourselves. */ + * config file, TPM state, virtiofsd sockets, runtime mounts, and SSH key material. */ _cleanup_free_ char *runtime_dir = NULL, *runtime_dir_suffix = NULL; _cleanup_(rm_rf_physical_and_freep) char *runtime_dir_destroy = NULL; @@ -2739,27 +2649,14 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { if (!runtime_dir_suffix) return log_oom(); - r = runtime_directory(arg_runtime_scope, runtime_dir_suffix, &runtime_dir); + r = runtime_directory_make(arg_runtime_scope, runtime_dir_suffix, &runtime_dir, &runtime_dir_destroy); if (r < 0) - return log_error_errno(r, "Failed to determine runtime directory: %m"); - if (r > 0) { - /* $RUNTIME_DIRECTORY was not set, so we got the fallback path and need to create and - * clean up the directory ourselves. - * - * If a previous vmspawn instance was killed without cleanup (e.g. SIGKILL), the directory may - * already exist with stale contents. This is harmless: varlink's sockaddr_un_unlink() removes stale - * sockets before bind(), and other files (QEMU config, SSH keys) are created fresh. This matches - * nspawn's approach of not proactively cleaning stale runtime directories. */ - r = mkdir_p(runtime_dir, 0755); - if (r < 0) - return log_error_errno(r, "Failed to create runtime directory '%s': %m", runtime_dir); + return log_error_errno(r, "Failed to create runtime directory: %m"); - runtime_dir_destroy = strdup(runtime_dir); - if (!runtime_dir_destroy) - return log_oom(); - } - /* When $RUNTIME_DIRECTORY is set the service manager created the directory for us and - * will destroy it (or preserve it, per RuntimeDirectoryPreserve=) when the service stops. */ + /* If a previous vmspawn instance was killed without cleanup (e.g. SIGKILL), the directory may + * already exist with stale contents. This is harmless: varlink's sockaddr_un_unlink() removes stale + * sockets before bind(), and other files (QEMU config, SSH keys) are created fresh. This matches + * nspawn's approach of not proactively cleaning stale runtime directories. */ log_debug("Using runtime directory: %s", runtime_dir); @@ -3471,25 +3368,21 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { if (arg_forward_journal) { _cleanup_free_ char *listen_address = NULL; - - ChaseFlags chase_flags = CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY; - if (endswith(arg_forward_journal, ".journal")) - chase_flags |= CHASE_PARENT; - - _cleanup_close_ int journal_fd = -EBADF; - r = chase(arg_forward_journal, /* root= */ NULL, chase_flags, /* ret_path= */ NULL, &journal_fd); - if (r < 0) - return log_error_errno(r, "Failed to create journal directory for '%s': %m", arg_forward_journal); - - r = chattr_fd(journal_fd, FS_NOCOW_FL, FS_NOCOW_FL); - if (r < 0) - log_debug_errno(r, "Failed to set NOCOW flag on journal directory for '%s', ignoring: %m", arg_forward_journal); + if (asprintf(&listen_address, "vsock:2:%u", child_cid) < 0) + return log_oom(); if (!GREEDY_REALLOC(children, n_children + 1)) return log_oom(); _cleanup_(fork_notify_terminate) PidRef child = PIDREF_NULL; - r = start_systemd_journal_remote(unit, child_cid, sd_socket_activate, &listen_address, &child); + r = fork_journal_remote( + listen_address, + arg_forward_journal, + arg_forward_journal_max_use, + arg_forward_journal_keep_free, + arg_forward_journal_max_file_size, + arg_forward_journal_max_files, + &child); if (r < 0) return r; -- 2.47.3