chiark / gitweb /
journalctl: fix verbose output when no logs are found
[elogind.git] / src / shared / logs-show.c
index 8897a10c2b427750742667a06247629c3bb8bce5..7947df3a89e90354c04aad2bbed399977f0b1709 100644 (file)
@@ -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,34 +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 _cleanup_free_ *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);
-        } 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);
@@ -303,7 +327,7 @@ static int output_verbose(
 
         const void *data;
         size_t length;
-        char _cleanup_free_ *cursor = NULL;
+        _cleanup_free_ char *cursor = NULL;
         uint64_t realtime;
         char ts[FORMAT_TIMESTAMP_MAX];
         int r;
@@ -315,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;
         }
 
@@ -329,25 +354,32 @@ static int output_verbose(
                 format_timestamp(ts, sizeof(ts), realtime),
                 cursor);
 
-        SD_JOURNAL_FOREACH_DATA(j, data, length) {
-                if (!shall_print(data, length, flags)) {
-                        const char *c;
-                        char bytes[FORMAT_BYTES_MAX];
+        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;
 
-                        c = memchr(data, '=', length);
-                        if (!c) {
-                                log_error("Invalid field.");
-                                return -EINVAL;
-                        }
+                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];
 
-                        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);
 
@@ -365,7 +397,7 @@ static int output_export(
         char sid[33];
         int r;
         usec_t realtime, monotonic;
-        char _cleanup_free_ *cursor = NULL;
+        _cleanup_free_ char *cursor = NULL;
         const void *data;
         size_t length;
 
@@ -401,7 +433,7 @@ static int output_export(
                 (unsigned long long) monotonic,
                 sd_id128_to_string(boot_id, sid));
 
-        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 */
@@ -409,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;
 
@@ -430,6 +462,9 @@ static int output_export(
                 fputc('\n', f);
         }
 
+        if (r < 0)
+                return r;
+
         fputc('\n', f);
 
         return 0;
@@ -448,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);
@@ -473,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);
@@ -494,7 +531,7 @@ static int output_json(
                 OutputFlags flags) {
 
         uint64_t realtime, monotonic;
-        char _cleanup_free_ *cursor = NULL;
+        _cleanup_free_ char *cursor = NULL;
         const void *data;
         size_t length;
         sd_id128_t boot_id;
@@ -556,7 +593,7 @@ static int output_json(
                 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;
@@ -590,6 +627,9 @@ static int output_json(
                 }
         }
 
+        if (r < 0)
+                return r;
+
         separator = true;
         do {
                 done = true;
@@ -930,6 +970,33 @@ int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
         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,
@@ -941,7 +1008,7 @@ int show_journal_by_unit(
                 OutputFlags flags,
                 bool system) {
 
-        sd_journal _cleanup_journal_close_ *j = NULL;
+        _cleanup_journal_close_ sd_journal*j = NULL;
         int r;
         int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM_ONLY;
 
@@ -956,6 +1023,10 @@ int show_journal_by_unit(
         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
@@ -963,6 +1034,8 @@ int show_journal_by_unit(
         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;