Zbigniew Jędrzejewski-Szmek a48f80
From a1b1bfdb3e18c69fe7b7665ac9f7256cf4485bbc Mon Sep 17 00:00:00 2001
Zbigniew Jędrzejewski-Szmek a48f80
From: Jan Janssen <medhefgo@web.de>
Zbigniew Jędrzejewski-Szmek a48f80
Date: Fri, 1 May 2015 15:15:16 +0200
Zbigniew Jędrzejewski-Szmek a48f80
Subject: [PATCH] journalctl: Improve boot ID lookup
Zbigniew Jędrzejewski-Szmek a48f80
Zbigniew Jędrzejewski-Szmek a48f80
This method should greatly improve offset based lookup, by simply jumping
Zbigniew Jędrzejewski-Szmek a48f80
from one boot to the next boot. It starts at the journal head to get the
Zbigniew Jędrzejewski-Szmek a48f80
a boot ID, makes a _BOOT_ID match and then comes from the opposite
Zbigniew Jędrzejewski-Szmek a48f80
journal direction (tail) to get to the end that boot. After flushing the matches
Zbigniew Jędrzejewski-Szmek a48f80
and advancing the journal from that exact position, we arrive at the start
Zbigniew Jędrzejewski-Szmek a48f80
of next boot. Rinse and repeat.
Zbigniew Jędrzejewski-Szmek a48f80
Zbigniew Jędrzejewski-Szmek a48f80
This is faster than the old method of aggregating the full boot listing just
Zbigniew Jędrzejewski-Szmek a48f80
so we can jump to a specific boot, which can be a real pain on big journals
Zbigniew Jędrzejewski-Szmek a48f80
just for a mere "-b -1" case.
Zbigniew Jędrzejewski-Szmek a48f80
Zbigniew Jędrzejewski-Szmek a48f80
As an additional benefit --list-boots should improve slightly too, because
Zbigniew Jędrzejewski-Szmek a48f80
it does less seeking.
Zbigniew Jędrzejewski-Szmek a48f80
Zbigniew Jędrzejewski-Szmek a48f80
Note that there can be a change in boot order with this lookup method
Zbigniew Jędrzejewski-Szmek a48f80
because it will use the order of boots in the journal, not the realtime stamp
Zbigniew Jędrzejewski-Szmek a48f80
stored in them. That's arguably better, though.
Zbigniew Jędrzejewski-Szmek a48f80
Another deficiency is that it will get confused with boots interleaving in the
Zbigniew Jędrzejewski-Szmek a48f80
journal, therefore, it will refuse operation in --merge, --file and --directory mode.
Zbigniew Jędrzejewski-Szmek a48f80
Zbigniew Jędrzejewski-Szmek a48f80
https://bugs.freedesktop.org/show_bug.cgi?id=72601
Zbigniew Jędrzejewski-Szmek a48f80
(cherry picked from commit 596a23293d28f93843aef86721b90043e74d3081)
Zbigniew Jędrzejewski-Szmek a48f80
---
Zbigniew Jędrzejewski-Szmek a48f80
 src/journal/journalctl.c | 275 ++++++++++++++++++++++++++++++-----------------
Zbigniew Jędrzejewski-Szmek a48f80
 1 file changed, 174 insertions(+), 101 deletions(-)
