From: Daan De Meyer Date: Fri, 27 Mar 2026 14:38:09 +0000 (+0000) Subject: vmspawn,journal-remote: add journal forwarding disk usage options X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=012d87c1fc3f22d7bc23a4389e710082c6666fc5;p=thirdparty%2Fsystemd.git vmspawn,journal-remote: add journal forwarding disk usage options Add options to vmspawn to configure journal-remote disk usage limits when forwarding journal entries from the VM. These are passed through as --max-use=, --keep-free=, --max-file-size=, and --max-files= command-line arguments to systemd-journal-remote. Add --max-use=, --keep-free=, --max-file-size=, and --max-files= command-line options to systemd-journal-remote to allow overriding the corresponding settings from the configuration file. Add $SYSTEMD_JOURNAL_REMOTE_CONFIG_FILE environment variable support to systemd-journal-remote. When set, the specified file is used instead of the default configuration file and drop-in directories. When set to the empty string or /dev/null, configuration file parsing is skipped entirely. vmspawn sets this to /dev/null in the child process to avoid inheriting the host's journal-remote configuration. Make fork_notify() argv parameter optional. When NULL is passed, fork_notify() returns 0 in the child (with $NOTIFY_SOCKET set) and lets the caller run custom code before exec. Returns 1 in the parent. This allows vmspawn to set environment variables in the child without polluting the parent process. Co-developed-by: Claude Opus 4.6 --- diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 53907546618..53f4a1a60c8 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -679,6 +679,11 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \ string format. Overrides the default maximum allowed size for a file-descriptor based input record to be stored in the journal. +* `$SYSTEMD_JOURNAL_REMOTE_CONFIG_FILE` – path to a configuration file for + `systemd-journal-remote`. When set, the specified file is used instead of the + default configuration file and drop-in directories. If set to the empty string + or `/dev/null`, configuration file parsing is skipped entirely. + * `$SYSTEMD_CATALOG` – path to the compiled catalog database file to use for `journalctl -x`, `journalctl --update-catalog`, `journalctl --list-catalog` and related calls. diff --git a/man/systemd-journal-remote.service.xml b/man/systemd-journal-remote.service.xml index d6258ce2fcd..7beb96403d1 100644 --- a/man/systemd-journal-remote.service.xml +++ b/man/systemd-journal-remote.service.xml @@ -333,6 +333,19 @@ + + + + + + + These options override the corresponding settings from the configuration file + (see journal-remote.conf5). + See that page for the descriptions of these options. + + + + diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml index 5749136a5d3..5c5ec4ccbcd 100644 --- a/man/systemd-vmspawn.xml +++ b/man/systemd-vmspawn.xml @@ -668,6 +668,21 @@ + + + + + + + These options configure the corresponding settings of + systemd-journal-remote8 + when forwarding journal entries from the VM. See + journal-remote.conf5 + for the descriptions of these settings. + + + + diff --git a/shell-completion/bash/systemd-vmspawn b/shell-completion/bash/systemd-vmspawn index b2b3f4f5a8b..efa0dae58de 100644 --- a/shell-completion/bash/systemd-vmspawn +++ b/shell-completion/bash/systemd-vmspawn @@ -38,7 +38,7 @@ _systemd_vmspawn() { [BIND]='--bind --bind-ro' [SSH_KEY]='--ssh-key' [CONSOLE]='--console' - [ARG]='--cpus --ram --vsock-cid -M --machine --uuid --private-users --background --set-credential --load-credential' + [ARG]='--cpus --ram --vsock-cid -M --machine --uuid --private-users --background --set-credential --load-credential --forward-journal-max-use --forward-journal-keep-free --forward-journal-max-file-size --forward-journal-max-files' [IMAGE_FORMAT]='--image-format' [IMAGE_DISK_TYPE]='--image-disk-type' ) diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c index d5277ad7ef1..4867bf36094 100644 --- a/src/journal-remote/journal-remote-main.c +++ b/src/journal-remote/journal-remote-main.c @@ -25,6 +25,7 @@ #include "parse-argument.h" #include "parse-helpers.h" #include "parse-util.h" +#include "path-util.h" #include "pretty-print.h" #include "process-util.h" #include "socket-netlink.h" @@ -829,6 +830,22 @@ static int parse_config(void) { {} }; + const char *config_file = secure_getenv("SYSTEMD_JOURNAL_REMOTE_CONFIG_FILE"); + if (config_file) { + if (isempty(config_file) || path_equal(config_file, "/dev/null")) + return 0; + + return config_parse( + /* unit= */ NULL, + config_file, + /* f= */ NULL, + "Remote\0", + config_item_table_lookup, items, + CONFIG_PARSE_WARN, + /* userdata= */ NULL, + /* ret_stat= */ NULL); + } + return config_parse_standard_file_with_dropins( "systemd/journal-remote.conf", "Remote\0", @@ -1004,6 +1021,30 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --gnutls-log= is not available."); #endif break; + + OPTION_LONG("max-use", "BYTES", "Maximum disk space to use"): + r = parse_size(arg, 1024, &arg_max_use); + if (r < 0) + return log_error_errno(r, "Failed to parse --max-use= value: %s", arg); + break; + + OPTION_LONG("keep-free", "BYTES", "Minimum disk space to keep free"): + r = parse_size(arg, 1024, &arg_keep_free); + if (r < 0) + return log_error_errno(r, "Failed to parse --keep-free= value: %s", arg); + break; + + OPTION_LONG("max-file-size", "BYTES", "Maximum size of individual journal files"): + r = parse_size(arg, 1024, &arg_max_size); + if (r < 0) + return log_error_errno(r, "Failed to parse --max-file-size= value: %s", arg); + break; + + OPTION_LONG("max-files", "N", "Maximum number of journal files to keep"): + r = safe_atou64(arg, &arg_n_max_files); + if (r < 0) + return log_error_errno(r, "Failed to parse --max-files= value: %s", arg); + break; } arg_files = strv_copy(option_parser_get_args(&state)); diff --git a/src/shared/fork-notify.c b/src/shared/fork-notify.c index 307197ac197..e9686786fe7 100644 --- a/src/shared/fork-notify.c +++ b/src/shared/fork-notify.c @@ -90,7 +90,6 @@ static int on_child_notify(sd_event_source *s, int fd, uint32_t revents, void *u int fork_notify(char * const *argv, PidRef *ret_pidref) { int r; - assert(!strv_isempty(argv)); assert(ret_pidref); if (!is_main_thread()) @@ -119,7 +118,7 @@ int fork_notify(char * const *argv, PidRef *ret_pidref) { if (r < 0) return r; - if (DEBUG_LOGGING) { + if (DEBUG_LOGGING && argv) { _cleanup_free_ char *l = quote_command_line(argv, SHELL_ESCAPE_EMPTY); log_debug("Invoking '%s' as child.", strnull(l)); } @@ -141,6 +140,11 @@ int fork_notify(char * const *argv, PidRef *ret_pidref) { _exit(EXIT_MEMORY); } + if (!argv) { + *ret_pidref = TAKE_PIDREF(child); + return 0; /* Let the caller run custom code in the child */ + } + r = invoke_callout_binary(argv[0], argv); log_debug_errno(r, "Failed to invoke %s: %m", argv[0]); _exit(EXIT_EXEC); @@ -164,7 +168,7 @@ int fork_notify(char * const *argv, PidRef *ret_pidref) { *ret_pidref = TAKE_PIDREF(child); - return 0; + return 1; /* In the parent */ } static void fork_notify_terminate_internal(PidRef *pidref) { diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index fa8c3402ffc..b7f03501f76 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -34,6 +34,7 @@ #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" @@ -159,6 +160,10 @@ 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 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 int arg_register = -1; static bool arg_keep_unit = false; static sd_id128_t arg_uuid = {}; @@ -844,6 +849,30 @@ static int parse_argv(int argc, char *argv[]) { 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_LONG("pass-ssh-key", "BOOL", "Create an SSH key to access the VM"): r = parse_boolean_argument("--pass-ssh-key=", arg, &arg_pass_ssh_key); if (r < 0) @@ -923,6 +952,12 @@ static int parse_argv(int argc, char *argv[]) { if (arg_ram_slots > 0 && arg_ram_max == 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Memory hotplug slots require a maximum RAM size"); + 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=."); + if (arg_ephemeral && arg_extra_drives.n_drives > 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --ephemeral with --extra-drive="); @@ -1628,9 +1663,38 @@ static int start_systemd_journal_remote( if (!argv) return log_oom(); - r = fork_notify(argv, ret_pidref); + 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);