From: Yu Watanabe Date: Sun, 13 Jul 2025 02:46:46 +0000 (+0900) Subject: journald: move several configuration related definitions to journald-config.[ch] X-Git-Tag: v258-rc1~34^2~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ef69668db20514543e369a63c40099d7e0c631a4;p=thirdparty%2Fsystemd.git journald: move several configuration related definitions to journald-config.[ch] No functional change, just refactoring and preparation for later changes. --- 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',