]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/logs-show.c
sd-event: rework API to support CLOCK_REALTIME_ALARM and CLOCK_BOOTTIME_ALARM, too
[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
LP
303 fprintf(f, "[%5llu.%06llu]",
304 (unsigned long long) (t / USEC_PER_SEC),
305 (unsigned long long) (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"
534 "__REALTIME_TIMESTAMP=%llu\n"
535 "__MONOTONIC_TIMESTAMP=%llu\n"
536 "_BOOT_ID=%s\n",
537 cursor,
538 (unsigned long long) realtime,
539 (unsigned long long) monotonic,
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
31f7bf19 550 if (!utf8_is_printable(data, length)) {
86aa7ba4
LP
551 const char *c;
552 uint64_t le64;
553
554 c = memchr(data, '=', length);
555 if (!c) {
556 log_error("Invalid field.");
557 return -EINVAL;
558 }
559
08ace05b
LP
560 fwrite(data, c - (const char*) data, 1, f);
561 fputc('\n', f);
86aa7ba4 562 le64 = htole64(length - (c - (const char*) data) - 1);
08ace05b
LP
563 fwrite(&le64, sizeof(le64), 1, f);
564 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
86aa7ba4 565 } else
08ace05b 566 fwrite(data, length, 1, f);
86aa7ba4 567
08ace05b 568 fputc('\n', f);
86aa7ba4
LP
569 }
570
a72b6353
ZJS
571 if (r < 0)
572 return r;
573
08ace05b 574 fputc('\n', f);
86aa7ba4
LP
575
576 return 0;
577}
578
240a5fe8 579void json_escape(
08ace05b
LP
580 FILE *f,
581 const char* p,
582 size_t l,
583 OutputFlags flags) {
584
585 assert(f);
586 assert(p);
587
93b73b06 588 if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
08ace05b
LP
589
590 fputs("null", f);
591
31f7bf19 592 else if (!utf8_is_printable(p, l)) {
86aa7ba4
LP
593 bool not_first = false;
594
08ace05b 595 fputs("[ ", f);
86aa7ba4
LP
596
597 while (l > 0) {
598 if (not_first)
08ace05b 599 fprintf(f, ", %u", (uint8_t) *p);
86aa7ba4
LP
600 else {
601 not_first = true;
08ace05b 602 fprintf(f, "%u", (uint8_t) *p);
86aa7ba4
LP
603 }
604
605 p++;
606 l--;
607 }
608
08ace05b 609 fputs(" ]", f);
86aa7ba4 610 } else {
08ace05b 611 fputc('\"', f);
86aa7ba4
LP
612
613 while (l > 0) {
614 if (*p == '"' || *p == '\\') {
08ace05b
LP
615 fputc('\\', f);
616 fputc(*p, f);
31f7bf19
ZJS
617 } else if (*p == '\n')
618 fputs("\\n", f);
619 else if (*p < ' ')
08ace05b
LP
620 fprintf(f, "\\u%04x", *p);
621 else
622 fputc(*p, f);
86aa7ba4
LP
623
624 p++;
625 l--;
626 }
627
08ace05b 628 fputc('\"', f);
86aa7ba4
LP
629 }
630}
631
08ace05b
LP
632static int output_json(
633 FILE *f,
634 sd_journal *j,
635 OutputMode mode,
636 unsigned n_columns,
637 OutputFlags flags) {
638
86aa7ba4 639 uint64_t realtime, monotonic;
7fd1b19b 640 _cleanup_free_ char *cursor = NULL;
86aa7ba4
LP
641 const void *data;
642 size_t length;
643 sd_id128_t boot_id;
2e729834 644 char sid[33], *k;
86aa7ba4 645 int r;
d99ae53a
LP
646 Hashmap *h = NULL;
647 bool done, separator;
86aa7ba4
LP
648
649 assert(j);
650
93b73b06
LP
651 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
652
86aa7ba4
LP
653 r = sd_journal_get_realtime_usec(j, &realtime);
654 if (r < 0) {
655 log_error("Failed to get realtime timestamp: %s", strerror(-r));
656 return r;
657 }
658
659 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
660 if (r < 0) {
661 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
662 return r;
663 }
664
665 r = sd_journal_get_cursor(j, &cursor);
666 if (r < 0) {
667 log_error("Failed to get cursor: %s", strerror(-r));
668 return r;
669 }
670
a6e87e90 671 if (mode == OUTPUT_JSON_PRETTY)
08ace05b
LP
672 fprintf(f,
673 "{\n"
674 "\t\"__CURSOR\" : \"%s\",\n"
675 "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
676 "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
677 "\t\"_BOOT_ID\" : \"%s\"",
678 cursor,
679 (unsigned long long) realtime,
680 (unsigned long long) monotonic,
681 sd_id128_to_string(boot_id, sid));
48383c25
LP
682 else {
683 if (mode == OUTPUT_JSON_SSE)
684 fputs("data: ", f);
685
08ace05b
LP
686 fprintf(f,
687 "{ \"__CURSOR\" : \"%s\", "
688 "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
689 "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
690 "\"_BOOT_ID\" : \"%s\"",
691 cursor,
692 (unsigned long long) realtime,
693 (unsigned long long) monotonic,
694 sd_id128_to_string(boot_id, sid));
48383c25 695 }
86aa7ba4 696
d99ae53a
LP
697 h = hashmap_new(string_hash_func, string_compare_func);
698 if (!h)
699 return -ENOMEM;
700
701 /* First round, iterate through the entry and count how often each field appears */
a72b6353 702 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
d99ae53a
LP
703 const char *eq;
704 char *n;
705 unsigned u;
86aa7ba4 706
112301ae
LP
707 if (length >= 9 &&
708 memcmp(data, "_BOOT_ID=", 9) == 0)
709 continue;
710
d99ae53a
LP
711 eq = memchr(data, '=', length);
712 if (!eq)
713 continue;
86aa7ba4 714
d99ae53a
LP
715 n = strndup(data, eq - (const char*) data);
716 if (!n) {
717 r = -ENOMEM;
718 goto finish;
719 }
a6e87e90 720
d99ae53a
LP
721 u = PTR_TO_UINT(hashmap_get(h, n));
722 if (u == 0) {
723 r = hashmap_put(h, n, UINT_TO_PTR(1));
724 if (r < 0) {
725 free(n);
726 goto finish;
727 }
728 } else {
729 r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
730 free(n);
731 if (r < 0)
732 goto finish;
733 }
86aa7ba4
LP
734 }
735
a72b6353
ZJS
736 if (r < 0)
737 return r;
738
d99ae53a
LP
739 separator = true;
740 do {
741 done = true;
742
743 SD_JOURNAL_FOREACH_DATA(j, data, length) {
744 const char *eq;
745 char *kk, *n;
746 size_t m;
747 unsigned u;
748
749 /* We already printed the boot id, from the data in
750 * the header, hence let's suppress it here */
751 if (length >= 9 &&
752 memcmp(data, "_BOOT_ID=", 9) == 0)
753 continue;
754
755 eq = memchr(data, '=', length);
756 if (!eq)
757 continue;
758
759 if (separator) {
760 if (mode == OUTPUT_JSON_PRETTY)
761 fputs(",\n\t", f);
762 else
763 fputs(", ", f);
764 }
765
766 m = eq - (const char*) data;
767
768 n = strndup(data, m);
769 if (!n) {
770 r = -ENOMEM;
771 goto finish;
772 }
773
774 u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
775 if (u == 0) {
776 /* We already printed this, let's jump to the next */
777 free(n);
778 separator = false;
779
780 continue;
781 } else if (u == 1) {
782 /* Field only appears once, output it directly */
783
784 json_escape(f, data, m, flags);
785 fputs(" : ", f);
786
787 json_escape(f, eq + 1, length - m - 1, flags);
788
789 hashmap_remove(h, n);
790 free(kk);
791 free(n);
792
793 separator = true;
794
795 continue;
796
797 } else {
798 /* Field appears multiple times, output it as array */
799 json_escape(f, data, m, flags);
800 fputs(" : [ ", f);
801 json_escape(f, eq + 1, length - m - 1, flags);
802
803 /* Iterate through the end of the list */
804
805 while (sd_journal_enumerate_data(j, &data, &length) > 0) {
806 if (length < m + 1)
807 continue;
808
809 if (memcmp(data, n, m) != 0)
810 continue;
811
812 if (((const char*) data)[m] != '=')
813 continue;
814
815 fputs(", ", f);
816 json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
817 }
818
819 fputs(" ]", f);
820
821 hashmap_remove(h, n);
822 free(kk);
823 free(n);
824
825 /* Iterate data fields form the beginning */
826 done = false;
827 separator = true;
828
829 break;
830 }
831 }
832
833 } while (!done);
834
a6e87e90 835 if (mode == OUTPUT_JSON_PRETTY)
08ace05b 836 fputs("\n}\n", f);
48383c25
LP
837 else if (mode == OUTPUT_JSON_SSE)
838 fputs("}\n\n", f);
a6e87e90 839 else
08ace05b 840 fputs(" }\n", f);
86aa7ba4 841
d99ae53a
LP
842 r = 0;
843
844finish:
845 while ((k = hashmap_steal_first_key(h)))
846 free(k);
847
848 hashmap_free(h);
849
850 return r;
86aa7ba4
LP
851}
852
08ace05b
LP
853static int output_cat(
854 FILE *f,
855 sd_journal *j,
856 OutputMode mode,
857 unsigned n_columns,
858 OutputFlags flags) {
859
d3f2bdbf
LP
860 const void *data;
861 size_t l;
862 int r;
863
864 assert(j);
08ace05b 865 assert(f);
d3f2bdbf 866
93b73b06
LP
867 sd_journal_set_data_threshold(j, 0);
868
d3f2bdbf
LP
869 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
870 if (r < 0) {
c198300f
LP
871 /* An entry without MESSAGE=? */
872 if (r == -ENOENT)
873 return 0;
874
d3f2bdbf
LP
875 log_error("Failed to get data: %s", strerror(-r));
876 return r;
877 }
878
879 assert(l >= 8);
880
08ace05b
LP
881 fwrite((const char*) data + 8, 1, l - 8, f);
882 fputc('\n', f);
d3f2bdbf
LP
883
884 return 0;
885}
886
08ace05b
LP
887static int (*output_funcs[_OUTPUT_MODE_MAX])(
888 FILE *f,
889 sd_journal*j,
890 OutputMode mode,
891 unsigned n_columns,
892 OutputFlags flags) = {
893
a6e87e90 894 [OUTPUT_SHORT] = output_short,
44bc6e1f 895 [OUTPUT_SHORT_ISO] = output_short,
f02d8367
ZJS
896 [OUTPUT_SHORT_PRECISE] = output_short,
897 [OUTPUT_SHORT_MONOTONIC] = output_short,
86aa7ba4
LP
898 [OUTPUT_VERBOSE] = output_verbose,
899 [OUTPUT_EXPORT] = output_export,
d3f2bdbf 900 [OUTPUT_JSON] = output_json,
a6e87e90 901 [OUTPUT_JSON_PRETTY] = output_json,
48383c25 902 [OUTPUT_JSON_SSE] = output_json,
d3f2bdbf 903 [OUTPUT_CAT] = output_cat
86aa7ba4
LP
904};
905
08ace05b
LP
906int output_journal(
907 FILE *f,
908 sd_journal *j,
909 OutputMode mode,
910 unsigned n_columns,
94e0bd7d
ZJS
911 OutputFlags flags,
912 bool *ellipsized) {
08ace05b 913
e268b81e 914 int ret;
df50185b 915 assert(mode >= 0);
86aa7ba4
LP
916 assert(mode < _OUTPUT_MODE_MAX);
917
34a35ece
LP
918 if (n_columns <= 0)
919 n_columns = columns();
920
08ace05b 921 ret = output_funcs[mode](f, j, mode, n_columns, flags);
e268b81e 922 fflush(stdout);
94e0bd7d
ZJS
923
924 if (ellipsized && ret > 0)
925 *ellipsized = true;
926
e268b81e 927 return ret;
86aa7ba4
LP
928}
929
ea6c2dd1
LP
930static int maybe_print_begin_newline(FILE *f, OutputFlags *flags) {
931 assert(f);
932 assert(flags);
933
934 if (!(*flags & OUTPUT_BEGIN_NEWLINE))
935 return 0;
936
937 /* Print a beginning new line if that's request, but only once
938 * on the first line we print. */
939
940 fputc('\n', f);
941 *flags &= ~OUTPUT_BEGIN_NEWLINE;
942 return 0;
943}
944
1a6c43e9
MT
945static int show_journal(FILE *f,
946 sd_journal *j,
947 OutputMode mode,
948 unsigned n_columns,
949 usec_t not_before,
950 unsigned how_many,
94e0bd7d
ZJS
951 OutputFlags flags,
952 bool *ellipsized) {
86aa7ba4 953
86aa7ba4 954 int r;
df50185b
LP
955 unsigned line = 0;
956 bool need_seek = false;
085d7120 957 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
86aa7ba4 958
1a6c43e9 959 assert(j);
df50185b
LP
960 assert(mode >= 0);
961 assert(mode < _OUTPUT_MODE_MAX);
1946b0bd
LP
962
963 /* Seek to end */
86aa7ba4
LP
964 r = sd_journal_seek_tail(j);
965 if (r < 0)
966 goto finish;
967
df50185b
LP
968 r = sd_journal_previous_skip(j, how_many);
969 if (r < 0)
970 goto finish;
86aa7ba4 971
df50185b
LP
972 for (;;) {
973 for (;;) {
974 usec_t usec;
975
976 if (need_seek) {
977 r = sd_journal_next(j);
978 if (r < 0)
979 goto finish;
980 }
981
982 if (r == 0)
983 break;
86aa7ba4 984
df50185b
LP
985 need_seek = true;
986
987 if (not_before > 0) {
988 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
989
990 /* -ESTALE is returned if the
991 timestamp is not from this boot */
992 if (r == -ESTALE)
993 continue;
994 else if (r < 0)
995 goto finish;
996
997 if (usec < not_before)
998 continue;
999 }
1000
1001 line ++;
ea6c2dd1 1002 maybe_print_begin_newline(f, &flags);
df50185b 1003
94e0bd7d 1004 r = output_journal(f, j, mode, n_columns, flags, ellipsized);
df50185b
LP
1005 if (r < 0)
1006 goto finish;
1007 }
1008
08984293
LP
1009 if (warn_cutoff && line < how_many && not_before > 0) {
1010 sd_id128_t boot_id;
1011 usec_t cutoff;
1012
1013 /* Check whether the cutoff line is too early */
1014
1015 r = sd_id128_get_boot(&boot_id);
1016 if (r < 0)
1017 goto finish;
1018
1019 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
1020 if (r < 0)
1021 goto finish;
1022
ea6c2dd1
LP
1023 if (r > 0 && not_before < cutoff) {
1024 maybe_print_begin_newline(f, &flags);
08ace05b 1025 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
ea6c2dd1 1026 }
08984293
LP
1027
1028 warn_cutoff = false;
1029 }
1030
085d7120 1031 if (!(flags & OUTPUT_FOLLOW))
86aa7ba4
LP
1032 break;
1033
e02d1cf7 1034 r = sd_journal_wait(j, (usec_t) -1);
df50185b
LP
1035 if (r < 0)
1036 goto finish;
1037
86aa7ba4
LP
1038 }
1039
1a6c43e9
MT
1040finish:
1041 return r;
1042}
1043
886a64fe 1044int add_matches_for_unit(sd_journal *j, const char *unit) {
1a6c43e9 1045 int r;
2d0b2e87 1046 char *m1, *m2, *m3, *m4;
1a6c43e9 1047
886a64fe 1048 assert(j);
1a6c43e9
MT
1049 assert(unit);
1050
2d0b2e87
ZJS
1051 m1 = strappenda("_SYSTEMD_UNIT=", unit);
1052 m2 = strappenda("COREDUMP_UNIT=", unit);
1053 m3 = strappenda("UNIT=", unit);
1054 m4 = strappenda("OBJECT_SYSTEMD_UNIT=", unit);
1a6c43e9 1055
886a64fe
ZJS
1056 (void)(
1057 /* Look for messages from the service itself */
1058 (r = sd_journal_add_match(j, m1, 0)) ||
1059
1060 /* Look for coredumps of the service */
1061 (r = sd_journal_add_disjunction(j)) ||
fdcd37df
ZJS
1062 (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
1063 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
886a64fe
ZJS
1064 (r = sd_journal_add_match(j, m2, 0)) ||
1065
1066 /* Look for messages from PID 1 about this service */
1067 (r = sd_journal_add_disjunction(j)) ||
1068 (r = sd_journal_add_match(j, "_PID=1", 0)) ||
2d0b2e87
ZJS
1069 (r = sd_journal_add_match(j, m3, 0)) ||
1070
1071 /* Look for messages from authorized daemons about this service */
1072 (r = sd_journal_add_disjunction(j)) ||
1073 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1074 (r = sd_journal_add_match(j, m4, 0))
886a64fe 1075 );
2d0b2e87 1076
69ae3ee0
ZJS
1077 if (r == 0 && endswith(unit, ".slice")) {
1078 char *m5 = strappend("_SYSTEMD_SLICE=", unit);
1079
1080 /* Show all messages belonging to a slice */
1081 (void)(
1082 (r = sd_journal_add_disjunction(j)) ||
1083 (r = sd_journal_add_match(j, m5, 0))
1084 );
1085 }
1086
886a64fe
ZJS
1087 return r;
1088}
1a6c43e9 1089
886a64fe
ZJS
1090int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
1091 int r;
2d0b2e87
ZJS
1092 char *m1, *m2, *m3, *m4;
1093 char muid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)];
1a6c43e9 1094
886a64fe
ZJS
1095 assert(j);
1096 assert(unit);
1a6c43e9 1097
2d0b2e87
ZJS
1098 m1 = strappenda("_SYSTEMD_USER_UNIT=", unit);
1099 m2 = strappenda("USER_UNIT=", unit);
1100 m3 = strappenda("COREDUMP_USER_UNIT=", unit);
1101 m4 = strappenda("OBJECT_SYSTEMD_USER_UNIT=", unit);
1102 sprintf(muid, "_UID=%lu", (unsigned long) uid);
1a6c43e9 1103
886a64fe
ZJS
1104 (void) (
1105 /* Look for messages from the user service itself */
1106 (r = sd_journal_add_match(j, m1, 0)) ||
2d0b2e87 1107 (r = sd_journal_add_match(j, muid, 0)) ||
886a64fe
ZJS
1108
1109 /* Look for messages from systemd about this service */
1110 (r = sd_journal_add_disjunction(j)) ||
1111 (r = sd_journal_add_match(j, m2, 0)) ||
2d0b2e87 1112 (r = sd_journal_add_match(j, muid, 0)) ||
886a64fe
ZJS
1113
1114 /* Look for coredumps of the service */
1115 (r = sd_journal_add_disjunction(j)) ||
1116 (r = sd_journal_add_match(j, m3, 0)) ||
2d0b2e87
ZJS
1117 (r = sd_journal_add_match(j, muid, 0)) ||
1118 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1119
1120 /* Look for messages from authorized daemons about this service */
1121 (r = sd_journal_add_disjunction(j)) ||
fdcd37df 1122 (r = sd_journal_add_match(j, m4, 0)) ||
2d0b2e87 1123 (r = sd_journal_add_match(j, muid, 0)) ||
fdcd37df 1124 (r = sd_journal_add_match(j, "_UID=0", 0))
886a64fe 1125 );
69ae3ee0
ZJS
1126
1127 if (r == 0 && endswith(unit, ".slice")) {
1128 char *m5 = strappend("_SYSTEMD_SLICE=", unit);
1129
1130 /* Show all messages belonging to a slice */
1131 (void)(
1132 (r = sd_journal_add_disjunction(j)) ||
1133 (r = sd_journal_add_match(j, m5, 0)) ||
1134 (r = sd_journal_add_match(j, muid, 0))
1135 );
1136 }
1137
1a6c43e9
MT
1138 return r;
1139}
1140
b6741478 1141static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
e04b0cdb 1142 _cleanup_close_pipe_ int pair[2] = { -1, -1 };
a4475f57 1143 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
b6741478
LP
1144 pid_t pid, child;
1145 siginfo_t si;
1146 char buf[37];
1147 ssize_t k;
1148 int r;
1149
1150 assert(machine);
1151 assert(boot_id);
1152
1153 if (!filename_is_safe(machine))
1154 return -EINVAL;
1155
e04b0cdb 1156 r = container_get_leader(machine, &pid);
b6741478
LP
1157 if (r < 0)
1158 return r;
e04b0cdb 1159
a4475f57 1160 r = namespace_open(pid, &pidnsfd, &mntnsfd, &rootfd);
b6741478
LP
1161 if (r < 0)
1162 return r;
1163
e04b0cdb 1164 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
b6741478
LP
1165 return -errno;
1166
1167 child = fork();
1168 if (child < 0)
1169 return -errno;
1170
1171 if (child == 0) {
1172 int fd;
1173
03e334a1 1174 pair[0] = safe_close(pair[0]);
b6741478 1175
a4475f57 1176 r = namespace_enter(pidnsfd, mntnsfd, rootfd);
b6741478
LP
1177 if (r < 0)
1178 _exit(EXIT_FAILURE);
1179
1180 fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
1181 if (fd < 0)
1182 _exit(EXIT_FAILURE);
1183
1184 k = loop_read(fd, buf, 36, false);
03e334a1 1185 safe_close(fd);
b6741478
LP
1186 if (k != 36)
1187 _exit(EXIT_FAILURE);
1188
e04b0cdb 1189 k = send(pair[1], buf, 36, MSG_NOSIGNAL);
b6741478
LP
1190 if (k != 36)
1191 _exit(EXIT_FAILURE);
1192
1193 _exit(EXIT_SUCCESS);
1194 }
1195
03e334a1 1196 pair[1] = safe_close(pair[1]);
b6741478 1197
b6741478
LP
1198 r = wait_for_terminate(child, &si);
1199 if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
1200 return r < 0 ? r : -EIO;
1201
fbadf045
LP
1202 k = recv(pair[0], buf, 36, 0);
1203 if (k != 36)
1204 return -EIO;
1205
b6741478
LP
1206 buf[36] = 0;
1207 r = sd_id128_from_string(buf, boot_id);
1208 if (r < 0)
1209 return r;
1210
1211 return 0;
1212}
1213
1214int add_match_this_boot(sd_journal *j, const char *machine) {
5ec76417
ZJS
1215 char match[9+32+1] = "_BOOT_ID=";
1216 sd_id128_t boot_id;
1217 int r;
1218
1219 assert(j);
1220
b6741478
LP
1221 if (machine) {
1222 r = get_boot_id_for_machine(machine, &boot_id);
1223 if (r < 0) {
1224 log_error("Failed to get boot id of container %s: %s", machine, strerror(-r));
1225 return r;
1226 }
1227 } else {
1228 r = sd_id128_get_boot(&boot_id);
1229 if (r < 0) {
1230 log_error("Failed to get boot id: %s", strerror(-r));
1231 return r;
1232 }
5ec76417
ZJS
1233 }
1234
1235 sd_id128_to_string(boot_id, match + 9);
1236 r = sd_journal_add_match(j, match, strlen(match));
1237 if (r < 0) {
1238 log_error("Failed to add match: %s", strerror(-r));
1239 return r;
1240 }
1241
1242 r = sd_journal_add_conjunction(j);
1243 if (r < 0)
1244 return r;
1245
1246 return 0;
1247}
1248
886a64fe 1249int show_journal_by_unit(
1a6c43e9
MT
1250 FILE *f,
1251 const char *unit,
1252 OutputMode mode,
1253 unsigned n_columns,
1254 usec_t not_before,
1255 unsigned how_many,
1256 uid_t uid,
886a64fe 1257 OutputFlags flags,
94e0bd7d
ZJS
1258 bool system,
1259 bool *ellipsized) {
1a6c43e9 1260
7fd1b19b 1261 _cleanup_journal_close_ sd_journal*j = NULL;
1a6c43e9 1262 int r;
a688baa8 1263 int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM;
1a6c43e9
MT
1264
1265 assert(mode >= 0);
1266 assert(mode < _OUTPUT_MODE_MAX);
1267 assert(unit);
1268
1a6c43e9
MT
1269 if (how_many <= 0)
1270 return 0;
1271
886a64fe 1272 r = sd_journal_open(&j, jflags);
f9045468 1273 if (r < 0)
763c7aa2 1274 return r;
f9045468 1275
b6741478 1276 r = add_match_this_boot(j, NULL);
5ec76417
ZJS
1277 if (r < 0)
1278 return r;
1279
886a64fe
ZJS
1280 if (system)
1281 r = add_matches_for_unit(j, unit);
1282 else
1283 r = add_matches_for_user_unit(j, unit, uid);
1a6c43e9 1284 if (r < 0)
763c7aa2 1285 return r;
1a6c43e9 1286
4ad16808
ZJS
1287 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
1288 _cleanup_free_ char *filter;
1289
1290 filter = journal_make_match_string(j);
1291 log_debug("Journal filter: %s", filter);
1292 }
5ec76417 1293
94e0bd7d 1294 return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
86aa7ba4 1295}
df50185b
LP
1296
1297static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
1298 [OUTPUT_SHORT] = "short",
44bc6e1f 1299 [OUTPUT_SHORT_ISO] = "short-iso",
f02d8367
ZJS
1300 [OUTPUT_SHORT_PRECISE] = "short-precise",
1301 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
df50185b
LP
1302 [OUTPUT_VERBOSE] = "verbose",
1303 [OUTPUT_EXPORT] = "export",
d3f2bdbf 1304 [OUTPUT_JSON] = "json",
a6e87e90 1305 [OUTPUT_JSON_PRETTY] = "json-pretty",
48383c25 1306 [OUTPUT_JSON_SSE] = "json-sse",
d3f2bdbf 1307 [OUTPUT_CAT] = "cat"
df50185b
LP
1308};
1309
1310DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);