summaryrefslogtreecommitdiff
path: root/src/journal
diff options
context:
space:
mode:
Diffstat (limited to 'src/journal')
-rw-r--r--src/journal/audit-type.c10
-rw-r--r--src/journal/audit-type.h3
-rw-r--r--src/journal/cat.c51
-rw-r--r--src/journal/catalog.c93
-rw-r--r--src/journal/compress.c43
-rw-r--r--src/journal/fsprg.h7
-rw-r--r--src/journal/journal-def.h1
-rw-r--r--src/journal/journal-file.c614
-rw-r--r--src/journal/journal-file.h8
-rw-r--r--src/journal/journal-send.c2
-rw-r--r--src/journal/journal-vacuum.c65
-rw-r--r--src/journal/journal-verify.c8
-rw-r--r--src/journal/journalctl.c263
-rw-r--r--src/journal/journald-audit.c12
-rw-r--r--src/journal/journald-audit.h4
-rw-r--r--src/journal/journald-context.c73
-rw-r--r--src/journal/journald-context.h3
-rw-r--r--src/journal/journald-kmsg.c37
-rw-r--r--src/journal/journald-kmsg.h2
-rw-r--r--src/journal/journald-native.c31
-rw-r--r--src/journal/journald-native.h2
-rw-r--r--src/journal/journald-rate-limit.c48
-rw-r--r--src/journal/journald-rate-limit.h4
-rw-r--r--src/journal/journald-server.c256
-rw-r--r--src/journal/journald-server.h2
-rw-r--r--src/journal/journald-stream.c35
-rw-r--r--src/journal/journald-stream.h2
-rw-r--r--src/journal/journald-syslog.c163
-rw-r--r--src/journal/journald-wall.c3
-rw-r--r--src/journal/journald-wall.h3
-rw-r--r--src/journal/journald.conf1
-rw-r--r--src/journal/lookup3.c6
-rw-r--r--src/journal/meson.build2
-rw-r--r--src/journal/mmap-cache.c28
-rw-r--r--src/journal/sd-journal.c87
-rw-r--r--src/journal/test-catalog.c25
-rw-r--r--src/journal/test-compress-benchmark.c20
-rw-r--r--src/journal/test-compress.c63
-rw-r--r--src/journal/test-journal-enum.c5
-rw-r--r--src/journal/test-journal-init.c3
-rw-r--r--src/journal/test-journal-interleaving.c15
-rw-r--r--src/journal/test-journal-match.c9
-rw-r--r--src/journal/test-journal-stream.c5
-rw-r--r--src/journal/test-journal-syslog.c42
-rw-r--r--src/journal/test-journal-verify.c9
-rw-r--r--src/journal/test-journal.c24
-rw-r--r--src/journal/test-mmap-cache.c2
47 files changed, 1244 insertions, 950 deletions
diff --git a/src/journal/audit-type.c b/src/journal/audit-type.c
index b30963da66..7b3dc1e9ab 100644
--- a/src/journal/audit-type.c
+++ b/src/journal/audit-type.c
@@ -1,12 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <stdio.h>
-#include <linux/audit.h>
-#if HAVE_AUDIT
-# include <libaudit.h>
-#endif
-
-#include "missing.h"
#include "audit-type.h"
+#include "missing_audit.h"
+
#include "audit_type-to-name.h"
-#include "macro.h"
diff --git a/src/journal/audit-type.h b/src/journal/audit-type.h
index 965b044cbf..069a883490 100644
--- a/src/journal/audit-type.h
+++ b/src/journal/audit-type.h
@@ -1,6 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <alloca.h>
+#include <stdio.h>
+
#include "macro.h"
const char *audit_type_to_string(int type);
diff --git a/src/journal/cat.c b/src/journal/cat.c
index 8a11c8c3d1..a84350fbc9 100644
--- a/src/journal/cat.c
+++ b/src/journal/cat.c
@@ -9,8 +9,11 @@
#include "sd-journal.h"
+#include "alloc-util.h"
#include "fd-util.h"
+#include "main-func.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "string-util.h"
#include "syslog-util.h"
#include "util.h"
@@ -19,7 +22,14 @@ static const char *arg_identifier = NULL;
static int arg_priority = LOG_INFO;
static bool arg_level_prefix = true;
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-cat", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Execute process with stdout/stderr connected to the journal.\n\n"
" -h --help Show this help\n"
@@ -27,7 +37,12 @@ static void help(void) {
" -t --identifier=STRING Set syslog identifier\n"
" -p --priority=PRIORITY Set priority value (0..7)\n"
" --level-prefix=BOOL Control whether level prefix shall be parsed\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -71,10 +86,9 @@ static int parse_argv(int argc, char *argv[]) {
case 'p':
arg_priority = log_level_from_string(optarg);
- if (arg_priority < 0) {
- log_error("Failed to parse priority value.");
- return -EINVAL;
- }
+ if (arg_priority < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse priority value.");
break;
case ARG_LEVEL_PREFIX: {
@@ -98,7 +112,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_close_ int fd = -1, saved_stderr = -1;
int r;
@@ -107,22 +121,18 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
fd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix);
- if (fd < 0) {
- r = log_error_errno(fd, "Failed to create stream fd: %m");
- goto finish;
- }
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to create stream fd: %m");
saved_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3);
r = rearrange_stdio(STDIN_FILENO, fd, fd); /* Invalidates fd on succcess + error! */
- fd = -1;
- if (r < 0) {
- log_error_errno(r, "Failed to rearrange stdout/stderr: %m");
- goto finish;
- }
+ TAKE_FD(fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to rearrange stdout/stderr: %m");
if (argc <= optind)
(void) execl("/bin/cat", "/bin/cat", NULL);
@@ -134,8 +144,7 @@ int main(int argc, char *argv[]) {
if (saved_stderr >= 0)
(void) dup3(saved_stderr, STDERR_FILENO, 0);
- log_error_errno(r, "Failed to execute process: %m");
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return log_error_errno(r, "Failed to execute process: %m");
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/journal/catalog.c b/src/journal/catalog.c
index f9118f0b62..3556a101bf 100644
--- a/src/journal/catalog.c
+++ b/src/journal/catalog.c
@@ -24,6 +24,7 @@
#include "strbuf.h"
#include "string-util.h"
#include "strv.h"
+#include "tmpfile-util.h"
#include "util.h"
const char * const catalog_file_dirs[] = {
@@ -49,31 +50,25 @@ typedef struct CatalogItem {
le64_t offset;
} CatalogItem;
-static void catalog_hash_func(const void *p, struct siphash *state) {
- const CatalogItem *i = p;
-
+static void catalog_hash_func(const CatalogItem *i, struct siphash *state) {
siphash24_compress(&i->id, sizeof(i->id), state);
siphash24_compress(i->language, strlen(i->language), state);
}
-static int catalog_compare_func(const void *a, const void *b) {
- const CatalogItem *i = a, *j = b;
+static int catalog_compare_func(const CatalogItem *a, const CatalogItem *b) {
unsigned k;
+ int r;
- for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) {
- if (i->id.bytes[k] < j->id.bytes[k])
- return -1;
- if (i->id.bytes[k] > j->id.bytes[k])
- return 1;
+ for (k = 0; k < ELEMENTSOF(b->id.bytes); k++) {
+ r = CMP(a->id.bytes[k], b->id.bytes[k]);
+ if (r != 0)
+ return r;
}
- return strcmp(i->language, j->language);
+ return strcmp(a->language, b->language);
}
-const struct hash_ops catalog_hash_ops = {
- .hash = catalog_hash_func,
- .compare = catalog_compare_func
-};
+DEFINE_HASH_OPS(catalog_hash_ops, CatalogItem, catalog_hash_func, catalog_compare_func);
static bool next_header(const char **s) {
const char *e;
@@ -218,14 +213,14 @@ static int catalog_entry_lang(const char* filename, int line,
size_t c;
c = strlen(t);
- if (c == 0) {
- log_error("[%s:%u] Language too short.", filename, line);
- return -EINVAL;
- }
- if (c > 31) {
- log_error("[%s:%u] language too long.", filename, line);
- return -EINVAL;
- }
+ if (c < 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "[%s:%u] Language too short.",
+ filename, line);
+ if (c > 31)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "[%s:%u] language too long.", filename,
+ line);
if (deflang) {
if (streq(t, deflang)) {
@@ -268,26 +263,23 @@ int catalog_import_file(Hashmap *h, const char *path) {
log_debug("File %s has language %s.", path, deflang);
for (;;) {
- char line[LINE_MAX];
+ _cleanup_free_ char *line = NULL;
size_t line_len;
- if (!fgets(line, sizeof(line), f)) {
- if (feof(f))
- break;
-
- return log_error_errno(errno, "Failed to read file %s: %m", path);
- }
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read file %s: %m", path);
+ if (r == 0)
+ break;
n++;
- truncate_nl(line);
-
- if (line[0] == 0) {
+ if (isempty(line)) {
empty_line = true;
continue;
}
- if (strchr(COMMENTS "\n", line[0]))
+ if (strchr(COMMENTS, line[0]))
continue;
if (empty_line &&
@@ -308,10 +300,11 @@ int catalog_import_file(Hashmap *h, const char *path) {
if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) {
if (got_id) {
- if (payload_size == 0) {
- log_error("[%s:%u] No payload text.", path, n);
- return -EINVAL;
- }
+ if (payload_size == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "[%s:%u] No payload text.",
+ path,
+ n);
r = finish_item(h, id, lang ?: deflang, payload, payload_size);
if (r < 0)
@@ -339,10 +332,10 @@ int catalog_import_file(Hashmap *h, const char *path) {
}
/* Payload */
- if (!got_id) {
- log_error("[%s:%u] Got payload before ID.", path, n);
- return -EINVAL;
- }
+ if (!got_id)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "[%s:%u] Got payload before ID.",
+ path, n);
line_len = strlen(line);
if (!GREEDY_REALLOC(payload, payload_allocated,
@@ -360,10 +353,10 @@ int catalog_import_file(Hashmap *h, const char *path) {
}
if (got_id) {
- if (payload_size == 0) {
- log_error("[%s:%u] No payload text.", path, n);
- return -EINVAL;
- }
+ if (payload_size == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "[%s:%u] No payload text.",
+ path, n);
r = finish_item(h, id, lang ?: deflang, payload, payload_size);
if (r < 0)
@@ -495,7 +488,7 @@ int catalog_update(const char* database, const char* root, const char* const* di
}
assert(n == hashmap_size(h));
- qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func);
+ typesafe_qsort(items, n, catalog_compare_func);
strbuf_complete(sb);
@@ -567,21 +560,21 @@ static const char *find_id(void *p, sd_id128_t id) {
strncpy(key.language, loc, sizeof(key.language));
key.language[strcspn(key.language, ".@")] = 0;
- f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
+ f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), (comparison_fn_t) catalog_compare_func);
if (!f) {
char *e;
e = strchr(key.language, '_');
if (e) {
*e = 0;
- f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
+ f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), (comparison_fn_t) catalog_compare_func);
}
}
}
if (!f) {
zero(key.language);
- f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
+ f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), (comparison_fn_t) catalog_compare_func);
}
if (!f)
diff --git a/src/journal/compress.c b/src/journal/compress.c
index 6baf15c8ff..e95ce2bcaa 100644
--- a/src/journal/compress.c
+++ b/src/journal/compress.c
@@ -95,11 +95,7 @@ int compress_blob_lz4(const void *src, uint64_t src_size,
if (src_size < 9)
return -ENOBUFS;
-#if LZ4_VERSION_NUMBER >= 10700
r = LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
-#else
- r = LZ4_compress_limitedOutput(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
-#endif
if (r <= 0)
return -ENOBUFS;
@@ -294,7 +290,6 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size,
* prefix */
int r;
- size_t size;
assert(src);
assert(src_size > 0);
@@ -311,23 +306,37 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size,
r = LZ4_decompress_safe_partial((char*)src + 8, *buffer, src_size - 8,
prefix_len + 1, *buffer_size);
- if (r >= 0)
- size = (unsigned) r;
- else {
- /* lz4 always tries to decode full "sequence", so in
- * pathological cases might need to decompress the
- * full field. */
+ /* One lz4 < 1.8.3, we might get "failure" (r < 0), or "success" where
+ * just a part of the buffer is decompressed. But if we get a smaller
+ * amount of bytes than requested, we don't know whether there isn't enough
+ * data to fill the requested size or whether we just got a partial answer.
+ */
+ if (r < 0 || (size_t) r < prefix_len + 1) {
+ size_t size;
+
+ if (LZ4_versionNumber() >= 10803)
+ /* We trust that the newer lz4 decompresses the number of bytes we
+ * requested if available in the compressed string. */
+ return 0;
+
+ if (r > 0)
+ /* Compare what we have first, in case of mismatch we can
+ * shortcut the full comparison. */
+ if (memcmp(*buffer, prefix, r) != 0)
+ return 0;
+
+ /* Before version 1.8.3, lz4 always tries to decode full a "sequence",
+ * so in pathological cases might need to decompress the full field. */
r = decompress_blob_lz4(src, src_size, buffer, buffer_size, &size, 0);
if (r < 0)
return r;
- }
- if (size >= prefix_len + 1)
- return memcmp(*buffer, prefix, prefix_len) == 0 &&
- ((const uint8_t*) *buffer)[prefix_len] == extra;
- else
- return 0;
+ if (size < prefix_len + 1)
+ return 0;
+ }
+ return memcmp(*buffer, prefix, prefix_len) == 0 &&
+ ((const uint8_t*) *buffer)[prefix_len] == extra;
#else
return -EPROTONOSUPPORT;
#endif
diff --git a/src/journal/fsprg.h b/src/journal/fsprg.h
index bf78c3e9c3..33412675b8 100644
--- a/src/journal/fsprg.h
+++ b/src/journal/fsprg.h
@@ -1,7 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#ifndef __fsprgh__
-#define __fsprgh__
+#pragma once
/*
* fsprg v0.1 - (seekable) forward-secure pseudorandom generator
@@ -22,7 +20,6 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
- *
*/
#include <inttypes.h>
@@ -63,5 +60,3 @@ void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx);
#ifdef __cplusplus
}
#endif
-
-#endif
diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h
index 43f70c861a..e48260206f 100644
--- a/src/journal/journal-def.h
+++ b/src/journal/journal-def.h
@@ -10,7 +10,6 @@
* If you change this file you probably should also change its documentation:
*
* http://www.freedesktop.org/wiki/Software/systemd/journal-files
- *
*/
typedef struct Header Header;
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 62e7f68a13..56827f9f36 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -10,6 +10,8 @@
#include <sys/uio.h>
#include <unistd.h>
+#include "sd-event.h"
+
#include "alloc-util.h"
#include "btrfs-util.h"
#include "chattr-util.h"
@@ -23,7 +25,6 @@
#include "parse-util.h"
#include "path-util.h"
#include "random-util.h"
-#include "sd-event.h"
#include "set.h"
#include "stat-util.h"
#include "string-util.h"
@@ -235,8 +236,7 @@ int journal_file_set_offline(JournalFile *f, bool wait) {
sigset_t ss, saved_ss;
int k;
- if (sigfillset(&ss) < 0)
- return -errno;
+ assert_se(sigfillset(&ss) >= 0);
r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss);
if (r > 0)
@@ -349,11 +349,8 @@ JournalFile* journal_file_close(JournalFile *f) {
#endif
if (f->post_change_timer) {
- int enabled;
-
- if (sd_event_source_get_enabled(f->post_change_timer, &enabled) >= 0)
- if (enabled == SD_EVENT_ONESHOT)
- journal_file_post_change(f);
+ if (sd_event_source_get_enabled(f->post_change_timer, NULL) > 0)
+ journal_file_post_change(f);
(void) sd_event_source_set_enabled(f->post_change_timer, SD_EVENT_OFF);
sd_event_source_unref(f->post_change_timer);
@@ -372,7 +369,7 @@ JournalFile* journal_file_close(JournalFile *f) {
* reenable all the good bits COW usually provides
* (such as data checksumming). */
- (void) chattr_fd(f->fd, 0, FS_NOCOW_FL);
+ (void) chattr_fd(f->fd, 0, FS_NOCOW_FL, NULL);
(void) btrfs_defrag_fd(f->fd);
}
@@ -568,13 +565,14 @@ static int journal_file_verify_header(JournalFile *f) {
if (state == STATE_ARCHIVED)
return -ESHUTDOWN; /* Already archived */
- else if (state == STATE_ONLINE) {
- log_debug("Journal file %s is already online. Assuming unclean closing.", f->path);
- return -EBUSY;
- } else if (state != STATE_OFFLINE) {
- log_debug("Journal file %s has unknown state %i.", f->path, state);
- return -EBUSY;
- }
+ else if (state == STATE_ONLINE)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBUSY),
+ "Journal file %s is already online. Assuming unclean closing.",
+ f->path);
+ else if (state != STATE_OFFLINE)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBUSY),
+ "Journal file %s has unknown state %i.",
+ f->path, state);
if (f->header->field_hash_table_size == 0 || f->header->data_hash_table_size == 0)
return -EBADMSG;
@@ -582,10 +580,10 @@ static int journal_file_verify_header(JournalFile *f) {
/* Don't permit appending to files from the future. Because otherwise the realtime timestamps wouldn't
* be strictly ordered in the entries in the file anymore, and we can't have that since it breaks
* bisection. */
- if (le64toh(f->header->tail_entry_realtime) > now(CLOCK_REALTIME)) {
- log_debug("Journal file %s is from the future, refusing to append new data to it that'd be older.", f->path);
- return -ETXTBSY;
- }
+ if (le64toh(f->header->tail_entry_realtime) > now(CLOCK_REALTIME))
+ return log_debug_errno(SYNTHETIC_ERRNO(ETXTBSY),
+ "Journal file %s is from the future, refusing to append new data to it that'd be older.",
+ f->path);
}
f->compress_xz = JOURNAL_HEADER_COMPRESSED_XZ(f->header);
@@ -749,153 +747,124 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
switch (o->object.type) {
case OBJECT_DATA: {
- if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
- log_debug("Bad n_entries: %"PRIu64": %"PRIu64,
- le64toh(o->data.n_entries), offset);
- return -EBADMSG;
- }
-
- if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) {
- log_debug("Bad object size (<= %zu): %"PRIu64": %"PRIu64,
- offsetof(DataObject, payload),
- le64toh(o->object.size),
- offset);
- return -EBADMSG;
- }
+ if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Bad n_entries: %" PRIu64 ": %" PRIu64,
+ le64toh(o->data.n_entries),
+ offset);
+
+ if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Bad object size (<= %zu): %" PRIu64 ": %" PRIu64,
+ offsetof(DataObject, payload),
+ le64toh(o->object.size),
+ offset);
if (!VALID64(le64toh(o->data.next_hash_offset)) ||
!VALID64(le64toh(o->data.next_field_offset)) ||
!VALID64(le64toh(o->data.entry_offset)) ||
- !VALID64(le64toh(o->data.entry_array_offset))) {
- log_debug("Invalid offset, next_hash_offset="OFSfmt", next_field_offset="OFSfmt
- ", entry_offset="OFSfmt", entry_array_offset="OFSfmt": %"PRIu64,
- le64toh(o->data.next_hash_offset),
- le64toh(o->data.next_field_offset),
- le64toh(o->data.entry_offset),
- le64toh(o->data.entry_array_offset),
- offset);
- return -EBADMSG;
- }
+ !VALID64(le64toh(o->data.entry_array_offset)))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid offset, next_hash_offset=" OFSfmt ", next_field_offset=" OFSfmt ", entry_offset=" OFSfmt ", entry_array_offset=" OFSfmt ": %" PRIu64,
+ le64toh(o->data.next_hash_offset),
+ le64toh(o->data.next_field_offset),
+ le64toh(o->data.entry_offset),
+ le64toh(o->data.entry_array_offset),
+ offset);
break;
}
case OBJECT_FIELD:
- if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) {
- log_debug(
- "Bad field size (<= %zu): %"PRIu64": %"PRIu64,
- offsetof(FieldObject, payload),
- le64toh(o->object.size),
- offset);
- return -EBADMSG;
- }
+ if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Bad field size (<= %zu): %" PRIu64 ": %" PRIu64,
+ offsetof(FieldObject, payload),
+ le64toh(o->object.size),
+ offset);
if (!VALID64(le64toh(o->field.next_hash_offset)) ||
- !VALID64(le64toh(o->field.head_data_offset))) {
- log_debug(
- "Invalid offset, next_hash_offset="OFSfmt
- ", head_data_offset="OFSfmt": %"PRIu64,
- le64toh(o->field.next_hash_offset),
- le64toh(o->field.head_data_offset),
- offset);
- return -EBADMSG;
- }
+ !VALID64(le64toh(o->field.head_data_offset)))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid offset, next_hash_offset=" OFSfmt ", head_data_offset=" OFSfmt ": %" PRIu64,
+ le64toh(o->field.next_hash_offset),
+ le64toh(o->field.head_data_offset),
+ offset);
break;
case OBJECT_ENTRY:
- if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) {
- log_debug(
- "Bad entry size (<= %zu): %"PRIu64": %"PRIu64,
- offsetof(EntryObject, items),
- le64toh(o->object.size),
- offset);
- return -EBADMSG;
- }
-
- if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) {
- log_debug(
- "Invalid number items in entry: %"PRIu64": %"PRIu64,
- (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem),
- offset);
- return -EBADMSG;
- }
-
- if (le64toh(o->entry.seqnum) <= 0) {
- log_debug(
- "Invalid entry seqnum: %"PRIx64": %"PRIu64,
- le64toh(o->entry.seqnum),
- offset);
- return -EBADMSG;
- }
-
- if (!VALID_REALTIME(le64toh(o->entry.realtime))) {
- log_debug(
- "Invalid entry realtime timestamp: %"PRIu64": %"PRIu64,
- le64toh(o->entry.realtime),
- offset);
- return -EBADMSG;
- }
-
- if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) {
- log_debug(
- "Invalid entry monotonic timestamp: %"PRIu64": %"PRIu64,
- le64toh(o->entry.monotonic),
- offset);
- return -EBADMSG;
- }
+ if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Bad entry size (<= %zu): %" PRIu64 ": %" PRIu64,
+ offsetof(EntryObject, items),
+ le64toh(o->object.size),
+ offset);
+
+ if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid number items in entry: %" PRIu64 ": %" PRIu64,
+ (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem),
+ offset);
+
+ if (le64toh(o->entry.seqnum) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid entry seqnum: %" PRIx64 ": %" PRIu64,
+ le64toh(o->entry.seqnum),
+ offset);
+
+ if (!VALID_REALTIME(le64toh(o->entry.realtime)))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid entry realtime timestamp: %" PRIu64 ": %" PRIu64,
+ le64toh(o->entry.realtime),
+ offset);
+
+ if (!VALID_MONOTONIC(le64toh(o->entry.monotonic)))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid entry monotonic timestamp: %" PRIu64 ": %" PRIu64,
+ le64toh(o->entry.monotonic),
+ offset);
break;
case OBJECT_DATA_HASH_TABLE:
case OBJECT_FIELD_HASH_TABLE:
if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
- (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) {
- log_debug(
- "Invalid %s hash table size: %"PRIu64": %"PRIu64,
- o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
- le64toh(o->object.size),
- offset);
- return -EBADMSG;
- }
+ (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid %s hash table size: %" PRIu64 ": %" PRIu64,
+ o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
+ le64toh(o->object.size),
+ offset);
break;
case OBJECT_ENTRY_ARRAY:
if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
- (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) {
- log_debug(
- "Invalid object entry array size: %"PRIu64": %"PRIu64,
- le64toh(o->object.size),
- offset);
- return -EBADMSG;
- }
-
- if (!VALID64(le64toh(o->entry_array.next_entry_array_offset))) {
- log_debug(
- "Invalid object entry array next_entry_array_offset: "OFSfmt": %"PRIu64,
- le64toh(o->entry_array.next_entry_array_offset),
- offset);
- return -EBADMSG;
- }
+ (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid object entry array size: %" PRIu64 ": %" PRIu64,
+ le64toh(o->object.size),
+ offset);
+
+ if (!VALID64(le64toh(o->entry_array.next_entry_array_offset)))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid object entry array next_entry_array_offset: " OFSfmt ": %" PRIu64,
+ le64toh(o->entry_array.next_entry_array_offset),
+ offset);
break;
case OBJECT_TAG:
- if (le64toh(o->object.size) != sizeof(TagObject)) {
- log_debug(
- "Invalid object tag size: %"PRIu64": %"PRIu64,
- le64toh(o->object.size),
- offset);
- return -EBADMSG;
- }
+ if (le64toh(o->object.size) != sizeof(TagObject))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid object tag size: %" PRIu64 ": %" PRIu64,
+ le64toh(o->object.size),
+ offset);
- if (!VALID_EPOCH(le64toh(o->tag.epoch))) {
- log_debug(
- "Invalid object tag epoch: %"PRIu64": %"PRIu64,
- le64toh(o->tag.epoch),
- offset);
- return -EBADMSG;
- }
+ if (!VALID_EPOCH(le64toh(o->tag.epoch)))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid object tag epoch: %" PRIu64 ": %" PRIu64,
+ le64toh(o->tag.epoch), offset);
break;
}
@@ -914,16 +883,16 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset
assert(ret);
/* Objects may only be located at multiple of 64 bit */
- if (!VALID64(offset)) {
- log_debug("Attempt to move to object at non-64bit boundary: %" PRIu64, offset);
- return -EBADMSG;
- }
+ if (!VALID64(offset))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to object at non-64bit boundary: %" PRIu64,
+ offset);
/* Object may not be located in the file header */
- if (offset < le64toh(f->header->header_size)) {
- log_debug("Attempt to move to object located in file header: %" PRIu64, offset);
- return -EBADMSG;
- }
+ if (offset < le64toh(f->header->header_size))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to object located in file header: %" PRIu64,
+ offset);
r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t, &tsize);
if (r < 0)
@@ -932,29 +901,29 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset
o = (Object*) t;
s = le64toh(o->object.size);
- if (s == 0) {
- log_debug("Attempt to move to uninitialized object: %" PRIu64, offset);
- return -EBADMSG;
- }
- if (s < sizeof(ObjectHeader)) {
- log_debug("Attempt to move to overly short object: %" PRIu64, offset);
- return -EBADMSG;
- }
-
- if (o->object.type <= OBJECT_UNUSED) {
- log_debug("Attempt to move to object with invalid type: %" PRIu64, offset);
- return -EBADMSG;
- }
-
- if (s < minimum_header_size(o)) {
- log_debug("Attempt to move to truncated object: %" PRIu64, offset);
- return -EBADMSG;
- }
-
- if (type > OBJECT_UNUSED && o->object.type != type) {
- log_debug("Attempt to move to object of unexpected type: %" PRIu64, offset);
- return -EBADMSG;
- }
+ if (s == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to uninitialized object: %" PRIu64,
+ offset);
+ if (s < sizeof(ObjectHeader))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to overly short object: %" PRIu64,
+ offset);
+
+ if (o->object.type <= OBJECT_UNUSED)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to object with invalid type: %" PRIu64,
+ offset);
+
+ if (s < minimum_header_size(o))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to truncated object: %" PRIu64,
+ offset);
+
+ if (type > OBJECT_UNUSED && o->object.type != type)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to object of unexpected type: %" PRIu64,
+ offset);
if (s > tsize) {
r = journal_file_move_to(f, type, false, offset, s, &t, NULL);
@@ -1846,6 +1815,9 @@ static int journal_file_append_entry_internal(
void journal_file_post_change(JournalFile *f) {
assert(f);
+ if (f->fd < 0)
+ return;
+
/* inotify() does not receive IN_MODIFY events from file
* accesses done via mmap(). After each access we hence
* trigger IN_MODIFY by truncating the journal file to its
@@ -1866,37 +1838,33 @@ static int post_change_thunk(sd_event_source *timer, uint64_t usec, void *userda
}
static void schedule_post_change(JournalFile *f) {
- sd_event_source *timer;
- int enabled, r;
uint64_t now;
+ int r;
assert(f);
assert(f->post_change_timer);
- timer = f->post_change_timer;
-
- r = sd_event_source_get_enabled(timer, &enabled);
+ r = sd_event_source_get_enabled(f->post_change_timer, NULL);
if (r < 0) {
log_debug_errno(r, "Failed to get ftruncate timer state: %m");
goto fail;
}
-
- if (enabled == SD_EVENT_ONESHOT)
+ if (r > 0)
return;
- r = sd_event_now(sd_event_source_get_event(timer), CLOCK_MONOTONIC, &now);
+ r = sd_event_now(sd_event_source_get_event(f->post_change_timer), CLOCK_MONOTONIC, &now);
if (r < 0) {
log_debug_errno(r, "Failed to get clock's now for scheduling ftruncate: %m");
goto fail;
}
- r = sd_event_source_set_time(timer, now+f->post_change_timer_period);
+ r = sd_event_source_set_time(f->post_change_timer, now + f->post_change_timer_period);
if (r < 0) {
log_debug_errno(r, "Failed to set time for scheduling ftruncate: %m");
goto fail;
}
- r = sd_event_source_set_enabled(timer, SD_EVENT_ONESHOT);
+ r = sd_event_source_set_enabled(f->post_change_timer, SD_EVENT_ONESHOT);
if (r < 0) {
log_debug_errno(r, "Failed to enable scheduled ftruncate: %m");
goto fail;
@@ -1933,14 +1901,8 @@ int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t)
return r;
}
-static int entry_item_cmp(const void *_a, const void *_b) {
- const EntryItem *a = _a, *b = _b;
-
- if (le64toh(a->object_offset) < le64toh(b->object_offset))
- return -1;
- if (le64toh(a->object_offset) > le64toh(b->object_offset))
- return 1;
- return 0;
+static int entry_item_cmp(const EntryItem *a, const EntryItem *b) {
+ return CMP(le64toh(a->object_offset), le64toh(b->object_offset));
}
int journal_file_append_entry(
@@ -1962,14 +1924,14 @@ int journal_file_append_entry(
assert(iovec || n_iovec == 0);
if (ts) {
- if (!VALID_REALTIME(ts->realtime)) {
- log_debug("Invalid realtime timestamp %"PRIu64", refusing entry.", ts->realtime);
- return -EBADMSG;
- }
- if (!VALID_MONOTONIC(ts->monotonic)) {
- log_debug("Invalid monotomic timestamp %"PRIu64", refusing entry.", ts->monotonic);
- return -EBADMSG;
- }
+ if (!VALID_REALTIME(ts->realtime))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid realtime timestamp %" PRIu64 ", refusing entry.",
+ ts->realtime);
+ if (!VALID_MONOTONIC(ts->monotonic))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid monotomic timestamp %" PRIu64 ", refusing entry.",
+ ts->monotonic);
} else {
dual_timestamp_get(&_ts);
ts = &_ts;
@@ -1999,7 +1961,7 @@ int journal_file_append_entry(
/* Order by the position on disk, in order to improve seek
* times for rotating media. */
- qsort_safe(items, n_iovec, sizeof(EntryItem), entry_item_cmp);
+ typesafe_qsort(items, n_iovec, entry_item_cmp);
r = journal_file_append_entry_internal(f, ts, boot_id, xor_hash, items, n_iovec, seqnum, ret, offset);
@@ -2619,6 +2581,8 @@ void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset) {
}
int journal_file_compare_locations(JournalFile *af, JournalFile *bf) {
+ int r;
+
assert(af);
assert(af->header);
assert(bf);
@@ -2638,10 +2602,9 @@ int journal_file_compare_locations(JournalFile *af, JournalFile *bf) {
/* If this is from the same seqnum source, compare
* seqnums */
- if (af->current_seqnum < bf->current_seqnum)
- return -1;
- if (af->current_seqnum > bf->current_seqnum)
- return 1;
+ r = CMP(af->current_seqnum, bf->current_seqnum);
+ if (r != 0)
+ return r;
/* Wow! This is weird, different data but the same
* seqnums? Something is borked, but let's make the
@@ -2651,25 +2614,18 @@ int journal_file_compare_locations(JournalFile *af, JournalFile *bf) {
if (sd_id128_equal(af->current_boot_id, bf->current_boot_id)) {
/* If the boot id matches, compare monotonic time */
- if (af->current_monotonic < bf->current_monotonic)
- return -1;
- if (af->current_monotonic > bf->current_monotonic)
- return 1;
+ r = CMP(af->current_monotonic, bf->current_monotonic);
+ if (r != 0)
+ return r;
}
/* Otherwise, compare UTC time */
- if (af->current_realtime < bf->current_realtime)
- return -1;
- if (af->current_realtime > bf->current_realtime)
- return 1;
+ r = CMP(af->current_realtime, bf->current_realtime);
+ if (r != 0)
+ return r;
/* Finally, compare by contents */
- if (af->current_xor_hash < bf->current_xor_hash)
- return -1;
- if (af->current_xor_hash > bf->current_xor_hash)
- return 1;
-
- return 0;
+ return CMP(af->current_xor_hash, bf->current_xor_hash);
}
static int bump_array_index(uint64_t *i, direction_t direction, uint64_t n) {
@@ -2759,10 +2715,10 @@ int journal_file_next_entry(
}
/* Ensure our array is properly ordered. */
- if (p > 0 && !check_properly_ordered(ofs, p, direction)) {
- log_debug("%s: entry array not properly ordered at entry %" PRIu64, f->path, i);
- return -EBADMSG;
- }
+ if (p > 0 && !check_properly_ordered(ofs, p, direction))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s: entry array not properly ordered at entry %" PRIu64,
+ f->path, i);
if (offset)
*offset = ofs;
@@ -2835,10 +2791,10 @@ int journal_file_next_entry_for_data(
}
/* Ensure our array is properly ordered. */
- if (p > 0 && check_properly_ordered(ofs, p, direction)) {
- log_debug("%s data entry array not properly ordered at entry %" PRIu64, f->path, i);
- return -EBADMSG;
- }
+ if (p > 0 && check_properly_ordered(ofs, p, direction))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s data entry array not properly ordered at entry %" PRIu64,
+ f->path, i);
if (offset)
*offset = ofs;
@@ -3233,30 +3189,30 @@ int journal_file_open(
if (fname && (flags & O_CREAT) && !endswith(fname, ".journal"))
return -EINVAL;
- f = new0(JournalFile, 1);
+ f = new(JournalFile, 1);
if (!f)
return -ENOMEM;
- f->fd = fd;
- f->mode = mode;
+ *f = (JournalFile) {
+ .fd = fd,
+ .mode = mode,
+
+ .flags = flags,
+ .prot = prot_from_flags(flags),
+ .writable = (flags & O_ACCMODE) != O_RDONLY,
- f->flags = flags;
- f->prot = prot_from_flags(flags);
- f->writable = (flags & O_ACCMODE) != O_RDONLY;
#if HAVE_LZ4
- f->compress_lz4 = compress;
+ .compress_lz4 = compress,
#elif HAVE_XZ
- f->compress_xz = compress;
+ .compress_xz = compress,
#endif
-
- if (compress_threshold_bytes == (uint64_t) -1)
- f->compress_threshold_bytes = DEFAULT_COMPRESS_THRESHOLD;
- else
- f->compress_threshold_bytes = MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes);
-
+ .compress_threshold_bytes = compress_threshold_bytes == (uint64_t) -1 ?
+ DEFAULT_COMPRESS_THRESHOLD :
+ MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes),
#if HAVE_GCRYPT
- f->seal = seal;
+ .seal = seal,
#endif
+ };
log_debug("Journal effective settings seal=%s compress=%s compress_threshold_bytes=%s",
yes_no(f->seal), yes_no(JOURNAL_FILE_COMPRESS(f)),
@@ -3446,74 +3402,145 @@ fail:
return r;
}
-int journal_file_rotate(JournalFile **f, bool compress, uint64_t compress_threshold_bytes, bool seal, Set *deferred_closes) {
+int journal_file_archive(JournalFile *f) {
_cleanup_free_ char *p = NULL;
- size_t l;
- JournalFile *old_file, *new_file = NULL;
- int r;
assert(f);
- assert(*f);
- old_file = *f;
-
- if (!old_file->writable)
+ if (!f->writable)
return -EINVAL;
/* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse
* rotation, since we don't know the actual path, and couldn't rename the file hence. */
- if (path_startswith(old_file->path, "/proc/self/fd"))
+ if (path_startswith(f->path, "/proc/self/fd"))
return -EINVAL;
- if (!endswith(old_file->path, ".journal"))
+ if (!endswith(f->path, ".journal"))
return -EINVAL;
- l = strlen(old_file->path);
- r = asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal",
- (int) l - 8, old_file->path,
- SD_ID128_FORMAT_VAL(old_file->header->seqnum_id),
- le64toh((*f)->header->head_entry_seqnum),
- le64toh((*f)->header->head_entry_realtime));
- if (r < 0)
+ if (asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal",
+ (int) strlen(f->path) - 8, f->path,
+ SD_ID128_FORMAT_VAL(f->header->seqnum_id),
+ le64toh(f->header->head_entry_seqnum),
+ le64toh(f->header->head_entry_realtime)) < 0)
return -ENOMEM;
- /* Try to rename the file to the archived version. If the file
- * already was deleted, we'll get ENOENT, let's ignore that
- * case. */
- r = rename(old_file->path, p);
- if (r < 0 && errno != ENOENT)
+ /* Try to rename the file to the archived version. If the file already was deleted, we'll get ENOENT, let's
+ * ignore that case. */
+ if (rename(f->path, p) < 0 && errno != ENOENT)
return -errno;
/* Sync the rename to disk */
- (void) fsync_directory_of_file(old_file->fd);
-
- /* Set as archive so offlining commits w/state=STATE_ARCHIVED.
- * Previously we would set old_file->header->state to STATE_ARCHIVED directly here,
- * but journal_file_set_offline() short-circuits when state != STATE_ONLINE, which
- * would result in the rotated journal never getting fsync() called before closing.
- * Now we simply queue the archive state by setting an archive bit, leaving the state
- * as STATE_ONLINE so proper offlining occurs. */
- old_file->archive = true;
-
- /* Currently, btrfs is not very good with out write patterns
- * and fragments heavily. Let's defrag our journal files when
- * we archive them */
- old_file->defrag_on_close = true;
-
- r = journal_file_open(-1, old_file->path, old_file->flags, old_file->mode, compress,
- compress_threshold_bytes, seal, NULL, old_file->mmap, deferred_closes,
- old_file, &new_file);
-
- if (deferred_closes &&
- set_put(deferred_closes, old_file) >= 0)
- (void) journal_file_set_offline(old_file, false);
- else
- (void) journal_file_close(old_file);
+ (void) fsync_directory_of_file(f->fd);
+
+ /* Set as archive so offlining commits w/state=STATE_ARCHIVED. Previously we would set old_file->header->state
+ * to STATE_ARCHIVED directly here, but journal_file_set_offline() short-circuits when state != STATE_ONLINE,
+ * which would result in the rotated journal never getting fsync() called before closing. Now we simply queue
+ * the archive state by setting an archive bit, leaving the state as STATE_ONLINE so proper offlining
+ * occurs. */
+ f->archive = true;
+
+ /* Currently, btrfs is not very good with out write patterns and fragments heavily. Let's defrag our journal
+ * files when we archive them */
+ f->defrag_on_close = true;
+
+ return 0;
+}
+
+JournalFile* journal_initiate_close(
+ JournalFile *f,
+ Set *deferred_closes) {
+
+ int r;
+
+ assert(f);
+
+ if (deferred_closes) {
+
+ r = set_put(deferred_closes, f);
+ if (r < 0)
+ log_debug_errno(r, "Failed to add file to deferred close set, closing immediately.");
+ else {
+ (void) journal_file_set_offline(f, false);
+ return NULL;
+ }
+ }
+
+ return journal_file_close(f);
+}
+
+int journal_file_rotate(
+ JournalFile **f,
+ bool compress,
+ uint64_t compress_threshold_bytes,
+ bool seal,
+ Set *deferred_closes) {
+ JournalFile *new_file = NULL;
+ int r;
+
+ assert(f);
+ assert(*f);
+
+ r = journal_file_archive(*f);
+ if (r < 0)
+ return r;
+
+ r = journal_file_open(
+ -1,
+ (*f)->path,
+ (*f)->flags,
+ (*f)->mode,
+ compress,
+ compress_threshold_bytes,
+ seal,
+ NULL, /* metrics */
+ (*f)->mmap,
+ deferred_closes,
+ *f, /* template */
+ &new_file);
+
+ journal_initiate_close(*f, deferred_closes);
*f = new_file;
+
return r;
}
+int journal_file_dispose(int dir_fd, const char *fname) {
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1;
+
+ assert(fname);
+
+ /* Renames a journal file to *.journal~, i.e. to mark it as corruped or otherwise uncleanly shutdown. Note that
+ * this is done without looking into the file or changing any of its contents. The idea is that this is called
+ * whenever something is suspicious and we want to move the file away and make clear that it is not accessed
+ * for writing anymore. */
+
+ if (!endswith(fname, ".journal"))
+ return -EINVAL;
+
+ if (asprintf(&p, "%.*s@%016" PRIx64 "-%016" PRIx64 ".journal~",
+ (int) strlen(fname) - 8, fname,
+ now(CLOCK_REALTIME),
+ random_u64()) < 0)
+ return -ENOMEM;
+
+ if (renameat(dir_fd, fname, dir_fd, p) < 0)
+ return -errno;
+
+ /* btrfs doesn't cope well with our write pattern and fragments heavily. Let's defrag all files we rotate */
+ fd = openat(dir_fd, p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ log_debug_errno(errno, "Failed to open file for defragmentation/FS_NOCOW_FL, ignoring: %m");
+ else {
+ (void) chattr_fd(fd, 0, FS_NOCOW_FL, NULL);
+ (void) btrfs_defrag_fd(fd);
+ }
+
+ return 0;
+}
+
int journal_file_open_reliably(
const char *fname,
int flags,
@@ -3528,8 +3555,6 @@ int journal_file_open_reliably(
JournalFile **ret) {
int r;
- size_t l;
- _cleanup_free_ char *p = NULL;
r = journal_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, mmap_cache,
deferred_closes, template, ret);
@@ -3555,25 +3580,12 @@ int journal_file_open_reliably(
return r;
/* The file is corrupted. Rotate it away and try it again (but only once) */
-
- l = strlen(fname);
- if (asprintf(&p, "%.*s@%016"PRIx64 "-%016"PRIx64 ".journal~",
- (int) l - 8, fname,
- now(CLOCK_REALTIME),
- random_u64()) < 0)
- return -ENOMEM;
-
- if (rename(fname, p) < 0)
- return -errno;
-
- /* btrfs doesn't cope well with our write pattern and
- * fragments heavily. Let's defrag all files we rotate */
-
- (void) chattr_path(p, 0, FS_NOCOW_FL);
- (void) btrfs_defrag(p);
-
log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
+ r = journal_file_dispose(AT_FDCWD, fname);
+ if (r < 0)
+ return r;
+
return journal_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, mmap_cache,
deferred_closes, template, ret);
}
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index c8114ee2d0..29e324d8cf 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -4,16 +4,16 @@
#include <inttypes.h>
#if HAVE_GCRYPT
-#include <gcrypt.h>
+# include <gcrypt.h>
#endif
+#include "sd-event.h"
#include "sd-id128.h"
#include "hashmap.h"
#include "journal-def.h"
#include "macro.h"
#include "mmap-cache.h"
-#include "sd-event.h"
#include "sparse-endian.h"
typedef struct JournalMetrics {
@@ -235,8 +235,12 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
void journal_file_dump(JournalFile *f);
void journal_file_print_header(JournalFile *f);
+int journal_file_archive(JournalFile *f);
+JournalFile* journal_initiate_close(JournalFile *f, Set *deferred_closes);
int journal_file_rotate(JournalFile **f, bool compress, uint64_t compress_threshold_bytes, bool seal, Set *deferred_closes);
+int journal_file_dispose(int dir_fd, const char *fname);
+
void journal_file_post_change(JournalFile *f);
int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t);
diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index a0621524a9..87056435fc 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -14,12 +14,12 @@
#include "alloc-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "io-util.h"
#include "memfd-util.h"
#include "socket-util.h"
#include "stdio-util.h"
#include "string-util.h"
+#include "tmpfile-util.h"
#include "util.h"
#define SNDBUF_SIZE (8*1024*1024)
diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c
index 8d3ae71440..2778ce40c5 100644
--- a/src/journal/journal-vacuum.c
+++ b/src/journal/journal-vacuum.c
@@ -15,6 +15,7 @@
#include "journal-vacuum.h"
#include "parse-util.h"
#include "string-util.h"
+#include "time-util.h"
#include "util.h"
#include "xattr-util.h"
@@ -29,30 +30,21 @@ struct vacuum_info {
bool have_seqnum;
};
-static int vacuum_compare(const void *_a, const void *_b) {
- const struct vacuum_info *a, *b;
-
- a = _a;
- b = _b;
+static int vacuum_compare(const struct vacuum_info *a, const struct vacuum_info *b) {
+ int r;
if (a->have_seqnum && b->have_seqnum &&
- sd_id128_equal(a->seqnum_id, b->seqnum_id)) {
- if (a->seqnum < b->seqnum)
- return -1;
- else if (a->seqnum > b->seqnum)
- return 1;
- else
- return 0;
- }
+ sd_id128_equal(a->seqnum_id, b->seqnum_id))
+ return CMP(a->seqnum, b->seqnum);
- if (a->realtime < b->realtime)
- return -1;
- else if (a->realtime > b->realtime)
- return 1;
- else if (a->have_seqnum && b->have_seqnum)
+ r = CMP(a->realtime, b->realtime);
+ if (r != 0)
+ return r;
+
+ if (a->have_seqnum && b->have_seqnum)
return memcmp(&a->seqnum_id, &b->seqnum_id, 16);
- else
- return strcmp(a->filename, b->filename);
+
+ return strcmp(a->filename, b->filename);
}
static void patch_realtime(
@@ -134,11 +126,10 @@ int journal_directory_vacuum(
usec_t *oldest_usec,
bool verbose) {
+ uint64_t sum = 0, freed = 0, n_active_files = 0;
+ size_t n_list = 0, n_allocated = 0, i;
_cleanup_closedir_ DIR *d = NULL;
struct vacuum_info *list = NULL;
- unsigned n_list = 0, i, n_active_files = 0;
- size_t n_allocated = 0;
- uint64_t sum = 0, freed = 0;
usec_t retention_limit = 0;
char sbytes[FORMAT_BYTES_MAX];
struct dirent *de;
@@ -149,13 +140,8 @@ int journal_directory_vacuum(
if (max_use <= 0 && max_retention_usec <= 0 && n_max_files <= 0)
return 0;
- if (max_retention_usec > 0) {
- retention_limit = now(CLOCK_REALTIME);
- if (retention_limit > max_retention_usec)
- retention_limit -= max_retention_usec;
- else
- max_retention_usec = retention_limit = 0;
- }
+ if (max_retention_usec > 0)
+ retention_limit = usec_sub_unsigned(now(CLOCK_REALTIME), max_retention_usec);
d = opendir(directory);
if (!d)
@@ -281,21 +267,22 @@ int journal_directory_vacuum(
goto finish;
}
- list[n_list].filename = TAKE_PTR(p);
- list[n_list].usage = size;
- list[n_list].seqnum = seqnum;
- list[n_list].realtime = realtime;
- list[n_list].seqnum_id = seqnum_id;
- list[n_list].have_seqnum = have_seqnum;
- n_list++;
+ list[n_list++] = (struct vacuum_info) {
+ .filename = TAKE_PTR(p),
+ .usage = size,
+ .seqnum = seqnum,
+ .realtime = realtime,
+ .seqnum_id = seqnum_id,
+ .have_seqnum = have_seqnum,
+ };
sum += size;
}
- qsort_safe(list, n_list, sizeof(struct vacuum_info), vacuum_compare);
+ typesafe_qsort(list, n_list, vacuum_compare);
for (i = 0; i < n_list; i++) {
- unsigned left;
+ uint64_t left;
left = n_active_files + n_list - i;
diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
index c2f0467c6e..5eff80a99f 100644
--- a/src/journal/journal-verify.c
+++ b/src/journal/journal-verify.c
@@ -17,6 +17,7 @@
#include "lookup3.h"
#include "macro.h"
#include "terminal-util.h"
+#include "tmpfile-util.h"
#include "util.h"
static void draw_progress(uint64_t p, usec_t *last_usec) {
@@ -60,10 +61,11 @@ static void draw_progress(uint64_t p, usec_t *last_usec) {
}
static uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) {
+ /* Calculates scale * p / m, but handles m == 0 safely, and saturates.
+ * Currently all callers use m >= 1, but we keep the check to be defensive.
+ */
- /* Calculates scale * p / m, but handles m == 0 safely, and saturates */
-
- if (p >= m || m == 0)
+ if (p >= m || m == 0) /* lgtm [cpp/constant-comparison] */
return scale;
return scale * p / m;
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 56b1be530d..14a02eda74 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -22,6 +22,7 @@
#endif
#include "sd-bus.h"
+#include "sd-device.h"
#include "sd-journal.h"
#include "acl-util.h"
@@ -30,12 +31,15 @@
#include "bus-util.h"
#include "catalog.h"
#include "chattr-util.h"
+#include "def.h"
+#include "device-private.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "fsprg.h"
#include "glob-util.h"
#include "hostname-util.h"
+#include "id128-print.h"
#include "io-util.h"
#include "journal-def.h"
#include "journal-internal.h"
@@ -50,6 +54,7 @@
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "rlimit-util.h"
#include "set.h"
#include "sigbus.h"
@@ -57,8 +62,7 @@
#include "strv.h"
#include "syslog-util.h"
#include "terminal-util.h"
-#include "udev-util.h"
-#include "udev.h"
+#include "tmpfile-util.h"
#include "unit-name.h"
#include "user-util.h"
@@ -82,10 +86,9 @@ static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out
r = pcre2_get_error_message(errorcode, buf, sizeof buf);
- log_error("Bad pattern \"%s\": %s",
- pattern,
- r < 0 ? "unknown error" : (char*) buf);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Bad pattern \"%s\": %s", pattern,
+ r < 0 ? "unknown error" : (char *)buf);
}
*out = p;
@@ -102,11 +105,10 @@ enum {
static OutputMode arg_output = OUTPUT_SHORT;
static bool arg_utc = false;
-static bool arg_pager_end = false;
static bool arg_follow = false;
static bool arg_full = true;
static bool arg_all = false;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static int arg_lines = ARG_LINES_DEFAULT;
static bool arg_no_tail = false;
static bool arg_quiet = false;
@@ -165,6 +167,7 @@ static enum {
ACTION_SYNC,
ACTION_ROTATE,
ACTION_VACUUM,
+ ACTION_ROTATE_AND_VACUUM,
ACTION_LIST_FIELDS,
ACTION_LIST_FIELD_NAMES,
} arg_action = ACTION_SHOW;
@@ -177,9 +180,8 @@ typedef struct BootId {
} BootId;
static int add_matches_for_device(sd_journal *j, const char *devpath) {
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
- _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
- struct udev_device *d = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ sd_device *d = NULL;
struct stat st;
int r;
@@ -191,33 +193,25 @@ static int add_matches_for_device(sd_journal *j, const char *devpath) {
return -EINVAL;
}
- udev = udev_new();
- if (!udev)
- return log_oom();
-
if (stat(devpath, &st) < 0)
return log_error_errno(errno, "Couldn't stat file: %m");
- r = udev_device_new_from_stat_rdev(udev, &st, &device);
+ r = device_new_from_stat_rdev(&device, &st);
if (r < 0)
- return log_error_errno(r, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
+ return log_error_errno(r, "Failed to get device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
- d = device;
- while (d) {
+ for (d = device; d; ) {
_cleanup_free_ char *match = NULL;
const char *subsys, *sysname, *devnode;
+ sd_device *parent;
- subsys = udev_device_get_subsystem(d);
- if (!subsys) {
- d = udev_device_get_parent(d);
- continue;
- }
+ r = sd_device_get_subsystem(d, &subsys);
+ if (r < 0)
+ goto get_parent;
- sysname = udev_device_get_sysname(d);
- if (!sysname) {
- d = udev_device_get_parent(d);
- continue;
- }
+ r = sd_device_get_sysname(d, &sysname);
+ if (r < 0)
+ goto get_parent;
match = strjoin("_KERNEL_DEVICE=+", subsys, ":", sysname);
if (!match)
@@ -227,8 +221,7 @@ static int add_matches_for_device(sd_journal *j, const char *devpath) {
if (r < 0)
return log_error_errno(r, "Failed to add match: %m");
- devnode = udev_device_get_devnode(d);
- if (devnode) {
+ if (sd_device_get_devname(d, &devnode) >= 0) {
_cleanup_free_ char *match1 = NULL;
r = stat(devnode, &st);
@@ -244,7 +237,11 @@ static int add_matches_for_device(sd_journal *j, const char *devpath) {
return log_error_errno(r, "Failed to add match: %m");
}
- d = udev_device_get_parent(d);
+get_parent:
+ if (sd_device_get_parent(d, &parent) < 0)
+ break;
+
+ d = parent;
}
r = add_match_this_boot(j, arg_machine);
@@ -297,9 +294,15 @@ static int parse_boot_descriptor(const char *x, sd_id128_t *boot_id, int *offset
return 0;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
- (void) pager_open(arg_no_pager, arg_pager_end);
+ (void) pager_open(arg_pager_flags);
+
+ r = terminal_urlify_man("journalctl", "1", &link);
+ if (r < 0)
+ return log_oom();
printf("%s [OPTIONS...] [MATCHES...]\n\n"
"Query the journal.\n\n"
@@ -329,7 +332,8 @@ static void help(void) {
" -o --output=STRING Change journal output mode (short, short-precise,\n"
" short-iso, short-iso-precise, short-full,\n"
" short-monotonic, short-unix, verbose, export,\n"
- " json, json-pretty, json-sse, cat, with-unit)\n"
+ " json, json-pretty, json-sse, json-seq, cat,\n"
+ " with-unit)\n"
" --output-fields=LIST Select fields to print in verbose/export/json modes\n"
" --utc Express time in Coordinated Universal Time (UTC)\n"
" -x --catalog Add message explanations where available\n"
@@ -342,11 +346,9 @@ static void 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"
-#if HAVE_GCRYPT
" --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"
-#endif
"\nCommands:\n"
" -h --help Show this help text\n"
" --version Show package version\n"
@@ -364,11 +366,13 @@ static void help(void) {
" --list-catalog Show all message IDs in the catalog\n"
" --dump-catalog Show entries in the message catalog\n"
" --update-catalog Update the message catalog database\n"
- " --new-id128 Generate a new 128-bit ID\n"
-#if HAVE_GCRYPT
" --setup-keys Generate a new FSS key pair\n"
-#endif
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -423,7 +427,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "no-full", no_argument, NULL, ARG_NO_FULL },
{ "lines", optional_argument, NULL, 'n' },
{ "no-tail", no_argument, NULL, ARG_NO_TAIL },
- { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
+ { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, /* deprecated */
{ "quiet", no_argument, NULL, 'q' },
{ "merge", no_argument, NULL, 'm' },
{ "this-boot", no_argument, NULL, ARG_THIS_BOOT }, /* deprecated */
@@ -482,18 +486,17 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case 'e':
- arg_pager_end = true;
+ arg_pager_flags |= PAGER_JUMP_TO_END;
if (arg_lines == ARG_LINES_DEFAULT)
arg_lines = 1000;
@@ -516,7 +519,7 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_CAT))
+ if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_JSON_SEQ, OUTPUT_CAT))
arg_quiet = true;
break;
@@ -685,7 +688,7 @@ static int parse_argv(int argc, char *argv[]) {
return r;
}
- arg_action = ACTION_VACUUM;
+ arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
case ARG_VACUUM_FILES:
@@ -695,7 +698,7 @@ static int parse_argv(int argc, char *argv[]) {
return r;
}
- arg_action = ACTION_VACUUM;
+ arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
case ARG_VACUUM_TIME:
@@ -705,7 +708,7 @@ static int parse_argv(int argc, char *argv[]) {
return r;
}
- arg_action = ACTION_VACUUM;
+ arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
#if HAVE_GCRYPT
@@ -741,7 +744,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_VERIFY_KEY:
case ARG_INTERVAL:
case ARG_FORCE:
- log_error("Forward-secure sealing not available.");
+ log_error("Compiled without forward-secure sealing support.");
return -EOPNOTSUPP;
#endif
@@ -894,7 +897,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_ROTATE:
- arg_action = ACTION_ROTATE;
+ arg_action = arg_action == ACTION_VACUUM ? ACTION_ROTATE_AND_VACUUM : ACTION_ROTATE;
break;
case ARG_SYNC:
@@ -1007,35 +1010,6 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-static int generate_new_id128(void) {
- sd_id128_t id;
- int r;
- unsigned i;
-
- r = sd_id128_randomize(&id);
- if (r < 0)
- return log_error_errno(r, "Failed to generate ID: %m");
-
- printf("As string:\n"
- SD_ID128_FORMAT_STR "\n\n"
- "As UUID:\n"
- "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
- "As man:sd-id128(3) macro:\n"
- "#define MESSAGE_XYZ SD_ID128_MAKE(",
- SD_ID128_FORMAT_VAL(id),
- SD_ID128_FORMAT_VAL(id));
- for (i = 0; i < 16; i++)
- printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
- fputs(")\n\n", stdout);
-
- printf("As Python constant:\n"
- ">>> import uuid\n"
- ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
- SD_ID128_FORMAT_VAL(id));
-
- return 0;
-}
-
static int add_matches(sd_journal *j, char **args) {
char **i;
bool have_term = false;
@@ -1096,10 +1070,10 @@ static int add_matches(sd_journal *j, char **args) {
r = add_matches_for_device(j, p);
if (r < 0)
return r;
- } else {
- log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "File is neither a device node, nor regular file, nor executable: %s",
+ *i);
have_term = true;
} else {
@@ -1111,10 +1085,9 @@ static int add_matches(sd_journal *j, char **args) {
return log_error_errno(r, "Failed to add match '%s': %m", *i);
}
- if (!strv_isempty(args) && !have_term) {
- log_error("\"+\" can only be used between terms");
- return -EINVAL;
- }
+ if (!strv_isempty(args) && !have_term)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "\"+\" can only be used between terms");
return 0;
}
@@ -1205,10 +1178,9 @@ static int discover_next_boot(sd_journal *j,
r = sd_journal_previous(j);
if (r < 0)
return r;
- else if (r == 0) {
- log_debug("Whoopsie! We found a boot ID but can't read its last entry.");
- return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */
- }
+ else if (r == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENODATA),
+ "Whoopsie! We found a boot ID but can't read its last entry."); /* This shouldn't happen. We just came from this very boot ID. */
r = sd_journal_get_realtime_usec(j, &next_boot->last);
if (r < 0)
@@ -1352,7 +1324,7 @@ static int list_boots(sd_journal *j) {
if (count == 0)
return count;
- (void) pager_open(arg_no_pager, arg_pager_end);
+ (void) pager_open(arg_pager_flags);
/* numbers are one less, but we need an extra char for the sign */
w = DECIMAL_STR_WIDTH(count - 1) + 1;
@@ -1761,7 +1733,7 @@ static int setup_keys(void) {
/* Enable secure remove, exclusion from dump, synchronous
* writing and in-place updating */
- r = chattr_fd(fd, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL);
+ r = chattr_fd(fd, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, NULL);
if (r < 0)
log_warning_errno(r, "Failed to set file attributes: %m");
@@ -1859,8 +1831,8 @@ finish:
return r;
#else
- log_error("Forward-secure sealing not available.");
- return -EOPNOTSUPP;
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Forward-secure sealing not available.");
#endif
}
@@ -1997,7 +1969,7 @@ static int send_signal_and_wait(int sig, const char *watch_path) {
/* See if a sync happened by now. */
r = read_timestamp_file(watch_path, &tstamp);
if (r < 0 && r != -ENOENT)
- return log_error_errno(errno, "Failed to read %s: %m", watch_path);
+ return log_error_errno(r, "Failed to read %s: %m", watch_path);
if (r >= 0 && tstamp >= start)
return 0;
@@ -2064,19 +2036,59 @@ static int sync_journal(void) {
return send_signal_and_wait(SIGRTMIN+1, "/run/systemd/journal/synced");
}
-int main(int argc, char *argv[]) {
+static int wait_for_change(sd_journal *j, int poll_fd) {
+ struct pollfd pollfds[] = {
+ { .fd = poll_fd, .events = POLLIN },
+ { .fd = STDOUT_FILENO },
+ };
+
+ struct timespec ts;
+ usec_t timeout;
int r;
+
+ assert(j);
+ assert(poll_fd >= 0);
+
+ /* Much like sd_journal_wait() but also keeps an eye on STDOUT, and exits as soon as we see a POLLHUP on that,
+ * i.e. when it is closed. */
+
+ r = sd_journal_get_timeout(j, &timeout);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine journal waiting time: %m");
+
+ if (ppoll(pollfds, ELEMENTSOF(pollfds),
+ timeout == USEC_INFINITY ? NULL : timespec_store(&ts, timeout), NULL) < 0) {
+ if (errno == EINTR)
+ return 0;
+
+ return log_error_errno(errno, "Couldn't wait for journal event: %m");
+ }
+
+ if (pollfds[1].revents & (POLLHUP|POLLERR)) /* STDOUT has been closed? */
+ return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED),
+ "Standard output has been closed.");
+
+ r = sd_journal_process(j);
+ if (r < 0)
+ return log_error_errno(r, "Failed to process journal events: %m");
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ bool previous_boot_id_valid = false, first_line = true, ellipsized = false, need_seek = false;
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
- bool need_seek = false;
sd_id128_t previous_boot_id;
- bool previous_boot_id_valid = false, first_line = true;
- int n_shown = 0;
- bool ellipsized = false;
+ int n_shown = 0, r, poll_fd = -1;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
+ /* Increase max number of open files if we can, we might needs this when browsing journal files, which might be
+ * split up into many files. */
+ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
@@ -2084,15 +2096,10 @@ int main(int argc, char *argv[]) {
signal(SIGWINCH, columns_lines_cache_reset);
sigbus_install();
- /* Increase max number of open files to 16K if we can, we
- * might needs this when browsing journal files, which might
- * be split up into many files. */
- setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384));
-
switch (arg_action) {
case ACTION_NEW_ID128:
- r = generate_new_id128();
+ r = id128_print_new(true);
goto finish;
case ACTION_SETUP_KEYS:
@@ -2104,7 +2111,7 @@ int main(int argc, char *argv[]) {
case ACTION_UPDATE_CATALOG: {
_cleanup_free_ char *database;
- database = path_join(arg_root, CATALOG_DATABASE, NULL);
+ database = path_join(arg_root, CATALOG_DATABASE);
if (!database) {
r = log_oom();
goto finish;
@@ -2117,7 +2124,7 @@ int main(int argc, char *argv[]) {
} else {
bool oneline = arg_action == ACTION_LIST_CATALOG;
- (void) pager_open(arg_no_pager, arg_pager_end);
+ (void) pager_open(arg_pager_flags);
if (optind < argc)
r = catalog_list_items(stdout, database, oneline, argv + optind);
@@ -2148,6 +2155,7 @@ int main(int argc, char *argv[]) {
case ACTION_DISK_USAGE:
case ACTION_LIST_BOOTS:
case ACTION_VACUUM:
+ case ACTION_ROTATE_AND_VACUUM:
case ACTION_LIST_FIELDS:
case ACTION_LIST_FIELD_NAMES:
/* These ones require access to the journal files, continue below. */
@@ -2265,6 +2273,14 @@ int main(int argc, char *argv[]) {
r = list_boots(j);
goto finish;
+ case ACTION_ROTATE_AND_VACUUM:
+
+ r = rotate();
+ if (r < 0)
+ goto finish;
+
+ _fallthrough_;
+
case ACTION_VACUUM: {
Directory *d;
Iterator i;
@@ -2391,15 +2407,15 @@ int main(int argc, char *argv[]) {
/* Opening the fd now means the first sd_journal_wait() will actually wait */
if (arg_follow) {
- r = sd_journal_get_fd(j);
- if (r == -EMFILE) {
- log_warning("Insufficent watch descriptors available. Reverting to -n.");
+ poll_fd = sd_journal_get_fd(j);
+ if (poll_fd == -EMFILE) {
+ log_warning_errno(poll_fd, "Insufficent watch descriptors available. Reverting to -n.");
arg_follow = false;
- } else if (r == -EMEDIUMTYPE) {
- log_error_errno(r, "The --follow switch is not supported in conjunction with reading from STDIN.");
+ } else if (poll_fd == -EMEDIUMTYPE) {
+ log_error_errno(poll_fd, "The --follow switch is not supported in conjunction with reading from STDIN.");
goto finish;
- } else if (r < 0) {
- log_error_errno(r, "Failed to get journal fd: %m");
+ } else if (poll_fd < 0) {
+ log_error_errno(poll_fd, "Failed to get journal fd: %m");
goto finish;
}
}
@@ -2476,7 +2492,7 @@ int main(int argc, char *argv[]) {
need_seek = true;
if (!arg_follow)
- (void) pager_open(arg_no_pager, arg_pager_end);
+ (void) pager_open(arg_pager_flags);
if (!arg_quiet && (arg_lines != 0 || arg_follow)) {
usec_t start, end;
@@ -2621,7 +2637,7 @@ int main(int argc, char *argv[]) {
need_seek = true;
if (r == -EADDRNOTAVAIL)
break;
- else if (r < 0 || ferror(stdout))
+ else if (r < 0)
goto finish;
n_shown++;
@@ -2659,11 +2675,10 @@ int main(int argc, char *argv[]) {
}
fflush(stdout);
- r = sd_journal_wait(j, (uint64_t) -1);
- if (r < 0) {
- log_error_errno(r, "Couldn't wait for journal event: %m");
+
+ r = wait_for_change(j, poll_fd);
+ if (r < 0)
goto finish;
- }
first_line = false;
}
diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c
index 87726684af..345e43ef44 100644
--- a/src/journal/journald-audit.c
+++ b/src/journal/journald-audit.c
@@ -313,7 +313,7 @@ static int map_all_fields(
}
}
-static void process_audit_string(Server *s, int type, const char *data, size_t size) {
+void process_audit_string(Server *s, int type, const char *data, size_t size) {
size_t n_iov_allocated = 0, n_iov = 0, z;
_cleanup_free_ struct iovec *iov = NULL;
uint64_t seconds, msec, id;
@@ -341,11 +341,12 @@ static void process_audit_string(Server *s, int type, const char *data, size_t s
if (!p)
return;
+ k = 0;
if (sscanf(p, "(%" PRIu64 ".%" PRIu64 ":%" PRIu64 "):%n",
&seconds,
&msec,
&id,
- &k) != 3)
+ &k) != 3 || k == 0)
return;
p += k;
@@ -497,7 +498,6 @@ static int enable_audit(int fd, bool b) {
}
int server_open_audit(Server *s) {
- static const int one = 1;
int r;
if (s->audit_fd < 0) {
@@ -526,11 +526,11 @@ int server_open_audit(Server *s) {
return 0;
}
} else
- fd_nonblock(s->audit_fd, 1);
+ (void) fd_nonblock(s->audit_fd, true);
- r = setsockopt(s->audit_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ r = setsockopt_int(s->audit_fd, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0)
- return log_error_errno(errno, "Failed to set SO_PASSCRED on audit socket: %m");
+ return log_error_errno(r, "Failed to set SO_PASSCRED on audit socket: %m");
r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, server_process_datagram, s);
if (r < 0)
diff --git a/src/journal/journald-audit.h b/src/journal/journald-audit.h
index 57bb1711c9..df41f81435 100644
--- a/src/journal/journald-audit.h
+++ b/src/journal/journald-audit.h
@@ -6,4 +6,6 @@
void server_process_audit_message(Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const union sockaddr_union *sa, socklen_t salen);
-int server_open_audit(Server*s);
+void process_audit_string(Server *s, int type, const char *data, size_t size);
+
+int server_open_audit(Server *s);
diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c
index ce07de1bfb..8253a45128 100644
--- a/src/journal/journald-context.c
+++ b/src/journal/journald-context.c
@@ -13,6 +13,8 @@
#include "io-util.h"
#include "journal-util.h"
#include "journald-context.h"
+#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
#include "string-util.h"
#include "syslog-util.h"
@@ -62,18 +64,13 @@
static int client_context_compare(const void *a, const void *b) {
const ClientContext *x = a, *y = b;
+ int r;
- if (x->timestamp < y->timestamp)
- return -1;
- if (x->timestamp > y->timestamp)
- return 1;
-
- if (x->pid < y->pid)
- return -1;
- if (x->pid > y->pid)
- return 1;
+ r = CMP(x->timestamp, y->timestamp);
+ if (r != 0)
+ return r;
- return 0;
+ return CMP(x->pid, y->pid);
}
static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
@@ -107,6 +104,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
c->timestamp = USEC_INFINITY;
c->extra_fields_mtime = NSEC_INFINITY;
c->log_level_max = -1;
+ c->log_rate_limit_interval = s->rate_limit_interval;
+ c->log_rate_limit_burst = s->rate_limit_burst;
r = hashmap_put(s->client_contexts, PID_TO_PTR(pid), c);
if (r < 0) {
@@ -118,7 +117,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
return 0;
}
-static void client_context_reset(ClientContext *c) {
+static void client_context_reset(Server *s, ClientContext *c) {
+ assert(s);
assert(c);
c->timestamp = USEC_INFINITY;
@@ -153,6 +153,9 @@ static void client_context_reset(ClientContext *c) {
c->extra_fields_mtime = NSEC_INFINITY;
c->log_level_max = -1;
+
+ c->log_rate_limit_interval = s->rate_limit_interval;
+ c->log_rate_limit_burst = s->rate_limit_burst;
}
static ClientContext* client_context_free(Server *s, ClientContext *c) {
@@ -166,7 +169,7 @@ static ClientContext* client_context_free(Server *s, ClientContext *c) {
if (c->in_lru)
assert_se(prioq_remove(s->client_contexts_lru, c, &c->lru_index) >= 0);
- client_context_reset(c);
+ client_context_reset(s, c);
return mfree(c);
}
@@ -250,9 +253,11 @@ static int client_context_read_cgroup(Server *s, ClientContext *c, const char *u
/* Try to acquire the current cgroup path */
r = cg_pid_get_path_shifted(c->pid, s->cgroup_root, &t);
- if (r < 0) {
+ if (r < 0 || empty_or_root(t)) {
- /* If that didn't work, we use the unit ID passed in as fallback, if we have nothing cached yet */
+ /* We use the unit ID passed in as fallback if we have nothing cached yet and cg_pid_get_path_shifted()
+ * failed or process is running in a root cgroup. Zombie processes are automatically migrated to root cgroup
+ * on cgroupsv1 and we want to be able to map log messages from them too. */
if (unit_id && !c->unit) {
c->unit = strdup(unit_id);
if (c->unit)
@@ -429,6 +434,42 @@ static int client_context_read_extra_fields(
return 0;
}
+static int client_context_read_log_rate_limit_interval(ClientContext *c) {
+ _cleanup_free_ char *value = NULL;
+ const char *p;
+ int r;
+
+ assert(c);
+
+ if (!c->unit)
+ return 0;
+
+ p = strjoina("/run/systemd/units/log-rate-limit-interval:", c->unit);
+ r = readlink_malloc(p, &value);
+ if (r < 0)
+ return r;
+
+ return safe_atou64(value, &c->log_rate_limit_interval);
+}
+
+static int client_context_read_log_rate_limit_burst(ClientContext *c) {
+ _cleanup_free_ char *value = NULL;
+ const char *p;
+ int r;
+
+ assert(c);
+
+ if (!c->unit)
+ return 0;
+
+ p = strjoina("/run/systemd/units/log-rate-limit-burst:", c->unit);
+ r = readlink_malloc(p, &value);
+ if (r < 0)
+ return r;
+
+ return safe_atou(value, &c->log_rate_limit_burst);
+}
+
static void client_context_really_refresh(
Server *s,
ClientContext *c,
@@ -455,6 +496,8 @@ static void client_context_really_refresh(
(void) client_context_read_invocation_id(s, c);
(void) client_context_read_log_level_max(s, c);
(void) client_context_read_extra_fields(s, c);
+ (void) client_context_read_log_rate_limit_interval(c);
+ (void) client_context_read_log_rate_limit_burst(c);
c->timestamp = timestamp;
@@ -485,7 +528,7 @@ void client_context_maybe_refresh(
/* If the data isn't pinned and if the cashed data is older than the upper limit, we flush it out
* entirely. This follows the logic that as long as an entry is pinned the PID reuse is unlikely. */
if (c->n_ref == 0 && c->timestamp + MAX_USEC < timestamp) {
- client_context_reset(c);
+ client_context_reset(s, c);
goto refresh;
}
diff --git a/src/journal/journald-context.h b/src/journal/journald-context.h
index 9df3a38eff..5e19c71f14 100644
--- a/src/journal/journald-context.h
+++ b/src/journal/journald-context.h
@@ -49,6 +49,9 @@ struct ClientContext {
size_t extra_fields_n_iovec;
void *extra_fields_data;
nsec_t extra_fields_mtime;
+
+ usec_t log_rate_limit_interval;
+ unsigned log_rate_limit_burst;
};
int client_context_get(
diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c
index e9aff13168..ce82102eed 100644
--- a/src/journal/journald-kmsg.c
+++ b/src/journal/journald-kmsg.c
@@ -6,10 +6,11 @@
#include <sys/socket.h>
#include <unistd.h>
-#include "libudev.h"
+#include "sd-device.h"
#include "sd-messages.h"
#include "alloc-util.h"
+#include "device-util.h"
#include "escape.h"
#include "fd-util.h"
#include "format-util.h"
@@ -93,7 +94,7 @@ static bool is_us(const char *identifier, const char *pid) {
streq(identifier, program_invocation_short_name);
}
-static void dev_kmsg_record(Server *s, const char *p, size_t l) {
+void dev_kmsg_record(Server *s, char *p, size_t l) {
_cleanup_free_ char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL, *identifier = NULL, *pid = NULL;
struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS];
@@ -191,7 +192,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
e = memchr(k, '\n', l);
if (!e)
- return;
+ goto finish;
*e = 0;
@@ -209,16 +210,13 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
}
if (kernel_device) {
- struct udev_device *ud;
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
- ud = udev_device_new_from_device_id(s->udev, kernel_device);
- if (ud) {
+ if (sd_device_new_from_device_id(&d, kernel_device) >= 0) {
const char *g;
- struct udev_list_entry *ll;
char *b;
- g = udev_device_get_devnode(ud);
- if (g) {
+ if (sd_device_get_devname(d, &g) >= 0) {
b = strappend("_UDEV_DEVNODE=", g);
if (b) {
iovec[n++] = IOVEC_MAKE_STRING(b);
@@ -226,8 +224,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
}
}
- g = udev_device_get_sysname(ud);
- if (g) {
+ if (sd_device_get_sysname(d, &g) >= 0) {
b = strappend("_UDEV_SYSNAME=", g);
if (b) {
iovec[n++] = IOVEC_MAKE_STRING(b);
@@ -236,25 +233,19 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
}
j = 0;
- ll = udev_device_get_devlinks_list_entry(ud);
- udev_list_entry_foreach(ll, ll) {
+ FOREACH_DEVICE_DEVLINK(d, g) {
- if (j > N_IOVEC_UDEV_FIELDS)
+ if (j >= N_IOVEC_UDEV_FIELDS)
break;
- g = udev_list_entry_get_name(ll);
- if (g) {
- b = strappend("_UDEV_DEVLINK=", g);
- if (b) {
- iovec[n++] = IOVEC_MAKE_STRING(b);
- z++;
- }
+ b = strappend("_UDEV_DEVLINK=", g);
+ if (b) {
+ iovec[n++] = IOVEC_MAKE_STRING(b);
+ z++;
}
j++;
}
-
- udev_device_unref(ud);
}
}
diff --git a/src/journal/journald-kmsg.h b/src/journal/journald-kmsg.h
index bff24ac310..2326bc8c93 100644
--- a/src/journal/journald-kmsg.h
+++ b/src/journal/journald-kmsg.h
@@ -9,3 +9,5 @@ int server_flush_dev_kmsg(Server *s);
void server_forward_kmsg(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred);
int server_open_kernel_seqnum(Server *s);
+
+void dev_kmsg_record(Server *s, char *p, size_t l);
diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c
index 5ff22a10af..e86178ed77 100644
--- a/src/journal/journald-native.c
+++ b/src/journal/journald-native.c
@@ -25,6 +25,7 @@
#include "selinux-util.h"
#include "socket-util.h"
#include "string-util.h"
+#include "strv.h"
#include "unaligned.h"
static bool allow_object_pid(const struct ucred *ucred) {
@@ -205,8 +206,7 @@ static int server_process_entry(
memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
if (journal_field_valid(p, e - p, false)) {
- iovec[n].iov_base = k;
- iovec[n].iov_len = (e - p) + 1 + l;
+ iovec[n] = IOVEC_MAKE(k, (e - p) + 1 + l);
entry_size += iovec[n].iov_len;
n++;
@@ -277,7 +277,7 @@ finish:
void server_process_native_message(
Server *s,
- const void *buffer, size_t buffer_size,
+ const char *buffer, size_t buffer_size,
const struct ucred *ucred,
const struct timeval *tv,
const char *label, size_t label_len) {
@@ -337,11 +337,7 @@ void server_process_native_file(
return;
}
- e = path_startswith(k, "/dev/shm/");
- if (!e)
- e = path_startswith(k, "/tmp/");
- if (!e)
- e = path_startswith(k, "/var/tmp/");
+ e = PATH_STARTSWITH_SET(k, "/dev/shm/", "/tmp/", "/var/tmp/");
if (!e) {
log_error("Received file outside of allowed directories. Refusing.");
return;
@@ -437,13 +433,12 @@ void server_process_native_file(
}
}
-int server_open_native_socket(Server*s) {
+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",
};
- static const int one = 1;
int r;
assert(s);
@@ -453,7 +448,7 @@ int server_open_native_socket(Server*s) {
if (s->native_fd < 0)
return log_error_errno(errno, "socket() failed: %m");
- (void) unlink(sa.un.sun_path);
+ (void) sockaddr_un_unlink(&sa.un);
r = bind(s->native_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
if (r < 0)
@@ -461,23 +456,23 @@ int server_open_native_socket(Server*s) {
(void) chmod(sa.un.sun_path, 0666);
} else
- fd_nonblock(s->native_fd, 1);
+ (void) fd_nonblock(s->native_fd, true);
- r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ r = setsockopt_int(s->native_fd, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0)
- return log_error_errno(errno, "SO_PASSCRED failed: %m");
+ return log_error_errno(r, "SO_PASSCRED failed: %m");
#if HAVE_SELINUX
if (mac_selinux_use()) {
- r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
+ r = setsockopt_int(s->native_fd, SOL_SOCKET, SO_PASSSEC, true);
if (r < 0)
- log_warning_errno(errno, "SO_PASSSEC failed: %m");
+ log_warning_errno(r, "SO_PASSSEC failed: %m");
}
#endif
- r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
+ r = setsockopt_int(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, true);
if (r < 0)
- return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
+ return log_error_errno(r, "SO_TIMESTAMP failed: %m");
r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, server_process_datagram, s);
if (r < 0)
diff --git a/src/journal/journald-native.h b/src/journal/journald-native.h
index 7211d4fab4..2a33ef74c5 100644
--- a/src/journal/journald-native.h
+++ b/src/journal/journald-native.h
@@ -5,7 +5,7 @@
void server_process_native_message(
Server *s,
- const void *buffer,
+ const char *buffer,
size_t buffer_size,
const struct ucred *ucred,
const struct timeval *tv,
diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c
index 6a8a36a736..0b42d53760 100644
--- a/src/journal/journald-rate-limit.c
+++ b/src/journal/journald-rate-limit.c
@@ -39,6 +39,10 @@ struct JournalRateLimitGroup {
JournalRateLimit *parent;
char *id;
+
+ /* Interval is stored to keep track of when the group expires */
+ usec_t interval;
+
JournalRateLimitPool pools[POOLS_MAX];
uint64_t hash;
@@ -47,8 +51,6 @@ struct JournalRateLimitGroup {
};
struct JournalRateLimit {
- usec_t interval;
- unsigned burst;
JournalRateLimitGroup* buckets[BUCKETS_MAX];
JournalRateLimitGroup *lru, *lru_tail;
@@ -58,18 +60,13 @@ struct JournalRateLimit {
uint8_t hash_key[16];
};
-JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst) {
+JournalRateLimit *journal_rate_limit_new(void) {
JournalRateLimit *r;
- assert(interval > 0 || burst == 0);
-
r = new0(JournalRateLimit, 1);
if (!r)
return NULL;
- r->interval = interval;
- r->burst = burst;
-
random_bytes(r->hash_key, sizeof(r->hash_key));
return r;
@@ -109,7 +106,7 @@ _pure_ static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, us
assert(g);
for (i = 0; i < POOLS_MAX; i++)
- if (g->pools[i].begin + g->parent->interval >= ts)
+ if (g->pools[i].begin + g->interval >= ts)
return false;
return true;
@@ -126,9 +123,8 @@ static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) {
journal_rate_limit_group_free(r->lru_tail);
}
-static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t ts) {
+static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t interval, usec_t ts) {
JournalRateLimitGroup *g;
- struct siphash state;
assert(r);
assert(id);
@@ -141,9 +137,9 @@ static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r,
if (!g->id)
goto fail;
- siphash24_init(&state, r->hash_key);
- string_hash_func(g->id, &state);
- g->hash = siphash24_finalize(&state);
+ g->hash = siphash24_string(g->id, r->hash_key);
+
+ g->interval = interval;
journal_rate_limit_vacuum(r, ts);
@@ -189,11 +185,10 @@ static unsigned burst_modulate(unsigned burst, uint64_t available) {
return burst;
}
-int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) {
+int journal_rate_limit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available) {
uint64_t h;
JournalRateLimitGroup *g;
JournalRateLimitPool *p;
- struct siphash state;
unsigned burst;
usec_t ts;
@@ -209,16 +204,9 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
if (!r)
return 1;
- if (r->interval == 0 || r->burst == 0)
- return 1;
-
- burst = burst_modulate(r->burst, available);
-
ts = now(CLOCK_MONOTONIC);
- siphash24_init(&state, r->hash_key);
- string_hash_func(id, &state);
- h = siphash24_finalize(&state);
+ h = siphash24_string(id, r->hash_key);
g = r->buckets[h % BUCKETS_MAX];
LIST_FOREACH(bucket, g, g)
@@ -226,10 +214,16 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
break;
if (!g) {
- g = journal_rate_limit_group_new(r, id, ts);
+ g = journal_rate_limit_group_new(r, id, rl_interval, ts);
if (!g)
return -ENOMEM;
- }
+ } else
+ g->interval = rl_interval;
+
+ if (rl_interval == 0 || rl_burst == 0)
+ return 1;
+
+ burst = burst_modulate(rl_burst, available);
p = &g->pools[priority_map[priority]];
@@ -240,7 +234,7 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
return 1;
}
- if (p->begin + r->interval < ts) {
+ if (p->begin + rl_interval < ts) {
unsigned s;
s = p->suppressed;
diff --git a/src/journal/journald-rate-limit.h b/src/journal/journald-rate-limit.h
index 3a7f106de0..a2992800fe 100644
--- a/src/journal/journald-rate-limit.h
+++ b/src/journal/journald-rate-limit.h
@@ -5,6 +5,6 @@
typedef struct JournalRateLimit JournalRateLimit;
-JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst);
+JournalRateLimit *journal_rate_limit_new(void);
void journal_rate_limit_free(JournalRateLimit *r);
-int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available);
+int journal_rate_limit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available);
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 4f1550ec5b..434325c179 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -9,7 +9,6 @@
#include <sys/statvfs.h>
#include <linux/sockios.h>
-#include "libudev.h"
#include "sd-daemon.h"
#include "sd-journal.h"
#include "sd-messages.h"
@@ -75,6 +74,8 @@
* for a bit of additional metadata. */
#define DEFAULT_LINE_MAX (48*1024)
+#define DEFERRED_CLOSES_MAX (4096)
+
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;
@@ -253,8 +254,9 @@ static int open_journal(
bool seal,
JournalMetrics *metrics,
JournalFile **ret) {
- int r;
+
JournalFile *f;
+ int r;
assert(s);
assert(fname);
@@ -309,7 +311,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
server_add_acls(s->system_journal, 0);
(void) cache_space_refresh(s, &s->system_storage);
patch_min_use(&s->system_storage);
- } else if (r < 0) {
+ } else {
if (!IN_SET(r, -ENOENT, -EROFS))
log_warning_errno(r, "Failed to open system journal: %m");
@@ -400,17 +402,21 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
if (uid_for_system_journal(uid))
return s->system_journal;
- r = sd_id128_get_machine(&machine);
- if (r < 0)
- return s->system_journal;
-
f = ordered_hashmap_get(s->user_journals, UID_TO_PTR(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)
+ SD_ID128_FORMAT_VAL(machine), uid) < 0) {
+ log_oom();
return s->system_journal;
+ }
while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) {
/* Too many open? Then let's close one */
@@ -460,17 +466,81 @@ static int do_rotate(
return r;
}
+static void server_process_deferred_closes(Server *s) {
+ JournalFile *f;
+ Iterator i;
+
+ /* Perform any deferred closes which aren't still offlining. */
+ SET_FOREACH(f, s->deferred_closes, i) {
+ if (journal_file_is_offlining(f))
+ continue;
+
+ (void) set_remove(s->deferred_closes, f);
+ (void) journal_file_close(f);
+ }
+}
+
+static void server_vacuum_deferred_closes(Server *s) {
+ assert(s);
+
+ /* Make some room in the deferred closes list, so that it doesn't grow without bounds */
+ if (set_size(s->deferred_closes) < DEFERRED_CLOSES_MAX)
+ return;
+
+ /* Let's first remove all journal files that might already have completed closing */
+ server_process_deferred_closes(s);
+
+ /* And now, let's close some more until we reach the limit again. */
+ while (set_size(s->deferred_closes) >= DEFERRED_CLOSES_MAX) {
+ JournalFile *f;
+
+ assert_se(f = set_steal_first(s->deferred_closes));
+ journal_file_close(f);
+ }
+}
+
+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;
+ 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");
+
+ if (asprintf(&path, "/var/log/journal/" SD_ID128_FORMAT_STR "/", SD_ID128_FORMAT_VAL(machine)) < 0)
+ return log_oom();
+
+ dir = opendir(path);
+ if (!dir)
+ return log_error_errno(errno, "Failed to open user journal directory '%s': %m", path);
+
+ if (ret_dir)
+ *ret_dir = TAKE_PTR(dir);
+ if (ret_path)
+ *ret_path = TAKE_PTR(path);
+
+ return 0;
+}
+
void server_rotate(Server *s) {
+ _cleanup_free_ char *path = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
JournalFile *f;
- void *k;
Iterator i;
+ void *k;
int r;
log_debug("Rotating...");
+ /* First, rotate the system journal (either in its runtime flavour or in its runtime flavour) */
(void) do_rotate(s, &s->runtime_journal, "runtime", false, 0);
(void) do_rotate(s, &s->system_journal, "system", s->seal, 0);
+ /* Then, rotate all user journals we have open (keeping them open) */
ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
r = do_rotate(s, &f, "user", s->seal, PTR_TO_UID(k));
if (r >= 0)
@@ -480,12 +550,94 @@ void server_rotate(Server *s) {
ordered_hashmap_remove(s->user_journals, k);
}
- /* Perform any deferred closes which aren't still offlining. */
- SET_FOREACH(f, s->deferred_closes, i)
- if (!journal_file_is_offlining(f)) {
- (void) set_remove(s->deferred_closes, f);
- (void) journal_file_close(f);
+ /* 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);
}
+ }
+
+ server_process_deferred_closes(s);
}
void server_sync(Server *s) {
@@ -938,7 +1090,7 @@ void server_dispatch_message(
if (c && c->unit) {
(void) determine_space(s, &available, NULL);
- rl = journal_rate_limit_test(s->rate_limit, c->unit, priority & LOG_PRIMASK, available);
+ rl = journal_rate_limit_test(s->rate_limit, c->unit, c->log_rate_limit_interval, c->log_rate_limit_burst, priority & LOG_PRIMASK, available);
if (rl == 0)
return;
@@ -1036,7 +1188,8 @@ int server_flush_to_var(Server *s, bool require_flag_file) {
r = 0;
finish:
- journal_file_post_change(s->system_journal);
+ if (s->system_journal)
+ journal_file_post_change(s->system_journal);
s->runtime_journal = journal_file_close(s->runtime_journal);
@@ -1096,10 +1249,10 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void
assert(s);
assert(fd == s->native_fd || fd == s->syslog_fd || fd == s->audit_fd);
- if (revents != EPOLLIN) {
- log_error("Got invalid event from epoll for datagram fd: %"PRIx32, revents);
- return -EIO;
- }
+ if (revents != EPOLLIN)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Got invalid event from epoll for datagram fd: %" PRIx32,
+ revents);
/* Try to get the right size, if we can. (Not all sockets support SIOCINQ, hence we just try, but don't rely on
* it.) */
@@ -1113,8 +1266,7 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void
if (!GREEDY_REALLOC(s->buffer, s->buffer_size, m))
return log_oom();
- iovec.iov_base = s->buffer;
- iovec.iov_len = s->buffer_size - 1; /* Leave room for trailing NUL we add later */
+ iovec = IOVEC_MAKE(s->buffer, s->buffer_size - 1); /* Leave room for trailing NUL we add later */
n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
if (n < 0) {
@@ -1595,11 +1747,9 @@ static int dispatch_watchdog(sd_event_source *es, uint64_t usec, void *userdata)
}
static int server_connect_notify(Server *s) {
- union sockaddr_union sa = {
- .un.sun_family = AF_UNIX,
- };
+ union sockaddr_union sa = {};
const char *e;
- int r;
+ int r, salen;
assert(s);
assert(s->notify_fd < 0);
@@ -1628,15 +1778,9 @@ static int server_connect_notify(Server *s) {
if (!e)
return 0;
- if (!IN_SET(e[0], '@', '/') || e[1] == 0) {
- log_error("NOTIFY_SOCKET set to an invalid value: %s", e);
- return -EINVAL;
- }
-
- if (strlen(e) > sizeof(sa.un.sun_path)) {
- log_error("NOTIFY_SOCKET path too long: %s", e);
- return -EINVAL;
- }
+ salen = sockaddr_un_set_path(&sa.un, e);
+ if (salen < 0)
+ return log_error_errno(salen, "NOTIFY_SOCKET set to invalid value '%s': %m", e);
s->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s->notify_fd < 0)
@@ -1644,11 +1788,7 @@ static int server_connect_notify(Server *s) {
(void) fd_inc_sndbuf(s->notify_fd, NOTIFY_SNDBUF_SIZE);
- strncpy(sa.un.sun_path, e, sizeof(sa.un.sun_path));
- if (sa.un.sun_path[0] == '@')
- sa.un.sun_path[0] = 0;
-
- r = connect(s->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+ r = connect(s->notify_fd, &sa.sa, salen);
if (r < 0)
return log_error_errno(errno, "Failed to connect to notify socket: %m");
@@ -1745,38 +1885,34 @@ int server_init(Server *s) {
if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/socket", 0) > 0) {
- if (s->native_fd >= 0) {
- log_error("Too many native sockets passed.");
- return -EINVAL;
- }
+ if (s->native_fd >= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many native sockets passed.");
s->native_fd = fd;
} else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, "/run/systemd/journal/stdout", 0) > 0) {
- if (s->stdout_fd >= 0) {
- log_error("Too many stdout sockets passed.");
- return -EINVAL;
- }
+ if (s->stdout_fd >= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many stdout sockets passed.");
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) {
- if (s->syslog_fd >= 0) {
- log_error("Too many /dev/log sockets passed.");
- return -EINVAL;
- }
+ if (s->syslog_fd >= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many /dev/log sockets passed.");
s->syslog_fd = fd;
} else if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) {
- if (s->audit_fd >= 0) {
- log_error("Too many audit sockets passed.");
- return -EINVAL;
- }
+ if (s->audit_fd >= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many audit sockets passed.");
s->audit_fd = fd;
@@ -1845,11 +1981,7 @@ int server_init(Server *s) {
if (r < 0)
return r;
- s->udev = udev_new();
- if (!s->udev)
- return -ENOMEM;
-
- s->rate_limit = journal_rate_limit_new(s->rate_limit_interval, s->rate_limit_burst);
+ s->rate_limit = journal_rate_limit_new();
if (!s->rate_limit)
return -ENOMEM;
@@ -1949,8 +2081,6 @@ void server_done(Server *s) {
if (s->mmap)
mmap_cache_unref(s->mmap);
-
- udev_unref(s->udev);
}
static const char* const storage_table[_STORAGE_MAX] = {
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index 983be8bb89..6d4847b0cd 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -137,8 +137,6 @@ struct Server {
Set *deferred_closes;
- struct udev *udev;
-
uint64_t *kernel_seqnum;
bool dev_kmsg_readable:1;
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index dbf3503a82..137c8f0446 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -12,6 +12,7 @@
#include "alloc-util.h"
#include "dirent-util.h"
+#include "env-file.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@@ -31,6 +32,7 @@
#include "stdio-util.h"
#include "string-util.h"
#include "syslog-util.h"
+#include "tmpfile-util.h"
#include "unit-name.h"
#define STDOUT_STREAMS_MAX 4096
@@ -125,7 +127,7 @@ void stdout_stream_free(StdoutStream *s) {
DEFINE_TRIVIAL_CLEANUP_FUNC(StdoutStream*, stdout_stream_free);
-static void stdout_stream_destroy(StdoutStream *s) {
+void stdout_stream_destroy(StdoutStream *s) {
if (!s)
return;
@@ -534,7 +536,7 @@ terminate:
return 0;
}
-static int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
+int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
_cleanup_(stdout_stream_freep) StdoutStream *stream = NULL;
sd_id128_t id;
int r;
@@ -596,10 +598,10 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
assert(s);
- if (revents != EPOLLIN) {
- log_error("Got invalid event from epoll for stdout server fd: %"PRIx32, revents);
- return -EIO;
- }
+ if (revents != EPOLLIN)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Got invalid event from epoll for stdout server fd: %" PRIx32,
+ revents);
fd = accept4(s->stdout_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
if (fd < 0) {
@@ -610,7 +612,17 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
}
if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
- log_warning("Too many stdout streams, refusing connection.");
+ struct ucred u;
+
+ r = getpeercred(fd, &u);
+
+ /* By closing fd here we make sure that the client won't wait too long for journald to
+ * gather all the data it adds to the error message to find out that the connection has
+ * just been refused.
+ */
+ fd = safe_close(fd);
+
+ server_driver_message(s, r < 0 ? 0 : u.pid, NULL, LOG_MESSAGE("Too many stdout streams, refusing connection."), NULL);
return 0;
}
@@ -641,7 +653,7 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) {
return log_oom();
}
- r = parse_env_file(NULL, stream->state_file, NEWLINE,
+ r = parse_env_file(NULL, stream->state_file,
"PRIORITY", &priority,
"LEVEL_PREFIX", &level_prefix,
"FORWARD_TO_SYSLOG", &forward_to_syslog,
@@ -649,8 +661,7 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) {
"FORWARD_TO_CONSOLE", &forward_to_console,
"IDENTIFIER", &stream->identifier,
"UNIT", &stream->unit_id,
- "STREAM_ID", &stream_id,
- NULL);
+ "STREAM_ID", &stream_id);
if (r < 0)
return log_error_errno(r, "Failed to read: %s", stream->state_file);
@@ -793,7 +804,7 @@ int server_open_stdout_socket(Server *s) {
if (s->stdout_fd < 0)
return log_error_errno(errno, "socket() failed: %m");
- (void) unlink(sa.un.sun_path);
+ (void) sockaddr_un_unlink(&sa.un);
r = bind(s->stdout_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
if (r < 0)
@@ -804,7 +815,7 @@ int server_open_stdout_socket(Server *s) {
if (listen(s->stdout_fd, SOMAXCONN) < 0)
return log_error_errno(errno, "listen(%s) failed: %m", sa.un.sun_path);
} else
- fd_nonblock(s->stdout_fd, 1);
+ (void) fd_nonblock(s->stdout_fd, true);
r = sd_event_add_io(s->event, &s->stdout_event_source, s->stdout_fd, EPOLLIN, stdout_stream_new, s);
if (r < 0)
diff --git a/src/journal/journald-stream.h b/src/journal/journald-stream.h
index bc5622ab3b..487376e763 100644
--- a/src/journal/journald-stream.h
+++ b/src/journal/journald-stream.h
@@ -10,4 +10,6 @@ int server_open_stdout_socket(Server *s);
int server_restore_streams(Server *s, FDSet *fds);
void stdout_stream_free(StdoutStream *s);
+int stdout_stream_install(Server *s, int fd, StdoutStream **ret);
+void stdout_stream_destroy(StdoutStream *s);
void stdout_stream_send_notify(StdoutStream *s);
diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c
index 9dea116722..a60a259bc4 100644
--- a/src/journal/journald-syslog.c
+++ b/src/journal/journald-syslog.c
@@ -117,7 +117,7 @@ void server_forward_syslog(Server *s, int priority, const char *identifier, cons
header_pid[STRLEN("[]: ") + DECIMAL_STR_MAX(pid_t) + 1];
int n = 0;
time_t t;
- struct tm *tm;
+ struct tm tm;
_cleanup_free_ char *ident_buf = NULL;
assert(s);
@@ -134,10 +134,9 @@ void server_forward_syslog(Server *s, int priority, const char *identifier, cons
/* Second: timestamp */
t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
- tm = localtime(&t);
- if (!tm)
+ if (!localtime_r(&t, &tm))
return;
- if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
+ if (strftime(header_time, sizeof(header_time), "%h %e %T ", &tm) <= 0)
return;
iovec[n++] = IOVEC_MAKE_STRING(header_time);
@@ -194,7 +193,7 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid)
e = l;
l--;
- if (p[l-1] == ']') {
+ if (l > 0 && p[l-1] == ']') {
size_t k = l-1;
for (;;) {
@@ -219,13 +218,16 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid)
if (t)
*identifier = t;
- if (strchr(WHITESPACE, p[e]))
+ /* Single space is used as separator */
+ if (p[e] != '\0' && strchr(WHITESPACE, p[e]))
e++;
+
+ l = (p - *buf) + e;
*buf = p + e;
- return e;
+ return l;
}
-static void syslog_skip_date(char **buf) {
+static int syslog_skip_timestamp(const char **buf) {
enum {
LETTER,
SPACE,
@@ -245,24 +247,21 @@ static void syslog_skip_date(char **buf) {
SPACE
};
- char *p;
+ const char *p, *t;
unsigned i;
assert(buf);
assert(*buf);
- p = *buf;
-
- for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
-
+ for (i = 0, p = *buf; i < ELEMENTSOF(sequence); i++, p++) {
if (!*p)
- return;
+ return 0;
switch (sequence[i]) {
case SPACE:
if (*p != ' ')
- return;
+ return 0;
break;
case SPACE_OR_NUMBER:
@@ -272,48 +271,57 @@ static void syslog_skip_date(char **buf) {
_fallthrough_;
case NUMBER:
if (*p < '0' || *p > '9')
- return;
+ return 0;
break;
case LETTER:
if (!(*p >= 'A' && *p <= 'Z') &&
!(*p >= 'a' && *p <= 'z'))
- return;
+ return 0;
break;
case COLON:
if (*p != ':')
- return;
+ return 0;
break;
}
}
+ t = *buf;
*buf = p;
+ return p - t;
}
void server_process_syslog_message(
Server *s,
const char *buf,
- size_t buf_len,
+ size_t raw_len,
const struct ucred *ucred,
const struct timeval *tv,
const char *label,
size_t label_len) {
- char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
- syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)], *msg;
- const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
- _cleanup_free_ char *identifier = NULL, *pid = NULL;
+ char *t, syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
+ syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
+ const char *msg, *syslog_ts, *a;
+ _cleanup_free_ char *identifier = NULL, *pid = NULL,
+ *dummy = NULL, *msg_msg = NULL, *msg_raw = NULL;
int priority = LOG_USER | LOG_INFO, r;
ClientContext *context = NULL;
struct iovec *iovec;
- size_t n = 0, m, i;
+ size_t n = 0, m, i, leading_ws, syslog_ts_len;
+ bool store_raw;
assert(s);
assert(buf);
+ /* The message cannot be empty. */
+ assert(raw_len > 0);
+ /* The buffer NUL-terminated and can be used a string. raw_len is the length
+ * without the terminating NUL byte, the buffer is actually one bigger. */
+ assert(buf[raw_len] == '\0');
if (ucred && pid_is_valid(ucred->pid)) {
r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
@@ -321,26 +329,50 @@ void server_process_syslog_message(
log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid);
}
- /* We are creating copy of the message because we want to forward original message verbatim to the legacy
- syslog implementation */
- for (i = buf_len; i > 0; i--)
+ /* We are creating a copy of the message because we want to forward the original message
+ verbatim to the legacy syslog implementation */
+ for (i = raw_len; i > 0; i--)
if (!strchr(WHITESPACE, buf[i-1]))
break;
- msg = newa(char, i + 1);
- *((char *) mempcpy(msg, buf, i)) = 0;
- msg = skip_leading_chars(msg, WHITESPACE);
+ leading_ws = strspn(buf, WHITESPACE);
+
+ if (i == 0)
+ /* The message contains only whitespaces */
+ msg = buf + raw_len;
+ else if (i == raw_len)
+ /* Nice! No need to strip anything on the end, let's optimize this a bit */
+ msg = buf + leading_ws;
+ else {
+ msg = dummy = new(char, i - leading_ws + 1);
+ if (!dummy) {
+ log_oom();
+ return;
+ }
- syslog_parse_priority((const char **)&msg, &priority, true);
+ memcpy(dummy, buf + leading_ws, i - leading_ws);
+ dummy[i - leading_ws] = 0;
+ }
+
+ /* We will add the SYSLOG_RAW= field when we stripped anything
+ * _or_ if the input message contained NUL bytes. */
+ store_raw = msg != buf || strlen(msg) != raw_len;
+
+ syslog_parse_priority(&msg, &priority, true);
if (!client_context_test_priority(context, priority))
return;
- if (s->forward_to_syslog)
- forward_syslog_raw(s, priority, buf, buf_len, ucred, tv);
+ syslog_ts = msg;
+ syslog_ts_len = syslog_skip_timestamp(&msg);
+ if (syslog_ts_len == 0)
+ /* We failed to parse the full timestamp, store the raw message too */
+ store_raw = true;
+
+ syslog_parse_identifier(&msg, &identifier, &pid);
- syslog_skip_date(&msg);
- syslog_parse_identifier((const char**)&msg, &identifier, &pid);
+ if (s->forward_to_syslog)
+ forward_syslog_raw(s, priority, buf, raw_len, ucred, tv);
if (s->forward_to_kmsg)
server_forward_kmsg(s, priority, identifier, msg, ucred);
@@ -351,7 +383,7 @@ void server_process_syslog_message(
if (s->forward_to_wall)
server_forward_wall(s, priority, identifier, msg, ucred);
- m = N_IOVEC_META_FIELDS + 6 + client_context_extra_fields_n_iovec(context);
+ m = N_IOVEC_META_FIELDS + 8 + client_context_extra_fields_n_iovec(context);
iovec = newa(struct iovec, m);
iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=syslog");
@@ -365,18 +397,46 @@ void server_process_syslog_message(
}
if (identifier) {
- syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier);
- iovec[n++] = IOVEC_MAKE_STRING(syslog_identifier);
+ a = strjoina("SYSLOG_IDENTIFIER=", identifier);
+ iovec[n++] = IOVEC_MAKE_STRING(a);
}
if (pid) {
- syslog_pid = strjoina("SYSLOG_PID=", pid);
- iovec[n++] = IOVEC_MAKE_STRING(syslog_pid);
+ a = strjoina("SYSLOG_PID=", pid);
+ iovec[n++] = IOVEC_MAKE_STRING(a);
}
- message = strjoina("MESSAGE=", msg);
- if (message)
- iovec[n++] = IOVEC_MAKE_STRING(message);
+ if (syslog_ts_len > 0) {
+ const size_t hlen = STRLEN("SYSLOG_TIMESTAMP=");
+
+ t = newa(char, hlen + syslog_ts_len);
+ memcpy(t, "SYSLOG_TIMESTAMP=", hlen);
+ memcpy(t + hlen, syslog_ts, syslog_ts_len);
+
+ iovec[n++] = IOVEC_MAKE(t, hlen + syslog_ts_len);
+ }
+
+ msg_msg = strjoin("MESSAGE=", msg);
+ if (!msg_msg) {
+ log_oom();
+ return;
+ }
+ iovec[n++] = IOVEC_MAKE_STRING(msg_msg);
+
+ if (store_raw) {
+ const size_t hlen = STRLEN("SYSLOG_RAW=");
+
+ msg_raw = new(char, hlen + raw_len);
+ if (!msg_raw) {
+ log_oom();
+ return;
+ }
+
+ memcpy(msg_raw, "SYSLOG_RAW=", hlen);
+ memcpy(msg_raw + hlen, buf, raw_len);
+
+ iovec[n++] = IOVEC_MAKE(msg_raw, hlen + raw_len);
+ }
server_dispatch_message(s, iovec, n, m, context, tv, priority, 0);
}
@@ -387,7 +447,6 @@ int server_open_syslog_socket(Server *s) {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/journal/dev-log",
};
- static const int one = 1;
int r;
assert(s);
@@ -397,7 +456,7 @@ int server_open_syslog_socket(Server *s) {
if (s->syslog_fd < 0)
return log_error_errno(errno, "socket() failed: %m");
- (void) unlink(sa.un.sun_path);
+ (void) sockaddr_un_unlink(&sa.un);
r = bind(s->syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
if (r < 0)
@@ -405,23 +464,23 @@ int server_open_syslog_socket(Server *s) {
(void) chmod(sa.un.sun_path, 0666);
} else
- fd_nonblock(s->syslog_fd, 1);
+ (void) fd_nonblock(s->syslog_fd, true);
- r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0)
- return log_error_errno(errno, "SO_PASSCRED failed: %m");
+ return log_error_errno(r, "SO_PASSCRED failed: %m");
#if HAVE_SELINUX
if (mac_selinux_use()) {
- r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
+ r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, true);
if (r < 0)
- log_warning_errno(errno, "SO_PASSSEC failed: %m");
+ log_warning_errno(r, "SO_PASSSEC failed: %m");
}
#endif
- r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
+ r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, true);
if (r < 0)
- return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
+ return log_error_errno(r, "SO_TIMESTAMP failed: %m");
r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s);
if (r < 0)
diff --git a/src/journal/journald-wall.c b/src/journal/journald-wall.c
index 75328aa94b..370c9b32e2 100644
--- a/src/journal/journald-wall.c
+++ b/src/journal/journald-wall.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2014 Sebastian Thorarensen
-***/
#include "alloc-util.h"
#include "format-util.h"
diff --git a/src/journal/journald-wall.h b/src/journal/journald-wall.h
index be27370d6b..d081c8254c 100644
--- a/src/journal/journald-wall.h
+++ b/src/journal/journald-wall.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
- Copyright © 2014 Sebastian Thorarensen
-***/
#include "journald-server.h"
diff --git a/src/journal/journald.conf b/src/journal/journald.conf
index 94d5c678aa..2f1c661153 100644
--- a/src/journal/journald.conf
+++ b/src/journal/journald.conf
@@ -40,3 +40,4 @@
#MaxLevelConsole=info
#MaxLevelWall=emerg
#LineMax=48K
+#ReadKMsg=yes
diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c
index ff194dd951..6c61f17c7d 100644
--- a/src/journal/lookup3.c
+++ b/src/journal/lookup3.c
@@ -319,7 +319,7 @@ uint32_t jenkins_hashlittle( const void *key, size_t length, uint32_t initval)
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
-#if !VALGRIND && !defined(__SANITIZE_ADDRESS__)
+#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER
switch(length)
{
@@ -504,7 +504,7 @@ void jenkins_hashlittle2(
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
-#if !VALGRIND && !defined(__SANITIZE_ADDRESS__)
+#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER
switch(length)
{
@@ -680,7 +680,7 @@ uint32_t jenkins_hashbig( const void *key, size_t length, uint32_t initval)
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
-#if !VALGRIND && !defined(__SANITIZE_ADDRESS__)
+#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER
switch(length)
{
diff --git a/src/journal/meson.build b/src/journal/meson.build
index 807dece545..e03d6dc232 100644
--- a/src/journal/meson.build
+++ b/src/journal/meson.build
@@ -34,7 +34,7 @@ endif
############################################################
audit_type_includes = [config_h,
- missing_h,
+ missing_audit_h,
'linux/audit.h']
if conf.get('HAVE_AUDIT') == 1
audit_type_includes += 'libaudit.h'
diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c
index 2366055f0a..90549f1c9f 100644
--- a/src/journal/mmap-cache.c
+++ b/src/journal/mmap-cache.c
@@ -53,7 +53,7 @@ struct MMapFileDescriptor {
};
struct MMapCache {
- int n_ref;
+ unsigned n_ref;
unsigned n_windows;
unsigned n_hit, n_missed;
@@ -85,14 +85,6 @@ MMapCache* mmap_cache_new(void) {
return m;
}
-MMapCache* mmap_cache_ref(MMapCache *m) {
- assert(m);
- assert(m->n_ref > 0);
-
- m->n_ref++;
- return m;
-}
-
static void window_unlink(Window *w) {
Context *c;
@@ -278,7 +270,7 @@ static void context_free(Context *c) {
free(c);
}
-static void mmap_cache_free(MMapCache *m) {
+static MMapCache *mmap_cache_free(MMapCache *m) {
int i;
assert(m);
@@ -292,22 +284,10 @@ static void mmap_cache_free(MMapCache *m) {
while (m->unused)
window_free(m->unused);
- free(m);
+ return mfree(m);
}
-MMapCache* mmap_cache_unref(MMapCache *m) {
-
- if (!m)
- return NULL;
-
- assert(m->n_ref > 0);
-
- m->n_ref--;
- if (m->n_ref == 0)
- mmap_cache_free(m);
-
- return NULL;
-}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(MMapCache, mmap_cache, mmap_cache_free);
static int make_room(MMapCache *m) {
assert(m);
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index 83abd82d1c..b5ff5b64f3 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -16,6 +16,8 @@
#include "catalog.h"
#include "compress.h"
#include "dirent-util.h"
+#include "env-file.h"
+#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
@@ -381,7 +383,7 @@ static char *match_make_string(Match *m) {
return strdup("none");
if (m->type == MATCH_DISCRETE)
- return strndup(m->data, m->size);
+ return cescape_length(m->data, m->size);
LIST_FOREACH(matches, i, m->matches) {
char *t, *k;
@@ -433,6 +435,8 @@ _public_ void sd_journal_flush_matches(sd_journal *j) {
}
_pure_ static int compare_with_location(JournalFile *f, Location *l) {
+ int r;
+
assert(f);
assert(l);
assert(f->location_type == LOCATION_SEEK);
@@ -449,35 +453,31 @@ _pure_ static int compare_with_location(JournalFile *f, Location *l) {
if (l->seqnum_set &&
sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) {
- if (f->current_seqnum < l->seqnum)
- return -1;
- if (f->current_seqnum > l->seqnum)
- return 1;
+ r = CMP(f->current_seqnum, l->seqnum);
+ if (r != 0)
+ return r;
}
if (l->monotonic_set &&
sd_id128_equal(f->current_boot_id, l->boot_id)) {
- if (f->current_monotonic < l->monotonic)
- return -1;
- if (f->current_monotonic > l->monotonic)
- return 1;
+ r = CMP(f->current_monotonic, l->monotonic);
+ if (r != 0)
+ return r;
}
if (l->realtime_set) {
- if (f->current_realtime < l->realtime)
- return -1;
- if (f->current_realtime > l->realtime)
- return 1;
+ r = CMP(f->current_realtime, l->realtime);
+ if (r != 0)
+ return r;
}
if (l->xor_hash_set) {
- if (f->current_xor_hash < l->xor_hash)
- return -1;
- if (f->current_xor_hash > l->xor_hash)
- return 1;
+ r = CMP(f->current_xor_hash, l->xor_hash);
+ if (r != 0)
+ return r;
}
return 0;
@@ -1184,8 +1184,7 @@ static bool file_has_type_prefix(const char *prefix, const char *filename) {
tilded = strjoina(full, "~");
atted = strjoina(prefix, "@");
- return streq(filename, full) ||
- streq(filename, tilded) ||
+ return STR_IN_SET(filename, full, tilded) ||
startswith(filename, atted);
}
@@ -1889,7 +1888,9 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
assert_return(machine_name_is_valid(machine), -EINVAL);
p = strjoina("/run/systemd/machines/", machine);
- r = parse_env_file(NULL, p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
+ r = parse_env_file(NULL, p,
+ "ROOT", &root,
+ "CLASS", &class);
if (r == -ENOENT)
return -EHOSTDOWN;
if (r < 0)
@@ -2824,31 +2825,30 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_
return r;
/* Let's do the type check by hand, since we used 0 context above. */
- if (o->object.type != OBJECT_DATA) {
- log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
- j->unique_file->path, j->unique_offset,
- o->object.type, OBJECT_DATA);
- return -EBADMSG;
- }
+ if (o->object.type != OBJECT_DATA)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s:offset " OFSfmt ": object has type %d, expected %d",
+ j->unique_file->path,
+ j->unique_offset,
+ o->object.type, OBJECT_DATA);
r = return_data(j, j->unique_file, o, &odata, &ol);
if (r < 0)
return r;
/* Check if we have at least the field name and "=". */
- if (ol <= k) {
- log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
- j->unique_file->path, j->unique_offset,
- ol, k + 1);
- return -EBADMSG;
- }
-
- if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
- log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
- j->unique_file->path, j->unique_offset,
- j->unique_field);
- return -EBADMSG;
- }
+ if (ol <= k)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
+ j->unique_file->path,
+ j->unique_offset, ol, k + 1);
+
+ if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=')
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s:offset " OFSfmt ": object does not start with \"%s=\"",
+ j->unique_file->path,
+ j->unique_offset,
+ j->unique_field);
/* OK, now let's see if we already returned this data
* object by checking if it exists in the earlier
@@ -2979,10 +2979,11 @@ _public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) {
return r;
/* Because we used OBJECT_UNUSED above, we need to do our type check manually */
- if (o->object.type != OBJECT_FIELD) {
- log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD);
- return -EBADMSG;
- }
+ if (o->object.type != OBJECT_FIELD)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s:offset " OFSfmt ": object has type %i, expected %i",
+ f->path, j->fields_offset,
+ o->object.type, OBJECT_FIELD);
sz = le64toh(o->object.size) - offsetof(Object, field.payload);
diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c
index 8eae993780..192bb0cb07 100644
--- a/src/journal/test-catalog.c
+++ b/src/journal/test-catalog.c
@@ -11,17 +11,16 @@
#include "catalog.h"
#include "fd-util.h"
#include "fs-util.h"
-#include "fileio.h"
#include "log.h"
#include "macro.h"
+#include "path-util.h"
#include "string-util.h"
+#include "strv.h"
+#include "tests.h"
+#include "tmpfile-util.h"
#include "util.h"
-static const char *catalog_dirs[] = {
- CATALOG_DIR,
- NULL,
-};
-
+static char** catalog_dirs = NULL;
static const char *no_catalog_dirs[] = {
"/bin/hopefully/with/no/catalog",
NULL
@@ -167,8 +166,8 @@ static void test_catalog_update(const char *database) {
assert_se(r == 0);
/* Make sure that we at least have some files loaded or the
- catalog_list below will fail. */
- r = catalog_update(database, NULL, catalog_dirs);
+ * catalog_list below will fail. */
+ r = catalog_update(database, NULL, (const char * const *) catalog_dirs);
assert_se(r == 0);
}
@@ -207,8 +206,14 @@ int main(int argc, char *argv[]) {
setlocale(LC_ALL, "de_DE.UTF-8");
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
+
+ /* If test-catalog is located at the build directory, then use catalogs in that.
+ * If it is not, e.g. installed by systemd-tests package, then use installed catalogs. */
+ catalog_dirs = STRV_MAKE(get_catalog_dir());
+
+ assert_se(access(catalog_dirs[0], F_OK) >= 0);
+ log_notice("Using catalog directory '%s'", catalog_dirs[0]);
test_catalog_file_lang();
diff --git a/src/journal/test-compress-benchmark.c b/src/journal/test-compress-benchmark.c
index 411df3fa7a..7f13b611e6 100644
--- a/src/journal/test-compress-benchmark.c
+++ b/src/journal/test-compress-benchmark.c
@@ -8,6 +8,7 @@
#include "process-util.h"
#include "random-util.h"
#include "string-util.h"
+#include "tests.h"
#include "util.h"
typedef int (compress_t)(const void *src, uint64_t src_size, void *dst,
@@ -142,30 +143,23 @@ static void test_compress_decompress(const char* label, const char* type,
int main(int argc, char *argv[]) {
#if HAVE_XZ || HAVE_LZ4
- const char *i;
- int r;
-
- log_set_max_level(LOG_INFO);
+ test_setup_logging(LOG_INFO);
if (argc >= 2) {
unsigned x;
assert_se(safe_atou(argv[1], &x) >= 0);
arg_duration = x * USEC_PER_SEC;
- } else {
- bool slow;
-
- r = getenv_bool("SYSTEMD_SLOW_TESTS");
- slow = r >= 0 ? r : SYSTEMD_SLOW_TESTS_DEFAULT;
-
- arg_duration = slow ? 2 * USEC_PER_SEC : USEC_PER_SEC / 50;
- }
+ } else
+ arg_duration = slow_tests_enabled() ?
+ 2 * USEC_PER_SEC : USEC_PER_SEC / 50;
if (argc == 3)
(void) safe_atozu(argv[2], &arg_start);
else
arg_start = getpid_cached();
+ const char *i;
NULSTR_FOREACH(i, "zeros\0simple\0random\0") {
#if HAVE_XZ
test_compress_decompress("XZ", i, compress_blob_xz, decompress_blob_xz);
@@ -176,6 +170,6 @@ int main(int argc, char *argv[]) {
}
return 0;
#else
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("No compression feature is enabled");
#endif
}
diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c
index 791c6fdffb..1b050b7052 100644
--- a/src/journal/test-compress.c
+++ b/src/journal/test-compress.c
@@ -7,11 +7,12 @@
#include "alloc-util.h"
#include "compress.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "macro.h"
#include "path-util.h"
#include "random-util.h"
+#include "tests.h"
+#include "tmpfile-util.h"
#include "util.h"
#if HAVE_XZ
@@ -131,6 +132,32 @@ static void test_decompress_startswith(int compression,
assert_se(r > 0);
}
+static void test_decompress_startswith_short(int compression,
+ compress_blob_t compress,
+ decompress_sw_t decompress_sw) {
+
+#define TEXT "HUGE=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+
+ char buf[1024];
+ size_t i, csize;
+ int r;
+
+ log_info("/* %s with %s */", __func__, object_compressed_to_string(compression));
+
+ r = compress(TEXT, sizeof TEXT, buf, sizeof buf, &csize);
+ assert_se(r == 0);
+
+ for (i = 1; i < strlen(TEXT); i++) {
+ size_t alloc_size = i;
+ _cleanup_free_ void *buf2 = NULL;
+
+ assert_se(buf2 = malloc(i));
+
+ assert_se(decompress_sw(buf, csize, &buf2, &alloc_size, TEXT, i, TEXT[i]) == 1);
+ assert_se(decompress_sw(buf, csize, &buf2, &alloc_size, TEXT, i, 'y') == 0);
+ }
+}
+
static void test_compress_stream(int compression,
const char* cat,
compress_stream_t compress,
@@ -197,21 +224,17 @@ static void test_compress_stream(int compression,
#if HAVE_LZ4
static void test_lz4_decompress_partial(void) {
- char buf[20000];
+ char buf[20000], buf2[100];
size_t buf_size = sizeof(buf), compressed;
int r;
_cleanup_free_ char *huge = NULL;
#define HUGE_SIZE (4096*1024)
- huge = malloc(HUGE_SIZE);
+ assert_se(huge = malloc(HUGE_SIZE));
memset(huge, 'x', HUGE_SIZE);
memcpy(huge, "HUGE=", 5);
-#if LZ4_VERSION_NUMBER >= 10700
r = LZ4_compress_default(huge, buf, HUGE_SIZE, buf_size);
-#else
- r = LZ4_compress_limitedOutput(huge, buf, HUGE_SIZE, buf_size);
-#endif
assert_se(r >= 0);
compressed = r;
log_info("Compressed %i → %zu", HUGE_SIZE, compressed);
@@ -226,14 +249,15 @@ static void test_lz4_decompress_partial(void) {
assert_se(r >= 0);
log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE, r);
- /* We expect this to fail, because that's how current lz4 works. If this
- * call succeeds, then lz4 has been fixed, and we need to change our code.
- */
- r = LZ4_decompress_safe_partial(buf, huge,
- compressed,
- 12, HUGE_SIZE-1);
- assert_se(r < 0);
- log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE-1, r);
+ for (size_t size = 1; size < sizeof(buf2); size++) {
+ /* This failed in older lz4s but works in newer ones. */
+ r = LZ4_decompress_safe_partial(buf, buf2, compressed, size, size);
+ log_info("Decompressed partial %zu/%zu → %i (%s)", size, size, r,
+ r < 0 ? "bad" : "good");
+ if (r >= 0 && LZ4_versionNumber() >= 10803)
+ /* lz4 <= 1.8.2 should fail that test, let's only check for newer ones */
+ assert_se(memcmp(buf2, huge, r) == 0);
+ }
}
#endif
@@ -253,7 +277,7 @@ int main(int argc, char *argv[]) {
memcpy(huge, "HUGE=", 5);
char_array_0(huge);
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
random_bytes(data + 7, sizeof(data) - 7);
@@ -275,6 +299,9 @@ int main(int argc, char *argv[]) {
test_compress_stream(OBJECT_COMPRESSED_XZ, "xzcat",
compress_stream_xz, decompress_stream_xz, srcfile);
+
+ test_decompress_startswith_short(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_startswith_xz);
+
#else
log_info("/* XZ test skipped */");
#endif
@@ -299,12 +326,16 @@ int main(int argc, char *argv[]) {
compress_stream_lz4, decompress_stream_lz4, srcfile);
test_lz4_decompress_partial();
+
+ test_decompress_startswith_short(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_startswith_lz4);
+
#else
log_info("/* LZ4 test skipped */");
#endif
return 0;
#else
+ log_info("/* XZ and LZ4 tests skipped */");
return EXIT_TEST_SKIP;
#endif
}
diff --git a/src/journal/test-journal-enum.c b/src/journal/test-journal-enum.c
index b25a983498..8e839920b9 100644
--- a/src/journal/test-journal-enum.c
+++ b/src/journal/test-journal-enum.c
@@ -7,12 +7,13 @@
#include "journal-internal.h"
#include "log.h"
#include "macro.h"
+#include "tests.h"
int main(int argc, char *argv[]) {
unsigned n = 0;
- _cleanup_(sd_journal_closep) sd_journal*j = NULL;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY) >= 0);
diff --git a/src/journal/test-journal-init.c b/src/journal/test-journal-init.c
index a43672b6e1..860baca383 100644
--- a/src/journal/test-journal-init.c
+++ b/src/journal/test-journal-init.c
@@ -5,6 +5,7 @@
#include "log.h"
#include "parse-util.h"
#include "rm-rf.h"
+#include "tests.h"
#include "util.h"
int main(int argc, char *argv[]) {
@@ -12,7 +13,7 @@ int main(int argc, char *argv[]) {
int r, i, I = 100;
char t[] = "/tmp/journal-stream-XXXXXX";
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
if (argc >= 2) {
r = safe_atoi(argv[1], &I);
diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c
index 1f0c9f8f2a..cf0561df61 100644
--- a/src/journal/test-journal-interleaving.c
+++ b/src/journal/test-journal-interleaving.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2013 Marius Vollmer
-***/
#include <fcntl.h>
#include <unistd.h>
@@ -9,15 +6,16 @@
#include "sd-journal.h"
#include "alloc-util.h"
+#include "io-util.h"
#include "journal-file.h"
#include "journal-vacuum.h"
#include "log.h"
#include "parse-util.h"
#include "rm-rf.h"
+#include "tests.h"
#include "util.h"
-/* This program tests skipping around in a multi-file journal.
- */
+/* This program tests skipping around in a multi-file journal. */
static bool arg_keep = false;
@@ -61,8 +59,7 @@ static void append_number(JournalFile *f, int n, uint64_t *seqnum) {
previous_ts = ts;
assert_se(asprintf(&p, "NUMBER=%d", n) >= 0);
- iovec[0].iov_base = p;
- iovec[0].iov_len = strlen(p);
+ iovec[0] = IOVEC_MAKE_STRING(p);
assert_ret(journal_file_append_entry(f, &ts, NULL, iovec, 1, seqnum, NULL, NULL));
free(p);
}
@@ -274,11 +271,11 @@ static void test_sequence_numbers(void) {
}
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
/* journal_file_open requires a valid machine id */
if (access("/etc/machine-id", F_OK) != 0)
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("/etc/machine-id not found");
arg_keep = argc > 1;
diff --git a/src/journal/test-journal-match.c b/src/journal/test-journal-match.c
index 4e5ad1791a..b17527916c 100644
--- a/src/journal/test-journal-match.c
+++ b/src/journal/test-journal-match.c
@@ -8,13 +8,14 @@
#include "journal-internal.h"
#include "log.h"
#include "string-util.h"
+#include "tests.h"
#include "util.h"
int main(int argc, char *argv[]) {
- _cleanup_(sd_journal_closep) sd_journal*j = NULL;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
_cleanup_free_ char *t;
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(sd_journal_open(&j, 0) >= 0);
@@ -23,6 +24,8 @@ int main(int argc, char *argv[]) {
assert_se(sd_journal_add_match(j, "", 0) < 0);
assert_se(sd_journal_add_match(j, "=", 0) < 0);
assert_se(sd_journal_add_match(j, "=xxxxx", 0) < 0);
+ assert_se(sd_journal_add_match(j, (uint8_t[4]){'A', '=', '\1', '\2'}, 4) >= 0);
+ assert_se(sd_journal_add_match(j, (uint8_t[5]){'B', '=', 'C', '\0', 'D'}, 5) >= 0);
assert_se(sd_journal_add_match(j, "HALLO=WALDO", 0) >= 0);
assert_se(sd_journal_add_match(j, "QUUX=mmmm", 0) >= 0);
assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0);
@@ -53,7 +56,7 @@ int main(int argc, char *argv[]) {
printf("resulting match expression is: %s\n", t);
- assert_se(streq(t, "(((L3=ok OR L3=yes) OR ((L4_2=ok OR L4_2=yes) AND (L4_1=ok OR L4_1=yes))) AND ((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO))))"));
+ assert_se(streq(t, "(((L3=ok OR L3=yes) OR ((L4_2=ok OR L4_2=yes) AND (L4_1=ok OR L4_1=yes))) AND ((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO) AND B=C\\000D AND A=\\001\\002)))"));
return 0;
}
diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c
index ae35c91eff..226c30f80a 100644
--- a/src/journal/test-journal-stream.c
+++ b/src/journal/test-journal-stream.c
@@ -12,6 +12,7 @@
#include "macro.h"
#include "parse-util.h"
#include "rm-rf.h"
+#include "tests.h"
#include "util.h"
#define N_ENTRIES 200
@@ -68,9 +69,9 @@ int main(int argc, char *argv[]) {
/* journal_file_open requires a valid machine id */
if (access("/etc/machine-id", F_OK) != 0)
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("/etc/machine-id not found");
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c
index 9ba86f6c8a..45be7e5bc4 100644
--- a/src/journal/test-journal-syslog.c
+++ b/src/journal/test-journal-syslog.c
@@ -4,9 +4,10 @@
#include "journald-syslog.h"
#include "macro.h"
#include "string-util.h"
+#include "syslog-util.h"
-static void test_syslog_parse_identifier(const char* str,
- const char *ident, const char*pid, int ret) {
+static void test_syslog_parse_identifier(const char *str,
+ const char *ident, const char *pid, const char *rest, int ret) {
const char *buf = str;
_cleanup_free_ char *ident2 = NULL, *pid2 = NULL;
int ret2;
@@ -16,12 +17,43 @@ static void test_syslog_parse_identifier(const char* str,
assert_se(ret == ret2);
assert_se(ident == ident2 || streq_ptr(ident, ident2));
assert_se(pid == pid2 || streq_ptr(pid, pid2));
+ assert_se(streq(buf, rest));
+}
+
+static void test_syslog_parse_priority(const char *str, int priority, int ret) {
+ const char *buf = str;
+ int priority2 = 0, ret2;
+
+ ret2 = syslog_parse_priority(&buf, &priority2, false);
+
+ assert_se(ret == ret2);
+ if (ret2 == 1)
+ assert_se(priority == priority2);
}
int main(void) {
- test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", 11);
- test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, 6);
- test_syslog_parse_identifier("pidu xxx", NULL, NULL, 0);
+ test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", "xxx", 11);
+ test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, "xxx", 6);
+ test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, " xxx", 6);
+ test_syslog_parse_identifier("pidu xxx", NULL, NULL, "pidu xxx", 0);
+ test_syslog_parse_identifier(" pidu xxx", NULL, NULL, " pidu xxx", 0);
+ test_syslog_parse_identifier("", NULL, NULL, "", 0);
+ test_syslog_parse_identifier(" ", NULL, NULL, " ", 0);
+ test_syslog_parse_identifier(":", "", NULL, "", 1);
+ test_syslog_parse_identifier(": ", "", NULL, " ", 2);
+ test_syslog_parse_identifier(" :", "", NULL, "", 2);
+ test_syslog_parse_identifier(" pidu:", "pidu", NULL, "", 8);
+ test_syslog_parse_identifier("pidu:", "pidu", NULL, "", 5);
+ test_syslog_parse_identifier("pidu: ", "pidu", NULL, "", 6);
+ test_syslog_parse_identifier("pidu : ", NULL, NULL, "pidu : ", 0);
+
+ test_syslog_parse_priority("<>", 0, 0);
+ test_syslog_parse_priority("<>aaa", 0, 0);
+ test_syslog_parse_priority("<aaaa>", 0, 0);
+ test_syslog_parse_priority("<aaaa>aaa", 0, 0);
+ test_syslog_parse_priority(" <aaaa>", 0, 0);
+ test_syslog_parse_priority(" <aaaa>aaa", 0, 0);
+ /* TODO: add test cases of valid priorities */
return 0;
}
diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c
index c1c3a82c42..c4fa41e076 100644
--- a/src/journal/test-journal-verify.c
+++ b/src/journal/test-journal-verify.c
@@ -5,11 +5,13 @@
#include <unistd.h>
#include "fd-util.h"
+#include "io-util.h"
#include "journal-file.h"
#include "journal-verify.h"
#include "log.h"
#include "rm-rf.h"
#include "terminal-util.h"
+#include "tests.h"
#include "util.h"
#define N_ENTRIES 6000
@@ -62,9 +64,9 @@ int main(int argc, char *argv[]) {
/* journal_file_open requires a valid machine id */
if (access("/etc/machine-id", F_OK) != 0)
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("/etc/machine-id not found");
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
@@ -82,8 +84,7 @@ int main(int argc, char *argv[]) {
assert_se(asprintf(&test, "RANDOM=%lu", random() % RANDOM_RANGE));
- iovec.iov_base = (void*) test;
- iovec.iov_len = strlen(test);
+ iovec = IOVEC_MAKE_STRING(test);
assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c
index 69bdff6760..0795e0da0a 100644
--- a/src/journal/test-journal.c
+++ b/src/journal/test-journal.c
@@ -3,11 +3,13 @@
#include <fcntl.h>
#include <unistd.h>
+#include "io-util.h"
#include "journal-authenticate.h"
#include "journal-file.h"
#include "journal-vacuum.h"
#include "log.h"
#include "rm-rf.h"
+#include "tests.h"
static bool arg_keep = false;
@@ -21,7 +23,7 @@ static void test_non_empty(void) {
sd_id128_t fake_boot_id;
char t[] = "/tmp/journal-XXXXXX";
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
@@ -31,16 +33,13 @@ static void test_non_empty(void) {
assert_se(dual_timestamp_get(&ts));
assert_se(sd_id128_randomize(&fake_boot_id) == 0);
- iovec.iov_base = (void*) test;
- iovec.iov_len = strlen(test);
+ iovec = IOVEC_MAKE_STRING(test);
assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
- iovec.iov_base = (void*) test2;
- iovec.iov_len = strlen(test2);
+ iovec = IOVEC_MAKE_STRING(test2);
assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
- iovec.iov_base = (void*) test;
- iovec.iov_len = strlen(test);
+ iovec = IOVEC_MAKE_STRING(test);
assert_se(journal_file_append_entry(f, &ts, &fake_boot_id, &iovec, 1, NULL, NULL, NULL) == 0);
#if HAVE_GCRYPT
@@ -112,7 +111,7 @@ static void test_empty(void) {
JournalFile *f1, *f2, *f3, *f4;
char t[] = "/tmp/journal-XXXXXX";
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
@@ -164,7 +163,7 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) {
assert_se(data_size <= sizeof(data));
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
@@ -173,8 +172,7 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) {
dual_timestamp_get(&ts);
- iovec.iov_base = (void*) data;
- iovec.iov_len = data_size;
+ iovec = IOVEC_MAKE(data, data_size);
assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
#if HAVE_GCRYPT
@@ -238,9 +236,11 @@ static void test_min_compress_size(void) {
int main(int argc, char *argv[]) {
arg_keep = argc > 1;
+ test_setup_logging(LOG_INFO);
+
/* journal_file_open requires a valid machine id */
if (access("/etc/machine-id", F_OK) != 0)
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("/etc/machine-id not found");
test_non_empty();
test_empty();
diff --git a/src/journal/test-mmap-cache.c b/src/journal/test-mmap-cache.c
index 5c55b35f57..8f755efdde 100644
--- a/src/journal/test-mmap-cache.c
+++ b/src/journal/test-mmap-cache.c
@@ -6,9 +6,9 @@
#include <unistd.h>
#include "fd-util.h"
-#include "fileio.h"
#include "macro.h"
#include "mmap-cache.h"
+#include "tmpfile-util.h"
#include "util.h"
int main(int argc, char *argv[]) {