journalctl: make --list-boots support -n/--lines= option

Also mention that -r/--reverse is supported by the command.
This commit is contained in:
Yu Watanabe 2024-04-26 13:40:40 +09:00
parent 5da5d848f9
commit d0936a7266
6 changed files with 84 additions and 15 deletions

View file

@ -851,10 +851,16 @@
<varlistentry>
<term><option>--list-boots</option></term>
<listitem><para>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.</para>
<listitem>
<para>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>-n/--lines=<optional>+</optional><replaceable>N</replaceable></option> option, only the
first (when the number prefixed with <literal>+</literal>) or the last (without prefix)
<replaceable>N</replaceable> entries will be shown. When specified with
<option>-r/--reverse</option>, the list will be shown in the reverse order.</para>
<xi:include href="version-info.xml" xpointer="v209"/></listitem>
<xi:include href="version-info.xml" xpointer="v209"/>
</listitem>
</varlistentry>
<varlistentry>

View file

@ -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);
}

View file

@ -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.");

View file

@ -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);
}

View file

@ -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)

View file

@ -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);