summaryrefslogtreecommitdiff
path: root/src/shared/logs-show.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/logs-show.c')
-rw-r--r--src/shared/logs-show.c410
1 files changed, 211 insertions, 199 deletions
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index 33afbe2f7f..525a948f36 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -21,6 +21,7 @@
#include "hostname-util.h"
#include "io-util.h"
#include "journal-internal.h"
+#include "json.h"
#include "log.h"
#include "logs-show.h"
#include "macro.h"
@@ -41,7 +42,7 @@
#define PRINT_LINE_THRESHOLD 3
#define PRINT_CHAR_THRESHOLD 300
-#define JSON_THRESHOLD 4096
+#define JSON_THRESHOLD 4096U
static int print_catalog(FILE *f, sd_journal *j) {
int r;
@@ -170,6 +171,10 @@ static bool print_multiline(
color_on = ANSI_HIGHLIGHT;
color_off = ANSI_NORMAL;
highlight_on = ANSI_HIGHLIGHT_RED;
+ } else if (priority >= LOG_DEBUG) {
+ color_on = ANSI_GREY;
+ color_off = ANSI_NORMAL;
+ highlight_on = ANSI_HIGHLIGHT_RED;
}
}
@@ -302,10 +307,9 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou
k = format_timestamp_utc(buf, sizeof(buf), x);
else
k = format_timestamp(buf, sizeof(buf), x);
- if (!k) {
- log_error("Failed to format timestamp: %"PRIu64, x);
- return -EINVAL;
- }
+ if (!k)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to format timestamp: %" PRIu64, x);
} else {
char usec[7];
@@ -320,18 +324,16 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou
break;
case OUTPUT_SHORT_ISO:
- if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)) <= 0) {
- log_error("Failed to format ISO time");
- return -EINVAL;
- }
+ if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to format ISO time");
break;
case OUTPUT_SHORT_ISO_PRECISE:
/* No usec in strftime, so we leave space and copy over */
- if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.xxxxxx%z", gettime_r(&t, &tm)) <= 0) {
- log_error("Failed to format ISO-precise time");
- return -EINVAL;
- }
+ if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.xxxxxx%z", gettime_r(&t, &tm)) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to format ISO-precise time");
xsprintf(usec, "%06"PRI_USEC, x % USEC_PER_SEC);
memcpy(buf + 20, usec, 6);
break;
@@ -339,10 +341,9 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou
case OUTPUT_SHORT:
case OUTPUT_SHORT_PRECISE:
- if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)) <= 0) {
- log_error("Failed to format syslog time");
- return -EINVAL;
- }
+ if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to format syslog time");
if (mode == OUTPUT_SHORT_PRECISE) {
size_t k;
@@ -351,10 +352,9 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou
k = sizeof(buf) - strlen(buf);
r = snprintf(buf + strlen(buf), k, ".%06"PRIu64, x % USEC_PER_SEC);
- if (r <= 0 || (size_t) r >= k) { /* too long? */
- log_error("Failed to format precise time");
- return -EINVAL;
- }
+ if (r <= 0 || (size_t) r >= k) /* too long? */
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to format precise time");
}
break;
@@ -374,7 +374,7 @@ static int output_short(
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
int r;
const void *data;
@@ -505,7 +505,7 @@ static int output_verbose(
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
const void *data;
size_t length;
@@ -562,10 +562,9 @@ static int output_verbose(
const char *on = "", *off = "";
c = memchr(data, '=', length);
- if (!c) {
- log_error("Invalid field.");
- return -EINVAL;
- }
+ if (!c)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid field.");
fieldlen = c - (const char*) data;
r = field_set_test(output_fields, data, fieldlen);
@@ -613,7 +612,7 @@ static int output_export(
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
sd_id128_t boot_id;
char sid[33];
@@ -657,10 +656,9 @@ static int output_export(
continue;
c = memchr(data, '=', length);
- if (!c) {
- log_error("Invalid field.");
- return -EINVAL;
- }
+ if (!c)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid field.");
r = field_set_test(output_fields, data, c - (const char *) data);
if (r < 0)
@@ -747,6 +745,96 @@ void json_escape(
}
}
+struct json_data {
+ JsonVariant* name;
+ size_t n_values;
+ JsonVariant* values[];
+};
+
+static int update_json_data(
+ Hashmap *h,
+ OutputFlags flags,
+ const char *name,
+ const void *value,
+ size_t size) {
+
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ struct json_data *d;
+ int r;
+
+ if (!(flags & OUTPUT_SHOW_ALL) && strlen(name) + 1 + size >= JSON_THRESHOLD)
+ r = json_variant_new_null(&v);
+ else if (utf8_is_printable(value, size))
+ r = json_variant_new_stringn(&v, value, size);
+ else
+ r = json_variant_new_array_bytes(&v, value, size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate JSON data: %m");
+
+ d = hashmap_get(h, name);
+ if (d) {
+ struct json_data *w;
+
+ w = realloc(d, offsetof(struct json_data, values) + sizeof(JsonVariant*) * (d->n_values + 1));
+ if (!w)
+ return log_oom();
+
+ d = w;
+ assert_se(hashmap_update(h, json_variant_string(d->name), d) >= 0);
+ } else {
+ _cleanup_(json_variant_unrefp) JsonVariant *n = NULL;
+
+ r = json_variant_new_string(&n, name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate JSON name variant: %m");
+
+ d = malloc0(offsetof(struct json_data, values) + sizeof(JsonVariant*));
+ if (!d)
+ return log_oom();
+
+ r = hashmap_put(h, json_variant_string(n), d);
+ if (r < 0) {
+ free(d);
+ return log_error_errno(r, "Failed to insert JSON name into hashmap: %m");
+ }
+
+ d->name = TAKE_PTR(n);
+ }
+
+ d->values[d->n_values++] = TAKE_PTR(v);
+ return 0;
+}
+
+static int update_json_data_split(
+ Hashmap *h,
+ OutputFlags flags,
+ Set *output_fields,
+ const void *data,
+ size_t size) {
+
+ const char *eq;
+ char *name;
+
+ assert(h);
+ assert(data || size == 0);
+
+ if (memory_startswith(data, size, "_BOOT_ID="))
+ return 0;
+
+ eq = memchr(data, '=', MIN(size, JSON_THRESHOLD));
+ if (!eq)
+ return 0;
+
+ if (eq == data)
+ return 0;
+
+ name = strndupa(data, eq - (const char*) data);
+ if (output_fields && !set_get(output_fields, name))
+ return 0;
+
+ return update_json_data(h, flags, name, eq + 1, size - (eq - (const char*) data) - 1);
+}
+
static int output_json(
FILE *f,
sd_journal *j,
@@ -754,21 +842,23 @@ static int output_json(
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
- uint64_t realtime, monotonic;
+ char sid[SD_ID128_STRING_MAX], usecbuf[DECIMAL_STR_MAX(usec_t)];
+ _cleanup_(json_variant_unrefp) JsonVariant *object = NULL;
_cleanup_free_ char *cursor = NULL;
- const void *data;
- size_t length;
+ uint64_t realtime, monotonic;
+ JsonVariant **array = NULL;
+ struct json_data *d;
sd_id128_t boot_id;
- char sid[33], *k;
- int r;
Hashmap *h = NULL;
- bool done, separator;
+ size_t n = 0;
+ Iterator i;
+ int r;
assert(j);
- sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
+ (void) sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
r = sd_journal_get_realtime_usec(j, &realtime);
if (r < 0)
@@ -782,182 +872,106 @@ static int output_json(
if (r < 0)
return log_error_errno(r, "Failed to get cursor: %m");
- if (mode == OUTPUT_JSON_PRETTY)
- fprintf(f,
- "{\n"
- "\t\"__CURSOR\" : \"%s\",\n"
- "\t\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\",\n"
- "\t\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\",\n"
- "\t\"_BOOT_ID\" : \"%s\"",
- cursor,
- realtime,
- monotonic,
- sd_id128_to_string(boot_id, sid));
- else {
- if (mode == OUTPUT_JSON_SSE)
- fputs("data: ", f);
-
- fprintf(f,
- "{ \"__CURSOR\" : \"%s\", "
- "\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\", "
- "\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\", "
- "\"_BOOT_ID\" : \"%s\"",
- cursor,
- realtime,
- monotonic,
- sd_id128_to_string(boot_id, sid));
- }
-
h = hashmap_new(&string_hash_ops);
if (!h)
return log_oom();
- /* First round, iterate through the entry and count how often each field appears */
- JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
- const char *eq;
- char *n;
- unsigned u;
-
- if (memory_startswith(data, length, "_BOOT_ID="))
- continue;
-
- eq = memchr(data, '=', length);
- if (!eq)
- continue;
-
- n = memdup_suffix0(data, eq - (const char*) data);
- if (!n) {
- r = log_oom();
- goto finish;
- }
-
- u = PTR_TO_UINT(hashmap_get(h, n));
- if (u == 0) {
- r = hashmap_put(h, n, UINT_TO_PTR(1));
- if (r < 0) {
- free(n);
- log_oom();
- goto finish;
- }
- } else {
- r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
- free(n);
- if (r < 0) {
- log_oom();
- goto finish;
- }
- }
- }
- if (r == -EBADMSG) {
- log_debug_errno(r, "Skipping message we can't read: %m");
- return 0;
- }
+ r = update_json_data(h, flags, "__CURSOR", cursor, strlen(cursor));
if (r < 0)
- return r;
-
- separator = true;
- do {
- done = true;
-
- SD_JOURNAL_FOREACH_DATA(j, data, length) {
- const char *eq;
- char *kk;
- _cleanup_free_ char *n = NULL;
- size_t m;
- unsigned u;
-
- /* We already printed the boot id from the data in
- * the header, hence let's suppress it here */
- if (memory_startswith(data, length, "_BOOT_ID="))
- continue;
-
- eq = memchr(data, '=', length);
- if (!eq)
- continue;
-
- m = eq - (const char*) data;
- n = memdup_suffix0(data, m);
- if (!n) {
- r = log_oom();
- goto finish;
- }
-
- if (output_fields && !set_get(output_fields, n))
- continue;
-
- if (separator)
- fputs(mode == OUTPUT_JSON_PRETTY ? ",\n\t" : ", ", f);
-
- u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
- if (u == 0)
- /* We already printed this, let's jump to the next */
- separator = false;
-
- else if (u == 1) {
- /* Field only appears once, output it directly */
-
- json_escape(f, data, m, flags);
- fputs(" : ", f);
-
- json_escape(f, eq + 1, length - m - 1, flags);
-
- hashmap_remove(h, n);
- free(kk);
+ goto finish;
- separator = true;
+ xsprintf(usecbuf, USEC_FMT, realtime);
+ r = update_json_data(h, flags, "__REALTIME_TIMESTAMP", usecbuf, strlen(usecbuf));
+ if (r < 0)
+ goto finish;
- } else {
- /* Field appears multiple times, output it as array */
- json_escape(f, data, m, flags);
- fputs(" : [ ", f);
- json_escape(f, eq + 1, length - m - 1, flags);
+ xsprintf(usecbuf, USEC_FMT, monotonic);
+ r = update_json_data(h, flags, "__MONOTONIC_TIMESTAMP", usecbuf, strlen(usecbuf));
+ if (r < 0)
+ goto finish;
- /* Iterate through the end of the list */
+ sd_id128_to_string(boot_id, sid);
+ r = update_json_data(h, flags, "_BOOT_ID", sid, strlen(sid));
+ if (r < 0)
+ goto finish;
- while (sd_journal_enumerate_data(j, &data, &length) > 0) {
- if (length < m + 1)
- continue;
+ for (;;) {
+ const void *data;
+ size_t size;
- if (memcmp(data, n, m) != 0)
- continue;
+ r = sd_journal_enumerate_data(j, &data, &size);
+ if (r == -EBADMSG) {
+ log_debug_errno(r, "Skipping message we can't read: %m");
+ r = 0;
+ goto finish;
+ }
+ if (r < 0) {
+ log_error_errno(r, "Failed to read journal: %m");
+ goto finish;
+ }
+ if (r == 0)
+ break;
- if (((const char*) data)[m] != '=')
- continue;
+ r = update_json_data_split(h, flags, output_fields, data, size);
+ if (r < 0)
+ goto finish;
+ }
- fputs(", ", f);
- json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
- }
+ array = new(JsonVariant*, hashmap_size(h)*2);
+ if (!array) {
+ r = log_oom();
+ goto finish;
+ }
- fputs(" ]", f);
+ HASHMAP_FOREACH(d, h, i) {
+ assert(d->n_values > 0);
- hashmap_remove(h, n);
- free(kk);
+ array[n++] = json_variant_ref(d->name);
- /* Iterate data fields form the beginning */
- done = false;
- separator = true;
+ if (d->n_values == 1)
+ array[n++] = json_variant_ref(d->values[0]);
+ else {
+ _cleanup_(json_variant_unrefp) JsonVariant *q = NULL;
- break;
+ r = json_variant_new_array(&q, d->values, d->n_values);
+ if (r < 0) {
+ log_error_errno(r, "Failed to create JSON array: %m");
+ goto finish;
}
+
+ array[n++] = TAKE_PTR(q);
}
+ }
- } while (!done);
+ r = json_variant_new_object(&object, array, n);
+ if (r < 0) {
+ log_error_errno(r, "Failed to allocate JSON object: %m");
+ goto finish;
+ }
- if (mode == OUTPUT_JSON_PRETTY)
- fputs("\n}\n", f);
- else if (mode == OUTPUT_JSON_SSE)
- fputs("}\n\n", f);
- else
- fputs(" }\n", f);
+ json_variant_dump(object,
+ output_mode_to_json_format_flags(mode) |
+ (FLAGS_SET(flags, OUTPUT_COLOR) ? JSON_FORMAT_COLOR : 0),
+ f, NULL);
r = 0;
finish:
- while ((k = hashmap_steal_first_key(h)))
- free(k);
+ while ((d = hashmap_steal_first(h))) {
+ size_t k;
+
+ json_variant_unref(d->name);
+ for (k = 0; k < d->n_values; k++)
+ json_variant_unref(d->values[k]);
+
+ free(d);
+ }
hashmap_free(h);
+ json_variant_unref_many(array, n);
+ free(array);
+
return r;
}
@@ -968,7 +982,7 @@ static int output_cat(
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
const void *data;
size_t l;
@@ -1018,12 +1032,12 @@ static int output_cat(
static int (*output_funcs[_OUTPUT_MODE_MAX])(
FILE *f,
- sd_journal*j,
+ sd_journal *j,
OutputMode mode,
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) = {
+ const size_t highlight[2]) = {
[OUTPUT_SHORT] = output_short,
[OUTPUT_SHORT_ISO] = output_short,
@@ -1037,6 +1051,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
[OUTPUT_JSON] = output_json,
[OUTPUT_JSON_PRETTY] = output_json,
[OUTPUT_JSON_SSE] = output_json,
+ [OUTPUT_JSON_SEQ] = output_json,
[OUTPUT_CAT] = output_cat,
[OUTPUT_WITH_UNIT] = output_short,
};
@@ -1048,7 +1063,7 @@ int show_journal_entry(
unsigned n_columns,
OutputFlags flags,
char **output_fields,
- size_t highlight[2],
+ const size_t highlight[2],
bool *ellipsized) {
int ret;
@@ -1321,7 +1336,8 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
- r = safe_fork("(sd-bootid)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
+ pidnsfd, mntnsfd, -1, -1, rootfd, &child);
if (r < 0)
return r;
if (r == 0) {
@@ -1329,10 +1345,6 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
pair[0] = safe_close(pair[0]);
- r = namespace_enter(pidnsfd, mntnsfd, -1, -1, rootfd);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
_exit(EXIT_FAILURE);
@@ -1351,7 +1363,7 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate_and_check("(sd-bootid)", child, 0);
+ r = wait_for_terminate_and_check("(sd-bootidns)", child, 0);
if (r < 0)
return r;
if (r != EXIT_SUCCESS)