Zbigniew Jędrzejewski-Szmek a48f80
Zbigniew Jędrzejewski-Szmek a48f80
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
Zbigniew Jędrzejewski-Szmek a48f80
index 12c869f5af..c26cc00f51 100644
Zbigniew Jędrzejewski-Szmek a48f80
--- a/src/journal/journalctl.c
Zbigniew Jędrzejewski-Szmek a48f80
+++ b/src/journal/journalctl.c
Zbigniew Jędrzejewski-Szmek a48f80
@@ -131,6 +131,7 @@ typedef struct boot_id_t {
Zbigniew Jędrzejewski-Szmek a48f80
         sd_id128_t id;
Zbigniew Jędrzejewski-Szmek a48f80
         uint64_t first;
Zbigniew Jędrzejewski-Szmek a48f80
         uint64_t last;
Zbigniew Jędrzejewski-Szmek a48f80
+        LIST_FIELDS(struct boot_id_t, boot_list);
Zbigniew Jędrzejewski-Szmek a48f80
 } boot_id_t;
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
 static void pager_open_if_enabled(void) {
Zbigniew Jędrzejewski-Szmek a48f80
@@ -735,6 +736,11 @@ static int parse_argv(int argc, char *argv[]) {
Zbigniew Jędrzejewski-Szmek a48f80
                 return -EINVAL;
Zbigniew Jędrzejewski-Szmek a48f80
         }
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
+        if ((arg_boot || arg_action == ACTION_LIST_BOOTS) && (arg_file || arg_directory || arg_merge)) {
Zbigniew Jędrzejewski-Szmek a48f80
+                log_error("Using --boot or --list-boots with --file, --directory or --merge is not supported.");
Zbigniew Jędrzejewski-Szmek a48f80
+                return -EINVAL;
Zbigniew Jędrzejewski-Szmek a48f80
+        }
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
         return 1;
Zbigniew Jędrzejewski-Szmek a48f80
 }
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
@@ -854,111 +860,203 @@ static int add_matches(sd_journal *j, char **args) {
Zbigniew Jędrzejewski-Szmek a48f80
         return 0;
Zbigniew Jędrzejewski-Szmek a48f80
 }
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-static int boot_id_cmp(const void *a, const void *b) {
Zbigniew Jędrzejewski-Szmek a48f80
-        uint64_t _a, _b;
Zbigniew Jędrzejewski-Szmek a48f80
+static int discover_next_boot(sd_journal *j,
Zbigniew Jędrzejewski-Szmek a48f80
+                              boot_id_t **boot,
Zbigniew Jędrzejewski-Szmek a48f80
+                              bool advance_older,
Zbigniew Jędrzejewski-Szmek a48f80
+                              bool read_realtime) {
Zbigniew Jędrzejewski-Szmek a48f80
+        int r;
Zbigniew Jędrzejewski-Szmek a48f80
+        char match[9+32+1] = "_BOOT_ID=";
Zbigniew Jędrzejewski-Szmek a48f80
+        _cleanup_free_ boot_id_t *next_boot = NULL;
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-        _a = ((const boot_id_t *)a)->first;
Zbigniew Jędrzejewski-Szmek a48f80
-        _b = ((const boot_id_t *)b)->first;
Zbigniew Jędrzejewski-Szmek a48f80
+        assert(j);
Zbigniew Jędrzejewski-Szmek a48f80
+        assert(boot);
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-        return _a < _b ? -1 : (_a > _b ? 1 : 0);
Zbigniew Jędrzejewski-Szmek a48f80
-}
Zbigniew Jędrzejewski-Szmek a48f80
+        /* We expect the journal to be on the last position of a boot
Zbigniew Jędrzejewski-Szmek a48f80
+         * (in relation to the direction we are going), so that the next
Zbigniew Jędrzejewski-Szmek a48f80
+         * invocation of sd_journal_next/previous will be from a different
Zbigniew Jędrzejewski-Szmek a48f80
+         * boot. We then collect any information we desire and then jump
Zbigniew Jędrzejewski-Szmek a48f80
+         * to the last location of the new boot by using a _BOOT_ID match
Zbigniew Jędrzejewski-Szmek a48f80
+         * coming from the other journal direction. */
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-static int get_boots(sd_journal *j,
Zbigniew Jędrzejewski-Szmek a48f80
-                     boot_id_t **boots,
Zbigniew Jędrzejewski-Szmek a48f80
-                     unsigned int *count,
Zbigniew Jędrzejewski-Szmek a48f80
-                     boot_id_t *query_ref_boot) {
Zbigniew Jędrzejewski-Szmek a48f80
-        int r;
Zbigniew Jędrzejewski-Szmek a48f80
-        const void *data;
Zbigniew Jędrzejewski-Szmek a48f80
-        size_t length, allocated = 0;
Zbigniew Jędrzejewski-Szmek a48f80
+        /* Make sure we aren't restricted by any _BOOT_ID matches, so that
Zbigniew Jędrzejewski-Szmek a48f80
+         * we can actually advance to a *different* boot. */
Zbigniew Jędrzejewski-Szmek a48f80
+        sd_journal_flush_matches(j);
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-        assert(j);
Zbigniew Jędrzejewski-Szmek a48f80
-        assert(boots);
Zbigniew Jędrzejewski-Szmek a48f80
-        assert(count);
Zbigniew Jędrzejewski-Szmek a48f80
+        if (advance_older)
Zbigniew Jędrzejewski-Szmek a48f80
+                r = sd_journal_previous(j);
Zbigniew Jędrzejewski-Szmek a48f80
+        else
Zbigniew Jędrzejewski-Szmek a48f80
+                r = sd_journal_next(j);
Zbigniew Jędrzejewski-Szmek a48f80
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
+                return r;
Zbigniew Jędrzejewski-Szmek a48f80
+        else if (r == 0)
Zbigniew Jędrzejewski-Szmek a48f80
+                return 0; /* End of journal, yay. */
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
+        next_boot = new0(boot_id_t, 1);
Zbigniew Jędrzejewski-Szmek a48f80
+        if (!next_boot)
Zbigniew Jędrzejewski-Szmek a48f80
+                return log_oom();
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-        r = sd_journal_query_unique(j, "_BOOT_ID");
Zbigniew Jędrzejewski-Szmek a48f80
+        r = sd_journal_get_monotonic_usec(j, NULL, &next_boot->id);
Zbigniew Jędrzejewski-Szmek a48f80
         if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
                 return r;
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-        *count = 0;
Zbigniew Jędrzejewski-Szmek a48f80
-        SD_JOURNAL_FOREACH_UNIQUE(j, data, length) {
Zbigniew Jędrzejewski-Szmek a48f80
-                boot_id_t *id;
Zbigniew Jędrzejewski-Szmek a48f80
+        if (read_realtime) {
Zbigniew Jędrzejewski-Szmek a48f80
+                r = sd_journal_get_realtime_usec(j, &next_boot->first);
Zbigniew Jędrzejewski-Szmek a48f80
+                if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
+                        return r;
Zbigniew Jędrzejewski-Szmek a48f80
+        }
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-                assert(startswith(data, "_BOOT_ID="));
Zbigniew Jędrzejewski-Szmek a48f80
+        /* Now seek to the last occurrence of this boot ID. */
Zbigniew Jędrzejewski-Szmek a48f80
+        sd_id128_to_string(next_boot->id, match + 9);
Zbigniew Jędrzejewski-Szmek a48f80
+        r = sd_journal_add_match(j, match, sizeof(match) - 1);
Zbigniew Jędrzejewski-Szmek a48f80
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
+                return r;
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-                if (!GREEDY_REALLOC(*boots, allocated, *count + 1))
Zbigniew Jędrzejewski-Szmek a48f80
-                        return log_oom();
Zbigniew Jędrzejewski-Szmek a48f80
+        if (advance_older)
Zbigniew Jędrzejewski-Szmek a48f80
+                r = sd_journal_seek_head(j);
Zbigniew Jędrzejewski-Szmek a48f80
+        else
Zbigniew Jędrzejewski-Szmek a48f80
+                r = sd_journal_seek_tail(j);
Zbigniew Jędrzejewski-Szmek a48f80
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
+                return r;
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-                id = *boots + *count;
Zbigniew Jędrzejewski-Szmek a48f80
+        if (advance_older)
Zbigniew Jędrzejewski-Szmek a48f80
+                r = sd_journal_next(j);
Zbigniew Jędrzejewski-Szmek a48f80
+        else
Zbigniew Jędrzejewski-Szmek a48f80
+                r = sd_journal_previous(j);
Zbigniew Jędrzejewski-Szmek a48f80
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
+                return r;
Zbigniew Jędrzejewski-Szmek a48f80
+        else if (r == 0)
Zbigniew Jędrzejewski-Szmek a48f80
+                return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-                r = sd_id128_from_string(((const char *)data) + strlen("_BOOT_ID="), &id->id);
Zbigniew Jędrzejewski-Szmek a48f80
+        if (read_realtime) {
Zbigniew Jędrzejewski-Szmek a48f80
+                r = sd_journal_get_realtime_usec(j, &next_boot->last);
Zbigniew Jędrzejewski-Szmek a48f80
                 if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
-                        continue;
Zbigniew Jędrzejewski-Szmek a48f80
+                        return r;
Zbigniew Jędrzejewski-Szmek a48f80
+        }
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
+        *boot = next_boot;
Zbigniew Jędrzejewski-Szmek a48f80
+        next_boot = NULL;
Zbigniew Jędrzejewski-Szmek a48f80
+        return 0;
Zbigniew Jędrzejewski-Szmek a48f80
+}
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
+static int get_boots(sd_journal *j,
Zbigniew Jędrzejewski-Szmek a48f80
+                     boot_id_t **boots,
Zbigniew Jędrzejewski-Szmek a48f80
+                     boot_id_t *query_ref_boot,
Zbigniew Jędrzejewski-Szmek a48f80
+                     int ref_boot_offset) {
Zbigniew Jędrzejewski-Szmek a48f80
+        bool skip_once;
Zbigniew Jędrzejewski-Szmek a48f80
+        int r, count = 0;
Zbigniew Jędrzejewski-Szmek a48f80
+        boot_id_t *head = NULL, *tail = NULL;
Zbigniew Jędrzejewski-Szmek a48f80
+        const bool advance_older = query_ref_boot && ref_boot_offset <= 0;
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
+        assert(j);
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
+        /* Adjust for the asymmetry that offset 0 is
Zbigniew Jędrzejewski-Szmek a48f80
+         * the last (and current) boot, while 1 is considered the
Zbigniew Jędrzejewski-Szmek a48f80
+         * (chronological) first boot in the journal. */
Zbigniew Jędrzejewski-Szmek a48f80
+        skip_once = query_ref_boot && sd_id128_is_null(query_ref_boot->id) && ref_boot_offset < 0;
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
+        /* Advance to the earliest/latest occurrence of our reference
Zbigniew Jędrzejewski-Szmek a48f80
+         * boot ID (taking our lookup direction into account), so that
Zbigniew Jędrzejewski-Szmek a48f80
+         * discover_next_boot() can do its job.
Zbigniew Jędrzejewski-Szmek a48f80
+         * If no reference is given, the journal head/tail will do,
Zbigniew Jędrzejewski-Szmek a48f80
+         * they're "virtual" boots after all. */
Zbigniew Jędrzejewski-Szmek a48f80
+        if (query_ref_boot && !sd_id128_is_null(query_ref_boot->id)) {
Zbigniew Jędrzejewski-Szmek a48f80
+                char match[9+32+1] = "_BOOT_ID=";
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
+                sd_journal_flush_matches(j);
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-                r = sd_journal_add_match(j, data, length);
Zbigniew Jędrzejewski-Szmek a48f80
+                sd_id128_to_string(query_ref_boot->id, match + 9);
Zbigniew Jędrzejewski-Szmek a48f80
+                r = sd_journal_add_match(j, match, sizeof(match) - 1);
Zbigniew Jędrzejewski-Szmek a48f80
                 if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
                         return r;
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-                r = sd_journal_seek_head(j);
Zbigniew Jędrzejewski-Szmek a48f80
+                if (advance_older)
Zbigniew Jędrzejewski-Szmek a48f80
+                        r = sd_journal_seek_head(j);
Zbigniew Jędrzejewski-Szmek a48f80
+                else
Zbigniew Jędrzejewski-Szmek a48f80
+                        r = sd_journal_seek_tail(j);
Zbigniew Jędrzejewski-Szmek a48f80
                 if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
                         return r;
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-                r = sd_journal_next(j);
Zbigniew Jędrzejewski-Szmek a48f80
+                if (advance_older)
Zbigniew Jędrzejewski-Szmek a48f80
+                        r = sd_journal_next(j);
Zbigniew Jędrzejewski-Szmek a48f80
+                else
Zbigniew Jędrzejewski-Szmek a48f80
+                        r = sd_journal_previous(j);
Zbigniew Jędrzejewski-Szmek a48f80
                 if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
                         return r;
Zbigniew Jędrzejewski-Szmek a48f80
                 else if (r == 0)
Zbigniew Jędrzejewski-Szmek a48f80
-                        goto flush;
Zbigniew Jędrzejewski-Szmek a48f80
-
Zbigniew Jędrzejewski-Szmek a48f80
-                r = sd_journal_get_realtime_usec(j, &id->first);
Zbigniew Jędrzejewski-Szmek a48f80
+                        goto finish;
Zbigniew Jędrzejewski-Szmek a48f80
+                else if (ref_boot_offset == 0) {
Zbigniew Jędrzejewski-Szmek a48f80
+                        count = 1;
Zbigniew Jędrzejewski-Szmek a48f80
+                        goto finish;
Zbigniew Jędrzejewski-Szmek a48f80
+                }
Zbigniew Jędrzejewski-Szmek a48f80
+        } else {
Zbigniew Jędrzejewski-Szmek a48f80
+                if (advance_older)
Zbigniew Jędrzejewski-Szmek a48f80
+                        r = sd_journal_seek_tail(j);
Zbigniew Jędrzejewski-Szmek a48f80
+                else
Zbigniew Jędrzejewski-Szmek a48f80
+                        r = sd_journal_seek_head(j);
Zbigniew Jędrzejewski-Szmek a48f80
                 if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
                         return r;
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-                if (query_ref_boot) {
Zbigniew Jędrzejewski-Szmek a48f80
-                        id->last = 0;
Zbigniew Jędrzejewski-Szmek a48f80
-                        if (sd_id128_equal(id->id, query_ref_boot->id))
Zbigniew Jędrzejewski-Szmek a48f80
-                                *query_ref_boot = *id;
Zbigniew Jędrzejewski-Szmek a48f80
-                } else {
Zbigniew Jędrzejewski-Szmek a48f80
-                        r = sd_journal_seek_tail(j);
Zbigniew Jędrzejewski-Szmek a48f80
-                        if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
-                                return r;
Zbigniew Jędrzejewski-Szmek a48f80
+                /* No sd_journal_next/previous here. */
Zbigniew Jędrzejewski-Szmek a48f80
+        }
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-                        r = sd_journal_previous(j);
Zbigniew Jędrzejewski-Szmek a48f80
-                        if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
-                                return r;
Zbigniew Jędrzejewski-Szmek a48f80
-                        else if (r == 0)
Zbigniew Jędrzejewski-Szmek a48f80
-                                goto flush;
Zbigniew Jędrzejewski-Szmek a48f80
+        while (true) {
Zbigniew Jędrzejewski-Szmek a48f80
+                _cleanup_free_ boot_id_t *current = NULL;
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-                        r = sd_journal_get_realtime_usec(j, &id->last);
Zbigniew Jędrzejewski-Szmek a48f80
-                        if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
-                                return r;
Zbigniew Jędrzejewski-Szmek a48f80
+                r = discover_next_boot(j, &current, advance_older, !query_ref_boot);
Zbigniew Jędrzejewski-Szmek a48f80
+                if (r < 0) {
Zbigniew Jędrzejewski-Szmek a48f80
+                        boot_id_t *id, *id_next;
Zbigniew Jędrzejewski-Szmek a48f80
+                        LIST_FOREACH_SAFE(boot_list, id, id_next, head)
Zbigniew Jędrzejewski-Szmek a48f80
+                                free(id);
Zbigniew Jędrzejewski-Szmek a48f80
+                        return r;
Zbigniew Jędrzejewski-Szmek a48f80
                 }
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-                (*count)++;
Zbigniew Jędrzejewski-Szmek a48f80
-        flush:
Zbigniew Jędrzejewski-Szmek a48f80
-                sd_journal_flush_matches(j);
Zbigniew Jędrzejewski-Szmek a48f80
+                if (!current)
Zbigniew Jędrzejewski-Szmek a48f80
+                        break;
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
+                if (query_ref_boot) {
Zbigniew Jędrzejewski-Szmek a48f80
+                        if (!skip_once)
Zbigniew Jędrzejewski-Szmek a48f80
+                                ref_boot_offset += advance_older ? 1 : -1;
Zbigniew Jędrzejewski-Szmek a48f80
+                        skip_once = false;
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
+                        if (ref_boot_offset == 0) {
Zbigniew Jędrzejewski-Szmek a48f80
+                                count = 1;
Zbigniew Jędrzejewski-Szmek a48f80
+                                query_ref_boot->id = current->id;
Zbigniew Jędrzejewski-Szmek a48f80
+                                break;
Zbigniew Jędrzejewski-Szmek a48f80
+                        }
Zbigniew Jędrzejewski-Szmek a48f80
+                } else {
Zbigniew Jędrzejewski-Szmek a48f80
+                        LIST_INSERT_AFTER(boot_list, head, tail, current);
Zbigniew Jędrzejewski-Szmek a48f80
+                        tail = current;
Zbigniew Jędrzejewski-Szmek a48f80
+                        current = NULL;
Zbigniew Jędrzejewski-Szmek a48f80
+                        count++;
Zbigniew Jędrzejewski-Szmek a48f80
+                }
Zbigniew Jędrzejewski-Szmek a48f80
         }
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-        qsort_safe(*boots, *count, sizeof(boot_id_t), boot_id_cmp);
Zbigniew Jędrzejewski-Szmek a48f80
-        return 0;
Zbigniew Jędrzejewski-Szmek a48f80
+finish:
Zbigniew Jędrzejewski-Szmek a48f80
+        if (boots)
Zbigniew Jędrzejewski-Szmek a48f80
+                *boots = head;
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
+        sd_journal_flush_matches(j);
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
+        return count;
Zbigniew Jędrzejewski-Szmek a48f80
 }
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
 static int list_boots(sd_journal *j) {
Zbigniew Jędrzejewski-Szmek a48f80
-        int r, w, i;
Zbigniew Jędrzejewski-Szmek a48f80
-        unsigned int count;
Zbigniew Jędrzejewski-Szmek a48f80
-        boot_id_t *id;
Zbigniew Jędrzejewski-Szmek a48f80
-        _cleanup_free_ boot_id_t *all_ids = NULL;
Zbigniew Jędrzejewski-Szmek a48f80
+        int w, i, count;
Zbigniew Jędrzejewski-Szmek a48f80
+        boot_id_t *id, *id_next, *all_ids;
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
         assert(j);
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-        r = get_boots(j, &all_ids, &count, NULL);
Zbigniew Jędrzejewski-Szmek a48f80
-        if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
-                return r;
Zbigniew Jędrzejewski-Szmek a48f80
+        count = get_boots(j, &all_ids, NULL, 0);
Zbigniew Jędrzejewski-Szmek a48f80
+        if (count <= 0)
Zbigniew Jędrzejewski-Szmek a48f80
+                return count;
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
         pager_open_if_enabled();
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
         /* numbers are one less, but we need an extra char for the sign */
Zbigniew Jędrzejewski-Szmek a48f80
         w = DECIMAL_STR_WIDTH(count - 1) + 1;
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-        for (id = all_ids, i = 0; id < all_ids + count; id++, i++) {
Zbigniew Jędrzejewski-Szmek a48f80
+        i = 0;
Zbigniew Jędrzejewski-Szmek a48f80
+        LIST_FOREACH_SAFE(boot_list, id, id_next, all_ids) {
Zbigniew Jędrzejewski-Szmek a48f80
                 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
                 printf("% *i " SD_ID128_FORMAT_STR " %s—%s\n",
Zbigniew Jędrzejewski-Szmek a48f80
@@ -966,39 +1064,8 @@ static int list_boots(sd_journal *j) {
Zbigniew Jędrzejewski-Szmek a48f80
                        SD_ID128_FORMAT_VAL(id->id),
Zbigniew Jędrzejewski-Szmek a48f80
                        format_timestamp_maybe_utc(a, sizeof(a), id->first),
Zbigniew Jędrzejewski-Szmek a48f80
                        format_timestamp_maybe_utc(b, sizeof(b), id->last));
Zbigniew Jędrzejewski-Szmek a48f80
-        }
Zbigniew Jędrzejewski-Szmek a48f80
-
Zbigniew Jędrzejewski-Szmek a48f80
-        return 0;
Zbigniew Jędrzejewski-Szmek a48f80
-}
Zbigniew Jędrzejewski-Szmek a48f80
-
Zbigniew Jędrzejewski-Szmek a48f80
-static int get_boot_id_by_offset(sd_journal *j, sd_id128_t *boot_id, int offset) {
Zbigniew Jędrzejewski-Szmek a48f80
-        int r;
Zbigniew Jędrzejewski-Szmek a48f80
-        unsigned int count;
Zbigniew Jędrzejewski-Szmek a48f80
-        boot_id_t ref_boot_id = {}, *id;
Zbigniew Jędrzejewski-Szmek a48f80
-        _cleanup_free_ boot_id_t *all_ids = NULL;
Zbigniew Jędrzejewski-Szmek a48f80
-
Zbigniew Jędrzejewski-Szmek a48f80
-        assert(j);
Zbigniew Jędrzejewski-Szmek a48f80
-        assert(boot_id);
Zbigniew Jędrzejewski-Szmek a48f80
-
Zbigniew Jędrzejewski-Szmek a48f80
-        ref_boot_id.id = *boot_id;
Zbigniew Jędrzejewski-Szmek a48f80
-        r = get_boots(j, &all_ids, &count, &ref_boot_id);
Zbigniew Jędrzejewski-Szmek a48f80
-        if (r < 0)
Zbigniew Jędrzejewski-Szmek a48f80
-                return r;
Zbigniew Jędrzejewski-Szmek a48f80
-
Zbigniew Jędrzejewski-Szmek a48f80
-        if (sd_id128_equal(*boot_id, SD_ID128_NULL)) {
Zbigniew Jędrzejewski-Szmek a48f80
-                if (offset > (int) count || offset <= -(int)count)
Zbigniew Jędrzejewski-Szmek a48f80
-                        return -EADDRNOTAVAIL;
Zbigniew Jędrzejewski-Szmek a48f80
-
Zbigniew Jędrzejewski-Szmek a48f80
-                *boot_id = all_ids[(offset <= 0)*count + offset - 1].id;
Zbigniew Jędrzejewski-Szmek a48f80
-        } else {
Zbigniew Jędrzejewski-Szmek a48f80
-                id = bsearch(&ref_boot_id, all_ids, count, sizeof(boot_id_t), boot_id_cmp);
Zbigniew Jędrzejewski-Szmek a48f80
-
Zbigniew Jędrzejewski-Szmek a48f80
-                if (!id ||
Zbigniew Jędrzejewski-Szmek a48f80
-                    offset <= 0 ? (id - all_ids) + offset < 0 :
Zbigniew Jędrzejewski-Szmek a48f80
-                                    (id - all_ids) + offset >= (int) count)
Zbigniew Jędrzejewski-Szmek a48f80
-                        return -EADDRNOTAVAIL;
Zbigniew Jędrzejewski-Szmek a48f80
-
Zbigniew Jędrzejewski-Szmek a48f80
-                *boot_id = (id + offset)->id;
Zbigniew Jędrzejewski-Szmek a48f80
+                i++;
Zbigniew Jędrzejewski-Szmek a48f80
+                free(id);
Zbigniew Jędrzejewski-Szmek a48f80
         }
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
         return 0;
Zbigniew Jędrzejewski-Szmek a48f80
@@ -1007,6 +1074,7 @@ static int get_boot_id_by_offset(sd_journal *j, sd_id128_t *boot_id, int offset)
Zbigniew Jędrzejewski-Szmek a48f80
 static int add_boot(sd_journal *j) {
Zbigniew Jędrzejewski-Szmek a48f80
         char match[9+32+1] = "_BOOT_ID=";
Zbigniew Jędrzejewski-Szmek a48f80
         int r;
Zbigniew Jędrzejewski-Szmek a48f80
+        boot_id_t ref_boot_id = {};
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
         assert(j);
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
@@ -1016,17 +1084,22 @@ static int add_boot(sd_journal *j) {
Zbigniew Jędrzejewski-Szmek a48f80
         if (arg_boot_offset == 0 && sd_id128_equal(arg_boot_id, SD_ID128_NULL))
Zbigniew Jędrzejewski-Szmek a48f80
                 return add_match_this_boot(j, arg_machine);
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-        r = get_boot_id_by_offset(j, &arg_boot_id, arg_boot_offset);
Zbigniew Jędrzejewski-Szmek a48f80
-        if (r < 0) {
Zbigniew Jędrzejewski-Szmek a48f80
-                if (sd_id128_equal(arg_boot_id, SD_ID128_NULL))
Zbigniew Jędrzejewski-Szmek a48f80
-                        log_error_errno(r, "Failed to look up boot %+i: %m", arg_boot_offset);
Zbigniew Jędrzejewski-Szmek a48f80
+        ref_boot_id.id = arg_boot_id;
Zbigniew Jędrzejewski-Szmek a48f80
+        r = get_boots(j, NULL, &ref_boot_id, arg_boot_offset);
Zbigniew Jędrzejewski-Szmek a48f80
+        assert(r <= 1);
Zbigniew Jędrzejewski-Szmek a48f80
+        if (r <= 0) {
Zbigniew Jędrzejewski-Szmek a48f80
+                const char *reason = (r == 0) ? "No such boot ID in journal" : strerror(-r);
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
+                if (sd_id128_is_null(arg_boot_id))
Zbigniew Jędrzejewski-Szmek a48f80
+                        log_error("Failed to look up boot %+i: %s", arg_boot_offset, reason);
Zbigniew Jędrzejewski-Szmek a48f80
                 else
Zbigniew Jędrzejewski-Szmek a48f80
                         log_error("Failed to look up boot ID "SD_ID128_FORMAT_STR"%+i: %s",
Zbigniew Jędrzejewski-Szmek a48f80
-                                  SD_ID128_FORMAT_VAL(arg_boot_id), arg_boot_offset, strerror(-r));
Zbigniew Jędrzejewski-Szmek a48f80
-                return r;
Zbigniew Jędrzejewski-Szmek a48f80
+                                  SD_ID128_FORMAT_VAL(arg_boot_id), arg_boot_offset, reason);
Zbigniew Jędrzejewski-Szmek a48f80
+
Zbigniew Jędrzejewski-Szmek a48f80
+                return r == 0 ? -ENODATA : r;
Zbigniew Jędrzejewski-Szmek a48f80
         }
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
-        sd_id128_to_string(arg_boot_id, match + 9);
Zbigniew Jędrzejewski-Szmek a48f80
+        sd_id128_to_string(ref_boot_id.id, match + 9);
Zbigniew Jędrzejewski-Szmek a48f80
 
Zbigniew Jędrzejewski-Szmek a48f80
         r = sd_journal_add_match(j, match, sizeof(match) - 1);
Zbigniew Jędrzejewski-Szmek a48f80
         if (r < 0)