diff --git a/man/systemd-journald.service.xml b/man/systemd-journald.service.xml
index 3a9d8049725..eb84a311338 100644
--- a/man/systemd-journald.service.xml
+++ b/man/systemd-journald.service.xml
@@ -218,6 +218,22 @@ systemd-tmpfiles --create --prefix /var/log/journal
+
+
+ SIGHUP
+
+ Upon reception of the SIGHUP process signal
+ systemd-journald will reload its configuration values
+ and update the kernel log buffer and journals to reflect the new configuration.
+ If ReadKmsg= has changed, the kernel log buffer will be flushed
+ and updated as part of the reload. The active journal (e.g., persistent, volatile)
+ will continue to be used with the updated configuration.
+ However, if the storage mode has changed from persistent to volatile
+ and the current journal in use is the persistent journal, then the active journal will
+ be switched to the volatile journal.
+
+
+
diff --git a/src/journal/fuzz-journald-audit.c b/src/journal/fuzz-journald-audit.c
index 059becc04fe..7558900ff51 100644
--- a/src/journal/fuzz-journald-audit.c
+++ b/src/journal/fuzz-journald-audit.c
@@ -9,7 +9,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
fuzz_setup_logging();
- assert_se(manager_new(&m) >= 0);
+ assert_se(manager_new(&m, /* namespace= */ NULL) >= 0);
dummy_manager_init(m, data, size);
process_audit_string(m, 0, m->buffer, size);
diff --git a/src/journal/fuzz-journald-kmsg.c b/src/journal/fuzz-journald-kmsg.c
index 9c65bcf1776..2ef36172887 100644
--- a/src/journal/fuzz-journald-kmsg.c
+++ b/src/journal/fuzz-journald-kmsg.c
@@ -12,7 +12,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
fuzz_setup_logging();
- assert_se(manager_new(&m) >= 0);
+ assert_se(manager_new(&m, /* namespace= */ NULL) >= 0);
dummy_manager_init(m, data, size);
dev_kmsg_record(m, m->buffer, size);
diff --git a/src/journal/fuzz-journald-native-fd.c b/src/journal/fuzz-journald-native-fd.c
index ea07c5a354c..356e59046fb 100644
--- a/src/journal/fuzz-journald-native-fd.c
+++ b/src/journal/fuzz-journald-native-fd.c
@@ -21,7 +21,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
fuzz_setup_logging();
- assert_se(manager_new(&m) >= 0);
+ assert_se(manager_new(&m, /* namespace= */ NULL) >= 0);
dummy_manager_init(m, NULL, 0);
sealed_fd = memfd_new_and_seal(NULL, data, size);
diff --git a/src/journal/fuzz-journald-stream.c b/src/journal/fuzz-journald-stream.c
index cc3298f0dde..602bd59c45f 100644
--- a/src/journal/fuzz-journald-stream.c
+++ b/src/journal/fuzz-journald-stream.c
@@ -23,7 +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) >= 0);
+ assert_se(manager_new(&m, /* namespace= */ NULL) >= 0);
dummy_manager_init(m, NULL, 0);
assert_se(stdout_stream_install(m, stream_fds[0], &stream) >= 0);
diff --git a/src/journal/fuzz-journald.c b/src/journal/fuzz-journald.c
index 5c2953dbdc2..0bb4d5d5650 100644
--- a/src/journal/fuzz-journald.c
+++ b/src/journal/fuzz-journald.c
@@ -9,7 +9,7 @@
void dummy_manager_init(Manager *m, const uint8_t *buffer, size_t size) {
assert(m);
- m->storage = STORAGE_NONE;
+ m->config.storage = STORAGE_NONE;
assert_se(sd_event_default(&m->event) >= 0);
if (buffer) {
@@ -33,7 +33,7 @@ void fuzz_journald_processing_function(
if (size == 0)
return;
- assert_se(manager_new(&m) >= 0);
+ 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/journald-console.c b/src/journal/journald-console.c
index 887b59b2690..73cccb4da98 100644
--- a/src/journal/journald-console.c
+++ b/src/journal/journald-console.c
@@ -52,7 +52,7 @@ void manager_forward_console(
assert(m);
assert(message);
- if (LOG_PRI(priority) > m->max_level_console)
+ if (LOG_PRI(priority) > m->config.max_level_console)
return;
/* First: timestamp */
diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf
index b2cfde3ade6..26cd08b3845 100644
--- a/src/journal/journald-gperf.gperf
+++ b/src/journal/journald-gperf.gperf
@@ -19,7 +19,7 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Journal.Storage, config_parse_storage, 0, offsetof(Manager, storage)
+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)
@@ -39,17 +39,17 @@ Journal.RuntimeKeepFree, config_parse_iec_uint64, 0, offsetof(Manager,
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, forward_to_syslog)
-Journal.ForwardToKMsg, config_parse_bool, 0, offsetof(Manager, forward_to_kmsg)
-Journal.ForwardToConsole, config_parse_bool, 0, offsetof(Manager, forward_to_console)
-Journal.ForwardToWall, config_parse_bool, 0, offsetof(Manager, forward_to_wall)
-Journal.ForwardToSocket, config_parse_forward_to_socket, 0, offsetof(Manager, forward_to_socket)
+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, max_level_store)
-Journal.MaxLevelSyslog, config_parse_log_level, 0, offsetof(Manager, max_level_syslog)
-Journal.MaxLevelKMsg, config_parse_log_level, 0, offsetof(Manager, max_level_kmsg)
-Journal.MaxLevelConsole, config_parse_log_level, 0, offsetof(Manager, max_level_console)
-Journal.MaxLevelWall, config_parse_log_level, 0, offsetof(Manager, max_level_wall)
-Journal.MaxLevelSocket, config_parse_log_level, 0, offsetof(Manager, max_level_socket)
+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)
diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c
index f8185baefc1..926cc823db5 100644
--- a/src/journal/journald-kmsg.c
+++ b/src/journal/journald-kmsg.c
@@ -46,7 +46,7 @@ void manager_forward_kmsg(
assert(priority <= 999);
assert(message);
- if (_unlikely_(LOG_PRI(priority) > m->max_level_kmsg))
+ if (_unlikely_(LOG_PRI(priority) > m->config.max_level_kmsg))
return;
if (_unlikely_(m->dev_kmsg_fd < 0))
@@ -128,7 +128,7 @@ void dev_kmsg_record(Manager *m, char *p, size_t l) {
if (r < 0 || priority < 0 || priority > 999)
return;
- if (m->forward_to_kmsg && LOG_FAC(priority) != LOG_KERN)
+ if (m->config.forward_to_kmsg && LOG_FAC(priority) != LOG_KERN)
return;
/* seqnum */
@@ -389,6 +389,10 @@ 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;
@@ -396,7 +400,7 @@ int manager_open_dev_kmsg(Manager *m) {
assert(m->dev_kmsg_fd < 0);
assert(!m->dev_kmsg_event_source);
- mode_t mode = O_CLOEXEC|O_NONBLOCK|O_NOCTTY|(m->read_kmsg ? O_RDWR : O_WRONLY);
+ mode_t mode = manager_kmsg_mode(m->read_kmsg);
_cleanup_close_ int fd = open("/dev/kmsg", mode);
if (fd < 0) {
@@ -441,3 +445,33 @@ int manager_open_kernel_seqnum(Manager *m) {
return 0;
}
+
+int manager_reload_dev_kmsg(Manager *m) {
+ int r;
+
+ assert(m);
+
+ /* Check if the fd has not yet been initialized. If so, 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;
+
+ /* Flush kmsg. */
+ r = manager_flush_dev_kmsg(m);
+ if (r < 0)
+ log_warning_errno(r, "Failed to flush /dev/kmsg on reload, ignoring: %m");
+
+ /* Set kmsg values to default. */
+ 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);
+}
diff --git a/src/journal/journald-kmsg.h b/src/journal/journald-kmsg.h
index e1f2114a8ad..51e5f34492d 100644
--- a/src/journal/journald-kmsg.h
+++ b/src/journal/journald-kmsg.h
@@ -5,6 +5,7 @@
int manager_open_dev_kmsg(Manager *m);
int manager_flush_dev_kmsg(Manager *m);
+int manager_reload_dev_kmsg(Manager *m);
void manager_forward_kmsg(Manager *m, int priority, const char *identifier, const char *message, const struct ucred *ucred);
diff --git a/src/journal/journald-manager.c b/src/journal/journald-manager.c
index 6aa732e0eec..9f44fb80b40 100644
--- a/src/journal/journald-manager.c
+++ b/src/journal/journald-manager.c
@@ -100,6 +100,7 @@
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,
@@ -269,6 +270,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 int manager_open_journal(
Manager *m,
bool reliably,
@@ -286,10 +293,7 @@ static int manager_open_journal(
assert(fname);
assert(ret);
- file_flags =
- (m->compress.enabled ? JOURNAL_COMPRESS : 0) |
- (seal ? JOURNAL_SEAL : 0) |
- JOURNAL_STRICT_ORDER;
+ file_flags = manager_get_file_flags(m, seal);
set_clear(m->deferred_closes);
@@ -363,7 +367,7 @@ static int manager_system_journal_open(
int r = 0;
if (!m->system_journal &&
- IN_SET(m->storage, STORAGE_PERSISTENT, STORAGE_AUTO) &&
+ IN_SET(m->config.storage, STORAGE_PERSISTENT, STORAGE_AUTO) &&
(flush_requested || manager_flushed_flag_is_set(m)) &&
!relinquish_requested) {
@@ -371,7 +375,7 @@ static int manager_system_journal_open(
*
* If in persistent mode: create /var/log/journal and the machine path */
- if (m->storage == STORAGE_PERSISTENT)
+ if (m->config.storage == STORAGE_PERSISTENT)
(void) mkdir_parents(m->system_storage.path, 0755);
(void) mkdir(m->system_storage.path, 0755);
@@ -408,7 +412,7 @@ static int manager_system_journal_open(
}
if (!m->runtime_journal &&
- (m->storage != STORAGE_NONE)) {
+ (m->config.storage != STORAGE_NONE)) {
fn = strjoina(m->runtime_storage.path, "/system.journal");
@@ -532,7 +536,7 @@ static JournalFile* manager_find_journal(Manager *m, uid_t uid) {
* persistent journal of any sort.
*
* Fixes https://github.com/systemd/systemd/issues/20390 */
- if (!IN_SET(m->storage, STORAGE_AUTO, STORAGE_PERSISTENT))
+ if (!IN_SET(m->config.storage, STORAGE_AUTO, STORAGE_PERSISTENT))
return NULL;
if (!uid_for_system_journal(uid)) {
@@ -1279,12 +1283,12 @@ void manager_dispatch_message(
if (n == 0)
return;
- if (LOG_PRI(priority) > m->max_level_store)
+ if (LOG_PRI(priority) > m->config.max_level_store)
return;
/* Stop early in case the information will not be stored
* in a journal. */
- if (m->storage == STORAGE_NONE)
+ if (m->config.storage == STORAGE_NONE)
return;
if (c && c->unit) {
@@ -1320,7 +1324,7 @@ int manager_flush_to_var(Manager *m, bool require_flag_file) {
assert(m);
- if (!IN_SET(m->storage, STORAGE_AUTO, STORAGE_PERSISTENT))
+ if (!IN_SET(m->config.storage, STORAGE_AUTO, STORAGE_PERSISTENT))
return 0;
if (m->namespace) /* Flushing concept does not exist for namespace instances */
@@ -1468,7 +1472,7 @@ finish:
int manager_relinquish_var(Manager *m) {
assert(m);
- if (m->storage == STORAGE_NONE)
+ if (m->config.storage == STORAGE_NONE)
return 0;
if (m->namespace) /* Concept does not exist for namespaced instances */
@@ -1859,6 +1863,10 @@ 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);
+ if (r < 0)
+ return r;
+
return 0;
}
@@ -1872,7 +1880,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0)
log_warning("Failed to parse forward to syslog switch \"%s\". Ignoring.", value);
else
- m->forward_to_syslog = r;
+ m->config_by_cmdline.forward_to_syslog = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_kmsg")) {
@@ -1880,7 +1888,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0)
log_warning("Failed to parse forward to kmsg switch \"%s\". Ignoring.", value);
else
- m->forward_to_kmsg = r;
+ m->config_by_cmdline.forward_to_kmsg = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_console")) {
@@ -1888,7 +1896,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0)
log_warning("Failed to parse forward to console switch \"%s\". Ignoring.", value);
else
- m->forward_to_console = r;
+ m->config_by_cmdline.forward_to_console = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_wall")) {
@@ -1896,7 +1904,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0)
log_warning("Failed to parse forward to wall switch \"%s\". Ignoring.", value);
else
- m->forward_to_wall = r;
+ m->config_by_cmdline.forward_to_wall = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_console")) {
@@ -1907,7 +1915,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0)
log_warning("Failed to parse max level console value \"%s\". Ignoring.", value);
else
- m->max_level_console = r;
+ m->config_by_cmdline.max_level_console = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_store")) {
@@ -1918,7 +1926,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0)
log_warning("Failed to parse max level store value \"%s\". Ignoring.", value);
else
- m->max_level_store = r;
+ m->config_by_cmdline.max_level_store = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_syslog")) {
@@ -1929,7 +1937,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0)
log_warning("Failed to parse max level syslog value \"%s\". Ignoring.", value);
else
- m->max_level_syslog = r;
+ m->config_by_cmdline.max_level_syslog = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_kmsg")) {
@@ -1940,7 +1948,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0)
log_warning("Failed to parse max level kmsg value \"%s\". Ignoring.", value);
else
- m->max_level_kmsg = r;
+ m->config_by_cmdline.max_level_kmsg = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_wall")) {
@@ -1951,7 +1959,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0)
log_warning("Failed to parse max level wall value \"%s\". Ignoring.", value);
else
- m->max_level_wall = r;
+ m->config_by_cmdline.max_level_wall = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_socket")) {
@@ -1962,7 +1970,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0)
log_warning("Failed to parse max level socket value \"%s\". Ignoring.", value);
else
- m->max_level_socket = r;
+ 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);
@@ -2394,7 +2402,7 @@ static void manager_load_credentials(Manager *m) {
if (r < 0)
log_debug_errno(r, "Failed to read credential journal.forward_to_socket, ignoring: %m");
else {
- r = socket_address_parse(&m->forward_to_socket, data);
+ 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);
}
@@ -2409,12 +2417,225 @@ static void manager_load_credentials(Manager *m) {
if (r < 0)
log_debug_errno(r, "Failed to parse storage '%s' from credential journal.storage, ignoring: %m", (char *) data);
else
- m->storage = r;
+ m->config_by_cred.storage = r;
}
}
-int manager_new(Manager **ret) {
+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 precendence 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 precendence 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.
+ */
+ 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) {
_cleanup_(manager_freep) Manager *m = NULL;
+ int r;
assert(ret);
@@ -2432,37 +2653,10 @@ int manager_new(Manager **ret) {
.notify_fd = -EBADF,
.forward_socket_fd = -EBADF,
- .compress.enabled = true,
- .compress.threshold_bytes = UINT64_MAX,
- .seal = true,
-
- .set_audit = true,
-
.watchdog_usec = USEC_INFINITY,
- .sync_interval_usec = DEFAULT_SYNC_INTERVAL_USEC,
.sync_scheduled = false,
- .ratelimit_interval = DEFAULT_RATE_LIMIT_INTERVAL,
- .ratelimit_burst = DEFAULT_RATE_LIMIT_BURST,
-
- .forward_to_wall = true,
- .forward_to_socket = { .sockaddr.sa.sa_family = AF_UNSPEC },
-
- .max_file_usec = DEFAULT_MAX_FILE_USEC,
-
- .max_level_store = LOG_DEBUG,
- .max_level_syslog = LOG_DEBUG,
- .max_level_kmsg = LOG_NOTICE,
- .max_level_console = LOG_INFO,
- .max_level_wall = LOG_EMERG,
- .max_level_socket = LOG_DEBUG,
-
- .line_max = DEFAULT_LINE_MAX,
-
- .runtime_storage.name = "Runtime Journal",
- .system_storage.name = "System Journal",
-
.kmsg_own_ratelimit = {
.interval = DEFAULT_KMSG_OWN_INTERVAL,
.burst = DEFAULT_KMSG_OWN_BURST,
@@ -2472,11 +2666,17 @@ int manager_new(Manager **ret) {
.sigrtmin18_info.memory_pressure_userdata = m,
};
+ r = manager_set_namespace(m, namespace);
+ if (r < 0)
+ return r;
+
+ manager_load_config(m);
+
*ret = TAKE_PTR(m);
return 0;
}
-int manager_init(Manager *m, const char *namespace) {
+int manager_init(Manager *m) {
const char *native_socket, *syslog_socket, *stdout_socket, *varlink_socket, *e;
_cleanup_fdset_free_ FDSet *fds = NULL;
int n, r, varlink_fd = -EBADF;
@@ -2484,33 +2684,6 @@ int manager_init(Manager *m, const char *namespace) {
assert(m);
- r = manager_set_namespace(m, namespace);
- if (r < 0)
- return r;
-
- /* By default, only read from /dev/kmsg if are the main namespace */
- m->read_kmsg = !m->namespace;
- m->storage = m->namespace ? STORAGE_PERSISTENT : STORAGE_AUTO;
-
- journal_reset_metrics(&m->system_storage.metrics);
- journal_reset_metrics(&m->runtime_storage.metrics);
-
- 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");
- }
-
- 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 "USEC_FMT",%u to 0,0",
- m->ratelimit_interval, m->ratelimit_burst);
- m->ratelimit_interval = m->ratelimit_burst = 0;
- }
-
e = getenv("RUNTIME_DIRECTORY");
if (e)
m->runtime_directory = strdup(e);
diff --git a/src/journal/journald-manager.h b/src/journal/journald-manager.h
index dda1184e25b..f1958f79bfe 100644
--- a/src/journal/journald-manager.h
+++ b/src/journal/journald-manager.h
@@ -55,6 +55,23 @@ 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;
@@ -111,12 +128,6 @@ typedef struct Manager {
bool sent_notify_ready;
bool sync_scheduled;
- bool forward_to_kmsg;
- bool forward_to_syslog;
- bool forward_to_console;
- bool forward_to_wall;
- SocketAddress forward_to_socket;
-
unsigned n_forward_syslog_missed;
usec_t last_warn_forward_syslog_missed;
@@ -130,14 +141,6 @@ typedef struct Manager {
char *tty_path;
- int max_level_store;
- int max_level_syslog;
- int max_level_kmsg;
- int max_level_console;
- int max_level_wall;
- int max_level_socket;
-
- Storage storage;
SplitMode split_mode;
MMapCache *mmap;
@@ -183,6 +186,21 @@ typedef struct Manager {
/* Pending synchronization requests with non-zero rqlen counter */
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.
+ */
+ JournalConfig config;
+ JournalConfig config_by_cred;
+ JournalConfig config_by_conf;
+ JournalConfig config_by_cmdline;
} Manager;
#define MANAGER_MACHINE_ID(s) ((s)->machine_id_field + STRLEN("_MACHINE_ID="))
@@ -209,6 +227,17 @@ 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, \
+ }
+
/* gperf lookup function */
const struct ConfigPerfItem* journald_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
@@ -225,8 +254,8 @@ 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);
-int manager_init(Manager *m, const char *namespace);
+int manager_new(Manager **ret, const char *namespace);
+int manager_init(Manager *m);
Manager* manager_free(Manager *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
void manager_full_sync(Manager *m, bool wait);
diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c
index 4288bd4f3f5..d9c6fad4f63 100644
--- a/src/journal/journald-native.c
+++ b/src/journal/journald-native.c
@@ -267,16 +267,16 @@ static int manager_process_entry(
if (r <= 0)
goto finish;
- if (m->forward_to_syslog)
+ if (m->config.forward_to_syslog)
manager_forward_syslog(m, syslog_fixup_facility(priority), identifier, message, ucred, tv);
- if (m->forward_to_kmsg)
+ if (m->config.forward_to_kmsg)
manager_forward_kmsg(m, priority, identifier, message, ucred);
- if (m->forward_to_console)
+ if (m->config.forward_to_console)
manager_forward_console(m, priority, identifier, message, ucred);
- if (m->forward_to_wall)
+ if (m->config.forward_to_wall)
manager_forward_wall(m, priority, identifier, message, ucred);
}
diff --git a/src/journal/journald-socket.c b/src/journal/journald-socket.c
index ba3c37e45b7..533fa664ced 100644
--- a/src/journal/journald-socket.c
+++ b/src/journal/journald-socket.c
@@ -23,13 +23,13 @@ static int manager_open_forward_socket(Manager *m) {
assert(m);
/* Noop if there is nothing to do. */
- if (m->forward_to_socket.sockaddr.sa.sa_family == AF_UNSPEC || m->namespace)
+ if (m->config.forward_to_socket.sockaddr.sa.sa_family == AF_UNSPEC || m->namespace)
return 0;
/* All ready, nothing to do. */
if (m->forward_socket_fd >= 0)
return 1;
- addr = &m->forward_to_socket;
+ addr = &m->config.forward_to_socket;
family = socket_address_family(addr);
@@ -86,7 +86,7 @@ int manager_forward_socket(
assert(n_iovec > 0);
assert(ts);
- if (LOG_PRI(priority) > m->max_level_socket)
+ if (LOG_PRI(priority) > m->config.max_level_socket)
return 0;
r = manager_open_forward_socket(m);
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index 45fc3257fff..cfd17fcd83d 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -251,16 +251,16 @@ static int stdout_stream_log(
if (r <= 0)
return r;
- if (s->forward_to_syslog || s->manager->forward_to_syslog)
+ if (s->forward_to_syslog || s->manager->config.forward_to_syslog)
manager_forward_syslog(s->manager, syslog_fixup_facility(priority), s->identifier, p, &s->ucred, NULL);
- if (s->forward_to_kmsg || s->manager->forward_to_kmsg)
+ if (s->forward_to_kmsg || s->manager->config.forward_to_kmsg)
manager_forward_kmsg(s->manager, priority, s->identifier, p, &s->ucred);
- if (s->forward_to_console || s->manager->forward_to_console)
+ if (s->forward_to_console || s->manager->config.forward_to_console)
manager_forward_console(s->manager, priority, s->identifier, p, &s->ucred);
- if (s->manager->forward_to_wall)
+ if (s->manager->config.forward_to_wall)
manager_forward_wall(s->manager, priority, s->identifier, p, &s->ucred);
m = N_IOVEC_META_FIELDS + 7 + client_context_extra_fields_n_iovec(s->context);
diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c
index ca204f25f5e..8e9c0e2b402 100644
--- a/src/journal/journald-syslog.c
+++ b/src/journal/journald-syslog.c
@@ -127,7 +127,7 @@ static void forward_syslog_raw(
assert(m);
assert(buffer);
- if (LOG_PRI(priority) > m->max_level_syslog)
+ if (LOG_PRI(priority) > m->config.max_level_syslog)
return;
iovec = IOVEC_MAKE((char *) buffer, buffer_len);
@@ -154,7 +154,7 @@ void manager_forward_syslog(
assert(priority <= 999);
assert(message);
- if (LOG_PRI(priority) > m->max_level_syslog)
+ if (LOG_PRI(priority) > m->config.max_level_syslog)
return;
/* First: priority field */
@@ -403,16 +403,16 @@ void manager_process_syslog_message(
syslog_parse_identifier(&msg, &identifier, &pid);
- if (m->forward_to_syslog)
+ if (m->config.forward_to_syslog)
forward_syslog_raw(m, priority, buf, raw_len, ucred, tv);
- if (m->forward_to_kmsg)
+ if (m->config.forward_to_kmsg)
manager_forward_kmsg(m, priority, identifier, msg, ucred);
- if (m->forward_to_console)
+ if (m->config.forward_to_console)
manager_forward_console(m, priority, identifier, msg, ucred);
- if (m->forward_to_wall)
+ if (m->config.forward_to_wall)
manager_forward_wall(m, priority, identifier, msg, ucred);
mm = N_IOVEC_META_FIELDS + 8 + client_context_extra_fields_n_iovec(context);
diff --git a/src/journal/journald-wall.c b/src/journal/journald-wall.c
index 375b03a6549..5c862821660 100644
--- a/src/journal/journald-wall.c
+++ b/src/journal/journald-wall.c
@@ -23,7 +23,7 @@ void manager_forward_wall(
assert(m);
assert(message);
- if (LOG_PRI(priority) > m->max_level_wall)
+ if (LOG_PRI(priority) > m->config.max_level_wall)
return;
if (ucred) {
diff --git a/src/journal/journald.c b/src/journal/journald.c
index 1fcae98f70b..95787418d22 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -52,11 +52,11 @@ static int run(int argc, char *argv[]) {
sigbus_install();
- r = manager_new(&m);
+ r = manager_new(&m, namespace);
if (r < 0)
return log_oom();
- r = manager_init(m, namespace);
+ r = manager_init(m);
if (r < 0)
return r;
diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c
index 11e251748a2..5b988e1f95d 100644
--- a/src/libsystemd/sd-journal/journal-file.c
+++ b/src/libsystemd/sd-journal/journal-file.c
@@ -4082,6 +4082,54 @@ 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,
@@ -4121,9 +4169,7 @@ int journal_file_open(
.fd = fd,
.mode = mode,
.open_flags = open_flags,
- .compress_threshold_bytes = compress_threshold_bytes == UINT64_MAX ?
- DEFAULT_COMPRESS_THRESHOLD :
- MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes),
+ .compress_threshold_bytes = get_compress_threshold_bytes(compress_threshold_bytes),
.strict_order = FLAGS_SET(file_flags, JOURNAL_STRICT_ORDER),
.newest_boot_id_prioq_idx = PRIOQ_IDX_NULL,
.last_direction = _DIRECTION_INVALID,
@@ -4232,17 +4278,9 @@ int journal_file_open(
}
#endif
- 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;
- }
+ r = set_metrics(f, metrics, template);
+ if (r < 0)
+ goto fail;
#if HAVE_GCRYPT
r = journal_file_hmac_setup(f);
diff --git a/src/libsystemd/sd-journal/journal-file.h b/src/libsystemd/sd-journal/journal-file.h
index 1fcfafbb16e..d00e5f8c535 100644
--- a/src/libsystemd/sd-journal/journal-file.h
+++ b/src/libsystemd/sd-journal/journal-file.h
@@ -149,6 +149,12 @@ 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);
diff --git a/test/units/TEST-04-JOURNAL.journal-reload.sh b/test/units/TEST-04-JOURNAL.journal-reload.sh
new file mode 100755
index 00000000000..53314a59864
--- /dev/null
+++ b/test/units/TEST-04-JOURNAL.journal-reload.sh
@@ -0,0 +1,316 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+MACHINE_ID="$(/run/systemd/journald.conf.d/reload.conf
+[Journal]
+Storage=persistent
+EOF
+
+systemctl daemon-reload
+systemctl restart systemd-journald.service
+
+# Add entries in system.
+journalctl --flush
+rand_val1=$(write_to_journal)
+verify_journals "$rand_val1" false true "Confirming test setup after flush."
+
+# Reload journald (persistent->persistent)
+systemctl reload systemd-journald.service
+
+# Reload should persist persistent journal.
+verify_journals "$rand_val1" false true "Persistent->Persistent System Reload: "
+
+rand_val1=$(write_to_journal)
+verify_journals "$rand_val1" false true "Persistent->Persistent System Post-Reload: "
+
+# Add entries in runtime
+journalctl --relinquish
+rand_val2=$(write_to_journal)
+verify_journals "$rand_val2" true false "Confirming test setup after relinquish."
+
+# Reload journald (persistent->persistent)
+systemctl reload systemd-journald.service
+
+# System journal entries should stay in system journal, runtime in runtime.
+verify_journals "$rand_val1" false true "Persistent->Persistent Runtime Reload 1: "
+verify_journals "$rand_val2" true false "Persistent->Persistent Runtime Reload 2: "
+
+# Write new message and confirm it's written to runtime.
+rand_val=$(write_to_journal)
+verify_journals "$rand_val" true false "Persistent->Persistent New Message After Reload: "
+
+# Flush and confirm that messages are written to system.
+journalctl --flush
+rand_val=$(write_to_journal)
+verify_journals "$rand_val" false true "Persistent->Volatile New Message Before Reload: "
+
+# Test persistent->volatile
+cat </run/systemd/journald.conf.d/reload.conf
+[Journal]
+Storage=volatile
+EOF
+
+# Confirm old message exists where it was written to (storage->storage).
+systemctl reload systemd-journald.service
+verify_journals "$rand_val" false true "Persistent->Volatile Reload: "
+
+# Confirm that messages are written to only runtime journal.
+rand_val=$(write_to_journal)
+verify_journals "$rand_val" true false "Persistent->Volatile New Message After Reload: "
+
+# Test volatile works and logs are NOT getting written to system journal despite flush.
+journalctl --flush
+rand_val=$(write_to_journal)
+verify_journals "$rand_val" true false "Persistent->Volatile New Message After Flush: "
+
+# Test that the new limits (e.g., RuntimeMaxUse) take effect on reload.
+# Write 1M of data to runtime journal
+max_size=1048656 # (1 * 1024 * 1024) = 1048576, but centos has a different minimum value for some reason.
+set +x
+dd if=/dev/urandom bs=1M count=5 | base64 | systemd-cat -t "$SYSLOG_ID"
+set -x
+journalctl --vacuum-size=2M
+
+total_size=$(du -sb "/run/log/journal/$MACHINE_ID" | cut -f1)
+if [ "$total_size" -lt "$max_size" ]; then
+ echo "ERROR: Journal size does not exceed RuntimeMaxUse limit"
+ cleanup
+ exit 1
+fi
+
+# Reload with RuntimeMaxUse=1M.
+cat </run/systemd/journald.conf.d/reload.conf
+[Journal]
+Storage=volatile
+RuntimeMaxUse=1M
+EOF
+
+# systemctl daemon-reload
+systemctl reload systemd-journald.service
+
+sleep 15 # Wait for RuntimeMaxUse change to take effect.
+
+# Confirm that runtime journal size shrunk to <=1M.
+total_size=$(du -sb "/run/log/journal/$MACHINE_ID" | cut -f1)
+if [ "$total_size" -gt "$max_size" ]; then
+ echo "ERROR: Journal size exceeds RuntimeMaxUse limit"
+ cleanup
+ exit 1
+fi
+
+# Prepare for volatile->persistent by getting rid of runtime limit. Otherwise, it will not write.
+cat </run/systemd/journald.conf.d/reload.conf
+[Journal]
+Storage=volatile
+EOF
+systemctl daemon-reload
+systemctl reload systemd-journald.service
+sleep 15 # Wait for RuntimeMaxUse change to take effect.
+
+journalctl --vacuum-size=1M
+sleep 5
+
+rand_val=$(write_to_journal)
+verify_journals "$rand_val" true false "Volatile->Persistent New Message Before Reload: "
+
+# Reload volatile->persistent
+cat </run/systemd/journald.conf.d/reload.conf
+[Journal]
+Storage=persistent
+EOF
+
+systemctl reload systemd-journald.service
+
+# Confirm that previous message is still in runtime journal.
+verify_journals "$rand_val" true false "Volatile->Persistent Reload: "
+
+# Confirm that new messages are written to runtime journal.
+rand_val=$(write_to_journal)
+verify_journals "$rand_val" true false "Volatile->Persistent New Message After Reload: "
+
+# Confirm that flushing writes to system journal.
+journalctl --flush
+verify_journals "$rand_val" false true "Volatile->Persistent New Message After Flush: "
+
+set +x
+dd if=/dev/urandom bs=1M count=5 | base64 | systemd-cat -t "$SYSLOG_ID"
+set -x
+
+max_size=$((2 * 1024 * 1024))
+total_size=$(du -sb "/var/log/journal/$MACHINE_ID" | cut -f1)
+if [ "$total_size" -lt "$max_size" ]; then
+ echo "ERROR: Journal size does not exceed SystemMaxUse limit"
+ cleanup
+ exit 1
+fi
+
+# Ensure reloading without limit does not interfere with SystemMaxUse test.
+systemctl reload systemd-journald.service
+total_size=$(du -sb "/var/log/journal/$MACHINE_ID" | cut -f1)
+if [ "$total_size" -lt "$max_size" ]; then
+ echo "ERROR: Journal size does not exceed SystemMaxUse limit"
+ cleanup
+ exit 1
+fi
+
+# Write to storage to prepare for SystemMaxFiles test.
+journalctl --flush
+
+num_var_journals=$(get_num_archived_journals "var")
+limit_var_journals=3
+if [ "$num_var_journals" -lt "$limit_var_journals" ]; then
+ echo "Creating archive files."
+ for (( i=0; i<=num_var_journals; i++ ))
+ do
+ echo "$TEST_MSG_PREFIX" | systemd-cat -t "$SYSLOG_ID"
+ journalctl --rotate
+ done
+
+ num_var_journals=$(get_num_archived_journals "var")
+ if [ "$num_var_journals" -lt "$limit_var_journals" ]; then
+ echo "ERROR: Number of journal files in /var/log/journal/$MACHINE_ID/ is less than $limit_var_journals"
+ cleanup
+ exit 1
+ fi
+fi
+
+# Reload with less SystemMaxUse and SystemMaxFiles.
+cat </run/systemd/journald.conf.d/reload.conf
+[Journal]
+Storage=persistent
+RuntimeMaxUse=2M
+SystemMaxUse=2M
+SystemMaxFiles=3
+EOF
+
+systemctl daemon-reload
+systemctl reload systemd-journald.service
+
+# New system journal needs to be created with the new configuration for change to take effect.
+journalctl --flush
+
+# Check SystemMaxFiles
+num_var_journals=$(get_num_archived_journals "var")
+if [ "$num_var_journals" -gt "$limit_var_journals" ]; then
+ echo "ERROR: Number of journal files in /var/log/journal/$MACHINE_ID/ is greater than $limit_var_journals"
+ cleanup
+ exit 1
+fi
+
+sleep 15
+
+# Check SystemMaxUse
+total_size=$(du -sb "/var/log/journal/$MACHINE_ID" | cut -f1)
+if [ "$total_size" -gt "$max_size" ]; then
+ echo "ERROR: Journal size exceeds SystemMaxUse limit"
+ cleanup
+ exit 1
+fi
+
+rm /run/systemd/journald.conf.d/reload.conf
+journalctl --vacuum-size=1M
+systemctl daemon-reload
+systemctl reload systemd-journald.service
diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in
index 45fd746526a..1fb080d2685 100644
--- a/units/systemd-journald.service.in
+++ b/units/systemd-journald.service.in
@@ -57,7 +57,7 @@ StandardOutput=null
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service
-Type=notify
+Type=notify-reload
PassEnvironment=TERM
{{SERVICE_WATCHDOG}}