X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fshared%2Flogs-show.c;h=7947df3a89e90354c04aad2bbed399977f0b1709;hp=04450a550466dd40894df16de4beb046569df1d5;hb=a72b63536f1da3a97677bf38590f22a962e5fe98;hpb=e8bc0ea2b100158bf35c635039226235aa879068 diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 04450a550..7947df3a8 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -30,6 +30,7 @@ #include "util.h" #include "utf8.h" #include "hashmap.h" +#include "journal-internal.h" #define PRINT_THRESHOLD 128 #define JSON_THRESHOLD 4096 @@ -94,12 +95,54 @@ static bool shall_print(const char *p, size_t l, OutputFlags flags) { if (l >= PRINT_THRESHOLD) return false; - if (!utf8_is_printable_n(p, l)) + if (!utf8_is_printable(p, l)) return false; return true; } +static void print_multiline(FILE *f, unsigned prefix, unsigned n_columns, int flags, int priority, const char* message, size_t message_len) { + const char *color_on = "", *color_off = ""; + const char *pos, *end; + bool continuation = false; + + if (flags & OUTPUT_COLOR) { + if (priority <= LOG_ERR) { + color_on = ANSI_HIGHLIGHT_RED_ON; + color_off = ANSI_HIGHLIGHT_OFF; + } else if (priority <= LOG_NOTICE) { + color_on = ANSI_HIGHLIGHT_ON; + color_off = ANSI_HIGHLIGHT_OFF; + } + } + + for (pos = message; pos < message + message_len; pos = end + 1) { + int len; + for (end = pos; end < message + message_len && *end != '\n'; end++) + ; + len = end - pos; + assert(len >= 0); + + if ((flags & OUTPUT_FULL_WIDTH) || (prefix + len + 1 < n_columns)) + fprintf(f, "%*s%s%.*s%s\n", + continuation * prefix, "", + color_on, len, pos, color_off); + else if (prefix < n_columns && n_columns - prefix >= 3) { + _cleanup_free_ char *e; + + e = ellipsize_mem(pos, len, n_columns - prefix, 90); + + if (!e) + fprintf(f, "%s%.*s%s\n", color_on, len, pos, color_off); + else + fprintf(f, "%s%s%s\n", color_on, e, color_off); + } else + fputs("...\n", f); + + continuation = true; + } +} + static int output_short( FILE *f, sd_journal *j, @@ -114,14 +157,13 @@ static int output_short( _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL; size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0; int p = LOG_INFO; - const char *color_on = "", *color_off = ""; assert(f); assert(j); sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : PRINT_THRESHOLD); - SD_JOURNAL_FOREACH_DATA(j, data, length) { + JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { r = parse_field(data, length, "PRIORITY=", &priority, &priority_len); if (r < 0) @@ -176,6 +218,9 @@ static int output_short( return r; } + if (r < 0) + return r; + if (!message) return 0; @@ -198,7 +243,7 @@ static int output_short( r = sd_journal_get_monotonic_usec(j, &t, &boot_id); if (r < 0) { - log_error("Failed to get monotonic: %s", strerror(-r)); + log_error("Failed to get monotonic timestamp: %s", strerror(-r)); return r; } @@ -223,7 +268,7 @@ static int output_short( r = sd_journal_get_realtime_usec(j, &x); if (r < 0) { - log_error("Failed to get realtime: %s", strerror(-r)); + log_error("Failed to get realtime timestamp: %s", strerror(-r)); return r; } @@ -259,36 +304,13 @@ static int output_short( n += fake_pid_len + 2; } - if (flags & OUTPUT_COLOR) { - if (p <= LOG_ERR) { - color_on = ANSI_HIGHLIGHT_RED_ON; - color_off = ANSI_HIGHLIGHT_OFF; - } else if (p <= LOG_NOTICE) { - color_on = ANSI_HIGHLIGHT_ON; - color_off = ANSI_HIGHLIGHT_OFF; - } - } - - if (flags & OUTPUT_SHOW_ALL) - fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off); - else if (!utf8_is_printable_n(message, message_len)) { + if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) { char bytes[FORMAT_BYTES_MAX]; fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len)); - } else if ((flags & OUTPUT_FULL_WIDTH) || (message_len + n + 1 < n_columns)) - fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off); - else if (n < n_columns && n_columns - n - 2 >= 3) { - char *e; - - e = ellipsize_mem(message, message_len, n_columns - n - 2, 90); - - if (!e) - fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off); - else - fprintf(f, ": %s%s%s\n", color_on, e, color_off); - - free(e); - } else - fputs("\n", f); + } else { + fputs(": ", f); + print_multiline(f, n + 2, n_columns, flags, p, message, message_len); + } if (flags & OUTPUT_CATALOG) print_catalog(f, j); @@ -305,7 +327,7 @@ static int output_verbose( const void *data; size_t length; - char *cursor; + _cleanup_free_ char *cursor = NULL; uint64_t realtime; char ts[FORMAT_TIMESTAMP_MAX]; int r; @@ -317,7 +339,8 @@ static int output_verbose( r = sd_journal_get_realtime_usec(j, &realtime); if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); + log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, + "Failed to get realtime timestamp: %s", strerror(-r)); return r; } @@ -331,27 +354,32 @@ static int output_verbose( format_timestamp(ts, sizeof(ts), realtime), cursor); - free(cursor); + JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { + const char *c; + int fieldlen; + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + fieldlen = c - (const char*) data; - SD_JOURNAL_FOREACH_DATA(j, data, length) { - if (!shall_print(data, length, flags)) { - const char *c; + if ((flags & OUTPUT_SHOW_ALL) || (length < PRINT_THRESHOLD && utf8_is_printable(data, length))) { + fprintf(f, " %.*s=", fieldlen, (const char*)data); + print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1); + } else { char bytes[FORMAT_BYTES_MAX]; - c = memchr(data, '=', length); - if (!c) { - log_error("Invalid field."); - return -EINVAL; - } - - fprintf(f, "\t%.*s=[%s blob data]\n", - (int) (c - (const char*) data), - (const char*) data, - format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1)); - } else - fprintf(f, "\t%.*s\n", (int) length, (const char*) data); + fprintf(f, " %.*s=[%s blob data]\n", + (int) (c - (const char*) data), + (const char*) data, + format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1)); + } } + if (r < 0) + return r; + if (flags & OUTPUT_CATALOG) print_catalog(f, j); @@ -369,7 +397,7 @@ static int output_export( char sid[33]; int r; usec_t realtime, monotonic; - char *cursor; + _cleanup_free_ char *cursor = NULL; const void *data; size_t length; @@ -405,9 +433,7 @@ static int output_export( (unsigned long long) monotonic, sd_id128_to_string(boot_id, sid)); - free(cursor); - - SD_JOURNAL_FOREACH_DATA(j, data, length) { + JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { /* We already printed the boot id, from the data in * the header, hence let's suppress it here */ @@ -415,7 +441,7 @@ static int output_export( memcmp(data, "_BOOT_ID=", 9) == 0) continue; - if (!utf8_is_printable_n(data, length)) { + if (!utf8_is_printable(data, length)) { const char *c; uint64_t le64; @@ -436,6 +462,9 @@ static int output_export( fputc('\n', f); } + if (r < 0) + return r; + fputc('\n', f); return 0; @@ -454,7 +483,7 @@ void json_escape( fputs("null", f); - else if (!utf8_is_printable_n(p, l)) { + else if (!utf8_is_printable(p, l)) { bool not_first = false; fputs("[ ", f); @@ -479,7 +508,9 @@ void json_escape( if (*p == '"' || *p == '\\') { fputc('\\', f); fputc(*p, f); - } else if (*p < ' ') + } else if (*p == '\n') + fputs("\\n", f); + else if (*p < ' ') fprintf(f, "\\u%04x", *p); else fputc(*p, f); @@ -500,11 +531,11 @@ static int output_json( OutputFlags flags) { uint64_t realtime, monotonic; - char *cursor, *k; + _cleanup_free_ char *cursor = NULL; const void *data; size_t length; sd_id128_t boot_id; - char sid[33]; + char sid[33], *k; int r; Hashmap *h = NULL; bool done, separator; @@ -556,14 +587,13 @@ static int output_json( (unsigned long long) monotonic, sd_id128_to_string(boot_id, sid)); } - free(cursor); h = hashmap_new(string_hash_func, string_compare_func); if (!h) return -ENOMEM; /* First round, iterate through the entry and count how often each field appears */ - SD_JOURNAL_FOREACH_DATA(j, data, length) { + JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { const char *eq; char *n; unsigned u; @@ -597,6 +627,9 @@ static int output_json( } } + if (r < 0) + return r; + separator = true; do { done = true; @@ -781,72 +814,22 @@ int output_journal( return ret; } -int show_journal_by_unit( - FILE *f, - const char *unit, - OutputMode mode, - unsigned n_columns, - usec_t not_before, - unsigned how_many, - OutputFlags flags) { +static int show_journal(FILE *f, + sd_journal *j, + OutputMode mode, + unsigned n_columns, + usec_t not_before, + unsigned how_many, + OutputFlags flags) { - _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL; - sd_journal *j = NULL; int r; unsigned line = 0; bool need_seek = false; int warn_cutoff = flags & OUTPUT_WARN_CUTOFF; + assert(j); assert(mode >= 0); assert(mode < _OUTPUT_MODE_MAX); - assert(unit); - - if (!endswith(unit, ".service") && - !endswith(unit, ".socket") && - !endswith(unit, ".mount") && - !endswith(unit, ".swap")) - return 0; - - if (how_many <= 0) - return 0; - - if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 || - asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 || - asprintf(&m3, "UNIT=%s", unit) < 0) { - r = -ENOMEM; - goto finish; - } - - r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY); - if (r < 0) - goto finish; - - /* Look for messages from the service itself */ - r = sd_journal_add_match(j, m1, 0); - if (r < 0) - goto finish; - - /* Look for coredumps of the service */ - r = sd_journal_add_disjunction(j); - if (r < 0) - goto finish; - r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0); - if (r < 0) - goto finish; - r = sd_journal_add_match(j, m2, 0); - if (r < 0) - goto finish; - - /* Look for messages from PID 1 about this service */ - r = sd_journal_add_disjunction(j); - if (r < 0) - goto finish; - r = sd_journal_add_match(j, "_PID=1", 0); - if (r < 0) - goto finish; - r = sd_journal_add_match(j, m3, 0); - if (r < 0) - goto finish; /* Seek to end */ r = sd_journal_seek_tail(j); @@ -923,12 +906,143 @@ int show_journal_by_unit( } finish: - if (j) - sd_journal_close(j); + return r; +} + +int add_matches_for_unit(sd_journal *j, const char *unit) { + int r; + _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL; + + assert(j); + assert(unit); + + if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 || + asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 || + asprintf(&m3, "UNIT=%s", unit) < 0) + return -ENOMEM; + + (void)( + /* Look for messages from the service itself */ + (r = sd_journal_add_match(j, m1, 0)) || + + /* Look for coredumps of the service */ + (r = sd_journal_add_disjunction(j)) || + (r = sd_journal_add_match(j, + "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) || + (r = sd_journal_add_match(j, m2, 0)) || + + /* Look for messages from PID 1 about this service */ + (r = sd_journal_add_disjunction(j)) || + (r = sd_journal_add_match(j, "_PID=1", 0)) || + (r = sd_journal_add_match(j, m3, 0)) + ); + return r; +} + +int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) { + int r; + _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL, *m4 = NULL; + + assert(j); + assert(unit); + if (asprintf(&m1, "_SYSTEMD_USER_UNIT=%s", unit) < 0 || + asprintf(&m2, "USER_UNIT=%s", unit) < 0 || + asprintf(&m3, "COREDUMP_USER_UNIT=%s", unit) < 0 || + asprintf(&m4, "_UID=%d", uid) < 0) + return -ENOMEM; + + (void) ( + /* Look for messages from the user service itself */ + (r = sd_journal_add_match(j, m1, 0)) || + (r = sd_journal_add_match(j, m4, 0)) || + + /* Look for messages from systemd about this service */ + (r = sd_journal_add_disjunction(j)) || + (r = sd_journal_add_match(j, m2, 0)) || + (r = sd_journal_add_match(j, m4, 0)) || + + /* Look for coredumps of the service */ + (r = sd_journal_add_disjunction(j)) || + (r = sd_journal_add_match(j, m3, 0)) || + (r = sd_journal_add_match(j, m4, 0)) + ); return r; } +int add_match_this_boot(sd_journal *j) { + char match[9+32+1] = "_BOOT_ID="; + sd_id128_t boot_id; + int r; + + assert(j); + + r = sd_id128_get_boot(&boot_id); + if (r < 0) { + log_error("Failed to get boot id: %s", strerror(-r)); + return r; + } + + sd_id128_to_string(boot_id, match + 9); + r = sd_journal_add_match(j, match, strlen(match)); + if (r < 0) { + log_error("Failed to add match: %s", strerror(-r)); + return r; + } + + r = sd_journal_add_conjunction(j); + if (r < 0) + return r; + + return 0; +} + +int show_journal_by_unit( + FILE *f, + const char *unit, + OutputMode mode, + unsigned n_columns, + usec_t not_before, + unsigned how_many, + uid_t uid, + OutputFlags flags, + bool system) { + + _cleanup_journal_close_ sd_journal*j = NULL; + int r; + int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM_ONLY; + + assert(mode >= 0); + assert(mode < _OUTPUT_MODE_MAX); + assert(unit); + + if (how_many <= 0) + return 0; + + r = sd_journal_open(&j, jflags); + if (r < 0) + return r; + + r = add_match_this_boot(j); + if (r < 0) + return r; + + if (system) + r = add_matches_for_unit(j, unit); + else + r = add_matches_for_user_unit(j, unit, uid); + if (r < 0) + return r; + + log_debug("Journal filter: %s", journal_make_match_string(j)); + + r = show_journal(f, j, mode, n_columns, not_before, how_many, flags); + if (r < 0) + return r; + + return 0; +} + static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { [OUTPUT_SHORT] = "short", [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",