From: Zbigniew Jędrzejewski-Szmek Date: Tue, 29 Oct 2013 03:43:57 +0000 (-0400) Subject: journalctl: add --list-boots to show boot IDs and times X-Git-Tag: v209~1763 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=f11880744c27209a42f502c690db86b38d2db14b journalctl: add --list-boots to show boot IDs and times Suggested by David Wilkins in https://bugzilla.redhat.com/show_bug.cgi?id=967521: > [Specific boot ID is a] bit of a palaver to obtain. I consulted the > verbose dump of the journal to discover the _BOOT_ID for the > timestamp, and then generated the journal dump for that boot using > journalctl _BOOT_ID=foo -o short-monotonic. --- diff --git a/man/journalctl.xml b/man/journalctl.xml index 508f1dd53..011fea280 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -486,6 +486,17 @@ + + + + Show a tabular list of + boot numbers (relative to current + boot), their IDs, and the timestamps + of the first and last message + pertaining to the boot. + + + diff --git a/shell-completion/bash/journalctl b/shell-completion/bash/journalctl index 3c40d57a9..942a25357 100644 --- a/shell-completion/bash/journalctl +++ b/shell-completion/bash/journalctl @@ -42,7 +42,7 @@ _journalctl() { --disk-usage -f --follow --header -h --help -l --local --new-id128 -m --merge --no-pager --no-tail -q --quiet --setup-keys --this-boot --verify - --version --list-catalog --update-catalog' + --version --list-catalog --update-catalog --list-boots' [ARG]='-b --boot --this-boot -D --directory -F --field -o --output -u --unit --user-unit' [ARGUNKNOWN]='-c --cursor --interval -n --lines -p --priority --since --until diff --git a/shell-completion/zsh/_journalctl b/shell-completion/zsh/_journalctl index 73646b57f..29ff3e34e 100644 --- a/shell-completion/zsh/_journalctl +++ b/shell-completion/zsh/_journalctl @@ -70,6 +70,7 @@ _arguments -s \ {-q,--quiet}"[Don't show privilege warning]" \ {-m,--merge}'[Show entries from all available journals]' \ {-b+,--boot=}'[Show data only from the specified boot or offset]:boot id or offset:_journal_boots' \ + '--list-boots[List boots ordered by time]' \ {-k,--dmesg}'[Show only kernel messages, Implies -b]' \ {-u+,--unit=}'[Show data only from the specified unit]:units:_journal_fields _SYSTEMD_UNIT' \ '--user-unit=[Show data only from the specified user session unit]:units:_journal_fields USER_UNIT' \ diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 275458c48..e38721df4 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -104,12 +104,14 @@ static enum { ACTION_DISK_USAGE, ACTION_LIST_CATALOG, ACTION_DUMP_CATALOG, - ACTION_UPDATE_CATALOG + ACTION_UPDATE_CATALOG, + ACTION_LIST_BOOTS, } arg_action = ACTION_SHOW; typedef struct boot_id_t { sd_id128_t id; - uint64_t timestamp; + uint64_t first; + uint64_t last; } boot_id_t; static int help(void) { @@ -125,6 +127,7 @@ static int help(void) { " --after-cursor=CURSOR Start showing entries from specified cursor\n" " --show-cursor Print the cursor after all the entries\n" " -b --boot[=ID] Show data only from ID or current boot if unspecified\n" + " --list-boots Show terse information about recorded boots\n" " -k --dmesg Show kernel message log from current boot\n" " -u --unit=UNIT Show data only from the specified unit\n" " --user-unit=UNIT Show data only from the specified user session unit\n" @@ -178,6 +181,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_NO_FULL, ARG_NO_TAIL, ARG_NEW_ID128, + ARG_LIST_BOOTS, ARG_USER, ARG_SYSTEM, ARG_ROOT, @@ -216,6 +220,7 @@ static int parse_argv(int argc, char *argv[]) { { "quiet", no_argument, NULL, 'q' }, { "merge", no_argument, NULL, 'm' }, { "boot", optional_argument, NULL, 'b' }, + { "list-boots", no_argument, NULL, ARG_LIST_BOOTS }, { "this-boot", optional_argument, NULL, 'b' }, /* deprecated */ { "dmesg", no_argument, NULL, 'k' }, { "system", no_argument, NULL, ARG_SYSTEM }, @@ -370,6 +375,10 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_LIST_BOOTS: + arg_action = ACTION_LIST_BOOTS; + break; + case 'k': arg_boot = arg_dmesg = true; break; @@ -698,12 +707,93 @@ static int add_matches(sd_journal *j, char **args) { static int boot_id_cmp(const void *a, const void *b) { uint64_t _a, _b; - _a = ((const boot_id_t *)a)->timestamp; - _b = ((const boot_id_t *)b)->timestamp; + _a = ((const boot_id_t *)a)->first; + _b = ((const boot_id_t *)b)->first; return _a < _b ? -1 : (_a > _b ? 1 : 0); } +static int list_boots(sd_journal *j) { + int r; + const void *data; + unsigned int count = 0; + int w, i; + size_t length, allocated = 0; + boot_id_t *id; + _cleanup_free_ boot_id_t *all_ids = NULL; + + r = sd_journal_query_unique(j, "_BOOT_ID"); + if (r < 0) + return r; + + SD_JOURNAL_FOREACH_UNIQUE(j, data, length) { + if (length < strlen("_BOOT_ID=")) + continue; + + if (!GREEDY_REALLOC(all_ids, allocated, count + 1)) + return log_oom(); + + id = &all_ids[count]; + + r = sd_id128_from_string(((const char *)data) + strlen("_BOOT_ID="), &id->id); + if (r < 0) + continue; + + r = sd_journal_add_match(j, data, length); + if (r < 0) + return r; + + r = sd_journal_seek_head(j); + if (r < 0) + return r; + + r = sd_journal_next(j); + if (r < 0) + return r; + else if (r == 0) + goto flush; + + r = sd_journal_get_realtime_usec(j, &id->first); + if (r < 0) + return r; + + r = sd_journal_seek_tail(j); + if (r < 0) + return r; + + r = sd_journal_previous(j); + if (r < 0) + return r; + else if (r == 0) + goto flush; + + r = sd_journal_get_realtime_usec(j, &id->last); + if (r < 0) + return r; + + count++; + flush: + sd_journal_flush_matches(j); + } + + qsort_safe(all_ids, count, sizeof(boot_id_t), boot_id_cmp); + + /* numbers are one less, but we need an extra char for the sign */ + w = DECIMAL_STR_WIDTH(count - 1) + 1; + + for (id = all_ids, i = 0; id < all_ids + count; id++, i++) { + char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX]; + + printf("% *i " SD_ID128_FORMAT_STR " %s—%s\n", + w, i - count + 1, + SD_ID128_FORMAT_VAL(id->id), + format_timestamp(a, sizeof(a), id->first), + format_timestamp(b, sizeof(b), id->last)); + } + + return 0; +} + static int get_relative_boot_id(sd_journal *j, sd_id128_t *boot_id, int relative) { int r; const void *data; @@ -749,7 +839,7 @@ static int get_relative_boot_id(sd_journal *j, sd_id128_t *boot_id, int relative else if (r == 0) goto flush; - r = sd_journal_get_realtime_usec(j, &id->timestamp); + r = sd_journal_get_realtime_usec(j, &id->first); if (r < 0) return r; @@ -1409,6 +1499,11 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; } + if (arg_action == ACTION_LIST_BOOTS) { + r = list_boots(j); + goto finish; + } + /* add_boot() must be called first! * It may need to seek the journal to find parent boot IDs. */ r = add_boot(j);