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/fuzz-journald-audit.c b/src/journal/fuzz-journald-audit.c index 7558900ff51..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) { @@ -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..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) { @@ -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..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" @@ -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-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 602bd59c45f..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) { @@ -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-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-util.c b/src/journal/fuzz-journald-util.c new file mode 100644 index 00000000000..cf624e6e425 --- /dev/null +++ b/src/journal/fuzz-journald-util.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-event.h" + +#include "alloc-util.h" +#include "fuzz-journald-util.h" +#include "journald-manager.h" +#include "tests.h" + +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_OK(sd_event_default(&m->event)); + + 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, + journal_process_t f) { + + if (size == 0) + return; + + _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-util.h b/src/journal/fuzz-journald-util.h new file mode 100644 index 00000000000..7eb491f36ad --- /dev/null +++ b/src/journal/fuzz-journald-util.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "journald-manager.h" + +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, + journal_process_t f); diff --git a/src/journal/fuzz-journald.c b/src/journal/fuzz-journald.c deleted file mode 100644 index 0bb4d5d5650..00000000000 --- a/src/journal/fuzz-journald.c +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "sd-event.h" - -#include "alloc-util.h" -#include "fuzz-journald.h" -#include "journald-manager.h" - -void dummy_manager_init(Manager *m, const uint8_t *buffer, size_t size) { - assert(m); - - m->config.storage = STORAGE_NONE; - assert_se(sd_event_default(&m->event) >= 0); - - if (buffer) { - m->buffer = memdup_suffix0(buffer, size); - assert_se(m->buffer); - } -} - -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; - - 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); -} diff --git a/src/journal/fuzz-journald.h b/src/journal/fuzz-journald.h deleted file mode 100644 index 0d1ebce50a7..00000000000 --- a/src/journal/fuzz-journald.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include "journald-manager.h" - -void dummy_manager_init(Manager *m, 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) -); diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c index ebf401c4c2c..6f08875ca30 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,18 @@ 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) { - /* We are listening now, try to enable audit if configured so */ - r = enable_audit(m->audit_fd, m->set_audit); - if (r < 0) - log_warning_errno(r, "Failed to issue audit enable call: %m"); - else if (m->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; } + +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 new file mode 100644 index 00000000000..4a514cd573f --- /dev/null +++ b/src/journal/journald-config.c @@ -0,0 +1,526 @@ +/* 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-audit.h" +#include "journald-config.h" +#include "journald-context.h" +#include "journald-kmsg.h" +#include "journald-manager.h" +#include "journald-socket.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) + +void journal_config_done(JournalConfig *c) { + assert(c); + + free(c->tty_path); +} + +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); + + 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); + + 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) {}; +} + +#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 + +void manager_merge_configs(Manager *m) { + assert(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); + + 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); + 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); + 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->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->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; + + 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) { + JournalConfig *c = 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 + c->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 + c->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 + c->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 + c->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 + c->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 + c->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 + c->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 + c->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 + c->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 + c->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->config_by_conf); +} + +static void manager_load_credentials(JournalConfig *c) { + _cleanup_free_ char *data = NULL; + int r; + + assert(c); + + 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"); + else { + r = socket_address_parse(&c->forward_to_socket, data); + if (r < 0) + log_debug_errno(r, "Failed to parse journal.forward_to_socket credential, ignoring: %s", data); + } + + data = mfree(data); + + 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 journal.storage credential, ignoring: %s", data); + else + c->storage = r; + } +} + +void manager_load_config(Manager *m) { + int r; + + assert(m); + + manager_load_credentials(&m->config_by_cred); + 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->config_by_cmdline, 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); + + journal_config_set_defaults(&m->config_by_conf); + 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); + + (void) notify_reloading(); + + _cleanup_(journal_config_done) JournalConfig old = TAKE_STRUCT(m->config); + + manager_reload_config(m); + + (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); + manager_reopen_journals(m, &old); + + 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; + + if (isempty(rvalue)) { + /* Empty assignment means default */ + *sz = DEFAULT_LINE_MAX; + 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; +} + +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; + + if (isempty(rvalue)) { + compress->enabled = -1; + compress->threshold_bytes = UINT64_MAX; + 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; +} + +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; + + 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; +} diff --git a/src/journal/journald-config.h b/src/journal/journald-config.h new file mode 100644 index 00000000000..c396c3a5603 --- /dev/null +++ b/src/journal/journald-config.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "journal-file.h" +#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 { + int enabled; + uint64_t threshold_bytes; +} JournalCompressOptions; + +typedef struct JournalConfig { + /* Storage=, cred: journal.storage */ + Storage storage; + /* 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 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); + +/* 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-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..1be51436130 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,10 @@ 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_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); 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-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..1e5ad5a4e15 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,18 +356,15 @@ 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..."); for (;;) { r = manager_read_dev_kmsg(m); - if (r < 0) + if (r <= 0) return r; - - if (r == 0) - break; } return 0; @@ -389,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; @@ -400,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->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) { @@ -409,7 +402,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; } @@ -432,11 +425,12 @@ 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. */ - 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); @@ -446,32 +440,53 @@ int manager_open_kernel_seqnum(Manager *m) { return 0; } -int manager_reload_dev_kmsg(Manager *m) { +void manager_close_kernel_seqnum(Manager *m) { + assert(m); + + manager_unmap_seqnum_file(m->kernel_seqnum, sizeof(*m->kernel_seqnum)); + m->kernel_seqnum = NULL; +} + +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->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 51e5f34492d..5c2eb1a5d24 100644 --- a/src/journal/journald-kmsg.h +++ b/src/journal/journald-kmsg.h @@ -5,10 +5,11 @@ 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); 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 8b10ce97106..bd2a05489df 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, @@ -160,6 +149,7 @@ static int cache_space_refresh(Manager *m, JournalStorage *storage) { int r; assert(m); + assert(storage); metrics = &storage->metrics; space = &storage->space; @@ -270,10 +260,12 @@ static void manager_add_acls(JournalFile *f, uid_t uid) { #endif } -static int manager_get_file_flags(Manager *m, bool seal) { - return (m->compress.enabled ? JOURNAL_COMPRESS : 0) | - (seal ? JOURNAL_SEAL : 0) | - JOURNAL_STRICT_ORDER; +static JournalFileFlags manager_get_file_flags(Manager *m, bool seal) { + assert(m); + + return (m->config.compress.enabled ? JOURNAL_COMPRESS : 0) | + (seal ? JOURNAL_SEAL : 0) | + JOURNAL_STRICT_ORDER; } static int manager_open_journal( @@ -303,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); @@ -314,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, @@ -386,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) { @@ -494,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) @@ -559,7 +551,6 @@ static int manager_do_rotate( bool seal, uint32_t uid) { - JournalFileFlags file_flags; int r; assert(m); @@ -567,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->config.compress.threshold_bytes, m->deferred_closes); if (r < 0) { if (*f) return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, @@ -682,10 +668,9 @@ 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->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, @@ -728,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) @@ -759,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) @@ -802,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); @@ -814,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, @@ -983,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."); @@ -1193,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 @@ -1863,140 +1847,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); @@ -2024,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; @@ -2250,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) @@ -2259,6 +2116,20 @@ static 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); @@ -2268,7 +2139,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 */ @@ -2336,7 +2207,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) @@ -2392,250 +2263,38 @@ static int manager_setup_memory_pressure(Manager *m) { return 0; } -static void manager_load_credentials(Manager *m) { - _cleanup_free_ void *data = NULL; - int r; - +void manager_reopen_journals(Manager *m, const JournalConfig *old) { 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); - } + 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 */ - data = mfree(data); + /* 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); - 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; - } -} + /* 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); -static void manager_set_defaults(Manager *m) { - assert(m); + (void) manager_system_journal_open(m, /* flush_requested = */ false, /* relinquish_requested = */ false); - 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) { - assert(m); - - int r; - - 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->seal), - m->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->compress.threshold_bytes, - &m->runtime_storage.metrics); - if (r < 0) - return log_warning_errno(r, "Failed to reload runtime journal on reload, ignoring: %m"); - } - - /* 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. - */ + /* To make the storage related settings applied, vacuum the storage. */ manager_vacuum(m, /* verbose */ false); - - 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) { +int manager_new(Manager **ret) { _cleanup_(manager_freep) Manager *m = NULL; - int r; assert(ret); @@ -2653,6 +2312,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, @@ -2666,11 +2328,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; @@ -2944,10 +2604,9 @@ 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->tty_path); free(m->cgroup_root); free(m->hostname_field); free(m->runtime_storage.path); @@ -2965,150 +2624,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); } - -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..c103a7fc2a6 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; @@ -112,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; @@ -131,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; @@ -163,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; @@ -188,15 +134,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,35 +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) -#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_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); @@ -271,5 +189,8 @@ 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); +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); +int manager_unlink_seqnum_file(Manager *m, const char *fname); 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); 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..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; @@ -91,9 +97,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/meson.build b/src/journal/meson.build index e5da683b380..c09f38dd91c 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', @@ -20,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( diff --git a/src/journal/test-journald-config.c b/src/journal/test-journald-config.c index 89e748182eb..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); \ @@ -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); @@ -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) \ diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index 5b988e1f95d..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); @@ -4578,6 +4540,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..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); @@ -315,6 +309,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);