X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fjournal%2Fjournalctl.c;h=c44be745b861568f68a00391ec7ac19f602d4cdd;hb=26fefda707e6511733f48da03b281a004dba6abf;hp=7415abc74f0160d4e73863d855f9f815263a4542;hpb=248fc619b5e3e24d78f171f95b85916eee7987bd;p=elogind.git diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 7415abc74..c44be745b 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -45,9 +45,9 @@ #include "logs-show.h" #include "util.h" #include "path-util.h" +#include "fileio.h" #include "build.h" #include "pager.h" -#include "logs-show.h" #include "strv.h" #include "journal-internal.h" #include "journal-def.h" @@ -63,15 +63,15 @@ 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; static bool arg_no_tail = false; static bool arg_quiet = false; static bool arg_merge = false; -static bool arg_boot_id = false; -static char *arg_boot_id_descriptor = NULL; +static bool arg_boot = false; +static char *arg_boot_descriptor = NULL; static bool arg_dmesg = false; static const char *arg_cursor = NULL; static const char *arg_after_cursor = NULL; @@ -103,12 +103,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) { @@ -124,6 +126,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" @@ -133,10 +136,11 @@ static int help(void) { " -n --lines[=INTEGER] Number of journal entries to show\n" " --no-tail Show all lines, even in follow mode\n" " -r --reverse Show the newest entries first\n" - " -o --output=STRING Change journal output mode (short, short-monotonic,\n" - " verbose, export, json, json-pretty, json-sse, cat)\n" + " -o --output=STRING Change journal output mode (short, short-iso,\n" + " 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" @@ -173,8 +177,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, @@ -206,12 +212,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 }, @@ -239,7 +247,7 @@ 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 } + {} }; int c, r; @@ -252,8 +260,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { case 'h': - help(); - return 0; + return help(); case ARG_VERSION: puts(PACKAGE_STRING); @@ -296,6 +303,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; @@ -346,17 +357,28 @@ static int parse_argv(int argc, char *argv[]) { break; case 'b': + arg_boot = true; + if (optarg) - arg_boot_id_descriptor = optarg; - else if (optind < argc && argv[optind][0] != '-') { - arg_boot_id_descriptor = argv[optind]; - optind++; + arg_boot_descriptor = optarg; + else if (optind < argc) { + int boot; + + if (argv[optind][0] != '-' || + safe_atoi(argv[optind], &boot) >= 0) { + arg_boot_descriptor = argv[optind]; + optind++; + } } - arg_boot_id = true; + + break; + + case ARG_LIST_BOOTS: + arg_action = ACTION_LIST_BOOTS; break; case 'k': - arg_boot_id = arg_dmesg = true; + arg_boot = arg_dmesg = true; break; case ARG_SYSTEM: @@ -519,9 +541,6 @@ static int parse_argv(int argc, char *argv[]) { return log_oom(); break; - case '?': - return -EINVAL; - case 'F': arg_field = optarg; break; @@ -546,9 +565,11 @@ 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"); } } @@ -620,8 +641,9 @@ static int add_matches(sd_journal *j, char **args) { if (streq(*i, "+")) r = sd_journal_add_disjunction(j); else if (path_is_absolute(*i)) { - _cleanup_free_ char *p, *t = NULL; + _cleanup_free_ char *p, *t = NULL, *t2 = NULL; const char *path; + _cleanup_free_ char *interpreter = NULL; struct stat st; p = canonicalize_file_name(*i); @@ -632,9 +654,27 @@ static int add_matches(sd_journal *j, char **args) { return -errno; } - if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) - t = strappend("_EXE=", path); - else if (S_ISCHR(st.st_mode)) + if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) { + if (executable_is_script(path, &interpreter) > 0) { + _cleanup_free_ char *comm; + + comm = strndup(path_get_file_name(path), 15); + if (!comm) + return log_oom(); + + t = strappend("_COMM=", comm); + + /* Append _EXE only if the interpreter is not a link. + Otherwise it might be outdated often. */ + if (lstat(interpreter, &st) == 0 && + !S_ISLNK(st.st_mode)) { + t2 = strappend("_EXE=", interpreter); + if (!t2) + return log_oom(); + } + } else + t = strappend("_EXE=", path); + } else if (S_ISCHR(st.st_mode)) asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev)); else if (S_ISBLK(st.st_mode)) asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev)); @@ -647,6 +687,8 @@ static int add_matches(sd_journal *j, char **args) { return log_oom(); r = sd_journal_add_match(j, t, 0); + if (t2) + r = sd_journal_add_match(j, t2, 0); } else r = sd_journal_add_match(j, *i, 0); @@ -662,32 +704,107 @@ 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; - unsigned int id_count = 0; + unsigned int count = 0; size_t length, allocated = 0; - boot_id_t ref_boot_id, *id; + boot_id_t ref_boot_id = {SD_ID128_NULL}, *id; _cleanup_free_ boot_id_t *all_ids = NULL; - bool find_first_boot = false, ref_boot_found = false; assert(j); assert(boot_id); - if (relative == 0) + if (relative == 0 && !sd_id128_equal(*boot_id, SD_ID128_NULL)) return 0; - if (sd_id128_equal(*boot_id, SD_ID128_NULL) && relative > 0) { - find_first_boot = true; - relative--; - } - r = sd_journal_query_unique(j, "_BOOT_ID"); if (r < 0) return r; @@ -696,123 +813,118 @@ static int get_relative_boot_id(sd_journal *j, sd_id128_t *boot_id, int relative if (length < strlen("_BOOT_ID=")) continue; - if (!GREEDY_REALLOC(all_ids, allocated, id_count + 1)) + if (!GREEDY_REALLOC(all_ids, allocated, count + 1)) return log_oom(); - id = &all_ids[id_count]; + id = &all_ids[count]; r = sd_id128_from_string(((const char *)data) + strlen("_BOOT_ID="), &id->id); - if (r < 0) { + if (r < 0) continue; - } - sd_journal_flush_matches(j); r = sd_journal_add_match(j, data, length); if (r < 0) - continue; + return r; r = sd_journal_seek_head(j); if (r < 0) - continue; + return r; r = sd_journal_next(j); - if (r <= 0) - continue; + if (r < 0) + return r; + 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) - continue; + return r; - if (!find_first_boot && sd_id128_equal(id->id, *boot_id)) { + if (sd_id128_equal(id->id, *boot_id)) ref_boot_id = *id; - ref_boot_found = true; - } - id_count++; + count++; + flush: + sd_journal_flush_matches(j); } - *boot_id = SD_ID128_NULL; - sd_journal_flush_matches(j); + qsort_safe(all_ids, count, sizeof(boot_id_t), boot_id_cmp); - if (id_count == 0 || (!find_first_boot && !ref_boot_found)) - return 0; + if (sd_id128_equal(*boot_id, SD_ID128_NULL)) { + if (relative > (int) count || relative <= -(int)count) + return -EADDRNOTAVAIL; - qsort(all_ids, id_count, sizeof(boot_id_t), boot_id_cmp); - if (find_first_boot) - id = all_ids; - else - id = bsearch(&ref_boot_id, all_ids, id_count, sizeof(boot_id_t), boot_id_cmp); + *boot_id = all_ids[(relative <= 0)*count + relative - 1].id; + } else { + id = bsearch(&ref_boot_id, all_ids, count, sizeof(boot_id_t), boot_id_cmp); - if (!id || (relative < 0 && ((id - all_ids) + relative) < 0) || - (relative >= 0 && (unsigned long)((id - all_ids) + relative) >= id_count)) - return 0; + if (!id || + relative <= 0 ? (id - all_ids) + relative < 0 : + (id - all_ids) + relative >= (int) count) + return -EADDRNOTAVAIL; + + *boot_id = (id + relative)->id; + } - id += relative; - *boot_id = id->id; return 0; } static int add_boot(sd_journal *j) { char match[9+32+1] = "_BOOT_ID="; - char *marker; - sd_id128_t boot_id; + char *offset; + sd_id128_t boot_id = SD_ID128_NULL; int r, relative = 0; assert(j); - if (!arg_boot_id) + if (!arg_boot) return 0; - if (arg_boot_id_descriptor) { - marker = strchr(arg_boot_id_descriptor, ':'); - if (marker) { - *marker = '\0'; - marker++; + if (!arg_boot_descriptor) + return add_match_this_boot(j); - if (*marker == '\0') - relative = -1; - else { - r = safe_atoi(marker, &relative); - if (r < 0) { - log_error("Failed to parse relative boot ID number '%s'", marker); - return -EINVAL; - } - } + if (strlen(arg_boot_descriptor) >= 32) { + char tmp = arg_boot_descriptor[32]; + arg_boot_descriptor[32] = '\0'; + r = sd_id128_from_string(arg_boot_descriptor, &boot_id); + arg_boot_descriptor[32] = tmp; + + if (r < 0) { + log_error("Failed to parse boot ID '%.32s': %s", + arg_boot_descriptor, strerror(-r)); + return r; } - } - if (isempty(arg_boot_id_descriptor)) { - if (relative > 0) { - /* We cannot look into the future. Instead, we look - * into the past (starting from first boot). The ID - * will be looked up later */ - boot_id = SD_ID128_NULL; - } else { - r = sd_id128_get_boot(&boot_id); - if (r < 0) { - log_error("Failed to get boot ID: %s", strerror(-r)); - return r; - } + offset = arg_boot_descriptor + 32; + + if (*offset && *offset != '-' && *offset != '+') { + log_error("Relative boot ID offset must start with a '+' or a '-', found '%s' ", offset); + return -EINVAL; } - } else { - r = sd_id128_from_string(arg_boot_id_descriptor, &boot_id); + } else + offset = arg_boot_descriptor; + + if (*offset) { + r = safe_atoi(offset, &relative); if (r < 0) { - log_error("Failed to parse boot ID: %s", strerror(-r)); - return r; + log_error("Failed to parse relative boot ID number '%s'", offset); + return -EINVAL; } } r = get_relative_boot_id(j, &boot_id, relative); if (r < 0) { - log_error("Failed to look up boot ID: %s", strerror(-r)); + if (sd_id128_equal(boot_id, SD_ID128_NULL)) + log_error("Failed to look up boot %+d: %s", relative, strerror(-r)); + else + log_error("Failed to look up boot ID "SD_ID128_FORMAT_STR"%+d: %s", + SD_ID128_FORMAT_VAL(boot_id), relative, strerror(-r)); return r; - } else if (sd_id128_equal(boot_id, SD_ID128_NULL)) { - log_error("Failed to find boot ID"); - return -1; } sd_id128_to_string(boot_id, match + 9); - r = sd_journal_add_match(j, match, strlen(match)); + + r = sd_journal_add_match(j, match, sizeof(match) - 1); if (r < 0) { log_error("Failed to add match: %s", strerror(-r)); return r; @@ -1282,11 +1394,12 @@ static int access_check(sd_journal *j) { int main(int argc, char *argv[]) { int r; - _cleanup_journal_close_ sd_journal*j = NULL; + _cleanup_journal_close_ sd_journal *j = NULL; bool need_seek = false; sd_id128_t previous_boot_id; bool previous_boot_id_valid = false, first_line = true; int n_shown = 0; + bool ellipsized = false; setlocale(LC_ALL, ""); log_parse_environment(); @@ -1383,6 +1496,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); @@ -1408,7 +1526,12 @@ int main(int argc, char *argv[]) { if (r < 0) return EXIT_FAILURE; - log_debug("Journal filter: %s", j->level0 ? journal_make_match_string(j) : "none"); + if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) { + _cleanup_free_ char *filter; + + filter = journal_make_match_string(j); + log_debug("Journal filter: %s", filter); + } if (arg_field) { const void *data; @@ -1582,14 +1705,13 @@ int main(int argc, char *argv[]) { if (!arg_merge) { sd_id128_t boot_id; - const char *color_on = on_tty() ? ANSI_HIGHLIGHT_ON : "", - *color_off = on_tty() ? ANSI_HIGHLIGHT_OFF : ""; 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("%s-- Reboot --%s\n", color_on, color_off); + printf("%s-- Reboot --%s\n", + ansi_highlight(), ansi_highlight_off()); previous_boot_id = boot_id; previous_boot_id_valid = true; @@ -1598,11 +1720,11 @@ 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; - r = output_journal(stdout, j, arg_output, 0, flags); + r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized); need_seek = true; if (r == -EADDRNOTAVAIL) break;