]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/logs-show.c
logind: also escape external data when saving to /run
[thirdparty/systemd.git] / src / shared / logs-show.c
CommitLineData
86aa7ba4
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
86aa7ba4
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
86aa7ba4 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
86aa7ba4
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <time.h>
23#include <assert.h>
24#include <errno.h>
df50185b 25#include <sys/poll.h>
2a410422 26#include <sys/socket.h>
55d7bfc1 27#include <string.h>
b6741478 28#include <fcntl.h>
86aa7ba4
LP
29
30#include "logs-show.h"
31#include "log.h"
32#include "util.h"
ba961854 33#include "utf8.h"
d99ae53a 34#include "hashmap.h"
b6741478 35#include "fileio.h"
dfb33a97 36#include "journal-internal.h"
86aa7ba4 37
a6f0104a
ZJS
38/* up to three lines (each up to 100 characters),
39 or 300 characters, whichever is less */
40#define PRINT_LINE_THRESHOLD 3
41#define PRINT_CHAR_THRESHOLD 300
42
08ace05b 43#define JSON_THRESHOLD 4096
86aa7ba4 44
d4205751
LP
45static int print_catalog(FILE *f, sd_journal *j) {
46 int r;
47 _cleanup_free_ char *t = NULL, *z = NULL;
48
49
50 r = sd_journal_get_catalog(j, &t);
51 if (r < 0)
52 return r;
53
54 z = strreplace(strstrip(t), "\n", "\n-- ");
55 if (!z)
56 return log_oom();
57
58 fputs("-- ", f);
59 fputs(z, f);
60 fputc('\n', f);
61
62 return 0;
63}
64
55d7bfc1
LP
65static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
66 size_t fl, nl;
67 void *buf;
68
69 assert(data);
70 assert(field);
71 assert(target);
72 assert(target_size);
73
74 fl = strlen(field);
75 if (length < fl)
76 return 0;
77
78 if (memcmp(data, field, fl))
79 return 0;
80
81 nl = length - fl;
bf967366 82 buf = malloc(nl+1);
0d0f0c50
SL
83 if (!buf)
84 return log_oom();
55d7bfc1 85
46b0d922
LP
86 memcpy(buf, (const char*) data + fl, nl);
87 ((char*)buf)[nl] = 0;
88
6c1e6b98 89 free(*target);
55d7bfc1
LP
90 *target = buf;
91 *target_size = nl;
92
93 return 1;
94}
95
08ace05b
LP
96static bool shall_print(const char *p, size_t l, OutputFlags flags) {
97 assert(p);
98
99 if (flags & OUTPUT_SHOW_ALL)
55d7bfc1
LP
100 return true;
101
a6f0104a 102 if (l >= PRINT_CHAR_THRESHOLD)
55d7bfc1
LP
103 return false;
104
31f7bf19 105 if (!utf8_is_printable(p, l))
55d7bfc1
LP
106 return false;
107
108 return true;
109}
110
00f117a5 111static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputFlags flags, int priority, const char* message, size_t message_len) {
31f7bf19
ZJS
112 const char *color_on = "", *color_off = "";
113 const char *pos, *end;
94e0bd7d 114 bool ellipsized = false;
a6f0104a 115 int line = 0;
31f7bf19
ZJS
116
117 if (flags & OUTPUT_COLOR) {
118 if (priority <= LOG_ERR) {
119 color_on = ANSI_HIGHLIGHT_RED_ON;
120 color_off = ANSI_HIGHLIGHT_OFF;
121 } else if (priority <= LOG_NOTICE) {
122 color_on = ANSI_HIGHLIGHT_ON;
123 color_off = ANSI_HIGHLIGHT_OFF;
124 }
125 }
126
47d80904
UU
127 /* A special case: make sure that we print a newline when
128 the message is empty. */
129 if (message_len == 0)
130 fputs("\n", f);
131
a6f0104a
ZJS
132 for (pos = message;
133 pos < message + message_len;
134 pos = end + 1, line++) {
135 bool continuation = line > 0;
136 bool tail_line;
31f7bf19
ZJS
137 int len;
138 for (end = pos; end < message + message_len && *end != '\n'; end++)
139 ;
140 len = end - pos;
141 assert(len >= 0);
142
2526d626 143 /* We need to figure out when we are showing not-last line, *and*
a6f0104a
ZJS
144 * will skip subsequent lines. In that case, we will put the dots
145 * at the end of the line, instead of putting dots in the middle
146 * or not at all.
147 */
148 tail_line =
149 line + 1 == PRINT_LINE_THRESHOLD ||
2526d626 150 end + 1 >= message + PRINT_CHAR_THRESHOLD;
a6f0104a
ZJS
151
152 if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) ||
153 (prefix + len + 1 < n_columns && !tail_line)) {
31f7bf19
ZJS
154 fprintf(f, "%*s%s%.*s%s\n",
155 continuation * prefix, "",
156 color_on, len, pos, color_off);
a6f0104a
ZJS
157 continue;
158 }
31f7bf19 159
a6f0104a
ZJS
160 /* Beyond this point, ellipsization will happen. */
161 ellipsized = true;
31f7bf19 162
a6f0104a
ZJS
163 if (prefix < n_columns && n_columns - prefix >= 3) {
164 if (n_columns - prefix > (unsigned) len + 3)
165 fprintf(f, "%*s%s%.*s...%s\n",
b4b02cbe
ZJS
166 continuation * prefix, "",
167 color_on, len, pos, color_off);
a6f0104a
ZJS
168 else {
169 _cleanup_free_ char *e;
170
171 e = ellipsize_mem(pos, len, n_columns - prefix,
172 tail_line ? 100 : 90);
173 if (!e)
174 fprintf(f, "%*s%s%.*s%s\n",
175 continuation * prefix, "",
176 color_on, len, pos, color_off);
177 else
178 fprintf(f, "%*s%s%s%s\n",
179 continuation * prefix, "",
180 color_on, e, color_off);
181 }
182 } else
31f7bf19
ZJS
183 fputs("...\n", f);
184
a6f0104a
ZJS
185 if (tail_line)
186 break;
31f7bf19 187 }
94e0bd7d
ZJS
188
189 return ellipsized;
31f7bf19
ZJS
190}
191
08ace05b
LP
192static int output_short(
193 FILE *f,
194 sd_journal *j,
195 OutputMode mode,
196 unsigned n_columns,
197 OutputFlags flags) {
198
86aa7ba4 199 int r;
86aa7ba4
LP
200 const void *data;
201 size_t length;
202 size_t n = 0;
08ace05b 203 _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
49826187
LP
204 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;
205 int p = LOG_INFO;
94e0bd7d 206 bool ellipsized = false;
86aa7ba4 207
08ace05b 208 assert(f);
86aa7ba4
LP
209 assert(j);
210
a6f0104a 211 /* Set the threshold to one bigger than the actual print
69ab8088 212 * threshold, so that if the line is actually longer than what
a6f0104a
ZJS
213 * we're willing to print, ellipsization will occur. This way
214 * we won't output a misleading line without any indication of
215 * truncation.
216 */
217 sd_journal_set_data_threshold(j, flags & (OUTPUT_SHOW_ALL|OUTPUT_FULL_WIDTH) ? 0 : PRINT_CHAR_THRESHOLD + 1);
93b73b06 218
a72b6353 219 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
55d7bfc1 220
49826187
LP
221 r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
222 if (r < 0)
08ace05b 223 return r;
49826187
LP
224 else if (r > 0)
225 continue;
226
55d7bfc1
LP
227 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
228 if (r < 0)
08ace05b 229 return r;
55d7bfc1
LP
230 else if (r > 0)
231 continue;
232
4cd9a9d9 233 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
55d7bfc1 234 if (r < 0)
08ace05b 235 return r;
55d7bfc1
LP
236 else if (r > 0)
237 continue;
238
239 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
240 if (r < 0)
08ace05b 241 return r;
55d7bfc1
LP
242 else if (r > 0)
243 continue;
244
245 r = parse_field(data, length, "_PID=", &pid, &pid_len);
246 if (r < 0)
08ace05b 247 return r;
55d7bfc1
LP
248 else if (r > 0)
249 continue;
250
6c1e6b98
LP
251 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
252 if (r < 0)
08ace05b 253 return r;
6c1e6b98
LP
254 else if (r > 0)
255 continue;
256
bf967366
LP
257 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
258 if (r < 0)
08ace05b 259 return r;
bf967366
LP
260 else if (r > 0)
261 continue;
262
263 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
264 if (r < 0)
08ace05b 265 return r;
bf967366
LP
266 else if (r > 0)
267 continue;
268
55d7bfc1
LP
269 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
270 if (r < 0)
08ace05b 271 return r;
55d7bfc1
LP
272 }
273
a72b6353
ZJS
274 if (r < 0)
275 return r;
276
08ace05b
LP
277 if (!message)
278 return 0;
55d7bfc1 279
e8bc0ea2
LP
280 if (!(flags & OUTPUT_SHOW_ALL))
281 strip_tab_ansi(&message, &message_len);
282
49826187
LP
283 if (priority_len == 1 && *priority >= '0' && *priority <= '7')
284 p = *priority - '0';
285
a6e87e90 286 if (mode == OUTPUT_SHORT_MONOTONIC) {
67a12205 287 uint64_t t;
3ebcdf8c
LP
288 sd_id128_t boot_id;
289
bf967366
LP
290 r = -ENOENT;
291
292 if (monotonic)
293 r = safe_atou64(monotonic, &t);
294
295 if (r < 0)
3ebcdf8c 296 r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
86aa7ba4 297
3ebcdf8c 298 if (r < 0) {
a72b6353 299 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
08ace05b 300 return r;
67a12205
LP
301 }
302
08ace05b 303 fprintf(f, "[%5llu.%06llu]",
de0671ee
ZJS
304 t / USEC_PER_SEC,
305 t % USEC_PER_SEC);
3ebcdf8c
LP
306
307 n += 1 + 5 + 1 + 6 + 1;
308
67a12205
LP
309 } else {
310 char buf[64];
bf967366 311 uint64_t x;
67a12205
LP
312 time_t t;
313 struct tm tm;
731a676c 314
bf967366
LP
315 r = -ENOENT;
316
317 if (realtime)
318 r = safe_atou64(realtime, &x);
319
320 if (r < 0)
321 r = sd_journal_get_realtime_usec(j, &x);
67a12205 322
67a12205 323 if (r < 0) {
a72b6353 324 log_error("Failed to get realtime timestamp: %s", strerror(-r));
08ace05b 325 return r;
67a12205
LP
326 }
327
bf967366 328 t = (time_t) (x / USEC_PER_SEC);
f02d8367
ZJS
329
330 switch(mode) {
331 case OUTPUT_SHORT_ISO:
44bc6e1f 332 r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", localtime_r(&t, &tm));
f02d8367
ZJS
333 break;
334 case OUTPUT_SHORT_PRECISE:
335 r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm));
336 if (r > 0) {
337 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
338 ".%06llu", x % USEC_PER_SEC);
339 }
340 break;
341 default:
44bc6e1f 342 r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm));
f02d8367 343 }
44bc6e1f
TT
344
345 if (r <= 0) {
67a12205 346 log_error("Failed to format time.");
44bc6e1f 347 return -EINVAL;
67a12205
LP
348 }
349
08ace05b 350 fputs(buf, f);
67a12205
LP
351 n += strlen(buf);
352 }
86aa7ba4 353
08ace05b
LP
354 if (hostname && shall_print(hostname, hostname_len, flags)) {
355 fprintf(f, " %.*s", (int) hostname_len, hostname);
55d7bfc1
LP
356 n += hostname_len + 1;
357 }
358
08ace05b
LP
359 if (identifier && shall_print(identifier, identifier_len, flags)) {
360 fprintf(f, " %.*s", (int) identifier_len, identifier);
4cd9a9d9 361 n += identifier_len + 1;
08ace05b
LP
362 } else if (comm && shall_print(comm, comm_len, flags)) {
363 fprintf(f, " %.*s", (int) comm_len, comm);
55d7bfc1 364 n += comm_len + 1;
b5936820 365 } else
08ace05b 366 fputc(' ', f);
86aa7ba4 367
08ace05b
LP
368 if (pid && shall_print(pid, pid_len, flags)) {
369 fprintf(f, "[%.*s]", (int) pid_len, pid);
55d7bfc1 370 n += pid_len + 2;
08ace05b
LP
371 } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
372 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
6c1e6b98 373 n += fake_pid_len + 2;
86aa7ba4
LP
374 }
375
31f7bf19 376 if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
e6acda19 377 char bytes[FORMAT_BYTES_MAX];
08ace05b 378 fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
31f7bf19
ZJS
379 } else {
380 fputs(": ", f);
94e0bd7d
ZJS
381 ellipsized |=
382 print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
31f7bf19 383 }
55d7bfc1 384
d4205751
LP
385 if (flags & OUTPUT_CATALOG)
386 print_catalog(f, j);
387
94e0bd7d 388 return ellipsized;
86aa7ba4
LP
389}
390
08ace05b
LP
391static int output_verbose(
392 FILE *f,
393 sd_journal *j,
394 OutputMode mode,
395 unsigned n_columns,
396 OutputFlags flags) {
397
86aa7ba4
LP
398 const void *data;
399 size_t length;
7fd1b19b 400 _cleanup_free_ char *cursor = NULL;
86aa7ba4 401 uint64_t realtime;
f02d8367 402 char ts[FORMAT_TIMESTAMP_MAX + 7];
86aa7ba4
LP
403 int r;
404
08ace05b 405 assert(f);
86aa7ba4
LP
406 assert(j);
407
93b73b06
LP
408 sd_journal_set_data_threshold(j, 0);
409
cf40f0be
ZJS
410 r = sd_journal_get_data(j, "_SOURCE_REALTIME_TIMESTAMP", &data, &length);
411 if (r == -ENOENT)
412 log_debug("Source realtime timestamp not found");
413 else if (r < 0) {
a72b6353 414 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
cf40f0be 415 "Failed to get source realtime timestamp: %s", strerror(-r));
86aa7ba4 416 return r;
cf40f0be
ZJS
417 } else {
418 _cleanup_free_ char *value = NULL;
419 size_t size;
420
421 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &value, &size);
422 if (r < 0)
423 log_debug("_SOURCE_REALTIME_TIMESTAMP invalid: %s", strerror(-r));
424 else {
425 r = safe_atou64(value, &realtime);
426 if (r < 0)
427 log_debug("Failed to parse realtime timestamp: %s",
428 strerror(-r));
429 }
430 }
431
432 if (r < 0) {
433 r = sd_journal_get_realtime_usec(j, &realtime);
434 if (r < 0) {
435 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
436 "Failed to get realtime timestamp: %s", strerror(-r));
437 return r;
438 }
86aa7ba4
LP
439 }
440
441 r = sd_journal_get_cursor(j, &cursor);
442 if (r < 0) {
443 log_error("Failed to get cursor: %s", strerror(-r));
444 return r;
445 }
446
08ace05b 447 fprintf(f, "%s [%s]\n",
f02d8367 448 format_timestamp_us(ts, sizeof(ts), realtime),
08ace05b 449 cursor);
86aa7ba4 450
a72b6353 451 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
31f7bf19
ZJS
452 const char *c;
453 int fieldlen;
7ac4fa7e
ZJS
454 const char *on = "", *off = "";
455
31f7bf19
ZJS
456 c = memchr(data, '=', length);
457 if (!c) {
458 log_error("Invalid field.");
459 return -EINVAL;
460 }
461 fieldlen = c - (const char*) data;
86aa7ba4 462
7ac4fa7e
ZJS
463 if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) {
464 on = ANSI_HIGHLIGHT_ON;
465 off = ANSI_HIGHLIGHT_OFF;
466 }
467
468 if (flags & OUTPUT_SHOW_ALL ||
a6f0104a
ZJS
469 (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
470 && utf8_is_printable(data, length))) {
7ac4fa7e 471 fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data);
31f7bf19 472 print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
7ac4fa7e 473 fputs(off, f);
31f7bf19
ZJS
474 } else {
475 char bytes[FORMAT_BYTES_MAX];
86aa7ba4 476
7ac4fa7e
ZJS
477 fprintf(f, " %s%.*s=[%s blob data]%s\n",
478 on,
31f7bf19
ZJS
479 (int) (c - (const char*) data),
480 (const char*) data,
7ac4fa7e
ZJS
481 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1),
482 off);
31f7bf19 483 }
86aa7ba4
LP
484 }
485
a72b6353
ZJS
486 if (r < 0)
487 return r;
488
d4205751
LP
489 if (flags & OUTPUT_CATALOG)
490 print_catalog(f, j);
491
86aa7ba4
LP
492 return 0;
493}
494
08ace05b
LP
495static int output_export(
496 FILE *f,
497 sd_journal *j,
498 OutputMode mode,
499 unsigned n_columns,
500 OutputFlags flags) {
501
86aa7ba4
LP
502 sd_id128_t boot_id;
503 char sid[33];
504 int r;
505 usec_t realtime, monotonic;
7fd1b19b 506 _cleanup_free_ char *cursor = NULL;
86aa7ba4
LP
507 const void *data;
508 size_t length;
509
510 assert(j);
511
93b73b06
LP
512 sd_journal_set_data_threshold(j, 0);
513
86aa7ba4
LP
514 r = sd_journal_get_realtime_usec(j, &realtime);
515 if (r < 0) {
516 log_error("Failed to get realtime timestamp: %s", strerror(-r));
517 return r;
518 }
519
520 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
521 if (r < 0) {
522 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
523 return r;
524 }
525
526 r = sd_journal_get_cursor(j, &cursor);
527 if (r < 0) {
528 log_error("Failed to get cursor: %s", strerror(-r));
529 return r;
530 }
531
08ace05b
LP
532 fprintf(f,
533 "__CURSOR=%s\n"
de0671ee
ZJS
534 "__REALTIME_TIMESTAMP="USEC_FMT"\n"
535 "__MONOTONIC_TIMESTAMP="USEC_FMT"\n"
08ace05b
LP
536 "_BOOT_ID=%s\n",
537 cursor,
de0671ee
ZJS
538 realtime,
539 monotonic,
08ace05b 540 sd_id128_to_string(boot_id, sid));
86aa7ba4 541
a72b6353 542 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
86aa7ba4 543
112301ae
LP
544 /* We already printed the boot id, from the data in
545 * the header, hence let's suppress it here */
546 if (length >= 9 &&
2a0e0692 547 startswith(data, "_BOOT_ID="))
112301ae
LP
548 continue;
549
0ade5ffe
ZJS
550 if (utf8_is_printable_newline(data, length, false))
551 fwrite(data, length, 1, f);
552 else {
86aa7ba4
LP
553 const char *c;
554 uint64_t le64;
555
556 c = memchr(data, '=', length);
557 if (!c) {
558 log_error("Invalid field.");
559 return -EINVAL;
560 }
561
08ace05b
LP
562 fwrite(data, c - (const char*) data, 1, f);
563 fputc('\n', f);
86aa7ba4 564 le64 = htole64(length - (c - (const char*) data) - 1);
08ace05b
LP
565 fwrite(&le64, sizeof(le64), 1, f);
566 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
0ade5ffe 567 }
86aa7ba4 568
08ace05b 569 fputc('\n', f);
86aa7ba4
LP
570 }
571
a72b6353
ZJS
572 if (r < 0)
573 return r;
574
08ace05b 575 fputc('\n', f);
86aa7ba4
LP
576
577 return 0;
578}
579
240a5fe8 580void json_escape(
08ace05b
LP
581 FILE *f,
582 const char* p,
583 size_t l,
584 OutputFlags flags) {
585
586 assert(f);
587 assert(p);
588
93b73b06 589 if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
08ace05b
LP
590
591 fputs("null", f);
592
31f7bf19 593 else if (!utf8_is_printable(p, l)) {
86aa7ba4
LP
594 bool not_first = false;
595
08ace05b 596 fputs("[ ", f);
86aa7ba4
LP
597
598 while (l > 0) {
599 if (not_first)
08ace05b 600 fprintf(f, ", %u", (uint8_t) *p);
86aa7ba4
LP
601 else {
602 not_first = true;
08ace05b 603 fprintf(f, "%u", (uint8_t) *p);
86aa7ba4
LP
604 }
605
606 p++;
607 l--;
608 }
609
08ace05b 610 fputs(" ]", f);
86aa7ba4 611 } else {
08ace05b 612 fputc('\"', f);
86aa7ba4
LP
613
614 while (l > 0) {
615 if (*p == '"' || *p == '\\') {
08ace05b
LP
616 fputc('\\', f);
617 fputc(*p, f);
31f7bf19
ZJS
618 } else if (*p == '\n')
619 fputs("\\n", f);
620 else if (*p < ' ')
08ace05b
LP
621 fprintf(f, "\\u%04x", *p);
622 else
623 fputc(*p, f);
86aa7ba4
LP
624
625 p++;
626 l--;
627 }
628
08ace05b 629 fputc('\"', f);
86aa7ba4
LP
630 }
631}
632
08ace05b
LP
633static int output_json(
634 FILE *f,
635 sd_journal *j,
636 OutputMode mode,
637 unsigned n_columns,
638 OutputFlags flags) {
639
86aa7ba4 640 uint64_t realtime, monotonic;
7fd1b19b 641 _cleanup_free_ char *cursor = NULL;
86aa7ba4
LP
642 const void *data;
643 size_t length;
644 sd_id128_t boot_id;
2e729834 645 char sid[33], *k;
86aa7ba4 646 int r;
d99ae53a
LP
647 Hashmap *h = NULL;
648 bool done, separator;
86aa7ba4
LP
649
650 assert(j);
651
93b73b06
LP
652 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
653
86aa7ba4
LP
654 r = sd_journal_get_realtime_usec(j, &realtime);
655 if (r < 0) {
656 log_error("Failed to get realtime timestamp: %s", strerror(-r));
657 return r;
658 }
659
660 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
661 if (r < 0) {
662 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
663 return r;
664 }
665
666 r = sd_journal_get_cursor(j, &cursor);
667 if (r < 0) {
668 log_error("Failed to get cursor: %s", strerror(-r));
669 return r;
670 }
671
a6e87e90 672 if (mode == OUTPUT_JSON_PRETTY)
08ace05b
LP
673 fprintf(f,
674 "{\n"
675 "\t\"__CURSOR\" : \"%s\",\n"
de0671ee
ZJS
676 "\t\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\",\n"
677 "\t\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\",\n"
08ace05b
LP
678 "\t\"_BOOT_ID\" : \"%s\"",
679 cursor,
de0671ee
ZJS
680 realtime,
681 monotonic,
08ace05b 682 sd_id128_to_string(boot_id, sid));
48383c25
LP
683 else {
684 if (mode == OUTPUT_JSON_SSE)
685 fputs("data: ", f);
686
08ace05b
LP
687 fprintf(f,
688 "{ \"__CURSOR\" : \"%s\", "
de0671ee
ZJS
689 "\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\", "
690 "\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\", "
08ace05b
LP
691 "\"_BOOT_ID\" : \"%s\"",
692 cursor,
de0671ee
ZJS
693 realtime,
694 monotonic,
08ace05b 695 sd_id128_to_string(boot_id, sid));
48383c25 696 }
86aa7ba4 697
d99ae53a
LP
698 h = hashmap_new(string_hash_func, string_compare_func);
699 if (!h)
700 return -ENOMEM;
701
702 /* First round, iterate through the entry and count how often each field appears */
a72b6353 703 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
d99ae53a
LP
704 const char *eq;
705 char *n;
706 unsigned u;
86aa7ba4 707
112301ae
LP
708 if (length >= 9 &&
709 memcmp(data, "_BOOT_ID=", 9) == 0)
710 continue;
711
d99ae53a
LP
712 eq = memchr(data, '=', length);
713 if (!eq)
714 continue;
86aa7ba4 715
d99ae53a
LP
716 n = strndup(data, eq - (const char*) data);
717 if (!n) {
718 r = -ENOMEM;
719 goto finish;
720 }
a6e87e90 721
d99ae53a
LP
722 u = PTR_TO_UINT(hashmap_get(h, n));
723 if (u == 0) {
724 r = hashmap_put(h, n, UINT_TO_PTR(1));
725 if (r < 0) {
726 free(n);
727 goto finish;
728 }
729 } else {
730 r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
731 free(n);
732 if (r < 0)
733 goto finish;
734 }
86aa7ba4
LP
735 }
736
a72b6353
ZJS
737 if (r < 0)
738 return r;
739
d99ae53a
LP
740 separator = true;
741 do {
742 done = true;
743
744 SD_JOURNAL_FOREACH_DATA(j, data, length) {
745 const char *eq;
746 char *kk, *n;
747 size_t m;
748 unsigned u;
749
750 /* We already printed the boot id, from the data in
751 * the header, hence let's suppress it here */
752 if (length >= 9 &&
753 memcmp(data, "_BOOT_ID=", 9) == 0)
754 continue;
755
756 eq = memchr(data, '=', length);
757 if (!eq)
758 continue;
759
760 if (separator) {
761 if (mode == OUTPUT_JSON_PRETTY)
762 fputs(",\n\t", f);
763 else
764 fputs(", ", f);
765 }
766
767 m = eq - (const char*) data;
768
769 n = strndup(data, m);
770 if (!n) {
771 r = -ENOMEM;
772 goto finish;
773 }
774
775 u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
776 if (u == 0) {
777 /* We already printed this, let's jump to the next */
778 free(n);
779 separator = false;
780
781 continue;
782 } else if (u == 1) {
783 /* Field only appears once, output it directly */
784
785 json_escape(f, data, m, flags);
786 fputs(" : ", f);
787
788 json_escape(f, eq + 1, length - m - 1, flags);
789
790 hashmap_remove(h, n);
791 free(kk);
792 free(n);
793
794 separator = true;
795
796 continue;
797
798 } else {
799 /* Field appears multiple times, output it as array */
800 json_escape(f, data, m, flags);
801 fputs(" : [ ", f);
802 json_escape(f, eq + 1, length - m - 1, flags);
803
804 /* Iterate through the end of the list */
805
806 while (sd_journal_enumerate_data(j, &data, &length) > 0) {
807 if (length < m + 1)
808 continue;
809
810 if (memcmp(data, n, m) != 0)
811 continue;
812
813 if (((const char*) data)[m] != '=')
814 continue;
815
816 fputs(", ", f);
817 json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
818 }
819
820 fputs(" ]", f);
821
822 hashmap_remove(h, n);
823 free(kk);
824 free(n);
825
826 /* Iterate data fields form the beginning */
827 done = false;
828 separator = true;
829
830 break;
831 }
832 }
833
834 } while (!done);
835
a6e87e90 836 if (mode == OUTPUT_JSON_PRETTY)
08ace05b 837 fputs("\n}\n", f);
48383c25
LP
838 else if (mode == OUTPUT_JSON_SSE)
839 fputs("}\n\n", f);
a6e87e90 840 else
08ace05b 841 fputs(" }\n", f);
86aa7ba4 842
d99ae53a
LP
843 r = 0;
844
845finish:
846 while ((k = hashmap_steal_first_key(h)))
847 free(k);
848
849 hashmap_free(h);
850
851 return r;
86aa7ba4
LP
852}
853
08ace05b
LP
854static int output_cat(
855 FILE *f,
856 sd_journal *j,
857 OutputMode mode,
858 unsigned n_columns,
859 OutputFlags flags) {
860
d3f2bdbf
LP
861 const void *data;
862 size_t l;
863 int r;
864
865 assert(j);
08ace05b 866 assert(f);
d3f2bdbf 867
93b73b06
LP
868 sd_journal_set_data_threshold(j, 0);
869
d3f2bdbf
LP
870 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
871 if (r < 0) {
c198300f
LP
872 /* An entry without MESSAGE=? */
873 if (r == -ENOENT)
874 return 0;
875
d3f2bdbf
LP
876 log_error("Failed to get data: %s", strerror(-r));
877 return r;
878 }
879
880 assert(l >= 8);
881
08ace05b
LP
882 fwrite((const char*) data + 8, 1, l - 8, f);
883 fputc('\n', f);
d3f2bdbf
LP
884
885 return 0;
886}
887
08ace05b
LP
888static int (*output_funcs[_OUTPUT_MODE_MAX])(
889 FILE *f,
890 sd_journal*j,
891 OutputMode mode,
892 unsigned n_columns,
893 OutputFlags flags) = {
894
a6e87e90 895 [OUTPUT_SHORT] = output_short,
44bc6e1f 896 [OUTPUT_SHORT_ISO] = output_short,
f02d8367
ZJS
897 [OUTPUT_SHORT_PRECISE] = output_short,
898 [OUTPUT_SHORT_MONOTONIC] = output_short,
86aa7ba4
LP
899 [OUTPUT_VERBOSE] = output_verbose,
900 [OUTPUT_EXPORT] = output_export,
d3f2bdbf 901 [OUTPUT_JSON] = output_json,
a6e87e90 902 [OUTPUT_JSON_PRETTY] = output_json,
48383c25 903 [OUTPUT_JSON_SSE] = output_json,
d3f2bdbf 904 [OUTPUT_CAT] = output_cat
86aa7ba4
LP
905};
906
08ace05b
LP
907int output_journal(
908 FILE *f,
909 sd_journal *j,
910 OutputMode mode,
911 unsigned n_columns,
94e0bd7d
ZJS
912 OutputFlags flags,
913 bool *ellipsized) {
08ace05b 914
e268b81e 915 int ret;
df50185b 916 assert(mode >= 0);
86aa7ba4
LP
917 assert(mode < _OUTPUT_MODE_MAX);
918
34a35ece
LP
919 if (n_columns <= 0)
920 n_columns = columns();
921
08ace05b 922 ret = output_funcs[mode](f, j, mode, n_columns, flags);
e268b81e 923 fflush(stdout);
94e0bd7d
ZJS
924
925 if (ellipsized && ret > 0)
926 *ellipsized = true;
927
e268b81e 928 return ret;
86aa7ba4
LP
929}
930
ea6c2dd1
LP
931static int maybe_print_begin_newline(FILE *f, OutputFlags *flags) {
932 assert(f);
933 assert(flags);
934
935 if (!(*flags & OUTPUT_BEGIN_NEWLINE))
936 return 0;
937
938 /* Print a beginning new line if that's request, but only once
939 * on the first line we print. */
940
941 fputc('\n', f);
942 *flags &= ~OUTPUT_BEGIN_NEWLINE;
943 return 0;
944}
945
1a6c43e9
MT
946static int show_journal(FILE *f,
947 sd_journal *j,
948 OutputMode mode,
949 unsigned n_columns,
950 usec_t not_before,
951 unsigned how_many,
94e0bd7d
ZJS
952 OutputFlags flags,
953 bool *ellipsized) {
86aa7ba4 954
86aa7ba4 955 int r;
df50185b
LP
956 unsigned line = 0;
957 bool need_seek = false;
085d7120 958 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
86aa7ba4 959
1a6c43e9 960 assert(j);
df50185b
LP
961 assert(mode >= 0);
962 assert(mode < _OUTPUT_MODE_MAX);
1946b0bd
LP
963
964 /* Seek to end */
86aa7ba4
LP
965 r = sd_journal_seek_tail(j);
966 if (r < 0)
967 goto finish;
968
df50185b
LP
969 r = sd_journal_previous_skip(j, how_many);
970 if (r < 0)
971 goto finish;
86aa7ba4 972
df50185b
LP
973 for (;;) {
974 for (;;) {
975 usec_t usec;
976
977 if (need_seek) {
978 r = sd_journal_next(j);
979 if (r < 0)
980 goto finish;
981 }
982
983 if (r == 0)
984 break;
86aa7ba4 985
df50185b
LP
986 need_seek = true;
987
988 if (not_before > 0) {
989 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
990
991 /* -ESTALE is returned if the
992 timestamp is not from this boot */
993 if (r == -ESTALE)
994 continue;
995 else if (r < 0)
996 goto finish;
997
998 if (usec < not_before)
999 continue;
1000 }
1001
1002 line ++;
ea6c2dd1 1003 maybe_print_begin_newline(f, &flags);
df50185b 1004
94e0bd7d 1005 r = output_journal(f, j, mode, n_columns, flags, ellipsized);
df50185b
LP
1006 if (r < 0)
1007 goto finish;
1008 }
1009
08984293
LP
1010 if (warn_cutoff && line < how_many && not_before > 0) {
1011 sd_id128_t boot_id;
1012 usec_t cutoff;
1013
1014 /* Check whether the cutoff line is too early */
1015
1016 r = sd_id128_get_boot(&boot_id);
1017 if (r < 0)
1018 goto finish;
1019
1020 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
1021 if (r < 0)
1022 goto finish;
1023
ea6c2dd1
LP
1024 if (r > 0 && not_before < cutoff) {
1025 maybe_print_begin_newline(f, &flags);
08ace05b 1026 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
ea6c2dd1 1027 }
08984293
LP
1028
1029 warn_cutoff = false;
1030 }
1031
085d7120 1032 if (!(flags & OUTPUT_FOLLOW))
86aa7ba4
LP
1033 break;
1034
e02d1cf7 1035 r = sd_journal_wait(j, (usec_t) -1);
df50185b
LP
1036 if (r < 0)
1037 goto finish;
1038
86aa7ba4
LP
1039 }
1040
1a6c43e9
MT
1041finish:
1042 return r;
1043}
1044
886a64fe 1045int add_matches_for_unit(sd_journal *j, const char *unit) {
1a6c43e9 1046 int r;
2d0b2e87 1047 char *m1, *m2, *m3, *m4;
1a6c43e9 1048
886a64fe 1049 assert(j);
1a6c43e9
MT
1050 assert(unit);
1051
2d0b2e87
ZJS
1052 m1 = strappenda("_SYSTEMD_UNIT=", unit);
1053 m2 = strappenda("COREDUMP_UNIT=", unit);
1054 m3 = strappenda("UNIT=", unit);
1055 m4 = strappenda("OBJECT_SYSTEMD_UNIT=", unit);
1a6c43e9 1056
886a64fe
ZJS
1057 (void)(
1058 /* Look for messages from the service itself */
1059 (r = sd_journal_add_match(j, m1, 0)) ||
1060
1061 /* Look for coredumps of the service */
1062 (r = sd_journal_add_disjunction(j)) ||
fdcd37df
ZJS
1063 (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
1064 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
886a64fe
ZJS
1065 (r = sd_journal_add_match(j, m2, 0)) ||
1066
1067 /* Look for messages from PID 1 about this service */
1068 (r = sd_journal_add_disjunction(j)) ||
1069 (r = sd_journal_add_match(j, "_PID=1", 0)) ||
2d0b2e87
ZJS
1070 (r = sd_journal_add_match(j, m3, 0)) ||
1071
1072 /* Look for messages from authorized daemons about this service */
1073 (r = sd_journal_add_disjunction(j)) ||
1074 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1075 (r = sd_journal_add_match(j, m4, 0))
886a64fe 1076 );
2d0b2e87 1077
69ae3ee0
ZJS
1078 if (r == 0 && endswith(unit, ".slice")) {
1079 char *m5 = strappend("_SYSTEMD_SLICE=", unit);
1080
1081 /* Show all messages belonging to a slice */
1082 (void)(
1083 (r = sd_journal_add_disjunction(j)) ||
1084 (r = sd_journal_add_match(j, m5, 0))
1085 );
1086 }
1087
886a64fe
ZJS
1088 return r;
1089}
1a6c43e9 1090
886a64fe
ZJS
1091int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
1092 int r;
2d0b2e87
ZJS
1093 char *m1, *m2, *m3, *m4;
1094 char muid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)];
1a6c43e9 1095
886a64fe
ZJS
1096 assert(j);
1097 assert(unit);
1a6c43e9 1098
2d0b2e87
ZJS
1099 m1 = strappenda("_SYSTEMD_USER_UNIT=", unit);
1100 m2 = strappenda("USER_UNIT=", unit);
1101 m3 = strappenda("COREDUMP_USER_UNIT=", unit);
1102 m4 = strappenda("OBJECT_SYSTEMD_USER_UNIT=", unit);
de0671ee 1103 sprintf(muid, "_UID="UID_FMT, uid);
1a6c43e9 1104
886a64fe
ZJS
1105 (void) (
1106 /* Look for messages from the user service itself */
1107 (r = sd_journal_add_match(j, m1, 0)) ||
2d0b2e87 1108 (r = sd_journal_add_match(j, muid, 0)) ||
886a64fe
ZJS
1109
1110 /* Look for messages from systemd about this service */
1111 (r = sd_journal_add_disjunction(j)) ||
1112 (r = sd_journal_add_match(j, m2, 0)) ||
2d0b2e87 1113 (r = sd_journal_add_match(j, muid, 0)) ||
886a64fe
ZJS
1114
1115 /* Look for coredumps of the service */
1116 (r = sd_journal_add_disjunction(j)) ||
1117 (r = sd_journal_add_match(j, m3, 0)) ||
2d0b2e87
ZJS
1118 (r = sd_journal_add_match(j, muid, 0)) ||
1119 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1120
1121 /* Look for messages from authorized daemons about this service */
1122 (r = sd_journal_add_disjunction(j)) ||
fdcd37df 1123 (r = sd_journal_add_match(j, m4, 0)) ||
2d0b2e87 1124 (r = sd_journal_add_match(j, muid, 0)) ||
fdcd37df 1125 (r = sd_journal_add_match(j, "_UID=0", 0))
886a64fe 1126 );
69ae3ee0
ZJS
1127
1128 if (r == 0 && endswith(unit, ".slice")) {
1129 char *m5 = strappend("_SYSTEMD_SLICE=", unit);
1130
1131 /* Show all messages belonging to a slice */
1132 (void)(
1133 (r = sd_journal_add_disjunction(j)) ||
1134 (r = sd_journal_add_match(j, m5, 0)) ||
1135 (r = sd_journal_add_match(j, muid, 0))
1136 );
1137 }
1138
1a6c43e9
MT
1139 return r;
1140}
1141
b6741478 1142static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
3d94f76c 1143 _cleanup_close_pair_ int pair[2] = { -1, -1 };
a4475f57 1144 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
b6741478
LP
1145 pid_t pid, child;
1146 siginfo_t si;
1147 char buf[37];
1148 ssize_t k;
1149 int r;
1150
1151 assert(machine);
1152 assert(boot_id);
1153
1154 if (!filename_is_safe(machine))
1155 return -EINVAL;
1156
e04b0cdb 1157 r = container_get_leader(machine, &pid);
b6741478
LP
1158 if (r < 0)
1159 return r;
e04b0cdb 1160
878cd7e9 1161 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
b6741478
LP
1162 if (r < 0)
1163 return r;
1164
e04b0cdb 1165 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
b6741478
LP
1166 return -errno;
1167
1168 child = fork();
1169 if (child < 0)
1170 return -errno;
1171
1172 if (child == 0) {
1173 int fd;
1174
03e334a1 1175 pair[0] = safe_close(pair[0]);
b6741478 1176
878cd7e9 1177 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
b6741478
LP
1178 if (r < 0)
1179 _exit(EXIT_FAILURE);
1180
1181 fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
1182 if (fd < 0)
1183 _exit(EXIT_FAILURE);
1184
1185 k = loop_read(fd, buf, 36, false);
03e334a1 1186 safe_close(fd);
b6741478
LP
1187 if (k != 36)
1188 _exit(EXIT_FAILURE);
1189
e04b0cdb 1190 k = send(pair[1], buf, 36, MSG_NOSIGNAL);
b6741478
LP
1191 if (k != 36)
1192 _exit(EXIT_FAILURE);
1193
1194 _exit(EXIT_SUCCESS);
1195 }
1196
03e334a1 1197 pair[1] = safe_close(pair[1]);
b6741478 1198
b6741478
LP
1199 r = wait_for_terminate(child, &si);
1200 if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
1201 return r < 0 ? r : -EIO;
1202
fbadf045
LP
1203 k = recv(pair[0], buf, 36, 0);
1204 if (k != 36)
1205 return -EIO;
1206
b6741478
LP
1207 buf[36] = 0;
1208 r = sd_id128_from_string(buf, boot_id);
1209 if (r < 0)
1210 return r;
1211
1212 return 0;
1213}
1214
1215int add_match_this_boot(sd_journal *j, const char *machine) {
5ec76417
ZJS
1216 char match[9+32+1] = "_BOOT_ID=";
1217 sd_id128_t boot_id;
1218 int r;
1219
1220 assert(j);
1221
b6741478
LP
1222 if (machine) {
1223 r = get_boot_id_for_machine(machine, &boot_id);
1224 if (r < 0) {
1225 log_error("Failed to get boot id of container %s: %s", machine, strerror(-r));
1226 return r;
1227 }
1228 } else {
1229 r = sd_id128_get_boot(&boot_id);
1230 if (r < 0) {
1231 log_error("Failed to get boot id: %s", strerror(-r));
1232 return r;
1233 }
5ec76417
ZJS
1234 }
1235
1236 sd_id128_to_string(boot_id, match + 9);
1237 r = sd_journal_add_match(j, match, strlen(match));
1238 if (r < 0) {
1239 log_error("Failed to add match: %s", strerror(-r));
1240 return r;
1241 }
1242
1243 r = sd_journal_add_conjunction(j);
1244 if (r < 0)
1245 return r;
1246
1247 return 0;
1248}
1249
886a64fe 1250int show_journal_by_unit(
1a6c43e9
MT
1251 FILE *f,
1252 const char *unit,
1253 OutputMode mode,
1254 unsigned n_columns,
1255 usec_t not_before,
1256 unsigned how_many,
1257 uid_t uid,
886a64fe 1258 OutputFlags flags,
94e0bd7d
ZJS
1259 bool system,
1260 bool *ellipsized) {
1a6c43e9 1261
7fd1b19b 1262 _cleanup_journal_close_ sd_journal*j = NULL;
1a6c43e9 1263 int r;
a688baa8 1264 int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM;
1a6c43e9
MT
1265
1266 assert(mode >= 0);
1267 assert(mode < _OUTPUT_MODE_MAX);
1268 assert(unit);
1269
1a6c43e9
MT
1270 if (how_many <= 0)
1271 return 0;
1272
886a64fe 1273 r = sd_journal_open(&j, jflags);
f9045468 1274 if (r < 0)
763c7aa2 1275 return r;
f9045468 1276
b6741478 1277 r = add_match_this_boot(j, NULL);
5ec76417
ZJS
1278 if (r < 0)
1279 return r;
1280
886a64fe
ZJS
1281 if (system)
1282 r = add_matches_for_unit(j, unit);
1283 else
1284 r = add_matches_for_user_unit(j, unit, uid);
1a6c43e9 1285 if (r < 0)
763c7aa2 1286 return r;
1a6c43e9 1287
4ad16808
ZJS
1288 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
1289 _cleanup_free_ char *filter;
1290
1291 filter = journal_make_match_string(j);
1292 log_debug("Journal filter: %s", filter);
1293 }
5ec76417 1294
94e0bd7d 1295 return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
86aa7ba4 1296}
df50185b
LP
1297
1298static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
1299 [OUTPUT_SHORT] = "short",
44bc6e1f 1300 [OUTPUT_SHORT_ISO] = "short-iso",
f02d8367
ZJS
1301 [OUTPUT_SHORT_PRECISE] = "short-precise",
1302 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
df50185b
LP
1303 [OUTPUT_VERBOSE] = "verbose",
1304 [OUTPUT_EXPORT] = "export",
d3f2bdbf 1305 [OUTPUT_JSON] = "json",
a6e87e90 1306 [OUTPUT_JSON_PRETTY] = "json-pretty",
48383c25 1307 [OUTPUT_JSON_SSE] = "json-sse",
d3f2bdbf 1308 [OUTPUT_CAT] = "cat"
df50185b
LP
1309};
1310
1311DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);