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