X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fjournal%2Fjournalctl.c;h=5d12c2b162ad83cb3dae2e7ead3707324ff6f338;hb=e9174f29c7e3ee45137537b126458718913a3ec5;hp=9a2d2553617ad9b50570f50ce4020e0da8a445db;hpb=289f910e16d0a962e128979b67fed5f2ef668e00;p=elogind.git diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 9a2d25536..5d12c2b16 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -48,7 +48,6 @@ #include "fileio.h" #include "build.h" #include "pager.h" -#include "logs-show.h" #include "strv.h" #include "journal-internal.h" #include "journal-def.h" @@ -64,7 +63,7 @@ static OutputMode arg_output = OUTPUT_SHORT; static bool arg_pager_end = false; static bool arg_follow = false; -static bool arg_full = false; +static bool arg_full = true; static bool arg_all = false; static bool arg_no_pager = false; static int arg_lines = -1; @@ -94,6 +93,7 @@ static bool arg_catalog = false; static bool arg_reverse = false; static int arg_journal_type = 0; static const char *arg_root = NULL; +static const char *arg_machine = NULL; static enum { ACTION_SHOW, @@ -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) { @@ -119,12 +121,14 @@ static int help(void) { "Flags:\n" " --system Show only the system journal\n" " --user Show only the user journal for current user\n" + " -M --machine=CONTAINER Operate on local container\n" " --since=DATE Start showing entries newer or of the specified date\n" " --until=DATE Stop showing entries older or of the specified date\n" " -c --cursor=CURSOR Start showing entries from specified cursor\n" " --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" @@ -138,7 +142,7 @@ static int help(void) { " short-precise, short-monotonic, verbose,\n" " export, json, json-pretty, json-sse, cat)\n" " -x --catalog Add message explanations where available\n" - " -l --full Do not ellipsize fields\n" + " --no-full Ellipsize fields\n" " -a --all Show all fields, including long and unprintable\n" " -q --quiet Don't show privilege warning\n" " --no-pager Do not pipe output into a pager\n" @@ -175,8 +179,10 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_NO_PAGER, + ARG_NO_FULL, ARG_NO_TAIL, ARG_NEW_ID128, + ARG_LIST_BOOTS, ARG_USER, ARG_SYSTEM, ARG_ROOT, @@ -208,12 +214,14 @@ static int parse_argv(int argc, char *argv[]) { { "output", required_argument, NULL, 'o' }, { "all", no_argument, NULL, 'a' }, { "full", no_argument, NULL, 'l' }, + { "no-full", no_argument, NULL, ARG_NO_FULL }, { "lines", optional_argument, NULL, 'n' }, { "no-tail", no_argument, NULL, ARG_NO_TAIL }, { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, { "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 }, @@ -241,7 +249,8 @@ static int parse_argv(int argc, char *argv[]) { { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG }, { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG }, { "reverse", no_argument, NULL, 'r' }, - { NULL, 0, NULL, 0 } + { "machine", required_argument, NULL, 'M' }, + {} }; int c, r; @@ -249,13 +258,12 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:u:F:xr", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:u:F:xrM:", options, NULL)) >= 0) { switch (c) { case 'h': - help(); - return 0; + return help(); case ARG_VERSION: puts(PACKAGE_STRING); @@ -298,6 +306,10 @@ static int parse_argv(int argc, char *argv[]) { arg_full = true; break; + case ARG_NO_FULL: + arg_full = false; + break; + case 'a': arg_all = true; break; @@ -364,6 +376,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; @@ -376,6 +392,10 @@ static int parse_argv(int argc, char *argv[]) { arg_journal_type |= SD_JOURNAL_CURRENT_USER; break; + case 'M': + arg_machine = optarg; + break; + case 'D': arg_directory = optarg; break; @@ -528,9 +548,6 @@ static int parse_argv(int argc, char *argv[]) { return log_oom(); break; - case '?': - return -EINVAL; - case 'F': arg_field = optarg; break; @@ -555,17 +572,19 @@ static int parse_argv(int argc, char *argv[]) { arg_reverse = true; break; - default: - log_error("Unknown option code %c", c); + case '?': return -EINVAL; + + default: + assert_not_reached("Unhandled option"); } } if (arg_follow && !arg_no_tail && arg_lines < 0) arg_lines = 10; - if (arg_directory && arg_file) { - log_error("Please specify either -D/--directory= or --file=, not both."); + if (!!arg_directory + !!arg_file + !!arg_machine > 1) { + log_error("Please specify either -D/--directory= or --file= or -M/--machine=, not more than one."); return -EINVAL; } @@ -646,7 +665,7 @@ static int add_matches(sd_journal *j, char **args) { if (executable_is_script(path, &interpreter) > 0) { _cleanup_free_ char *comm; - comm = strndup(path_get_file_name(path), 15); + comm = strndup(basename(path), 15); if (!comm) return log_oom(); @@ -692,12 +711,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; @@ -743,7 +843,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; @@ -755,7 +855,7 @@ static int get_relative_boot_id(sd_journal *j, sd_id128_t *boot_id, int relative sd_journal_flush_matches(j); } - qsort(all_ids, count, sizeof(boot_id_t), boot_id_cmp); + qsort_safe(all_ids, count, sizeof(boot_id_t), boot_id_cmp); if (sd_id128_equal(*boot_id, SD_ID128_NULL)) { if (relative > (int) count || relative <= -(int)count) @@ -788,7 +888,7 @@ static int add_boot(sd_journal *j) { return 0; if (!arg_boot_descriptor) - return add_match_this_boot(j); + return add_match_this_boot(j, arg_machine); if (strlen(arg_boot_descriptor) >= 32) { char tmp = arg_boot_descriptor[32]; @@ -1367,6 +1467,8 @@ int main(int argc, char *argv[]) { r = sd_journal_open_directory(&j, arg_directory, arg_journal_type); else if (arg_file) r = sd_journal_open_files(&j, (const char**) arg_file, 0); + else if (arg_machine) + r = sd_journal_open_container(&j, arg_machine, 0); else r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type); if (r < 0) { @@ -1403,6 +1505,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); @@ -1622,7 +1729,7 @@ int main(int argc, char *argv[]) { flags = arg_all * OUTPUT_SHOW_ALL | - (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | + arg_full * OUTPUT_FULL_WIDTH | on_tty() * OUTPUT_COLOR | arg_catalog * OUTPUT_CATALOG;