chiark / gitweb /
fix a couple of issues found with llvm-analyze
[elogind.git] / src / shared / logs-show.c
index 4c59ca34cd55d8b132682b52ba8fe9024e130007..60eb896ade6e76b1b84e2ae175d5c81fcf064697 100644 (file)
 #include "logs-show.h"
 #include "log.h"
 #include "util.h"
+#include "utf8.h"
 
 #define PRINT_THRESHOLD 128
 
-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 parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
         size_t fl, nl;
         void *buf;
@@ -59,12 +50,11 @@ static int parse_field(const void *data, size_t length, const char *field, char
 
         nl = length - fl;
         buf = malloc(nl+1);
+        if (!buf)
+                return log_oom();
+
         memcpy(buf, (const char*) data + fl, nl);
         ((char*)buf)[nl] = 0;
-        if (!buf) {
-                log_error("Out of memory");
-                return -ENOMEM;
-        }
 
         free(*target);
         *target = buf;
@@ -80,24 +70,33 @@ static bool shall_print(bool show_all, char *p, size_t l) {
         if (l > PRINT_THRESHOLD)
                 return false;
 
-        if (contains_unprintable(p, l))
+        if (!utf8_is_printable_n(p, l))
                 return false;
 
         return true;
 }
 
-static int output_short(sd_journal *j, unsigned line, unsigned n_columns, bool show_all, bool monotonic_mode) {
+static int output_short(sd_journal *j, unsigned line, unsigned n_columns,
+                        OutputFlags flags) {
         int r;
         const void *data;
         size_t length;
         size_t n = 0;
-        char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = 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;
+        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(j);
 
         SD_JOURNAL_FOREACH_DATA(j, data, length) {
 
+                r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
+                if (r < 0)
+                        goto finish;
+                else if (r > 0)
+                        continue;
+
                 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
                 if (r < 0)
                         goto finish;
@@ -150,7 +149,10 @@ static int output_short(sd_journal *j, unsigned line, unsigned n_columns, bool s
                 goto finish;
         }
 
-        if (monotonic_mode) {
+        if (priority_len == 1 && *priority >= '0' && *priority <= '7')
+                p = *priority - '0';
+
+        if (flags & OUTPUT_MONOTONIC_MODE) {
                 uint64_t t;
                 sd_id128_t boot_id;
 
@@ -202,44 +204,59 @@ static int output_short(sd_journal *j, unsigned line, unsigned n_columns, bool s
                 n += strlen(buf);
         }
 
-        if (hostname && shall_print(show_all, hostname, hostname_len)) {
+        if (hostname && shall_print(flags & OUTPUT_SHOW_ALL,
+                                    hostname, hostname_len)) {
                 printf(" %.*s", (int) hostname_len, hostname);
                 n += hostname_len + 1;
         }
 
-        if (identifier && shall_print(show_all, identifier, identifier_len)) {
+        if (identifier && shall_print(flags & OUTPUT_SHOW_ALL,
+                                      identifier, identifier_len)) {
                 printf(" %.*s", (int) identifier_len, identifier);
                 n += identifier_len + 1;
-        } else if (comm && shall_print(show_all, comm, comm_len)) {
+        } else if (comm && shall_print(flags & OUTPUT_SHOW_ALL,
+                                       comm, comm_len)) {
                 printf(" %.*s", (int) comm_len, comm);
                 n += comm_len + 1;
         } else
                 putchar(' ');
 
-        if (pid && shall_print(show_all, pid, pid_len)) {
+        if (pid && shall_print(flags & OUTPUT_SHOW_ALL, pid, pid_len)) {
                 printf("[%.*s]", (int) pid_len, pid);
                 n += pid_len + 2;
-        } else if (fake_pid && shall_print(show_all, fake_pid, fake_pid_len)) {
+        } else if (fake_pid && shall_print(flags & OUTPUT_SHOW_ALL,
+                                           fake_pid, fake_pid_len)) {
                 printf("[%.*s]", (int) fake_pid_len, fake_pid);
                 n += fake_pid_len + 2;
         }
 
-        if (show_all)
-                printf(": %.*s\n", (int) message_len, message);
-        else if (contains_unprintable(message, message_len)) {
+        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)
+                printf(": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
+        else if (!utf8_is_printable_n(message, message_len)) {
                 char bytes[FORMAT_BYTES_MAX];
                 printf(": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
-        } else if (message_len + n < n_columns)
-                printf(": %.*s\n", (int) message_len, message);
-        else if (n < n_columns) {
+        } else if ((flags & OUTPUT_FULL_WIDTH) ||
+                   (message_len + n + 1 < n_columns))
+                printf(": %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)
-                        printf(": %.*s\n", (int) message_len, message);
+                        printf(": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
                 else
-                        printf(": %s\n", e);
+                        printf(": %s%s%s\n", color_on, e, color_off);
 
                 free(e);
         } else
@@ -256,19 +273,23 @@ finish:
         free(message);
         free(monotonic);
         free(realtime);
+        free(priority);
 
         return r;
 }
 
-static int output_short_realtime(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
-        return output_short(j, line, n_columns, show_all, false);
+static int output_short_realtime(sd_journal *j, unsigned line,
+                                 unsigned n_columns, OutputFlags flags) {
+        return output_short(j, line, n_columns, flags & ~OUTPUT_MONOTONIC_MODE);
 }
 
-static int output_short_monotonic(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
-        return output_short(j, line, n_columns, show_all, true);
+static int output_short_monotonic(sd_journal *j, unsigned line,
+                                  unsigned n_columns, OutputFlags flags) {
+        return output_short(j, line, n_columns, flags | OUTPUT_MONOTONIC_MODE);
 }
 
-static int output_verbose(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
+static int output_verbose(sd_journal *j, unsigned line,
+                          unsigned n_columns, OutputFlags flags) {
         const void *data;
         size_t length;
         char *cursor;
@@ -297,8 +318,8 @@ static int output_verbose(sd_journal *j, unsigned line, unsigned n_columns, bool
         free(cursor);
 
         SD_JOURNAL_FOREACH_DATA(j, data, length) {
-                if (!show_all && (length > PRINT_THRESHOLD ||
-                                  contains_unprintable(data, length))) {
+                if (!(flags & OUTPUT_SHOW_ALL) && (length > PRINT_THRESHOLD ||
+                                  !utf8_is_printable_n(data, length))) {
                         const char *c;
                         char bytes[FORMAT_BYTES_MAX];
 
@@ -319,7 +340,8 @@ static int output_verbose(sd_journal *j, unsigned line, unsigned n_columns, bool
         return 0;
 }
 
-static int output_export(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
+static int output_export(sd_journal *j, unsigned line,
+                         unsigned n_columns, OutputFlags flags) {
         sd_id128_t boot_id;
         char sid[33];
         int r;
@@ -367,7 +389,7 @@ static int output_export(sd_journal *j, unsigned line, unsigned n_columns, bool
                     memcmp(data, "_BOOT_ID=", 9) == 0)
                         continue;
 
-                if (contains_unprintable(data, length)) {
+                if (!utf8_is_printable_n(data, length)) {
                         const char *c;
                         uint64_t le64;
 
@@ -394,8 +416,7 @@ static int output_export(sd_journal *j, unsigned line, unsigned n_columns, bool
 }
 
 static void json_escape(const char* p, size_t l) {
-
-        if (contains_unprintable(p, l)) {
+        if (!utf8_is_printable_n(p, l)) {
                 bool not_first = false;
 
                 fputs("[ ", stdout);
@@ -431,7 +452,8 @@ static void json_escape(const char* p, size_t l) {
         }
 }
 
-static int output_json(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
+static int output_json(sd_journal *j, unsigned line,
+                       unsigned n_columns, OutputFlags flags) {
         uint64_t realtime, monotonic;
         char *cursor;
         const void *data;
@@ -504,7 +526,8 @@ static int output_json(sd_journal *j, unsigned line, unsigned n_columns, bool sh
         return 0;
 }
 
-static int output_cat(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
+static int output_cat(sd_journal *j, unsigned line,
+                      unsigned n_columns, OutputFlags flags) {
         const void *data;
         size_t l;
         int r;
@@ -525,7 +548,8 @@ static int output_cat(sd_journal *j, unsigned line, unsigned n_columns, bool sho
         return 0;
 }
 
-static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line, unsigned n_columns, bool show_all) = {
+static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line,
+                                             unsigned n_columns, OutputFlags flags) = {
         [OUTPUT_SHORT] = output_short_realtime,
         [OUTPUT_SHORT_MONOTONIC] = output_short_monotonic,
         [OUTPUT_VERBOSE] = output_verbose,
@@ -534,14 +558,15 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line, unsign
         [OUTPUT_CAT] = output_cat
 };
 
-int output_journal(sd_journal *j, OutputMode mode, unsigned line, unsigned n_columns, bool show_all) {
+int output_journal(sd_journal *j, OutputMode mode, unsigned line,
+                   unsigned n_columns, OutputFlags flags) {
         assert(mode >= 0);
         assert(mode < _OUTPUT_MODE_MAX);
 
         if (n_columns <= 0)
                 n_columns = columns();
 
-        return output_funcs[mode](j, line, n_columns, show_all);
+        return output_funcs[mode](j, line, n_columns, flags);
 }
 
 int show_journal_by_unit(
@@ -550,15 +575,14 @@ int show_journal_by_unit(
                 unsigned n_columns,
                 usec_t not_before,
                 unsigned how_many,
-                bool show_all,
-                bool follow) {
+                OutputFlags flags) {
 
         char *m = NULL;
         sd_journal *j = NULL;
         int r;
-        int fd;
         unsigned line = 0;
         bool need_seek = false;
+        int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
 
         assert(mode >= 0);
         assert(mode < _OUTPUT_MODE_MAX);
@@ -582,10 +606,6 @@ int show_journal_by_unit(
         if (r < 0)
                 goto finish;
 
-        fd = sd_journal_get_fd(j);
-        if (fd < 0)
-                goto finish;
-
         r = sd_journal_add_match(j, m, strlen(m));
         if (r < 0)
                 goto finish;
@@ -634,19 +654,35 @@ int show_journal_by_unit(
 
                         line ++;
 
-                        r = output_journal(j, mode, line, n_columns, show_all);
+                        r = output_journal(j, mode, line, n_columns, flags);
                         if (r < 0)
                                 goto finish;
                 }
 
-                if (!follow)
-                        break;
+                if (warn_cutoff && line < how_many && not_before > 0) {
+                        sd_id128_t boot_id;
+                        usec_t cutoff;
 
-                r = fd_wait_for_event(fd, POLLIN, (usec_t) -1);
-                if (r < 0)
-                        goto finish;
+                        /* Check whether the cutoff line is too early */
+
+                        r = sd_id128_get_boot(&boot_id);
+                        if (r < 0)
+                                goto finish;
+
+                        r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
+                        if (r < 0)
+                                goto finish;
+
+                        if (r > 0 && not_before < cutoff)
+                                printf("Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
+
+                        warn_cutoff = false;
+                }
+
+                if (!(flags & OUTPUT_FOLLOW))
+                        break;
 
-                r = sd_journal_process(j);
+                r = sd_journal_wait(j, (usec_t) -1);
                 if (r < 0)
                         goto finish;