X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fjournal%2Fjournalctl.c;h=9fbc9a3489ef846d8c9d5f03f327f5b66e70c80d;hb=edfb521a21c44f7b4c91d4ef6bffd84f2c241363;hp=56a8abea57d8de67c718d162ca7374afd8b90dc8;hpb=a6e87e90ede66815989ba2db92a07102a69906fe;p=elogind.git diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 56a8abea5..9fbc9a348 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -29,9 +29,12 @@ #include #include #include +#include #include #include #include +#include +#include #include @@ -55,24 +58,28 @@ static OutputMode arg_output = OUTPUT_SHORT; static bool arg_follow = false; static bool arg_show_all = false; static bool arg_no_pager = false; -static int arg_lines = -1; +static unsigned arg_lines = 0; static bool arg_no_tail = false; static bool arg_quiet = false; -static bool arg_local = false; +static bool arg_merge = false; static bool arg_this_boot = false; +static const char *arg_cursor = NULL; static const char *arg_directory = NULL; static int arg_priorities = 0xFF; static const char *arg_verify_key = NULL; #ifdef HAVE_GCRYPT static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC; #endif +static usec_t arg_since, arg_until; +static bool arg_since_set = false, arg_until_set = false; static enum { ACTION_SHOW, ACTION_NEW_ID128, ACTION_PRINT_HEADER, ACTION_SETUP_KEYS, - ACTION_VERIFY + ACTION_VERIFY, + ACTION_DISK_USAGE, } arg_action = ACTION_SHOW; static int help(void) { @@ -83,19 +90,23 @@ static int help(void) { " --version Show package version\n" " --no-pager Do not pipe output into a pager\n" " -a --all Show all fields, including long and unprintable\n" + " -c --cursor=CURSOR Start showing entries from specified cursor\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" " -f --follow Follow journal\n" - " -n --lines=INTEGER Journal entries to show\n" + " -n --lines[=INTEGER] Number of journal entries to show\n" " --no-tail Show all lines, even in follow mode\n" " -o --output=STRING Change journal output mode (short, short-monotonic,\n" - " verbose, export, json, json-pretty, cat)\n" + " verbose, export, json, json-pretty, json-sse, cat)\n" " -q --quiet Don't show privilege warning\n" - " -l --local Only local entries\n" + " -m --merge Show entries from all available journals\n" " -b --this-boot Show data only from current boot\n" " -D --directory=PATH Show journal files from directory\n" " -p --priority=RANGE Show only messages within the specified priority range\n\n" "Commands:\n" " --new-id128 Generate a new 128 Bit ID\n" " --header Show journal header information\n" + " --disk-usage Show total disk usage\n" #ifdef HAVE_GCRYPT " --setup-keys Generate new FSS key pair\n" " --interval=TIME Time interval for changing the FSS sealing key\n" @@ -118,7 +129,10 @@ static int parse_argv(int argc, char *argv[]) { ARG_SETUP_KEYS, ARG_INTERVAL, ARG_VERIFY, - ARG_VERIFY_KEY + ARG_VERIFY_KEY, + ARG_DISK_USAGE, + ARG_SINCE, + ARG_UNTIL }; static const struct option options[] = { @@ -128,11 +142,11 @@ static int parse_argv(int argc, char *argv[]) { { "follow", no_argument, NULL, 'f' }, { "output", required_argument, NULL, 'o' }, { "all", no_argument, NULL, 'a' }, - { "lines", required_argument, NULL, 'n' }, + { "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' }, - { "local", no_argument, NULL, 'l' }, + { "merge", no_argument, NULL, 'm' }, { "this-boot", no_argument, NULL, 'b' }, { "directory", required_argument, NULL, 'D' }, { "header", no_argument, NULL, ARG_HEADER }, @@ -141,6 +155,10 @@ static int parse_argv(int argc, char *argv[]) { { "interval", required_argument, NULL, ARG_INTERVAL }, { "verify", no_argument, NULL, ARG_VERIFY }, { "verify-key", required_argument, NULL, ARG_VERIFY_KEY }, + { "disk-usage", no_argument, NULL, ARG_DISK_USAGE }, + { "cursor", required_argument, NULL, 'c' }, + { "since", required_argument, NULL, ARG_SINCE }, + { "until", required_argument, NULL, ARG_UNTIL }, { NULL, 0, NULL, 0 } }; @@ -149,7 +167,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:", options, NULL)) >= 0) { switch (c) { @@ -169,15 +187,23 @@ static int parse_argv(int argc, char *argv[]) { case 'f': arg_follow = true; + signal(SIGWINCH, columns_cache_reset); break; case 'o': - arg_output = output_mode_from_string(optarg); + arg_output = output_mode_from_string(optarg); if (arg_output < 0) { - log_error("Unknown output '%s'.", optarg); + log_error("Unknown output format '%s'.", optarg); return -EINVAL; } + if (arg_output == OUTPUT_EXPORT || + arg_output == OUTPUT_JSON || + arg_output == OUTPUT_JSON_PRETTY || + arg_output == OUTPUT_JSON_SSE || + arg_output == OUTPUT_CAT) + arg_quiet = true; + break; case 'a': @@ -185,11 +211,15 @@ static int parse_argv(int argc, char *argv[]) { break; case 'n': - r = safe_atoi(optarg, &arg_lines); - if (r < 0 || arg_lines < 0) { - log_error("Failed to parse lines '%s'", optarg); - return -EINVAL; - } + if (optarg) { + r = safe_atou(optarg, &arg_lines); + if (r < 0 || arg_lines <= 0) { + log_error("Failed to parse lines '%s'", optarg); + return -EINVAL; + } + } else + arg_lines = 10; + break; case ARG_NO_TAIL: @@ -204,8 +234,8 @@ static int parse_argv(int argc, char *argv[]) { arg_quiet = true; break; - case 'l': - arg_local = true; + case 'm': + arg_merge = true; break; case 'b': @@ -216,6 +246,10 @@ static int parse_argv(int argc, char *argv[]) { arg_directory = optarg; break; + case 'c': + arg_cursor = optarg; + break; + case ARG_HEADER: arg_action = ACTION_PRINT_HEADER; break; @@ -224,6 +258,10 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_VERIFY; break; + case ARG_DISK_USAGE: + arg_action = ACTION_DISK_USAGE; + break; + #ifdef HAVE_GCRYPT case ARG_SETUP_KEYS: arg_action = ACTION_SETUP_KEYS; @@ -233,7 +271,7 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERIFY_KEY: arg_action = ACTION_VERIFY; arg_verify_key = optarg; - arg_local = true; + arg_merge = false; break; case ARG_INTERVAL: @@ -301,6 +339,24 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_SINCE: + r = parse_timestamp(optarg, &arg_since); + if (r < 0) { + log_error("Failed to parse timestamp: %s", optarg); + return -EINVAL; + } + arg_since_set = true; + break; + + case ARG_UNTIL: + r = parse_timestamp(optarg, &arg_until); + if (r < 0) { + log_error("Failed to parse timestamp: %s", optarg); + return -EINVAL; + } + arg_until_set = true; + break; + case '?': return -EINVAL; @@ -310,9 +366,19 @@ static int parse_argv(int argc, char *argv[]) { } } - if (arg_follow && !arg_no_tail && arg_lines < 0) + if (arg_follow && !arg_no_tail && arg_lines <= 0) arg_lines = 10; + if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) { + log_error("--since= must be before --until=."); + return -EINVAL; + } + + if (arg_cursor && arg_since_set) { + log_error("Please specify either --since= or --cursor=, not both."); + return -EINVAL; + } + return 1; } @@ -626,8 +692,13 @@ static int setup_keys(void) { fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine)); #ifdef HAVE_QRENCODE - fprintf(stderr, "\nTo transfer the verification key to your phone please scan the QR code below:\n\n"); - print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine); + /* If this is not an UTF-8 system don't print any QR codes */ + setlocale(LC_CTYPE, ""); + + if (streq_ptr(nl_langinfo(CODESET), "UTF-8")) { + fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr); + print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine); + } #endif free(hn); } @@ -663,14 +734,14 @@ static int verify(sd_journal *j) { HASHMAP_FOREACH(f, j->files, i) { int k; - usec_t from, to, total; + usec_t first, validated, last; #ifdef HAVE_GCRYPT if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path); #endif - k = journal_file_verify(f, arg_verify_key, &from, &to, &total, true); + k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true); if (k == -EINVAL) { /* If the key was invalid give up right-away. */ return k; @@ -682,14 +753,14 @@ static int verify(sd_journal *j) { log_info("PASS: %s", f->path); if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) { - if (from > 0) { + if (validated > 0) { log_info("=> Validated from %s to %s, final %s entries not sealed.", - format_timestamp(a, sizeof(a), from), - format_timestamp(b, sizeof(b), to), - format_timespan(c, sizeof(c), total > to ? total - to : 0)); - } else if (total > 0) + format_timestamp(a, sizeof(a), first), + format_timestamp(b, sizeof(b), validated), + format_timespan(c, sizeof(c), last > validated ? last - validated : 0)); + } else if (last > 0) log_info("=> No sealing yet, %s of entries not sealed.", - format_timespan(c, sizeof(c), total)); + format_timespan(c, sizeof(c), last - first)); else log_info("=> No sealing yet, no entries in file."); } @@ -702,11 +773,11 @@ static int verify(sd_journal *j) { int main(int argc, char *argv[]) { int r; sd_journal *j = NULL; - unsigned line = 0; bool need_seek = false; sd_id128_t previous_boot_id; bool previous_boot_id_valid = false; bool have_pager; + unsigned n_shown = 0; log_parse_environment(); log_open(); @@ -728,7 +799,7 @@ int main(int argc, char *argv[]) { if (arg_directory) r = sd_journal_open_directory(&j, arg_directory, 0); else - r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0); + r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY); if (r < 0) { log_error("Failed to open journal: %s", strerror(-r)); @@ -746,6 +817,19 @@ int main(int argc, char *argv[]) { goto finish; } + if (arg_action == ACTION_DISK_USAGE) { + uint64_t bytes; + char sbytes[FORMAT_BYTES_MAX]; + + r = sd_journal_get_usage(j, &bytes); + if (r < 0) + goto finish; + + printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes)); + r = 0; + goto finish; + } + #ifdef HAVE_ACL if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) { log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages."); @@ -775,27 +859,24 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; - if (!arg_quiet) { - usec_t start, end; - char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX]; - - r = sd_journal_get_cutoff_realtime_usec(j, &start, &end); + if (arg_cursor) { + r = sd_journal_seek_cursor(j, arg_cursor); if (r < 0) { - log_error("Failed to get cutoff: %s", strerror(-r)); + log_error("Failed to seek to cursor: %s", strerror(-r)); goto finish; } - if (r > 0) { - if (arg_follow) - printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start)); - else - printf("Logs begin at %s, end at %s.\n", - format_timestamp(start_buf, sizeof(start_buf), start), - format_timestamp(end_buf, sizeof(end_buf), end)); + r = sd_journal_next(j); + + } else if (arg_since_set) { + r = sd_journal_seek_realtime_usec(j, arg_since); + if (r < 0) { + log_error("Failed to seek to date: %s", strerror(-r)); + goto finish; } - } + r = sd_journal_next(j); - if (arg_lines >= 0) { + } else if (arg_lines > 0) { r = sd_journal_seek_tail(j); if (r < 0) { log_error("Failed to seek to tail: %s", strerror(-r)); @@ -803,6 +884,7 @@ int main(int argc, char *argv[]) { } r = sd_journal_previous_skip(j, arg_lines); + } else { r = sd_journal_seek_head(j); if (r < 0) { @@ -821,13 +903,29 @@ int main(int argc, char *argv[]) { on_tty(); have_pager = !arg_no_pager && !arg_follow && pager_open(); + if (!arg_quiet) { + usec_t start, end; + char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX]; + + r = sd_journal_get_cutoff_realtime_usec(j, &start, &end); + if (r < 0) { + log_error("Failed to get cutoff: %s", strerror(-r)); + goto finish; + } + + if (r > 0) { + if (arg_follow) + printf("---- Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start)); + else + printf("---- Logs begin at %s, end at %s.\n", + format_timestamp(start_buf, sizeof(start_buf), start), + format_timestamp(end_buf, sizeof(end_buf), end)); + } + } + for (;;) { - for (;;) { - sd_id128_t boot_id; - int flags = - arg_show_all * OUTPUT_SHOW_ALL | - have_pager * OUTPUT_FULL_WIDTH | - on_tty() * OUTPUT_COLOR; + while (arg_lines == 0 || arg_follow || n_shown < arg_lines) { + int flags; if (need_seek) { r = sd_journal_next(j); @@ -840,23 +938,41 @@ int main(int argc, char *argv[]) { if (r == 0) break; - r = sd_journal_get_monotonic_usec(j, NULL, &boot_id); - if (r >= 0) { - if (previous_boot_id_valid && - !sd_id128_equal(boot_id, previous_boot_id)) - printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n"); + if (arg_until_set) { + usec_t usec; + + r = sd_journal_get_realtime_usec(j, &usec); + if (r < 0) { + log_error("Failed to determine timestamp: %s", strerror(-r)); + goto finish; + } + } + + if (!arg_merge) { + sd_id128_t boot_id; - previous_boot_id = boot_id; - previous_boot_id_valid = true; + r = sd_journal_get_monotonic_usec(j, NULL, &boot_id); + if (r >= 0) { + if (previous_boot_id_valid && + !sd_id128_equal(boot_id, previous_boot_id)) + printf(ANSI_HIGHLIGHT_ON "---- Reboot ----" ANSI_HIGHLIGHT_OFF "\n"); + + previous_boot_id = boot_id; + previous_boot_id_valid = true; + } } - line ++; + flags = + arg_show_all * OUTPUT_SHOW_ALL | + have_pager * OUTPUT_FULL_WIDTH | + on_tty() * OUTPUT_COLOR; - r = output_journal(j, arg_output, line, 0, flags); + r = output_journal(stdout, j, arg_output, 0, flags); if (r < 0) goto finish; need_seek = true; + n_shown++; } if (!arg_follow) @@ -864,7 +980,7 @@ int main(int argc, char *argv[]) { r = sd_journal_wait(j, (uint64_t) -1); if (r < 0) { - log_error("Couldn't wait for log event: %s", strerror(-r)); + log_error("Couldn't wait for journal event: %s", strerror(-r)); goto finish; } }