X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fjournal%2Fjournalctl.c;h=fc1fed277c4df6a0921b2be11f5d9125ad43097e;hp=701518244c116e63a487eaaa839cd8772f2c60db;hb=2100675e9fe0707fae8cee13fbe9316e94ef98f9;hpb=b3a0ad5ab142a142ab526aeb3d0b69e98e4e523c diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 701518244..fc1fed277 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -35,319 +35,14 @@ #include "util.h" #include "build.h" #include "pager.h" +#include "logs-show.h" -#define PRINT_THRESHOLD 128 - -static enum { - OUTPUT_SHORT, - OUTPUT_VERBOSE, - OUTPUT_EXPORT, - OUTPUT_JSON, - _OUTPUT_MAX -} arg_output = OUTPUT_SHORT; +static output_mode arg_output = OUTPUT_SHORT; static bool arg_follow = false; static bool arg_show_all = false; static bool arg_no_pager = false; - -static bool contains_unprintable(const void *p, size_t l) { - const char *j; - - for (j = p; j < (const char *) p + l; j++) - if (*j < ' ' || *j >= 127) - return true; - - return false; -} - -static int output_short(sd_journal *j, unsigned line) { - int r; - uint64_t realtime; - time_t t; - struct tm tm; - char buf[64]; - const void *data; - size_t length; - size_t n = 0; - - assert(j); - - r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) { - log_error("Failed to get realtime: %s", strerror(-r)); - return r; - } - - t = (time_t) (realtime / USEC_PER_SEC); - if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) { - log_error("Failed to format time."); - return -EINVAL; - } - - fputs(buf, stdout); - n += strlen(buf); - - if (sd_journal_get_data(j, "_HOSTNAME", &data, &length) >= 0 && - (arg_show_all || (!contains_unprintable(data, length) && - length < PRINT_THRESHOLD))) { - printf(" %.*s", (int) length - 10, ((const char*) data) + 10); - n += length - 10 + 1; - } - - if (sd_journal_get_data(j, "MESSAGE", &data, &length) >= 0) { - if (arg_show_all) - printf(" %.*s", (int) length - 8, ((const char*) data) + 8); - else if (contains_unprintable(data, length)) - fputs(" [blob data]", stdout); - else if (length - 8 + n < columns()) - printf(" %.*s", (int) length - 8, ((const char*) data) + 8); - else if (n < columns()) { - char *e; - - e = ellipsize_mem((const char *) data + 8, length - 8, columns() - n - 2, 90); - - if (!e) - printf(" %.*s", (int) length - 8, ((const char*) data) + 8); - else - printf(" %s", e); - - free(e); - } - } - - fputc('\n', stdout); - - return 0; -} - -static int output_verbose(sd_journal *j, unsigned line) { - const void *data; - size_t length; - char *cursor; - uint64_t realtime; - char ts[FORMAT_TIMESTAMP_MAX]; - int r; - - assert(j); - - r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - return r; - } - - printf("%s [%s]\n", - format_timestamp(ts, sizeof(ts), realtime), - cursor); - - free(cursor); - - SD_JOURNAL_FOREACH_DATA(j, data, length) { - if (!arg_show_all && (length > PRINT_THRESHOLD || - contains_unprintable(data, length))) { - const char *c; - - c = memchr(data, '=', length); - if (!c) { - log_error("Invalid field."); - return -EINVAL; - } - - printf("\t%.*s=[blob data]\n", - (int) (c - (const char*) data), - (const char*) data); - } else - printf("\t%.*s\n", (int) length, (const char*) data); - } - - return 0; -} - -static int output_export(sd_journal *j, unsigned line) { - sd_id128_t boot_id; - char sid[33]; - int r; - usec_t realtime, monotonic; - char *cursor; - const void *data; - size_t length; - - assert(j); - - r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); - if (r < 0) { - log_error("Failed to get monotonic timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - return r; - } - - printf(".cursor=%s\n" - ".realtime=%llu\n" - ".monotonic=%llu\n" - ".boot_id=%s\n", - cursor, - (unsigned long long) realtime, - (unsigned long long) monotonic, - sd_id128_to_string(boot_id, sid)); - - free(cursor); - - SD_JOURNAL_FOREACH_DATA(j, data, length) { - - if (contains_unprintable(data, length)) { - const char *c; - uint64_t le64; - - c = memchr(data, '=', length); - if (!c) { - log_error("Invalid field."); - return -EINVAL; - } - - fwrite(data, c - (const char*) data, 1, stdout); - fputc('\n', stdout); - le64 = htole64(length - (c - (const char*) data) - 1); - fwrite(&le64, sizeof(le64), 1, stdout); - fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout); - } else - fwrite(data, length, 1, stdout); - - fputc('\n', stdout); - } - - fputc('\n', stdout); - - return 0; -} - -static void json_escape(const char* p, size_t l) { - - if (contains_unprintable(p, l)) { - bool not_first = false; - - fputs("[ ", stdout); - - while (l > 0) { - if (not_first) - printf(", %u", (uint8_t) *p); - else { - not_first = true; - printf("%u", (uint8_t) *p); - } - - p++; - l--; - } - - fputs(" ]", stdout); - } else { - fputc('\"', stdout); - - while (l > 0) { - if (*p == '"' || *p == '\\') { - fputc('\\', stdout); - fputc(*p, stdout); - } else - fputc(*p, stdout); - - p++; - l--; - } - - fputc('\"', stdout); - } -} - -static int output_json(sd_journal *j, unsigned line) { - uint64_t realtime, monotonic; - char *cursor; - const void *data; - size_t length; - sd_id128_t boot_id; - char sid[33]; - int r; - - assert(j); - - r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); - if (r < 0) { - log_error("Failed to get monotonic timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - return r; - } - - if (line == 1) - fputc('\n', stdout); - else - fputs(",\n", stdout); - - printf("{\n" - "\t\".cursor\" : \"%s\",\n" - "\t\".realtime\" : %llu,\n" - "\t\".monotonic\" : %llu,\n" - "\t\".boot_id\" : \"%s\"", - cursor, - (unsigned long long) realtime, - (unsigned long long) monotonic, - sd_id128_to_string(boot_id, sid)); - - free(cursor); - - SD_JOURNAL_FOREACH_DATA(j, data, length) { - const char *c; - - c = memchr(data, '=', length); - if (!c) { - log_error("Invalid field."); - return -EINVAL; - } - - fputs(",\n\t", stdout); - json_escape(data, c - (const char*) data); - fputs(" : ", stdout); - json_escape(c + 1, length - (c - (const char*) data) - 1); - } - - fputs("\n}", stdout); - fflush(stdout); - - return 0; -} - -static int (*output_funcs[_OUTPUT_MAX])(sd_journal*j, unsigned line) = { - [OUTPUT_SHORT] = output_short, - [OUTPUT_VERBOSE] = output_verbose, - [OUTPUT_EXPORT] = output_export, - [OUTPUT_JSON] = output_json -}; +static int arg_lines = -1; static int help(void) { @@ -358,6 +53,7 @@ static int help(void) { " --no-pager Do not pipe output into a pager\n" " -a --all Show all properties, including long and unprintable\n" " -f --follow Follow journal\n" + " -n --lines=INTEGER Lines to show\n" " -o --output=STRING Change output mode (short, verbose, export, json)\n", program_invocation_short_name); @@ -378,15 +74,16 @@ 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' }, { NULL, 0, NULL, 0 } }; - int c; + int c, r; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hfo:a", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "hfo:an:", options, NULL)) >= 0) { switch (c) { @@ -427,6 +124,14 @@ static int parse_argv(int argc, char *argv[]) { arg_show_all = true; break; + case 'n': + r = safe_atoi(optarg, &arg_lines); + if (r < 0) { + log_error("Failed to parse lines '%s'", optarg); + return -EINVAL; + } + break; + case '?': return -EINVAL; @@ -471,10 +176,24 @@ int main(int argc, char *argv[]) { goto finish; } - r = sd_journal_seek_head(j); - if (r < 0) { - log_error("Failed to seek to head: %s", strerror(-r)); - goto finish; + if (arg_lines >= 0) { + r = sd_journal_seek_tail(j); + if (r < 0) { + log_error("Failed to seek to tail: %s", strerror(-r)); + goto finish; + } + + r = sd_journal_previous_skip(j, arg_lines); + if (r < 0) { + log_error("Failed to iterate through journal: %s", strerror(-r)); + goto finish; + } + } else { + r = sd_journal_seek_head(j); + if (r < 0) { + log_error("Failed to seek to head: %s", strerror(-r)); + goto finish; + } } if (!arg_no_pager && !arg_follow) { @@ -503,7 +222,7 @@ int main(int argc, char *argv[]) { line ++; - r = output_funcs[arg_output](j, line); + r = output_journal(j, arg_output, line, arg_show_all); if (r < 0) goto finish; }