diff --git a/pkgs/os-specific/linux/systemd/fixes.patch b/pkgs/os-specific/linux/systemd/fixes.patch index c1c768dbacb..89a40f93b89 100644 --- a/pkgs/os-specific/linux/systemd/fixes.patch +++ b/pkgs/os-specific/linux/systemd/fixes.patch @@ -220,7 +220,7 @@ index 25c4a0b..e1f2d6d 100644 flags |= SHOW_MASKED; else if (strneq ("equivalent", word, l)) diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c -index 70a5918..1926e52 100644 +index 70a5918..a5661e8 100644 --- a/src/fsck/fsck.c +++ b/src/fsck/fsck.c @@ -315,8 +315,7 @@ int main(int argc, char *argv[]) { @@ -272,11 +272,231 @@ index e487369..ff4e9c9 100644 - return r < 0 ? EXIT_FAILURE : r; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } +diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c +index 7dd8878..70a9a13 100644 +--- a/src/journal-remote/journal-remote-parse.c ++++ b/src/journal-remote/journal-remote-parse.c +@@ -344,22 +344,25 @@ int process_data(RemoteSource *source) { + LLLLLLLL0011223344...\n + */ + sep = memchr(line, '=', n); +- if (sep) ++ if (sep) { + /* chomp newline */ + n--; +- else ++ ++ r = iovw_put(&source->iovw, line, n); ++ if (r < 0) ++ return r; ++ } else { + /* replace \n with = */ + line[n-1] = '='; +- log_trace("Received: %.*s", (int) n, line); + +- r = iovw_put(&source->iovw, line, n); +- if (r < 0) { +- log_error("Failed to put line in iovect"); +- return r; ++ source->field_len = n; ++ source->state = STATE_DATA_START; ++ ++ /* we cannot put the field in iovec until we have all data */ + } + +- if (!sep) +- source->state = STATE_DATA_START; ++ log_trace("Received: %.*s (%s)", (int) n, line, sep ? "text" : "binary"); ++ + return 0; /* continue */ + } + +@@ -382,6 +385,7 @@ int process_data(RemoteSource *source) { + + case STATE_DATA: { + void *data; ++ char *field; + + assert(source->data_size > 0); + +@@ -396,11 +400,12 @@ int process_data(RemoteSource *source) { + + assert(data); + +- r = iovw_put(&source->iovw, data, source->data_size); +- if (r < 0) { +- log_error("failed to put binary buffer in iovect"); ++ field = (char*) data - sizeof(uint64_t) - source->field_len; ++ memmove(field + sizeof(uint64_t), field, source->field_len); ++ ++ r = iovw_put(&source->iovw, field + sizeof(uint64_t), source->field_len + source->data_size); ++ if (r < 0) + return r; +- } + + source->state = STATE_DATA_FINISH; + +diff --git a/src/journal-remote/journal-remote-parse.h b/src/journal-remote/journal-remote-parse.h +index 8499f4e..22db550 100644 +--- a/src/journal-remote/journal-remote-parse.h ++++ b/src/journal-remote/journal-remote-parse.h +@@ -42,7 +42,9 @@ typedef struct RemoteSource { + size_t offset; /* offset to the beginning of live data in the buffer */ + size_t scanned; /* number of bytes since the beginning of data without a newline */ + size_t filled; /* total number of bytes in the buffer */ +- size_t data_size; /* size of the binary data chunk being processed */ ++ ++ size_t field_len; /* used for binary fields: the field name length */ ++ size_t data_size; /* and the size of the binary data chunk being processed */ + + struct iovec_wrapper iovw; + +diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c +index 5ab1982..1f980ee 100644 +--- a/src/journal/journal-authenticate.c ++++ b/src/journal/journal-authenticate.c +@@ -229,7 +229,7 @@ int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { + return 0; + } + +-int journal_file_hmac_put_object(JournalFile *f, int type, Object *o, uint64_t p) { ++int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) { + int r; + + assert(f); +@@ -246,7 +246,7 @@ int journal_file_hmac_put_object(JournalFile *f, int type, Object *o, uint64_t p + if (r < 0) + return r; + } else { +- if (type >= 0 && o->object.type != type) ++ if (type > OBJECT_UNUSED && o->object.type != type) + return -EBADMSG; + } + +diff --git a/src/journal/journal-authenticate.h b/src/journal/journal-authenticate.h +index 0aaf836..565fe84 100644 +--- a/src/journal/journal-authenticate.h ++++ b/src/journal/journal-authenticate.h +@@ -33,7 +33,7 @@ int journal_file_append_first_tag(JournalFile *f); + int journal_file_hmac_setup(JournalFile *f); + int journal_file_hmac_start(JournalFile *f); + int journal_file_hmac_put_header(JournalFile *f); +-int journal_file_hmac_put_object(JournalFile *f, int type, Object *o, uint64_t p); ++int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p); + + int journal_file_fss_load(JournalFile *f); + int journal_file_parse_verification_key(JournalFile *f, const char *key); +diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h +index e55fa19..ab089cb 100644 +--- a/src/journal/journal-def.h ++++ b/src/journal/journal-def.h +@@ -52,8 +52,8 @@ typedef struct HashItem HashItem; + typedef struct FSSHeader FSSHeader; + + /* Object types */ +-enum { +- OBJECT_UNUSED, ++typedef enum ObjectType { ++ OBJECT_UNUSED, /* also serves as "any type" or "additional context" */ + OBJECT_DATA, + OBJECT_FIELD, + OBJECT_ENTRY, +@@ -62,7 +62,7 @@ enum { + OBJECT_ENTRY_ARRAY, + OBJECT_TAG, + _OBJECT_TYPE_MAX +-}; ++} ObjectType; + + /* Object flags */ + enum { diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c -index 8a2c0fc..9de3ddd 100644 +index 8a2c0fc..c55a4dc 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c -@@ -1657,7 +1657,7 @@ static int generic_array_bisect( +@@ -374,7 +374,13 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) + return 0; + } + +-static int journal_file_move_to(JournalFile *f, int context, bool keep_always, uint64_t offset, uint64_t size, void **ret) { ++static unsigned type_to_context(ObjectType type) { ++ /* One context for each type, plus one catch-all for the rest */ ++ assert_cc(_OBJECT_TYPE_MAX <= MMAP_CACHE_MAX_CONTEXTS); ++ return type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX ? type : 0; ++} ++ ++static int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_always, uint64_t offset, uint64_t size, void **ret) { + assert(f); + assert(ret); + +@@ -391,7 +397,7 @@ static int journal_file_move_to(JournalFile *f, int context, bool keep_always, u + return -EADDRNOTAVAIL; + } + +- return mmap_cache_get(f->mmap, f->fd, f->prot, context, keep_always, offset, size, &f->last_stat, ret, NULL); ++ return mmap_cache_get(f->mmap, f->fd, f->prot, type_to_context(type), keep_always, offset, size, &f->last_stat, ret); + } + + static uint64_t minimum_header_size(Object *o) { +@@ -412,7 +418,7 @@ static uint64_t minimum_header_size(Object *o) { + return table[o->object.type]; + } + +-int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret) { ++int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret) { + int r; + void *t; + Object *o; +@@ -425,7 +431,7 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec + if (!VALID64(offset)) + return -EFAULT; + +- r = journal_file_move_to(f, type_to_context(type), false, offset, sizeof(ObjectHeader), &t); ++ r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t); + if (r < 0) + return r; + +@@ -441,11 +447,11 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec + if (s < minimum_header_size(o)) + return -EBADMSG; + +- if (type > 0 && o->object.type != type) ++ if (type > OBJECT_UNUSED && o->object.type != type) + return -EBADMSG; + + if (s > sizeof(ObjectHeader)) { +- r = journal_file_move_to(f, o->object.type, false, offset, s, &t); ++ r = journal_file_move_to(f, type, false, offset, s, &t); + if (r < 0) + return r; + +@@ -482,14 +488,14 @@ static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) { + return r; + } + +-int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset) { ++int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset) { + int r; + uint64_t p; + Object *tail, *o; + void *t; + + assert(f); +- assert(type > 0 && type < _OBJECT_TYPE_MAX); ++ assert(type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX); + assert(size >= sizeof(ObjectHeader)); + assert(offset); + assert(ret); +@@ -502,7 +508,7 @@ int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object * + if (p == 0) + p = le64toh(f->header->header_size); + else { +- r = journal_file_move_to_object(f, -1, p, &tail); ++ r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &tail); + if (r < 0) + return r; + +@@ -1657,7 +1663,7 @@ static int generic_array_bisect( } } @@ -285,6 +505,369 @@ index 8a2c0fc..9de3ddd 100644 if (direction == DIRECTION_UP) { i = n; subtract_one = true; +@@ -1793,23 +1799,6 @@ _pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle + return TEST_RIGHT; + } + +-int journal_file_move_to_entry_by_offset( +- JournalFile *f, +- uint64_t p, +- direction_t direction, +- Object **ret, +- uint64_t *offset) { +- +- return generic_array_bisect(f, +- le64toh(f->header->entry_array_offset), +- le64toh(f->header->n_entries), +- p, +- test_object_offset, +- direction, +- ret, offset, NULL); +-} +- +- + static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) { + Object *o; + int r; +@@ -1939,9 +1928,81 @@ int journal_file_move_to_entry_by_monotonic( + ret, offset, NULL); + } + ++void journal_file_reset_location(JournalFile *f) { ++ f->location_type = LOCATION_HEAD; ++ f->current_offset = 0; ++ f->current_seqnum = 0; ++ f->current_realtime = 0; ++ f->current_monotonic = 0; ++ zero(f->current_boot_id); ++ f->current_xor_hash = 0; ++} ++ ++void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset) { ++ f->location_type = LOCATION_SEEK; ++ f->current_offset = offset; ++ f->current_seqnum = le64toh(o->entry.seqnum); ++ f->current_realtime = le64toh(o->entry.realtime); ++ f->current_monotonic = le64toh(o->entry.monotonic); ++ f->current_boot_id = o->entry.boot_id; ++ f->current_xor_hash = le64toh(o->entry.xor_hash); ++} ++ ++int journal_file_compare_locations(JournalFile *af, JournalFile *bf) { ++ assert(af); ++ assert(bf); ++ assert(af->location_type == LOCATION_SEEK); ++ assert(bf->location_type == LOCATION_SEEK); ++ ++ /* If contents and timestamps match, these entries are ++ * identical, even if the seqnum does not match */ ++ if (sd_id128_equal(af->current_boot_id, bf->current_boot_id) && ++ af->current_monotonic == bf->current_monotonic && ++ af->current_realtime == bf->current_realtime && ++ af->current_xor_hash == bf->current_xor_hash) ++ return 0; ++ ++ if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) { ++ ++ /* 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; ++ ++ /* Wow! This is weird, different data but the same ++ * seqnums? Something is borked, but let's make the ++ * best of it and compare by time. */ ++ } ++ ++ 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; ++ } ++ ++ /* Otherwise, compare UTC time */ ++ if (af->current_realtime < bf->current_realtime) ++ return -1; ++ if (af->current_realtime > bf->current_realtime) ++ return 1; ++ ++ /* 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; ++} ++ + int journal_file_next_entry( + JournalFile *f, +- Object *o, uint64_t p, ++ uint64_t p, + direction_t direction, + Object **ret, uint64_t *offset) { + +@@ -1949,18 +2010,14 @@ int journal_file_next_entry( + int r; + + assert(f); +- assert(p > 0 || !o); + + n = le64toh(f->header->n_entries); + if (n <= 0) + return 0; + +- if (!o) ++ if (p == 0) + i = direction == DIRECTION_DOWN ? 0 : n - 1; + else { +- if (o->object.type != OBJECT_ENTRY) +- return -EINVAL; +- + r = generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), +@@ -2006,55 +2063,6 @@ int journal_file_next_entry( + return 1; + } + +-int journal_file_skip_entry( +- JournalFile *f, +- Object *o, uint64_t p, +- int64_t skip, +- Object **ret, uint64_t *offset) { +- +- uint64_t i, n; +- int r; +- +- assert(f); +- assert(o); +- assert(p > 0); +- +- if (o->object.type != OBJECT_ENTRY) +- return -EINVAL; +- +- r = generic_array_bisect(f, +- le64toh(f->header->entry_array_offset), +- le64toh(f->header->n_entries), +- p, +- test_object_offset, +- DIRECTION_DOWN, +- NULL, NULL, +- &i); +- if (r <= 0) +- return r; +- +- /* Calculate new index */ +- if (skip < 0) { +- if ((uint64_t) -skip >= i) +- i = 0; +- else +- i = i - (uint64_t) -skip; +- } else +- i += (uint64_t) skip; +- +- n = le64toh(f->header->n_entries); +- if (n <= 0) +- return -EBADMSG; +- +- if (i >= n) +- i = n-1; +- +- return generic_array_get(f, +- le64toh(f->header->entry_array_offset), +- i, +- ret, offset); +-} +- + int journal_file_next_entry_for_data( + JournalFile *f, + Object *o, uint64_t p, +@@ -2289,7 +2297,7 @@ void journal_file_dump(JournalFile *f) { + + p = le64toh(f->header->header_size); + while (p != 0) { +- r = journal_file_move_to_object(f, -1, p, &o); ++ r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); + if (r < 0) + goto fail; + +diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h +index 211e121..ca17c97 100644 +--- a/src/journal/journal-file.h ++++ b/src/journal/journal-file.h +@@ -48,6 +48,20 @@ typedef enum direction { + DIRECTION_DOWN + } direction_t; + ++typedef enum LocationType { ++ /* The first and last entries, resp. */ ++ LOCATION_HEAD, ++ LOCATION_TAIL, ++ ++ /* We already read the entry we currently point to, and the ++ * next one to read should probably not be this one again. */ ++ LOCATION_DISCRETE, ++ ++ /* We should seek to the precise location specified, and ++ * return it, as we haven't read it yet. */ ++ LOCATION_SEEK ++} LocationType; ++ + typedef struct JournalFile { + int fd; + +@@ -63,6 +77,8 @@ typedef struct JournalFile { + bool tail_entry_monotonic_valid:1; + + direction_t last_direction; ++ LocationType location_type; ++ uint64_t last_n_entries; + + char *path; + struct stat last_stat; +@@ -72,6 +88,11 @@ typedef struct JournalFile { + HashItem *field_hash_table; + + uint64_t current_offset; ++ uint64_t current_seqnum; ++ uint64_t current_realtime; ++ uint64_t current_monotonic; ++ sd_id128_t current_boot_id; ++ uint64_t current_xor_hash; + + JournalMetrics metrics; + MMapCache *mmap; +@@ -160,13 +181,13 @@ static inline bool VALID_EPOCH(uint64_t u) { + #define JOURNAL_HEADER_COMPRESSED_LZ4(h) \ + (!!(le32toh((h)->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED_LZ4)) + +-int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret); ++int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret); + + uint64_t journal_file_entry_n_items(Object *o) _pure_; + uint64_t journal_file_entry_array_n_items(Object *o) _pure_; + uint64_t journal_file_hash_table_n_items(Object *o) _pure_; + +-int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset); ++int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset); + int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqno, Object **ret, uint64_t *offset); + + int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); +@@ -175,12 +196,13 @@ int journal_file_find_data_object_with_hash(JournalFile *f, const void *data, ui + int journal_file_find_field_object(JournalFile *f, const void *field, uint64_t size, Object **ret, uint64_t *offset); + int journal_file_find_field_object_with_hash(JournalFile *f, const void *field, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset); + +-int journal_file_next_entry(JournalFile *f, Object *o, uint64_t p, direction_t direction, Object **ret, uint64_t *offset); +-int journal_file_skip_entry(JournalFile *f, Object *o, uint64_t p, int64_t skip, Object **ret, uint64_t *offset); ++void journal_file_reset_location(JournalFile *f); ++void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset); ++int journal_file_compare_locations(JournalFile *af, JournalFile *bf); ++int journal_file_next_entry(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *offset); + + int journal_file_next_entry_for_data(JournalFile *f, Object *o, uint64_t p, uint64_t data_offset, direction_t direction, Object **ret, uint64_t *offset); + +-int journal_file_move_to_entry_by_offset(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); + int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); + int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); + int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset); +@@ -205,21 +227,3 @@ int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t * + int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot, usec_t *from, usec_t *to); + + bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec); +- +- +-static unsigned type_to_context(int type) { +- /* One context for each type, plus one catch-all for the rest */ +- return type > 0 && type < _OBJECT_TYPE_MAX ? type : 0; +-} +- +-static inline int journal_file_object_keep(JournalFile *f, Object *o, uint64_t offset, void **release_cookie) { +- unsigned context = type_to_context(o->object.type); +- uint64_t s = le64toh(o->object.size); +- +- return mmap_cache_get(f->mmap, f->fd, f->prot, context, true, +- offset, s, &f->last_stat, NULL, release_cookie); +-} +- +-static inline int journal_file_object_release(JournalFile *f, void *release_cookie) { +- return mmap_cache_release(f->mmap, f->fd, release_cookie); +-} +diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h +index 70847db..e99050c 100644 +--- a/src/journal/journal-internal.h ++++ b/src/journal/journal-internal.h +@@ -57,20 +57,6 @@ struct Match { + LIST_HEAD(Match, matches); + }; + +-typedef enum LocationType { +- /* The first and last entries, resp. */ +- LOCATION_HEAD, +- LOCATION_TAIL, +- +- /* We already read the entry we currently point to, and the +- * next one to read should probably not be this one again. */ +- LOCATION_DISCRETE, +- +- /* We should seek to the precise location specified, and +- * return it, as we haven't read it yet. */ +- LOCATION_SEEK +-} LocationType; +- + struct Location { + LocationType type; + +diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c +index f74adcb..5baa22d 100644 +--- a/src/journal/journal-verify.c ++++ b/src/journal/journal-verify.c +@@ -368,7 +368,7 @@ static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { + + c = (a + b) / 2; + +- r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z, NULL); ++ r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z); + if (r < 0) + return r; + +@@ -865,7 +865,7 @@ int journal_file_verify( + if (show_progress) + draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec); + +- r = journal_file_move_to_object(f, -1, p, &o); ++ r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); + if (r < 0) { + error(p, "invalid object"); + goto fail; +@@ -1085,11 +1085,11 @@ int journal_file_verify( + q = last_tag; + + while (q <= p) { +- r = journal_file_move_to_object(f, -1, q, &o); ++ r = journal_file_move_to_object(f, OBJECT_UNUSED, q, &o); + if (r < 0) + goto fail; + +- r = journal_file_hmac_put_object(f, -1, o, q); ++ r = journal_file_hmac_put_object(f, OBJECT_UNUSED, o, q); + if (r < 0) + goto fail; + +@@ -1097,7 +1097,7 @@ int journal_file_verify( + } + + /* Position might have changed, let's reposition things */ +- r = journal_file_move_to_object(f, -1, p, &o); ++ r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); + if (r < 0) + goto fail; + diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index f50faf4..03579fd 100644 --- a/src/journal/journalctl.c @@ -298,6 +881,21 @@ index f50faf4..03579fd 100644 arg_lines = 10; if (!!arg_directory + !!arg_file + !!arg_machine > 1) { +diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c +index a635202..655e2dd 100644 +--- a/src/journal/journald-native.c ++++ b/src/journal/journald-native.c +@@ -132,8 +132,8 @@ void server_process_native_message( + + /* A property follows */ + +- /* n received properties, +1 for _TRANSPORT */ +- if (!GREEDY_REALLOC(iovec, m, n + 1 + N_IOVEC_META_FIELDS + !!object_pid * N_IOVEC_OBJECT_FIELDS)) { ++ /* n existing properties, 1 new, +1 for _TRANSPORT */ ++ if (!GREEDY_REALLOC(iovec, m, n + 2 + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS)) { + log_oom(); + break; + } diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 12735c4..08b143b 100644 --- a/src/journal/journald-server.c @@ -310,6 +908,739 @@ index 12735c4..08b143b 100644 if (s->mmap) mmap_cache_unref(s->mmap); +diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c +index b7db6f1..f6f669d 100644 +--- a/src/journal/mmap-cache.c ++++ b/src/journal/mmap-cache.c +@@ -38,7 +38,7 @@ typedef struct FileDescriptor FileDescriptor; + struct Window { + MMapCache *cache; + +- unsigned keep_always; ++ bool keep_always; + bool in_unused; + + int prot; +@@ -76,7 +76,7 @@ struct MMapCache { + + + Hashmap *fds; +- Hashmap *contexts; ++ Context *contexts[MMAP_CACHE_MAX_CONTEXTS]; + + LIST_HEAD(Window, unused); + Window *last_unused; +@@ -185,7 +185,7 @@ static void context_detach_window(Context *c) { + c->window = NULL; + LIST_REMOVE(by_window, w->contexts, c); + +- if (!w->contexts && w->keep_always == 0) { ++ if (!w->contexts && !w->keep_always) { + /* Not used anymore? */ + LIST_PREPEND(unused, c->cache->unused, w); + if (!c->cache->last_unused) +@@ -219,18 +219,13 @@ static void context_attach_window(Context *c, Window *w) { + + static Context *context_add(MMapCache *m, unsigned id) { + Context *c; +- int r; + + assert(m); + +- c = hashmap_get(m->contexts, UINT_TO_PTR(id + 1)); ++ c = m->contexts[id]; + if (c) + return c; + +- r = hashmap_ensure_allocated(&m->contexts, NULL); +- if (r < 0) +- return NULL; +- + c = new0(Context, 1); + if (!c) + return NULL; +@@ -238,11 +233,8 @@ static Context *context_add(MMapCache *m, unsigned id) { + c->cache = m; + c->id = id; + +- r = hashmap_put(m->contexts, UINT_TO_PTR(id + 1), c); +- if (r < 0) { +- free(c); +- return NULL; +- } ++ assert(!m->contexts[id]); ++ m->contexts[id] = c; + + return c; + } +@@ -252,8 +244,10 @@ static void context_free(Context *c) { + + context_detach_window(c); + +- if (c->cache) +- assert_se(hashmap_remove(c->cache->contexts, UINT_TO_PTR(c->id + 1))); ++ if (c->cache) { ++ assert(c->cache->contexts[c->id] == c); ++ c->cache->contexts[c->id] = NULL; ++ } + + free(c); + } +@@ -302,15 +296,14 @@ static FileDescriptor* fd_add(MMapCache *m, int fd) { + } + + static void mmap_cache_free(MMapCache *m) { +- Context *c; + FileDescriptor *f; ++ int i; + + assert(m); + +- while ((c = hashmap_first(m->contexts))) +- context_free(c); +- +- hashmap_free(m->contexts); ++ for (i = 0; i < MMAP_CACHE_MAX_CONTEXTS; i++) ++ if (m->contexts[i]) ++ context_free(m->contexts[i]); + + while ((f = hashmap_first(m->fds))) + fd_free(f); +@@ -352,8 +345,7 @@ static int try_context( + bool keep_always, + uint64_t offset, + size_t size, +- void **ret, +- void **release_cookie) { ++ void **ret) { + + Context *c; + +@@ -361,8 +353,9 @@ static int try_context( + assert(m->n_ref > 0); + assert(fd >= 0); + assert(size > 0); ++ assert(ret); + +- c = hashmap_get(m->contexts, UINT_TO_PTR(context+1)); ++ c = m->contexts[context]; + if (!c) + return 0; + +@@ -378,12 +371,9 @@ static int try_context( + return 0; + } + +- c->window->keep_always += keep_always; ++ c->window->keep_always |= keep_always; + +- if (ret) +- *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset); +- if (keep_always && release_cookie) +- *release_cookie = c->window; ++ *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset); + return 1; + } + +@@ -395,8 +385,7 @@ static int find_mmap( + bool keep_always, + uint64_t offset, + size_t size, +- void **ret, +- void **release_cookie) { ++ void **ret) { + + FileDescriptor *f; + Window *w; +@@ -427,10 +416,7 @@ static int find_mmap( + context_attach_window(c, w); + w->keep_always += keep_always; + +- if (ret) +- *ret = (uint8_t*) w->ptr + (offset - w->offset); +- if (keep_always && release_cookie) +- *release_cookie = c->window; ++ *ret = (uint8_t*) w->ptr + (offset - w->offset); + return 1; + } + +@@ -443,8 +429,7 @@ static int add_mmap( + uint64_t offset, + size_t size, + struct stat *st, +- void **ret, +- void **release_cookie) { ++ void **ret) { + + uint64_t woffset, wsize; + Context *c; +@@ -457,6 +442,7 @@ static int add_mmap( + assert(m->n_ref > 0); + assert(fd >= 0); + assert(size > 0); ++ assert(ret); + + woffset = offset & ~((uint64_t) page_size() - 1ULL); + wsize = size + (offset - woffset); +@@ -526,10 +512,7 @@ static int add_mmap( + c->window = w; + LIST_PREPEND(by_window, w->contexts, c); + +- if (ret) +- *ret = (uint8_t*) w->ptr + (offset - w->offset); +- if (keep_always && release_cookie) +- *release_cookie = c->window; ++ *ret = (uint8_t*) w->ptr + (offset - w->offset); + return 1; + + outofmem: +@@ -546,8 +529,7 @@ int mmap_cache_get( + uint64_t offset, + size_t size, + struct stat *st, +- void **ret, +- void **release_cookie) { ++ void **ret) { + + int r; + +@@ -555,16 +537,18 @@ int mmap_cache_get( + assert(m->n_ref > 0); + assert(fd >= 0); + assert(size > 0); ++ assert(ret); ++ assert(context < MMAP_CACHE_MAX_CONTEXTS); + + /* Check whether the current context is the right one already */ +- r = try_context(m, fd, prot, context, keep_always, offset, size, ret, release_cookie); ++ r = try_context(m, fd, prot, context, keep_always, offset, size, ret); + if (r != 0) { + m->n_hit ++; + return r; + } + + /* Search for a matching mmap */ +- r = find_mmap(m, fd, prot, context, keep_always, offset, size, ret, release_cookie); ++ r = find_mmap(m, fd, prot, context, keep_always, offset, size, ret); + if (r != 0) { + m->n_hit ++; + return r; +@@ -573,39 +557,7 @@ int mmap_cache_get( + m->n_missed++; + + /* Create a new mmap */ +- return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret, release_cookie); +-} +- +-int mmap_cache_release( +- MMapCache *m, +- int fd, +- void *release_cookie) { +- +- FileDescriptor *f; +- Window *w; +- +- assert(m); +- assert(m->n_ref > 0); +- assert(fd >= 0); +- +- f = hashmap_get(m->fds, INT_TO_PTR(fd + 1)); +- if (!f) +- return -EBADF; +- +- assert(f->fd == fd); +- +- LIST_FOREACH(by_fd, w, f->windows) +- if (w == release_cookie) +- break; +- +- if (!w) +- return -ENOENT; +- +- if (w->keep_always == 0) +- return -ENOLCK; +- +- w->keep_always -= 1; +- return 0; ++ return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret); + } + + void mmap_cache_close_fd(MMapCache *m, int fd) { +@@ -621,18 +573,6 @@ void mmap_cache_close_fd(MMapCache *m, int fd) { + fd_free(f); + } + +-void mmap_cache_close_context(MMapCache *m, unsigned context) { +- Context *c; +- +- assert(m); +- +- c = hashmap_get(m->contexts, UINT_TO_PTR(context + 1)); +- if (!c) +- return; +- +- context_free(c); +-} +- + unsigned mmap_cache_get_hit(MMapCache *m) { + assert(m); + +diff --git a/src/journal/mmap-cache.h b/src/journal/mmap-cache.h +index 76e5316..fe2c83d 100644 +--- a/src/journal/mmap-cache.h ++++ b/src/journal/mmap-cache.h +@@ -25,6 +25,8 @@ + #include + #include + ++#define MMAP_CACHE_MAX_CONTEXTS 8 ++ + typedef struct MMapCache MMapCache; + + MMapCache* mmap_cache_new(void); +@@ -40,14 +42,8 @@ int mmap_cache_get( + uint64_t offset, + size_t size, + struct stat *st, +- void **ret, +- void **release_cookie); +-int mmap_cache_release( +- MMapCache *m, +- int fd, +- void *release_cookie); ++ void **ret); + void mmap_cache_close_fd(MMapCache *m, int fd); +-void mmap_cache_close_context(MMapCache *m, unsigned context); + + unsigned mmap_cache_get_hit(MMapCache *m); + unsigned mmap_cache_get_missed(MMapCache *m); +diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c +index cf21c4d..cb7fc32 100644 +--- a/src/journal/sd-journal.c ++++ b/src/journal/sd-journal.c +@@ -87,7 +87,7 @@ static void detach_location(sd_journal *j) { + j->current_field = 0; + + ORDERED_HASHMAP_FOREACH(f, j->files, i) +- f->current_offset = 0; ++ journal_file_reset_location(f); + } + + static void reset_location(sd_journal *j) { +@@ -114,20 +114,19 @@ static void init_location(Location *l, LocationType type, JournalFile *f, Object + l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true; + } + +-static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o, +- direction_t direction, uint64_t offset) { ++static void set_location(sd_journal *j, JournalFile *f, Object *o) { + assert(j); +- assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK); + assert(f); + assert(o); + +- init_location(&j->current_location, type, f, o); ++ init_location(&j->current_location, LOCATION_DISCRETE, f, o); + + j->current_file = f; + j->current_field = 0; + +- f->last_direction = direction; +- f->current_offset = offset; ++ /* Let f know its candidate entry was picked. */ ++ assert(f->location_type == LOCATION_SEEK); ++ f->location_type = LOCATION_DISCRETE; + } + + static int match_is_valid(const void *data, size_t size) { +@@ -413,144 +412,51 @@ _public_ void sd_journal_flush_matches(sd_journal *j) { + detach_location(j); + } + +-static int compare_entry_order(JournalFile *af, Object *_ao, +- JournalFile *bf, uint64_t bp) { +- +- uint64_t a, b; +- Object *ao, *bo; +- int r; +- +- assert(af); +- assert(bf); +- assert(_ao); +- +- /* The mmap cache might invalidate the object from the first +- * file if we look at the one from the second file. Hence +- * temporarily copy the header of the first one, and look at +- * that only. */ +- ao = alloca(offsetof(EntryObject, items)); +- memcpy(ao, _ao, offsetof(EntryObject, items)); +- +- r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo); +- if (r < 0) +- return strcmp(af->path, bf->path); +- +- /* We operate on two different files here, hence we can access +- * two objects at the same time, which we normally can't. +- * +- * If contents and timestamps match, these entries are +- * identical, even if the seqnum does not match */ +- +- if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) && +- ao->entry.monotonic == bo->entry.monotonic && +- ao->entry.realtime == bo->entry.realtime && +- ao->entry.xor_hash == bo->entry.xor_hash) +- return 0; +- +- if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) { +- +- /* If this is from the same seqnum source, compare +- * seqnums */ +- a = le64toh(ao->entry.seqnum); +- b = le64toh(bo->entry.seqnum); +- +- if (a < b) +- return -1; +- if (a > b) +- return 1; +- +- /* Wow! This is weird, different data but the same +- * seqnums? Something is borked, but let's make the +- * best of it and compare by time. */ +- } +- +- if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) { +- +- /* If the boot id matches, compare monotonic time */ +- a = le64toh(ao->entry.monotonic); +- b = le64toh(bo->entry.monotonic); +- +- if (a < b) +- return -1; +- if (a > b) +- return 1; +- } +- +- /* Otherwise, compare UTC time */ +- a = le64toh(ao->entry.realtime); +- b = le64toh(bo->entry.realtime); +- +- if (a < b) +- return -1; +- if (a > b) +- return 1; +- +- /* Finally, compare by contents */ +- a = le64toh(ao->entry.xor_hash); +- b = le64toh(bo->entry.xor_hash); +- +- if (a < b) +- return -1; +- if (a > b) +- return 1; +- +- return 0; +-} +- +-_pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) { +- uint64_t a; +- +- assert(af); +- assert(ao); ++_pure_ static int compare_with_location(JournalFile *f, Location *l) { ++ assert(f); + assert(l); ++ assert(f->location_type == LOCATION_SEEK); + assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK); + + if (l->monotonic_set && +- sd_id128_equal(ao->entry.boot_id, l->boot_id) && ++ sd_id128_equal(f->current_boot_id, l->boot_id) && + l->realtime_set && +- le64toh(ao->entry.realtime) == l->realtime && ++ f->current_realtime == l->realtime && + l->xor_hash_set && +- le64toh(ao->entry.xor_hash) == l->xor_hash) ++ f->current_xor_hash == l->xor_hash) + return 0; + + if (l->seqnum_set && +- sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) { +- +- a = le64toh(ao->entry.seqnum); ++ sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) { + +- if (a < l->seqnum) ++ if (f->current_seqnum < l->seqnum) + return -1; +- if (a > l->seqnum) ++ if (f->current_seqnum > l->seqnum) + return 1; + } + + if (l->monotonic_set && +- sd_id128_equal(ao->entry.boot_id, l->boot_id)) { ++ sd_id128_equal(f->current_boot_id, l->boot_id)) { + +- a = le64toh(ao->entry.monotonic); +- +- if (a < l->monotonic) ++ if (f->current_monotonic < l->monotonic) + return -1; +- if (a > l->monotonic) ++ if (f->current_monotonic > l->monotonic) + return 1; + } + + if (l->realtime_set) { + +- a = le64toh(ao->entry.realtime); +- +- if (a < l->realtime) ++ if (f->current_realtime < l->realtime) + return -1; +- if (a > l->realtime) ++ if (f->current_realtime > l->realtime) + return 1; + } + + if (l->xor_hash_set) { +- a = le64toh(ao->entry.xor_hash); + +- if (a < l->xor_hash) ++ if (f->current_xor_hash < l->xor_hash) + return -1; +- if (a > l->xor_hash) ++ if (f->current_xor_hash > l->xor_hash) + return 1; + } + +@@ -766,9 +672,9 @@ static int find_location_with_matches( + /* No matches is simple */ + + if (j->current_location.type == LOCATION_HEAD) +- return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset); ++ return journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset); + if (j->current_location.type == LOCATION_TAIL) +- return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset); ++ return journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset); + if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) + return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset); + if (j->current_location.monotonic_set) { +@@ -779,7 +685,7 @@ static int find_location_with_matches( + if (j->current_location.realtime_set) + return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset); + +- return journal_file_next_entry(f, NULL, 0, direction, ret, offset); ++ return journal_file_next_entry(f, 0, direction, ret, offset); + } else + return find_location_for_match(j, j->level0, f, direction, ret, offset); + } +@@ -791,49 +697,61 @@ static int next_with_matches( + Object **ret, + uint64_t *offset) { + +- Object *c; +- uint64_t cp; +- + assert(j); + assert(f); + assert(ret); + assert(offset); + +- c = *ret; +- cp = *offset; +- + /* No matches is easy. We simple advance the file + * pointer by one. */ + if (!j->level0) +- return journal_file_next_entry(f, c, cp, direction, ret, offset); ++ return journal_file_next_entry(f, f->current_offset, direction, ret, offset); + + /* If we have a match then we look for the next matching entry + * with an offset at least one step larger */ +- return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset); ++ return next_for_match(j, j->level0, f, ++ direction == DIRECTION_DOWN ? f->current_offset + 1 ++ : f->current_offset - 1, ++ direction, ret, offset); + } + +-static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) { ++static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction) { + Object *c; +- uint64_t cp; ++ uint64_t cp, n_entries; + int r; + + assert(j); + assert(f); + +- if (f->last_direction == direction && f->current_offset > 0) { +- cp = f->current_offset; ++ n_entries = le64toh(f->header->n_entries); + +- r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c); +- if (r < 0) +- return r; ++ /* If we hit EOF before, we don't need to look into this file again ++ * unless direction changed or new entries appeared. */ ++ if (f->last_direction == direction && f->location_type == LOCATION_TAIL && ++ n_entries == f->last_n_entries) ++ return 0; + +- r = next_with_matches(j, f, direction, &c, &cp); +- if (r <= 0) +- return r; ++ f->last_n_entries = n_entries; ++ ++ if (f->last_direction == direction && f->current_offset > 0) { ++ /* LOCATION_SEEK here means we did the work in a previous ++ * iteration and the current location already points to a ++ * candidate entry. */ ++ if (f->location_type != LOCATION_SEEK) { ++ r = next_with_matches(j, f, direction, &c, &cp); ++ if (r <= 0) ++ return r; ++ ++ journal_file_save_location(f, c, cp); ++ } + } else { ++ f->last_direction = direction; ++ + r = find_location_with_matches(j, f, direction, &c, &cp); + if (r <= 0) + return r; ++ ++ journal_file_save_location(f, c, cp); + } + + /* OK, we found the spot, now let's advance until an entry +@@ -848,30 +766,25 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc + if (j->current_location.type == LOCATION_DISCRETE) { + int k; + +- k = compare_with_location(f, c, &j->current_location); ++ k = compare_with_location(f, &j->current_location); + + found = direction == DIRECTION_DOWN ? k > 0 : k < 0; + } else + found = true; + +- if (found) { +- if (ret) +- *ret = c; +- if (offset) +- *offset = cp; ++ if (found) + return 1; +- } + + r = next_with_matches(j, f, direction, &c, &cp); + if (r <= 0) + return r; ++ ++ journal_file_save_location(f, c, cp); + } + } + + static int real_journal_next(sd_journal *j, direction_t direction) { + JournalFile *f, *new_file = NULL; +- uint64_t new_offset = 0; +- uint64_t p = 0; + Iterator i; + Object *o; + int r; +@@ -882,38 +795,38 @@ static int real_journal_next(sd_journal *j, direction_t direction) { + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + bool found; + +- r = next_beyond_location(j, f, direction, &o, &p); ++ r = next_beyond_location(j, f, direction); + if (r < 0) { + log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r)); + remove_file_real(j, f); + continue; +- } else if (r == 0) ++ } else if (r == 0) { ++ f->location_type = LOCATION_TAIL; + continue; ++ } + + if (!new_file) + found = true; + else { + int k; + +- k = compare_entry_order(f, o, new_file, new_offset); ++ k = journal_file_compare_locations(f, new_file); + + found = direction == DIRECTION_DOWN ? k < 0 : k > 0; + } + +- if (found) { ++ if (found) + new_file = f; +- new_offset = p; +- } + } + + if (!new_file) + return 0; + +- r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o); ++ r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o); + if (r < 0) + return r; + +- set_location(j, LOCATION_DISCRETE, new_file, o, direction, new_offset); ++ set_location(j, new_file, o); + + return 1; + } +@@ -2526,7 +2439,6 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_ + size_t ol; + bool found; + int r; +- void *release_cookie; + + /* Proceed to next data object in the field's linked list */ + if (j->unique_offset == 0) { +@@ -2552,10 +2464,10 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_ + continue; + } + +- /* We do not use the type context here, but 0 instead, +- * so that we can look at this data object at the same ++ /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED ++ * instead, so that we can look at this data object at the same + * time as one on another file */ +- r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o); ++ r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o); + if (r < 0) + return r; + +@@ -2567,10 +2479,6 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_ + return -EBADMSG; + } + +- r = journal_file_object_keep(j->unique_file, o, j->unique_offset, &release_cookie); +- if (r < 0) +- return r; +- + r = return_data(j, j->unique_file, o, &odata, &ol); + if (r < 0) + return r; +@@ -2615,10 +2523,6 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_ + found = true; + } + +- r = journal_file_object_release(j->unique_file, release_cookie); +- if (r < 0) +- return r; +- + if (found) + continue; + diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 372f3ed..d56ee51 100644 --- a/src/libsystemd-network/network-internal.c @@ -875,6 +2206,30 @@ index 28eaa6a..3866308 100644 reboot(RB_ENABLE_CAD); switch (a) { +diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h +index eb24372..00237a2 100644 +--- a/src/systemd/sd-journal.h ++++ b/src/systemd/sd-journal.h +@@ -138,13 +138,15 @@ int sd_journal_reliable_fd(sd_journal *j); + int sd_journal_get_catalog(sd_journal *j, char **text); + int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **text); + ++/* the inverse condition avoids ambiguity of danling 'else' after the macro */ + #define SD_JOURNAL_FOREACH(j) \ +- if (sd_journal_seek_head(j) >= 0) \ +- while (sd_journal_next(j) > 0) ++ if (sd_journal_seek_head(j) < 0) { } \ ++ else while (sd_journal_next(j) > 0) + ++/* the inverse condition avoids ambiguity of danling 'else' after the macro */ + #define SD_JOURNAL_FOREACH_BACKWARDS(j) \ +- if (sd_journal_seek_tail(j) >= 0) \ +- while (sd_journal_previous(j) > 0) ++ if (sd_journal_seek_tail(j) < 0) { } \ ++ else while (sd_journal_previous(j) > 0) + + #define SD_JOURNAL_FOREACH_DATA(j, data, l) \ + for (sd_journal_restart_data(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; ) diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index 63d64b2..57264de 100644 --- a/src/test/test-path-util.c