]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/logs-show.c
logs-show: make output_timestamp_realtime() only take realtime timestamp
[thirdparty/systemd.git] / src / shared / logs-show.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
86aa7ba4 2
86aa7ba4 3#include <errno.h>
b6741478 4#include <fcntl.h>
a8fbdf54
TA
5#include <signal.h>
6#include <stdint.h>
7#include <stdlib.h>
a8fbdf54 8#include <syslog.h>
a8fbdf54
TA
9#include <unistd.h>
10
11#include "sd-id128.h"
12#include "sd-journal.h"
86aa7ba4 13
b5efdb8a 14#include "alloc-util.h"
3ffd4af2 15#include "fd-util.h"
f97b34a6 16#include "format-util.h"
d8e32c47 17#include "glyph-util.h"
d99ae53a 18#include "hashmap.h"
07630cea 19#include "hostname-util.h"
b5ea030d 20#include "id128-util.h"
c004493c 21#include "io-util.h"
dfb33a97 22#include "journal-internal.h"
1a8f0ce6 23#include "journal-util.h"
8e044443 24#include "json.h"
2108b567 25#include "locale-util.h"
07630cea 26#include "log.h"
3ffd4af2 27#include "logs-show.h"
a8fbdf54
TA
28#include "macro.h"
29#include "output-mode.h"
6bedfcbb 30#include "parse-util.h"
76f7cc3f 31#include "pretty-print.h"
a8fbdf54 32#include "sparse-endian.h"
29a753df 33#include "stdio-util.h"
8b43440b 34#include "string-table.h"
07630cea 35#include "string-util.h"
cc25a67e 36#include "strv.h"
288a74cc 37#include "terminal-util.h"
a8fbdf54 38#include "time-util.h"
07630cea 39#include "utf8.h"
2108b567 40#include "web-util.h"
86aa7ba4 41
07630cea 42/* up to three lines (each up to 100 characters) or 300 characters, whichever is less */
a6f0104a
ZJS
43#define PRINT_LINE_THRESHOLD 3
44#define PRINT_CHAR_THRESHOLD 300
45
8e044443 46#define JSON_THRESHOLD 4096U
86aa7ba4 47
d4205751 48static int print_catalog(FILE *f, sd_journal *j) {
d4205751 49 _cleanup_free_ char *t = NULL, *z = NULL;
ee5c1311 50 const char *newline, *prefix;
2108b567
LP
51 int r;
52
53 assert(j);
d4205751 54
d4205751 55 r = sd_journal_get_catalog(j, &t);
2108b567
LP
56 if (r == -ENOENT)
57 return 0;
d4205751 58 if (r < 0)
2108b567 59 return log_error_errno(r, "Failed to find catalog entry: %m");
d4205751 60
ee5c1311
LP
61 if (is_locale_utf8())
62 prefix = strjoina(special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), special_glyph(SPECIAL_GLYPH_LIGHT_SHADE));
63 else
64 prefix = "--";
65
cd7ae1b4 66 newline = strjoina(ansi_normal(), "\n", ansi_grey(), prefix, ansi_normal(), " ", ansi_green());
ee5c1311
LP
67
68 z = strreplace(strstrip(t), "\n", newline);
d4205751
LP
69 if (!z)
70 return log_oom();
71
cd7ae1b4 72 fprintf(f, "%s%s %s%s", ansi_grey(), prefix, ansi_normal(), ansi_green());
d4205751 73 fputs(z, f);
cd7ae1b4 74 fprintf(f, "%s\n", ansi_normal());
d4205751 75
2108b567
LP
76 return 1;
77}
78
79static int url_from_catalog(sd_journal *j, char **ret) {
80 _cleanup_free_ char *t = NULL, *url = NULL;
81 const char *weblink;
82 int r;
83
84 assert(j);
85 assert(ret);
86
87 r = sd_journal_get_catalog(j, &t);
88 if (r == -ENOENT)
89 goto notfound;
90 if (r < 0)
91 return log_error_errno(r, "Failed to find catalog entry: %m");
92
50ed5cbf
LP
93 weblink = find_line_startswith(t, "Documentation:");
94 if (!weblink)
95 goto notfound;
2108b567
LP
96
97 /* Skip whitespace to value */
98 weblink += strspn(weblink, " \t");
99
100 /* Cut out till next whitespace/newline */
e8bec624 101 url = strdupcspn(weblink, WHITESPACE);
2108b567
LP
102 if (!url)
103 return log_oom();
104
105 if (!documentation_url_is_valid(url))
106 goto notfound;
107
108 *ret = TAKE_PTR(url);
109 return 1;
110
111notfound:
112 *ret = NULL;
d4205751
LP
113 return 0;
114}
115
b1cc0822
LP
116static int parse_field(
117 const void *data,
118 size_t length,
119 const char *field,
120 size_t field_len,
121 char **target,
122 size_t *target_len) {
123
ed054520 124 size_t nl;
d813d7a3 125 char *buf;
55d7bfc1
LP
126
127 assert(data);
128 assert(field);
129 assert(target);
55d7bfc1 130
ed054520 131 if (length < field_len)
55d7bfc1
LP
132 return 0;
133
ed054520 134 if (memcmp(data, field, field_len))
55d7bfc1
LP
135 return 0;
136
ed054520 137 nl = length - field_len;
c165d97d 138
ed054520 139 buf = newdup_suffix0(char, (const char*) data + field_len, nl);
0d0f0c50
SL
140 if (!buf)
141 return log_oom();
55d7bfc1 142
b1cc0822 143 free_and_replace(*target, buf);
d813d7a3 144
ed054520
VC
145 if (target_len)
146 *target_len = nl;
55d7bfc1
LP
147
148 return 1;
149}
150
ed054520
VC
151typedef struct ParseFieldVec {
152 const char *field;
153 size_t field_len;
154 char **target;
155 size_t *target_len;
156} ParseFieldVec;
157
e3eec1fd
LP
158#define PARSE_FIELD_VEC_ENTRY(_field, _target, _target_len) { \
159 .field = _field, \
160 .field_len = strlen(_field), \
161 .target = _target, \
162 .target_len = _target_len \
163 }
ed054520 164
b1cc0822
LP
165static int parse_fieldv(
166 const void *data,
167 size_t length,
168 const ParseFieldVec *fields,
169 size_t n_fields) {
170
171 int r;
ed054520 172
b1cc0822 173 for (size_t i = 0; i < n_fields; i++) {
ed054520 174 const ParseFieldVec *f = &fields[i];
ed054520
VC
175
176 r = parse_field(data, length, f->field, f->field_len, f->target, f->target_len);
177 if (r < 0)
178 return r;
b1cc0822 179 if (r > 0)
ed054520
VC
180 break;
181 }
182
183 return 0;
184}
185
2f063186
ZJS
186static int field_set_test(const Set *fields, const char *name, size_t n) {
187 char *s;
cc25a67e
LK
188
189 if (!fields)
190 return 1;
191
2f82562b 192 s = strndupa_safe(name, n);
54ff74d2 193 return set_contains(fields, s);
cc25a67e
LK
194}
195
08ace05b
LP
196static bool shall_print(const char *p, size_t l, OutputFlags flags) {
197 assert(p);
198
199 if (flags & OUTPUT_SHOW_ALL)
55d7bfc1
LP
200 return true;
201
a6f0104a 202 if (l >= PRINT_CHAR_THRESHOLD)
55d7bfc1
LP
203 return false;
204
31f7bf19 205 if (!utf8_is_printable(p, l))
55d7bfc1
LP
206 return false;
207
208 return true;
209}
210
b4766d5f
ZJS
211static bool print_multiline(
212 FILE *f,
213 unsigned prefix,
214 unsigned n_columns,
215 OutputFlags flags,
216 int priority,
194da5ca 217 bool audit,
b4766d5f
ZJS
218 const char* message,
219 size_t message_len,
05c7d9bf 220 size_t highlight[2]) {
b4766d5f
ZJS
221
222 const char *color_on = "", *color_off = "", *highlight_on = "";
31f7bf19 223 const char *pos, *end;
94e0bd7d 224 bool ellipsized = false;
a6f0104a 225 int line = 0;
31f7bf19 226
194da5ca 227 if (flags & OUTPUT_COLOR) {
37b8d2f6 228 get_log_colors(priority, &color_on, &color_off, &highlight_on);
31f7bf19 229
194da5ca
ZJS
230 if (audit && strempty(color_on)) {
231 color_on = ANSI_BLUE;
232 color_off = ANSI_NORMAL;
233 }
234 }
235
47d80904
UU
236 /* A special case: make sure that we print a newline when
237 the message is empty. */
238 if (message_len == 0)
239 fputs("\n", f);
240
a6f0104a
ZJS
241 for (pos = message;
242 pos < message + message_len;
243 pos = end + 1, line++) {
a6f0104a 244 bool tail_line;
f996072f 245 int len, indent = (line > 0) * prefix;
31f7bf19
ZJS
246 for (end = pos; end < message + message_len && *end != '\n'; end++)
247 ;
248 len = end - pos;
249 assert(len >= 0);
250
2526d626 251 /* We need to figure out when we are showing not-last line, *and*
a6f0104a
ZJS
252 * will skip subsequent lines. In that case, we will put the dots
253 * at the end of the line, instead of putting dots in the middle
254 * or not at all.
255 */
256 tail_line =
257 line + 1 == PRINT_LINE_THRESHOLD ||
2526d626 258 end + 1 >= message + PRINT_CHAR_THRESHOLD;
a6f0104a
ZJS
259
260 if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) ||
261 (prefix + len + 1 < n_columns && !tail_line)) {
b4766d5f
ZJS
262 if (highlight &&
263 (size_t) (pos - message) <= highlight[0] &&
264 highlight[0] < (size_t) len) {
265
266 fprintf(f, "%*s%s%.*s",
f996072f 267 indent, "",
b4766d5f
ZJS
268 color_on, (int) highlight[0], pos);
269 fprintf(f, "%s%.*s",
270 highlight_on,
271 (int) (MIN((size_t) len, highlight[1]) - highlight[0]),
272 pos + highlight[0]);
273 if ((size_t) len > highlight[1])
274 fprintf(f, "%s%.*s",
275 color_on,
276 (int) (len - highlight[1]),
277 pos + highlight[1]);
278 fprintf(f, "%s\n", color_off);
279
280 } else
281 fprintf(f, "%*s%s%.*s%s\n",
f996072f 282 indent, "",
b4766d5f 283 color_on, len, pos, color_off);
a6f0104a
ZJS
284 continue;
285 }
31f7bf19 286
a6f0104a
ZJS
287 /* Beyond this point, ellipsization will happen. */
288 ellipsized = true;
31f7bf19 289
a6f0104a
ZJS
290 if (prefix < n_columns && n_columns - prefix >= 3) {
291 if (n_columns - prefix > (unsigned) len + 3)
292 fprintf(f, "%*s%s%.*s...%s\n",
f996072f 293 indent, "",
b4b02cbe 294 color_on, len, pos, color_off);
a6f0104a 295 else {
c2b2df60 296 _cleanup_free_ char *e = NULL;
a6f0104a
ZJS
297
298 e = ellipsize_mem(pos, len, n_columns - prefix,
299 tail_line ? 100 : 90);
300 if (!e)
301 fprintf(f, "%*s%s%.*s%s\n",
f996072f 302 indent, "",
a6f0104a
ZJS
303 color_on, len, pos, color_off);
304 else
305 fprintf(f, "%*s%s%s%s\n",
f996072f 306 indent, "",
a6f0104a
ZJS
307 color_on, e, color_off);
308 }
309 } else
31f7bf19
ZJS
310 fputs("...\n", f);
311
a6f0104a
ZJS
312 if (tail_line)
313 break;
31f7bf19 314 }
94e0bd7d
ZJS
315
316 return ellipsized;
31f7bf19
ZJS
317}
318
893bcd3d 319static int output_timestamp_monotonic(
b1cc0822
LP
320 FILE *f,
321 OutputMode mode,
4e30b87d 322 const dual_timestamp *display_ts,
893bcd3d 323 const sd_id128_t *boot_id,
4e30b87d 324 const dual_timestamp *previous_display_ts,
893bcd3d
DB
325 const sd_id128_t *previous_boot_id) {
326
327 int written_chars = 0;
328
29a753df 329 assert(f);
4e30b87d 330 assert(display_ts);
893bcd3d 331 assert(boot_id);
4e30b87d 332 assert(previous_display_ts);
893bcd3d 333 assert(previous_boot_id);
29a753df 334
4e30b87d 335 if (!VALID_MONOTONIC(display_ts->monotonic))
275e6be0 336 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid monotonic timestamp available");
29a753df 337
4e30b87d 338 written_chars += fprintf(f, "[%5"PRI_USEC".%06"PRI_USEC, display_ts->monotonic / USEC_PER_SEC, display_ts->monotonic % USEC_PER_SEC);
893bcd3d
DB
339
340 if (mode == OUTPUT_SHORT_DELTA) {
341 uint64_t delta;
342 bool reliable_ts = true;
343
4e30b87d
LP
344 if (VALID_MONOTONIC(previous_display_ts->monotonic) && sd_id128_equal(*boot_id, *previous_boot_id))
345 delta = usec_sub_unsigned(display_ts->monotonic, previous_display_ts->monotonic);
346 else if (VALID_REALTIME(display_ts->realtime) && VALID_REALTIME(previous_display_ts->realtime)) {
347 delta = usec_sub_unsigned(display_ts->realtime, previous_display_ts->realtime);
893bcd3d
DB
348 reliable_ts = false;
349 } else {
350 written_chars += fprintf(f, "%16s", "");
351 goto finish;
352 }
353
354 written_chars += fprintf(f, " <%5"PRI_USEC".%06"PRI_USEC"%s>", delta / USEC_PER_SEC, delta % USEC_PER_SEC, reliable_ts ? " " : "*");
355 }
356
357finish:
358 written_chars += fprintf(f, "%s", "]");
893bcd3d 359 return written_chars;
29a753df
LP
360}
361
275e6be0
DB
362static int output_timestamp_realtime(
363 FILE *f,
364 sd_journal *j,
365 OutputMode mode,
366 OutputFlags flags,
6ed286d2 367 usec_t usec) {
275e6be0 368
89eb3d7c 369 char buf[CONST_MAX(FORMAT_TIMESTAMP_MAX, 64U)];
29a753df
LP
370
371 assert(f);
372 assert(j);
373
6ed286d2
YW
374 if (!VALID_REALTIME(usec))
375 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid realtime timestamp available.");
29a753df 376
49805b3d 377 if (IN_SET(mode, OUTPUT_SHORT_FULL, OUTPUT_WITH_UNIT)) {
29a753df
LP
378 const char *k;
379
380 if (flags & OUTPUT_UTC)
6ed286d2 381 k = format_timestamp_style(buf, sizeof(buf), usec, TIMESTAMP_UTC);
29a753df 382 else
6ed286d2 383 k = format_timestamp(buf, sizeof(buf), usec);
baaa35ad
ZJS
384 if (!k)
385 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
6ed286d2 386 "Failed to format timestamp: %" PRIu64, usec);
29a753df
LP
387
388 } else {
f9c443b7
YW
389 struct tm tm;
390 time_t t;
7e563bfc 391
6ed286d2 392 t = (time_t) (usec / USEC_PER_SEC);
29a753df
LP
393
394 switch (mode) {
395
396 case OUTPUT_SHORT_UNIX:
6ed286d2 397 xsprintf(buf, "%10"PRI_TIME".%06"PRIu64, t, usec % USEC_PER_SEC);
29a753df
LP
398 break;
399
400 case OUTPUT_SHORT_ISO:
0693e6b2 401 case OUTPUT_SHORT_ISO_PRECISE: {
402 size_t tail = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S",
403 localtime_or_gmtime_r(&t, &tm, flags & OUTPUT_UTC));
404 if (tail == 0)
6ed286d2 405 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to format ISO time.");
7e563bfc 406
0693e6b2 407 /* No usec in strftime, need to append */
408 if (mode == OUTPUT_SHORT_ISO_PRECISE) {
6ed286d2 409 assert_se(snprintf_ok(buf + tail, ELEMENTSOF(buf) - tail, ".%06"PRI_USEC, usec % USEC_PER_SEC));
0693e6b2 410 tail += 7;
411 }
f9c443b7 412
e2e01259 413 int h = tm.tm_gmtoff / 60 / 60;
414 int m = labs((tm.tm_gmtoff / 60) % 60);
415 snprintf(buf + tail, ELEMENTSOF(buf) - tail, "%+03d:%02d", h, m);
29a753df 416 break;
f9c443b7 417 }
0693e6b2 418
29a753df
LP
419 case OUTPUT_SHORT:
420 case OUTPUT_SHORT_PRECISE:
421
f9c443b7
YW
422 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S",
423 localtime_or_gmtime_r(&t, &tm, flags & OUTPUT_UTC)) <= 0)
6ed286d2 424 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to format syslog time.");
29a753df
LP
425
426 if (mode == OUTPUT_SHORT_PRECISE) {
29a753df 427 assert(sizeof(buf) > strlen(buf));
6ed286d2
YW
428 if (!snprintf_ok(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%06"PRIu64, usec % USEC_PER_SEC))
429 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to format precise time.");
29a753df
LP
430 }
431 break;
432
433 default:
04499a70 434 assert_not_reached();
29a753df
LP
435 }
436 }
437
438 fputs(buf, f);
439 return (int) strlen(buf);
440}
441
63d2c755
YW
442static void parse_display_timestamp(
443 sd_journal *j,
444 const char *realtime,
445 const char *monotonic,
446 dual_timestamp *ret_display_ts,
447 sd_id128_t *ret_boot_id) {
448
449 bool realtime_good = false, monotonic_good = false, boot_id_good = false;
450
451 assert(j);
452 assert(ret_display_ts);
453 assert(ret_boot_id);
454
455 if (realtime)
456 realtime_good = safe_atou64(realtime, &ret_display_ts->realtime) >= 0;
457 if (!realtime_good || !VALID_REALTIME(ret_display_ts->realtime))
458 realtime_good = sd_journal_get_realtime_usec(j, &ret_display_ts->realtime) >= 0;
459 if (!realtime_good)
460 ret_display_ts->realtime = USEC_INFINITY;
461
462 if (monotonic)
463 monotonic_good = safe_atou64(monotonic, &ret_display_ts->monotonic) >= 0;
464 if (!monotonic_good || !VALID_MONOTONIC(ret_display_ts->monotonic))
465 monotonic_good = boot_id_good = sd_journal_get_monotonic_usec(j, &ret_display_ts->monotonic, ret_boot_id) >= 0;
466 if (!monotonic_good)
467 ret_display_ts->monotonic = USEC_INFINITY;
468
469 if (!boot_id_good)
470 boot_id_good = sd_journal_get_monotonic_usec(j, NULL, ret_boot_id) >= 0;
471 if (!boot_id_good)
472 *ret_boot_id = SD_ID128_NULL;
473}
474
08ace05b
LP
475static int output_short(
476 FILE *f,
477 sd_journal *j,
478 OutputMode mode,
479 unsigned n_columns,
cc25a67e 480 OutputFlags flags,
2f063186 481 const Set *output_fields,
275e6be0 482 const size_t highlight[2],
63d2c755
YW
483 dual_timestamp *previous_display_ts, /* in and out */
484 sd_id128_t *previous_boot_id) { /* in and out */
08ace05b 485
86aa7ba4 486 int r;
86aa7ba4 487 const void *data;
2108b567
LP
488 size_t length, n = 0;
489 _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL,
275e6be0 490 *message = NULL, *priority = NULL, *transport = NULL,
63d2c755
YW
491 *config_file = NULL, *unit = NULL, *user_unit = NULL, *documentation_url = NULL,
492 *realtime = NULL, *monotonic = NULL;
2108b567 493 size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0,
275e6be0 494 priority_len = 0, transport_len = 0, config_file_len = 0,
2108b567 495 unit_len = 0, user_unit_len = 0, documentation_url_len = 0;
63d2c755
YW
496 dual_timestamp display_ts;
497 sd_id128_t boot_id;
49826187 498 int p = LOG_INFO;
194da5ca 499 bool ellipsized = false, audit;
ed054520 500 const ParseFieldVec fields[] = {
63d2c755
YW
501 PARSE_FIELD_VEC_ENTRY("_PID=", &pid, &pid_len ),
502 PARSE_FIELD_VEC_ENTRY("_COMM=", &comm, &comm_len ),
503 PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message, &message_len ),
504 PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority, &priority_len ),
505 PARSE_FIELD_VEC_ENTRY("_TRANSPORT=", &transport, &transport_len ),
506 PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname, &hostname_len ),
507 PARSE_FIELD_VEC_ENTRY("SYSLOG_PID=", &fake_pid, &fake_pid_len ),
508 PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len ),
509 PARSE_FIELD_VEC_ENTRY("CONFIG_FILE=", &config_file, &config_file_len ),
510 PARSE_FIELD_VEC_ENTRY("_SYSTEMD_UNIT=", &unit, &unit_len ),
511 PARSE_FIELD_VEC_ENTRY("_SYSTEMD_USER_UNIT=", &user_unit, &user_unit_len ),
512 PARSE_FIELD_VEC_ENTRY("DOCUMENTATION=", &documentation_url, &documentation_url_len),
513 PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, NULL ),
514 PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, NULL ),
ed054520 515 };
b4766d5f 516 size_t highlight_shifted[] = {highlight ? highlight[0] : 0, highlight ? highlight[1] : 0};
86aa7ba4 517
08ace05b 518 assert(f);
86aa7ba4 519 assert(j);
4e30b87d 520 assert(previous_display_ts);
893bcd3d 521 assert(previous_boot_id);
86aa7ba4 522
b1cc0822
LP
523 /* Set the threshold to one bigger than the actual print threshold, so that if the line is actually
524 * longer than what we're willing to print, ellipsization will occur. This way we won't output a
525 * misleading line without any indication of truncation.
a6f0104a 526 */
b1cc0822 527 (void) sd_journal_set_data_threshold(j, flags & (OUTPUT_SHOW_ALL|OUTPUT_FULL_WIDTH) ? 0 : PRINT_CHAR_THRESHOLD + 1);
93b73b06 528
a72b6353 529 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
ed054520 530 r = parse_fieldv(data, length, fields, ELEMENTSOF(fields));
55d7bfc1 531 if (r < 0)
08ace05b 532 return r;
55d7bfc1 533 }
a3b076f6 534 if (IN_SET(r, -EBADMSG, -EADDRNOTAVAIL)) {
d00f1d57
LP
535 log_debug_errno(r, "Skipping message we can't read: %m");
536 return 0;
537 }
a72b6353 538 if (r < 0)
b56d608e 539 return log_error_errno(r, "Failed to get journal fields: %m");
a72b6353 540
07d21025
LP
541 if (!message) {
542 log_debug("Skipping message without MESSAGE= field.");
08ace05b 543 return 0;
07d21025 544 }
55d7bfc1 545
25aa35d4
SZ
546 if (identifier && set_contains(j->exclude_syslog_identifiers, identifier))
547 return 0;
548
63d2c755
YW
549 parse_display_timestamp(j, realtime, monotonic, &display_ts, &boot_id);
550
e8bc0ea2 551 if (!(flags & OUTPUT_SHOW_ALL))
b4766d5f 552 strip_tab_ansi(&message, &message_len, highlight_shifted);
e8bc0ea2 553
61cecfa0 554 if (flags & OUTPUT_TRUNCATE_NEWLINE)
555 truncate_nl_full(message, &message_len);
556
49826187
LP
557 if (priority_len == 1 && *priority >= '0' && *priority <= '7')
558 p = *priority - '0';
559
194da5ca
ZJS
560 audit = streq_ptr(transport, "audit");
561
893bcd3d 562 if (IN_SET(mode, OUTPUT_SHORT_MONOTONIC, OUTPUT_SHORT_DELTA))
63d2c755 563 r = output_timestamp_monotonic(f, mode, &display_ts, &boot_id, previous_display_ts, previous_boot_id);
29a753df 564 else
6ed286d2 565 r = output_timestamp_realtime(f, j, mode, flags, display_ts.realtime);
29a753df
LP
566 if (r < 0)
567 return r;
568 n += r;
86aa7ba4 569
29a753df 570 if (flags & OUTPUT_NO_HOSTNAME) {
991e274b 571 /* Suppress display of the hostname if this is requested. */
12104159 572 hostname = mfree(hostname);
991e274b
LP
573 hostname_len = 0;
574 }
575
08ace05b
LP
576 if (hostname && shall_print(hostname, hostname_len, flags)) {
577 fprintf(f, " %.*s", (int) hostname_len, hostname);
55d7bfc1
LP
578 n += hostname_len + 1;
579 }
580
76f7cc3f
ZJS
581 if (mode == OUTPUT_WITH_UNIT && ((unit && shall_print(unit, unit_len, flags)) ||
582 (user_unit && shall_print(user_unit, user_unit_len, flags)))) {
49805b3d
LB
583 if (unit) {
584 fprintf(f, " %.*s", (int) unit_len, unit);
585 n += unit_len + 1;
586 }
587 if (user_unit) {
588 if (unit)
589 fprintf(f, "/%.*s", (int) user_unit_len, user_unit);
590 else
591 fprintf(f, " %.*s", (int) user_unit_len, user_unit);
592 n += unit_len + 1;
593 }
594 } else if (identifier && shall_print(identifier, identifier_len, flags)) {
08ace05b 595 fprintf(f, " %.*s", (int) identifier_len, identifier);
4cd9a9d9 596 n += identifier_len + 1;
08ace05b
LP
597 } else if (comm && shall_print(comm, comm_len, flags)) {
598 fprintf(f, " %.*s", (int) comm_len, comm);
55d7bfc1 599 n += comm_len + 1;
b5936820 600 } else
1248e840 601 fputs(" unknown", f);
86aa7ba4 602
08ace05b
LP
603 if (pid && shall_print(pid, pid_len, flags)) {
604 fprintf(f, "[%.*s]", (int) pid_len, pid);
55d7bfc1 605 n += pid_len + 2;
08ace05b
LP
606 } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
607 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
6c1e6b98 608 n += fake_pid_len + 2;
86aa7ba4
LP
609 }
610
2108b567
LP
611 fputs(": ", f);
612
613 if (urlify_enabled()) {
614 _cleanup_free_ char *c = NULL;
615
616 /* Insert a hyperlink to a documentation URL before the message. Note that we don't make the
617 * whole message a hyperlink, since otherwise the whole screen might end up being just
618 * hyperlinks. Moreover, we want to be able to highlight parts of the message (such as the
619 * config file, see below) hence let's keep the documentation URL link separate. */
620
621 if (documentation_url && shall_print(documentation_url, documentation_url_len, flags)) {
622 c = strndup(documentation_url, documentation_url_len);
623 if (!c)
624 return log_oom();
625
626 if (!documentation_url_is_valid(c)) /* Eat up invalid links */
627 c = mfree(c);
628 }
629
630 if (!c)
631 (void) url_from_catalog(j, &c); /* Acquire from catalog if not embedded in log message itself */
632
633 if (c) {
634 _cleanup_free_ char *urlified = NULL;
635
636 if (terminal_urlify(c, special_glyph(SPECIAL_GLYPH_EXTERNAL_LINK), &urlified) >= 0) {
637 fputs(urlified, f);
638 fputc(' ', f);
639 }
640 }
641 }
642
2b59bf51
ZJS
643 if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len))
644 fprintf(f, "[%s blob data]\n", FORMAT_BYTES(message_len));
645 else {
76f7cc3f
ZJS
646
647 /* URLify config_file string in message, if the message starts with it.
648 * Skip URLification if the highlighted pattern overlaps. */
649 if (config_file &&
650 message_len >= config_file_len &&
651 memcmp(message, config_file, config_file_len) == 0 &&
85fbebe6 652 (message_len == config_file_len || IN_SET(message[config_file_len], ':', ' ')) &&
76f7cc3f
ZJS
653 (!highlight || highlight_shifted[0] == 0 || highlight_shifted[0] > config_file_len)) {
654
655 _cleanup_free_ char *t = NULL, *urlified = NULL;
656
657 t = strndup(config_file, config_file_len);
658 if (t && terminal_urlify_path(t, NULL, &urlified) >= 0) {
85fbebe6
ZJS
659 size_t urlified_len = strlen(urlified);
660 size_t shift = urlified_len - config_file_len;
76f7cc3f
ZJS
661 char *joined;
662
85fbebe6 663 joined = realloc(urlified, message_len + shift);
76f7cc3f 664 if (joined) {
85fbebe6 665 memcpy(joined + urlified_len, message + config_file_len, message_len - config_file_len);
76f7cc3f 666 free_and_replace(message, joined);
85fbebe6 667 TAKE_PTR(urlified);
76f7cc3f
ZJS
668 message_len += shift;
669 if (highlight) {
670 highlight_shifted[0] += shift;
671 highlight_shifted[1] += shift;
672 }
673 }
674 }
675 }
676
94e0bd7d 677 ellipsized |=
194da5ca 678 print_multiline(f, n + 2, n_columns, flags, p, audit,
b4766d5f
ZJS
679 message, message_len,
680 highlight_shifted);
31f7bf19 681 }
55d7bfc1 682
d4205751 683 if (flags & OUTPUT_CATALOG)
2108b567 684 (void) print_catalog(f, j);
d4205751 685
63d2c755
YW
686 *previous_display_ts = display_ts;
687 *previous_boot_id = boot_id;
688
94e0bd7d 689 return ellipsized;
86aa7ba4
LP
690}
691
63d2c755
YW
692static int get_display_timestamp(
693 sd_journal *j,
694 dual_timestamp *ret_display_ts,
695 sd_id128_t *ret_boot_id) {
696
697 const void *data;
698 _cleanup_free_ char *realtime = NULL, *monotonic = NULL;
699 size_t length;
700 const ParseFieldVec message_fields[] = {
701 PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, NULL),
702 PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, NULL),
703 };
704 int r;
705
706 assert(j);
707 assert(ret_display_ts);
708 assert(ret_boot_id);
709
710 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
711 r = parse_fieldv(data, length, message_fields, ELEMENTSOF(message_fields));
712 if (r < 0)
713 return r;
714
715 if (realtime && monotonic)
716 break;
717 }
718 if (r < 0)
719 return r;
720
721 (void) parse_display_timestamp(j, realtime, monotonic, ret_display_ts, ret_boot_id);
722
723 /* Restart all data before */
724 sd_journal_restart_data(j);
725 sd_journal_restart_unique(j);
726 sd_journal_restart_fields(j);
727
728 return 0;
729}
730
08ace05b
LP
731static int output_verbose(
732 FILE *f,
733 sd_journal *j,
734 OutputMode mode,
735 unsigned n_columns,
cc25a67e 736 OutputFlags flags,
2f063186 737 const Set *output_fields,
275e6be0 738 const size_t highlight[2],
63d2c755
YW
739 dual_timestamp *previous_display_ts, /* unused */
740 sd_id128_t *previous_boot_id) { /* unused */
08ace05b 741
86aa7ba4
LP
742 const void *data;
743 size_t length;
7fd1b19b 744 _cleanup_free_ char *cursor = NULL;
275e6be0 745 char buf[FORMAT_TIMESTAMP_MAX + 7];
63d2c755
YW
746 dual_timestamp display_ts;
747 sd_id128_t boot_id;
8924973a 748 const char *timestamp;
86aa7ba4
LP
749 int r;
750
08ace05b 751 assert(f);
86aa7ba4
LP
752 assert(j);
753
b1cc0822 754 (void) sd_journal_set_data_threshold(j, 0);
93b73b06 755
63d2c755
YW
756 r = get_display_timestamp(j, &display_ts, &boot_id);
757 if (IN_SET(r, -EBADMSG, -EADDRNOTAVAIL)) {
758 log_debug_errno(r, "Skipping message we can't read: %m");
759 return 0;
760 }
761 if (r < 0)
762 return log_error_errno(r, "Failed to get journal fields: %m");
763
764 if (!VALID_REALTIME(display_ts.realtime))
275e6be0 765 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid realtime timestamp available");
86aa7ba4
LP
766
767 r = sd_journal_get_cursor(j, &cursor);
f647962d
MS
768 if (r < 0)
769 return log_error_errno(r, "Failed to get cursor: %m");
86aa7ba4 770
63d2c755 771 timestamp = format_timestamp_style(buf, sizeof buf, display_ts.realtime,
7b3eb5c9 772 flags & OUTPUT_UTC ? TIMESTAMP_US_UTC : TIMESTAMP_US);
8b701558
LP
773 fprintf(f, "%s%s%s %s[%s]%s\n",
774 timestamp && (flags & OUTPUT_COLOR) ? ANSI_UNDERLINE : "",
8924973a 775 timestamp ?: "(no timestamp)",
8b701558
LP
776 timestamp && (flags & OUTPUT_COLOR) ? ANSI_NORMAL : "",
777 (flags & OUTPUT_COLOR) ? ANSI_GREY : "",
778 cursor,
779 (flags & OUTPUT_COLOR) ? ANSI_NORMAL : "");
86aa7ba4 780
a72b6353 781 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
79dc477f 782 _cleanup_free_ char *urlified = NULL;
8b701558
LP
783 const char *on = "", *off = "";
784 const char *c, *p = NULL;
785 size_t fieldlen, valuelen;
7ac4fa7e 786
31f7bf19 787 c = memchr(data, '=', length);
baaa35ad 788 if (!c)
805d67c5
YW
789 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
790
31f7bf19 791 fieldlen = c - (const char*) data;
805d67c5
YW
792 if (!journal_field_valid(data, fieldlen, true))
793 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
86aa7ba4 794
cc25a67e
LK
795 r = field_set_test(output_fields, data, fieldlen);
796 if (r < 0)
797 return r;
79dc477f 798 if (r == 0)
cc25a67e
LK
799 continue;
800
79dc477f 801 valuelen = length - 1 - fieldlen;
8b701558
LP
802 p = c + 1;
803
804 if (flags & OUTPUT_COLOR) {
805 if (startswith(data, "MESSAGE=")) {
806 on = ANSI_HIGHLIGHT;
807 off = ANSI_NORMAL;
808 } else if (startswith(data, "CONFIG_FILE=")) {
809 _cleanup_free_ char *u = NULL;
810
811 u = memdup_suffix0(p, valuelen);
812 if (!u)
813 return log_oom();
814
815 if (terminal_urlify_path(u, NULL, &urlified) >= 0) {
816 p = urlified;
817 valuelen = strlen(urlified);
818 }
79dc477f 819
8b701558
LP
820 } else if (startswith(data, "_")) {
821 /* Highlight trusted data as such */
822 on = ANSI_GREEN;
823 off = ANSI_NORMAL;
79dc477f 824 }
8b701558 825 }
7ac4fa7e 826
8980058a 827 if ((flags & OUTPUT_SHOW_ALL) ||
a6f0104a
ZJS
828 (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
829 && utf8_is_printable(data, length))) {
8b701558 830 fprintf(f, " %s%.*s=", on, (int) fieldlen, (const char*)data);
194da5ca 831 print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, false,
79dc477f 832 p, valuelen,
194da5ca 833 NULL);
7ac4fa7e 834 fputs(off, f);
2b59bf51 835 } else
7ac4fa7e
ZJS
836 fprintf(f, " %s%.*s=[%s blob data]%s\n",
837 on,
31f7bf19
ZJS
838 (int) (c - (const char*) data),
839 (const char*) data,
2b59bf51 840 FORMAT_BYTES(length - (c - (const char *) data) - 1),
7ac4fa7e 841 off);
86aa7ba4 842 }
a72b6353
ZJS
843 if (r < 0)
844 return r;
845
d4205751 846 if (flags & OUTPUT_CATALOG)
2108b567 847 (void) print_catalog(f, j);
d4205751 848
86aa7ba4
LP
849 return 0;
850}
851
08ace05b
LP
852static int output_export(
853 FILE *f,
854 sd_journal *j,
855 OutputMode mode,
856 unsigned n_columns,
cc25a67e 857 OutputFlags flags,
2f063186 858 const Set *output_fields,
275e6be0 859 const size_t highlight[2],
63d2c755
YW
860 dual_timestamp *previous_display_ts, /* unused */
861 sd_id128_t *previous_boot_id) { /* unused */
08ace05b 862
2bc70e2e 863 sd_id128_t journal_boot_id, seqnum_id;
7fd1b19b 864 _cleanup_free_ char *cursor = NULL;
2bc70e2e 865 usec_t monotonic, realtime;
86aa7ba4 866 const void *data;
2bc70e2e 867 uint64_t seqnum;
86aa7ba4 868 size_t length;
85b55869 869 int r;
86aa7ba4
LP
870
871 assert(j);
872
b1cc0822 873 (void) sd_journal_set_data_threshold(j, 0);
93b73b06 874
86aa7ba4 875 r = sd_journal_get_cursor(j, &cursor);
f647962d
MS
876 if (r < 0)
877 return log_error_errno(r, "Failed to get cursor: %m");
86aa7ba4 878
417cbcd6 879 r = sd_journal_get_realtime_usec(j, &realtime);
880 if (r < 0)
881 return log_error_errno(r, "Failed to get realtime timestamp: %m");
882
883 r = sd_journal_get_monotonic_usec(j, &monotonic, &journal_boot_id);
884 if (r < 0)
885 return log_error_errno(r, "Failed to get monotonic timestamp: %m");
886
2bc70e2e
LP
887 r = sd_journal_get_seqnum(j, &seqnum, &seqnum_id);
888 if (r < 0)
889 return log_error_errno(r, "Failed to get seqnum: %m");
890
08ace05b
LP
891 fprintf(f,
892 "__CURSOR=%s\n"
2bc70e2e
LP
893 "__REALTIME_TIMESTAMP=" USEC_FMT "\n"
894 "__MONOTONIC_TIMESTAMP=" USEC_FMT "\n"
895 "__SEQNUM=%" PRIu64 "\n"
896 "__SEQNUM_ID=%s\n"
08ace05b
LP
897 "_BOOT_ID=%s\n",
898 cursor,
417cbcd6 899 realtime,
900 monotonic,
2bc70e2e
LP
901 seqnum,
902 SD_ID128_TO_STRING(seqnum_id),
417cbcd6 903 SD_ID128_TO_STRING(journal_boot_id));
86aa7ba4 904
a72b6353 905 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
805d67c5 906 size_t fieldlen;
cc25a67e 907 const char *c;
86aa7ba4 908
0ab896b3
ZJS
909 /* We already printed the boot id from the data in the header, hence let's suppress it here */
910 if (memory_startswith(data, length, "_BOOT_ID="))
112301ae
LP
911 continue;
912
cc25a67e 913 c = memchr(data, '=', length);
baaa35ad 914 if (!c)
805d67c5 915 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
cc25a67e 916
805d67c5
YW
917 fieldlen = c - (const char*) data;
918 if (!journal_field_valid(data, fieldlen, true))
919 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
920
921 r = field_set_test(output_fields, data, fieldlen);
cc25a67e
LK
922 if (r < 0)
923 return r;
924 if (!r)
925 continue;
926
0ade5ffe
ZJS
927 if (utf8_is_printable_newline(data, length, false))
928 fwrite(data, length, 1, f);
929 else {
86aa7ba4
LP
930 uint64_t le64;
931
805d67c5 932 fwrite(data, fieldlen, 1, f);
08ace05b 933 fputc('\n', f);
805d67c5 934 le64 = htole64(length - fieldlen - 1);
08ace05b 935 fwrite(&le64, sizeof(le64), 1, f);
805d67c5 936 fwrite(c + 1, length - fieldlen - 1, 1, f);
0ade5ffe 937 }
86aa7ba4 938
08ace05b 939 fputc('\n', f);
86aa7ba4 940 }
a3b076f6 941 if (IN_SET(r, -EADDRNOTAVAIL, -EBADMSG)) {
4f5e1723
AW
942 log_debug_errno(r, "Skipping message we can't read: %m");
943 return 0;
944 }
86aa7ba4 945
a72b6353
ZJS
946 if (r < 0)
947 return r;
948
08ace05b 949 fputc('\n', f);
86aa7ba4
LP
950
951 return 0;
952}
953
240a5fe8 954void json_escape(
08ace05b
LP
955 FILE *f,
956 const char* p,
957 size_t l,
958 OutputFlags flags) {
959
960 assert(f);
961 assert(p);
962
93b73b06 963 if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
08ace05b
LP
964 fputs("null", f);
965
8980058a 966 else if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(p, l)) {
86aa7ba4
LP
967 bool not_first = false;
968
08ace05b 969 fputs("[ ", f);
86aa7ba4
LP
970
971 while (l > 0) {
972 if (not_first)
08ace05b 973 fprintf(f, ", %u", (uint8_t) *p);
86aa7ba4
LP
974 else {
975 not_first = true;
08ace05b 976 fprintf(f, "%u", (uint8_t) *p);
86aa7ba4
LP
977 }
978
979 p++;
980 l--;
981 }
982
08ace05b 983 fputs(" ]", f);
86aa7ba4 984 } else {
e768a4f0 985 fputc('"', f);
86aa7ba4
LP
986
987 while (l > 0) {
4c701096 988 if (IN_SET(*p, '"', '\\')) {
08ace05b
LP
989 fputc('\\', f);
990 fputc(*p, f);
31f7bf19
ZJS
991 } else if (*p == '\n')
992 fputs("\\n", f);
91a8a108
DM
993 else if ((uint8_t) *p < ' ')
994 fprintf(f, "\\u%04x", (uint8_t) *p);
08ace05b
LP
995 else
996 fputc(*p, f);
86aa7ba4
LP
997
998 p++;
999 l--;
1000 }
1001
e768a4f0 1002 fputc('"', f);
86aa7ba4
LP
1003 }
1004}
1005
a9e536a6 1006typedef struct JsonData {
8e044443 1007 JsonVariant* name;
ddd6875d 1008 JsonVariant* values;
a9e536a6 1009} JsonData;
8e044443 1010
ddd6875d 1011static JsonData* json_data_free(JsonData *d) {
ee9d31a6
DDM
1012 if (!d)
1013 return NULL;
1014
1015 json_variant_unref(d->name);
ddd6875d 1016 json_variant_unref(d->values);
ee9d31a6
DDM
1017
1018 return mfree(d);
1019}
1020
ddd6875d
YW
1021DEFINE_TRIVIAL_CLEANUP_FUNC(JsonData*, json_data_free);
1022
ee9d31a6
DDM
1023DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(json_data_hash_ops_free,
1024 char, string_hash_func, string_compare_func,
a9e536a6 1025 JsonData, json_data_free);
ee9d31a6 1026
8e044443
LP
1027static int update_json_data(
1028 Hashmap *h,
1029 OutputFlags flags,
1030 const char *name,
1031 const void *value,
1032 size_t size) {
1033
1034 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
ddd6875d 1035 JsonData *d;
8e044443
LP
1036 int r;
1037
8cc3cdac
LP
1038 assert(name);
1039 assert(value);
1040
1041 if (size == SIZE_MAX)
1042 size = strlen(value);
1043
8e044443
LP
1044 if (!(flags & OUTPUT_SHOW_ALL) && strlen(name) + 1 + size >= JSON_THRESHOLD)
1045 r = json_variant_new_null(&v);
1046 else if (utf8_is_printable(value, size))
1047 r = json_variant_new_stringn(&v, value, size);
1048 else
1049 r = json_variant_new_array_bytes(&v, value, size);
1050 if (r < 0)
1051 return log_error_errno(r, "Failed to allocate JSON data: %m");
1052
1053 d = hashmap_get(h, name);
1054 if (d) {
ddd6875d
YW
1055 r = json_variant_append_array(&d->values, v);
1056 if (r < 0)
1057 return log_error_errno(r, "Failed to append JSON value into array: %m");
1058 } else {
1059 _cleanup_(json_data_freep) JsonData *e = NULL;
8e044443 1060
ddd6875d
YW
1061 e = new0(JsonData, 1);
1062 if (!e)
8e044443
LP
1063 return log_oom();
1064
ddd6875d 1065 r = json_variant_new_string(&e->name, name);
8e044443
LP
1066 if (r < 0)
1067 return log_error_errno(r, "Failed to allocate JSON name variant: %m");
1068
ddd6875d
YW
1069 r = json_variant_append_array(&e->values, v);
1070 if (r < 0)
1071 return log_error_errno(r, "Failed to create JSON value array: %m");
8e044443 1072
ddd6875d
YW
1073 r = hashmap_put(h, json_variant_string(e->name), e);
1074 if (r < 0)
1075 return log_error_errno(r, "Failed to insert JSON data into hashmap: %m");
8e044443 1076
ddd6875d 1077 TAKE_PTR(e);
8e044443
LP
1078 }
1079
8e044443
LP
1080 return 0;
1081}
1082
1083static int update_json_data_split(
1084 Hashmap *h,
1085 OutputFlags flags,
2f063186 1086 const Set *output_fields,
8e044443
LP
1087 const void *data,
1088 size_t size) {
1089
805d67c5 1090 size_t fieldlen;
8e044443
LP
1091 const char *eq;
1092 char *name;
1093
1094 assert(h);
1095 assert(data || size == 0);
1096
1097 if (memory_startswith(data, size, "_BOOT_ID="))
1098 return 0;
1099
1100 eq = memchr(data, '=', MIN(size, JSON_THRESHOLD));
1101 if (!eq)
1102 return 0;
1103
805d67c5
YW
1104 fieldlen = eq - (const char*) data;
1105 if (!journal_field_valid(data, fieldlen, true))
1106 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
8e044443 1107
2f82562b 1108 name = strndupa_safe(data, fieldlen);
2f063186 1109 if (output_fields && !set_contains(output_fields, name))
8e044443
LP
1110 return 0;
1111
805d67c5 1112 return update_json_data(h, flags, name, eq + 1, size - fieldlen - 1);
8e044443
LP
1113}
1114
08ace05b
LP
1115static int output_json(
1116 FILE *f,
1117 sd_journal *j,
1118 OutputMode mode,
1119 unsigned n_columns,
cc25a67e 1120 OutputFlags flags,
2f063186 1121 const Set *output_fields,
275e6be0 1122 const size_t highlight[2],
63d2c755
YW
1123 dual_timestamp *previous_display_ts, /* unused */
1124 sd_id128_t *previous_boot_id) { /* unused */
08ace05b 1125
2bc70e2e 1126 char usecbuf[CONST_MAX(DECIMAL_STR_MAX(usec_t), DECIMAL_STR_MAX(uint64_t))];
8e044443 1127 _cleanup_(json_variant_unrefp) JsonVariant *object = NULL;
ee9d31a6 1128 _cleanup_hashmap_free_ Hashmap *h = NULL;
2bc70e2e 1129 sd_id128_t journal_boot_id, seqnum_id;
7fd1b19b 1130 _cleanup_free_ char *cursor = NULL;
2bc70e2e 1131 usec_t realtime, monotonic;
8e044443 1132 JsonVariant **array = NULL;
a9e536a6 1133 JsonData *d;
2bc70e2e 1134 uint64_t seqnum;
8e044443 1135 size_t n = 0;
8e044443 1136 int r;
86aa7ba4
LP
1137
1138 assert(j);
1139
8e044443 1140 (void) sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
93b73b06 1141
86aa7ba4 1142 r = sd_journal_get_cursor(j, &cursor);
f647962d
MS
1143 if (r < 0)
1144 return log_error_errno(r, "Failed to get cursor: %m");
86aa7ba4 1145
417cbcd6 1146 r = sd_journal_get_realtime_usec(j, &realtime);
1147 if (r < 0)
1148 return log_error_errno(r, "Failed to get realtime timestamp: %m");
1149
1150 r = sd_journal_get_monotonic_usec(j, &monotonic, &journal_boot_id);
1151 if (r < 0)
1152 return log_error_errno(r, "Failed to get monotonic timestamp: %m");
1153
2bc70e2e
LP
1154 r = sd_journal_get_seqnum(j, &seqnum, &seqnum_id);
1155 if (r < 0)
1156 return log_error_errno(r, "Failed to get seqnum: %m");
1157
ee9d31a6 1158 h = hashmap_new(&json_data_hash_ops_free);
d99ae53a 1159 if (!h)
b56d608e 1160 return log_oom();
d99ae53a 1161
8cc3cdac 1162 r = update_json_data(h, flags, "__CURSOR", cursor, SIZE_MAX);
a72b6353 1163 if (r < 0)
ee9d31a6 1164 return r;
d99ae53a 1165
417cbcd6 1166 xsprintf(usecbuf, USEC_FMT, realtime);
8cc3cdac 1167 r = update_json_data(h, flags, "__REALTIME_TIMESTAMP", usecbuf, SIZE_MAX);
8e044443 1168 if (r < 0)
ee9d31a6 1169 return r;
d99ae53a 1170
417cbcd6 1171 xsprintf(usecbuf, USEC_FMT, monotonic);
8cc3cdac 1172 r = update_json_data(h, flags, "__MONOTONIC_TIMESTAMP", usecbuf, SIZE_MAX);
8e044443 1173 if (r < 0)
ee9d31a6 1174 return r;
d99ae53a 1175
8cc3cdac 1176 r = update_json_data(h, flags, "_BOOT_ID", SD_ID128_TO_STRING(journal_boot_id), SIZE_MAX);
8e044443 1177 if (r < 0)
ee9d31a6 1178 return r;
d99ae53a 1179
2bc70e2e
LP
1180 xsprintf(usecbuf, USEC_FMT, seqnum);
1181 r = update_json_data(h, flags, "__SEQNUM", usecbuf, SIZE_MAX);
1182 if (r < 0)
ee9d31a6 1183 return r;
2bc70e2e
LP
1184
1185 r = update_json_data(h, flags, "__SEQNUM_ID", SD_ID128_TO_STRING(seqnum_id), SIZE_MAX);
1186 if (r < 0)
ee9d31a6 1187 return r;
2bc70e2e 1188
8e044443
LP
1189 for (;;) {
1190 const void *data;
1191 size_t size;
d99ae53a 1192
8e044443 1193 r = sd_journal_enumerate_data(j, &data, &size);
a3b076f6 1194 if (IN_SET(r, -EBADMSG, -EADDRNOTAVAIL)) {
8e044443 1195 log_debug_errno(r, "Skipping message we can't read: %m");
ee9d31a6 1196 return 0;
8e044443 1197 }
ee9d31a6
DDM
1198 if (r < 0)
1199 return log_error_errno(r, "Failed to read journal: %m");
8e044443
LP
1200 if (r == 0)
1201 break;
d99ae53a 1202
8e044443
LP
1203 r = update_json_data_split(h, flags, output_fields, data, size);
1204 if (r < 0)
ee9d31a6 1205 return r;
8e044443 1206 }
d99ae53a 1207
8e044443 1208 array = new(JsonVariant*, hashmap_size(h)*2);
ee9d31a6
DDM
1209 if (!array)
1210 return log_oom();
1211
1212 CLEANUP_ARRAY(array, n, json_variant_unref_many);
d99ae53a 1213
90e74a66 1214 HASHMAP_FOREACH(d, h) {
ddd6875d 1215 assert(json_variant_elements(d->values) > 0);
d99ae53a 1216
8e044443 1217 array[n++] = json_variant_ref(d->name);
d99ae53a 1218
ddd6875d
YW
1219 if (json_variant_elements(d->values) == 1)
1220 array[n++] = json_variant_ref(json_variant_by_index(d->values, 0));
1221 else
1222 array[n++] = json_variant_ref(d->values);
8e044443 1223 }
d99ae53a 1224
8e044443 1225 r = json_variant_new_object(&object, array, n);
ee9d31a6
DDM
1226 if (r < 0)
1227 return log_error_errno(r, "Failed to allocate JSON object: %m");
d99ae53a 1228
ee9d31a6
DDM
1229 return json_variant_dump(object,
1230 output_mode_to_json_format_flags(mode) |
1231 (FLAGS_SET(flags, OUTPUT_COLOR) ? JSON_FORMAT_COLOR : 0),
1232 f, NULL);
86aa7ba4
LP
1233}
1234
4d5d1bba 1235static int output_cat_field(
08ace05b
LP
1236 FILE *f,
1237 sd_journal *j,
cc25a67e 1238 OutputFlags flags,
e3eec1fd 1239 int prio,
4d5d1bba 1240 const char *field,
05c7d9bf 1241 const size_t highlight[2]) {
08ace05b 1242
e3eec1fd 1243 const char *color_on = "", *color_off = "", *highlight_on = "";
d3f2bdbf 1244 const void *data;
4d5d1bba 1245 size_t l, fl;
d3f2bdbf
LP
1246 int r;
1247
e3eec1fd
LP
1248 if (FLAGS_SET(flags, OUTPUT_COLOR))
1249 get_log_colors(prio, &color_on, &color_off, &highlight_on);
93b73b06 1250
4d5d1bba 1251 r = sd_journal_get_data(j, field, &data, &l);
a3b076f6 1252 if (IN_SET(r, -EBADMSG, -EADDRNOTAVAIL)) {
4f5e1723
AW
1253 log_debug_errno(r, "Skipping message we can't read: %m");
1254 return 0;
1255 }
4d5d1bba
LP
1256 if (r == -ENOENT) /* An entry without the requested field */
1257 return 0;
1258 if (r < 0)
8d3d7072 1259 return log_error_errno(r, "Failed to get data: %m");
d3f2bdbf 1260
4d5d1bba
LP
1261 fl = strlen(field);
1262 assert(l >= fl + 1);
1263 assert(((char*) data)[fl] == '=');
1264
1265 data = (const uint8_t*) data + fl + 1;
1266 l -= fl + 1;
d3f2bdbf 1267
e3eec1fd
LP
1268 if (FLAGS_SET(flags, OUTPUT_COLOR)) {
1269 if (highlight) {
1270 assert(highlight[0] <= highlight[1]);
1271 assert(highlight[1] <= l);
1272
1273 fputs(color_on, f);
1274 fwrite((const char*) data, 1, highlight[0], f);
1275 fputs(highlight_on, f);
1276 fwrite((const char*) data + highlight[0], 1, highlight[1] - highlight[0], f);
1277 fputs(color_on, f);
1278 fwrite((const char*) data + highlight[1], 1, l - highlight[1], f);
1279 fputs(color_off, f);
1280 } else {
1281 fputs(color_on, f);
1282 fwrite((const char*) data, 1, l, f);
1283 fputs(color_off, f);
1284 }
b4766d5f 1285 } else
4d5d1bba
LP
1286 fwrite((const char*) data, 1, l, f);
1287
08ace05b 1288 fputc('\n', f);
4d5d1bba
LP
1289 return 0;
1290}
1291
1292static int output_cat(
1293 FILE *f,
1294 sd_journal *j,
1295 OutputMode mode,
1296 unsigned n_columns,
1297 OutputFlags flags,
2f063186 1298 const Set *output_fields,
275e6be0 1299 const size_t highlight[2],
63d2c755
YW
1300 dual_timestamp *previous_display_ts, /* unused */
1301 sd_id128_t *previous_boot_id) { /* unused */
4d5d1bba 1302
e3eec1fd 1303 int r, prio = LOG_INFO;
4d5d1bba 1304 const char *field;
4d5d1bba
LP
1305
1306 assert(j);
1307 assert(f);
1308
1309 (void) sd_journal_set_data_threshold(j, 0);
1310
e3eec1fd
LP
1311 if (FLAGS_SET(flags, OUTPUT_COLOR)) {
1312 const void *data;
1313 size_t l;
1314
1315 /* Determine priority of this entry, so that we can color it nicely */
1316
1317 r = sd_journal_get_data(j, "PRIORITY", &data, &l);
a3b076f6 1318 if (IN_SET(r, -EBADMSG, -EADDRNOTAVAIL)) {
e3eec1fd
LP
1319 log_debug_errno(r, "Skipping message we can't read: %m");
1320 return 0;
1321 }
1322 if (r < 0) {
1323 if (r != -ENOENT)
1324 return log_error_errno(r, "Failed to get data: %m");
1325
1326 /* An entry without PRIORITY */
1327 } else if (l == 10 && memcmp(data, "PRIORITY=", 9) == 0) {
1328 char c = ((char*) data)[9];
1329
1330 if (c >= '0' && c <= '7')
1331 prio = c - '0';
1332 }
1333 }
1334
4d5d1bba 1335 if (set_isempty(output_fields))
e3eec1fd 1336 return output_cat_field(f, j, flags, prio, "MESSAGE", highlight);
4d5d1bba 1337
90e74a66 1338 SET_FOREACH(field, output_fields) {
e3eec1fd 1339 r = output_cat_field(f, j, flags, prio, field, streq(field, "MESSAGE") ? highlight : NULL);
4d5d1bba
LP
1340 if (r < 0)
1341 return r;
1342 }
d3f2bdbf
LP
1343
1344 return 0;
1345}
1346
4e30b87d 1347typedef int (*output_func_t)(
08ace05b 1348 FILE *f,
f2a3de01 1349 sd_journal *j,
08ace05b
LP
1350 OutputMode mode,
1351 unsigned n_columns,
cc25a67e 1352 OutputFlags flags,
2f063186 1353 const Set *output_fields,
275e6be0 1354 const size_t highlight[2],
63d2c755
YW
1355 dual_timestamp *previous_display_ts,
1356 sd_id128_t *previous_boot_id);
4e30b87d 1357
08ace05b 1358
4e30b87d 1359static output_func_t output_funcs[_OUTPUT_MODE_MAX] = {
71b7a6c4
ZJS
1360 [OUTPUT_SHORT] = output_short,
1361 [OUTPUT_SHORT_ISO] = output_short,
7e563bfc 1362 [OUTPUT_SHORT_ISO_PRECISE] = output_short,
71b7a6c4
ZJS
1363 [OUTPUT_SHORT_PRECISE] = output_short,
1364 [OUTPUT_SHORT_MONOTONIC] = output_short,
893bcd3d 1365 [OUTPUT_SHORT_DELTA] = output_short,
71b7a6c4
ZJS
1366 [OUTPUT_SHORT_UNIX] = output_short,
1367 [OUTPUT_SHORT_FULL] = output_short,
1368 [OUTPUT_VERBOSE] = output_verbose,
1369 [OUTPUT_EXPORT] = output_export,
1370 [OUTPUT_JSON] = output_json,
1371 [OUTPUT_JSON_PRETTY] = output_json,
1372 [OUTPUT_JSON_SSE] = output_json,
1373 [OUTPUT_JSON_SEQ] = output_json,
1374 [OUTPUT_CAT] = output_cat,
1375 [OUTPUT_WITH_UNIT] = output_short,
86aa7ba4
LP
1376};
1377
9b972c9a 1378int show_journal_entry(
08ace05b
LP
1379 FILE *f,
1380 sd_journal *j,
1381 OutputMode mode,
1382 unsigned n_columns,
94e0bd7d 1383 OutputFlags flags,
c5da14cd 1384 Set *output_fields,
05c7d9bf 1385 const size_t highlight[2],
893bcd3d 1386 bool *ellipsized,
4e30b87d 1387 dual_timestamp *previous_display_ts,
893bcd3d 1388 sd_id128_t *previous_boot_id) {
08ace05b 1389
be327321
ZJS
1390 int r;
1391
df50185b 1392 assert(mode >= 0);
86aa7ba4 1393 assert(mode < _OUTPUT_MODE_MAX);
4e30b87d 1394 assert(previous_display_ts);
893bcd3d 1395 assert(previous_boot_id);
86aa7ba4 1396
34a35ece
LP
1397 if (n_columns <= 0)
1398 n_columns = columns();
1399
4e30b87d
LP
1400 r = output_funcs[mode](
1401 f,
1402 j,
1403 mode,
1404 n_columns,
1405 flags,
1406 output_fields,
1407 highlight,
4e30b87d
LP
1408 previous_display_ts,
1409 previous_boot_id);
893bcd3d 1410
be327321 1411 if (ellipsized && r > 0)
94e0bd7d
ZJS
1412 *ellipsized = true;
1413
be327321 1414 return r;
86aa7ba4
LP
1415}
1416
ea6c2dd1
LP
1417static int maybe_print_begin_newline(FILE *f, OutputFlags *flags) {
1418 assert(f);
1419 assert(flags);
1420
1421 if (!(*flags & OUTPUT_BEGIN_NEWLINE))
1422 return 0;
1423
1424 /* Print a beginning new line if that's request, but only once
1425 * on the first line we print. */
1426
1427 fputc('\n', f);
1428 *flags &= ~OUTPUT_BEGIN_NEWLINE;
1429 return 0;
1430}
1431
889e3960
ZJS
1432int show_journal(
1433 FILE *f,
1434 sd_journal *j,
1435 OutputMode mode,
1436 unsigned n_columns,
1437 usec_t not_before,
1438 unsigned how_many,
1439 OutputFlags flags,
1440 bool *ellipsized) {
86aa7ba4 1441
86aa7ba4 1442 int r;
df50185b
LP
1443 unsigned line = 0;
1444 bool need_seek = false;
085d7120 1445 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
4e30b87d 1446 dual_timestamp previous_display_ts = DUAL_TIMESTAMP_NULL;
893bcd3d 1447 sd_id128_t previous_boot_id = SD_ID128_NULL;
86aa7ba4 1448
1a6c43e9 1449 assert(j);
df50185b
LP
1450 assert(mode >= 0);
1451 assert(mode < _OUTPUT_MODE_MAX);
1946b0bd 1452
f5fbe71d 1453 if (how_many == UINT_MAX)
889e3960
ZJS
1454 need_seek = true;
1455 else {
1456 /* Seek to end */
1457 r = sd_journal_seek_tail(j);
1458 if (r < 0)
1459 return log_error_errno(r, "Failed to seek to tail: %m");
86aa7ba4 1460
889e3960
ZJS
1461 r = sd_journal_previous_skip(j, how_many);
1462 if (r < 0)
1463 return log_error_errno(r, "Failed to skip previous: %m");
1464 }
86aa7ba4 1465
df50185b 1466 for (;;) {
27f31daf 1467 usec_t usec;
df50185b 1468
27f31daf
HD
1469 if (need_seek) {
1470 r = sd_journal_next(j);
1471 if (r < 0)
1472 return log_error_errno(r, "Failed to iterate through journal: %m");
1473 }
df50185b 1474
27f31daf
HD
1475 if (r == 0)
1476 break;
df50185b 1477
27f31daf 1478 need_seek = true;
df50185b 1479
27f31daf
HD
1480 if (not_before > 0) {
1481 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
df50185b 1482
27f31daf
HD
1483 /* -ESTALE is returned if the timestamp is not from this boot */
1484 if (r == -ESTALE)
1485 continue;
b1cc0822 1486 if (r < 0)
27f31daf 1487 return log_error_errno(r, "Failed to get journal time: %m");
df50185b 1488
27f31daf
HD
1489 if (usec < not_before)
1490 continue;
df50185b
LP
1491 }
1492
27f31daf
HD
1493 line++;
1494 maybe_print_begin_newline(f, &flags);
08984293 1495
4e30b87d
LP
1496 r = show_journal_entry(
1497 f,
1498 j,
1499 mode,
1500 n_columns,
1501 flags,
1502 /* output_fields= */ NULL,
1503 /* highlight= */ NULL,
1504 ellipsized,
1505 &previous_display_ts,
1506 &previous_boot_id);
27f31daf
HD
1507 if (r < 0)
1508 return r;
1509 }
08984293 1510
27f31daf
HD
1511 if (warn_cutoff && line < how_many && not_before > 0) {
1512 sd_id128_t boot_id;
1513 usec_t cutoff = 0;
08984293 1514
27f31daf 1515 /* Check whether the cutoff line is too early */
1a8f0ce6 1516
27f31daf
HD
1517 r = sd_id128_get_boot(&boot_id);
1518 if (r < 0)
1519 return log_error_errno(r, "Failed to get boot id: %m");
1a8f0ce6 1520
27f31daf
HD
1521 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
1522 if (r < 0)
1523 return log_error_errno(r, "Failed to get journal cutoff time: %m");
1a8f0ce6 1524
27f31daf
HD
1525 if (r > 0 && not_before < cutoff) {
1526 maybe_print_begin_newline(f, &flags);
08984293 1527
27f31daf
HD
1528 /* If we logged *something* and no permission error happened, than we can reliably
1529 * emit the warning about rotation. If we didn't log anything and access errors
1530 * happened, emit hint about permissions. Otherwise, give a generic message, since we
1531 * can't diagnose the issue. */
08984293 1532
27f31daf 1533 bool noaccess = journal_access_blocked(j);
86aa7ba4 1534
27f31daf 1535 if (line == 0 && noaccess)
17e90001 1536 fprintf(f, "Warning: some journal files were not opened due to insufficient permissions.\n");
27f31daf 1537 else if (!noaccess)
71311efe 1538 fprintf(f, "Notice: journal has been rotated since unit was started, output may be incomplete.\n");
27f31daf
HD
1539 else
1540 fprintf(f, "Warning: journal has been rotated since unit was started and some journal "
1541 "files were not opened due to insufficient permissions, output may be incomplete.\n");
1542 }
df50185b 1543
27f31daf 1544 warn_cutoff = false;
86aa7ba4
LP
1545 }
1546
b56d608e 1547 return 0;
1a6c43e9
MT
1548}
1549
886a64fe 1550int add_matches_for_unit(sd_journal *j, const char *unit) {
1a6c43e9
MT
1551 int r;
1552
886a64fe 1553 assert(j);
1a6c43e9
MT
1554 assert(unit);
1555
bcd558f1 1556 (void) (
886a64fe 1557 /* Look for messages from the service itself */
bcd558f1 1558 (r = journal_add_match_pair(j, "_SYSTEMD_UNIT", unit)) ||
886a64fe
ZJS
1559
1560 /* Look for coredumps of the service */
1561 (r = sd_journal_add_disjunction(j)) ||
e1771c8e
LP
1562 (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", SIZE_MAX)) ||
1563 (r = sd_journal_add_match(j, "_UID=0", SIZE_MAX)) ||
bcd558f1 1564 (r = journal_add_match_pair(j, "COREDUMP_UNIT", unit)) ||
886a64fe
ZJS
1565
1566 /* Look for messages from PID 1 about this service */
1567 (r = sd_journal_add_disjunction(j)) ||
e1771c8e 1568 (r = sd_journal_add_match(j, "_PID=1", SIZE_MAX)) ||
bcd558f1 1569 (r = journal_add_match_pair(j, "UNIT", unit)) ||
2d0b2e87
ZJS
1570
1571 /* Look for messages from authorized daemons about this service */
1572 (r = sd_journal_add_disjunction(j)) ||
e1771c8e 1573 (r = sd_journal_add_match(j, "_UID=0", SIZE_MAX)) ||
bcd558f1 1574 (r = journal_add_match_pair(j, "OBJECT_SYSTEMD_UNIT", unit))
886a64fe 1575 );
2d0b2e87 1576
bcd558f1 1577 if (r == 0 && endswith(unit, ".slice"))
69ae3ee0 1578 /* Show all messages belonging to a slice */
bcd558f1 1579 (void) (
69ae3ee0 1580 (r = sd_journal_add_disjunction(j)) ||
bcd558f1
YW
1581 (r = journal_add_match_pair(j, "_SYSTEMD_SLICE", unit))
1582 );
69ae3ee0 1583
886a64fe
ZJS
1584 return r;
1585}
1a6c43e9 1586
886a64fe
ZJS
1587int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
1588 int r;
1a6c43e9 1589
886a64fe
ZJS
1590 assert(j);
1591 assert(unit);
1a6c43e9 1592
886a64fe
ZJS
1593 (void) (
1594 /* Look for messages from the user service itself */
bcd558f1
YW
1595 (r = journal_add_match_pair(j, "_SYSTEMD_USER_UNIT", unit)) ||
1596 (r = journal_add_matchf(j, "_UID="UID_FMT, uid)) ||
886a64fe
ZJS
1597
1598 /* Look for messages from systemd about this service */
1599 (r = sd_journal_add_disjunction(j)) ||
bcd558f1
YW
1600 (r = journal_add_match_pair(j, "USER_UNIT", unit)) ||
1601 (r = journal_add_matchf(j, "_UID="UID_FMT, uid)) ||
886a64fe
ZJS
1602
1603 /* Look for coredumps of the service */
1604 (r = sd_journal_add_disjunction(j)) ||
bcd558f1
YW
1605 (r = journal_add_match_pair(j, "COREDUMP_USER_UNIT", unit)) ||
1606 (r = journal_add_matchf(j, "_UID="UID_FMT, uid)) ||
e1771c8e 1607 (r = sd_journal_add_match(j, "_UID=0", SIZE_MAX)) ||
2d0b2e87
ZJS
1608
1609 /* Look for messages from authorized daemons about this service */
1610 (r = sd_journal_add_disjunction(j)) ||
bcd558f1
YW
1611 (r = journal_add_match_pair(j, "OBJECT_SYSTEMD_USER_UNIT", unit)) ||
1612 (r = journal_add_matchf(j, "_UID="UID_FMT, uid)) ||
e1771c8e 1613 (r = sd_journal_add_match(j, "_UID=0", SIZE_MAX))
886a64fe 1614 );
69ae3ee0 1615
bcd558f1 1616 if (r == 0 && endswith(unit, ".slice"))
69ae3ee0 1617 /* Show all messages belonging to a slice */
bcd558f1 1618 (void) (
69ae3ee0 1619 (r = sd_journal_add_disjunction(j)) ||
bcd558f1
YW
1620 (r = journal_add_match_pair(j, "_SYSTEMD_USER_SLICE", unit)) ||
1621 (r = journal_add_matchf(j, "_UID="UID_FMT, uid))
1622 );
69ae3ee0 1623
1a6c43e9
MT
1624 return r;
1625}
1626
c93d3c05 1627int add_match_boot_id(sd_journal *j, sd_id128_t id) {
b6741478
LP
1628 int r;
1629
c93d3c05 1630 assert(j);
b6741478 1631
0aad30d4
YW
1632 if (sd_id128_is_null(id)) {
1633 r = sd_id128_get_boot(&id);
6c767d1e 1634 if (r < 0)
0aad30d4 1635 return log_error_errno(r, "Failed to get boot ID: %m");
b6741478
LP
1636 }
1637
0aad30d4 1638 r = journal_add_match_pair(j, "_BOOT_ID", SD_ID128_TO_STRING(id));
b6741478 1639 if (r < 0)
0aad30d4 1640 return log_error_errno(r, "Failed to add match: %m");
b6741478
LP
1641
1642 return 0;
1643}
1644
1645int add_match_this_boot(sd_journal *j, const char *machine) {
5ec76417
ZJS
1646 sd_id128_t boot_id;
1647 int r;
1648
1649 assert(j);
1650
8e976dc9
YW
1651 r = id128_get_boot_for_machine(machine, &boot_id);
1652 if (r < 0)
1653 return log_error_errno(r, "Failed to get boot ID%s%s: %m",
1654 isempty(machine) ? "" : " of container ", machine);
5ec76417 1655
c93d3c05 1656 r = add_match_boot_id(j, boot_id);
f647962d 1657 if (r < 0)
0aad30d4 1658 return r;
5ec76417
ZJS
1659
1660 r = sd_journal_add_conjunction(j);
1661 if (r < 0)
b56d608e 1662 return log_error_errno(r, "Failed to add conjunction: %m");
5ec76417
ZJS
1663
1664 return 0;
1665}
1666
886a64fe 1667int show_journal_by_unit(
1a6c43e9
MT
1668 FILE *f,
1669 const char *unit,
d93dda3a 1670 const char *log_namespace,
1a6c43e9
MT
1671 OutputMode mode,
1672 unsigned n_columns,
1673 usec_t not_before,
1674 unsigned how_many,
1675 uid_t uid,
886a64fe 1676 OutputFlags flags,
3c756001
LP
1677 int journal_open_flags,
1678 bool system_unit,
94e0bd7d 1679 bool *ellipsized) {
1a6c43e9 1680
4afd3348 1681 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
1a6c43e9
MT
1682 int r;
1683
1684 assert(mode >= 0);
1685 assert(mode < _OUTPUT_MODE_MAX);
1686 assert(unit);
1687
1a6c43e9
MT
1688 if (how_many <= 0)
1689 return 0;
1690
f7f062bf
YW
1691 r = sd_journal_open_namespace(&j, log_namespace,
1692 journal_open_flags |
1693 SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE |
1694 SD_JOURNAL_ASSUME_IMMUTABLE);
f9045468 1695 if (r < 0)
b56d608e 1696 return log_error_errno(r, "Failed to open journal: %m");
f9045468 1697
3c756001 1698 if (system_unit)
886a64fe
ZJS
1699 r = add_matches_for_unit(j, unit);
1700 else
1701 r = add_matches_for_user_unit(j, unit, uid);
1a6c43e9 1702 if (r < 0)
b56d608e 1703 return log_error_errno(r, "Failed to add unit matches: %m");
1a6c43e9 1704
f8202704
VC
1705 r = sd_journal_add_conjunction(j);
1706 if (r < 0)
1707 return log_error_errno(r, "Failed to add conjunction: %m");
1708
1709 r = add_match_this_boot(j, NULL);
1710 if (r < 0)
1711 return r;
1712
f1d34068 1713 if (DEBUG_LOGGING) {
c2b2df60 1714 _cleanup_free_ char *filter = NULL;
4ad16808
ZJS
1715
1716 filter = journal_make_match_string(j);
b56d608e
LP
1717 if (!filter)
1718 return log_oom();
1719
4ad16808
ZJS
1720 log_debug("Journal filter: %s", filter);
1721 }
5ec76417 1722
94e0bd7d 1723 return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
86aa7ba4 1724}
8081939d
YW
1725
1726static int discover_next_boot(
1727 sd_journal *j,
1728 sd_id128_t previous_boot_id,
1729 bool advance_older,
1730 BootId *ret) {
1731
185e2016 1732 _cleanup_set_free_ Set *broken_ids = NULL;
8081939d
YW
1733 int r;
1734
1735 assert(j);
1736 assert(ret);
1737
1738 /* We expect the journal to be on the last position of a boot
1739 * (in relation to the direction we are going), so that the next
1740 * invocation of sd_journal_next/previous will be from a different
1741 * boot. We then collect any information we desire and then jump
1742 * to the last location of the new boot by using a _BOOT_ID match
1743 * coming from the other journal direction. */
1744
1745 /* Make sure we aren't restricted by any _BOOT_ID matches, so that
1746 * we can actually advance to a *different* boot. */
1747 sd_journal_flush_matches(j);
1748
185e2016
YW
1749 for (;;) {
1750 sd_id128_t *id_dup;
1751 BootId boot;
1752
0141b214 1753 r = sd_journal_step_one(j, !advance_older);
8081939d
YW
1754 if (r < 0)
1755 return r;
0141b214 1756 if (r == 0) {
8081939d
YW
1757 *ret = (BootId) {};
1758 return 0; /* End of journal, yay. */
1759 }
1760
1761 r = sd_journal_get_monotonic_usec(j, NULL, &boot.id);
1762 if (r < 0)
1763 return r;
1764
1765 /* We iterate through this in a loop, until the boot ID differs from the previous one. Note that
1766 * normally, this will only require a single iteration, as we moved to the last entry of the previous
1767 * boot entry already. However, it might happen that the per-journal-field entry arrays are less
1768 * complete than the main entry array, and hence might reference an entry that's not actually the last
1769 * one of the boot ID as last one. Let's hence use the per-field array is initial seek position to
1770 * speed things up, but let's not trust that it is complete, and hence, manually advance as
1771 * necessary. */
1772
185e2016
YW
1773 if (!sd_id128_is_null(previous_boot_id) && sd_id128_equal(boot.id, previous_boot_id))
1774 continue;
8081939d 1775
185e2016
YW
1776 if (set_contains(broken_ids, &boot.id))
1777 continue;
8081939d 1778
185e2016
YW
1779 /* Yay, we found a new boot ID from the entry object. Let's check there exist corresponding
1780 * entries matching with the _BOOT_ID= data. */
8081939d 1781
185e2016
YW
1782 r = add_match_boot_id(j, boot.id);
1783 if (r < 0)
1784 return r;
8081939d 1785
185e2016
YW
1786 /* First, seek to the first (or the last when we are going upwards) occurrence of this boot ID.
1787 * You may think this is redundant. Yes, that's redundant unless the journal is corrupted.
1788 * But when the journal is corrupted, especially, badly 'truncated', then the below may fail.
1789 * See https://github.com/systemd/systemd/pull/29334#issuecomment-1736567951. */
1790 if (advance_older)
1791 r = sd_journal_seek_tail(j);
1792 else
1793 r = sd_journal_seek_head(j);
1794 if (r < 0)
1795 return r;
8081939d 1796
185e2016
YW
1797 r = sd_journal_step_one(j, 0);
1798 if (r < 0)
1799 return r;
1800 if (r == 0) {
1801 log_debug("Whoopsie! We found a boot ID %s but can't read its first entry. "
1802 "The journal seems to be corrupted. Ignoring the boot ID.",
1803 SD_ID128_TO_STRING(boot.id));
1804 goto try_again;
1805 }
8081939d 1806
185e2016
YW
1807 r = sd_journal_get_realtime_usec(j, &boot.first_usec);
1808 if (r < 0)
1809 return r;
1810
1811 /* Next, seek to the last occurrence of this boot ID. */
1812 if (advance_older)
1813 r = sd_journal_seek_head(j);
1814 else
1815 r = sd_journal_seek_tail(j);
1816 if (r < 0)
1817 return r;
1818
1819 r = sd_journal_step_one(j, 0);
1820 if (r < 0)
1821 return r;
1822 if (r == 0) {
1823 log_debug("Whoopsie! We found a boot ID %s but can't read its last entry. "
1824 "The journal seems to be corrupted. Ignoring the boot ID.",
1825 SD_ID128_TO_STRING(boot.id));
1826 goto try_again;
1827 }
1828
1829 r = sd_journal_get_realtime_usec(j, &boot.last_usec);
1830 if (r < 0)
1831 return r;
1832
1833 sd_journal_flush_matches(j);
1834 *ret = boot;
1835 return 1;
1836
1837 try_again:
1838 /* Save the bad boot ID. */
1839 id_dup = newdup(sd_id128_t, &boot.id, 1);
1840 if (!id_dup)
1841 return -ENOMEM;
1842
1843 r = set_ensure_consume(&broken_ids, &id128_hash_ops_free, id_dup);
1844 if (r < 0)
1845 return r;
1846
1847 /* Move to the previous position again. */
1848 sd_journal_flush_matches(j);
1849
1850 if (!sd_id128_is_null(previous_boot_id)) {
1851 r = add_match_boot_id(j, previous_boot_id);
1852 if (r < 0)
1853 return r;
1854 }
1855
1856 if (advance_older)
1857 r = sd_journal_seek_head(j);
1858 else
1859 r = sd_journal_seek_tail(j);
1860 if (r < 0)
1861 return r;
1862
1863 r = sd_journal_step_one(j, 0);
1864 if (r < 0)
1865 return r;
1866 if (r == 0)
1867 return log_debug_errno(SYNTHETIC_ERRNO(ENODATA),
1868 "Whoopsie! Cannot seek to the last entry of boot %s.",
1869 SD_ID128_TO_STRING(previous_boot_id));
1870
1871 sd_journal_flush_matches(j);
1872 }
8081939d
YW
1873}
1874
1875int journal_find_boot_by_id(sd_journal *j, sd_id128_t boot_id) {
1876 int r;
1877
1878 assert(j);
1879 assert(!sd_id128_is_null(boot_id));
1880
1881 sd_journal_flush_matches(j);
1882
1883 r = add_match_boot_id(j, boot_id);
1884 if (r < 0)
1885 return r;
1886
1887 r = sd_journal_seek_head(j); /* seek to oldest */
1888 if (r < 0)
1889 return r;
1890
1891 r = sd_journal_next(j); /* read the oldest entry */
1892 if (r < 0)
1893 return r;
1894
1895 /* At this point the read pointer is positioned at the oldest occurrence of the reference boot ID.
1896 * After flushing the matches, one more invocation of _previous() will hence place us at the
1897 * following entry, which must then have an older boot ID */
1898
1899 sd_journal_flush_matches(j);
1900 return r > 0;
1901}
1902
1903int journal_find_boot_by_offset(sd_journal *j, int offset, sd_id128_t *ret) {
1904 bool advance_older;
1905 int r;
1906
1907 assert(j);
1908 assert(ret);
1909
1910 /* Adjust for the asymmetry that offset 0 is the last (and current) boot, while 1 is considered the
1911 * (chronological) first boot in the journal. */
1912 advance_older = offset <= 0;
1913
1914 if (advance_older)
1915 r = sd_journal_seek_tail(j); /* seek to newest */
1916 else
1917 r = sd_journal_seek_head(j); /* seek to oldest */
1918 if (r < 0)
1919 return r;
1920
1921 /* No sd_journal_next()/_previous() here.
1922 *
1923 * At this point the read pointer is positioned after the newest/before the oldest entry in the whole
1924 * journal. The next invocation of _previous()/_next() will hence position us at the newest/oldest
1925 * entry we have. */
1926
1927 sd_id128_t boot_id = SD_ID128_NULL;
1928 for (int off = !advance_older; ; off += advance_older ? -1 : 1) {
1929 BootId boot;
1930
1931 r = discover_next_boot(j, boot_id, advance_older, &boot);
1932 if (r < 0)
1933 return r;
1934 if (r == 0) {
1935 *ret = SD_ID128_NULL;
1936 return false;
1937 }
1938
1939 boot_id = boot.id;
1940 log_debug("Found boot ID %s by offset %i", SD_ID128_TO_STRING(boot_id), off);
1941
1942 if (off == offset)
1943 break;
1944 }
1945
1946 *ret = boot_id;
1947 return true;
1948}
1949
1950int journal_get_boots(sd_journal *j, BootId **ret_boots, size_t *ret_n_boots) {
1951 _cleanup_free_ BootId *boots = NULL;
1952 size_t n_boots = 0;
1953 int r;
1954
1955 assert(j);
1956 assert(ret_boots);
1957 assert(ret_n_boots);
1958
1959 r = sd_journal_seek_head(j); /* seek to oldest */
1960 if (r < 0)
1961 return r;
1962
1963 /* No sd_journal_next()/_previous() here.
1964 *
1965 * At this point the read pointer is positioned before the oldest entry in the whole journal. The
1966 * next invocation of _next() will hence position us at the oldest entry we have. */
1967
1968 sd_id128_t previous_boot_id = SD_ID128_NULL;
1969 for (;;) {
1970 BootId boot;
1971
1972 r = discover_next_boot(j, previous_boot_id, /* advance_older = */ false, &boot);
1973 if (r < 0)
1974 return r;
1975 if (r == 0)
1976 break;
1977
1978 previous_boot_id = boot.id;
1979
1980 FOREACH_ARRAY(i, boots, n_boots)
1981 if (sd_id128_equal(i->id, boot.id))
1982 /* The boot id is already stored, something wrong with the journal files.
1983 * Exiting as otherwise this problem would cause an infinite loop. */
1e8c0c67 1984 goto finish;
8081939d
YW
1985
1986 if (!GREEDY_REALLOC(boots, n_boots + 1))
1987 return -ENOMEM;
1988
1989 boots[n_boots++] = boot;
1990 }
1991
1e8c0c67 1992 finish:
8081939d
YW
1993 *ret_boots = TAKE_PTR(boots);
1994 *ret_n_boots = n_boots;
1995 return n_boots > 0;
1996}