From ef69668db20514543e369a63c40099d7e0c631a4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jul 2025 11:46:46 +0900 Subject: [PATCH 01/24] journald: move several configuration related definitions to journald-config.[ch] No functional change, just refactoring and preparation for later changes. --- src/journal/journald-config.c | 513 +++++++++++++++++++++++++++++++++ src/journal/journald-config.h | 63 ++++ src/journal/journald-forward.h | 5 + src/journal/journald-manager.c | 491 +------------------------------ src/journal/journald-manager.h | 86 +----- src/journal/meson.build | 1 + 6 files changed, 595 insertions(+), 564 deletions(-) create mode 100644 src/journal/journald-config.c create mode 100644 src/journal/journald-config.h diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c new file mode 100644 index 00000000000..58d86b507b9 --- /dev/null +++ b/src/journal/journald-config.c @@ -0,0 +1,513 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-event.h" + +#include "conf-parser.h" +#include "creds-util.h" +#include "daemon-util.h" +#include "journald-config.h" +#include "journald-kmsg.h" +#include "journald-manager.h" +#include "log.h" +#include "parse-util.h" +#include "proc-cmdline.h" +#include "socket-netlink.h" +#include "string-table.h" +#include "string-util.h" +#include "syslog-util.h" +#include "time-util.h" + +#define DEFAULT_SYNC_INTERVAL_USEC (5*USEC_PER_MINUTE) +#define DEFAULT_RATE_LIMIT_INTERVAL (30*USEC_PER_SEC) +#define DEFAULT_RATE_LIMIT_BURST 10000 +#define DEFAULT_MAX_FILE_USEC USEC_PER_MONTH + +/* Pick a good default that is likely to fit into AF_UNIX and AF_INET SOCK_DGRAM datagrams, and even leaves some room + * for a bit of additional metadata. */ +#define DEFAULT_LINE_MAX (48*1024) + +#define JOURNAL_CONFIG_INIT \ + (JournalConfig) { \ + .forward_to_socket = (SocketAddress) { .sockaddr.sa.sa_family = AF_UNSPEC }, \ + .storage = _STORAGE_INVALID, \ + .max_level_store = -1, \ + .max_level_syslog = -1, \ + .max_level_kmsg = -1, \ + .max_level_console = -1, \ + .max_level_wall = -1, \ + .max_level_socket = -1, \ + } + +static void manager_set_defaults(Manager *m) { + assert(m); + + m->compress.enabled = true; + m->compress.threshold_bytes = UINT64_MAX; + + m->seal = true; + + /* By default, only read from /dev/kmsg if are the main namespace */ + m->read_kmsg = !m->namespace; + + m->set_audit = true; + + m->sync_interval_usec = DEFAULT_SYNC_INTERVAL_USEC; + + m->ratelimit_interval = DEFAULT_RATE_LIMIT_INTERVAL; + m->ratelimit_burst = DEFAULT_RATE_LIMIT_BURST; + + m->system_storage.name = "System Journal"; + journal_reset_metrics(&m->system_storage.metrics); + + m->runtime_storage.name = "Runtime Journal"; + journal_reset_metrics(&m->runtime_storage.metrics); + + m->max_file_usec = DEFAULT_MAX_FILE_USEC; + + m->config.forward_to_wall = true; + + m->config.max_level_store = LOG_DEBUG; + m->config.max_level_syslog = LOG_DEBUG; + m->config.max_level_kmsg = LOG_NOTICE; + m->config.max_level_console = LOG_INFO; + m->config.max_level_wall = LOG_EMERG; + m->config.max_level_socket = LOG_DEBUG; + + m->line_max = DEFAULT_LINE_MAX; +} + +static void manager_reset_configs(Manager *m) { + assert(m); + + m->config_by_cmdline = JOURNAL_CONFIG_INIT; + m->config_by_conf = JOURNAL_CONFIG_INIT; + m->config_by_cred = JOURNAL_CONFIG_INIT; +} + +static void manager_merge_forward_to_socket(Manager *m) { + assert(m); + + /* Conf file takes precedence over credentials. */ + if (m->config_by_conf.forward_to_socket.sockaddr.sa.sa_family != AF_UNSPEC) + m->config.forward_to_socket = m->config_by_conf.forward_to_socket; + else if (m->config_by_cred.forward_to_socket.sockaddr.sa.sa_family != AF_UNSPEC) + m->config.forward_to_socket = m->config_by_cred.forward_to_socket; + else + m->config.forward_to_socket = (SocketAddress) { .sockaddr.sa.sa_family = AF_UNSPEC }; +} + +static void manager_merge_storage(Manager *m) { + assert(m); + + /* Conf file takes precedence over credentials. */ + if (m->config_by_conf.storage != _STORAGE_INVALID) + m->config.storage = m->config_by_conf.storage; + else if (m->config_by_cred.storage != _STORAGE_INVALID) + m->config.storage = m->config_by_cred.storage; + else + m->config.storage = m->namespace ? STORAGE_PERSISTENT : STORAGE_AUTO; +} + +#define MERGE_BOOL(name, default_value) \ + (m->config.name = (m->config_by_cmdline.name ? m->config_by_cmdline.name : \ + m->config_by_conf.name ? m->config_by_conf.name : \ + m->config_by_cred.name ? m->config_by_cred.name : \ + default_value)) + +#define MERGE_NON_NEGATIVE(name, default_value) \ + (m->config.name = (m->config_by_cmdline.name >= 0 ? m->config_by_cmdline.name : \ + m->config_by_conf.name >= 0 ? m->config_by_conf.name : \ + m->config_by_cred.name >= 0 ? m->config_by_cred.name : \ + default_value)) + +static void manager_merge_configs(Manager *m) { + assert(m); + + /* + * From highest to lowest priority: cmdline, conf, cred + */ + manager_merge_storage(m); + manager_merge_forward_to_socket(m); + + MERGE_BOOL(forward_to_syslog, false); + MERGE_BOOL(forward_to_kmsg, false); + MERGE_BOOL(forward_to_console, false); + MERGE_BOOL(forward_to_wall, true); + + MERGE_NON_NEGATIVE(max_level_store, LOG_DEBUG); + MERGE_NON_NEGATIVE(max_level_syslog, LOG_DEBUG); + MERGE_NON_NEGATIVE(max_level_kmsg, LOG_NOTICE); + MERGE_NON_NEGATIVE(max_level_console, LOG_INFO); + MERGE_NON_NEGATIVE(max_level_wall, LOG_EMERG); + MERGE_NON_NEGATIVE(max_level_socket, LOG_DEBUG); +} + +static void manager_adjust_configs(Manager *m) { + assert(m); + + if (!!m->ratelimit_interval != !!m->ratelimit_burst) { /* One set to 0 and the other not? */ + log_debug("Setting both rate limit interval and burst from %s/%u to 0/0", + FORMAT_TIMESPAN(m->ratelimit_interval, USEC_PER_SEC), + m->ratelimit_burst); + m->ratelimit_interval = m->ratelimit_burst = 0; + } +} + +static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { + Manager *m = ASSERT_PTR(data); + int r; + + if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_syslog")) { + + r = value ? parse_boolean(value) : true; + if (r < 0) + log_warning("Failed to parse forward to syslog switch \"%s\". Ignoring.", value); + else + m->config_by_cmdline.forward_to_syslog = r; + + } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_kmsg")) { + + r = value ? parse_boolean(value) : true; + if (r < 0) + log_warning("Failed to parse forward to kmsg switch \"%s\". Ignoring.", value); + else + m->config_by_cmdline.forward_to_kmsg = r; + + } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_console")) { + + r = value ? parse_boolean(value) : true; + if (r < 0) + log_warning("Failed to parse forward to console switch \"%s\". Ignoring.", value); + else + m->config_by_cmdline.forward_to_console = r; + + } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_wall")) { + + r = value ? parse_boolean(value) : true; + if (r < 0) + log_warning("Failed to parse forward to wall switch \"%s\". Ignoring.", value); + else + m->config_by_cmdline.forward_to_wall = r; + + } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_console")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = log_level_from_string(value); + if (r < 0) + log_warning("Failed to parse max level console value \"%s\". Ignoring.", value); + else + m->config_by_cmdline.max_level_console = r; + + } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_store")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = log_level_from_string(value); + if (r < 0) + log_warning("Failed to parse max level store value \"%s\". Ignoring.", value); + else + m->config_by_cmdline.max_level_store = r; + + } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_syslog")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = log_level_from_string(value); + if (r < 0) + log_warning("Failed to parse max level syslog value \"%s\". Ignoring.", value); + else + m->config_by_cmdline.max_level_syslog = r; + + } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_kmsg")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = log_level_from_string(value); + if (r < 0) + log_warning("Failed to parse max level kmsg value \"%s\". Ignoring.", value); + else + m->config_by_cmdline.max_level_kmsg = r; + + } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_wall")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = log_level_from_string(value); + if (r < 0) + log_warning("Failed to parse max level wall value \"%s\". Ignoring.", value); + else + m->config_by_cmdline.max_level_wall = r; + + } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_socket")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = log_level_from_string(value); + if (r < 0) + log_warning("Failed to parse max level socket value \"%s\". Ignoring.", value); + else + m->config_by_cmdline.max_level_socket = r; + + } else if (startswith(key, "systemd.journald")) + log_warning("Unknown journald kernel command line option \"%s\". Ignoring.", key); + + /* do not warn about state here, since probably systemd already did */ + return 0; +} + +static void manager_parse_config_file(Manager *m) { + const char *conf_file; + + assert(m); + + if (m->namespace) + conf_file = strjoina("systemd/journald@", m->namespace, ".conf"); + else + conf_file = "systemd/journald.conf"; + + (void) config_parse_standard_file_with_dropins( + conf_file, + "Journal\0", + config_item_perf_lookup, + journald_gperf_lookup, + CONFIG_PARSE_WARN, + m); +} + +static void manager_load_credentials(Manager *m) { + _cleanup_free_ void *data = NULL; + int r; + + assert(m); + + r = read_credential("journal.forward_to_socket", &data, NULL); + if (r < 0) + log_debug_errno(r, "Failed to read credential journal.forward_to_socket, ignoring: %m"); + else { + r = socket_address_parse(&m->config_by_cred.forward_to_socket, data); + if (r < 0) + log_debug_errno(r, "Failed to parse socket address '%s' from credential journal.forward_to_socket, ignoring: %m", (char *) data); + } + + data = mfree(data); + + r = read_credential("journal.storage", &data, NULL); + if (r < 0) + log_debug_errno(r, "Failed to read credential journal.storage, ignoring: %m"); + else { + r = storage_from_string(data); + if (r < 0) + log_debug_errno(r, "Failed to parse storage '%s' from credential journal.storage, ignoring: %m", (char *) data); + else + m->config_by_cred.storage = r; + } +} + +void manager_load_config(Manager *m) { + int r; + + assert(m); + + manager_set_defaults(m); + manager_reset_configs(m); + + manager_load_credentials(m); + manager_parse_config_file(m); + + if (!m->namespace) { + /* Parse kernel command line, but only if we are not a namespace instance */ + r = proc_cmdline_parse(parse_proc_cmdline_item, m, PROC_CMDLINE_STRIP_RD_PREFIX); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + } + + manager_merge_configs(m); + + manager_adjust_configs(m); +} + +static void manager_reload_config(Manager *m) { + assert(m); + + manager_set_defaults(m); + + m->config_by_conf = JOURNAL_CONFIG_INIT; + manager_parse_config_file(m); + + manager_merge_configs(m); + manager_adjust_configs(m); +} + +int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + Manager *m = ASSERT_PTR(userdata); + int r; + + (void) notify_reloading(); + + manager_reload_config(m); + + r = manager_reload_dev_kmsg(m); + if (r < 0) + return r; + + r = manager_reload_journals(m); + if (r < 0) + return r; + + log_info("Config file reloaded."); + (void) sd_notify(/* unset_environment */ false, NOTIFY_READY_MESSAGE); + + return 0; +} + +static const char* const storage_table[_STORAGE_MAX] = { + [STORAGE_AUTO] = "auto", + [STORAGE_VOLATILE] = "volatile", + [STORAGE_PERSISTENT] = "persistent", + [STORAGE_NONE] = "none" +}; + +DEFINE_STRING_TABLE_LOOKUP(storage, Storage); +DEFINE_CONFIG_PARSE_ENUM(config_parse_storage, storage, Storage); + +static const char* const split_mode_table[_SPLIT_MAX] = { + [SPLIT_LOGIN] = "login", + [SPLIT_UID] = "uid", + [SPLIT_NONE] = "none", +}; + +DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode); +DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode); + +int config_parse_line_max( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + size_t *sz = ASSERT_PTR(data); + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) + /* Empty assignment means default */ + *sz = DEFAULT_LINE_MAX; + else { + uint64_t v; + + r = parse_size(rvalue, 1024, &v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse LineMax= value, ignoring: %s", rvalue); + return 0; + } + + if (v < 79) { + /* Why specify 79 here as minimum line length? Simply, because the most common traditional + * terminal size is 80ch, and it might make sense to break one character before the natural + * line break would occur on that. */ + log_syntax(unit, LOG_WARNING, filename, line, 0, "LineMax= too small, clamping to 79: %s", rvalue); + *sz = 79; + } else if (v > (uint64_t) (SSIZE_MAX-1)) { + /* So, why specify SSIZE_MAX-1 here? Because that's one below the largest size value read() + * can return, and we need one extra byte for the trailing NUL byte. Of course IRL such large + * memory allocations will fail anyway, hence this limit is mostly theoretical anyway, as we'll + * fail much earlier anyway. */ + log_syntax(unit, LOG_WARNING, filename, line, 0, "LineMax= too large, clamping to %" PRIu64 ": %s", (uint64_t) (SSIZE_MAX-1), rvalue); + *sz = SSIZE_MAX-1; + } else + *sz = (size_t) v; + } + + return 0; +} + +int config_parse_compress( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + JournalCompressOptions* compress = ASSERT_PTR(data); + int r; + + assert(filename); + assert(rvalue); + + if (isempty(rvalue)) { + compress->enabled = true; + compress->threshold_bytes = UINT64_MAX; + } else if (streq(rvalue, "1")) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Compress= ambiguously specified as 1, enabling compression with default threshold"); + compress->enabled = true; + } else if (streq(rvalue, "0")) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Compress= ambiguously specified as 0, disabling compression"); + compress->enabled = false; + } else { + r = parse_boolean(rvalue); + if (r < 0) { + r = parse_size(rvalue, 1024, &compress->threshold_bytes); + if (r < 0) + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse Compress= value, ignoring: %s", rvalue); + else + compress->enabled = true; + } else + compress->enabled = r; + } + + return 0; +} + +int config_parse_forward_to_socket( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + SocketAddress* addr = ASSERT_PTR(data); + int r; + + assert(filename); + assert(rvalue); + + if (isempty(rvalue)) + *addr = (SocketAddress) { .sockaddr.sa.sa_family = AF_UNSPEC }; + else { + r = socket_address_parse(addr, rvalue); + if (r < 0) + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse ForwardToSocket= value, ignoring: %s", rvalue); + } + + return 0; +} diff --git a/src/journal/journald-config.h b/src/journal/journald-config.h new file mode 100644 index 00000000000..ffa6c5f2b87 --- /dev/null +++ b/src/journal/journald-config.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "journald-forward.h" +#include "socket-util.h" + +typedef enum Storage { + STORAGE_AUTO, + STORAGE_VOLATILE, + STORAGE_PERSISTENT, + STORAGE_NONE, + _STORAGE_MAX, + _STORAGE_INVALID = -EINVAL, +} Storage; + +typedef enum SplitMode { + SPLIT_UID, + SPLIT_LOGIN, /* deprecated */ + SPLIT_NONE, + _SPLIT_MAX, + _SPLIT_INVALID = -EINVAL, +} SplitMode; + +typedef struct JournalCompressOptions { + bool enabled; + uint64_t threshold_bytes; +} JournalCompressOptions; + +typedef struct JournalConfig { + Storage storage; + + bool forward_to_kmsg; + bool forward_to_syslog; + bool forward_to_console; + bool forward_to_wall; + + SocketAddress forward_to_socket; + + int max_level_store; + int max_level_syslog; + int max_level_kmsg; + int max_level_console; + int max_level_wall; + int max_level_socket; +} JournalConfig; + +void manager_load_config(Manager *m); +int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata); + +/* Defined in generated journald-gperf.c */ +const struct ConfigPerfItem* journald_gperf_lookup(const char *key, GPERF_LEN_TYPE length); + +const char* storage_to_string(Storage s) _const_; +Storage storage_from_string(const char *s) _pure_; + +const char* split_mode_to_string(SplitMode s) _const_; +SplitMode split_mode_from_string(const char *s) _pure_; + +CONFIG_PARSER_PROTOTYPE(config_parse_storage); +CONFIG_PARSER_PROTOTYPE(config_parse_line_max); +CONFIG_PARSER_PROTOTYPE(config_parse_compress); +CONFIG_PARSER_PROTOTYPE(config_parse_forward_to_socket); +CONFIG_PARSER_PROTOTYPE(config_parse_split_mode); diff --git a/src/journal/journald-forward.h b/src/journal/journald-forward.h index 41214b87f69..65458f46ab0 100644 --- a/src/journal/journald-forward.h +++ b/src/journal/journald-forward.h @@ -4,6 +4,11 @@ #include "conf-parser-forward.h" /* IWYU pragma: export */ #include "forward.h" /* IWYU pragma: export */ +typedef enum Storage Storage; +typedef enum SplitMode SplitMode; +typedef struct JournalCompressOptions JournalCompressOptions; +typedef struct JournalConfig JournalConfig; + typedef struct Manager Manager; typedef struct StreamSyncReq StreamSyncReq; typedef struct SyncReq SyncReq; diff --git a/src/journal/journald-manager.c b/src/journal/journald-manager.c index 8b10ce97106..ea8ca436530 100644 --- a/src/journal/journald-manager.c +++ b/src/journal/journald-manager.c @@ -38,6 +38,7 @@ #include "journal-internal.h" #include "journal-vacuum.h" #include "journald-audit.h" +#include "journald-config.h" #include "journald-context.h" #include "journald-kmsg.h" #include "journald-manager.h" @@ -55,7 +56,6 @@ #include "parse-util.h" #include "path-util.h" #include "prioq.h" -#include "proc-cmdline.h" #include "process-util.h" #include "rm-rf.h" #include "set.h" @@ -63,7 +63,6 @@ #include "socket-netlink.h" #include "socket-util.h" #include "stdio-util.h" -#include "string-table.h" #include "string-util.h" #include "strv.h" #include "syslog-util.h" @@ -73,11 +72,6 @@ #define USER_JOURNALS_MAX 1024 -#define DEFAULT_SYNC_INTERVAL_USEC (5*USEC_PER_MINUTE) -#define DEFAULT_RATE_LIMIT_INTERVAL (30*USEC_PER_SEC) -#define DEFAULT_RATE_LIMIT_BURST 10000 -#define DEFAULT_MAX_FILE_USEC USEC_PER_MONTH - #define DEFAULT_KMSG_OWN_INTERVAL (5 * USEC_PER_SEC) #define DEFAULT_KMSG_OWN_BURST 50 @@ -88,10 +82,6 @@ /* The period to insert between posting changes for coalescing */ #define POST_CHANGE_TIMER_INTERVAL_USEC (250*USEC_PER_MSEC) -/* Pick a good default that is likely to fit into AF_UNIX and AF_INET SOCK_DGRAM datagrams, and even leaves some room - * for a bit of additional metadata. */ -#define DEFAULT_LINE_MAX (48*1024) - #define DEFERRED_CLOSES_MAX (4096) #define IDLE_TIMEOUT_USEC (30*USEC_PER_SEC) @@ -100,7 +90,6 @@ static int manager_schedule_sync(Manager *m, int priority); static int manager_refresh_idle_timer(Manager *m); -static int dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata); static int manager_determine_path_usage( Manager *m, @@ -1863,140 +1852,13 @@ static int manager_setup_signals(Manager *m) { if (r < 0) return r; - r = sd_event_add_signal(m->event, NULL, SIGHUP|SD_EVENT_SIGNAL_PROCMASK, dispatch_reload_signal, m); + r = sd_event_add_signal(m->event, NULL, SIGHUP|SD_EVENT_SIGNAL_PROCMASK, manager_dispatch_reload_signal, m); if (r < 0) return r; return 0; } -static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { - Manager *m = ASSERT_PTR(data); - int r; - - if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_syslog")) { - - r = value ? parse_boolean(value) : true; - if (r < 0) - log_warning("Failed to parse forward to syslog switch \"%s\". Ignoring.", value); - else - m->config_by_cmdline.forward_to_syslog = r; - - } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_kmsg")) { - - r = value ? parse_boolean(value) : true; - if (r < 0) - log_warning("Failed to parse forward to kmsg switch \"%s\". Ignoring.", value); - else - m->config_by_cmdline.forward_to_kmsg = r; - - } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_console")) { - - r = value ? parse_boolean(value) : true; - if (r < 0) - log_warning("Failed to parse forward to console switch \"%s\". Ignoring.", value); - else - m->config_by_cmdline.forward_to_console = r; - - } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_wall")) { - - r = value ? parse_boolean(value) : true; - if (r < 0) - log_warning("Failed to parse forward to wall switch \"%s\". Ignoring.", value); - else - m->config_by_cmdline.forward_to_wall = r; - - } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_console")) { - - if (proc_cmdline_value_missing(key, value)) - return 0; - - r = log_level_from_string(value); - if (r < 0) - log_warning("Failed to parse max level console value \"%s\". Ignoring.", value); - else - m->config_by_cmdline.max_level_console = r; - - } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_store")) { - - if (proc_cmdline_value_missing(key, value)) - return 0; - - r = log_level_from_string(value); - if (r < 0) - log_warning("Failed to parse max level store value \"%s\". Ignoring.", value); - else - m->config_by_cmdline.max_level_store = r; - - } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_syslog")) { - - if (proc_cmdline_value_missing(key, value)) - return 0; - - r = log_level_from_string(value); - if (r < 0) - log_warning("Failed to parse max level syslog value \"%s\". Ignoring.", value); - else - m->config_by_cmdline.max_level_syslog = r; - - } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_kmsg")) { - - if (proc_cmdline_value_missing(key, value)) - return 0; - - r = log_level_from_string(value); - if (r < 0) - log_warning("Failed to parse max level kmsg value \"%s\". Ignoring.", value); - else - m->config_by_cmdline.max_level_kmsg = r; - - } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_wall")) { - - if (proc_cmdline_value_missing(key, value)) - return 0; - - r = log_level_from_string(value); - if (r < 0) - log_warning("Failed to parse max level wall value \"%s\". Ignoring.", value); - else - m->config_by_cmdline.max_level_wall = r; - - } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_socket")) { - - if (proc_cmdline_value_missing(key, value)) - return 0; - - r = log_level_from_string(value); - if (r < 0) - log_warning("Failed to parse max level socket value \"%s\". Ignoring.", value); - else - m->config_by_cmdline.max_level_socket = r; - - } else if (startswith(key, "systemd.journald")) - log_warning("Unknown journald kernel command line option \"%s\". Ignoring.", key); - - /* do not warn about state here, since probably systemd already did */ - return 0; -} - -static int manager_parse_config_file(Manager *m) { - const char *conf_file; - - assert(m); - - if (m->namespace) - conf_file = strjoina("systemd/journald@", m->namespace, ".conf"); - else - conf_file = "systemd/journald.conf"; - - return config_parse_standard_file_with_dropins( - conf_file, - "Journal\0", - config_item_perf_lookup, journald_gperf_lookup, - CONFIG_PARSE_WARN, - /* userdata= */ m); -} - static int manager_dispatch_sync(sd_event_source *es, usec_t t, void *userdata) { Manager *m = ASSERT_PTR(userdata); @@ -2392,187 +2254,7 @@ static int manager_setup_memory_pressure(Manager *m) { return 0; } -static void manager_load_credentials(Manager *m) { - _cleanup_free_ void *data = NULL; - int r; - - assert(m); - - r = read_credential("journal.forward_to_socket", &data, NULL); - if (r < 0) - log_debug_errno(r, "Failed to read credential journal.forward_to_socket, ignoring: %m"); - else { - r = socket_address_parse(&m->config_by_cred.forward_to_socket, data); - if (r < 0) - log_debug_errno(r, "Failed to parse socket address '%s' from credential journal.forward_to_socket, ignoring: %m", (char *) data); - } - - data = mfree(data); - - r = read_credential("journal.storage", &data, NULL); - if (r < 0) - log_debug_errno(r, "Failed to read credential journal.storage, ignoring: %m"); - else { - r = storage_from_string(data); - if (r < 0) - log_debug_errno(r, "Failed to parse storage '%s' from credential journal.storage, ignoring: %m", (char *) data); - else - m->config_by_cred.storage = r; - } -} - -static void manager_set_defaults(Manager *m) { - assert(m); - - m->compress.enabled = true; - m->compress.threshold_bytes = UINT64_MAX; - - m->seal = true; - - /* By default, only read from /dev/kmsg if are the main namespace */ - m->read_kmsg = !m->namespace; - - m->set_audit = true; - - m->sync_interval_usec = DEFAULT_SYNC_INTERVAL_USEC; - - m->ratelimit_interval = DEFAULT_RATE_LIMIT_INTERVAL; - m->ratelimit_burst = DEFAULT_RATE_LIMIT_BURST; - - m->system_storage.name = "System Journal"; - journal_reset_metrics(&m->system_storage.metrics); - - m->runtime_storage.name = "Runtime Journal"; - journal_reset_metrics(&m->runtime_storage.metrics); - - m->max_file_usec = DEFAULT_MAX_FILE_USEC; - - m->config.forward_to_wall = true; - - m->config.max_level_store = LOG_DEBUG; - m->config.max_level_syslog = LOG_DEBUG; - m->config.max_level_kmsg = LOG_NOTICE; - m->config.max_level_console = LOG_INFO; - m->config.max_level_wall = LOG_EMERG; - m->config.max_level_socket = LOG_DEBUG; - - m->line_max = DEFAULT_LINE_MAX; -} - -static void manager_reset_configs(Manager *m) { - assert(m); - - m->config_by_cmdline = JOURNAL_CONFIG_INIT; - m->config_by_conf = JOURNAL_CONFIG_INIT; - m->config_by_cred = JOURNAL_CONFIG_INIT; -} - -static void manager_adjust_configs(Manager *m) { - assert(m); - - if (!!m->ratelimit_interval != !!m->ratelimit_burst) { /* One set to 0 and the other not? */ - log_debug( - "Setting both rate limit interval and burst from %s/%u to 0/0", - FORMAT_TIMESPAN(m->ratelimit_interval, USEC_PER_SEC), - m->ratelimit_burst); - m->ratelimit_interval = m->ratelimit_burst = 0; - } -} - -static void manager_merge_forward_to_socket(Manager *m) { - assert(m); - - /* Conf file takes precedence over credentials. */ - if (m->config_by_conf.forward_to_socket.sockaddr.sa.sa_family != AF_UNSPEC) - m->config.forward_to_socket = m->config_by_conf.forward_to_socket; - else if (m->config_by_cred.forward_to_socket.sockaddr.sa.sa_family != AF_UNSPEC) - m->config.forward_to_socket = m->config_by_cred.forward_to_socket; - else - m->config.forward_to_socket = (SocketAddress) { .sockaddr.sa.sa_family = AF_UNSPEC }; -} - -static void manager_merge_storage(Manager *m) { - assert(m); - - /* Conf file takes precedence over credentials. */ - if (m->config_by_conf.storage != _STORAGE_INVALID) - m->config.storage = m->config_by_conf.storage; - else if (m->config_by_cred.storage != _STORAGE_INVALID) - m->config.storage = m->config_by_cred.storage; - else - m->config.storage = m->namespace ? STORAGE_PERSISTENT : STORAGE_AUTO; -} - -#define MERGE_BOOL(name, default_value) \ - (m->config.name = (m->config_by_cmdline.name ? m->config_by_cmdline.name : \ - m->config_by_conf.name ? m->config_by_conf.name : \ - m->config_by_cred.name ? m->config_by_cred.name : \ - default_value)) - -#define MERGE_NON_NEGATIVE(name, default_value) \ - (m->config.name = (m->config_by_cmdline.name >= 0 ? m->config_by_cmdline.name : \ - m->config_by_conf.name >= 0 ? m->config_by_conf.name : \ - m->config_by_cred.name >= 0 ? m->config_by_cred.name : \ - default_value)) - -static void manager_merge_configs(Manager *m) { - assert(m); - - /* - * From highest to lowest priority: cmdline, conf, cred - */ - manager_merge_storage(m); - manager_merge_forward_to_socket(m); - - MERGE_BOOL(forward_to_syslog, false); - MERGE_BOOL(forward_to_kmsg, false); - MERGE_BOOL(forward_to_console, false); - MERGE_BOOL(forward_to_wall, true); - - MERGE_NON_NEGATIVE(max_level_store, LOG_DEBUG); - MERGE_NON_NEGATIVE(max_level_syslog, LOG_DEBUG); - MERGE_NON_NEGATIVE(max_level_kmsg, LOG_NOTICE); - MERGE_NON_NEGATIVE(max_level_console, LOG_INFO); - MERGE_NON_NEGATIVE(max_level_wall, LOG_EMERG); - MERGE_NON_NEGATIVE(max_level_socket, LOG_DEBUG); -} - -static void manager_load_config(Manager *m) { - assert(m); - - int r; - - manager_set_defaults(m); - manager_reset_configs(m); - - manager_load_credentials(m); - manager_parse_config_file(m); - - if (!m->namespace) { - /* Parse kernel command line, but only if we are not a namespace instance */ - r = proc_cmdline_parse(parse_proc_cmdline_item, m, PROC_CMDLINE_STRIP_RD_PREFIX); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - } - - manager_merge_configs(m); - - manager_adjust_configs(m); -} - -static void manager_reload_config(Manager *m) { - assert(m); - - manager_set_defaults(m); - - m->config_by_conf = JOURNAL_CONFIG_INIT; - manager_parse_config_file(m); - - manager_merge_configs(m); - manager_adjust_configs(m); -} - -static int manager_reload_journals(Manager *m) { +int manager_reload_journals(Manager *m) { assert(m); int r; @@ -2611,28 +2293,6 @@ static int manager_reload_journals(Manager *m) { return 0; } -static int dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - Manager *m = ASSERT_PTR(userdata); - int r; - - (void) notify_reloading(); - - manager_reload_config(m); - - r = manager_reload_dev_kmsg(m); - if (r < 0) - return r; - - r = manager_reload_journals(m); - if (r < 0) - return r; - - log_info("Config file reloaded."); - (void) sd_notify(/* unset_environment */ false, NOTIFY_READY_MESSAGE); - - return 0; -} - int manager_new(Manager **ret, const char *namespace) { _cleanup_(manager_freep) Manager *m = NULL; int r; @@ -2967,148 +2627,3 @@ Manager* manager_free(Manager *m) { return mfree(m); } - -static const char* const storage_table[_STORAGE_MAX] = { - [STORAGE_AUTO] = "auto", - [STORAGE_VOLATILE] = "volatile", - [STORAGE_PERSISTENT] = "persistent", - [STORAGE_NONE] = "none" -}; - -DEFINE_STRING_TABLE_LOOKUP(storage, Storage); -DEFINE_CONFIG_PARSE_ENUM(config_parse_storage, storage, Storage); - -static const char* const split_mode_table[_SPLIT_MAX] = { - [SPLIT_LOGIN] = "login", - [SPLIT_UID] = "uid", - [SPLIT_NONE] = "none", -}; - -DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode); -DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode); - -int config_parse_line_max( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - size_t *sz = ASSERT_PTR(data); - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - if (isempty(rvalue)) - /* Empty assignment means default */ - *sz = DEFAULT_LINE_MAX; - else { - uint64_t v; - - r = parse_size(rvalue, 1024, &v); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse LineMax= value, ignoring: %s", rvalue); - return 0; - } - - if (v < 79) { - /* Why specify 79 here as minimum line length? Simply, because the most common traditional - * terminal size is 80ch, and it might make sense to break one character before the natural - * line break would occur on that. */ - log_syntax(unit, LOG_WARNING, filename, line, 0, "LineMax= too small, clamping to 79: %s", rvalue); - *sz = 79; - } else if (v > (uint64_t) (SSIZE_MAX-1)) { - /* So, why specify SSIZE_MAX-1 here? Because that's one below the largest size value read() - * can return, and we need one extra byte for the trailing NUL byte. Of course IRL such large - * memory allocations will fail anyway, hence this limit is mostly theoretical anyway, as we'll - * fail much earlier anyway. */ - log_syntax(unit, LOG_WARNING, filename, line, 0, "LineMax= too large, clamping to %" PRIu64 ": %s", (uint64_t) (SSIZE_MAX-1), rvalue); - *sz = SSIZE_MAX-1; - } else - *sz = (size_t) v; - } - - return 0; -} - -int config_parse_compress( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - JournalCompressOptions* compress = ASSERT_PTR(data); - int r; - - assert(filename); - assert(rvalue); - - if (isempty(rvalue)) { - compress->enabled = true; - compress->threshold_bytes = UINT64_MAX; - } else if (streq(rvalue, "1")) { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "Compress= ambiguously specified as 1, enabling compression with default threshold"); - compress->enabled = true; - } else if (streq(rvalue, "0")) { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "Compress= ambiguously specified as 0, disabling compression"); - compress->enabled = false; - } else { - r = parse_boolean(rvalue); - if (r < 0) { - r = parse_size(rvalue, 1024, &compress->threshold_bytes); - if (r < 0) - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse Compress= value, ignoring: %s", rvalue); - else - compress->enabled = true; - } else - compress->enabled = r; - } - - return 0; -} - -int config_parse_forward_to_socket( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - SocketAddress* addr = ASSERT_PTR(data); - int r; - - assert(filename); - assert(rvalue); - - if (isempty(rvalue)) - *addr = (SocketAddress) { .sockaddr.sa.sa_family = AF_UNSPEC }; - else { - r = socket_address_parse(addr, rvalue); - if (r < 0) - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse ForwardToSocket= value, ignoring: %s", rvalue); - } - - return 0; -} diff --git a/src/journal/journald-manager.h b/src/journal/journald-manager.h index 4bcb75aa8fd..ac538a12e75 100644 --- a/src/journal/journald-manager.h +++ b/src/journal/journald-manager.h @@ -3,33 +3,12 @@ #include "common-signal.h" #include "journal-file.h" +#include "journald-config.h" #include "journald-forward.h" #include "list.h" #include "ratelimit.h" #include "socket-util.h" -typedef enum Storage { - STORAGE_AUTO, - STORAGE_VOLATILE, - STORAGE_PERSISTENT, - STORAGE_NONE, - _STORAGE_MAX, - _STORAGE_INVALID = -EINVAL, -} Storage; - -typedef enum SplitMode { - SPLIT_UID, - SPLIT_LOGIN, /* deprecated */ - SPLIT_NONE, - _SPLIT_MAX, - _SPLIT_INVALID = -EINVAL, -} SplitMode; - -typedef struct JournalCompressOptions { - bool enabled; - uint64_t threshold_bytes; -} JournalCompressOptions; - typedef struct JournalStorageSpace { usec_t timestamp; @@ -55,23 +34,6 @@ typedef struct SeqnumData { uint64_t seqnum; } SeqnumData; -typedef struct JournalConfig { - SocketAddress forward_to_socket; - Storage storage; - - bool forward_to_kmsg; - bool forward_to_syslog; - bool forward_to_console; - bool forward_to_wall; - - int max_level_store; - int max_level_syslog; - int max_level_kmsg; - int max_level_console; - int max_level_wall; - int max_level_socket; -} JournalConfig; - typedef struct Manager { char *namespace; @@ -188,15 +150,14 @@ typedef struct Manager { LIST_HEAD(SyncReq, sync_req_pending_rqlen); /* These structs are used to preserve configurations set by credentials and command line. - config - main configuration used by journald manager, - config_by_cred - configuration set by credentials, - config_by_conf - configuration set by configuration file, - config_by_cmdline - configuration set by command line. - The priority order of the sub-configurations is: - config_by_cmdline > config_by_conf > config_by_cred - where A > B means that if the two have the same setting, - A's value overrides B's value for that setting. - */ + * - config - main configuration used by journald manager, + * - config_by_cred - configuration set by credentials, + * - config_by_conf - configuration set by configuration file, + * - config_by_cmdline - configuration set by command line. + * The priority order of the sub-configurations is: + * config_by_cmdline > config_by_conf > config_by_cred + * where A > B means that if the two have the same setting, A's value overrides B's value for that + * setting. */ JournalConfig config; JournalConfig config_by_cred; JournalConfig config_by_conf; @@ -227,34 +188,6 @@ void manager_dispatch_message(Manager *m, struct iovec *iovec, size_t n, size_t void manager_driver_message_internal(Manager *m, pid_t object_pid, const char *format, ...) _sentinel_; #define manager_driver_message(...) manager_driver_message_internal(__VA_ARGS__, NULL) -#define JOURNAL_CONFIG_INIT \ - (JournalConfig) { \ - .forward_to_socket = (SocketAddress) { .sockaddr.sa.sa_family = AF_UNSPEC }, \ - .storage = _STORAGE_INVALID, \ - .max_level_store = -1, \ - .max_level_syslog = -1, \ - .max_level_kmsg = -1, \ - .max_level_console = -1, \ - .max_level_wall = -1, \ - .max_level_socket = -1, \ - } - -/* gperf lookup function */ -const struct ConfigPerfItem* journald_gperf_lookup(const char *key, GPERF_LEN_TYPE length); - -CONFIG_PARSER_PROTOTYPE(config_parse_storage); -CONFIG_PARSER_PROTOTYPE(config_parse_line_max); -CONFIG_PARSER_PROTOTYPE(config_parse_compress); -CONFIG_PARSER_PROTOTYPE(config_parse_forward_to_socket); - -const char* storage_to_string(Storage s) _const_; -Storage storage_from_string(const char *s) _pure_; - -CONFIG_PARSER_PROTOTYPE(config_parse_split_mode); - -const char* split_mode_to_string(SplitMode s) _const_; -SplitMode split_mode_from_string(const char *s) _pure_; - int manager_new(Manager **ret, const char *namespace); int manager_init(Manager *m); Manager* manager_free(Manager *m); @@ -271,5 +204,6 @@ int manager_process_datagram(sd_event_source *es, int fd, uint32_t revents, void void manager_space_usage_message(Manager *m, JournalStorage *storage); int manager_start_or_stop_idle_timer(Manager *m); +int manager_reload_journals(Manager *m); int manager_map_seqnum_file(Manager *m, const char *fname, size_t size, void **ret); diff --git a/src/journal/meson.build b/src/journal/meson.build index e5da683b380..5f34cd6866c 100644 --- a/src/journal/meson.build +++ b/src/journal/meson.build @@ -6,6 +6,7 @@ systemd_journald_sources = files( systemd_journald_extract_sources = files( 'journald-audit.c', 'journald-client.c', + 'journald-config.c', 'journald-console.c', 'journald-context.c', 'journald-kmsg.c', From a322e43ba93aedb85cd68e53498c64c2691ede64 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jul 2025 16:21:26 +0900 Subject: [PATCH 02/24] journald-manager: make manager_get_file_flags() return JournalFileFlags This also adds a missing assertion. --- src/journal/journald-manager.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/journal/journald-manager.c b/src/journal/journald-manager.c index ea8ca436530..f6700fcc681 100644 --- a/src/journal/journald-manager.c +++ b/src/journal/journald-manager.c @@ -259,10 +259,12 @@ static void manager_add_acls(JournalFile *f, uid_t uid) { #endif } -static int manager_get_file_flags(Manager *m, bool seal) { +static JournalFileFlags manager_get_file_flags(Manager *m, bool seal) { + assert(m); + return (m->compress.enabled ? JOURNAL_COMPRESS : 0) | - (seal ? JOURNAL_SEAL : 0) | - JOURNAL_STRICT_ORDER; + (seal ? JOURNAL_SEAL : 0) | + JOURNAL_STRICT_ORDER; } static int manager_open_journal( From 31b25d42900731adbf2aa677eed779585262491a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jul 2025 16:17:12 +0900 Subject: [PATCH 03/24] journald-manager: use manager_get_file_flags() more This also adds one missing assertion and drop spurious empty line. No functional change, just refactoring. --- src/journal/journald-manager.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/journal/journald-manager.c b/src/journal/journald-manager.c index f6700fcc681..3be6296cea3 100644 --- a/src/journal/journald-manager.c +++ b/src/journal/journald-manager.c @@ -149,6 +149,7 @@ static int cache_space_refresh(Manager *m, JournalStorage *storage) { int r; assert(m); + assert(storage); metrics = &storage->metrics; space = &storage->space; @@ -550,7 +551,6 @@ static int manager_do_rotate( bool seal, uint32_t uid) { - JournalFileFlags file_flags; int r; assert(m); @@ -558,12 +558,7 @@ static int manager_do_rotate( if (!*f) return -EINVAL; - file_flags = - (m->compress.enabled ? JOURNAL_COMPRESS : 0)| - (seal ? JOURNAL_SEAL : 0) | - JOURNAL_STRICT_ORDER; - - r = journal_file_rotate(f, m->mmap, file_flags, m->compress.threshold_bytes, m->deferred_closes); + r = journal_file_rotate(f, m->mmap, manager_get_file_flags(m, seal), m->compress.threshold_bytes, m->deferred_closes); if (r < 0) { if (*f) return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, @@ -673,8 +668,7 @@ static int manager_archive_offline_user_journals(Manager *m) { fd, full, O_RDWR, - (m->compress.enabled ? JOURNAL_COMPRESS : 0) | - (m->seal ? JOURNAL_SEAL : 0), /* strict order does not matter here */ + manager_get_file_flags(m, m->seal) & ~JOURNAL_STRICT_ORDER, /* strict order does not matter here */ 0640, m->compress.threshold_bytes, &m->system_storage.metrics, @@ -793,7 +787,6 @@ static void manager_sync(Manager *m, bool wait) { } static void manager_do_vacuum(Manager *m, JournalStorage *storage, bool verbose) { - int r; assert(m); From 3941032c6c5cd1e573904caf2d9557610ffc4f50 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jul 2025 15:21:50 +0900 Subject: [PATCH 04/24] journald-audit: do not control kernel auditing by non-default namespace instances by default The kernel (thus system-wide) auditing should not be controlled by non-default namespace instances, unless explicitly requested. --- man/journald.conf.xml | 2 +- src/journal/journald-config.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/man/journald.conf.xml b/man/journald.conf.xml index 30132bcffc5..1a68ba86985 100644 --- a/man/journald.conf.xml +++ b/man/journald.conf.xml @@ -482,7 +482,7 @@ kernel auditing on start-up. If disabled it will turn it off. If unset it will neither enable nor disable it, leaving the previous state unchanged. This means if another tool turns on auditing even if systemd-journald left it off, it will still collect the generated - messages. Defaults to on. + messages. Defaults to on in the default journal namespace, and unset otherwise. Note that this option does not control whether systemd-journald collects generated audit records, it just controls whether it tells the kernel to generate them. If you need diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c index 58d86b507b9..c266833b035 100644 --- a/src/journal/journald-config.c +++ b/src/journal/journald-config.c @@ -49,7 +49,9 @@ static void manager_set_defaults(Manager *m) { /* By default, only read from /dev/kmsg if are the main namespace */ m->read_kmsg = !m->namespace; - m->set_audit = true; + /* By default, kernel auditing is enabled by the main namespace instance, and not controlled by + * non-default namespace instances. */ + m->set_audit = m->namespace ? -1 : true; m->sync_interval_usec = DEFAULT_SYNC_INTERVAL_USEC; From 5b585edd2f6fa2df323a6770fcf97d45a052d4ec Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jul 2025 14:16:55 +0900 Subject: [PATCH 05/24] journald-config: use char* for credential data This also slightly update log messages, and make manager_load_credentials() take JournalConfig. No functional change, just refactoring. --- src/journal/journald-config.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c index c266833b035..e1811586d9f 100644 --- a/src/journal/journald-config.c +++ b/src/journal/journald-config.c @@ -283,32 +283,32 @@ static void manager_parse_config_file(Manager *m) { m); } -static void manager_load_credentials(Manager *m) { - _cleanup_free_ void *data = NULL; +static void manager_load_credentials(JournalConfig *c) { + _cleanup_free_ char *data = NULL; int r; - assert(m); + assert(c); - r = read_credential("journal.forward_to_socket", &data, NULL); + r = read_credential("journal.forward_to_socket", (void**) &data, /* ret_size = */ NULL); if (r < 0) - log_debug_errno(r, "Failed to read credential journal.forward_to_socket, ignoring: %m"); + log_debug_errno(r, "Failed to read credential 'journal.forward_to_socket', ignoring: %m"); else { - r = socket_address_parse(&m->config_by_cred.forward_to_socket, data); + r = socket_address_parse(&c->forward_to_socket, data); if (r < 0) - log_debug_errno(r, "Failed to parse socket address '%s' from credential journal.forward_to_socket, ignoring: %m", (char *) data); + log_debug_errno(r, "Failed to parse journal.forward_to_socket credential, ignoring: %s", data); } data = mfree(data); - r = read_credential("journal.storage", &data, NULL); + r = read_credential("journal.storage", (void**) &data, /* ret_size = */ NULL); if (r < 0) log_debug_errno(r, "Failed to read credential journal.storage, ignoring: %m"); else { r = storage_from_string(data); if (r < 0) - log_debug_errno(r, "Failed to parse storage '%s' from credential journal.storage, ignoring: %m", (char *) data); + log_debug_errno(r, "Failed to parse journal.storage credential, ignoring: %s", data); else - m->config_by_cred.storage = r; + c->storage = r; } } @@ -320,7 +320,7 @@ void manager_load_config(Manager *m) { manager_set_defaults(m); manager_reset_configs(m); - manager_load_credentials(m); + manager_load_credentials(&m->config_by_cred); manager_parse_config_file(m); if (!m->namespace) { From d13a471c45c1eb7f04942485d41f0c2c40734c6b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jul 2025 14:37:57 +0900 Subject: [PATCH 06/24] journald-config: make kernel command line parser take JournalConfig --- src/journal/journald-config.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c index e1811586d9f..667111a21a2 100644 --- a/src/journal/journald-config.c +++ b/src/journal/journald-config.c @@ -156,7 +156,7 @@ static void manager_adjust_configs(Manager *m) { } static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { - Manager *m = ASSERT_PTR(data); + JournalConfig *c = ASSERT_PTR(data); int r; if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_syslog")) { @@ -165,7 +165,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (r < 0) log_warning("Failed to parse forward to syslog switch \"%s\". Ignoring.", value); else - m->config_by_cmdline.forward_to_syslog = r; + c->forward_to_syslog = r; } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_kmsg")) { @@ -173,7 +173,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (r < 0) log_warning("Failed to parse forward to kmsg switch \"%s\". Ignoring.", value); else - m->config_by_cmdline.forward_to_kmsg = r; + c->forward_to_kmsg = r; } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_console")) { @@ -181,7 +181,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (r < 0) log_warning("Failed to parse forward to console switch \"%s\". Ignoring.", value); else - m->config_by_cmdline.forward_to_console = r; + c->forward_to_console = r; } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_wall")) { @@ -189,7 +189,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (r < 0) log_warning("Failed to parse forward to wall switch \"%s\". Ignoring.", value); else - m->config_by_cmdline.forward_to_wall = r; + c->forward_to_wall = r; } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_console")) { @@ -200,7 +200,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (r < 0) log_warning("Failed to parse max level console value \"%s\". Ignoring.", value); else - m->config_by_cmdline.max_level_console = r; + c->max_level_console = r; } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_store")) { @@ -211,7 +211,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (r < 0) log_warning("Failed to parse max level store value \"%s\". Ignoring.", value); else - m->config_by_cmdline.max_level_store = r; + c->max_level_store = r; } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_syslog")) { @@ -222,7 +222,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (r < 0) log_warning("Failed to parse max level syslog value \"%s\". Ignoring.", value); else - m->config_by_cmdline.max_level_syslog = r; + c->max_level_syslog = r; } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_kmsg")) { @@ -233,7 +233,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (r < 0) log_warning("Failed to parse max level kmsg value \"%s\". Ignoring.", value); else - m->config_by_cmdline.max_level_kmsg = r; + c->max_level_kmsg = r; } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_wall")) { @@ -244,7 +244,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (r < 0) log_warning("Failed to parse max level wall value \"%s\". Ignoring.", value); else - m->config_by_cmdline.max_level_wall = r; + c->max_level_wall = r; } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_socket")) { @@ -255,7 +255,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (r < 0) log_warning("Failed to parse max level socket value \"%s\". Ignoring.", value); else - m->config_by_cmdline.max_level_socket = r; + c->max_level_socket = r; } else if (startswith(key, "systemd.journald")) log_warning("Unknown journald kernel command line option \"%s\". Ignoring.", key); @@ -325,7 +325,7 @@ void manager_load_config(Manager *m) { if (!m->namespace) { /* Parse kernel command line, but only if we are not a namespace instance */ - r = proc_cmdline_parse(parse_proc_cmdline_item, m, PROC_CMDLINE_STRIP_RD_PREFIX); + r = proc_cmdline_parse(parse_proc_cmdline_item, &m->config_by_cmdline, PROC_CMDLINE_STRIP_RD_PREFIX); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); } From 208c9cf988e7f4f3ceb95c448a1609aaf9f39c9c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jul 2025 12:41:43 +0900 Subject: [PATCH 07/24] journald-config: always clear threshold_bytes even when boolean value is specified Otherwise, previously specified threshold may not be cleared. --- src/journal/journald-config.c | 53 +++++++++++++++++------------- src/journal/test-journald-config.c | 24 +++++++------- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c index 667111a21a2..ff3bad5a952 100644 --- a/src/journal/journald-config.c +++ b/src/journal/journald-config.c @@ -451,36 +451,43 @@ int config_parse_compress( void *data, void *userdata) { - JournalCompressOptions* compress = ASSERT_PTR(data); + JournalCompressOptions *compress = ASSERT_PTR(data); int r; - assert(filename); - assert(rvalue); - if (isempty(rvalue)) { compress->enabled = true; compress->threshold_bytes = UINT64_MAX; - } else if (streq(rvalue, "1")) { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "Compress= ambiguously specified as 1, enabling compression with default threshold"); - compress->enabled = true; - } else if (streq(rvalue, "0")) { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "Compress= ambiguously specified as 0, disabling compression"); - compress->enabled = false; - } else { - r = parse_boolean(rvalue); - if (r < 0) { - r = parse_size(rvalue, 1024, &compress->threshold_bytes); - if (r < 0) - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse Compress= value, ignoring: %s", rvalue); - else - compress->enabled = true; - } else - compress->enabled = r; + return 0; } + if (streq(rvalue, "1")) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Compress= ambiguously specified as 1, enabling compression with default threshold."); + compress->enabled = true; + compress->threshold_bytes = UINT64_MAX; + return 0; + } + + if (streq(rvalue, "0")) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Compress= ambiguously specified as 0, disabling compression."); + compress->enabled = false; + compress->threshold_bytes = UINT64_MAX; + return 0; + } + + r = parse_boolean(rvalue); + if (r >= 0) { + compress->enabled = r; + compress->threshold_bytes = UINT64_MAX; + return 0; + } + + r = parse_size(rvalue, 1024, &compress->threshold_bytes); + if (r < 0) + return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); + + compress->enabled = true; return 0; } diff --git a/src/journal/test-journald-config.c b/src/journal/test-journald-config.c index 89e748182eb..5eb089a8cb1 100644 --- a/src/journal/test-journald-config.c +++ b/src/journal/test-journald-config.c @@ -25,21 +25,21 @@ _COMPRESS_PARSE_CHECK(str, enabled, threshold, conf##__COUNTER__) TEST(config_compress) { - COMPRESS_PARSE_CHECK("yes", true, 111); - COMPRESS_PARSE_CHECK("no", false, 111); - COMPRESS_PARSE_CHECK("y", true, 111); - COMPRESS_PARSE_CHECK("n", false, 111); - COMPRESS_PARSE_CHECK("true", true, 111); - COMPRESS_PARSE_CHECK("false", false, 111); - COMPRESS_PARSE_CHECK("t", true, 111); - COMPRESS_PARSE_CHECK("f", false, 111); - COMPRESS_PARSE_CHECK("on", true, 111); - COMPRESS_PARSE_CHECK("off", false, 111); + COMPRESS_PARSE_CHECK("yes", true, UINT64_MAX); + COMPRESS_PARSE_CHECK("no", false, UINT64_MAX); + COMPRESS_PARSE_CHECK("y", true, UINT64_MAX); + COMPRESS_PARSE_CHECK("n", false, UINT64_MAX); + COMPRESS_PARSE_CHECK("true", true, UINT64_MAX); + COMPRESS_PARSE_CHECK("false", false, UINT64_MAX); + COMPRESS_PARSE_CHECK("t", true, UINT64_MAX); + COMPRESS_PARSE_CHECK("f", false, UINT64_MAX); + COMPRESS_PARSE_CHECK("on", true, UINT64_MAX); + COMPRESS_PARSE_CHECK("off", false, UINT64_MAX); /* Weird size/bool overlapping case. We preserve backward compatibility instead of assuming these are byte * counts. */ - COMPRESS_PARSE_CHECK("1", true, 111); - COMPRESS_PARSE_CHECK("0", false, 111); + COMPRESS_PARSE_CHECK("1", true, UINT64_MAX); + COMPRESS_PARSE_CHECK("0", false, UINT64_MAX); /* IEC sizing */ COMPRESS_PARSE_CHECK("1B", true, 1); From 9f819a77911a9adc004276a22597df0584769b91 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jul 2025 15:28:52 +0900 Subject: [PATCH 08/24] journald-config: modernize config_parse_line_max() and config_parse_forward_to_socket() No functional change, just refactoring. --- src/journal/journald-config.c | 71 +++++++++++++++-------------------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c index ff3bad5a952..648ac55d7ec 100644 --- a/src/journal/journald-config.c +++ b/src/journal/journald-config.c @@ -403,39 +403,33 @@ int config_parse_line_max( size_t *sz = ASSERT_PTR(data); int r; - assert(filename); - assert(lvalue); - assert(rvalue); - - if (isempty(rvalue)) + if (isempty(rvalue)) { /* Empty assignment means default */ *sz = DEFAULT_LINE_MAX; - else { - uint64_t v; - - r = parse_size(rvalue, 1024, &v); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse LineMax= value, ignoring: %s", rvalue); - return 0; - } - - if (v < 79) { - /* Why specify 79 here as minimum line length? Simply, because the most common traditional - * terminal size is 80ch, and it might make sense to break one character before the natural - * line break would occur on that. */ - log_syntax(unit, LOG_WARNING, filename, line, 0, "LineMax= too small, clamping to 79: %s", rvalue); - *sz = 79; - } else if (v > (uint64_t) (SSIZE_MAX-1)) { - /* So, why specify SSIZE_MAX-1 here? Because that's one below the largest size value read() - * can return, and we need one extra byte for the trailing NUL byte. Of course IRL such large - * memory allocations will fail anyway, hence this limit is mostly theoretical anyway, as we'll - * fail much earlier anyway. */ - log_syntax(unit, LOG_WARNING, filename, line, 0, "LineMax= too large, clamping to %" PRIu64 ": %s", (uint64_t) (SSIZE_MAX-1), rvalue); - *sz = SSIZE_MAX-1; - } else - *sz = (size_t) v; + return 0; } + uint64_t v; + r = parse_size(rvalue, 1024, &v); + if (r < 0) + return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); + + if (v < 79) { + /* Why specify 79 here as minimum line length? Simply, because the most common traditional + * terminal size is 80ch, and it might make sense to break one character before the natural + * line break would occur on that. */ + log_syntax(unit, LOG_WARNING, filename, line, 0, "LineMax= too small, clamping to 79: %s", rvalue); + *sz = 79; + } else if (v > (uint64_t) (SSIZE_MAX-1)) { + /* So, why specify SSIZE_MAX-1 here? Because that's one below the largest size value read() + * can return, and we need one extra byte for the trailing NUL byte. Of course IRL such large + * memory allocations will fail anyway, hence this limit is mostly theoretical anyway, as we'll + * fail much earlier anyway. */ + log_syntax(unit, LOG_WARNING, filename, line, 0, "LineMax= too large, clamping to %" PRIu64 ": %s", (uint64_t) (SSIZE_MAX-1), rvalue); + *sz = SSIZE_MAX-1; + } else + *sz = (size_t) v; + return 0; } @@ -503,20 +497,17 @@ int config_parse_forward_to_socket( void *data, void *userdata) { - SocketAddress* addr = ASSERT_PTR(data); + SocketAddress *addr = ASSERT_PTR(data); int r; - assert(filename); - assert(rvalue); - - if (isempty(rvalue)) - *addr = (SocketAddress) { .sockaddr.sa.sa_family = AF_UNSPEC }; - else { - r = socket_address_parse(addr, rvalue); - if (r < 0) - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse ForwardToSocket= value, ignoring: %s", rvalue); + if (isempty(rvalue)) { + *addr = (SocketAddress) {}; + return 0; } + r = socket_address_parse(addr, rvalue); + if (r < 0) + return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); + return 0; } From f48cf2a96dfdc23fe30ba0f870125fe55cab64c7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jul 2025 15:29:22 +0900 Subject: [PATCH 09/24] journald: move all config entries from Manager to JournalConfig Previously, only config entries controlled by multiple sources were located in JournalConfig, and still other config entries were in Manager. That's hard to maintain. Let's move all config entries to JournalConfig. This also makes JournalConfig.forward_to_kmsg and friends tristate. Otherwise, even if a higher precedence config source disables the feature, it may be enabled by a lower precedence config. --- src/journal/journald-audit.c | 6 +- src/journal/journald-config.c | 208 +++++++++++++++-------------- src/journal/journald-config.h | 55 ++++++-- src/journal/journald-console.c | 2 +- src/journal/journald-context.c | 8 +- src/journal/journald-gperf.gperf | 68 +++++----- src/journal/journald-kmsg.c | 12 +- src/journal/journald-manager.c | 55 ++++---- src/journal/journald-manager.h | 16 --- src/journal/journald-stream.c | 4 +- src/journal/journald.c | 4 +- src/journal/test-journald-config.c | 8 +- 12 files changed, 243 insertions(+), 203 deletions(-) diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c index ebf401c4c2c..479a4d27919 100644 --- a/src/journal/journald-audit.c +++ b/src/journal/journald-audit.c @@ -544,12 +544,12 @@ int manager_open_audit(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add audit fd to event loop: %m"); - if (m->set_audit >= 0) { + if (m->config.set_audit >= 0) { /* We are listening now, try to enable audit if configured so */ - r = enable_audit(m->audit_fd, m->set_audit); + r = enable_audit(m->audit_fd, m->config.set_audit); if (r < 0) log_warning_errno(r, "Failed to issue audit enable call: %m"); - else if (m->set_audit > 0) + else if (m->config.set_audit > 0) log_debug("Auditing in kernel turned on."); else log_debug("Auditing in kernel turned off."); diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c index 648ac55d7ec..cece07411f4 100644 --- a/src/journal/journald-config.c +++ b/src/journal/journald-config.c @@ -26,115 +26,121 @@ * for a bit of additional metadata. */ #define DEFAULT_LINE_MAX (48*1024) -#define JOURNAL_CONFIG_INIT \ - (JournalConfig) { \ - .forward_to_socket = (SocketAddress) { .sockaddr.sa.sa_family = AF_UNSPEC }, \ - .storage = _STORAGE_INVALID, \ - .max_level_store = -1, \ - .max_level_syslog = -1, \ - .max_level_kmsg = -1, \ - .max_level_console = -1, \ - .max_level_wall = -1, \ - .max_level_socket = -1, \ - } +void journal_config_done(JournalConfig *c) { + assert(c); -static void manager_set_defaults(Manager *m) { - assert(m); - - m->compress.enabled = true; - m->compress.threshold_bytes = UINT64_MAX; - - m->seal = true; - - /* By default, only read from /dev/kmsg if are the main namespace */ - m->read_kmsg = !m->namespace; - - /* By default, kernel auditing is enabled by the main namespace instance, and not controlled by - * non-default namespace instances. */ - m->set_audit = m->namespace ? -1 : true; - - m->sync_interval_usec = DEFAULT_SYNC_INTERVAL_USEC; - - m->ratelimit_interval = DEFAULT_RATE_LIMIT_INTERVAL; - m->ratelimit_burst = DEFAULT_RATE_LIMIT_BURST; - - m->system_storage.name = "System Journal"; - journal_reset_metrics(&m->system_storage.metrics); - - m->runtime_storage.name = "Runtime Journal"; - journal_reset_metrics(&m->runtime_storage.metrics); - - m->max_file_usec = DEFAULT_MAX_FILE_USEC; - - m->config.forward_to_wall = true; - - m->config.max_level_store = LOG_DEBUG; - m->config.max_level_syslog = LOG_DEBUG; - m->config.max_level_kmsg = LOG_NOTICE; - m->config.max_level_console = LOG_INFO; - m->config.max_level_wall = LOG_EMERG; - m->config.max_level_socket = LOG_DEBUG; - - m->line_max = DEFAULT_LINE_MAX; + free(c->tty_path); } -static void manager_reset_configs(Manager *m) { +static void journal_config_set_defaults(JournalConfig *c) { + assert(c); + + journal_config_done(c); + + *c = (JournalConfig) { + .storage = _STORAGE_INVALID, + .compress.enabled = -1, + .compress.threshold_bytes = UINT64_MAX, + .seal = -1, + .read_kmsg = -1, + .set_audit = -1, + .ratelimit_interval = DEFAULT_RATE_LIMIT_INTERVAL, + .ratelimit_burst = DEFAULT_RATE_LIMIT_BURST, + .forward_to_syslog = -1, + .forward_to_kmsg = -1, + .forward_to_console = -1, + .forward_to_wall = -1, + .max_level_store = -1, + .max_level_syslog = -1, + .max_level_kmsg = -1, + .max_level_console = -1, + .max_level_wall = -1, + .max_level_socket = -1, + }; + + journal_reset_metrics(&c->system_storage_metrics); + journal_reset_metrics(&c->runtime_storage_metrics); +} + +static void manager_merge_journal_compress_options(Manager *m) { assert(m); - m->config_by_cmdline = JOURNAL_CONFIG_INIT; - m->config_by_conf = JOURNAL_CONFIG_INIT; - m->config_by_cred = JOURNAL_CONFIG_INIT; + if (m->config_by_cmdline.compress.enabled >= 0) + m->config.compress = m->config_by_cmdline.compress; + else if (m->config_by_conf.compress.enabled >= 0) + m->config.compress = m->config_by_conf.compress; + else if (m->config_by_cred.compress.enabled >= 0) + m->config.compress = m->config_by_cred.compress; + else + m->config.compress = (JournalCompressOptions) { + .enabled = true, + .threshold_bytes = UINT64_MAX, + }; } static void manager_merge_forward_to_socket(Manager *m) { assert(m); - /* Conf file takes precedence over credentials. */ - if (m->config_by_conf.forward_to_socket.sockaddr.sa.sa_family != AF_UNSPEC) + if (m->config_by_cmdline.forward_to_socket.sockaddr.sa.sa_family != AF_UNSPEC) + m->config.forward_to_socket = m->config_by_cmdline.forward_to_socket; + else if (m->config_by_conf.forward_to_socket.sockaddr.sa.sa_family != AF_UNSPEC) m->config.forward_to_socket = m->config_by_conf.forward_to_socket; else if (m->config_by_cred.forward_to_socket.sockaddr.sa.sa_family != AF_UNSPEC) m->config.forward_to_socket = m->config_by_cred.forward_to_socket; else - m->config.forward_to_socket = (SocketAddress) { .sockaddr.sa.sa_family = AF_UNSPEC }; + m->config.forward_to_socket = (SocketAddress) {}; } -static void manager_merge_storage(Manager *m) { - assert(m); +#define MERGE_NON_NEGATIVE(name, default_value) \ + m->config.name = \ + m->config_by_cmdline.name >= 0 ? m->config_by_cmdline.name : \ + m->config_by_conf.name >= 0 ? m->config_by_conf.name : \ + m->config_by_cred.name >= 0 ? m->config_by_cred.name : \ + default_value - /* Conf file takes precedence over credentials. */ - if (m->config_by_conf.storage != _STORAGE_INVALID) - m->config.storage = m->config_by_conf.storage; - else if (m->config_by_cred.storage != _STORAGE_INVALID) - m->config.storage = m->config_by_cred.storage; - else - m->config.storage = m->namespace ? STORAGE_PERSISTENT : STORAGE_AUTO; -} - -#define MERGE_BOOL(name, default_value) \ - (m->config.name = (m->config_by_cmdline.name ? m->config_by_cmdline.name : \ - m->config_by_conf.name ? m->config_by_conf.name : \ - m->config_by_cred.name ? m->config_by_cred.name : \ - default_value)) - -#define MERGE_NON_NEGATIVE(name, default_value) \ - (m->config.name = (m->config_by_cmdline.name >= 0 ? m->config_by_cmdline.name : \ - m->config_by_conf.name >= 0 ? m->config_by_conf.name : \ - m->config_by_cred.name >= 0 ? m->config_by_cred.name : \ - default_value)) +#define MERGE_NON_ZERO(name, default_value) \ + m->config.name = \ + m->config_by_cmdline.name ?: \ + m->config_by_conf.name ?: \ + m->config_by_cred.name ?: \ + default_value static void manager_merge_configs(Manager *m) { assert(m); - /* - * From highest to lowest priority: cmdline, conf, cred - */ - manager_merge_storage(m); + /* From highest to lowest priority: cmdline, conf, cred */ + + journal_config_done(&m->config); + + MERGE_NON_NEGATIVE(storage, STORAGE_AUTO); + manager_merge_journal_compress_options(m); + MERGE_NON_NEGATIVE(seal, true); + /* By default, /dev/kmsg is read only by the main namespace instance. */ + MERGE_NON_NEGATIVE(read_kmsg, !m->namespace); + /* By default, kernel auditing is enabled by the main namespace instance, and not controlled by + * non-default namespace instances. */ + MERGE_NON_NEGATIVE(set_audit, m->namespace ? -1 : true); + MERGE_NON_ZERO(sync_interval_usec, DEFAULT_SYNC_INTERVAL_USEC); + + /* TODO: also merge them when comdline or credentials support to configure them. */ + m->config.ratelimit_interval = m->config_by_conf.ratelimit_interval; + m->config.ratelimit_burst = m->config_by_conf.ratelimit_burst; + m->config.system_storage_metrics = m->config_by_conf.system_storage_metrics; + m->config.runtime_storage_metrics = m->config_by_conf.runtime_storage_metrics; + + MERGE_NON_ZERO(max_retention_usec, 0); + MERGE_NON_ZERO(max_file_usec, DEFAULT_MAX_FILE_USEC); + MERGE_NON_NEGATIVE(forward_to_syslog, false); + MERGE_NON_NEGATIVE(forward_to_kmsg, false); + MERGE_NON_NEGATIVE(forward_to_console, false); + MERGE_NON_NEGATIVE(forward_to_wall, true); manager_merge_forward_to_socket(m); - MERGE_BOOL(forward_to_syslog, false); - MERGE_BOOL(forward_to_kmsg, false); - MERGE_BOOL(forward_to_console, false); - MERGE_BOOL(forward_to_wall, true); + if (strdup_to(&m->config.tty_path, + m->config_by_cmdline.tty_path ?: + m->config_by_conf.tty_path ?: + m->config_by_cred.tty_path) < 0) + log_oom_debug(); MERGE_NON_NEGATIVE(max_level_store, LOG_DEBUG); MERGE_NON_NEGATIVE(max_level_syslog, LOG_DEBUG); @@ -142,17 +148,24 @@ static void manager_merge_configs(Manager *m) { MERGE_NON_NEGATIVE(max_level_console, LOG_INFO); MERGE_NON_NEGATIVE(max_level_wall, LOG_EMERG); MERGE_NON_NEGATIVE(max_level_socket, LOG_DEBUG); + MERGE_NON_NEGATIVE(split_mode, SPLIT_UID); + MERGE_NON_ZERO(line_max, DEFAULT_LINE_MAX); } static void manager_adjust_configs(Manager *m) { assert(m); - if (!!m->ratelimit_interval != !!m->ratelimit_burst) { /* One set to 0 and the other not? */ + if ((m->config.ratelimit_interval == 0) != (m->config.ratelimit_burst == 0)) { /* One set to 0 and the other not? */ log_debug("Setting both rate limit interval and burst from %s/%u to 0/0", - FORMAT_TIMESPAN(m->ratelimit_interval, USEC_PER_SEC), - m->ratelimit_burst); - m->ratelimit_interval = m->ratelimit_burst = 0; + FORMAT_TIMESPAN(m->config.ratelimit_interval, USEC_PER_SEC), + m->config.ratelimit_burst); + m->config.ratelimit_interval = 0; + m->config.ratelimit_burst = 0; } + + /* copy metrics to manager */ + m->system_storage.metrics = m->config.system_storage_metrics; + m->runtime_storage.metrics = m->config.runtime_storage_metrics; } static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { @@ -280,7 +293,7 @@ static void manager_parse_config_file(Manager *m) { config_item_perf_lookup, journald_gperf_lookup, CONFIG_PARSE_WARN, - m); + &m->config_by_conf); } static void manager_load_credentials(JournalConfig *c) { @@ -317,8 +330,9 @@ void manager_load_config(Manager *m) { assert(m); - manager_set_defaults(m); - manager_reset_configs(m); + journal_config_set_defaults(&m->config_by_conf); + journal_config_set_defaults(&m->config_by_cred); + journal_config_set_defaults(&m->config_by_cmdline); manager_load_credentials(&m->config_by_cred); manager_parse_config_file(m); @@ -331,18 +345,14 @@ void manager_load_config(Manager *m) { } manager_merge_configs(m); - manager_adjust_configs(m); } static void manager_reload_config(Manager *m) { assert(m); - manager_set_defaults(m); - - m->config_by_conf = JOURNAL_CONFIG_INIT; + journal_config_set_defaults(&m->config_by_conf); manager_parse_config_file(m); - manager_merge_configs(m); manager_adjust_configs(m); } @@ -449,7 +459,7 @@ int config_parse_compress( int r; if (isempty(rvalue)) { - compress->enabled = true; + compress->enabled = -1; compress->threshold_bytes = UINT64_MAX; return 0; } diff --git a/src/journal/journald-config.h b/src/journal/journald-config.h index ffa6c5f2b87..31077acba2d 100644 --- a/src/journal/journald-config.h +++ b/src/journal/journald-config.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include "journal-file.h" #include "journald-forward.h" #include "socket-util.h" @@ -22,28 +23,66 @@ typedef enum SplitMode { } SplitMode; typedef struct JournalCompressOptions { - bool enabled; + int enabled; uint64_t threshold_bytes; } JournalCompressOptions; typedef struct JournalConfig { + /* Storage=, cred: journal.storage */ Storage storage; - - bool forward_to_kmsg; - bool forward_to_syslog; - bool forward_to_console; - bool forward_to_wall; - + /* Compress= */ + JournalCompressOptions compress; + /* Seal= */ + int seal; + /* ReadKMsg= */ + int read_kmsg; + /* Audit= */ + int set_audit; + /* SyncIntervalSec= */ + usec_t sync_interval_usec; + /* RateLimitIntervalSec= */ + usec_t ratelimit_interval; + /* RateLimitBurst= */ + unsigned ratelimit_burst; + /* SystemMaxUse=, SystemMaxFileSize=, SystemKeepFree=, SystemMaxFiles= */ + JournalMetrics system_storage_metrics; + /* RuntimeMaxUse=, RuntimeMaxFileSize=, RuntimeKeepFree=, RuntimeMaxFiles= */ + JournalMetrics runtime_storage_metrics; + /* MaxRetentionSec= */ + usec_t max_retention_usec; + /* MaxFileSec= */ + usec_t max_file_usec; + /* ForwardToSyslog=, proc: systemd.journald.forward_to_syslog */ + int forward_to_syslog; + /* ForwardToKMsg=, proc: systemd.journald.forward_to_kmsg */ + int forward_to_kmsg; + /* ForwardToConsole=, proc: systemd.journald.forward_to_console */ + int forward_to_console; + /* ForwardToWall=, proc: systemd.journald.forward_to_wall */ + int forward_to_wall; + /* ForwardToSocket=, cred: journal.forward_to_socket */ SocketAddress forward_to_socket; - + /* TTYPath= */ + char *tty_path; + /* MaxLevelStore=, proc: systemd.journald.max_level_store */ int max_level_store; + /* MaxLevelSyslog=, proc: systemd.journald.max_level_syslog */ int max_level_syslog; + /* MaxLevelKMsg=, proc: systemd.journald.max_level_kmsg */ int max_level_kmsg; + /* MaxLevelConsole=, proc: systemd.journald.max_level_console */ int max_level_console; + /* MaxLevelWall=, systemd.journald.max_level_wall */ int max_level_wall; + /* MaxLevelSocket=, systemd.journald.max_level_socket */ int max_level_socket; + /* SplitMode= */ + SplitMode split_mode; + /* LineMax= */ + size_t line_max; } JournalConfig; +void journal_config_done(JournalConfig *c); void manager_load_config(Manager *m); int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata); diff --git a/src/journal/journald-console.c b/src/journal/journald-console.c index 73cccb4da98..0f376f9e5a4 100644 --- a/src/journal/journald-console.c +++ b/src/journal/journald-console.c @@ -91,7 +91,7 @@ void manager_forward_console( iovec[n++] = IOVEC_MAKE_STRING(color_off); iovec[n++] = IOVEC_MAKE_STRING("\n"); - tty = m->tty_path ?: "/dev/console"; + tty = m->config.tty_path ?: "/dev/console"; /* Before you ask: yes, on purpose we open/close the console for each log line we write individually. This is a * good strategy to avoid journald getting killed by the kernel's SAK concept (it doesn't fix this entirely, diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c index a6b34f14e41..e6d0c70e020 100644 --- a/src/journal/journald-context.c +++ b/src/journal/journald-context.c @@ -139,8 +139,8 @@ static int client_context_new(Manager *m, pid_t pid, ClientContext **ret) { .timestamp = USEC_INFINITY, .extra_fields_mtime = NSEC_INFINITY, .log_level_max = -1, - .log_ratelimit_interval = m->ratelimit_interval, - .log_ratelimit_burst = m->ratelimit_burst, + .log_ratelimit_interval = m->config.ratelimit_interval, + .log_ratelimit_burst = m->config.ratelimit_burst, .capability_quintet = CAPABILITY_QUINTET_NULL, }; @@ -188,8 +188,8 @@ static void client_context_reset(Manager *m, ClientContext *c) { c->log_level_max = -1; - c->log_ratelimit_interval = m->ratelimit_interval; - c->log_ratelimit_burst = m->ratelimit_burst; + c->log_ratelimit_interval = m->config.ratelimit_interval; + c->log_ratelimit_burst = m->config.ratelimit_burst; c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns); c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns); diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf index 26cd08b3845..23caa287ffd 100644 --- a/src/journal/journald-gperf.gperf +++ b/src/journal/journald-gperf.gperf @@ -6,7 +6,7 @@ _Pragma("GCC diagnostic ignored \"-Wzero-as-null-pointer-constant\"") #endif #include #include "conf-parser.h" -#include "journald-manager.h" +#include "journald-config.h" %} struct ConfigPerfItem; %null_strings @@ -19,37 +19,37 @@ struct ConfigPerfItem; %struct-type %includes %% -Journal.Storage, config_parse_storage, 0, offsetof(Manager, config_by_conf.storage) -Journal.Compress, config_parse_compress, 0, offsetof(Manager, compress) -Journal.Seal, config_parse_bool, 0, offsetof(Manager, seal) -Journal.ReadKMsg, config_parse_bool, 0, offsetof(Manager, read_kmsg) -Journal.Audit, config_parse_tristate, 0, offsetof(Manager, set_audit) -Journal.SyncIntervalSec, config_parse_sec, 0, offsetof(Manager, sync_interval_usec) +Journal.Storage, config_parse_storage, 0, offsetof(JournalConfig, storage) +Journal.Compress, config_parse_compress, 0, offsetof(JournalConfig, compress) +Journal.Seal, config_parse_tristate, 0, offsetof(JournalConfig, seal) +Journal.ReadKMsg, config_parse_tristate, 0, offsetof(JournalConfig, read_kmsg) +Journal.Audit, config_parse_tristate, 0, offsetof(JournalConfig, set_audit) +Journal.SyncIntervalSec, config_parse_sec, 0, offsetof(JournalConfig, sync_interval_usec) # The following is a legacy name for compatibility -Journal.RateLimitInterval, config_parse_sec, 0, offsetof(Manager, ratelimit_interval) -Journal.RateLimitIntervalSec,config_parse_sec, 0, offsetof(Manager, ratelimit_interval) -Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(Manager, ratelimit_burst) -Journal.SystemMaxUse, config_parse_iec_uint64, 0, offsetof(Manager, system_storage.metrics.max_use) -Journal.SystemMaxFileSize, config_parse_iec_uint64, 0, offsetof(Manager, system_storage.metrics.max_size) -Journal.SystemKeepFree, config_parse_iec_uint64, 0, offsetof(Manager, system_storage.metrics.keep_free) -Journal.SystemMaxFiles, config_parse_uint64, 0, offsetof(Manager, system_storage.metrics.n_max_files) -Journal.RuntimeMaxUse, config_parse_iec_uint64, 0, offsetof(Manager, runtime_storage.metrics.max_use) -Journal.RuntimeMaxFileSize, config_parse_iec_uint64, 0, offsetof(Manager, runtime_storage.metrics.max_size) -Journal.RuntimeKeepFree, config_parse_iec_uint64, 0, offsetof(Manager, runtime_storage.metrics.keep_free) -Journal.RuntimeMaxFiles, config_parse_uint64, 0, offsetof(Manager, runtime_storage.metrics.n_max_files) -Journal.MaxRetentionSec, config_parse_sec, 0, offsetof(Manager, max_retention_usec) -Journal.MaxFileSec, config_parse_sec, 0, offsetof(Manager, max_file_usec) -Journal.ForwardToSyslog, config_parse_bool, 0, offsetof(Manager, config_by_conf.forward_to_syslog) -Journal.ForwardToKMsg, config_parse_bool, 0, offsetof(Manager, config_by_conf.forward_to_kmsg) -Journal.ForwardToConsole, config_parse_bool, 0, offsetof(Manager, config_by_conf.forward_to_console) -Journal.ForwardToWall, config_parse_bool, 0, offsetof(Manager, config_by_conf.forward_to_wall) -Journal.ForwardToSocket, config_parse_forward_to_socket, 0, offsetof(Manager, config_by_conf.forward_to_socket) -Journal.TTYPath, config_parse_path, 0, offsetof(Manager, tty_path) -Journal.MaxLevelStore, config_parse_log_level, 0, offsetof(Manager, config_by_conf.max_level_store) -Journal.MaxLevelSyslog, config_parse_log_level, 0, offsetof(Manager, config_by_conf.max_level_syslog) -Journal.MaxLevelKMsg, config_parse_log_level, 0, offsetof(Manager, config_by_conf.max_level_kmsg) -Journal.MaxLevelConsole, config_parse_log_level, 0, offsetof(Manager, config_by_conf.max_level_console) -Journal.MaxLevelWall, config_parse_log_level, 0, offsetof(Manager, config_by_conf.max_level_wall) -Journal.MaxLevelSocket, config_parse_log_level, 0, offsetof(Manager, config_by_conf.max_level_socket) -Journal.SplitMode, config_parse_split_mode, 0, offsetof(Manager, split_mode) -Journal.LineMax, config_parse_line_max, 0, offsetof(Manager, line_max) +Journal.RateLimitInterval, config_parse_sec, 0, offsetof(JournalConfig, ratelimit_interval) +Journal.RateLimitIntervalSec, config_parse_sec, 0, offsetof(JournalConfig, ratelimit_interval) +Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(JournalConfig, ratelimit_burst) +Journal.SystemMaxUse, config_parse_iec_uint64, 0, offsetof(JournalConfig, system_storage_metrics.max_use) +Journal.SystemMaxFileSize, config_parse_iec_uint64, 0, offsetof(JournalConfig, system_storage_metrics.max_size) +Journal.SystemKeepFree, config_parse_iec_uint64, 0, offsetof(JournalConfig, system_storage_metrics.keep_free) +Journal.SystemMaxFiles, config_parse_uint64, 0, offsetof(JournalConfig, system_storage_metrics.n_max_files) +Journal.RuntimeMaxUse, config_parse_iec_uint64, 0, offsetof(JournalConfig, runtime_storage_metrics.max_use) +Journal.RuntimeMaxFileSize, config_parse_iec_uint64, 0, offsetof(JournalConfig, runtime_storage_metrics.max_size) +Journal.RuntimeKeepFree, config_parse_iec_uint64, 0, offsetof(JournalConfig, runtime_storage_metrics.keep_free) +Journal.RuntimeMaxFiles, config_parse_uint64, 0, offsetof(JournalConfig, runtime_storage_metrics.n_max_files) +Journal.MaxRetentionSec, config_parse_sec, 0, offsetof(JournalConfig, max_retention_usec) +Journal.MaxFileSec, config_parse_sec, 0, offsetof(JournalConfig, max_file_usec) +Journal.ForwardToSyslog, config_parse_tristate, 0, offsetof(JournalConfig, forward_to_syslog) +Journal.ForwardToKMsg, config_parse_tristate, 0, offsetof(JournalConfig, forward_to_kmsg) +Journal.ForwardToConsole, config_parse_tristate, 0, offsetof(JournalConfig, forward_to_console) +Journal.ForwardToWall, config_parse_tristate, 0, offsetof(JournalConfig, forward_to_wall) +Journal.ForwardToSocket, config_parse_forward_to_socket, 0, offsetof(JournalConfig, forward_to_socket) +Journal.TTYPath, config_parse_path, 0, offsetof(JournalConfig, tty_path) +Journal.MaxLevelStore, config_parse_log_level, 0, offsetof(JournalConfig, max_level_store) +Journal.MaxLevelSyslog, config_parse_log_level, 0, offsetof(JournalConfig, max_level_syslog) +Journal.MaxLevelKMsg, config_parse_log_level, 0, offsetof(JournalConfig, max_level_kmsg) +Journal.MaxLevelConsole, config_parse_log_level, 0, offsetof(JournalConfig, max_level_console) +Journal.MaxLevelWall, config_parse_log_level, 0, offsetof(JournalConfig, max_level_wall) +Journal.MaxLevelSocket, config_parse_log_level, 0, offsetof(JournalConfig, max_level_socket) +Journal.SplitMode, config_parse_split_mode, 0, offsetof(JournalConfig, split_mode) +Journal.LineMax, config_parse_line_max, 0, offsetof(JournalConfig, line_max) diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index 926cc823db5..ec05e1f1b48 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -332,7 +332,7 @@ static int manager_read_dev_kmsg(Manager *m) { assert(m); assert(m->dev_kmsg_fd >= 0); - assert(m->read_kmsg); + assert(m->config.read_kmsg); l = read(m->dev_kmsg_fd, buffer, sizeof(buffer) - 1); if (l == 0) @@ -356,7 +356,7 @@ int manager_flush_dev_kmsg(Manager *m) { if (m->dev_kmsg_fd < 0) return 0; - if (!m->read_kmsg) + if (!m->config.read_kmsg) return 0; log_debug("Flushing /dev/kmsg..."); @@ -400,7 +400,7 @@ int manager_open_dev_kmsg(Manager *m) { assert(m->dev_kmsg_fd < 0); assert(!m->dev_kmsg_event_source); - mode_t mode = manager_kmsg_mode(m->read_kmsg); + mode_t mode = manager_kmsg_mode(m->config.read_kmsg); _cleanup_close_ int fd = open("/dev/kmsg", mode); if (fd < 0) { @@ -409,7 +409,7 @@ int manager_open_dev_kmsg(Manager *m) { return 0; } - if (!m->read_kmsg) { + if (!m->config.read_kmsg) { m->dev_kmsg_fd = TAKE_FD(fd); return 0; } @@ -436,7 +436,7 @@ int manager_open_kernel_seqnum(Manager *m) { /* We store the seqnum we last read in an mmapped file. That way we can just use it like a variable, * but it is persistent and automatically flushed at reboot. */ - if (!m->read_kmsg) + if (!m->config.read_kmsg) return 0; r = manager_map_seqnum_file(m, "kernel-seqnum", sizeof(uint64_t), (void**) &m->kernel_seqnum); @@ -455,7 +455,7 @@ int manager_reload_dev_kmsg(Manager *m) { if (m->dev_kmsg_fd < 0) return manager_open_dev_kmsg(m); - mode_t mode = manager_kmsg_mode(m->read_kmsg); + mode_t mode = manager_kmsg_mode(m->config.read_kmsg); int flags = fcntl(m->dev_kmsg_fd, F_GETFL); if (flags < 0) /* Proceed with reload in case the flags have changed. */ diff --git a/src/journal/journald-manager.c b/src/journal/journald-manager.c index 3be6296cea3..727425225ff 100644 --- a/src/journal/journald-manager.c +++ b/src/journal/journald-manager.c @@ -263,7 +263,7 @@ static void manager_add_acls(JournalFile *f, uid_t uid) { static JournalFileFlags manager_get_file_flags(Manager *m, bool seal) { assert(m); - return (m->compress.enabled ? JOURNAL_COMPRESS : 0) | + return (m->config.compress.enabled ? JOURNAL_COMPRESS : 0) | (seal ? JOURNAL_SEAL : 0) | JOURNAL_STRICT_ORDER; } @@ -295,7 +295,7 @@ static int manager_open_journal( open_flags, file_flags, 0640, - m->compress.threshold_bytes, + m->config.compress.threshold_bytes, metrics, m->mmap, &f); @@ -306,7 +306,7 @@ static int manager_open_journal( open_flags, file_flags, 0640, - m->compress.threshold_bytes, + m->config.compress.threshold_bytes, metrics, m->mmap, /* template= */ NULL, @@ -378,7 +378,7 @@ static int manager_system_journal_open( /* reliably= */ true, fn, O_RDWR|O_CREAT, - m->seal, + m->config.seal, &m->system_storage.metrics, &m->system_journal); if (r >= 0) { @@ -486,7 +486,7 @@ static int manager_find_user_journal(Manager *m, uid_t uid, JournalFile **ret) { /* reliably= */ true, p, O_RDWR|O_CREAT, - m->seal, + m->config.seal, &m->system_storage.metrics, &f); if (r < 0) @@ -558,7 +558,7 @@ static int manager_do_rotate( if (!*f) return -EINVAL; - r = journal_file_rotate(f, m->mmap, manager_get_file_flags(m, seal), m->compress.threshold_bytes, m->deferred_closes); + r = journal_file_rotate(f, m->mmap, manager_get_file_flags(m, seal), m->config.compress.threshold_bytes, m->deferred_closes); if (r < 0) { if (*f) return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, @@ -668,9 +668,9 @@ static int manager_archive_offline_user_journals(Manager *m) { fd, full, O_RDWR, - manager_get_file_flags(m, m->seal) & ~JOURNAL_STRICT_ORDER, /* strict order does not matter here */ + manager_get_file_flags(m, m->config.seal) & ~JOURNAL_STRICT_ORDER, /* strict order does not matter here */ 0640, - m->compress.threshold_bytes, + m->config.compress.threshold_bytes, &m->system_storage.metrics, m->mmap, /* template= */ NULL, @@ -713,11 +713,11 @@ void manager_rotate(Manager *m) { /* First, rotate the system journal (either in its runtime flavour or in its runtime flavour) */ (void) manager_do_rotate(m, &m->runtime_journal, "runtime", /* seal= */ false, /* uid= */ 0); - (void) manager_do_rotate(m, &m->system_journal, "system", m->seal, /* uid= */ 0); + (void) manager_do_rotate(m, &m->system_journal, "system", m->config.seal, /* uid= */ 0); /* Then, rotate all user journals we have open (keeping them open) */ ORDERED_HASHMAP_FOREACH_KEY(f, k, m->user_journals) { - r = manager_do_rotate(m, &f, "user", m->seal, PTR_TO_UID(k)); + r = manager_do_rotate(m, &f, "user", m->config.seal, PTR_TO_UID(k)); if (r >= 0) ordered_hashmap_replace(m->user_journals, k, f); else if (!f) @@ -744,12 +744,12 @@ static void manager_rotate_journal(Manager *m, JournalFile *f, uid_t uid) { * 💣💣💣 This invalidate 'f', and the caller cannot reuse the passed JournalFile object. 💣💣💣 */ if (f == m->system_journal) - (void) manager_do_rotate(m, &m->system_journal, "system", m->seal, /* uid= */ 0); + (void) manager_do_rotate(m, &m->system_journal, "system", m->config.seal, /* uid= */ 0); else if (f == m->runtime_journal) (void) manager_do_rotate(m, &m->runtime_journal, "runtime", /* seal= */ false, /* uid= */ 0); else { assert(ordered_hashmap_get(m->user_journals, UID_TO_PTR(uid)) == f); - r = manager_do_rotate(m, &f, "user", m->seal, uid); + r = manager_do_rotate(m, &f, "user", m->config.seal, uid); if (r >= 0) ordered_hashmap_replace(m->user_journals, UID_TO_PTR(uid), f); else if (!f) @@ -798,7 +798,7 @@ static void manager_do_vacuum(Manager *m, JournalStorage *storage, bool verbose) manager_space_usage_message(m, storage); r = journal_directory_vacuum(storage->path, storage->space.limit, - storage->metrics.n_max_files, m->max_retention_usec, + storage->metrics.n_max_files, m->config.max_retention_usec, &m->oldest_file_usec, verbose); if (r < 0 && r != -ENOENT) log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT, @@ -967,7 +967,7 @@ static void manager_write_to_journal( if (!f) return; - if (journal_file_rotate_suggested(f, m->max_file_usec, LOG_DEBUG)) { + if (journal_file_rotate_suggested(f, m->config.max_file_usec, LOG_DEBUG)) { if (vacuumed) { log_ratelimit_warning(JOURNAL_LOG_RATELIMIT, "Suppressing rotation, as we already rotated immediately before write attempt. Giving up."); @@ -1177,10 +1177,10 @@ static void manager_dispatch_message_real( iovec[n++] = in_initrd() ? IOVEC_MAKE_STRING("_RUNTIME_SCOPE=initrd") : IOVEC_MAKE_STRING("_RUNTIME_SCOPE=system"); assert(n <= mm); - if (m->split_mode == SPLIT_UID && c && uid_is_valid(c->uid)) + if (m->config.split_mode == SPLIT_UID && c && uid_is_valid(c->uid)) /* Split up strictly by (non-root) UID */ journal_uid = c->uid; - else if (m->split_mode == SPLIT_LOGIN && c && c->uid > 0 && uid_is_valid(c->owner_uid)) + else if (m->config.split_mode == SPLIT_LOGIN && c && c->uid > 0 && uid_is_valid(c->owner_uid)) /* Split up by login UIDs. We do this only if the * realuid is not root, in order not to accidentally * leak privileged information to the user that is @@ -1881,21 +1881,21 @@ static int manager_schedule_sync(Manager *m, int priority) { if (m->sync_scheduled) return 0; - if (m->sync_interval_usec > 0) { + if (m->config.sync_interval_usec > 0) { if (!m->sync_event_source) { r = sd_event_add_time_relative( m->event, &m->sync_event_source, CLOCK_MONOTONIC, - m->sync_interval_usec, 0, + m->config.sync_interval_usec, 0, manager_dispatch_sync, m); if (r < 0) return r; r = sd_event_source_set_priority(m->sync_event_source, SD_EVENT_PRIORITY_IMPORTANT); } else { - r = sd_event_source_set_time_relative(m->sync_event_source, m->sync_interval_usec); + r = sd_event_source_set_time_relative(m->sync_event_source, m->config.sync_interval_usec); if (r < 0) return r; @@ -2125,7 +2125,7 @@ static bool manager_is_idle(Manager *m) { /* If a retention maximum is set larger than the idle time we need to be running to enforce it, hence * turn off the idle logic. */ - if (m->max_retention_usec > IDLE_TIMEOUT_USEC) + if (m->config.max_retention_usec > IDLE_TIMEOUT_USEC) return false; /* We aren't idle if we have a varlink client */ @@ -2258,8 +2258,8 @@ int manager_reload_journals(Manager *m) { /* Current journal can continue being used. Update config values as needed. */ r = journal_file_reload( m->system_journal, - manager_get_file_flags(m, m->seal), - m->compress.threshold_bytes, + manager_get_file_flags(m, m->config.seal), + m->config.compress.threshold_bytes, &m->system_storage.metrics); if (r < 0) return log_warning_errno(r, "Failed to reload system journal on reload, ignoring: %m"); @@ -2273,7 +2273,7 @@ int manager_reload_journals(Manager *m) { r = journal_file_reload( m->runtime_journal, manager_get_file_flags(m, /* seal */ false), - m->compress.threshold_bytes, + m->config.compress.threshold_bytes, &m->runtime_storage.metrics); if (r < 0) return log_warning_errno(r, "Failed to reload runtime journal on reload, ignoring: %m"); @@ -2308,6 +2308,9 @@ int manager_new(Manager **ret, const char *namespace) { .notify_fd = -EBADF, .forward_socket_fd = -EBADF, + .system_storage.name = "System Journal", + .runtime_storage.name = "Runtime Journal", + .watchdog_usec = USEC_INFINITY, .sync_scheduled = false, @@ -2602,7 +2605,6 @@ Manager* manager_free(Manager *m) { manager_unmap_seqnum_file(m->kernel_seqnum, sizeof(*m->kernel_seqnum)); free(m->buffer); - free(m->tty_path); free(m->cgroup_root); free(m->hostname_field); free(m->runtime_storage.path); @@ -2620,5 +2622,10 @@ Manager* manager_free(Manager *m) { sync_req_free(req); prioq_free(m->sync_req_boottime_prioq); + journal_config_done(&m->config); + journal_config_done(&m->config_by_cred); + journal_config_done(&m->config_by_conf); + journal_config_done(&m->config_by_cmdline); + return mfree(m); } diff --git a/src/journal/journald-manager.h b/src/journal/journald-manager.h index ac538a12e75..05a84bf8f18 100644 --- a/src/journal/journald-manager.h +++ b/src/journal/journald-manager.h @@ -74,18 +74,10 @@ typedef struct Manager { char *buffer; OrderedHashmap *ratelimit_groups_by_id; - usec_t sync_interval_usec; - usec_t ratelimit_interval; - unsigned ratelimit_burst; JournalStorage runtime_storage; JournalStorage system_storage; - JournalCompressOptions compress; - int set_audit; - bool seal; - bool read_kmsg; - bool send_watchdog; bool sent_notify_ready; bool sync_scheduled; @@ -93,18 +85,12 @@ typedef struct Manager { unsigned n_forward_syslog_missed; usec_t last_warn_forward_syslog_missed; - usec_t max_retention_usec; - usec_t max_file_usec; usec_t oldest_file_usec; LIST_HEAD(StdoutStream, stdout_streams); LIST_HEAD(StdoutStream, stdout_streams_notify_queue); unsigned n_stdout_streams; - char *tty_path; - - SplitMode split_mode; - MMapCache *mmap; Set *deferred_closes; @@ -125,8 +111,6 @@ typedef struct Manager { usec_t last_realtime_clock; - size_t line_max; - /* Caching of client metadata */ Hashmap *client_contexts; Prioq *client_contexts_lru; diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index cfd17fcd83d..650b81da5f8 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -454,7 +454,7 @@ static size_t stdout_stream_line_max(StdoutStream *s) { return STDOUT_STREAM_SETUP_PROTOCOL_LINE_MAX; /* After the protocol's "setup" phase is complete, let's use whatever the user configured */ - return s->manager->line_max; + return s->manager->config.line_max; } static int stdout_stream_scan( @@ -557,7 +557,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, /* Try to make use of the allocated buffer in full, but never read more than the configured line size. Also, * always leave room for a terminating NUL we might need to add. */ - limit = MIN(allocated - 1, MAX(s->manager->line_max, STDOUT_STREAM_SETUP_PROTOCOL_LINE_MAX)); + limit = MIN(allocated - 1, MAX(s->manager->config.line_max, STDOUT_STREAM_SETUP_PROTOCOL_LINE_MAX)); assert(s->length <= limit); iovec = IOVEC_MAKE(s->buffer + s->length, limit - s->length); diff --git a/src/journal/journald.c b/src/journal/journald.c index 95787418d22..393e90e1b52 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -91,9 +91,9 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to get the current time: %m"); - if (m->max_retention_usec > 0 && m->oldest_file_usec > 0) { + if (m->config.max_retention_usec > 0 && m->oldest_file_usec > 0) { /* Calculate when to rotate the next time */ - t = usec_sub_unsigned(usec_add(m->oldest_file_usec, m->max_retention_usec), n); + t = usec_sub_unsigned(usec_add(m->oldest_file_usec, m->config.max_retention_usec), n); /* The retention time is reached, so let's vacuum! */ if (t <= 0) { diff --git a/src/journal/test-journald-config.c b/src/journal/test-journald-config.c index 5eb089a8cb1..735e4d664a8 100644 --- a/src/journal/test-journald-config.c +++ b/src/journal/test-journald-config.c @@ -13,7 +13,7 @@ #define _COMPRESS_PARSE_CHECK(str, enab, thresh, varname) \ do { \ - JournalCompressOptions varname = {true, 111}; \ + JournalCompressOptions varname = {-222, 111}; \ config_parse_compress("", "", 0, "", 0, "", 0, str, \ &varname, NULL); \ assert_se((enab) == varname.enabled); \ @@ -48,9 +48,9 @@ TEST(config_compress) { COMPRESS_PARSE_CHECK("1G", true, 1024 * 1024 * 1024); /* Invalid Case */ - COMPRESS_PARSE_CHECK("-1", true, 111); - COMPRESS_PARSE_CHECK("blah blah", true, 111); - COMPRESS_PARSE_CHECK("", true, UINT64_MAX); + COMPRESS_PARSE_CHECK("-1", -222, 111); + COMPRESS_PARSE_CHECK("blah blah", -222, 111); + COMPRESS_PARSE_CHECK("", -1, UINT64_MAX); } #define _FORWARD_TO_SOCKET_PARSE_CHECK_FAILS(str, addr, varname) \ From 32bd43d768a4bdd54481c5e37ce9ea3d1009a824 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jul 2025 17:19:46 +0900 Subject: [PATCH 10/24] journald: do not read configs by journald fuzzers --- src/journal/fuzz-journald-audit.c | 3 +-- src/journal/fuzz-journald-kmsg.c | 3 +-- src/journal/fuzz-journald-native-fd.c | 3 +-- src/journal/fuzz-journald-stream.c | 3 +-- src/journal/fuzz-journald.c | 34 +++++++++++++-------------- src/journal/fuzz-journald.h | 14 ++++++++--- src/journal/journald-config.c | 8 ++----- src/journal/journald-config.h | 2 ++ src/journal/journald-manager.c | 13 ++++------ src/journal/journald-manager.h | 3 ++- src/journal/journald.c | 8 ++++++- 11 files changed, 49 insertions(+), 45 deletions(-) diff --git a/src/journal/fuzz-journald-audit.c b/src/journal/fuzz-journald-audit.c index 7558900ff51..07470ecd2f0 100644 --- a/src/journal/fuzz-journald-audit.c +++ b/src/journal/fuzz-journald-audit.c @@ -9,8 +9,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { fuzz_setup_logging(); - assert_se(manager_new(&m, /* namespace= */ NULL) >= 0); - dummy_manager_init(m, data, size); + dummy_manager_new(&m, data, size); process_audit_string(m, 0, m->buffer, size); return 0; diff --git a/src/journal/fuzz-journald-kmsg.c b/src/journal/fuzz-journald-kmsg.c index 2ef36172887..041662686e2 100644 --- a/src/journal/fuzz-journald-kmsg.c +++ b/src/journal/fuzz-journald-kmsg.c @@ -12,8 +12,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { fuzz_setup_logging(); - assert_se(manager_new(&m, /* namespace= */ NULL) >= 0); - dummy_manager_init(m, data, size); + dummy_manager_new(&m, data, size); dev_kmsg_record(m, m->buffer, size); return 0; diff --git a/src/journal/fuzz-journald-native-fd.c b/src/journal/fuzz-journald-native-fd.c index 356e59046fb..a6ff87556d8 100644 --- a/src/journal/fuzz-journald-native-fd.c +++ b/src/journal/fuzz-journald-native-fd.c @@ -21,8 +21,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { fuzz_setup_logging(); - assert_se(manager_new(&m, /* namespace= */ NULL) >= 0); - dummy_manager_init(m, NULL, 0); + dummy_manager_new(&m, NULL, 0); sealed_fd = memfd_new_and_seal(NULL, data, size); assert_se(sealed_fd >= 0); diff --git a/src/journal/fuzz-journald-stream.c b/src/journal/fuzz-journald-stream.c index 602bd59c45f..1ee49461d39 100644 --- a/src/journal/fuzz-journald-stream.c +++ b/src/journal/fuzz-journald-stream.c @@ -23,8 +23,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { fuzz_setup_logging(); assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, stream_fds) >= 0); - assert_se(manager_new(&m, /* namespace= */ NULL) >= 0); - dummy_manager_init(m, NULL, 0); + dummy_manager_new(&m, NULL, 0); assert_se(stdout_stream_install(m, stream_fds[0], &stream) >= 0); fd0 = TAKE_FD(stream_fds[0]); /* avoid double close */ diff --git a/src/journal/fuzz-journald.c b/src/journal/fuzz-journald.c index 0bb4d5d5650..b94a249c986 100644 --- a/src/journal/fuzz-journald.c +++ b/src/journal/fuzz-journald.c @@ -5,35 +5,33 @@ #include "alloc-util.h" #include "fuzz-journald.h" #include "journald-manager.h" +#include "tests.h" -void dummy_manager_init(Manager *m, const uint8_t *buffer, size_t size) { - assert(m); +void dummy_manager_new(Manager **ret, const uint8_t *buffer, size_t size) { + _cleanup_(manager_freep) Manager *m = NULL; + assert(ret); + + ASSERT_OK(manager_new(&m)); + manager_merge_configs(m); m->config.storage = STORAGE_NONE; - assert_se(sd_event_default(&m->event) >= 0); + ASSERT_OK(sd_event_default(&m->event)); - if (buffer) { - m->buffer = memdup_suffix0(buffer, size); - assert_se(m->buffer); - } + if (buffer) + ASSERT_NOT_NULL(m->buffer = memdup_suffix0(buffer, size)); + + *ret = TAKE_PTR(m); } void fuzz_journald_processing_function( const uint8_t *data, size_t size, - void (*f)(Manager *m, const char *buf, size_t raw_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len) - ) { - - _cleanup_(manager_freep) Manager *m = NULL; - char *label = NULL; - size_t label_len = 0; - struct ucred *ucred = NULL; - struct timeval *tv = NULL; + journal_process_t f) { if (size == 0) return; - assert_se(manager_new(&m, /* namespace= */ NULL) >= 0); - dummy_manager_init(m, data, size); - (*f)(m, m->buffer, size, ucred, tv, label, label_len); + _cleanup_(manager_freep) Manager *m = NULL; + dummy_manager_new(&m, data, size); + f(m, m->buffer, size, /* ucred = */ NULL, /* tv = */ NULL, /* label = */ NULL, /* label_len = */ 0); } diff --git a/src/journal/fuzz-journald.h b/src/journal/fuzz-journald.h index 0d1ebce50a7..7eb491f36ad 100644 --- a/src/journal/fuzz-journald.h +++ b/src/journal/fuzz-journald.h @@ -3,10 +3,18 @@ #include "journald-manager.h" -void dummy_manager_init(Manager *m, const uint8_t *buffer, size_t size); +typedef void (*journal_process_t)( + Manager *m, + const char *buf, + size_t raw_len, + const struct ucred *ucred, + const struct timeval *tv, + const char *label, + size_t label_len); + +void dummy_manager_new(Manager **ret, const uint8_t *buffer, size_t size); void fuzz_journald_processing_function( const uint8_t *data, size_t size, - void (*f)(Manager *m, const char *buf, size_t raw_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len) -); + journal_process_t f); diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c index cece07411f4..42245bf8d0d 100644 --- a/src/journal/journald-config.c +++ b/src/journal/journald-config.c @@ -32,7 +32,7 @@ void journal_config_done(JournalConfig *c) { free(c->tty_path); } -static void journal_config_set_defaults(JournalConfig *c) { +void journal_config_set_defaults(JournalConfig *c) { assert(c); journal_config_done(c); @@ -105,7 +105,7 @@ static void manager_merge_forward_to_socket(Manager *m) { m->config_by_cred.name ?: \ default_value -static void manager_merge_configs(Manager *m) { +void manager_merge_configs(Manager *m) { assert(m); /* From highest to lowest priority: cmdline, conf, cred */ @@ -330,10 +330,6 @@ void manager_load_config(Manager *m) { assert(m); - journal_config_set_defaults(&m->config_by_conf); - journal_config_set_defaults(&m->config_by_cred); - journal_config_set_defaults(&m->config_by_cmdline); - manager_load_credentials(&m->config_by_cred); manager_parse_config_file(m); diff --git a/src/journal/journald-config.h b/src/journal/journald-config.h index 31077acba2d..c396c3a5603 100644 --- a/src/journal/journald-config.h +++ b/src/journal/journald-config.h @@ -83,6 +83,8 @@ typedef struct JournalConfig { } JournalConfig; void journal_config_done(JournalConfig *c); +void journal_config_set_defaults(JournalConfig *c); +void manager_merge_configs(Manager *m); void manager_load_config(Manager *m); int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata); diff --git a/src/journal/journald-manager.c b/src/journal/journald-manager.c index 727425225ff..b74dcfa7889 100644 --- a/src/journal/journald-manager.c +++ b/src/journal/journald-manager.c @@ -2193,7 +2193,7 @@ static int manager_refresh_idle_timer(Manager *m) { return 1; } -static int manager_set_namespace(Manager *m, const char *namespace) { +int manager_set_namespace(Manager *m, const char *namespace) { assert(m); if (!namespace) @@ -2288,9 +2288,8 @@ int manager_reload_journals(Manager *m) { return 0; } -int manager_new(Manager **ret, const char *namespace) { +int manager_new(Manager **ret) { _cleanup_(manager_freep) Manager *m = NULL; - int r; assert(ret); @@ -2324,11 +2323,9 @@ int manager_new(Manager **ret, const char *namespace) { .sigrtmin18_info.memory_pressure_userdata = m, }; - r = manager_set_namespace(m, namespace); - if (r < 0) - return r; - - manager_load_config(m); + journal_config_set_defaults(&m->config_by_conf); + journal_config_set_defaults(&m->config_by_cred); + journal_config_set_defaults(&m->config_by_cmdline); *ret = TAKE_PTR(m); return 0; diff --git a/src/journal/journald-manager.h b/src/journal/journald-manager.h index 05a84bf8f18..a9ef3f394f8 100644 --- a/src/journal/journald-manager.h +++ b/src/journal/journald-manager.h @@ -172,7 +172,8 @@ void manager_dispatch_message(Manager *m, struct iovec *iovec, size_t n, size_t void manager_driver_message_internal(Manager *m, pid_t object_pid, const char *format, ...) _sentinel_; #define manager_driver_message(...) manager_driver_message_internal(__VA_ARGS__, NULL) -int manager_new(Manager **ret, const char *namespace); +int manager_new(Manager **ret); +int manager_set_namespace(Manager *m, const char *namespace); int manager_init(Manager *m); Manager* manager_free(Manager *m); DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); diff --git a/src/journal/journald.c b/src/journal/journald.c index 393e90e1b52..e7ed6a556c9 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -52,10 +52,16 @@ static int run(int argc, char *argv[]) { sigbus_install(); - r = manager_new(&m, namespace); + r = manager_new(&m); if (r < 0) return log_oom(); + r = manager_set_namespace(m, namespace); + if (r < 0) + return r; + + manager_load_config(m); + r = manager_init(m); if (r < 0) return r; From f1bd0007c0898db85f807bb0faf2dfc51f3b3531 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jul 2025 17:26:29 +0900 Subject: [PATCH 11/24] journald: rename fuzz-journald.[ch] -> fuzz-journald-util.[ch] --- src/journal/fuzz-journald-audit.c | 2 +- src/journal/fuzz-journald-kmsg.c | 2 +- src/journal/fuzz-journald-native-fd.c | 2 +- src/journal/fuzz-journald-native.c | 2 +- src/journal/fuzz-journald-stream.c | 2 +- src/journal/fuzz-journald-syslog.c | 2 +- src/journal/{fuzz-journald.c => fuzz-journald-util.c} | 2 +- src/journal/{fuzz-journald.h => fuzz-journald-util.h} | 0 src/journal/meson.build | 2 +- 9 files changed, 8 insertions(+), 8 deletions(-) rename src/journal/{fuzz-journald.c => fuzz-journald-util.c} (96%) rename src/journal/{fuzz-journald.h => fuzz-journald-util.h} (100%) diff --git a/src/journal/fuzz-journald-audit.c b/src/journal/fuzz-journald-audit.c index 07470ecd2f0..768dcb4ca3e 100644 --- a/src/journal/fuzz-journald-audit.c +++ b/src/journal/fuzz-journald-audit.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "fuzz.h" -#include "fuzz-journald.h" +#include "fuzz-journald-util.h" #include "journald-audit.h" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { diff --git a/src/journal/fuzz-journald-kmsg.c b/src/journal/fuzz-journald-kmsg.c index 041662686e2..25aa4305675 100644 --- a/src/journal/fuzz-journald-kmsg.c +++ b/src/journal/fuzz-journald-kmsg.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "fuzz.h" -#include "fuzz-journald.h" +#include "fuzz-journald-util.h" #include "journald-kmsg.h" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { diff --git a/src/journal/fuzz-journald-native-fd.c b/src/journal/fuzz-journald-native-fd.c index a6ff87556d8..36a94b7b921 100644 --- a/src/journal/fuzz-journald-native-fd.c +++ b/src/journal/fuzz-journald-native-fd.c @@ -4,7 +4,7 @@ #include "fd-util.h" #include "fuzz.h" -#include "fuzz-journald.h" +#include "fuzz-journald-util.h" #include "journald-native.h" #include "memfd-util.h" #include "process-util.h" diff --git a/src/journal/fuzz-journald-native.c b/src/journal/fuzz-journald-native.c index 2aa491df137..3bb40662a05 100644 --- a/src/journal/fuzz-journald-native.c +++ b/src/journal/fuzz-journald-native.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "fuzz.h" -#include "fuzz-journald.h" +#include "fuzz-journald-util.h" #include "journald-native.h" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { diff --git a/src/journal/fuzz-journald-stream.c b/src/journal/fuzz-journald-stream.c index 1ee49461d39..eb1feda6623 100644 --- a/src/journal/fuzz-journald-stream.c +++ b/src/journal/fuzz-journald-stream.c @@ -8,7 +8,7 @@ #include "fd-util.h" #include "fuzz.h" -#include "fuzz-journald.h" +#include "fuzz-journald-util.h" #include "journald-stream.h" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { diff --git a/src/journal/fuzz-journald-syslog.c b/src/journal/fuzz-journald-syslog.c index a48ce88ac54..9d93b60fa5c 100644 --- a/src/journal/fuzz-journald-syslog.c +++ b/src/journal/fuzz-journald-syslog.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "fuzz.h" -#include "fuzz-journald.h" +#include "fuzz-journald-util.h" #include "journald-syslog.h" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { diff --git a/src/journal/fuzz-journald.c b/src/journal/fuzz-journald-util.c similarity index 96% rename from src/journal/fuzz-journald.c rename to src/journal/fuzz-journald-util.c index b94a249c986..cf624e6e425 100644 --- a/src/journal/fuzz-journald.c +++ b/src/journal/fuzz-journald-util.c @@ -3,7 +3,7 @@ #include "sd-event.h" #include "alloc-util.h" -#include "fuzz-journald.h" +#include "fuzz-journald-util.h" #include "journald-manager.h" #include "tests.h" diff --git a/src/journal/fuzz-journald.h b/src/journal/fuzz-journald-util.h similarity index 100% rename from src/journal/fuzz-journald.h rename to src/journal/fuzz-journald-util.h diff --git a/src/journal/meson.build b/src/journal/meson.build index 5f34cd6866c..c09f38dd91c 100644 --- a/src/journal/meson.build +++ b/src/journal/meson.build @@ -21,7 +21,7 @@ systemd_journald_extract_sources = files( 'journald-wall.c', # Build fuzz-journald.c as part of systemd-journald so we only compile it once instead of once per # fuzz test. - 'fuzz-journald.c', + 'fuzz-journald-util.c', ) journald_gperf_c = custom_target( From 61b1f7d2d3e8546d1da4ec8d020c4bbfb7041344 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jul 2025 23:23:20 +0900 Subject: [PATCH 12/24] journald-kmsg: shorten manager_flush_dev_kmsg() a bit --- src/journal/journald-kmsg.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index ec05e1f1b48..95163491c37 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -363,11 +363,8 @@ int manager_flush_dev_kmsg(Manager *m) { for (;;) { r = manager_read_dev_kmsg(m); - if (r < 0) + if (r <= 0) return r; - - if (r == 0) - break; } return 0; From 059efb97e397b76c9ef5c15449427ec6f74b006f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jul 2025 00:24:17 +0900 Subject: [PATCH 13/24] journald-kmsg: introduce manager_close_kernel_seqnum() helper function --- src/journal/journald-kmsg.c | 8 ++++++++ src/journal/journald-kmsg.h | 1 + src/journal/journald-manager.c | 4 ++-- src/journal/journald-manager.h | 1 + 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index 95163491c37..1344397cb84 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -429,6 +429,7 @@ int manager_open_kernel_seqnum(Manager *m) { int r; assert(m); + assert(!m->kernel_seqnum); /* We store the seqnum we last read in an mmapped file. That way we can just use it like a variable, * but it is persistent and automatically flushed at reboot. */ @@ -443,6 +444,13 @@ int manager_open_kernel_seqnum(Manager *m) { return 0; } +void manager_close_kernel_seqnum(Manager *m) { + assert(m); + + manager_unmap_seqnum_file(m->kernel_seqnum, sizeof(*m->kernel_seqnum)); + m->kernel_seqnum = NULL; +} + int manager_reload_dev_kmsg(Manager *m) { int r; diff --git a/src/journal/journald-kmsg.h b/src/journal/journald-kmsg.h index 51e5f34492d..5429e9adcd7 100644 --- a/src/journal/journald-kmsg.h +++ b/src/journal/journald-kmsg.h @@ -10,5 +10,6 @@ int manager_reload_dev_kmsg(Manager *m); void manager_forward_kmsg(Manager *m, int priority, const char *identifier, const char *message, const struct ucred *ucred); int manager_open_kernel_seqnum(Manager *m); +void manager_close_kernel_seqnum(Manager *m); void dev_kmsg_record(Manager *m, char *p, size_t l); diff --git a/src/journal/journald-manager.c b/src/journal/journald-manager.c index b74dcfa7889..87102a53070 100644 --- a/src/journal/journald-manager.c +++ b/src/journal/journald-manager.c @@ -2107,7 +2107,7 @@ int manager_map_seqnum_file( return 0; } -static void manager_unmap_seqnum_file(void *p, size_t size) { +void manager_unmap_seqnum_file(void *p, size_t size) { assert(size > 0); if (!p) @@ -2599,7 +2599,7 @@ Manager* manager_free(Manager *m) { ordered_hashmap_free(m->ratelimit_groups_by_id); manager_unmap_seqnum_file(m->seqnum, sizeof(*m->seqnum)); - manager_unmap_seqnum_file(m->kernel_seqnum, sizeof(*m->kernel_seqnum)); + manager_close_kernel_seqnum(m); free(m->buffer); free(m->cgroup_root); diff --git a/src/journal/journald-manager.h b/src/journal/journald-manager.h index a9ef3f394f8..6237c55d9f9 100644 --- a/src/journal/journald-manager.h +++ b/src/journal/journald-manager.h @@ -192,3 +192,4 @@ int manager_start_or_stop_idle_timer(Manager *m); int manager_reload_journals(Manager *m); int manager_map_seqnum_file(Manager *m, const char *fname, size_t size, void **ret); +void manager_unmap_seqnum_file(void *p, size_t size); From 1416a3df43aba6812336bbab77d74bb4095bcf49 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jul 2025 01:01:06 +0900 Subject: [PATCH 14/24] journald: introduce manager_unlink_seqnum_file() Currently unused, but will be used later. --- src/journal/journald-manager.c | 14 ++++++++++++++ src/journal/journald-manager.h | 1 + 2 files changed, 15 insertions(+) diff --git a/src/journal/journald-manager.c b/src/journal/journald-manager.c index 87102a53070..494bf7c4811 100644 --- a/src/journal/journald-manager.c +++ b/src/journal/journald-manager.c @@ -2116,6 +2116,20 @@ void manager_unmap_seqnum_file(void *p, size_t size) { assert_se(munmap(p, size) >= 0); } +int manager_unlink_seqnum_file(Manager *m, const char *fname) { + assert(m); + assert(fname); + + _cleanup_free_ char *fn = path_join(m->runtime_directory, fname); + if (!fn) + return log_oom(); + + if (unlink(fn) < 0 && errno != ENOENT) + return log_warning_errno(errno, "Failed to remove '%s': %m", fname); + + return 0; +} + static bool manager_is_idle(Manager *m) { assert(m); diff --git a/src/journal/journald-manager.h b/src/journal/journald-manager.h index 6237c55d9f9..4453fbe9adc 100644 --- a/src/journal/journald-manager.h +++ b/src/journal/journald-manager.h @@ -193,3 +193,4 @@ int manager_reload_journals(Manager *m); int manager_map_seqnum_file(Manager *m, const char *fname, size_t size, void **ret); void manager_unmap_seqnum_file(void *p, size_t size); +int manager_unlink_seqnum_file(Manager *m, const char *fname); From 7bc133fe5628c9decc7c768d2ab189eb5b308398 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 13 Jul 2025 23:24:08 +0900 Subject: [PATCH 15/24] journald-kmsg: fix reopening /dev/kmsg The previous logic was completely broken: - the access mode comparison is broken, - flushing kmsg did not work, as the configuration is already disabled, - seqnum file is not opened when previously disabled, - failure in reopening /dev/kmsg should not be critical. This fixes the above issues. --- src/journal/journald-config.c | 6 ++--- src/journal/journald-kmsg.c | 46 +++++++++++++++++++++++------------ src/journal/journald-kmsg.h | 2 +- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c index 42245bf8d0d..a2275a173ef 100644 --- a/src/journal/journald-config.c +++ b/src/journal/journald-config.c @@ -359,11 +359,11 @@ int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_sig (void) notify_reloading(); + _cleanup_(journal_config_done) JournalConfig old = TAKE_STRUCT(m->config); + manager_reload_config(m); - r = manager_reload_dev_kmsg(m); - if (r < 0) - return r; + (void) manager_reopen_dev_kmsg(m, old.read_kmsg); r = manager_reload_journals(m); if (r < 0) diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index 1344397cb84..855839113b7 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -451,32 +451,46 @@ void manager_close_kernel_seqnum(Manager *m) { m->kernel_seqnum = NULL; } -int manager_reload_dev_kmsg(Manager *m) { +static int manager_unlink_kernel_seqnum(Manager *m) { + assert(m); + assert(!m->kernel_seqnum); /* The file must not be mmap()ed. */ + + return manager_unlink_seqnum_file(m, "kernel-seqnum"); +} + +int manager_reopen_dev_kmsg(Manager *m, bool old_read_kmsg) { int r; assert(m); - /* Check if the fd has not yet been initialized. If so, open /dev/kmsg. */ + /* If the fd has not yet been initialized, let's shortcut and simply open /dev/kmsg. */ if (m->dev_kmsg_fd < 0) return manager_open_dev_kmsg(m); - mode_t mode = manager_kmsg_mode(m->config.read_kmsg); - int flags = fcntl(m->dev_kmsg_fd, F_GETFL); - if (flags < 0) - /* Proceed with reload in case the flags have changed. */ - log_warning_errno(errno, "Failed to get flags for /dev/kmsg, ignoring: %m"); - else if ((flags & O_ACCMODE_STRICT) == mode) - /* Mode is the same. No-op. */ - return 0; + if (m->config.read_kmsg == old_read_kmsg) + return 0; /* Setting is unchanged. */ - /* Flush kmsg. */ - r = manager_flush_dev_kmsg(m); - if (r < 0) - log_warning_errno(r, "Failed to flush /dev/kmsg on reload, ignoring: %m"); + if (!m->config.read_kmsg) { + /* If reading kmsg was enabled but now disable, let's flush the buffer before disabling it. */ + m->config.read_kmsg = true; + (void) manager_flush_dev_kmsg(m); + m->config.read_kmsg = false; - /* Set kmsg values to default. */ + /* seqnum file is not necessary anymore. Let's close it. */ + manager_close_kernel_seqnum(m); + + /* Also, unlink the file name as we will not warn some kmsg are lost when reading kmsg is + * re-enabled later. */ + manager_unlink_kernel_seqnum(m); + } + + /* Close previously configured event source and opened file descriptor. */ m->dev_kmsg_event_source = sd_event_source_disable_unref(m->dev_kmsg_event_source); m->dev_kmsg_fd = safe_close(m->dev_kmsg_fd); - return manager_open_dev_kmsg(m); + r = manager_open_dev_kmsg(m); + if (r < 0) + return r; + + return manager_open_kernel_seqnum(m); } diff --git a/src/journal/journald-kmsg.h b/src/journal/journald-kmsg.h index 5429e9adcd7..5c2eb1a5d24 100644 --- a/src/journal/journald-kmsg.h +++ b/src/journal/journald-kmsg.h @@ -5,7 +5,7 @@ int manager_open_dev_kmsg(Manager *m); int manager_flush_dev_kmsg(Manager *m); -int manager_reload_dev_kmsg(Manager *m); +int manager_reopen_dev_kmsg(Manager *m, bool old_read_kmsg); void manager_forward_kmsg(Manager *m, int priority, const char *identifier, const char *message, const struct ucred *ucred); From 63e990308f844f22291a6433337aa806ce6a1e81 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jul 2025 00:56:10 +0900 Subject: [PATCH 16/24] journald-kmsg: merge manager_kmsg_mode() into manager_open_dev_kmsg() As it is now only used by the function. --- src/journal/journald-kmsg.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index 855839113b7..1e5ad5a4e15 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -386,10 +386,6 @@ static int dispatch_dev_kmsg(sd_event_source *es, int fd, uint32_t revents, void return manager_read_dev_kmsg(m); } -static mode_t manager_kmsg_mode(bool read_kmsg) { - return O_CLOEXEC|O_NONBLOCK|O_NOCTTY|(read_kmsg ? O_RDWR : O_WRONLY); -} - int manager_open_dev_kmsg(Manager *m) { int r; @@ -397,7 +393,7 @@ int manager_open_dev_kmsg(Manager *m) { assert(m->dev_kmsg_fd < 0); assert(!m->dev_kmsg_event_source); - mode_t mode = manager_kmsg_mode(m->config.read_kmsg); + mode_t mode = O_CLOEXEC|O_NONBLOCK|O_NOCTTY|(m->config.read_kmsg ? O_RDWR : O_WRONLY); _cleanup_close_ int fd = open("/dev/kmsg", mode); if (fd < 0) { From 884274cbe6300b091b6462c18f7e1e2c62adc9ee Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jul 2025 01:20:36 +0900 Subject: [PATCH 17/24] journald-audit: make enable_audit() take Manager object And rename it to manager_set_kernel_audit(), and make it log failures. --- src/journal/journald-audit.c | 39 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c index 479a4d27919..05374f3329e 100644 --- a/src/journal/journald-audit.c +++ b/src/journal/journald-audit.c @@ -460,7 +460,15 @@ void manager_process_audit_message( process_audit_string(m, nl->nlmsg_type, NLMSG_DATA(nl), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr))); } -static int enable_audit(int fd, bool b) { +static int manager_set_kernel_audit(Manager *m) { + int r; + + assert(m); + assert(m->audit_fd >= 0); + + if (m->config.set_audit < 0) + return 0; + struct { union { struct nlmsghdr header; @@ -474,7 +482,7 @@ static int enable_audit(int fd, bool b) { .header.nlmsg_seq = 1, .header.nlmsg_pid = 0, .body.mask = AUDIT_STATUS_ENABLED, - .body.enabled = b, + .body.enabled = m->config.set_audit, }; union sockaddr_union sa = { .nl.nl_family = AF_NETLINK, @@ -491,17 +499,18 @@ static int enable_audit(int fd, bool b) { .msg_namelen = sizeof(sa.nl), }; - ssize_t n; - - n = sendmsg(fd, &mh, MSG_NOSIGNAL); + r = 0; + ssize_t n = sendmsg(m->audit_fd, &mh, MSG_NOSIGNAL); if (n < 0) - return -errno; + r = -errno; if (n != NLMSG_LENGTH(sizeof(struct audit_status))) - return -EIO; + r = SYNTHETIC_ERRNO(EIO); + if (r < 0) + return log_warning_errno(r, "Failed to %s kernel auditing: %m", enable_disable(m->config.set_audit)); - /* We don't wait for the result here, we can't do anything - * about it anyway */ + /* We don't wait for the result here, we can't do anything about it anyway. */ + log_debug("Auditing in kernel is %s.", enabled_disabled(m->config.set_audit)); return 0; } @@ -544,16 +553,6 @@ int manager_open_audit(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add audit fd to event loop: %m"); - if (m->config.set_audit >= 0) { - /* We are listening now, try to enable audit if configured so */ - r = enable_audit(m->audit_fd, m->config.set_audit); - if (r < 0) - log_warning_errno(r, "Failed to issue audit enable call: %m"); - else if (m->config.set_audit > 0) - log_debug("Auditing in kernel turned on."); - else - log_debug("Auditing in kernel turned off."); - } - + (void) manager_set_kernel_audit(m); return 0; } From cbf59f4bb27390199eb9eb13a69da67d82339c85 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jul 2025 01:24:32 +0900 Subject: [PATCH 18/24] journald-audit: apply new Audit= setting on reload --- src/journal/journald-audit.c | 12 ++++++++++++ src/journal/journald-audit.h | 1 + src/journal/journald-config.c | 2 ++ 3 files changed, 15 insertions(+) diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c index 05374f3329e..6f08875ca30 100644 --- a/src/journal/journald-audit.c +++ b/src/journal/journald-audit.c @@ -556,3 +556,15 @@ int manager_open_audit(Manager *m) { (void) manager_set_kernel_audit(m); return 0; } + +void manager_reset_kernel_audit(Manager *m, int old_set_audit) { + assert(m); + + if (m->audit_fd < 0) + return; + + if (m->config.set_audit == old_set_audit) + return; + + (void) manager_set_kernel_audit(m); +} diff --git a/src/journal/journald-audit.h b/src/journal/journald-audit.h index 17ba2efbbb6..1f1b778e372 100644 --- a/src/journal/journald-audit.h +++ b/src/journal/journald-audit.h @@ -10,3 +10,4 @@ void manager_process_audit_message(Manager *m, const void *buffer, size_t buffer void process_audit_string(Manager *m, int type, const char *data, size_t size); int manager_open_audit(Manager *m); +void manager_reset_kernel_audit(Manager *m, int old_set_audit); diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c index a2275a173ef..5e6b0727516 100644 --- a/src/journal/journald-config.c +++ b/src/journal/journald-config.c @@ -5,6 +5,7 @@ #include "conf-parser.h" #include "creds-util.h" #include "daemon-util.h" +#include "journald-audit.h" #include "journald-config.h" #include "journald-kmsg.h" #include "journald-manager.h" @@ -364,6 +365,7 @@ int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_sig manager_reload_config(m); (void) manager_reopen_dev_kmsg(m, old.read_kmsg); + manager_reset_kernel_audit(m, old.set_audit); r = manager_reload_journals(m); if (r < 0) From 20f23d13b4cf075d92433be8d2eb7a6951e2ee8e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jul 2025 01:42:26 +0900 Subject: [PATCH 19/24] journald-socket: close previous socket when ForwardToSocket= is changed on reload --- src/journal/journald-config.c | 2 ++ src/journal/journald-socket.c | 17 +++++++++++++++++ src/journal/journald-socket.h | 1 + 3 files changed, 20 insertions(+) diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c index 5e6b0727516..b36f0a3bbea 100644 --- a/src/journal/journald-config.c +++ b/src/journal/journald-config.c @@ -9,6 +9,7 @@ #include "journald-config.h" #include "journald-kmsg.h" #include "journald-manager.h" +#include "journald-socket.h" #include "log.h" #include "parse-util.h" #include "proc-cmdline.h" @@ -366,6 +367,7 @@ int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_sig (void) manager_reopen_dev_kmsg(m, old.read_kmsg); manager_reset_kernel_audit(m, old.set_audit); + manager_reload_forward_socket(m, &old.forward_to_socket); r = manager_reload_journals(m); if (r < 0) diff --git a/src/journal/journald-socket.c b/src/journal/journald-socket.c index 533fa664ced..ca341dcbdda 100644 --- a/src/journal/journald-socket.c +++ b/src/journal/journald-socket.c @@ -163,3 +163,20 @@ int manager_forward_socket( return 0; } + +void manager_reload_forward_socket(Manager *m, const SocketAddress *old) { + assert(m); + assert(old); + + /* The socket is not opened yet or already closed. There is nothing we need to do now. The socket + * will be opened when necessary. */ + if (m->forward_socket_fd < 0) + return; + + if (socket_address_equal(&m->config.forward_to_socket, old)) + return; + + /* A different socket address is specified. Let's close the old socket. New socket will be opened + * when necessary. */ + m->forward_socket_fd = safe_close(m->forward_socket_fd); +} diff --git a/src/journal/journald-socket.h b/src/journal/journald-socket.h index 835c2459b81..4fae00698d4 100644 --- a/src/journal/journald-socket.h +++ b/src/journal/journald-socket.h @@ -4,3 +4,4 @@ #include "journald-forward.h" int manager_forward_socket(Manager *m, const struct iovec *iovec, size_t n, const dual_timestamp *ts, int priority); +void manager_reload_forward_socket(Manager *m, const SocketAddress *old); From c029cb45a8115dcd43b5a7224a2411267b223048 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jul 2025 01:56:45 +0900 Subject: [PATCH 20/24] journald-config: ForwardToSocket= is not supported in non-default namespace instance --- src/journal/journald-config.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c index b36f0a3bbea..c19737f4a3f 100644 --- a/src/journal/journald-config.c +++ b/src/journal/journald-config.c @@ -168,6 +168,11 @@ static void manager_adjust_configs(Manager *m) { /* copy metrics to manager */ m->system_storage.metrics = m->config.system_storage_metrics; m->runtime_storage.metrics = m->config.runtime_storage_metrics; + + if (m->config.forward_to_socket.sockaddr.sa.sa_family != AF_UNSPEC && m->namespace) { + log_debug("ForwardToSocket= is not supported in non-default namespace instance."); + m->config.forward_to_socket = (SocketAddress) {}; + } } static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { From 7b1f842d4474962947bc61a9fadcb4fab3f6ebda Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jul 2025 02:22:53 +0900 Subject: [PATCH 21/24] journald-context: update ratelimit interval and burst on reload --- src/journal/journald-config.c | 2 ++ src/journal/journald-context.c | 43 ++++++++++++++++++++++++++++++++-- src/journal/journald-context.h | 3 +++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c index c19737f4a3f..cf5b1b359c5 100644 --- a/src/journal/journald-config.c +++ b/src/journal/journald-config.c @@ -7,6 +7,7 @@ #include "daemon-util.h" #include "journald-audit.h" #include "journald-config.h" +#include "journald-context.h" #include "journald-kmsg.h" #include "journald-manager.h" #include "journald-socket.h" @@ -373,6 +374,7 @@ int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_sig (void) manager_reopen_dev_kmsg(m, old.read_kmsg); manager_reset_kernel_audit(m, old.set_audit); manager_reload_forward_socket(m, &old.forward_to_socket); + manager_refresh_client_contexts_on_reload(m, old.ratelimit_interval, old.ratelimit_burst); r = manager_reload_journals(m); if (r < 0) diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c index e6d0c70e020..1be51436130 100644 --- a/src/journal/journald-context.c +++ b/src/journal/journald-context.c @@ -190,6 +190,8 @@ static void client_context_reset(Manager *m, ClientContext *c) { c->log_ratelimit_interval = m->config.ratelimit_interval; c->log_ratelimit_burst = m->config.ratelimit_burst; + c->log_ratelimit_interval_from_unit = false; + c->log_ratelimit_burst_from_unit = false; c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns); c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns); @@ -496,7 +498,12 @@ static int client_context_read_log_ratelimit_interval(ClientContext *c) { if (r < 0) return r; - return safe_atou64(value, &c->log_ratelimit_interval); + r = safe_atou64(value, &c->log_ratelimit_interval); + if (r < 0) + return r; + + c->log_ratelimit_interval_from_unit = true; + return 0; } static int client_context_read_log_ratelimit_burst(ClientContext *c) { @@ -514,7 +521,12 @@ static int client_context_read_log_ratelimit_burst(ClientContext *c) { if (r < 0) return r; - return safe_atou(value, &c->log_ratelimit_burst); + r = safe_atou(value, &c->log_ratelimit_burst); + if (r < 0) + return r; + + c->log_ratelimit_burst_from_unit = true; + return 0; } static void client_context_really_refresh( @@ -599,6 +611,33 @@ refresh: client_context_really_refresh(m, c, ucred, label, label_size, unit_id, timestamp); } +static void client_context_refresh_on_reload(Manager *m, ClientContext *c) { + assert(m); + + if (!c) + return; + + if (!c->log_ratelimit_interval_from_unit) + c->log_ratelimit_interval = m->config.ratelimit_interval; + + if (!c->log_ratelimit_burst_from_unit) + c->log_ratelimit_burst = m->config.ratelimit_burst; +} + +void manager_refresh_client_contexts_on_reload(Manager *m, usec_t old_interval, unsigned old_burst) { + assert(m); + + if (m->config.ratelimit_interval == old_interval && m->config.ratelimit_burst == old_burst) + return; + + client_context_refresh_on_reload(m, m->my_context); + client_context_refresh_on_reload(m, m->pid1_context); + + ClientContext *c; + HASHMAP_FOREACH(c, m->client_contexts) + client_context_refresh_on_reload(m, c); +} + static void client_context_try_shrink_to(Manager *m, size_t limit) { ClientContext *c; usec_t t; diff --git a/src/journal/journald-context.h b/src/journal/journald-context.h index b728e398761..ecc7185092d 100644 --- a/src/journal/journald-context.h +++ b/src/journal/journald-context.h @@ -50,6 +50,8 @@ typedef struct ClientContext { usec_t log_ratelimit_interval; unsigned log_ratelimit_burst; + bool log_ratelimit_interval_from_unit; + bool log_ratelimit_burst_from_unit; Set *log_filter_allowed_patterns; Set *log_filter_denied_patterns; @@ -81,6 +83,7 @@ void client_context_maybe_refresh( const char *unit_id, usec_t tstamp); +void manager_refresh_client_contexts_on_reload(Manager *m, usec_t old_interval, unsigned old_burst); void client_context_acquire_default(Manager *m); void client_context_flush_all(Manager *m); void client_context_flush_regular(Manager *m); From a09dc6e5fed4258e89480ceae7d0e8822a291ce0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jul 2025 03:24:23 +0900 Subject: [PATCH 22/24] journal-file: introduce journal_metrics_equal() Currently not used, but will be used later. --- src/libsystemd/sd-journal/journal-file.c | 13 +++++++++++++ src/libsystemd/sd-journal/journal-file.h | 1 + 2 files changed, 14 insertions(+) diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index 5b988e1f95d..209c58b95a3 100644 --- a/src/libsystemd/sd-journal/journal-file.c +++ b/src/libsystemd/sd-journal/journal-file.c @@ -4578,6 +4578,19 @@ void journal_reset_metrics(JournalMetrics *m) { }; } +bool journal_metrics_equal(const JournalMetrics *x, const JournalMetrics *y) { + assert(x); + assert(y); + + return + x->max_size == y->max_size && + x->min_size == y->min_size && + x->max_use == y->max_use && + x->min_use == y->min_use && + x->keep_free == y->keep_free && + x->n_max_files == y->n_max_files; +} + int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *ret_from, usec_t *ret_to) { assert(f); assert(f->header); diff --git a/src/libsystemd/sd-journal/journal-file.h b/src/libsystemd/sd-journal/journal-file.h index d00e5f8c535..d1be26ca4c9 100644 --- a/src/libsystemd/sd-journal/journal-file.h +++ b/src/libsystemd/sd-journal/journal-file.h @@ -315,6 +315,7 @@ void journal_file_post_change(JournalFile *f); int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t); void journal_reset_metrics(JournalMetrics *m); +bool journal_metrics_equal(const JournalMetrics *x, const JournalMetrics *y); int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *ret_from, usec_t *ret_to); int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot, usec_t *ret_from, usec_t *ret_to); From 922d037f349b72139480cdded41b3fa23a3001be Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jul 2025 02:55:55 +0900 Subject: [PATCH 23/24] journald-manager: rework reopening journal files on reload Previous implementations had several issues: - user journals were not updated, - transition from volatile -> persistent storage transition was not handled. Let's make all journal files closed when at least one journal file related configurations are changed, and reopen necessary journals with requested settings. --- src/journal/journald-config.c | 6 +--- src/journal/journald-manager.c | 55 ++++++++++++++-------------------- src/journal/journald-manager.h | 2 +- 3 files changed, 25 insertions(+), 38 deletions(-) diff --git a/src/journal/journald-config.c b/src/journal/journald-config.c index cf5b1b359c5..4a514cd573f 100644 --- a/src/journal/journald-config.c +++ b/src/journal/journald-config.c @@ -363,7 +363,6 @@ static void manager_reload_config(Manager *m) { int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { Manager *m = ASSERT_PTR(userdata); - int r; (void) notify_reloading(); @@ -375,10 +374,7 @@ int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_sig manager_reset_kernel_audit(m, old.set_audit); manager_reload_forward_socket(m, &old.forward_to_socket); manager_refresh_client_contexts_on_reload(m, old.ratelimit_interval, old.ratelimit_burst); - - r = manager_reload_journals(m); - if (r < 0) - return r; + manager_reopen_journals(m, &old); log_info("Config file reloaded."); (void) sd_notify(/* unset_environment */ false, NOTIFY_READY_MESSAGE); diff --git a/src/journal/journald-manager.c b/src/journal/journald-manager.c index 494bf7c4811..bd2a05489df 100644 --- a/src/journal/journald-manager.c +++ b/src/journal/journald-manager.c @@ -2263,43 +2263,34 @@ static int manager_setup_memory_pressure(Manager *m) { return 0; } -int manager_reload_journals(Manager *m) { +void manager_reopen_journals(Manager *m, const JournalConfig *old) { assert(m); - int r; + if (m->config.storage == old->storage && + m->config.compress.enabled == old->compress.enabled && + m->config.compress.threshold_bytes == old->compress.threshold_bytes && + m->config.seal == old->seal && + m->config.sync_interval_usec == old->sync_interval_usec && + journal_metrics_equal(&m->config.system_storage_metrics, &old->system_storage_metrics) && + journal_metrics_equal(&m->config.runtime_storage_metrics, &old->runtime_storage_metrics)) + return; /* no-op */ - if (m->system_journal && IN_SET(m->config.storage, STORAGE_PERSISTENT, STORAGE_AUTO)) { - /* Current journal can continue being used. Update config values as needed. */ - r = journal_file_reload( - m->system_journal, - manager_get_file_flags(m, m->config.seal), - m->config.compress.threshold_bytes, - &m->system_storage.metrics); - if (r < 0) - return log_warning_errno(r, "Failed to reload system journal on reload, ignoring: %m"); - } else if (m->system_journal && m->config.storage == STORAGE_VOLATILE) { - /* Journal needs to be switched from system to runtime. */ - r = manager_relinquish_var(m); - if (r < 0) - return log_warning_errno(r, "Failed to relinquish to runtime journal on reload, ignoring: %m"); - } else if (m->runtime_journal && IN_SET(m->config.storage, STORAGE_PERSISTENT, STORAGE_AUTO, STORAGE_VOLATILE)) { - /* Current journal can continue being used. Update config values as needed.*/ - r = journal_file_reload( - m->runtime_journal, - manager_get_file_flags(m, /* seal */ false), - m->config.compress.threshold_bytes, - &m->runtime_storage.metrics); - if (r < 0) - return log_warning_errno(r, "Failed to reload runtime journal on reload, ignoring: %m"); - } + /* Explicitly close the runtime journal to make it reopened later by manager_system_journal_open(). + * But only when volatile (or no) storage is requested. If auto or persistent storage is requested, + * we may need to flush the runtime journal to the persistent storage, it will done through + * manager_system_journal_open(). Hence, we should not touch the runtime journal here in that case. */ + if (IN_SET(m->config.storage, STORAGE_VOLATILE, STORAGE_NONE)) + m->runtime_journal = journal_file_offline_close(m->runtime_journal); - /* If journal-related configuration, such as SystemMaxUse, SystemMaxFileSize, RuntimeMaxUse, RuntimeMaxFileSize, - * were to change, then we can vacuum for the change to take effect. For example, if pre-reload SystemMaxUse=2M, - * current usage=1.5M, and the post-reload SystemMaxUse=1M, the vacuum can shrink it to 1M. - */ + /* Close other journals unconditionally to make the new settings applied. */ + m->system_journal = journal_file_offline_close(m->system_journal); + ordered_hashmap_clear(m->user_journals); + set_clear(m->deferred_closes); + + (void) manager_system_journal_open(m, /* flush_requested = */ false, /* relinquish_requested = */ false); + + /* To make the storage related settings applied, vacuum the storage. */ manager_vacuum(m, /* verbose */ false); - - return 0; } int manager_new(Manager **ret) { diff --git a/src/journal/journald-manager.h b/src/journal/journald-manager.h index 4453fbe9adc..c103a7fc2a6 100644 --- a/src/journal/journald-manager.h +++ b/src/journal/journald-manager.h @@ -189,7 +189,7 @@ int manager_process_datagram(sd_event_source *es, int fd, uint32_t revents, void void manager_space_usage_message(Manager *m, JournalStorage *storage); int manager_start_or_stop_idle_timer(Manager *m); -int manager_reload_journals(Manager *m); +void manager_reopen_journals(Manager *m, const JournalConfig *old); int manager_map_seqnum_file(Manager *m, const char *fname, size_t size, void **ret); void manager_unmap_seqnum_file(void *p, size_t size); From f724d51dd2f4a40d4f11a16563f66e4f75e62d3c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jul 2025 04:33:54 +0900 Subject: [PATCH 24/24] journal-file: drop unused journal_file_reload() This partially reverts df5b3426f60bb626f46f93fbdacecae274c1645b. --- src/libsystemd/sd-journal/journal-file.c | 66 +++++------------------- src/libsystemd/sd-journal/journal-file.h | 6 --- 2 files changed, 14 insertions(+), 58 deletions(-) diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index 209c58b95a3..f47e94fac8b 100644 --- a/src/libsystemd/sd-journal/journal-file.c +++ b/src/libsystemd/sd-journal/journal-file.c @@ -4082,54 +4082,6 @@ static void journal_default_metrics(JournalMetrics *m, int fd, bool compact) { m->n_max_files); } -static uint64_t get_compress_threshold_bytes(uint64_t compress_threshold_bytes) { - return compress_threshold_bytes == UINT64_MAX ? - DEFAULT_COMPRESS_THRESHOLD : - MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes); -} - -static int set_metrics(JournalFile *f, JournalMetrics *metrics, JournalFile *template) { - assert(f); - int r; - - if (!journal_file_writable(f)) - return 0; - - if (metrics) { - journal_default_metrics(metrics, f->fd, JOURNAL_HEADER_COMPACT(f->header)); - f->metrics = *metrics; - } else if (template) - f->metrics = template->metrics; - - r = journal_file_refresh_header(f); - if (r < 0) - return log_error_errno(r, "Failed to refresh journal file header. Error to be handled by caller."); - - return 0; -} - -int journal_file_reload( - JournalFile *f, - JournalFileFlags file_flags, - uint64_t compress_threshold_bytes, - JournalMetrics *metrics) { - - assert(f); - assert((file_flags & ~_JOURNAL_FILE_FLAGS_ALL) == 0); - assert(metrics); - - int r; - - f->compress_threshold_bytes = get_compress_threshold_bytes(compress_threshold_bytes); - - r = set_metrics(f, metrics, /* template */ NULL); - if (r < 0) - /* Journal file header failed to be rotated. The changes may not have taken effect in this case. */ - return r; - - return 0; -} - int journal_file_open( int fd, const char *fname, @@ -4169,7 +4121,9 @@ int journal_file_open( .fd = fd, .mode = mode, .open_flags = open_flags, - .compress_threshold_bytes = get_compress_threshold_bytes(compress_threshold_bytes), + .compress_threshold_bytes = compress_threshold_bytes == UINT64_MAX ? + DEFAULT_COMPRESS_THRESHOLD : + MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes), .strict_order = FLAGS_SET(file_flags, JOURNAL_STRICT_ORDER), .newest_boot_id_prioq_idx = PRIOQ_IDX_NULL, .last_direction = _DIRECTION_INVALID, @@ -4278,9 +4232,17 @@ int journal_file_open( } #endif - r = set_metrics(f, metrics, template); - if (r < 0) - goto fail; + if (journal_file_writable(f)) { + if (metrics) { + journal_default_metrics(metrics, f->fd, JOURNAL_HEADER_COMPACT(f->header)); + f->metrics = *metrics; + } else if (template) + f->metrics = template->metrics; + + r = journal_file_refresh_header(f); + if (r < 0) + goto fail; + } #if HAVE_GCRYPT r = journal_file_hmac_setup(f); diff --git a/src/libsystemd/sd-journal/journal-file.h b/src/libsystemd/sd-journal/journal-file.h index d1be26ca4c9..f36db997449 100644 --- a/src/libsystemd/sd-journal/journal-file.h +++ b/src/libsystemd/sd-journal/journal-file.h @@ -149,12 +149,6 @@ int journal_file_open( JournalFile *template, JournalFile **ret); -int journal_file_reload( - JournalFile *f, - JournalFileFlags file_flags, - uint64_t compress_threshold_bytes, - JournalMetrics *metrics); - int journal_file_set_offline_thread_join(JournalFile *f); JournalFile* journal_file_close(JournalFile *j); int journal_file_fstat(JournalFile *f);