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