From d83f7e4c9218fe409ff6b0269061f27fba1c1b79 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2019 13:59:59 +0100 Subject: [PATCH 01/49] journald: why bitwise XOR when boolean != is easier to read? --- src/journal/journald-server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index f6caf0e212..305a5eff37 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -2043,7 +2043,7 @@ int server_init(Server *s) { if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - if (!!s->ratelimit_interval ^ !!s->ratelimit_burst) { + if (!!s->ratelimit_interval != !!s->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", s->ratelimit_interval, s->ratelimit_burst); s->ratelimit_interval = s->ratelimit_burst = 0; From 659a77bec6d574659e717f9be16105f7a2178bbe Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2019 14:00:40 +0100 Subject: [PATCH 02/49] journald: add missing logging for some errors --- src/journal/journald-server.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 305a5eff37..286dda2971 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -2177,11 +2177,11 @@ int server_init(Server *s) { s->ratelimit = journal_ratelimit_new(); if (!s->ratelimit) - return -ENOMEM; + return log_oom(); r = cg_get_root_path(&s->cgroup_root); if (r < 0) - return r; + return log_error_errno(r, "Failed to acquire cgroup root path: %m"); server_cache_hostname(s); server_cache_boot_id(s); @@ -2190,7 +2190,7 @@ int server_init(Server *s) { s->runtime_storage.path = path_join("/run/log/journal", SERVER_MACHINE_ID(s)); s->system_storage.path = path_join("/var/log/journal", SERVER_MACHINE_ID(s)); if (!s->runtime_storage.path || !s->system_storage.path) - return -ENOMEM; + return log_oom(); (void) server_connect_notify(s); From 99d0d05a10e4cfd8bad878cc18f1ab1e042c733e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2019 14:09:35 +0100 Subject: [PATCH 03/49] journald: use free_and_replace() where appropriate --- src/journal/journald-server.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 286dda2971..ad0d2f7349 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -741,8 +741,7 @@ static void server_cache_hostname(Server *s) { if (!x) return; - free(s->hostname_field); - s->hostname_field = x; + free_and_replace(s->hostname_field, x); } static bool shall_try_append_again(JournalFile *f, int r) { From dbac26257881ef0ad505685afd33eb4d91d3c8b9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2019 15:20:42 +0100 Subject: [PATCH 04/49] journald: fix indentation --- src/journal/journald-kmsg.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index 366298758c..0f4d7704b2 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -24,11 +24,11 @@ #include "string-util.h" void server_forward_kmsg( - Server *s, - int priority, - const char *identifier, - const char *message, - const struct ucred *ucred) { + Server *s, + int priority, + const char *identifier, + const char *message, + const struct ucred *ucred) { _cleanup_free_ char *ident_buf = NULL; struct iovec iovec[5]; From a2735a4549e89f8c1158f17d06eb26fe0ec277d4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2019 15:21:06 +0100 Subject: [PATCH 05/49] journald: don't bother with seqnum file if we don't read form /dev/kmsg anyway --- src/journal/journald-kmsg.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index 0f4d7704b2..f1e9dee38e 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -416,15 +416,17 @@ fail: } int server_open_kernel_seqnum(Server *s) { - _cleanup_close_ int fd; + _cleanup_close_ int fd = -1; uint64_t *p; int r; assert(s); - /* We store the seqnum we last read in an mmaped file. That - * way we can just use it like a variable, but it is - * persistent and automatically flushed at reboot. */ + /* We store the seqnum we last read in an mmaped file. That way we can just use it like a variable, + * but it is persistent and automatically flushed at reboot. */ + + if (!s->read_kmsg) + return 0; fd = open("/run/systemd/journal/kernel-seqnum", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); if (fd < 0) { From 2066f4fe30f512bc3bbcfc3543993c618599d24b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2019 15:21:31 +0100 Subject: [PATCH 06/49] journald: specifying _pure_ on static functions is unnecessary, compiler can figure that out on its own --- src/journal/journald-rate-limit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c index af7196c6de..fa21e7da29 100644 --- a/src/journal/journald-rate-limit.c +++ b/src/journal/journald-rate-limit.c @@ -99,7 +99,7 @@ void journal_ratelimit_free(JournalRateLimit *r) { free(r); } -_pure_ static bool journal_ratelimit_group_expired(JournalRateLimitGroup *g, usec_t ts) { +static bool journal_ratelimit_group_expired(JournalRateLimitGroup *g, usec_t ts) { unsigned i; assert(g); From a30e35f85aa9917e59183cecfcbbee986d549b46 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2019 15:22:24 +0100 Subject: [PATCH 07/49] journald: let's use unlink_and_free() where we can --- src/journal/journald-stream.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 22a70ce5a6..098ee96e79 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -17,6 +17,7 @@ #include "escape.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "io-util.h" #include "journald-console.h" #include "journald-context.h" @@ -139,7 +140,7 @@ void stdout_stream_destroy(StdoutStream *s) { } static int stdout_stream_save(StdoutStream *s) { - _cleanup_free_ char *temp_path = NULL; + _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; int r; @@ -214,6 +215,8 @@ static int stdout_stream_save(StdoutStream *s) { goto fail; } + temp_path = mfree(temp_path); + if (!s->fdstore && !s->in_notify_queue) { LIST_PREPEND(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s); s->in_notify_queue = true; @@ -229,10 +232,6 @@ static int stdout_stream_save(StdoutStream *s) { fail: (void) unlink(s->state_file); - - if (temp_path) - (void) unlink(temp_path); - return log_error_errno(r, "Failed to save stream data %s: %m", s->state_file); } From 7e7ef3bfb2832373aff615c1bbf2b246cbe795ca Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2019 15:22:40 +0100 Subject: [PATCH 08/49] journald: let's use TAKE_PTR() and TAKE_FD() where appropriate --- src/journal/journald-stream.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 098ee96e79..4f756b3b92 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -631,8 +631,7 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) { if (ret) *ret = stream; - stream = NULL; - + TAKE_PTR(stream); return 0; } @@ -675,7 +674,7 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent if (r < 0) return r; - fd = -1; + TAKE_FD(fd); return 0; } From 8548f4f09ba6b257a919d111437079720dd1c70c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2019 15:23:17 +0100 Subject: [PATCH 09/49] journald: line break overly long function header --- src/journal/journald-syslog.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index a60a259bc4..decf569dd7 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -25,7 +25,12 @@ /* Warn once every 30s if we missed syslog message */ #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC) -static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) { +static void forward_syslog_iovec( + Server *s, + const struct iovec *iovec, + unsigned n_iovec, + const struct ucred *ucred, + const struct timeval *tv) { static const union sockaddr_union sa = { .un.sun_family = AF_UNIX, From 74dd8f57593236888fab504aae0376e02726e278 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2019 15:28:53 +0100 Subject: [PATCH 10/49] journald: use structured initialization --- src/journal/journald-context.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c index cd4cfbee48..c2d8972355 100644 --- a/src/journal/journald-context.c +++ b/src/journal/journald-context.c @@ -117,23 +117,24 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) { if (r < 0) return r; - c = new0(ClientContext, 1); + c = new(ClientContext, 1); if (!c) return -ENOMEM; - c->pid = pid; - - c->uid = UID_INVALID; - c->gid = GID_INVALID; - c->auditid = AUDIT_SESSION_INVALID; - c->loginuid = UID_INVALID; - c->owner_uid = UID_INVALID; - c->lru_index = PRIOQ_IDX_NULL; - c->timestamp = USEC_INFINITY; - c->extra_fields_mtime = NSEC_INFINITY; - c->log_level_max = -1; - c->log_ratelimit_interval = s->ratelimit_interval; - c->log_ratelimit_burst = s->ratelimit_burst; + *c = (ClientContext) { + .pid = pid, + .uid = UID_INVALID, + .gid = GID_INVALID, + .auditid = AUDIT_SESSION_INVALID, + .loginuid = UID_INVALID, + .owner_uid = UID_INVALID, + .lru_index = PRIOQ_IDX_NULL, + .timestamp = USEC_INFINITY, + .extra_fields_mtime = NSEC_INFINITY, + .log_level_max = -1, + .log_ratelimit_interval = s->ratelimit_interval, + .log_ratelimit_burst = s->ratelimit_burst, + }; r = hashmap_put(s->client_contexts, PID_TO_PTR(pid), c); if (r < 0) { From b42b9479a8e25cb22de06046f3f809cdb8694442 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2019 16:11:20 +0100 Subject: [PATCH 11/49] journald: hide current storage determination in helper call --- src/journal/journald-server.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index ad0d2f7349..1440d82102 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -163,13 +163,19 @@ static void patch_min_use(JournalStorage *storage) { storage->metrics.min_use = MAX(storage->metrics.min_use, storage->space.vfs_used); } +static JournalStorage* server_current_storage(Server *s) { + assert(s); + + return s->system_journal ? &s->system_storage : &s->runtime_storage; +} + static int determine_space(Server *s, uint64_t *available, uint64_t *limit) { JournalStorage *js; int r; assert(s); - js = s->system_journal ? &s->system_storage : &s->runtime_storage; + js = server_current_storage(s); r = cache_space_refresh(s, js); if (r >= 0) { @@ -189,7 +195,7 @@ void server_space_usage_message(Server *s, JournalStorage *storage) { assert(s); if (!storage) - storage = s->system_journal ? &s->system_storage : &s->runtime_storage; + storage = server_current_storage(s); if (cache_space_refresh(s, storage) < 0) return; From 46e2348a586b46fcc21f493d18d65f7ea43467fc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2019 16:24:57 +0100 Subject: [PATCH 12/49] journald: simplify find_journal() a bit Let's use the already precalculated persistent storage path instead of deriving it again from the machine ID. --- src/journal/journald-server.c | 37 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 1440d82102..00dd77c14b 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -379,27 +379,23 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_ static JournalFile* find_journal(Server *s, uid_t uid) { _cleanup_free_ char *p = NULL; - int r; JournalFile *f; - sd_id128_t machine; + int r; assert(s); - /* A rotate that fails to create the new journal (ENOSPC) leaves the - * rotated journal as NULL. Unless we revisit opening, even after - * space is made available we'll continue to return NULL indefinitely. + /* A rotate that fails to create the new journal (ENOSPC) leaves the rotated journal as NULL. Unless + * we revisit opening, even after space is made available we'll continue to return NULL indefinitely. * - * system_journal_open() is a noop if the journals are already open, so - * we can just call it here to recover from failed rotates (or anything - * else that's left the journals as NULL). + * system_journal_open() is a noop if the journals are already open, so we can just call it here to + * recover from failed rotates (or anything else that's left the journals as NULL). * * Fixes https://github.com/systemd/systemd/issues/3968 */ (void) system_journal_open(s, false, false); - /* We split up user logs only on /var, not on /run. If the - * runtime file is open, we write to it exclusively, in order - * to guarantee proper order as soon as we flush /run to - * /var and close the runtime file. */ + /* We split up user logs only on /var, not on /run. If the runtime file is open, we write to it + * exclusively, in order to guarantee proper order as soon as we flush /run to /var and close the + * runtime file. */ if (s->runtime_journal) return s->runtime_journal; @@ -411,22 +407,14 @@ static JournalFile* find_journal(Server *s, uid_t uid) { if (f) return f; - r = sd_id128_get_machine(&machine); - if (r < 0) { - log_debug_errno(r, "Failed to determine machine ID, using system log: %m"); - return s->system_journal; - } - - if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/user-"UID_FMT".journal", - SD_ID128_FORMAT_VAL(machine), uid) < 0) { + if (asprintf(&p, "%s/user-" UID_FMT ".journal", s->system_storage.path, uid) < 0) { log_oom(); return s->system_journal; } + /* Too many open? Then let's close one (or more) */ while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) { - /* Too many open? Then let's close one */ - f = ordered_hashmap_steal_first(s->user_journals); - assert(f); + assert_se(f = ordered_hashmap_steal_first(s->user_journals)); (void) journal_file_close(f); } @@ -434,14 +422,13 @@ static JournalFile* find_journal(Server *s, uid_t uid) { if (r < 0) return s->system_journal; - server_add_acls(f, uid); - r = ordered_hashmap_put(s->user_journals, UID_TO_PTR(uid), f); if (r < 0) { (void) journal_file_close(f); return s->system_journal; } + server_add_acls(f, uid); return f; } From 4e00337b1621046e44e0419066bcaf3df16261a2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2019 16:39:18 +0100 Subject: [PATCH 13/49] journald: let's simplify rotating of offline user journals Let's just use the path that is already stored in JournalStorage, instead of generating our own. While we are at it, split out the loop into its own function. --- src/journal/journald-server.c | 200 ++++++++++++++++------------------ 1 file changed, 96 insertions(+), 104 deletions(-) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 00dd77c14b..8ec42d6dcb 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -491,36 +491,110 @@ static void server_vacuum_deferred_closes(Server *s) { } } -static int open_user_journal_directory(Server *s, DIR **ret_dir, char **ret_path) { - _cleanup_closedir_ DIR *dir = NULL; - _cleanup_free_ char *path = NULL; - sd_id128_t machine; +static int vacuum_offline_user_journals(Server *s) { + _cleanup_closedir_ DIR *d = NULL; int r; assert(s); - r = sd_id128_get_machine(&machine); - if (r < 0) - return log_error_errno(r, "Failed to determine machine ID, ignoring: %m"); + d = opendir(s->system_storage.path); + if (!d) { + if (errno == ENOENT) + return 0; - if (asprintf(&path, "/var/log/journal/" SD_ID128_FORMAT_STR "/", SD_ID128_FORMAT_VAL(machine)) < 0) - return log_oom(); + return log_error_errno(errno, "Failed to open %s: %m", s->system_storage.path); + } - dir = opendir(path); - if (!dir) - return log_error_errno(errno, "Failed to open user journal directory '%s': %m", path); + for (;;) { + _cleanup_free_ char *u = NULL, *full = NULL; + _cleanup_close_ int fd = -1; + const char *a, *b; + struct dirent *de; + JournalFile *f; + uid_t uid; - if (ret_dir) - *ret_dir = TAKE_PTR(dir); - if (ret_path) - *ret_path = TAKE_PTR(path); + errno = 0; + de = readdir_no_dot(d); + if (!de) { + if (errno != 0) + log_warning_errno(errno, "Failed to enumerate %s, ignoring: %m", s->system_storage.path); + + break; + } + + a = startswith(de->d_name, "user-"); + if (!a) + continue; + b = endswith(de->d_name, ".journal"); + if (!b) + continue; + + u = strndup(a, b-a); + if (!u) + return log_oom(); + + r = parse_uid(u, &uid); + if (r < 0) { + log_debug_errno(r, "Failed to parse UID from file name '%s', ignoring: %m", de->d_name); + continue; + } + + /* Already rotated in the above loop? i.e. is it an open user journal? */ + if (ordered_hashmap_contains(s->user_journals, UID_TO_PTR(uid))) + continue; + + full = path_join(s->system_storage.path, de->d_name); + if (!full) + return log_oom(); + + fd = openat(dirfd(d), de->d_name, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK); + if (fd < 0) { + log_full_errno(IN_SET(errno, ELOOP, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno, + "Failed to open journal file '%s' for rotation: %m", full); + continue; + } + + /* Make some room in the set of deferred close()s */ + server_vacuum_deferred_closes(s); + + /* Open the file briefly, so that we can archive it */ + r = journal_file_open(fd, + full, + O_RDWR, + 0640, + s->compress.enabled, + s->compress.threshold_bytes, + s->seal, + &s->system_storage.metrics, + s->mmap, + s->deferred_closes, + NULL, + &f); + if (r < 0) { + log_warning_errno(r, "Failed to read journal file %s for rotation, trying to move it out of the way: %m", full); + + r = journal_file_dispose(dirfd(d), de->d_name); + if (r < 0) + log_warning_errno(r, "Failed to move %s out of the way, ignoring: %m", full); + else + log_debug("Successfully moved %s out of the way.", full); + + continue; + } + + TAKE_FD(fd); /* Donated to journal_file_open() */ + + r = journal_file_archive(f); + if (r < 0) + log_debug_errno(r, "Failed to archive journal file '%s', ignoring: %m", full); + + f = journal_initiate_close(f, s->deferred_closes); + } return 0; } void server_rotate(Server *s) { - _cleanup_free_ char *path = NULL; - _cleanup_closedir_ DIR *d = NULL; JournalFile *f; Iterator i; void *k; @@ -542,92 +616,10 @@ void server_rotate(Server *s) { ordered_hashmap_remove(s->user_journals, k); } - /* Finally, also rotate all user journals we currently do not have open. (But do so only if we actually have - * access to /var, i.e. are not in the log-to-runtime-journal mode). */ - if (!s->runtime_journal && - open_user_journal_directory(s, &d, &path) >= 0) { - - struct dirent *de; - - FOREACH_DIRENT(de, d, log_warning_errno(errno, "Failed to enumerate %s, ignoring: %m", path)) { - _cleanup_free_ char *u = NULL, *full = NULL; - _cleanup_close_ int fd = -1; - const char *a, *b; - uid_t uid; - - a = startswith(de->d_name, "user-"); - if (!a) - continue; - b = endswith(de->d_name, ".journal"); - if (!b) - continue; - - u = strndup(a, b-a); - if (!u) { - log_oom(); - break; - } - - r = parse_uid(u, &uid); - if (r < 0) { - log_debug_errno(r, "Failed to parse UID from file name '%s', ignoring: %m", de->d_name); - continue; - } - - /* Already rotated in the above loop? i.e. is it an open user journal? */ - if (ordered_hashmap_contains(s->user_journals, UID_TO_PTR(uid))) - continue; - - full = strjoin(path, de->d_name); - if (!full) { - log_oom(); - break; - } - - fd = openat(dirfd(d), de->d_name, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK); - if (fd < 0) { - log_full_errno(IN_SET(errno, ELOOP, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno, - "Failed to open journal file '%s' for rotation: %m", full); - continue; - } - - /* Make some room in the set of deferred close()s */ - server_vacuum_deferred_closes(s); - - /* Open the file briefly, so that we can archive it */ - r = journal_file_open(fd, - full, - O_RDWR, - 0640, - s->compress.enabled, - s->compress.threshold_bytes, - s->seal, - &s->system_storage.metrics, - s->mmap, - s->deferred_closes, - NULL, - &f); - if (r < 0) { - log_warning_errno(r, "Failed to read journal file %s for rotation, trying to move it out of the way: %m", full); - - r = journal_file_dispose(dirfd(d), de->d_name); - if (r < 0) - log_warning_errno(r, "Failed to move %s out of the way, ignoring: %m", full); - else - log_debug("Successfully moved %s out of the way.", full); - - continue; - } - - TAKE_FD(fd); /* Donated to journal_file_open() */ - - r = journal_file_archive(f); - if (r < 0) - log_debug_errno(r, "Failed to archive journal file '%s', ignoring: %m", full); - - f = journal_initiate_close(f, s->deferred_closes); - } - } + /* Finally, also rotate all user journals we currently do not have open. (But do so only if we + * actually have access to /var, i.e. are not in the log-to-runtime-journal mode). */ + if (!s->runtime_journal) + (void) vacuum_offline_user_journals(s); server_process_deferred_closes(s); } From 4f603103736369c365f904261e71204d9f9f1ac5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 25 Nov 2019 12:50:36 +0100 Subject: [PATCH 14/49] journald: minor coding style updates --- src/journal/journald-server.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 8ec42d6dcb..f7779934ff 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -76,11 +76,18 @@ #define DEFERRED_CLOSES_MAX (4096) -static int determine_path_usage(Server *s, const char *path, uint64_t *ret_used, uint64_t *ret_free) { +static int determine_path_usage( + Server *s, + const char *path, + uint64_t *ret_used, + uint64_t *ret_free) { + _cleanup_closedir_ DIR *d = NULL; struct dirent *de; struct statvfs ss; + assert(s); + assert(path); assert(ret_used); assert(ret_free); From d6f46470f562a4a6f13a97df3b884969a1acf173 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 25 Nov 2019 12:52:42 +0100 Subject: [PATCH 15/49] journald: when create journal directories use calculated paths --- src/journal/journald-server.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index f7779934ff..6f78dffaba 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -306,14 +306,12 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_ (flush_requested || flushed_flag_is_set()) && !relinquish_requested) { - /* If in auto mode: first try to create the machine - * path, but not the prefix. + /* If in auto mode: first try to create the machine path, but not the prefix. * - * If in persistent mode: create /var/log/journal and - * the machine path */ + * If in persistent mode: create /var/log/journal and the machine path */ if (s->storage == STORAGE_PERSISTENT) - (void) mkdir_p("/var/log/journal/", 0755); + (void) mkdir_parents(s->system_storage.path, 0755); (void) mkdir(s->system_storage.path, 0755); @@ -330,12 +328,11 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_ r = 0; } - /* If the runtime journal is open, and we're post-flush, we're - * recovering from a failed system journal rotate (ENOSPC) - * for which the runtime journal was reopened. + /* If the runtime journal is open, and we're post-flush, we're recovering from a failed + * system journal rotate (ENOSPC) for which the runtime journal was reopened. * - * Perform an implicit flush to var, leaving the runtime - * journal closed, now that the system journal is back. + * Perform an implicit flush to var, leaving the runtime journal closed, now that the system + * journal is back. */ if (!flush_requested) (void) server_flush_to_var(s, true); @@ -362,12 +359,10 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_ } else { - /* OK, we really need the runtime journal, so create - * it if necessary. */ + /* OK, we really need the runtime journal, so create it if necessary. */ - (void) mkdir("/run/log", 0755); - (void) mkdir("/run/log/journal", 0755); - (void) mkdir_parents(fn, 0750); + (void) mkdir_parents(s->runtime_storage.path, 0755); + (void) mkdir(s->runtime_storage.path, 0750); r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_storage.metrics, &s->runtime_journal); if (r < 0) @@ -461,7 +456,6 @@ static int do_rotate( } server_add_acls(*f, uid); - return r; } From b1852c48c12754f7d0e7705150b7f9488f34f4a9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 25 Nov 2019 15:00:38 +0100 Subject: [PATCH 16/49] journald: allow running multiple instances of journald If we do, we operate on a separate set of logs and runtime objects The namespace is configured via argv[1]. Fixes: #12123 Fixes: #10230 #9519 (These latter two issues ask for slightly different stuff, but the usecases generally can be solved by running separate instances of journald now, hence also declaring that as "Fixes:") --- src/basic/syslog-util.c | 33 ++++++ src/basic/syslog-util.h | 2 + src/journal/journald-context.c | 4 +- src/journal/journald-kmsg.c | 6 +- src/journal/journald-native.c | 16 ++- src/journal/journald-native.h | 2 +- src/journal/journald-server.c | 197 ++++++++++++++++++++++++++------- src/journal/journald-server.h | 8 +- src/journal/journald-stream.c | 31 ++++-- src/journal/journald-stream.h | 2 +- src/journal/journald-syslog.c | 28 +++-- src/journal/journald-syslog.h | 2 +- src/journal/journald.c | 21 +++- 13 files changed, 275 insertions(+), 77 deletions(-) diff --git a/src/basic/syslog-util.c b/src/basic/syslog-util.c index 29c9ec9ac4..caeba46db4 100644 --- a/src/basic/syslog-util.c +++ b/src/basic/syslog-util.c @@ -2,10 +2,15 @@ #include +#include "sd-id128.h" + +#include "glob-util.h" #include "hexdecoct.h" #include "macro.h" +#include "path-util.h" #include "string-table.h" #include "syslog-util.h" +#include "unit-name.h" int syslog_parse_priority(const char **p, int *priority, bool with_facility) { int a = 0, b = 0, c = 0; @@ -96,3 +101,31 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG); bool log_level_is_valid(int level) { return level >= 0 && level <= LOG_DEBUG; } + +/* The maximum size for a log namespace length. This is the file name size limit 255 minus the size of a + * formatted machine ID minus a separator char */ +#define LOG_NAMESPACE_MAX (NAME_MAX - (SD_ID128_STRING_MAX - 1) - 1) + +bool log_namespace_name_valid(const char *s) { + /* Let's make sure the namespace fits in a filename that is prefixed with the machine ID and a dot + * (so that /var/log/journal/. can be created based on it). Also make sure it + * is suitable as unit instance name, and does not contain fishy characters. */ + + if (!filename_is_valid(s)) + return false; + + if (strlen(s) > LOG_NAMESPACE_MAX) + return false; + + if (!unit_instance_is_valid(s)) + return false; + + if (!string_is_safe(s)) + return false; + + /* Let's avoid globbing for now */ + if (string_is_glob(s)) + return false; + + return true; +} diff --git a/src/basic/syslog-util.h b/src/basic/syslog-util.h index 8f419e8151..998641fa01 100644 --- a/src/basic/syslog-util.h +++ b/src/basic/syslog-util.h @@ -12,3 +12,5 @@ int log_level_from_string(const char *s); bool log_level_is_valid(int level); int syslog_parse_priority(const char **p, int *priority, bool with_facility); + +bool log_namespace_name_valid(const char *s); diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c index c2d8972355..bac67036b0 100644 --- a/src/journal/journald-context.c +++ b/src/journal/journald-context.c @@ -780,7 +780,9 @@ void client_context_acquire_default(Server *s) { log_warning_errno(r, "Failed to acquire our own context, ignoring: %m"); } - if (!s->pid1_context) { + if (!s->namespace && !s->pid1_context) { + /* Acquire PID1's context, but only if we are in non-namespaced mode, since PID 1 is only + * going to log to the non-namespaced journal instance. */ r = client_context_acquire(s, 1, NULL, NULL, 0, NULL, &s->pid1_context); if (r < 0) diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index f1e9dee38e..ec404145ee 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -417,6 +417,7 @@ fail: int server_open_kernel_seqnum(Server *s) { _cleanup_close_ int fd = -1; + const char *fn; uint64_t *p; int r; @@ -428,9 +429,10 @@ int server_open_kernel_seqnum(Server *s) { if (!s->read_kmsg) return 0; - fd = open("/run/systemd/journal/kernel-seqnum", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); + fn = strjoina(s->runtime_directory, "/kernel-seqnum"); + fd = open(fn, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); if (fd < 0) { - log_error_errno(errno, "Failed to open /run/systemd/journal/kernel-seqnum, ignoring: %m"); + log_error_errno(errno, "Failed to open %s, ignoring: %m", fn); return 0; } diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index 73a96da9f7..30d988544f 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -450,17 +450,21 @@ void server_process_native_file( } } -int server_open_native_socket(Server *s) { - - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/socket", - }; +int server_open_native_socket(Server *s, const char *native_socket) { int r; assert(s); + assert(native_socket); if (s->native_fd < 0) { + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + }; + + r = sockaddr_un_set_path(&sa.un, native_socket); + if (r < 0) + return log_error_errno(r, "Unable to use namespace path %s for AF_UNIX socket: %m", native_socket); + s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (s->native_fd < 0) return log_error_errno(errno, "socket() failed: %m"); diff --git a/src/journal/journald-native.h b/src/journal/journald-native.h index 2a33ef74c5..8d06636262 100644 --- a/src/journal/journald-native.h +++ b/src/journal/journald-native.h @@ -20,4 +20,4 @@ void server_process_native_file( const char *label, size_t label_len); -int server_open_native_socket(Server *s); +int server_open_native_socket(Server *s, const char *native_socket); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 6f78dffaba..5f1faee784 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -44,6 +44,7 @@ #include "missing_audit.h" #include "mkdir.h" #include "parse-util.h" +#include "path-util.h" #include "proc-cmdline.h" #include "process-util.h" #include "rm-rf.h" @@ -293,8 +294,18 @@ static int open_journal( return r; } -static bool flushed_flag_is_set(void) { - return access("/run/systemd/journal/flushed", F_OK) >= 0; +static bool flushed_flag_is_set(Server *s) { + const char *fn; + + assert(s); + + /* We don't support the "flushing" concept for namespace instances, we assume them to always have + * access to /var */ + if (s->namespace) + return true; + + fn = strjoina(s->runtime_directory, "/flushed"); + return access(fn, F_OK) >= 0; } static int system_journal_open(Server *s, bool flush_requested, bool relinquish_requested) { @@ -303,7 +314,7 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_ if (!s->system_journal && IN_SET(s->storage, STORAGE_PERSISTENT, STORAGE_AUTO) && - (flush_requested || flushed_flag_is_set()) && + (flush_requested || flushed_flag_is_set(s)) && !relinquish_requested) { /* If in auto mode: first try to create the machine path, but not the prefix. @@ -988,6 +999,9 @@ static void dispatch_message_real( if (!isempty(s->hostname_field)) iovec[n++] = IOVEC_MAKE_STRING(s->hostname_field); + if (!isempty(s->namespace_field)) + iovec[n++] = IOVEC_MAKE_STRING(s->namespace_field); + assert(n <= m); if (s->split_mode == SPLIT_UID && c && uid_is_valid(c->uid)) @@ -1100,10 +1114,11 @@ void server_dispatch_message( } int server_flush_to_var(Server *s, bool require_flag_file) { - sd_journal *j = NULL; char ts[FORMAT_TIMESPAN_MAX]; - usec_t start; + sd_journal *j = NULL; + const char *fn; unsigned n = 0; + usec_t start; int r, k; assert(s); @@ -1111,10 +1126,13 @@ int server_flush_to_var(Server *s, bool require_flag_file) { if (!IN_SET(s->storage, STORAGE_AUTO, STORAGE_PERSISTENT)) return 0; - if (!s->runtime_journal) + if (s->namespace) /* Flushing concept does not exist for namespace instances */ return 0; - if (require_flag_file && !flushed_flag_is_set()) + if (!s->runtime_journal) /* Nothing to flush? */ + return 0; + + if (require_flag_file && !flushed_flag_is_set(s)) return 0; (void) system_journal_open(s, true, false); @@ -1122,7 +1140,7 @@ int server_flush_to_var(Server *s, bool require_flag_file) { if (!s->system_journal) return 0; - log_debug("Flushing to /var..."); + log_debug("Flushing to %s...", s->system_storage.path); start = now(CLOCK_MONOTONIC); @@ -1182,33 +1200,39 @@ finish: s->runtime_journal = journal_file_close(s->runtime_journal); if (r >= 0) - (void) rm_rf("/run/log/journal", REMOVE_ROOT); + (void) rm_rf(s->runtime_storage.path, REMOVE_ROOT); sd_journal_close(j); server_driver_message(s, 0, NULL, - LOG_MESSAGE("Time spent on flushing to /var is %s for %u entries.", + LOG_MESSAGE("Time spent on flushing to %s is %s for %u entries.", + s->system_storage.path, format_timespan(ts, sizeof(ts), now(CLOCK_MONOTONIC) - start, 0), n), NULL); - k = touch("/run/systemd/journal/flushed"); + fn = strjoina(s->runtime_directory, "/flushed"); + k = touch(fn); if (k < 0) - log_warning_errno(k, "Failed to touch /run/systemd/journal/flushed, ignoring: %m"); + log_warning_errno(k, "Failed to touch %s, ignoring: %m", fn); return r; } static int server_relinquish_var(Server *s) { + const char *fn; assert(s); if (s->storage == STORAGE_NONE) return 0; + if (s->namespace) /* Concept does not exist for namespaced instances */ + return -EOPNOTSUPP; + if (s->runtime_journal && !s->system_journal) return 0; - log_debug("Relinquishing /var..."); + log_debug("Relinquishing %s...", s->system_storage.path); (void) system_journal_open(s, false, true); @@ -1216,8 +1240,9 @@ static int server_relinquish_var(Server *s) { ordered_hashmap_clear_with_destructor(s->user_journals, journal_file_close); set_clear_with_destructor(s->deferred_closes, journal_file_close); - if (unlink("/run/systemd/journal/flushed") < 0 && errno != ENOENT) - log_warning_errno(errno, "Failed to unlink /run/systemd/journal/flushed, ignoring: %m"); + fn = strjoina(s->runtime_directory, "/flushed"); + if (unlink(fn) < 0 && errno != ENOENT) + log_warning_errno(errno, "Failed to unlink %s, ignoring: %m", fn); return 0; } @@ -1355,6 +1380,11 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo * assert(s); + if (s->namespace) { + log_error("Received SIGUSR1 signal from PID " PID_FMT ", but flushing runtime journals not supported for namespaced instances.", si->ssi_pid); + return 0; + } + log_info("Received SIGUSR1 signal from PID " PID_FMT ", as request to flush runtime journal.", si->ssi_pid); server_full_flush(s); @@ -1362,6 +1392,7 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo * } static void server_full_rotate(Server *s) { + const char *fn; int r; assert(s); @@ -1375,9 +1406,10 @@ static void server_full_rotate(Server *s) { patch_min_use(&s->runtime_storage); /* Let clients know when the most recent rotation happened. */ - r = write_timestamp_file_atomic("/run/systemd/journal/rotated", now(CLOCK_MONOTONIC)); + fn = strjoina(s->runtime_directory, "/rotated"); + r = write_timestamp_file_atomic(fn, now(CLOCK_MONOTONIC)); if (r < 0) - log_warning_errno(r, "Failed to write /run/systemd/journal/rotated, ignoring: %m"); + log_warning_errno(r, "Failed to write %s, ignoring: %m", fn); } static int dispatch_sigusr2(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { @@ -1403,6 +1435,7 @@ static int dispatch_sigterm(sd_event_source *es, const struct signalfd_siginfo * } static void server_full_sync(Server *s) { + const char *fn; int r; assert(s); @@ -1410,9 +1443,10 @@ static void server_full_sync(Server *s) { server_sync(s); /* Let clients know when the most recent sync happened. */ - r = write_timestamp_file_atomic("/run/systemd/journal/synced", now(CLOCK_MONOTONIC)); + fn = strjoina(s->runtime_directory, "/synced"); + r = write_timestamp_file_atomic(fn, now(CLOCK_MONOTONIC)); if (r < 0) - log_warning_errno(r, "Failed to write /run/systemd/journal/synced, ignoring: %m"); + log_warning_errno(r, "Failed to write %s, ignoring: %m", fn); return; } @@ -1577,8 +1611,28 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat } static int server_parse_config_file(Server *s) { + int r; + assert(s); + if (s->namespace) { + const char *namespaced; + + /* If we are running in namespace mode, load the namespace specific configuration file, and nothing else */ + namespaced = strjoina(PKGSYSCONFDIR "/journald@", s->namespace, ".conf"); + + r = config_parse( + NULL, + namespaced, NULL, + "Journal\0", + config_item_perf_lookup, journald_gperf_lookup, + CONFIG_PARSE_WARN, s); + if (r < 0) + return r; + + return 0; + } + return config_parse_many_nulstr(PKGSYSCONFDIR "/journald.conf", CONF_PATHS_NULSTR("systemd/journald.conf.d"), "Journal\0", @@ -1921,6 +1975,8 @@ static int vl_method_flush_to_var(Varlink *link, JsonVariant *parameters, Varlin if (json_variant_elements(parameters) > 0) return varlink_error_invalid_parameter(link, parameters); + if (s->namespace) + return varlink_error(link, "io.systemd.Journal.NotSupportedByNamespaces", NULL); log_info("Received client request to flush runtime journal."); server_full_flush(s); @@ -1936,14 +1992,17 @@ static int vl_method_relinquish_var(Varlink *link, JsonVariant *parameters, Varl if (json_variant_elements(parameters) > 0) return varlink_error_invalid_parameter(link, parameters); + if (s->namespace) + return varlink_error(link, "io.systemd.Journal.NotSupportedByNamespaces", NULL); - log_info("Received client request to relinquish /var access."); + log_info("Received client request to relinquish %s access.", s->system_storage.path); server_relinquish_var(s); return varlink_reply(link, NULL); } static int server_open_varlink(Server *s) { + const char *fn; int r; assert(s); @@ -1963,7 +2022,9 @@ static int server_open_varlink(Server *s) { if (r < 0) return r; - r = varlink_server_listen_address(s->varlink_server, "/run/systemd/journal/io.systemd.journal", 0600); + fn = strjoina(s->runtime_directory, "/io.systemd.journal"); + + r = varlink_server_listen_address(s->varlink_server, fn, 0600); if (r < 0) return r; @@ -1974,10 +2035,31 @@ static int server_open_varlink(Server *s) { return 0; } -int server_init(Server *s) { +static int set_namespace(Server *s, const char *namespace) { + assert(s); + + if (!namespace) + return 0; + + if (!log_namespace_name_valid(namespace)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified namespace name not valid, refusing: %s", namespace); + + s->namespace = strdup(namespace); + if (!s->namespace) + return log_oom(); + + s->namespace_field = strjoin("_NAMESPACE=", namespace); + if (!s->namespace_field) + return log_oom(); + + return 1; +} + +int server_init(Server *s, const char *namespace) { + const char *native_socket, *syslog_socket, *stdout_socket, *e; _cleanup_fdset_free_ FDSet *fds = NULL; - int n, r, fd; bool no_sockets; + int n, r, fd; assert(s); @@ -1993,7 +2075,6 @@ int server_init(Server *s) { .compress.enabled = true, .compress.threshold_bytes = (uint64_t) -1, .seal = true, - .read_kmsg = true, .watchdog_usec = USEC_INFINITY, @@ -2019,14 +2100,25 @@ int server_init(Server *s) { .system_storage.name = "System Journal", }; + r = set_namespace(s, namespace); + if (r < 0) + return r; + + /* By default, only read from /dev/kmsg if are the main namespace */ + s->read_kmsg = !s->namespace; + s->storage = s->namespace ? STORAGE_PERSISTENT : STORAGE_AUTO; + journal_reset_metrics(&s->system_storage.metrics); journal_reset_metrics(&s->runtime_storage.metrics); server_parse_config_file(s); - r = proc_cmdline_parse(parse_proc_cmdline_item, s, PROC_CMDLINE_STRIP_RD_PREFIX); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + if (!s->namespace) { + /* Parse kernel command line, but only if we are not a namespace instance */ + r = proc_cmdline_parse(parse_proc_cmdline_item, s, PROC_CMDLINE_STRIP_RD_PREFIX); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + } if (!!s->ratelimit_interval != !!s->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", @@ -2034,7 +2126,17 @@ int server_init(Server *s) { s->ratelimit_interval = s->ratelimit_burst = 0; } - (void) mkdir_p("/run/systemd/journal", 0755); + e = getenv("RUNTIME_DIRECTORY"); + if (e) + s->runtime_directory = strdup(e); + else if (s->namespace) + s->runtime_directory = strjoin("/run/systemd/journal.", s->namespace); + else + s->runtime_directory = strdup("/run/systemd/journal"); + if (!s->runtime_directory) + return log_oom(); + + (void) mkdir_p(s->runtime_directory, 0755); s->user_journals = ordered_hashmap_new(NULL); if (!s->user_journals) @@ -2056,9 +2158,13 @@ int server_init(Server *s) { if (n < 0) return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); + native_socket = strjoina(s->runtime_directory, "/socket"); + stdout_socket = strjoina(s->runtime_directory, "/stdout"); + syslog_socket = strjoina(s->runtime_directory, "/dev-log"); + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { - if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/socket", 0) > 0) { + if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, native_socket, 0) > 0) { if (s->native_fd >= 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), @@ -2066,7 +2172,7 @@ int server_init(Server *s) { s->native_fd = fd; - } else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, "/run/systemd/journal/stdout", 0) > 0) { + } else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, stdout_socket, 0) > 0) { if (s->stdout_fd >= 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), @@ -2074,8 +2180,7 @@ int server_init(Server *s) { s->stdout_fd = fd; - } else if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/dev/log", 0) > 0 || - sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/dev-log", 0) > 0) { + } else if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, syslog_socket, 0) > 0) { if (s->syslog_fd >= 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), @@ -2118,17 +2223,17 @@ int server_init(Server *s) { /* always open stdout, syslog, native, and kmsg sockets */ /* systemd-journald.socket: /run/systemd/journal/stdout */ - r = server_open_stdout_socket(s); + r = server_open_stdout_socket(s, stdout_socket); if (r < 0) return r; /* systemd-journald-dev-log.socket: /run/systemd/journal/dev-log */ - r = server_open_syslog_socket(s); + r = server_open_syslog_socket(s, syslog_socket); if (r < 0) return r; /* systemd-journald.socket: /run/systemd/journal/socket */ - r = server_open_native_socket(s); + r = server_open_native_socket(s, native_socket); if (r < 0) return r; @@ -2172,9 +2277,21 @@ int server_init(Server *s) { server_cache_boot_id(s); server_cache_machine_id(s); - s->runtime_storage.path = path_join("/run/log/journal", SERVER_MACHINE_ID(s)); - s->system_storage.path = path_join("/var/log/journal", SERVER_MACHINE_ID(s)); - if (!s->runtime_storage.path || !s->system_storage.path) + if (s->namespace) + s->runtime_storage.path = strjoin("/run/log/journal/", SERVER_MACHINE_ID(s), ".", s->namespace); + else + s->runtime_storage.path = strjoin("/run/log/journal/", SERVER_MACHINE_ID(s)); + if (!s->runtime_storage.path) + return log_oom(); + + e = getenv("LOGS_DIRECTORY"); + if (e) + s->system_storage.path = strdup(e); + else if (s->namespace) + s->system_storage.path = strjoin("/var/log/journal/", SERVER_MACHINE_ID(s), ".", s->namespace); + else + s->system_storage.path = strjoin("/var/log/journal/", SERVER_MACHINE_ID(s)); + if (!s->system_storage.path) return log_oom(); (void) server_connect_notify(s); @@ -2203,6 +2320,9 @@ void server_maybe_append_tags(Server *s) { void server_done(Server *s) { assert(s); + free(s->namespace); + free(s->namespace_field); + set_free_with_destructor(s->deferred_closes, journal_file_close); while (s->stdout_streams) @@ -2253,6 +2373,7 @@ void server_done(Server *s) { free(s->hostname_field); free(s->runtime_storage.path); free(s->system_storage.path); + free(s->runtime_directory); mmap_cache_unref(s->mmap); } diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index a609e9db06..812c1eca8d 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -60,6 +60,8 @@ typedef struct JournalStorage { } JournalStorage; struct Server { + char *namespace; + int syslog_fd; int native_fd; int stdout_fd; @@ -147,6 +149,8 @@ struct Server { char machine_id_field[sizeof("_MACHINE_ID=") + 32]; char boot_id_field[sizeof("_BOOT_ID=") + 32]; char *hostname_field; + char *namespace_field; + char *runtime_directory; /* Cached cgroup root, so that we don't have to query that all the time */ char *cgroup_root; @@ -172,7 +176,7 @@ struct Server { #define SERVER_MACHINE_ID(s) ((s)->machine_id_field + STRLEN("_MACHINE_ID=")) /* Extra fields for any log messages */ -#define N_IOVEC_META_FIELDS 22 +#define N_IOVEC_META_FIELDS 23 /* Extra fields for log messages that contain OBJECT_PID= (i.e. log about another process) */ #define N_IOVEC_OBJECT_FIELDS 18 @@ -204,7 +208,7 @@ 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 server_init(Server *s); +int server_init(Server *s, const char *namespace); void server_done(Server *s); void server_sync(Server *s); int server_vacuum(Server *s, bool verbose); diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 4f756b3b92..671e627357 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -157,11 +157,12 @@ static int stdout_stream_save(StdoutStream *s) { return log_warning_errno(errno, "Failed to stat connected stream: %m"); /* We use device and inode numbers as identifier for the stream */ - if (asprintf(&s->state_file, "/run/systemd/journal/streams/%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0) + r = asprintf(&s->state_file, "%s/streams/%lu:%lu", s->server->runtime_directory, (unsigned long) st.st_dev, (unsigned long) st.st_ino); + if (r < 0) return log_oom(); } - (void) mkdir_p("/run/systemd/journal/streams", 0755); + (void) mkdir_parents(s->state_file, 0755); r = fopen_temporary(s->state_file, &f, &temp_path); if (r < 0) @@ -692,7 +693,7 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) { assert(fname); if (!stream->state_file) { - stream->state_file = path_join("/run/systemd/journal/streams", fname); + stream->state_file = path_join(stream->server->runtime_directory, "streams", fname); if (!stream->state_file) return log_oom(); } @@ -781,14 +782,16 @@ static int stdout_stream_restore(Server *s, const char *fname, int fd) { int server_restore_streams(Server *s, FDSet *fds) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; + const char *path; int r; - d = opendir("/run/systemd/journal/streams"); + path = strjoina(s->runtime_directory, "/streams"); + d = opendir(path); if (!d) { if (errno == ENOENT) return 0; - return log_warning_errno(errno, "Failed to enumerate /run/systemd/journal/streams: %m"); + return log_warning_errno(errno, "Failed to enumerate %s: %m", path); } FOREACH_DIRENT(de, d, goto fail) { @@ -816,8 +819,7 @@ int server_restore_streams(Server *s, FDSet *fds) { /* No file descriptor? Then let's delete the state file */ log_debug("Cannot restore stream file %s", de->d_name); if (unlinkat(dirfd(d), de->d_name, 0) < 0) - log_warning_errno(errno, "Failed to remove /run/systemd/journal/streams/%s: %m", - de->d_name); + log_warning_errno(errno, "Failed to remove %s%s: %m", path, de->d_name); continue; } @@ -834,16 +836,21 @@ fail: return log_error_errno(errno, "Failed to read streams directory: %m"); } -int server_open_stdout_socket(Server *s) { - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/stdout", - }; +int server_open_stdout_socket(Server *s, const char *stdout_socket) { int r; assert(s); + assert(stdout_socket); if (s->stdout_fd < 0) { + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + }; + + r = sockaddr_un_set_path(&sa.un, stdout_socket); + if (r < 0) + return log_error_errno(r, "Unable to use namespace path %s for AF_UNIX socket: %m", stdout_socket); + s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (s->stdout_fd < 0) return log_error_errno(errno, "socket() failed: %m"); diff --git a/src/journal/journald-stream.h b/src/journal/journald-stream.h index 487376e763..7ab0016618 100644 --- a/src/journal/journald-stream.h +++ b/src/journal/journald-stream.h @@ -6,7 +6,7 @@ typedef struct StdoutStream StdoutStream; #include "fdset.h" #include "journald-server.h" -int server_open_stdout_socket(Server *s); +int server_open_stdout_socket(Server *s, const char *stdout_socket); int server_restore_streams(Server *s, FDSet *fds); void stdout_stream_free(StdoutStream *s); diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index decf569dd7..59ce983bb9 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -32,9 +32,8 @@ static void forward_syslog_iovec( const struct ucred *ucred, const struct timeval *tv) { - static const union sockaddr_union sa = { + union sockaddr_union sa = { .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/syslog", }; struct msghdr msghdr = { .msg_iov = (struct iovec *) iovec, @@ -47,11 +46,20 @@ static void forward_syslog_iovec( struct cmsghdr cmsghdr; uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; } control; + const char *j; + int r; assert(s); assert(iovec); assert(n_iovec > 0); + j = strjoina(s->runtime_directory, "/syslog"); + r = sockaddr_un_set_path(&sa.un, j); + if (r < 0) { + log_debug_errno(r, "Forwarding socket path %s too long for AF_UNIX, not forwarding: %m", j); + return; + } + if (ucred) { zero(control); msghdr.msg_control = &control; @@ -446,17 +454,21 @@ void server_process_syslog_message( server_dispatch_message(s, iovec, n, m, context, tv, priority, 0); } -int server_open_syslog_socket(Server *s) { - - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/dev-log", - }; +int server_open_syslog_socket(Server *s, const char *syslog_socket) { int r; assert(s); + assert(syslog_socket); if (s->syslog_fd < 0) { + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + }; + + r = sockaddr_un_set_path(&sa.un, syslog_socket); + if (r < 0) + return log_error_errno(r, "Unable to use namespace path %s for AF_UNIX socket: %m", syslog_socket); + s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (s->syslog_fd < 0) return log_error_errno(errno, "socket() failed: %m"); diff --git a/src/journal/journald-syslog.h b/src/journal/journald-syslog.h index 7ca5897264..5ad601001c 100644 --- a/src/journal/journald-syslog.h +++ b/src/journal/journald-syslog.h @@ -10,6 +10,6 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid); void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv); void server_process_syslog_message(Server *s, const char *buf, size_t buf_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len); -int server_open_syslog_socket(Server *s); +int server_open_syslog_socket(Server *s, const char *syslog_socket); void server_maybe_warn_forward_syslog_missed(Server *s); diff --git a/src/journal/journald.c b/src/journal/journald.c index 5e7b1dcb4a..cd3a412c2e 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -14,14 +14,17 @@ #include "sigbus.h" int main(int argc, char *argv[]) { + const char *namespace; Server server; int r; - if (argc > 1) { - log_error("This program does not take arguments."); + if (argc > 2) { + log_error("This program takes one or no arguments."); return EXIT_FAILURE; } + namespace = argc > 1 ? empty_to_null(argv[1]) : NULL; + log_set_prohibit_ipc(true); log_set_target(LOG_TARGET_AUTO); log_set_facility(LOG_SYSLOG); @@ -32,7 +35,7 @@ int main(int argc, char *argv[]) { sigbus_install(); - r = server_init(&server); + r = server_init(&server, namespace); if (r < 0) goto finish; @@ -40,7 +43,11 @@ int main(int argc, char *argv[]) { server_flush_to_var(&server, true); server_flush_dev_kmsg(&server); - log_debug("systemd-journald running as pid "PID_FMT, getpid_cached()); + if (server.namespace) + log_debug("systemd-journald running as PID "PID_FMT" for namespace '%s'.", getpid_cached(), server.namespace ?: ""); + else + log_debug("systemd-journald running as PID "PID_FMT" for the system.", getpid_cached()); + server_driver_message(&server, 0, "MESSAGE_ID=" SD_MESSAGE_JOURNAL_START_STR, LOG_MESSAGE("Journal started"), @@ -99,7 +106,11 @@ int main(int argc, char *argv[]) { server_maybe_warn_forward_syslog_missed(&server); } - log_debug("systemd-journald stopped as pid "PID_FMT, getpid_cached()); + if (server.namespace) + log_debug("systemd-journald stopped as PID "PID_FMT" for namespace '%s'.", getpid_cached(), server.namespace ?: ""); + else + log_debug("systemd-journald stopped as PID "PID_FMT" for the system.", getpid_cached()); + server_driver_message(&server, 0, "MESSAGE_ID=" SD_MESSAGE_JOURNAL_STOP_STR, LOG_MESSAGE("Journal stopped"), From 1ee51fbd7075dc066c2bb506edd6471a00758e0a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 25 Nov 2019 15:37:35 +0100 Subject: [PATCH 17/49] units: add unit files for instantiated journal daemons --- units/meson.build | 2 ++ units/systemd-journald@.service.in | 46 ++++++++++++++++++++++++++++++ units/systemd-journald@.socket | 24 ++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 units/systemd-journald@.service.in create mode 100644 units/systemd-journald@.socket diff --git a/units/meson.build b/units/meson.build index d99cafb39f..c0b69566d1 100644 --- a/units/meson.build +++ b/units/meson.build @@ -114,6 +114,7 @@ units = [ ['systemd-kexec.service', ''], ['systemd-machine-id-commit.service', '', 'sysinit.target.wants/'], + ['systemd-journald@.socket', ''], ['systemd-networkd.socket', 'ENABLE_NETWORKD'], ['systemd-poweroff.service', ''], ['systemd-reboot.service', ''], @@ -180,6 +181,7 @@ in_units = [ ['systemd-journal-upload.service', 'ENABLE_REMOTE HAVE_LIBCURL'], ['systemd-journald.service', '', 'sysinit.target.wants/'], + ['systemd-journald@.service', ''], ['systemd-localed.service', 'ENABLE_LOCALED', 'dbus-org.freedesktop.locale1.service'], ['systemd-logind.service', 'ENABLE_LOGIND', diff --git a/units/systemd-journald@.service.in b/units/systemd-journald@.service.in new file mode 100644 index 0000000000..e7274925a9 --- /dev/null +++ b/units/systemd-journald@.service.in @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Service for Namespace %i +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +Requires=systemd-journald@%i.socket +After=systemd-journald@%i.socket + +[Service] +CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE +DevicePolicy=closed +ExecStart=@rootlibexecdir@/systemd-journald %i +FileDescriptorStoreMax=4224 +Group=systemd-journal +IPAddressDeny=any +LockPersonality=yes +LogsDirectory=journal/%m.%i +LogsDirectoryMode=02755 +MemoryDenyWriteExecute=yes +NoNewPrivileges=yes +Restart=always +RestartSec=0 +RestrictAddressFamilies=AF_UNIX AF_NETLINK +RestrictNamespaces=yes +RestrictRealtime=yes +RestrictSUIDSGID=yes +RuntimeDirectory=systemd/journal.%i +RuntimeDirectoryPreserve=yes +Sockets=systemd-journald@%i.socket +StandardOutput=null +SystemCallArchitectures=native +SystemCallErrorNumber=EPERM +SystemCallFilter=@system-service +Type=notify +@SERVICE_WATCHDOG@ + +# If there are many split up journal files we need a lot of fds to access them +# all in parallel. +LimitNOFILE=@HIGH_RLIMIT_NOFILE@ diff --git a/units/systemd-journald@.socket b/units/systemd-journald@.socket new file mode 100644 index 0000000000..3badd7842a --- /dev/null +++ b/units/systemd-journald@.socket @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Socket for Namespace %i +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +StopWhenUnneeded=yes + +[Socket] +Service=systemd-journald@%i.service +ListenStream=/run/systemd/journal.%i/stdout +ListenDatagram=/run/systemd/journal.%i/socket +ListenDatagram=/run/systemd/journal.%i/dev-log +SocketMode=0666 +PassCredentials=yes +PassSecurity=yes +ReceiveBuffer=8M +SendBuffer=8M From 839d1b20147470c194765b48c1c4acd1d0602512 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 25 Nov 2019 16:22:10 +0100 Subject: [PATCH 18/49] string-util: add brief explanatory comment --- src/basic/string-util.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 61809ab065..8f812d7cbe 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -1054,6 +1054,8 @@ bool string_is_safe(const char *p) { if (!p) return false; + /* Checks if the specified string contains no quotes or control characters */ + for (t = p; *t; t++) { if (*t > 0 && *t < ' ') /* no control characters */ return false; From 91dd5f7cbe6f04f9ccbf35bf5c9633b985212394 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 25 Nov 2019 16:22:45 +0100 Subject: [PATCH 19/49] core: add new LogNamespace= execution setting --- src/core/dbus-execute.c | 27 ++++++++++++++++ src/core/execute.c | 42 ++++++++++++++++++++++--- src/core/execute.h | 2 ++ src/core/load-fragment-gperf.gperf.m4 | 1 + src/core/load-fragment.c | 45 ++++++++++++++++++++++++++- src/core/load-fragment.h | 1 + src/core/namespace.c | 23 +++++++++++++- src/core/namespace.h | 1 + src/core/unit.c | 20 +++++++++--- src/shared/bus-unit-util.c | 3 +- src/test/test-namespace.c | 1 + src/test/test-ns.c | 1 + 12 files changed, 155 insertions(+), 12 deletions(-) diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 4f412649c8..9ff3f157f5 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -766,6 +766,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("LogRateLimitIntervalUSec", "t", bus_property_get_usec, offsetof(ExecContext, log_ratelimit_interval_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LogRateLimitBurst", "u", bus_property_get_unsigned, offsetof(ExecContext, log_ratelimit_burst), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LogExtraFields", "aay", property_get_log_extra_fields, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LogNamespace", "s", NULL, offsetof(ExecContext, log_namespace), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CapabilityBoundingSet", "t", NULL, offsetof(ExecContext, capability_bounding_set), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("AmbientCapabilities", "t", NULL, offsetof(ExecContext, capability_ambient_set), SD_BUS_VTABLE_PROPERTY_CONST), @@ -1436,6 +1437,32 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "LogNamespace")) { + const char *n; + + r = sd_bus_message_read(message, "s", &n); + if (r < 0) + return r; + + if (!isempty(n) && !log_namespace_name_valid(n)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log namespace name not valid"); + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + + if (isempty(n)) { + c->log_namespace = mfree(c->log_namespace); + unit_write_settingf(u, flags, name, "%s=", name); + } else { + r = free_and_strdup(&c->log_namespace, n); + if (r < 0) + return r; + + unit_write_settingf(u, flags, name, "%s=%s", name, n); + } + } + + return 1; + } else if (streq(name, "LogExtraFields")) { size_t n = 0; diff --git a/src/core/execute.c b/src/core/execute.c index b6a4f5479b..27777c0d85 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -265,15 +265,27 @@ static int open_null_as(int flags, int nfd) { return move_fd(fd, nfd, false); } -static int connect_journal_socket(int fd, uid_t uid, gid_t gid) { - static const union sockaddr_union sa = { +static int connect_journal_socket( + int fd, + const char *log_namespace, + uid_t uid, + gid_t gid) { + + union sockaddr_union sa = { .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/stdout", }; uid_t olduid = UID_INVALID; gid_t oldgid = GID_INVALID; + const char *j; int r; + j = log_namespace ? + strjoina("/run/systemd/journal.", log_namespace, "/stdout") : + "/run/systemd/journal/stdout"; + r = sockaddr_un_set_path(&sa.un, j); + if (r < 0) + return r; + if (gid_is_valid(gid)) { oldgid = getgid(); @@ -328,7 +340,7 @@ static int connect_logger_as( if (fd < 0) return -errno; - r = connect_journal_socket(fd, uid, gid); + r = connect_journal_socket(fd, context->log_namespace, uid, gid); if (r < 0) return r; @@ -1686,7 +1698,7 @@ static int build_environment( assert(p); assert(ret); - our_env = new0(char*, 14 + _EXEC_DIRECTORY_TYPE_MAX); + our_env = new0(char*, 15 + _EXEC_DIRECTORY_TYPE_MAX); if (!our_env) return -ENOMEM; @@ -1795,6 +1807,14 @@ static int build_environment( our_env[n_env++] = x; } + if (c->log_namespace) { + x = strjoin("LOG_NAMESPACE=", c->log_namespace); + if (!x) + return -ENOMEM; + + our_env[n_env++] = x; + } + for (t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) { _cleanup_free_ char *pre = NULL, *joined = NULL; const char *n; @@ -1919,6 +1939,9 @@ static bool exec_needs_mount_namespace( !strv_isempty(context->directories[EXEC_DIRECTORY_LOGS].paths))) return true; + if (context->log_namespace) + return true; + return false; } @@ -2517,6 +2540,9 @@ static bool insist_on_sandboxing( if (!path_equal(bind_mounts[i].source, bind_mounts[i].destination)) return true; + if (context->log_namespace) + return true; + return false; } @@ -2600,6 +2626,7 @@ static int apply_mount_namespace( context->n_temporary_filesystems, tmp, var, + context->log_namespace, needs_sandboxing ? context->protect_home : PROTECT_HOME_NO, needs_sandboxing ? context->protect_system : PROTECT_SYSTEM_NO, context->mount_flags, @@ -4140,6 +4167,8 @@ void exec_context_done(ExecContext *c) { c->stdin_data_size = 0; c->network_namespace_path = mfree(c->network_namespace_path); + + c->log_namespace = mfree(c->log_namespace); } int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) { @@ -4675,6 +4704,9 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { } } + if (c->log_namespace) + fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace); + if (c->secure_bits) { _cleanup_free_ char *str = NULL; diff --git a/src/core/execute.h b/src/core/execute.h index 5aacac4a40..09c1510aaf 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -250,6 +250,8 @@ struct ExecContext { int log_level_max; + char *log_namespace; + bool private_tmp; bool private_network; bool private_devices; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 9e622597be..69abdb65ba 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -119,6 +119,7 @@ $1.ProtectKernelLogs, config_parse_bool, 0, $1.ProtectClock, config_parse_bool, 0, offsetof($1, exec_context.protect_clock) $1.ProtectControlGroups, config_parse_bool, 0, offsetof($1, exec_context.protect_control_groups) $1.NetworkNamespacePath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.network_namespace_path) +$1.LogNamespace, config_parse_log_namespace, 0, offsetof($1, exec_context) $1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network) $1.PrivateUsers, config_parse_bool, 0, offsetof($1, exec_context.private_users) $1.PrivateMounts, config_parse_bool, 0, offsetof($1, exec_context.private_mounts) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 379be9c96b..71a9873da4 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -52,10 +52,11 @@ #include "stat-util.h" #include "string-util.h" #include "strv.h" +#include "syslog-util.h" +#include "time-util.h" #include "unit-name.h" #include "unit-printf.h" #include "user-util.h" -#include "time-util.h" #include "web-util.h" static int parse_socket_protocol(const char *s) { @@ -2519,6 +2520,48 @@ int config_parse_log_extra_fields( } } +int config_parse_log_namespace( + 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) { + + _cleanup_free_ char *k = NULL; + ExecContext *c = data; + const Unit *u = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(c); + + if (isempty(rvalue)) { + c->log_namespace = mfree(c->log_namespace); + return 0; + } + + r = unit_full_printf(u, rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue); + return 0; + } + + if (!log_namespace_name_valid(k)) { + log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Specified log namespace name is not valid: %s", k); + return 0; + } + + free_and_replace(c->log_namespace, k); + return 0; +} + int config_parse_unit_condition_path( const char *unit, const char *filename, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 28613ef5b3..b6b46b2449 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -107,6 +107,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_keyring_mode); CONFIG_PARSER_PROTOTYPE(config_parse_job_timeout_sec); CONFIG_PARSER_PROTOTYPE(config_parse_job_running_timeout_sec); CONFIG_PARSER_PROTOTYPE(config_parse_log_extra_fields); +CONFIG_PARSER_PROTOTYPE(config_parse_log_namespace); CONFIG_PARSER_PROTOTYPE(config_parse_collect_mode); CONFIG_PARSER_PROTOTYPE(config_parse_pid_file); CONFIG_PARSER_PROTOTYPE(config_parse_exit_status); diff --git a/src/core/namespace.c b/src/core/namespace.c index f121cb7ca4..7785405830 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -1132,6 +1132,7 @@ static size_t namespace_calculate_mounts( size_t n_temporary_filesystems, const char* tmp_dir, const char* var_tmp_dir, + const char* log_namespace, ProtectHome protect_home, ProtectSystem protect_system) { @@ -1166,7 +1167,8 @@ static size_t namespace_calculate_mounts( (ns_info->protect_control_groups ? 1 : 0) + protect_home_cnt + protect_system_cnt + (ns_info->protect_hostname ? 2 : 0) + - (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0); + (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0) + + !!log_namespace; } static void normalize_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) { @@ -1247,6 +1249,7 @@ int setup_namespace( size_t n_temporary_filesystems, const char* tmp_dir, const char* var_tmp_dir, + const char *log_namespace, ProtectHome protect_home, ProtectSystem protect_system, unsigned long mount_flags, @@ -1323,6 +1326,7 @@ int setup_namespace( n_bind_mounts, n_temporary_filesystems, tmp_dir, var_tmp_dir, + log_namespace, protect_home, protect_system); if (n_mounts > 0) { @@ -1428,6 +1432,23 @@ int setup_namespace( }; } + if (log_namespace) { + _cleanup_free_ char *q; + + q = strjoin("/run/systemd/journal.", log_namespace); + if (!q) { + r = -ENOMEM; + goto finish; + } + + *(m++) = (MountEntry) { + .path_const = "/run/systemd/journal", + .mode = BIND_MOUNT_RECURSIVE, + .read_only = true, + .source_malloc = TAKE_PTR(q), + }; + } + assert(mounts + n_mounts == m); /* Prepend the root directory where that's necessary */ diff --git a/src/core/namespace.h b/src/core/namespace.h index 60a6abcd45..ef6c9bdc9b 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -84,6 +84,7 @@ int setup_namespace( size_t n_temporary_filesystems, const char *tmp_dir, const char *var_tmp_dir, + const char *log_namespace, ProtectHome protect_home, ProtectSystem protect_system, unsigned long mount_flags, diff --git a/src/core/unit.c b/src/core/unit.c index 8570eaefb4..4089702335 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -1059,13 +1059,25 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { !IN_SET(c->std_error, EXEC_OUTPUT_JOURNAL, EXEC_OUTPUT_JOURNAL_AND_CONSOLE, EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE, - EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE)) + EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE) && + !c->log_namespace) return 0; - /* If syslog or kernel logging is requested, make sure our own - * logging daemon is run first. */ + /* If syslog or kernel logging is requested (or log namespacing is), make sure our own logging daemon + * is run first. */ - r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE); + if (c->log_namespace) { + _cleanup_free_ char *socket_unit = NULL; + + r = unit_name_build_from_type("systemd-journald", c->log_namespace, UNIT_SOCKET, &socket_unit); + if (r < 0) + return r; + + r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, socket_unit, true, UNIT_DEPENDENCY_FILE); + if (r < 0) + return r; + } else + r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE); if (r < 0) return r; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 254007ef11..28d85944a8 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -833,7 +833,8 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con "RuntimeDirectoryPreserve", "Personality", "KeyringMode", - "NetworkNamespacePath")) + "NetworkNamespacePath", + "LogNamespace")) return bus_append_string(m, field, eq); if (STR_IN_SET(field, "IgnoreSIGPIPE", diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c index 41ea733b7e..f2bfc6c62b 100644 --- a/src/test/test-namespace.c +++ b/src/test/test-namespace.c @@ -148,6 +148,7 @@ static void test_protect_kernel_logs(void) { NULL, 0, NULL, NULL, + NULL, PROTECT_HOME_NO, PROTECT_SYSTEM_NO, 0, diff --git a/src/test/test-ns.c b/src/test/test-ns.c index e9233a1643..48a5ff9f2d 100644 --- a/src/test/test-ns.c +++ b/src/test/test-ns.c @@ -72,6 +72,7 @@ int main(int argc, char *argv[]) { &(TemporaryFileSystem) { .path = (char*) "/var", .options = (char*) "ro" }, 1, tmp_dir, var_tmp_dir, + NULL, PROTECT_HOME_NO, PROTECT_SYSTEM_NO, 0, From a6214d9643c1d8db360e35dd024e6dd7387451c0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 25 Nov 2019 18:31:44 +0100 Subject: [PATCH 20/49] journalctl: move pcre function code down We usually put the static arguments at the top of each source files, do so here too, and thus move the first code down. --- src/journal/journalctl.c | 56 +++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 17565abe21..1fcb880753 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -70,36 +70,8 @@ #include "varlink.h" #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE) - #define PROCESS_INOTIFY_INTERVAL 1024 /* Every 1,024 messages processed */ -#if HAVE_PCRE2 -DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pcre2_code_free); - -static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) { - int errorcode, r; - PCRE2_SIZE erroroffset; - pcre2_code *p; - - p = pcre2_compile((PCRE2_SPTR8) pattern, - PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL); - if (!p) { - unsigned char buf[LINE_MAX]; - - r = pcre2_get_error_message(errorcode, buf, sizeof buf); - - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Bad pattern \"%s\": %s", pattern, - r < 0 ? "unknown error" : (char *)buf); - } - - *out = p; - return 0; -} - -#endif - enum { /* Special values for arg_lines */ ARG_LINES_DEFAULT = -2, @@ -149,7 +121,6 @@ static uint64_t arg_vacuum_size = 0; static uint64_t arg_vacuum_n_files = 0; static usec_t arg_vacuum_time = 0; static char **arg_output_fields = NULL; - #if HAVE_PCRE2 static const char *arg_pattern = NULL; static pcre2_code *arg_compiled_pattern = NULL; @@ -184,6 +155,33 @@ typedef struct BootId { LIST_FIELDS(struct BootId, boot_list); } BootId; +#if HAVE_PCRE2 +DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pcre2_code_free); + +static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) { + int errorcode, r; + PCRE2_SIZE erroroffset; + pcre2_code *p; + + p = pcre2_compile((PCRE2_SPTR8) pattern, + PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL); + if (!p) { + unsigned char buf[LINE_MAX]; + + r = pcre2_get_error_message(errorcode, buf, sizeof buf); + + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Bad pattern \"%s\": %s", pattern, + r < 0 ? "unknown error" : (char *)buf); + } + + *out = p; + return 0; +} + +#endif + static int add_matches_for_device(sd_journal *j, const char *devpath) { _cleanup_(sd_device_unrefp) sd_device *device = NULL; sd_device *d = NULL; From 0491150b5cb4985dd1393b1bebe54c40c1d8b340 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 25 Nov 2019 18:42:52 +0100 Subject: [PATCH 21/49] journalctl: use log_error_errno() wherever we can --- src/journal/journalctl.c | 74 ++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 45 deletions(-) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 1fcb880753..d837630007 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -531,10 +531,8 @@ static int parse_argv(int argc, char *argv[]) { } arg_output = output_mode_from_string(optarg); - if (arg_output < 0) { - log_error("Unknown output format '%s'.", optarg); - return -EINVAL; - } + if (arg_output < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown output format '%s'.", optarg); if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_JSON_SEQ, OUTPUT_CAT)) arg_quiet = true; @@ -559,10 +557,8 @@ static int parse_argv(int argc, char *argv[]) { arg_lines = ARG_LINES_ALL; else { r = safe_atoi(optarg, &arg_lines); - if (r < 0 || arg_lines < 0) { - log_error("Failed to parse lines '%s'", optarg); - return -EINVAL; - } + if (r < 0 || arg_lines < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse lines '%s'", optarg); } } else { arg_lines = 10; @@ -708,30 +704,24 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VACUUM_SIZE: r = parse_size(optarg, 1024, &arg_vacuum_size); - if (r < 0) { - log_error("Failed to parse vacuum size: %s", optarg); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to parse vacuum size: %s", optarg); arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM; break; case ARG_VACUUM_FILES: r = safe_atou64(optarg, &arg_vacuum_n_files); - if (r < 0) { - log_error("Failed to parse vacuum files: %s", optarg); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to parse vacuum files: %s", optarg); arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM; break; case ARG_VACUUM_TIME: r = parse_sec(optarg, &arg_vacuum_time); - if (r < 0) { - log_error("Failed to parse vacuum time: %s", optarg); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to parse vacuum time: %s", optarg); arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM; break; @@ -746,7 +736,6 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_VERIFY_KEY: - arg_action = ACTION_VERIFY; r = free_and_strdup(&arg_verify_key, optarg); if (r < 0) return r; @@ -754,23 +743,23 @@ static int parse_argv(int argc, char *argv[]) { * in ps or htop output. */ memset(optarg, 'x', strlen(optarg)); + arg_action = ACTION_VERIFY; arg_merge = false; break; case ARG_INTERVAL: r = parse_sec(optarg, &arg_interval); - if (r < 0 || arg_interval <= 0) { - log_error("Failed to parse sealing key change interval: %s", optarg); - return -EINVAL; - } + if (r < 0 || arg_interval <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to parse sealing key change interval: %s", optarg); break; #else case ARG_SETUP_KEYS: case ARG_VERIFY_KEY: case ARG_INTERVAL: case ARG_FORCE: - log_error("Compiled without forward-secure sealing support."); - return -EOPNOTSUPP; + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Compiled without forward-secure sealing support."); #endif case 'p': { @@ -790,10 +779,9 @@ static int parse_argv(int argc, char *argv[]) { to = log_level_from_string(dots + 2); free(a); - if (from < 0 || to < 0) { - log_error("Failed to parse log level range %s", optarg); - return -EINVAL; - } + if (from < 0 || to < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to parse log level range %s", optarg); arg_priorities = 0; @@ -809,10 +797,9 @@ static int parse_argv(int argc, char *argv[]) { int p, i; p = log_level_from_string(optarg); - if (p < 0) { - log_error("Unknown log level %s", optarg); - return -EINVAL; - } + if (p < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Unknown log level %s", optarg); arg_priorities = 0; @@ -846,19 +833,17 @@ static int parse_argv(int argc, char *argv[]) { case 'S': r = parse_timestamp(optarg, &arg_since); - if (r < 0) { - log_error("Failed to parse timestamp: %s", optarg); - return -EINVAL; - } + if (r < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to parse timestamp: %s", optarg); arg_since_set = true; break; case 'U': r = parse_timestamp(optarg, &arg_until); - if (r < 0) { - log_error("Failed to parse timestamp: %s", optarg); - return -EINVAL; - } + if (r < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to parse timestamp: %s", optarg); arg_until_set = true; break; @@ -2134,8 +2119,7 @@ int main(int argc, char *argv[]) { if (geteuid() != 0) { /* The file descriptor returned by OpenMachineRootDirectory() will be owned by users/groups of * the container, thus we need root privileges to override them. */ - log_error("Using the --machine= switch requires root privileges."); - r = -EPERM; + r = log_error_errno(SYNTHETIC_ERRNO(EPERM), "Using the --machine= switch requires root privileges."); goto finish; } From e7238caf0cf53555e2e858150c543bd802fd28f5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 25 Nov 2019 18:44:19 +0100 Subject: [PATCH 22/49] journalctl: use automatic memory cleanup --- src/journal/journalctl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index d837630007..3ab79d11a5 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -767,7 +767,7 @@ static int parse_argv(int argc, char *argv[]) { dots = strstr(optarg, ".."); if (dots) { - char *a; + _cleanup_free_ char *a = NULL; int from, to, i; /* a range */ @@ -777,7 +777,6 @@ static int parse_argv(int argc, char *argv[]) { from = log_level_from_string(a); to = log_level_from_string(dots + 2); - free(a); if (from < 0 || to < 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), From 68312977db5e306d617e8fe9d73481a8682d0bfc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 26 Nov 2019 11:23:52 +0100 Subject: [PATCH 23/49] journal: properly mark two definitions that are deprecated with GCC attributes for that --- src/journal-remote/journal-upload.c | 8 ++++++-- src/journal/sd-journal.c | 2 +- src/systemd/_sd-common.h | 12 ++++++++++++ src/systemd/sd-journal.h | 4 ++-- src/test/generate-sym-test.py | 3 +++ 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index cc96825288..031e82587d 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -754,9 +754,13 @@ static int open_journal(sd_journal **j) { r = sd_journal_open_directory(j, arg_directory, arg_journal_type); else if (arg_file) r = sd_journal_open_files(j, (const char**) arg_file, 0); - else if (arg_machine) + else if (arg_machine) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + /* FIXME: replace with D-Bus call OpenMachineRootDirectory() so that things also work with raw disk images */ r = sd_journal_open_container(j, arg_machine, 0); - else +#pragma GCC diagnostic pop + } else r = sd_journal_open(j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type); if (r < 0) log_error_errno(r, "Failed to open %s: %m", diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 837aecdf60..093eb6619e 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -1875,7 +1875,7 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in char *p; int r; - /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in + /* This is deprecated, people should use machined's OpenMachineRootDirectory() call instead in * combination with sd_journal_open_directory_fd(). */ assert_return(machine, -EINVAL); diff --git a/src/systemd/_sd-common.h b/src/systemd/_sd-common.h index b3ee7bbc24..8158ee733e 100644 --- a/src/systemd/_sd-common.h +++ b/src/systemd/_sd-common.h @@ -45,6 +45,18 @@ typedef void (*_sd_destroy_t)(void *userdata); # define _sd_pure_ __attribute__((__pure__)) #endif +/* Note that strictly speaking __deprecated__ has been available before GCC 6. However, starting with GCC 6 + * it also works on enum values, which we are interested in. Since this is a developer-facing feature anyway + * (as opposed to build engineer-facing), let's hence conditionalize this to gcc 6, given that the developers + * are probably going to use something newer anyway. */ +#ifndef _sd_deprecated_ +# if __GNUC__ >= 6 +# define _sd_deprecated_ __attribute__((__deprecated__)) +# else +# define _sd_deprecated_ +# endif +#endif + #ifndef _SD_STRINGIFY # define _SD_XSTRINGIFY(x) #x # define _SD_STRINGIFY(x) _SD_XSTRINGIFY(x) diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h index b4bf3b9176..dbca4ebce2 100644 --- a/src/systemd/sd-journal.h +++ b/src/systemd/sd-journal.h @@ -70,7 +70,7 @@ enum { SD_JOURNAL_CURRENT_USER = 1 << 3, SD_JOURNAL_OS_ROOT = 1 << 4, - SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM /* deprecated name */ + SD_JOURNAL_SYSTEM_ONLY _sd_deprecated_ = SD_JOURNAL_SYSTEM /* deprecated name */ }; /* Wakeup event types */ @@ -85,7 +85,7 @@ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags); int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags); int sd_journal_open_files(sd_journal **ret, const char **paths, int flags); int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags); -int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); /* deprecated */ +int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) _sd_deprecated_; /* deprecated */ void sd_journal_close(sd_journal *j); int sd_journal_previous(sd_journal *j); diff --git a/src/test/generate-sym-test.py b/src/test/generate-sym-test.py index 4d358b8e34..fdb9e3ecb7 100755 --- a/src/test/generate-sym-test.py +++ b/src/test/generate-sym-test.py @@ -6,6 +6,9 @@ for header in sys.argv[2:]: print('#include "{}"'.format(header.split('/')[-1])) print(''' +/* We want to check deprecated symbols too, without complaining */ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + const void* symbols[] = {''') for line in open(sys.argv[1]): From 33ff74643e2a492e5bf29214615ba617c1984156 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 26 Nov 2019 11:24:33 +0100 Subject: [PATCH 24/49] journalctl: use an anonymous array when an array is needed I am pretty sure this makes things more readable, since the expected argument here is actually an array. --- src/journal/journalctl.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 3ab79d11a5..6a44986413 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -2104,10 +2104,9 @@ int main(int argc, char *argv[]) { r = sd_journal_open_directory(&j, arg_directory, arg_journal_type); else if (arg_root) r = sd_journal_open_directory(&j, arg_root, arg_journal_type | SD_JOURNAL_OS_ROOT); - else if (arg_file_stdin) { - int ifd = STDIN_FILENO; - r = sd_journal_open_files_fd(&j, &ifd, 1, 0); - } else if (arg_file) + else if (arg_file_stdin) + r = sd_journal_open_files_fd(&j, (int[]) { STDIN_FILENO }, 1, 0); + else if (arg_file) r = sd_journal_open_files(&j, (const char**) arg_file, 0); else if (arg_machine) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; From 2f5435a14757fd6f96f3d1faaa010a4464fb4825 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 12:08:39 +0100 Subject: [PATCH 25/49] journal: use structured initialization --- src/journal/sd-journal.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 093eb6619e..d2df5ee3b3 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -204,16 +204,17 @@ static bool same_field(const void *_a, size_t s, const void *_b, size_t t) { static Match *match_new(Match *p, MatchType t) { Match *m; - m = new0(Match, 1); + m = new(Match, 1); if (!m) return NULL; - m->type = t; + *m = (Match) { + .type = t, + .parent = p, + }; - if (p) { - m->parent = p; + if (p) LIST_PREPEND(matches, p->matches, m); - } return m; } @@ -1573,14 +1574,16 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) m = hashmap_get(j->directories_by_path, path); if (!m) { - m = new0(Directory, 1); + m = new(Directory, 1); if (!m) { r = -ENOMEM; goto fail; } - m->is_root = false; - m->path = path; + *m = (Directory) { + .is_root = false, + .path = path, + }; if (hashmap_put(j->directories_by_path, m->path, m) < 0) { free(m); From 456aa87906252169ecd3df9a44d0663f8bca5744 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 12:11:24 +0100 Subject: [PATCH 26/49] journal: allow opening journal files specific to some namespace --- src/journal/journal-internal.h | 1 + src/journal/sd-journal.c | 106 ++++++++++++++++++++++++++++----- src/libsystemd/libsystemd.sym | 1 + src/systemd/sd-journal.h | 13 ++-- 4 files changed, 102 insertions(+), 19 deletions(-) diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h index 80a69da4d8..1454df602b 100644 --- a/src/journal/journal-internal.h +++ b/src/journal/journal-internal.h @@ -69,6 +69,7 @@ struct sd_journal { char *path; char *prefix; + char *namespace; OrderedHashmap *files; IteratedCache *files_cache; diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index d2df5ee3b3..622fe04209 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -39,6 +39,7 @@ #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "syslog-util.h" #define JOURNAL_FILES_MAX 7168 @@ -1435,22 +1436,63 @@ static void remove_file_real(sd_journal *j, JournalFile *f) { static int dirname_is_machine_id(const char *fn) { sd_id128_t id, machine; + const char *e; int r; + /* Returns true if the specified directory name matches the local machine ID */ + r = sd_id128_get_machine(&machine); if (r < 0) return r; - r = sd_id128_from_string(fn, &id); + e = strchr(fn, '.'); + if (e) { + const char *k; + + /* Looks like it has a namespace suffix. Verify that. */ + if (!log_namespace_name_valid(e + 1)) + return false; + + k = strndupa(fn, e - fn); + r = sd_id128_from_string(k, &id); + } else + r = sd_id128_from_string(fn, &id); if (r < 0) return r; return sd_id128_equal(id, machine); } +static int dirname_has_namespace(const char *fn, const char *namespace) { + const char *e; + + /* Returns true if the specified directory name matches the specified namespace */ + + e = strchr(fn, '.'); + if (e) { + const char *k; + + if (!namespace) + return false; + + if (!streq(e + 1, namespace)) + return false; + + k = strndupa(fn, e - fn); + return id128_is_valid(k); + } + + if (namespace) + return false; + + return id128_is_valid(fn); +} + static bool dirent_is_journal_file(const struct dirent *de) { assert(de); + /* Returns true if the specified directory entry looks like a journal file we might be interested in */ + if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) return false; @@ -1458,13 +1500,26 @@ static bool dirent_is_journal_file(const struct dirent *de) { endswith(de->d_name, ".journal~"); } -static bool dirent_is_id128_subdir(const struct dirent *de) { +static bool dirent_is_journal_subdir(const struct dirent *de) { + const char *e, *n; assert(de); + /* returns true if the specified directory entry looks like a directory that might contain journal + * files we might be interested in, i.e. is either a 128bit ID or a 128bit ID suffixed by a + * namespace. */ + if (!IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN)) return false; - return id128_is_valid(de->d_name); + e = strchr(de->d_name, '.'); + if (!e) + return id128_is_valid(de->d_name); /* No namespace */ + + n = strndupa(de->d_name, e - de->d_name); + if (!id128_is_valid(n)) + return false; + + return log_namespace_name_valid(e + 1); } static int directory_open(sd_journal *j, const char *path, DIR **ret) { @@ -1501,7 +1556,7 @@ static void directory_enumerate(sd_journal *j, Directory *m, DIR *d) { if (dirent_is_journal_file(de)) (void) add_file_by_name(j, m->path, de->d_name); - if (m->is_root && dirent_is_id128_subdir(de)) + if (m->is_root && dirent_is_journal_subdir(de)) (void) add_directory(j, m->path, de->d_name); } @@ -1541,7 +1596,11 @@ static void directory_watch(sd_journal *j, Directory *m, int fd, uint32_t mask) } } -static int add_directory(sd_journal *j, const char *prefix, const char *dirname) { +static int add_directory( + sd_journal *j, + const char *prefix, + const char *dirname) { + _cleanup_free_ char *path = NULL; _cleanup_closedir_ DIR *d = NULL; Directory *m; @@ -1566,6 +1625,11 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run"))) return 0; + if (!(FLAGS_SET(j->flags, SD_JOURNAL_ALL_NAMESPACES) || + dirname_has_namespace(dirname, j->namespace) > 0 || + (FLAGS_SET(j->flags, SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE) && dirname_has_namespace(dirname, NULL) > 0))) + return 0; + r = directory_open(j, path, &d); if (r < 0) { log_debug_errno(r, "Failed to open directory '%s': %m", path); @@ -1806,7 +1870,7 @@ static int allocate_inotify(sd_journal *j) { return hashmap_ensure_allocated(&j->directories_by_wd, NULL); } -static sd_journal *journal_new(int flags, const char *path) { +static sd_journal *journal_new(int flags, const char *path, const char *namespace) { _cleanup_(sd_journal_closep) sd_journal *j = NULL; j = new0(sd_journal, 1); @@ -1832,6 +1896,12 @@ static sd_journal *journal_new(int flags, const char *path) { j->path = t; } + if (namespace) { + j->namespace = strdup(namespace); + if (!j->namespace) + return NULL; + } + j->files = ordered_hashmap_new(&path_hash_ops); if (!j->files) return NULL; @@ -1848,16 +1918,19 @@ static sd_journal *journal_new(int flags, const char *path) { #define OPEN_ALLOWED_FLAGS \ (SD_JOURNAL_LOCAL_ONLY | \ SD_JOURNAL_RUNTIME_ONLY | \ - SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER) + SD_JOURNAL_SYSTEM | \ + SD_JOURNAL_CURRENT_USER | \ + SD_JOURNAL_ALL_NAMESPACES | \ + SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE) -_public_ int sd_journal_open(sd_journal **ret, int flags) { +_public_ int sd_journal_open_namespace(sd_journal **ret, const char *namespace, int flags) { _cleanup_(sd_journal_closep) sd_journal *j = NULL; int r; assert_return(ret, -EINVAL); assert_return((flags & ~OPEN_ALLOWED_FLAGS) == 0, -EINVAL); - j = journal_new(flags, NULL); + j = journal_new(flags, NULL, namespace); if (!j) return -ENOMEM; @@ -1869,6 +1942,10 @@ _public_ int sd_journal_open(sd_journal **ret, int flags) { return 0; } +_public_ int sd_journal_open(sd_journal **ret, int flags) { + return sd_journal_open_namespace(ret, NULL, flags); +} + #define OPEN_CONTAINER_ALLOWED_FLAGS \ (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM) @@ -1900,7 +1977,7 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in if (!streq_ptr(class, "container")) return -EIO; - j = journal_new(flags, root); + j = journal_new(flags, root, NULL); if (!j) return -ENOMEM; @@ -1924,7 +2001,7 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f assert_return(path, -EINVAL); assert_return((flags & ~OPEN_DIRECTORY_ALLOWED_FLAGS) == 0, -EINVAL); - j = journal_new(flags, path); + j = journal_new(flags, path, NULL); if (!j) return -ENOMEM; @@ -1947,7 +2024,7 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla assert_return(ret, -EINVAL); assert_return(flags == 0, -EINVAL); - j = journal_new(flags, NULL); + j = journal_new(flags, NULL, NULL); if (!j) return -ENOMEM; @@ -1982,7 +2059,7 @@ _public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) { if (!S_ISDIR(st.st_mode)) return -EBADFD; - j = journal_new(flags, NULL); + j = journal_new(flags, NULL, NULL); if (!j) return -ENOMEM; @@ -2010,7 +2087,7 @@ _public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fd assert_return(n_fds > 0, -EBADF); assert_return(flags == 0, -EINVAL); - j = journal_new(flags, NULL); + j = journal_new(flags, NULL, NULL); if (!j) return -ENOMEM; @@ -2082,6 +2159,7 @@ _public_ void sd_journal_close(sd_journal *j) { free(j->path); free(j->prefix); + free(j->namespace); free(j->unique_field); free(j->fields_buffer); free(j); diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index a3dd9f68b7..17a39e1d0f 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -693,4 +693,5 @@ global: sd_event_source_get_child_process_own; sd_event_source_set_child_process_own; sd_event_source_send_child_signal; + sd_journal_open_namespace; } LIBSYSTEMD_243; diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h index dbca4ebce2..29ff40ab26 100644 --- a/src/systemd/sd-journal.h +++ b/src/systemd/sd-journal.h @@ -64,11 +64,13 @@ typedef struct sd_journal sd_journal; /* Open flags */ enum { - SD_JOURNAL_LOCAL_ONLY = 1 << 0, - SD_JOURNAL_RUNTIME_ONLY = 1 << 1, - SD_JOURNAL_SYSTEM = 1 << 2, - SD_JOURNAL_CURRENT_USER = 1 << 3, - SD_JOURNAL_OS_ROOT = 1 << 4, + SD_JOURNAL_LOCAL_ONLY = 1 << 0, + SD_JOURNAL_RUNTIME_ONLY = 1 << 1, + SD_JOURNAL_SYSTEM = 1 << 2, + SD_JOURNAL_CURRENT_USER = 1 << 3, + SD_JOURNAL_OS_ROOT = 1 << 4, + SD_JOURNAL_ALL_NAMESPACES = 1 << 5, /* Show all namespaces, not just the default or specified one */ + SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE = 1 << 6, /* Show default namespace in addition to specified one */ SD_JOURNAL_SYSTEM_ONLY _sd_deprecated_ = SD_JOURNAL_SYSTEM /* deprecated name */ }; @@ -81,6 +83,7 @@ enum { }; int sd_journal_open(sd_journal **ret, int flags); +int sd_journal_open_namespace(sd_journal **ret, const char *name_space, int flags); int sd_journal_open_directory(sd_journal **ret, const char *path, int flags); int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags); int sd_journal_open_files(sd_journal **ret, const char **paths, int flags); From 31e99dd2cc37556a7145ecee2ca008a29d8ce060 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 12:13:36 +0100 Subject: [PATCH 27/49] journal: make constant argument actually 'const' --- src/journal/sd-journal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 622fe04209..bced8af3e3 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -2561,7 +2561,7 @@ static void process_q_overflow(sd_journal *j) { log_debug("Reiteration complete."); } -static void process_inotify_event(sd_journal *j, struct inotify_event *e) { +static void process_inotify_event(sd_journal *j, const struct inotify_event *e) { Directory *d; assert(j); From 6b25db87a180dd03180aa383c1c5d0dba893961d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 25 Nov 2019 18:49:52 +0100 Subject: [PATCH 28/49] journalctl: add new --namespace= switch for showing logs for namespace --- src/journal/journalctl.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 6a44986413..1040d475fa 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -115,8 +115,10 @@ static const char *arg_field = NULL; static bool arg_catalog = false; static bool arg_reverse = false; static int arg_journal_type = 0; +static int arg_namespace_flags = 0; static char *arg_root = NULL; static const char *arg_machine = NULL; +static const char *arg_namespace = NULL; static uint64_t arg_vacuum_size = 0; static uint64_t arg_vacuum_n_files = 0; static usec_t arg_vacuum_time = 0; @@ -354,6 +356,7 @@ static int help(void) { " -D --directory=PATH Show journal files from directory\n" " --file=PATH Show journal file\n" " --root=ROOT Operate on files below a root directory\n" + " --namespace=NAMESPACE Show journal data from specified namespace\n" " --interval=TIME Time interval for changing the FSS sealing key\n" " --verify-key=KEY Specify FSS verification key\n" " --force Override of the FSS key pair with --setup-keys\n" @@ -426,6 +429,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_VACUUM_TIME, ARG_NO_HOSTNAME, ARG_OUTPUT_FIELDS, + ARG_NAMESPACE, }; static const struct option options[] = { @@ -490,6 +494,7 @@ static int parse_argv(int argc, char *argv[]) { { "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME }, { "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME }, { "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS }, + { "namespace", required_argument, NULL, ARG_NAMESPACE }, {} }; @@ -650,6 +655,23 @@ static int parse_argv(int argc, char *argv[]) { arg_machine = optarg; break; + case ARG_NAMESPACE: + if (streq(optarg, "*")) { + arg_namespace_flags = SD_JOURNAL_ALL_NAMESPACES; + arg_namespace = NULL; + } else if (startswith(optarg, "+")) { + arg_namespace_flags = SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE; + arg_namespace = optarg + 1; + } else if (isempty(optarg)) { + arg_namespace_flags = 0; + arg_namespace = NULL; + } else { + arg_namespace_flags = 0; + arg_namespace = optarg; + } + + break; + case 'D': arg_directory = optarg; break; @@ -1926,15 +1948,19 @@ static int verify(sd_journal *j) { static int simple_varlink_call(const char *option, const char *method) { _cleanup_(varlink_flush_close_unrefp) Varlink *link = NULL; - const char *error; + const char *error, *fn; int r; if (arg_machine) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "%s is not supported in conjunction with --machine=.", option); - r = varlink_connect_address(&link, "/run/systemd/journal/io.systemd.journal"); + fn = arg_namespace ? + strjoina("/run/systemd/journal.", arg_namespace, "/io.systemd.journal") : + "/run/systemd/journal/io.systemd.journal"; + + r = varlink_connect_address(&link, fn); if (r < 0) - return log_error_errno(r, "Failed to connect to /run/systemd/journal/io.systemd.journal: %m"); + return log_error_errno(r, "Failed to connect to %s: %m", fn); (void) varlink_set_description(link, "journal"); (void) varlink_set_relative_timeout(link, USEC_INFINITY); @@ -2157,7 +2183,11 @@ int main(int argc, char *argv[]) { if (r < 0) safe_close(fd); } else - r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type); + r = sd_journal_open_namespace( + &j, + arg_namespace, + (arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY) | + arg_namespace_flags | arg_journal_type); if (r < 0) { log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal"); goto finish; From 21fa231ece5e37fe9855844df8b2ec7be21306c7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 12:13:08 +0100 Subject: [PATCH 29/49] journalctl: drop misplaced empty line --- src/journal/journalctl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 1040d475fa..f5d208edcd 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -1421,7 +1421,6 @@ static int add_boot(sd_journal *j) { * so take the slow path if log location is specified. */ if (arg_boot_offset == 0 && sd_id128_is_null(arg_boot_id) && !arg_directory && !arg_file && !arg_root) - return add_match_this_boot(j, arg_machine); boot_id = arg_boot_id; From d93dda3afef4d8bfae8e4d20122adfa5e2cc41f8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 12:13:59 +0100 Subject: [PATCH 30/49] systemctl: show logs for correct namespace of service --- src/login/loginctl.c | 2 ++ src/machine/machinectl.c | 1 + src/shared/logs-show.c | 3 ++- src/shared/logs-show.h | 1 + src/systemctl/systemctl.c | 4 ++++ 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 6fea2c9e33..2e39f55763 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -576,6 +576,7 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li show_journal_by_unit( stdout, i.scope, + NULL, arg_output, 0, i.timestamp.monotonic, @@ -660,6 +661,7 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) show_journal_by_unit( stdout, i.slice, + NULL, arg_output, 0, i.timestamp.monotonic, diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 2377416b44..61234582a3 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -626,6 +626,7 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) { show_journal_by_unit( stdout, i->unit, + NULL, arg_output, 0, i->timestamp.monotonic, diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index ce0a4acf9c..605412aec5 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -1453,6 +1453,7 @@ int add_match_this_boot(sd_journal *j, const char *machine) { int show_journal_by_unit( FILE *f, const char *unit, + const char *log_namespace, OutputMode mode, unsigned n_columns, usec_t not_before, @@ -1473,7 +1474,7 @@ int show_journal_by_unit( if (how_many <= 0) return 0; - r = sd_journal_open(&j, journal_open_flags); + r = sd_journal_open_namespace(&j, log_namespace, journal_open_flags | SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE); if (r < 0) return log_error_errno(r, "Failed to open journal: %m"); diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index 1e0c4ea146..345efa4b2b 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -46,6 +46,7 @@ int add_matches_for_user_unit( int show_journal_by_unit( FILE *f, const char *unit, + const char *namespace, OutputMode mode, unsigned n_columns, usec_t not_before, diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 60872bc537..5547058296 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -4007,6 +4007,8 @@ typedef struct UnitStatusInfo { int exit_code, exit_status; + const char *log_namespace; + usec_t condition_timestamp; bool condition_result; LIST_HEAD(UnitCondition, conditions); @@ -4545,6 +4547,7 @@ static void print_status_info( show_journal_by_unit( stdout, i->id, + i->log_namespace, arg_output, 0, i->inactive_exit_timestamp_monotonic, @@ -5491,6 +5494,7 @@ static int show_one( { "ExecMainExitTimestamp", "t", NULL, offsetof(UnitStatusInfo, exit_timestamp) }, { "ExecMainCode", "i", NULL, offsetof(UnitStatusInfo, exit_code) }, { "ExecMainStatus", "i", NULL, offsetof(UnitStatusInfo, exit_status) }, + { "LogNamespace", "s", NULL, offsetof(UnitStatusInfo, log_namespace) }, { "ConditionTimestamp", "t", NULL, offsetof(UnitStatusInfo, condition_timestamp) }, { "ConditionResult", "b", NULL, offsetof(UnitStatusInfo, condition_result) }, { "Conditions", "a(sbbsi)", map_conditions, 0 }, From 24352691713179e27491b9294625b96b95fd9ed1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 13:03:51 +0100 Subject: [PATCH 31/49] journald: add logging for one error we lacked logging for --- src/journal/journald.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/journal/journald.c b/src/journal/journald.c index cd3a412c2e..d78ab3aa20 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -62,8 +62,10 @@ int main(int argc, char *argv[]) { usec_t t = USEC_INFINITY, n; r = sd_event_get_state(server.event); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to get event loop state: %m"); goto finish; + } if (r == SD_EVENT_FINISHED) break; From d98580e4380edd569c41ea950da19c88905cf11e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 13:04:07 +0100 Subject: [PATCH 32/49] journald: use structured initialization --- src/journal/journald-stream.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 671e627357..7905887152 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -590,12 +590,14 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) { if (r < 0) return log_error_errno(r, "Failed to generate stream ID: %m"); - stream = new0(StdoutStream, 1); + stream = new(StdoutStream, 1); if (!stream) return log_oom(); - stream->fd = -1; - stream->priority = LOG_INFO; + *stream = (StdoutStream) { + .fd = -1, + .priority = LOG_INFO, + }; xsprintf(stream->id_field, "_STREAM_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id)); From c4f601f20535883c870c1381a0b10815a2e7128f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 13:04:33 +0100 Subject: [PATCH 33/49] varlink: add API for determining number of current connections --- src/shared/varlink.c | 6 ++++++ src/shared/varlink.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/shared/varlink.c b/src/shared/varlink.c index 77cea00cb9..4295eb7ce2 100644 --- a/src/shared/varlink.c +++ b/src/shared/varlink.c @@ -2460,6 +2460,12 @@ int varlink_server_set_connections_max(VarlinkServer *s, unsigned m) { return 0; } +unsigned varlink_server_current_connections(VarlinkServer *s) { + assert_return(s, UINT_MAX); + + return s->n_connections; +} + int varlink_server_set_description(VarlinkServer *s, const char *description) { assert_return(s, -EINVAL); diff --git a/src/shared/varlink.h b/src/shared/varlink.h index 0d9617d403..6272b33228 100644 --- a/src/shared/varlink.h +++ b/src/shared/varlink.h @@ -150,6 +150,8 @@ unsigned varlink_server_connections_per_uid_max(VarlinkServer *s); int varlink_server_set_connections_per_uid_max(VarlinkServer *s, unsigned m); int varlink_server_set_connections_max(VarlinkServer *s, unsigned m); +unsigned varlink_server_current_connections(VarlinkServer *s); + int varlink_server_set_description(VarlinkServer *s, const char *description); DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_unref); From 6d4d6002606ed5356f236ba28149493c5fb14c52 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 14:43:41 +0100 Subject: [PATCH 34/49] varlink: add ability to register callback for disconnections --- src/shared/varlink.c | 23 +++++++++++++++++++++-- src/shared/varlink.h | 2 ++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/shared/varlink.c b/src/shared/varlink.c index 4295eb7ce2..012ce5308c 100644 --- a/src/shared/varlink.c +++ b/src/shared/varlink.c @@ -168,6 +168,7 @@ struct VarlinkServer { Hashmap *methods; VarlinkConnect connect_callback; + VarlinkDisconnect disconnect_callback; sd_event *event; int64_t event_priority; @@ -1146,6 +1147,7 @@ int varlink_flush(Varlink *v) { } static void varlink_detach_server(Varlink *v) { + VarlinkServer *saved_server; assert(v); if (!v->server) @@ -1169,8 +1171,15 @@ static void varlink_detach_server(Varlink *v) { v->server->n_connections--; /* If this is a connection associated to a server, then let's disconnect the server and the - * connection from each other. This drops the dangling reference that connect_callback() set up. */ - v->server = varlink_server_unref(v->server); + * connection from each other. This drops the dangling reference that connect_callback() set up. But + * before we release the references, let's call the disconnection callback if it is defined. */ + + saved_server = TAKE_PTR(v->server); + + if (saved_server->disconnect_callback) + saved_server->disconnect_callback(saved_server, v, saved_server->userdata); + + varlink_server_unref(saved_server); varlink_unref(v); } @@ -2413,6 +2422,16 @@ int varlink_server_bind_connect(VarlinkServer *s, VarlinkConnect callback) { return 0; } +int varlink_server_bind_disconnect(VarlinkServer *s, VarlinkDisconnect callback) { + assert_return(s, -EINVAL); + + if (callback && s->disconnect_callback && callback != s->disconnect_callback) + return -EBUSY; + + s->disconnect_callback = callback; + return 0; +} + unsigned varlink_server_connections_max(VarlinkServer *s) { int dts; diff --git a/src/shared/varlink.h b/src/shared/varlink.h index 6272b33228..7440f2ca44 100644 --- a/src/shared/varlink.h +++ b/src/shared/varlink.h @@ -51,6 +51,7 @@ typedef enum VarlinkServerFlags { typedef int (*VarlinkMethod)(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata); typedef int (*VarlinkReply)(Varlink *link, JsonVariant *parameters, const char *error_id, VarlinkReplyFlags flags, void *userdata); typedef int (*VarlinkConnect)(VarlinkServer *server, Varlink *link, void *userdata); +typedef void (*VarlinkDisconnect)(VarlinkServer *server, Varlink *link, void *userdata); int varlink_connect_address(Varlink **ret, const char *address); int varlink_connect_fd(Varlink **ret, int fd); @@ -134,6 +135,7 @@ int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMeth int varlink_server_bind_method_many_internal(VarlinkServer *s, ...); #define varlink_server_bind_method_many(s, ...) varlink_server_bind_method_many_internal(s, __VA_ARGS__, NULL) int varlink_server_bind_connect(VarlinkServer *s, VarlinkConnect connect); +int varlink_server_bind_disconnect(VarlinkServer *s, VarlinkDisconnect disconnect); void* varlink_server_set_userdata(VarlinkServer *s, void *userdata); void* varlink_server_get_userdata(VarlinkServer *s); From 65c398c031a3065caf34d258d4f9c50cd33a122c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 14:45:24 +0100 Subject: [PATCH 35/49] journald: add exit on idle --- src/journal/journald-server.c | 140 ++++++++++++++++++++++++++++- src/journal/journald-server.h | 4 + src/journal/journald-stream.c | 4 + units/systemd-journald@.service.in | 2 - 4 files changed, 146 insertions(+), 4 deletions(-) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 5f1faee784..3c2f55941b 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -77,6 +77,8 @@ #define DEFERRED_CLOSES_MAX (4096) +#define IDLE_TIMEOUT_USEC (30*USEC_PER_SEC) + static int determine_path_usage( Server *s, const char *path, @@ -1216,6 +1218,7 @@ finish: if (k < 0) log_warning_errno(k, "Failed to touch %s, ignoring: %m", fn); + server_refresh_idle_timer(s); return r; } @@ -1244,10 +1247,16 @@ static int server_relinquish_var(Server *s) { if (unlink(fn) < 0 && errno != ENOENT) log_warning_errno(errno, "Failed to unlink %s, ignoring: %m", fn); + server_refresh_idle_timer(s); return 0; } -int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata) { +int server_process_datagram( + sd_event_source *es, + int fd, + uint32_t revents, + void *userdata) { + Server *s = userdata; struct ucred *ucred = NULL; struct timeval *tv = NULL; @@ -1362,6 +1371,8 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void } close_many(fds, n_fds); + + server_refresh_idle_timer(s); return 0; } @@ -1373,6 +1384,8 @@ static void server_full_flush(Server *s) { server_vacuum(s, false); server_space_usage_message(s, NULL); + + server_refresh_idle_timer(s); } static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { @@ -2001,6 +2014,28 @@ static int vl_method_relinquish_var(Varlink *link, JsonVariant *parameters, Varl return varlink_reply(link, NULL); } +static int vl_connect(VarlinkServer *server, Varlink *link, void *userdata) { + Server *s = userdata; + + assert(server); + assert(link); + assert(s); + + (void) server_start_or_stop_idle_timer(s); /* maybe we are no longer idle */ + + return 0; +} + +static void vl_disconnect(VarlinkServer *server, Varlink *link, void *userdata) { + Server *s = userdata; + + assert(server); + assert(link); + assert(s); + + (void) server_start_or_stop_idle_timer(s); /* maybe we are idle now */ +} + static int server_open_varlink(Server *s) { const char *fn; int r; @@ -2022,6 +2057,14 @@ static int server_open_varlink(Server *s) { if (r < 0) return r; + r = varlink_server_bind_connect(s->varlink_server, vl_connect); + if (r < 0) + return r; + + r = varlink_server_bind_disconnect(s->varlink_server, vl_disconnect); + if (r < 0) + return r; + fn = strjoina(s->runtime_directory, "/io.systemd.journal"); r = varlink_server_listen_address(s->varlink_server, fn, 0600); @@ -2035,6 +2078,93 @@ static int server_open_varlink(Server *s) { return 0; } +static bool server_is_idle(Server *s) { + assert(s); + + /* The server for the main namespace is never idle */ + if (!s->namespace) + return false; + + /* 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 (s->max_retention_usec > IDLE_TIMEOUT_USEC) + return false; + + /* We aren't idle if we have a varlink client */ + if (varlink_server_current_connections(s->varlink_server) > 0) + return false; + + /* If we have stdout streams we aren't idle */ + if (s->n_stdout_streams > 0) + return false; + + return true; +} + +static int server_idle_handler(sd_event_source *source, uint64_t usec, void *userdata) { + Server *s = userdata; + + assert(source); + assert(s); + + log_debug("Server is idle, exiting."); + sd_event_exit(s->event, 0); + return 0; +} + +int server_start_or_stop_idle_timer(Server *s) { + _cleanup_(sd_event_source_unrefp) sd_event_source *source = NULL; + usec_t when; + int r; + + assert(s); + + if (!server_is_idle(s)) { + s->idle_event_source = sd_event_source_disable_unref(s->idle_event_source); + return 0; + } + + if (s->idle_event_source) + return 1; + + r = sd_event_now(s->event, CLOCK_MONOTONIC, &when); + if (r < 0) + return log_error_errno(r, "Failed to determine current time: %m"); + + r = sd_event_add_time(s->event, &source, CLOCK_MONOTONIC, usec_add(when, IDLE_TIMEOUT_USEC), 0, server_idle_handler, s); + if (r < 0) + return log_error_errno(r, "Failed to allocate idle timer: %m"); + + r = sd_event_source_set_priority(source, SD_EVENT_PRIORITY_IDLE); + if (r < 0) + return log_error_errno(r, "Failed to set idle timer priority: %m"); + + (void) sd_event_source_set_description(source, "idle-timer"); + + s->idle_event_source = TAKE_PTR(source); + return 1; +} + +int server_refresh_idle_timer(Server *s) { + usec_t when; + int r; + + assert(s); + + if (!s->idle_event_source) + return 0; + + r = sd_event_now(s->event, CLOCK_MONOTONIC, &when); + if (r < 0) + return log_error_errno(r, "Failed to determine current time: %m"); + + r = sd_event_source_set_time(s->idle_event_source, usec_add(when, IDLE_TIMEOUT_USEC)); + if (r < 0) + return log_error_errno(r, "Failed to refresh idle timer: %m"); + + return 1; +} + static int set_namespace(Server *s, const char *namespace) { assert(s); @@ -2298,7 +2428,12 @@ int server_init(Server *s, const char *namespace) { (void) client_context_acquire_default(s); - return system_journal_open(s, false, false); + r = system_journal_open(s, false, false); + if (r < 0) + return r; + + server_start_or_stop_idle_timer(s); + return 0; } void server_maybe_append_tags(Server *s) { @@ -2351,6 +2486,7 @@ void server_done(Server *s) { sd_event_source_unref(s->hostname_event_source); sd_event_source_unref(s->notify_event_source); sd_event_source_unref(s->watchdog_event_source); + sd_event_source_unref(s->idle_event_source); sd_event_unref(s->event); safe_close(s->syslog_fd); diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index 812c1eca8d..f3405e967c 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -86,6 +86,7 @@ struct Server { sd_event_source *hostname_event_source; sd_event_source *notify_event_source; sd_event_source *watchdog_event_source; + sd_event_source *idle_event_source; JournalFile *runtime_journal; JournalFile *system_journal; @@ -218,3 +219,6 @@ int server_flush_to_var(Server *s, bool require_flag_file); void server_maybe_append_tags(Server *s); int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata); void server_space_usage_message(Server *s, JournalStorage *storage); + +int server_start_or_stop_idle_timer(Server *s); +int server_refresh_idle_timer(Server *s); diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 7905887152..d5147cb61a 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -110,6 +110,8 @@ void stdout_stream_free(StdoutStream *s) { if (s->in_notify_queue) LIST_REMOVE(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s); + + (void) server_start_or_stop_idle_timer(s->server); /* Maybe we are idle now? */ } if (s->event_source) { @@ -631,6 +633,8 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) { LIST_PREPEND(stdout_stream, s->stdout_streams, stream); s->n_stdout_streams++; + (void) server_start_or_stop_idle_timer(s); /* Maybe no longer idle? */ + if (ret) *ret = stream; diff --git a/units/systemd-journald@.service.in b/units/systemd-journald@.service.in index e7274925a9..43082fa41b 100644 --- a/units/systemd-journald@.service.in +++ b/units/systemd-journald@.service.in @@ -25,8 +25,6 @@ LogsDirectory=journal/%m.%i LogsDirectoryMode=02755 MemoryDenyWriteExecute=yes NoNewPrivileges=yes -Restart=always -RestartSec=0 RestrictAddressFamilies=AF_UNIX AF_NETLINK RestrictNamespaces=yes RestrictRealtime=yes From dc5437c78bbfe02d7bc0d8adad25575aeaf75c28 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 14:47:37 +0100 Subject: [PATCH 36/49] journald: add ability to activate by varlink socket If we have exit on idle, then operations such as "journalctl --namespace=foo --rotate" should work even if the journal daemon is currently not running. (Note that we don't do activation by varlink for the main instance of journald, I am not sure the deadlocks it might introduce are worth it) --- src/core/unit.c | 10 +++++++++- src/journal/journald-server.c | 26 +++++++++++++++++--------- units/meson.build | 1 + units/systemd-journald-varlink@.socket | 18 ++++++++++++++++++ units/systemd-journald@.service.in | 4 ++-- 5 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 units/systemd-journald-varlink@.socket diff --git a/src/core/unit.c b/src/core/unit.c index 4089702335..6b97c35ffa 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -1067,7 +1067,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { * is run first. */ if (c->log_namespace) { - _cleanup_free_ char *socket_unit = NULL; + _cleanup_free_ char *socket_unit = NULL, *varlink_socket_unit = NULL; r = unit_name_build_from_type("systemd-journald", c->log_namespace, UNIT_SOCKET, &socket_unit); if (r < 0) @@ -1076,6 +1076,14 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, socket_unit, true, UNIT_DEPENDENCY_FILE); if (r < 0) return r; + + r = unit_name_build_from_type("systemd-journald-varlink", c->log_namespace, UNIT_SOCKET, &varlink_socket_unit); + if (r < 0) + return r; + + r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, varlink_socket_unit, true, UNIT_DEPENDENCY_FILE); + if (r < 0) + return r; } else r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE); if (r < 0) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 3c2f55941b..eb124aa73f 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -2036,8 +2036,7 @@ static void vl_disconnect(VarlinkServer *server, Varlink *link, void *userdata) (void) server_start_or_stop_idle_timer(s); /* maybe we are idle now */ } -static int server_open_varlink(Server *s) { - const char *fn; +static int server_open_varlink(Server *s, const char *socket, int fd) { int r; assert(s); @@ -2065,9 +2064,10 @@ static int server_open_varlink(Server *s) { if (r < 0) return r; - fn = strjoina(s->runtime_directory, "/io.systemd.journal"); - - r = varlink_server_listen_address(s->varlink_server, fn, 0600); + if (fd < 0) + r = varlink_server_listen_address(s->varlink_server, socket, 0600); + else + r = varlink_server_listen_fd(s->varlink_server, fd); if (r < 0) return r; @@ -2186,10 +2186,10 @@ static int set_namespace(Server *s, const char *namespace) { } int server_init(Server *s, const char *namespace) { - const char *native_socket, *syslog_socket, *stdout_socket, *e; + const char *native_socket, *syslog_socket, *stdout_socket, *varlink_socket, *e; _cleanup_fdset_free_ FDSet *fds = NULL; + int n, r, fd, varlink_fd = -1; bool no_sockets; - int n, r, fd; assert(s); @@ -2291,6 +2291,7 @@ int server_init(Server *s, const char *namespace) { native_socket = strjoina(s->runtime_directory, "/socket"); stdout_socket = strjoina(s->runtime_directory, "/stdout"); syslog_socket = strjoina(s->runtime_directory, "/dev-log"); + varlink_socket = strjoina(s->runtime_directory, "/io.systemd.journal"); for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { @@ -2318,6 +2319,13 @@ int server_init(Server *s, const char *namespace) { s->syslog_fd = fd; + } else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, varlink_socket, 0) > 0) { + + if (varlink_fd >= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Too many varlink sockets passed."); + + varlink_fd = fd; } else if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) { if (s->audit_fd >= 0) @@ -2348,7 +2356,7 @@ int server_init(Server *s, const char *namespace) { fds = fdset_free(fds); } - no_sockets = s->native_fd < 0 && s->stdout_fd < 0 && s->syslog_fd < 0 && s->audit_fd < 0; + no_sockets = s->native_fd < 0 && s->stdout_fd < 0 && s->syslog_fd < 0 && s->audit_fd < 0 && varlink_fd < 0; /* always open stdout, syslog, native, and kmsg sockets */ @@ -2379,7 +2387,7 @@ int server_init(Server *s, const char *namespace) { return r; } - r = server_open_varlink(s); + r = server_open_varlink(s, varlink_socket, varlink_fd); if (r < 0) return r; diff --git a/units/meson.build b/units/meson.build index c0b69566d1..0dc85ac64a 100644 --- a/units/meson.build +++ b/units/meson.build @@ -115,6 +115,7 @@ units = [ ['systemd-machine-id-commit.service', '', 'sysinit.target.wants/'], ['systemd-journald@.socket', ''], + ['systemd-journald-varlink@.socket', ''], ['systemd-networkd.socket', 'ENABLE_NETWORKD'], ['systemd-poweroff.service', ''], ['systemd-reboot.service', ''], diff --git a/units/systemd-journald-varlink@.socket b/units/systemd-journald-varlink@.socket new file mode 100644 index 0000000000..b6730c2fa5 --- /dev/null +++ b/units/systemd-journald-varlink@.socket @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Varlink Socket for Namespace %i +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +StopWhenUnneeded=yes + +[Socket] +Service=systemd-journald@%i.service +ListenStream=/run/systemd/journal.%i/io.systemd.journal +SocketMode=0600 diff --git a/units/systemd-journald@.service.in b/units/systemd-journald@.service.in index 43082fa41b..e7ea919328 100644 --- a/units/systemd-journald@.service.in +++ b/units/systemd-journald@.service.in @@ -10,8 +10,8 @@ [Unit] Description=Journal Service for Namespace %i Documentation=man:systemd-journald.service(8) man:journald.conf(5) -Requires=systemd-journald@%i.socket -After=systemd-journald@%i.socket +Requires=systemd-journald@%i.socket systemd-journald-varlink@%i.socket +After=systemd-journald@%i.socket systemd-journald-varlink@%i.socket [Service] CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE From db23d83bd49bb04ffb96ce130d2ff004a60a248b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 17:57:51 +0100 Subject: [PATCH 37/49] test: add simple test for log namespaces --- test/TEST-44-LOG-NAMESPACE/Makefile | 1 + test/TEST-44-LOG-NAMESPACE/test.sh | 39 +++++++++++++++++++++++++ test/TEST-44-LOG-NAMESPACE/testsuite.sh | 19 ++++++++++++ 3 files changed, 59 insertions(+) create mode 120000 test/TEST-44-LOG-NAMESPACE/Makefile create mode 100755 test/TEST-44-LOG-NAMESPACE/test.sh create mode 100755 test/TEST-44-LOG-NAMESPACE/testsuite.sh diff --git a/test/TEST-44-LOG-NAMESPACE/Makefile b/test/TEST-44-LOG-NAMESPACE/Makefile new file mode 120000 index 0000000000..e9f93b1104 --- /dev/null +++ b/test/TEST-44-LOG-NAMESPACE/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-44-LOG-NAMESPACE/test.sh b/test/TEST-44-LOG-NAMESPACE/test.sh new file mode 100755 index 0000000000..4dfb4418db --- /dev/null +++ b/test/TEST-44-LOG-NAMESPACE/test.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e +TEST_DESCRIPTION="test log namespaces" + +. $TEST_BASE_DIR/test-functions + +test_setup() { + create_empty_image_rootdir + + ( + LOG_LEVEL=5 + eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) + + setup_basic_environment + + mask_supporting_services + + # setup the testsuite service + cat >$initdir/etc/systemd/system/testsuite.service < /tmp/hello-world +journalctl > /tmp/no-hello-world + +grep "hello world" /tmp/hello-world +! grep "hello world" /tmp/no-hello-world + +systemd-analyze log-level info + +echo OK > /testok + +exit 0 From 0f5a4f9cd9695081c63ff9040a471a5790851f53 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 19:13:34 +0100 Subject: [PATCH 38/49] tmpfiles: merge lines for the same inodes --- tmpfiles.d/systemd.conf.m4 | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/tmpfiles.d/systemd.conf.m4 b/tmpfiles.d/systemd.conf.m4 index 4914e8ec8c..b251c4bcde 100644 --- a/tmpfiles.d/systemd.conf.m4 +++ b/tmpfiles.d/systemd.conf.m4 @@ -32,17 +32,14 @@ Z /run/log/journal/%m ~2750 root systemd-journal - - m4_ifdef(`HAVE_ACL',`m4_dnl m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl m4_ifdef(`ENABLE_WHEEL_GROUP',`` -a+ /run/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x -a+ /run/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x +a+ /run/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x,group:adm:r-x,group:wheel:r-x a+ /run/log/journal/%m/*.journal* - - - - group:adm:r--,group:wheel:r-- '',`` -a+ /run/log/journal/%m - - - - d:group:adm:r-x -a+ /run/log/journal/%m - - - - group:adm:r-x +a+ /run/log/journal/%m - - - - d:group:adm:r-x,group:adm:r-x a+ /run/log/journal/%m/*.journal* - - - - group:adm:r-- '')',`m4_dnl m4_ifdef(`ENABLE_WHEEL_GROUP',`` -a+ /run/log/journal/%m - - - - d:group:wheel:r-x -a+ /run/log/journal/%m - - - - group:wheel:r-x +a+ /run/log/journal/%m - - - - d:group:wheel:r-x,group:wheel:r-x a+ /run/log/journal/%m/*.journal* - - - - group:wheel:r-- '')')')m4_dnl @@ -52,23 +49,17 @@ z /var/log/journal/%m/system.journal 0640 root systemd-journal - - m4_ifdef(`HAVE_ACL',`m4_dnl m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl m4_ifdef(`ENABLE_WHEEL_GROUP',`` -a+ /var/log/journal - - - - d:group::r-x,d:group:adm:r-x,d:group:wheel:r-x -a+ /var/log/journal - - - - group::r-x,group:adm:r-x,group:wheel:r-x -a+ /var/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x -a+ /var/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x +a+ /var/log/journal - - - - d:group::r-x,d:group:adm:r-x,d:group:wheel:r-x,group::r-x,group:adm:r-x,group:wheel:r-x +a+ /var/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x,group:adm:r-x,group:wheel:r-x a+ /var/log/journal/%m/system.journal - - - - group:adm:r--,group:wheel:r-- '', `` -a+ /var/log/journal - - - - d:group::r-x,d:group:adm:r-x -a+ /var/log/journal - - - - group::r-x,group:adm:r-x -a+ /var/log/journal/%m - - - - d:group:adm:r-x -a+ /var/log/journal/%m - - - - group:adm:r-x +a+ /var/log/journal - - - - d:group::r-x,d:group:adm:r-x,group::r-x,group:adm:r-x +a+ /var/log/journal/%m - - - - d:group:adm:r-x,group:adm:r-x a+ /var/log/journal/%m/system.journal - - - - group:adm:r-- '')',`m4_dnl m4_ifdef(`ENABLE_WHEEL_GROUP',`` -a+ /var/log/journal - - - - d:group::r-x,d:group:wheel:r-x -a+ /var/log/journal - - - - group::r-x,group:wheel:r-x -a+ /var/log/journal/%m - - - - d:group:wheel:r-x -a+ /var/log/journal/%m - - - - group:wheel:r-x +a+ /var/log/journal - - - - d:group::r-x,d:group:wheel:r-x,group::r-x,group:wheel:r-x +a+ /var/log/journal/%m - - - - d:group:wheel:r-x,group:wheel:r-x a+ /var/log/journal/%m/system.journal - - - - group:wheel:r-- '')')')m4_dnl From fb38a7beb8157a0674559441de957359d16d85be Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 19:15:11 +0100 Subject: [PATCH 39/49] tmpfiles: apply ACLs to top-level journal directory in /run, too We already apply them to the directory in /var. Let's do the same in /run too. That's because due to the log namespace logic we nowadays can gain additional subdirs there during regular operation. --- tmpfiles.d/systemd.conf.m4 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tmpfiles.d/systemd.conf.m4 b/tmpfiles.d/systemd.conf.m4 index b251c4bcde..11d87d275b 100644 --- a/tmpfiles.d/systemd.conf.m4 +++ b/tmpfiles.d/systemd.conf.m4 @@ -32,13 +32,16 @@ Z /run/log/journal/%m ~2750 root systemd-journal - - m4_ifdef(`HAVE_ACL',`m4_dnl m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl m4_ifdef(`ENABLE_WHEEL_GROUP',`` +a+ /run/log/journal - - - - d:group::r-x,d:group:adm:r-x,d:group:wheel:r-x,group::r-x,group:adm:r-x,group:wheel:r-x a+ /run/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x,group:adm:r-x,group:wheel:r-x a+ /run/log/journal/%m/*.journal* - - - - group:adm:r--,group:wheel:r-- '',`` +a+ /run/log/journal - - - - d:group::r-x,d:group:adm:r-x,group::r-x,group:adm:r-x a+ /run/log/journal/%m - - - - d:group:adm:r-x,group:adm:r-x a+ /run/log/journal/%m/*.journal* - - - - group:adm:r-- '')',`m4_dnl m4_ifdef(`ENABLE_WHEEL_GROUP',`` +a+ /run/log/journal - - - - d:group::r-x,d:group:wheel:r-x,group::r-x,group:wheel:r-x a+ /run/log/journal/%m - - - - d:group:wheel:r-x,group:wheel:r-x a+ /run/log/journal/%m/*.journal* - - - - group:wheel:r-- '')')')m4_dnl From 5591cd4e204143731c1ef85b5fb5614dabee8d0b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 21:18:10 +0100 Subject: [PATCH 40/49] units: sort settings in systemd-journald.service again --- units/systemd-journald.service.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in index 303d5a4826..0233f389cf 100644 --- a/units/systemd-journald.service.in +++ b/units/systemd-journald.service.in @@ -16,7 +16,6 @@ After=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-a Before=sysinit.target [Service] -OOMScoreAdjust=-250 CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_SYSLOG CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE DeviceAllow=char-* rw ExecStart=@rootlibexecdir@/systemd-journald @@ -25,6 +24,7 @@ IPAddressDeny=any LockPersonality=yes MemoryDenyWriteExecute=yes NoNewPrivileges=yes +OOMScoreAdjust=-250 Restart=always RestartSec=0 RestrictAddressFamilies=AF_UNIX AF_NETLINK From 340cb115b388f4b55047c9dbf7241cbebe924d0b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 21:18:28 +0100 Subject: [PATCH 41/49] units: define RuntimeDirectory= in systemd-journald.service It doesn't get us much, but makes the differences between the templated and non-templated versions a bit smaller. --- units/systemd-journald.service.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in index 0233f389cf..5144868bcb 100644 --- a/units/systemd-journald.service.in +++ b/units/systemd-journald.service.in @@ -31,6 +31,8 @@ RestrictAddressFamilies=AF_UNIX AF_NETLINK RestrictNamespaces=yes RestrictRealtime=yes RestrictSUIDSGID=yes +RuntimeDirectory=systemd/journal +RuntimeDirectoryPreserve=yes Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket StandardOutput=null SystemCallArchitectures=native From 23d8c56046f189b0c1791b5b8611a93dda39effe Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 28 Nov 2019 10:50:49 +0100 Subject: [PATCH 42/49] journalctl: underline sections in --help --- src/journal/journalctl.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index f5d208edcd..4e2a8f6d11 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -313,9 +313,9 @@ static int help(void) { if (r < 0) return log_oom(); - printf("%s [OPTIONS...] [MATCHES...]\n\n" - "%sQuery the journal.%s\n\n" - "Options:\n" + printf("%1$s [OPTIONS...] [MATCHES...]\n\n" + "%5$sQuery the journal.%6$s\n\n" + "%3$sOptions:%4$s\n" " --system Show the system journal\n" " --user Show the user journal for the current user\n" " -M --machine=CONTAINER Operate on local container\n" @@ -360,7 +360,7 @@ static int help(void) { " --interval=TIME Time interval for changing the FSS sealing key\n" " --verify-key=KEY Specify FSS verification key\n" " --force Override of the FSS key pair with --setup-keys\n" - "\nCommands:\n" + "\n%3$sCommands:%4$s\n" " -h --help Show this help text\n" " --version Show package version\n" " -N --fields List all field names currently used\n" @@ -380,10 +380,11 @@ static int help(void) { " --dump-catalog Show entries in the message catalog\n" " --update-catalog Update the message catalog database\n" " --setup-keys Generate a new FSS key pair\n" - "\nSee the %s for details.\n" + "\nSee the %2$s for details.\n" , program_invocation_short_name - , ansi_highlight(), ansi_normal() , link + , ansi_underline(), ansi_normal() + , ansi_highlight(), ansi_normal() ); return 0; From efcbcd0d043f6c9d1f5d9af4028abca907beb4da Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 19:25:51 +0100 Subject: [PATCH 43/49] man: document journald namespaces --- man/rules/meson.build | 5 ++- man/systemd-journald.service.xml | 55 +++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/man/rules/meson.build b/man/rules/meson.build index 0e6c3d8270..f4b161661a 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -737,7 +737,10 @@ manpages = [ ['systemd-journald', 'systemd-journald-audit.socket', 'systemd-journald-dev-log.socket', - 'systemd-journald.socket'], + 'systemd-journald-varlink@.socket', + 'systemd-journald.socket', + 'systemd-journald@.service', + 'systemd-journald@.socket'], ''], ['systemd-localed.service', '8', ['systemd-localed'], 'ENABLE_LOCALED'], ['systemd-logind.service', '8', ['systemd-logind'], 'ENABLE_LOGIND'], diff --git a/man/systemd-journald.service.xml b/man/systemd-journald.service.xml index ec9f974f2d..f4ce4e4fe2 100644 --- a/man/systemd-journald.service.xml +++ b/man/systemd-journald.service.xml @@ -20,6 +20,9 @@ systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket + systemd-journald@.service + systemd-journald@.socket + systemd-journald-varlink@.socket systemd-journald Journal service @@ -29,6 +32,9 @@ systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket + systemd-journald@.service + systemd-journald@.socket + systemd-journald-varlink@.socket /usr/lib/systemd/systemd-journald @@ -129,6 +135,40 @@ systemd-tmpfiles --create --prefix /var/log/journal EPIPE right from the beginning. + + Journal Namespaces + + Journal 'namespaces' are both a mechanism for logically isolating the log stream of projects + consisting of one or more services from the rest of the system and a mechanism for improving + performance. Multiple journal namespaces may exist simultaneously, each defining its own, independent log + stream managed by its own instance of systemd-journald. Namespaces are independent of + each other, both in the data store and in the IPC interface. By default only a single 'default' namespace + exists, managed by systemd-journald.service (and its associated socket + units). Additional namespaces are created by starting an instance of the + systemd-journald@.service service template. The instance name is the namespace + identifier, which is a short string used for referencing the journal namespace. Service units may be + assigned to a specific journal namespace through the LogNamespace= unit file setting, + see systemd.exec5 for + details. The switch of + journalctl1 may be + used to view the log stream of a specific namespace. If the switch is not used the log stream of the + default namespace is shown, i.e. log data from other namespaces is not visible. + + Services associated with a specific log namespace may log via syslog, the native logging protocol + of the journal and via stdout/stderr; the logging from all three transports is associated with the + namespace. + + By default only the default namespace will collect kernel and audit log messages. + + The systemd-journald instance of the default namespace is configured through + /etc/systemd/journald.conf (see below), while the other instances are configured + through /etc/systemd/journald@NAMESPACE.conf. The journal + log data for the default namespace is placed in + /var/log/journal/MACHINE_ID (see below) while the data + for the other namespaces is located in + /var/log/journal/MACHINE_ID.NAMESPACE. + + Signals @@ -190,6 +230,9 @@ systemd-tmpfiles --create --prefix /var/log/journal + + Note that these kernel command line options are only honoured by the default namespace, see + above. @@ -279,12 +322,14 @@ systemd-tmpfiles --create --prefix /var/log/journal /run/systemd/journal/socket /run/systemd/journal/stdout - Sockets and other paths that - systemd-journald will listen on that are - visible in the file system. In addition to these, journald can - listen for audit events using netlink. + Sockets and other file node paths that systemd-journald will + listen on and are visible in the file system. In addition to these, + systemd-journald can listen for audit events using netlink7. + + If journal namespacing is used these paths are slightly altered to include a namespace identifier, see above. @@ -296,7 +341,7 @@ systemd-tmpfiles --create --prefix /var/log/journal systemd.journal-fields7, sd-journal3, systemd-coredump8, - setfacl1, + setfacl1, sd_journal_print3, pydoc systemd.journal From 6bc4361997b15e9e5fe2efd9bb8640c24ce6c072 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 21:38:07 +0100 Subject: [PATCH 44/49] man: document journald@NAMESPACE.conf --- man/journald.conf.xml | 50 +++++++++++++++++++++---------------------- man/rules/meson.build | 2 +- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/man/journald.conf.xml b/man/journald.conf.xml index 824db668c5..afbbe64bb8 100644 --- a/man/journald.conf.xml +++ b/man/journald.conf.xml @@ -18,6 +18,7 @@ journald.conf journald.conf.d + journald@.conf Journal service configuration files @@ -26,6 +27,7 @@ /etc/systemd/journald.conf.d/*.conf /run/systemd/journald.conf.d/*.conf /usr/lib/systemd/journald.conf.d/*.conf + /etc/systemd/journald@NAMESPACE.conf @@ -37,6 +39,12 @@ systemd.syntax5 for a general description of the syntax. + The systemd-journald instance managing the default namespace is configured by + /etc/systemd/journald.conf and associated drop-ins. Instances managing other + namespaces read /etc/systemd/journald@NAMESPACE.conf with + the namespace identifier filled in. This allows each namespace to carry a distinct configuration. See + systemd-journald.service8 + for details about journal namespaces. @@ -52,29 +60,19 @@ Storage= - Controls where to store journal data. One of - volatile, - persistent, - auto and - none. If - volatile, journal - log data will be stored only in memory, i.e. below the - /run/log/journal hierarchy (which is - created if needed). If persistent, data - will be stored preferably on disk, i.e. below the - /var/log/journal hierarchy (which is - created if needed), with a fallback to - /run/log/journal (which is created if - needed), during early boot and if the disk is not writable. - auto is similar to - persistent but the directory - /var/log/journal is not created if - needed, so that its existence controls where log data goes. - none turns off all storage, all log data - received will be dropped. Forwarding to other targets, such as - the console, the kernel log buffer, or a syslog socket will - still work however. Defaults to - auto. + Controls where to store journal data. One of volatile, + persistent, auto and none. If + volatile, journal log data will be stored only in memory, i.e. below the + /run/log/journal hierarchy (which is created if needed). If + persistent, data will be stored preferably on disk, i.e. below the + /var/log/journal hierarchy (which is created if needed), with a fallback to + /run/log/journal (which is created if needed), during early boot and if the disk + is not writable. auto is similar to persistent but the + directory /var/log/journal is not created if needed, so that its existence + controls where log data goes. none turns off all storage, all log data received + will be dropped. Forwarding to other targets, such as the console, the kernel log buffer, or a syslog + socket will still work however. Defaults to auto in the default journal namespace, + and persistent in all others. @@ -399,9 +397,9 @@ ReadKMsg= - Takes a boolean value. If enabled (the - default), journal reads /dev/kmsg - messages generated by the kernel. + Takes a boolean value. If enabled systemd-journal processes + /dev/kmsg messages generated by the kernel. In the default journal namespace + this option is enabled by default, it is disabled in all others. diff --git a/man/rules/meson.build b/man/rules/meson.build index f4b161661a..5cff8ae320 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -24,7 +24,7 @@ manpages = [ ['journal-remote.conf', '5', ['journal-remote.conf.d'], 'HAVE_MICROHTTPD'], ['journal-upload.conf', '5', ['journal-upload.conf.d'], 'HAVE_MICROHTTPD'], ['journalctl', '1', [], ''], - ['journald.conf', '5', ['journald.conf.d'], ''], + ['journald.conf', '5', ['journald.conf.d', 'journald@.conf'], ''], ['kernel-command-line', '7', [], ''], ['kernel-install', '8', [], ''], ['libudev', '3', [], ''], From 7d8155b3df1344cbf2c81c05ce01580d1aec96ff Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Nov 2019 21:29:47 +0100 Subject: [PATCH 45/49] man: document new _NAMESPACE= journal field --- man/systemd.journal-fields.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/man/systemd.journal-fields.xml b/man/systemd.journal-fields.xml index 9248726b9e..a0771f3c13 100644 --- a/man/systemd.journal-fields.xml +++ b/man/systemd.journal-fields.xml @@ -358,6 +358,15 @@ marking the log line end. + + _NAMESPACE= + + If this file was written by a systemd-journald instance managing a + journal namespace that is not the default, this field contains the namespace identifier. See + systemd-journald.service8 + for details about journal namespaces. + + From 5b0a76d107eb710d8c16854706a378d302aa1845 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 28 Nov 2019 10:02:02 +0100 Subject: [PATCH 46/49] man: document LogNamespace= unit setting --- man/systemd.exec.xml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index b7f44c9473..8f1695ad29 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -70,6 +70,10 @@ or (or their combinations with console output, see below) automatically acquire dependencies of type After= on systemd-journald.socket. + + Units using LogNamespace= will automatically gain ordering and + requirement dependencies on the two socket units associated with + systemd-journald@.service instances. @@ -2254,6 +2258,36 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy + + LogNamespace= + + Run the unit's processes in the specified journal namespace. Expects a short + user-defined string identifying the namespace. If not used the processes of the service are run in + the default journal namespace, i.e. their log stream is collected and processed by + systemd-journald.service. If this option is used any log data generated by + processes of this unit (regardless if via the syslog(), journal native logging + or stdout/stderr logging) is collected and processed by an instance of the + systemd-journald@.service template unit, which manages the specified + namespace. The log data is stored in a data store independent from the default log namespace's data + store. See + systemd-journald.service8 + for details about journal namespaces. + + Internally, journal namespaces are implemented through Linux mount namespacing and + over-mounting the directory that contains the relevant AF_UNIX sockets used for + logging in the unit's mount namespace. Since mount namespaces are used this setting disconnects + propagation of mounts from the unit's processes to the host, similar to how + ReadOnlyPaths= and similar settings (see above) work. Journal namespaces may hence + not be used for services that need to establish mount points on the host. + + When this option is used the unit will automatically gain ordering and requirement dependencies + on the two socket units associated with the systemd-journald@.service instance + so that they are automatically established prior to the unit starting up. Note that when this option + is used log output of this service does not appear in the regular + journalctl1 + output, unless the option is used. + + SyslogIdentifier= From 241c8f67f65a1051068a1728daaca5bc49183c69 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 28 Nov 2019 10:36:54 +0100 Subject: [PATCH 47/49] man: document the new sd_journal_open_namespace() API --- man/rules/meson.build | 4 +++- man/sd_journal_open.xml | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/man/rules/meson.build b/man/rules/meson.build index 5cff8ae320..9416c736c0 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -550,7 +550,9 @@ manpages = [ ''], ['sd_journal_open', '3', - ['SD_JOURNAL_CURRENT_USER', + ['SD_JOURNAL_ALL_NAMESPACES', + 'SD_JOURNAL_CURRENT_USER', + 'SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE', 'SD_JOURNAL_LOCAL_ONLY', 'SD_JOURNAL_OS_ROOT', 'SD_JOURNAL_RUNTIME_ONLY', diff --git a/man/sd_journal_open.xml b/man/sd_journal_open.xml index 47e57107cd..9884beae1a 100644 --- a/man/sd_journal_open.xml +++ b/man/sd_journal_open.xml @@ -29,6 +29,8 @@ SD_JOURNAL_SYSTEM SD_JOURNAL_CURRENT_USER SD_JOURNAL_OS_ROOT + SD_JOURNAL_ALL_NAMESPACES + SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE Open the system journal for reading @@ -42,6 +44,13 @@ int flags + + int sd_journal_open_namespace + sd_journal **ret + const char *namespace + int flags + + int sd_journal_open_directory sd_journal **ret @@ -101,6 +110,19 @@ SD_JOURNAL_CURRENT_USER are specified, all journal file types will be opened. + sd_journal_open_namespace() is similar to + sd_journal_open() but takes an additional namespace parameter + that specifies which journal namespace to operate on. If specified as NULL the call + is identical to sd_journal_open(). If non-NULL only data from + the namespace identified by the specified parameter is accessed. This call understands two additional + flags: if SD_JOURNAL_ALL_NAMESPACES is specified the + namespace parameter is ignored and all defined namespaces are accessed + simultaneously; if SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE the specified namespace and + the default namespace are accessed but no others (this flag has no effect when + namespace is passed as NULL). For details about journal + namespaces see + systemd-journald.service8. + sd_journal_open_directory() is similar to sd_journal_open() but takes an absolute directory path as argument. All journal files in this directory will be opened and interleaved automatically. This call also takes a flags argument. The flags parameters accepted by this call are From 2b6b8bd3f727be539faacffad96756bc515e6ac6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 28 Nov 2019 10:42:33 +0100 Subject: [PATCH 48/49] man: document --namespace= switch of journalctl --- man/journalctl.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/man/journalctl.xml b/man/journalctl.xml index f6703b06d6..46b22be699 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -749,6 +749,18 @@ + + + + Takes a journal namespace identifier string as argument. If not specified the data + collected by the default namespace is shown. If specified shows the log data of the specified + namespace instead. If the namespace is specified as * data from all namespaces is + shown, interleaved. If the namespace identifier is prefixed with + data from the + specified namespace and the default namespace is shown, interleaved, but no other. For details about + journal namespaces see + systemd-journald.service8. + + From ad23785246357b6366c5a1683c9722ae52841ed9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 28 Nov 2019 11:07:15 +0100 Subject: [PATCH 49/49] update TODO --- TODO | 5 ----- 1 file changed, 5 deletions(-) diff --git a/TODO b/TODO index 8695372bc6..a9902ddff2 100644 --- a/TODO +++ b/TODO @@ -894,11 +894,6 @@ Features: - journald: when we drop syslog messages because the syslog socket is full, make sure to write how many messages are lost as first thing to syslog when it works again. - - change systemd-journal-flush into a service that stays around during - boot, and causes the journal to be moved back to /run on shutdown, - so that we do not keep /var busy. This needs to happen synchronously, - hence doing this via signals is not going to work. - - optionally support running journald from the command line for testing purposes in external projects - journald: allow per-priority and per-service retention times when rotating/vacuuming - journald: make use of uid-range.h to managed uid ranges to split journals in.