From d0936a72660598118052636c1340f060cffc61c7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 26 Apr 2024 13:40:40 +0900 Subject: [PATCH] journalctl: make --list-boots support -n/--lines= option Also mention that -r/--reverse is supported by the command. --- man/journalctl.xml | 14 +++++--- src/journal/journalctl-misc.c | 28 ++++++++++++---- src/journal/journalctl.c | 2 +- .../sd-journal/test-journal-interleaving.c | 32 ++++++++++++++++++- src/shared/logs-show.c | 18 +++++++++-- src/shared/logs-show.h | 7 +++- 6 files changed, 85 insertions(+), 16 deletions(-) diff --git a/man/journalctl.xml b/man/journalctl.xml index 7d6820064e..caa10a056b 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -851,10 +851,16 @@ - Show a tabular list of boot numbers (relative to the current boot), their IDs, and - the timestamps of the first and last message pertaining to the boot. - - + + Show a tabular list of boot numbers (relative to the current boot), their IDs, and the + timestamps of the first and last message pertaining to the boot. When specified with + option, only the + first (when the number prefixed with +) or the last (without prefix) + N entries will be shown. When specified with + , the list will be shown in the reverse order. + + + diff --git a/src/journal/journalctl-misc.c b/src/journal/journalctl-misc.c index 65785d4d35..8ca6ea2143 100644 --- a/src/journal/journalctl-misc.c +++ b/src/journal/journalctl-misc.c @@ -111,7 +111,11 @@ int action_list_boots(void) { if (r < 0) return r; - r = journal_get_boots(j, &boots, &n_boots); + r = journal_get_boots( + j, + /* advance_older = */ arg_lines_needs_seek_end(), + /* max_ids = */ arg_lines >= 0 ? (size_t) arg_lines : SIZE_MAX, + &boots, &n_boots); if (r < 0) return log_error_errno(r, "Failed to determine boots: %m"); if (r == 0) @@ -132,13 +136,25 @@ int action_list_boots(void) { (void) table_set_sort(table, (size_t) 0); (void) table_set_reverse(table, 0, arg_reverse); - FOREACH_ARRAY(i, boots, n_boots) { + for (int i = 0; i < (int) n_boots; i++) { + int index; + + if (arg_lines_needs_seek_end()) + /* With --lines=N, we only know the negative index, and the older ID is located earlier. */ + index = -i; + else if (arg_lines >= 0) + /* With --lines=+N, we only know the positive index, and the newer ID is located earlier. */ + index = i + 1; + else + /* Otherwise, show negative index. Note, in this case, newer ID is located earlier. */ + index = i + 1 - (int) n_boots; + r = table_add_many(table, - TABLE_INT, (int)(i - boots) - (int) n_boots + 1, + TABLE_INT, index, TABLE_SET_ALIGN_PERCENT, 100, - TABLE_ID128, i->id, - TABLE_TIMESTAMP, i->first_usec, - TABLE_TIMESTAMP, i->last_usec); + TABLE_ID128, boots[i].id, + TABLE_TIMESTAMP, boots[i].first_usec, + TABLE_TIMESTAMP, boots[i].last_usec); if (r < 0) return table_log_add_error(r); } diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 868808e432..45173a6813 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -957,7 +957,7 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --reverse or --follow, not both."); - if (arg_lines >= 0 && arg_lines_oldest && (arg_reverse || arg_follow)) + if (arg_action == ACTION_SHOW && arg_lines >= 0 && arg_lines_oldest && (arg_reverse || arg_follow)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--lines=+N is unsupported when --reverse or --follow is specified."); diff --git a/src/libsystemd/sd-journal/test-journal-interleaving.c b/src/libsystemd/sd-journal/test-journal-interleaving.c index 6f43891535..d98b3ce8cb 100644 --- a/src/libsystemd/sd-journal/test-journal-interleaving.c +++ b/src/libsystemd/sd-journal/test-journal-interleaving.c @@ -468,7 +468,10 @@ static void test_boot_id_one(void (*setup)(void), size_t n_boots_expected) { setup(); assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE)); - assert_se(journal_get_boots(j, &boots, &n_boots) >= 0); + assert_se(journal_get_boots( + j, + /* advance_older = */ false, /* max_ids = */ SIZE_MAX, + &boots, &n_boots) >= 0); assert_se(boots); assert_se(n_boots == n_boots_expected); @@ -492,6 +495,33 @@ static void test_boot_id_one(void (*setup)(void), size_t n_boots_expected) { } } + for (size_t i = 0; i <= n_boots_expected + 1; i++) { + _cleanup_free_ BootId *boots_limited = NULL; + size_t n_boots_limited; + + assert_se(journal_get_boots( + j, + /* advance_older = */ false, /* max_ids = */ i, + &boots_limited, &n_boots_limited) >= 0); + assert_se(boots_limited || i == 0); + assert_se(n_boots_limited == MIN(i, n_boots_expected)); + assert_se(memcmp_safe(boots, boots_limited, n_boots_limited * sizeof(BootId)) == 0); + } + + for (size_t i = 0; i <= n_boots_expected + 1; i++) { + _cleanup_free_ BootId *boots_limited = NULL; + size_t n_boots_limited; + + assert_se(journal_get_boots( + j, + /* advance_older = */ true, /* max_ids = */ i, + &boots_limited, &n_boots_limited) >= 0); + assert_se(boots_limited || i == 0); + assert_se(n_boots_limited == MIN(i, n_boots_expected)); + for (size_t k = 0; k < n_boots_limited; k++) + assert_se(memcmp(&boots[n_boots - k - 1], &boots_limited[k], sizeof(BootId)) == 0); + } + test_done(t); } diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index c96f501f0e..c71c868889 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -1986,7 +1986,13 @@ int journal_find_boot(sd_journal *j, sd_id128_t boot_id, int offset, sd_id128_t } } -int journal_get_boots(sd_journal *j, BootId **ret_boots, size_t *ret_n_boots) { +int journal_get_boots( + sd_journal *j, + bool advance_older, + size_t max_ids, + BootId **ret_boots, + size_t *ret_n_boots) { + _cleanup_free_ BootId *boots = NULL; size_t n_boots = 0; int r; @@ -1997,7 +2003,10 @@ int journal_get_boots(sd_journal *j, BootId **ret_boots, size_t *ret_n_boots) { sd_journal_flush_matches(j); - r = sd_journal_seek_head(j); /* seek to oldest */ + if (advance_older) + r = sd_journal_seek_tail(j); /* seek to newest */ + else + r = sd_journal_seek_head(j); /* seek to oldest */ if (r < 0) return r; @@ -2010,7 +2019,10 @@ int journal_get_boots(sd_journal *j, BootId **ret_boots, size_t *ret_n_boots) { for (;;) { BootId boot; - r = discover_next_boot(j, previous_boot_id, /* advance_older = */ false, &boot); + if (n_boots >= max_ids) + break; + + r = discover_next_boot(j, previous_boot_id, advance_older, &boot); if (r < 0) return r; if (r == 0) diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index b1b42bdae7..7e7b2af901 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -71,4 +71,9 @@ void json_escape( OutputFlags flags); int journal_find_boot(sd_journal *j, sd_id128_t boot_id, int offset, sd_id128_t *ret); -int journal_get_boots(sd_journal *j, BootId **ret_boots, size_t *ret_n_boots); +int journal_get_boots( + sd_journal *j, + bool advance_older, + size_t max_ids, + BootId **ret_boots, + size_t *ret_n_boots); -- 2.25.1