]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/logs-show.c
nspawn: generate a new randomized boot ID for each container
[thirdparty/systemd.git] / src / shared / logs-show.c
CommitLineData
86aa7ba4
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
86aa7ba4
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
86aa7ba4 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
86aa7ba4
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <time.h>
23#include <assert.h>
24#include <errno.h>
df50185b 25#include <sys/poll.h>
55d7bfc1 26#include <string.h>
86aa7ba4
LP
27
28#include "logs-show.h"
29#include "log.h"
30#include "util.h"
ba961854 31#include "utf8.h"
86aa7ba4
LP
32
33#define PRINT_THRESHOLD 128
34
55d7bfc1
LP
35static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
36 size_t fl, nl;
37 void *buf;
38
39 assert(data);
40 assert(field);
41 assert(target);
42 assert(target_size);
43
44 fl = strlen(field);
45 if (length < fl)
46 return 0;
47
48 if (memcmp(data, field, fl))
49 return 0;
50
51 nl = length - fl;
bf967366 52 buf = malloc(nl+1);
0d0f0c50
SL
53 if (!buf)
54 return log_oom();
55d7bfc1 55
46b0d922
LP
56 memcpy(buf, (const char*) data + fl, nl);
57 ((char*)buf)[nl] = 0;
58
6c1e6b98 59 free(*target);
55d7bfc1
LP
60 *target = buf;
61 *target_size = nl;
62
63 return 1;
64}
65
66static bool shall_print(bool show_all, char *p, size_t l) {
67 if (show_all)
68 return true;
69
70 if (l > PRINT_THRESHOLD)
71 return false;
72
ba961854 73 if (!utf8_is_printable_n(p, l))
55d7bfc1
LP
74 return false;
75
76 return true;
77}
78
25277cd7
ZJS
79static int output_short(sd_journal *j, unsigned line, unsigned n_columns,
80 OutputFlags flags) {
86aa7ba4 81 int r;
86aa7ba4
LP
82 const void *data;
83 size_t length;
84 size_t n = 0;
49826187
LP
85 char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
86 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;
87 int p = LOG_INFO;
88 const char *color_on = "", *color_off = "";
86aa7ba4
LP
89
90 assert(j);
91
55d7bfc1
LP
92 SD_JOURNAL_FOREACH_DATA(j, data, length) {
93
49826187
LP
94 r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
95 if (r < 0)
96 goto finish;
97 else if (r > 0)
98 continue;
99
55d7bfc1
LP
100 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
101 if (r < 0)
102 goto finish;
103 else if (r > 0)
104 continue;
105
4cd9a9d9 106 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
55d7bfc1
LP
107 if (r < 0)
108 goto finish;
109 else if (r > 0)
110 continue;
111
112 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
113 if (r < 0)
114 goto finish;
115 else if (r > 0)
116 continue;
117
118 r = parse_field(data, length, "_PID=", &pid, &pid_len);
119 if (r < 0)
120 goto finish;
121 else if (r > 0)
122 continue;
123
6c1e6b98
LP
124 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
125 if (r < 0)
126 goto finish;
127 else if (r > 0)
128 continue;
129
bf967366
LP
130 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
131 if (r < 0)
132 goto finish;
133 else if (r > 0)
134 continue;
135
136 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
137 if (r < 0)
138 goto finish;
139 else if (r > 0)
140 continue;
141
55d7bfc1
LP
142 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
143 if (r < 0)
144 goto finish;
145 }
146
147 if (!message) {
148 r = 0;
149 goto finish;
150 }
151
49826187
LP
152 if (priority_len == 1 && *priority >= '0' && *priority <= '7')
153 p = *priority - '0';
154
25277cd7 155 if (flags & OUTPUT_MONOTONIC_MODE) {
67a12205 156 uint64_t t;
3ebcdf8c
LP
157 sd_id128_t boot_id;
158
bf967366
LP
159 r = -ENOENT;
160
161 if (monotonic)
162 r = safe_atou64(monotonic, &t);
163
164 if (r < 0)
3ebcdf8c 165 r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
86aa7ba4 166
3ebcdf8c
LP
167 if (r < 0) {
168 log_error("Failed to get monotonic: %s", strerror(-r));
169 goto finish;
67a12205
LP
170 }
171
3ebcdf8c
LP
172 printf("[%5llu.%06llu]",
173 (unsigned long long) (t / USEC_PER_SEC),
174 (unsigned long long) (t % USEC_PER_SEC));
175
176 n += 1 + 5 + 1 + 6 + 1;
177
67a12205
LP
178 } else {
179 char buf[64];
bf967366 180 uint64_t x;
67a12205
LP
181 time_t t;
182 struct tm tm;
731a676c 183
bf967366
LP
184 r = -ENOENT;
185
186 if (realtime)
187 r = safe_atou64(realtime, &x);
188
189 if (r < 0)
190 r = sd_journal_get_realtime_usec(j, &x);
67a12205 191
67a12205
LP
192 if (r < 0) {
193 log_error("Failed to get realtime: %s", strerror(-r));
194 goto finish;
195 }
196
bf967366 197 t = (time_t) (x / USEC_PER_SEC);
67a12205
LP
198 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
199 log_error("Failed to format time.");
200 goto finish;
201 }
202
203 fputs(buf, stdout);
204 n += strlen(buf);
205 }
86aa7ba4 206
25277cd7
ZJS
207 if (hostname && shall_print(flags & OUTPUT_SHOW_ALL,
208 hostname, hostname_len)) {
55d7bfc1
LP
209 printf(" %.*s", (int) hostname_len, hostname);
210 n += hostname_len + 1;
211 }
212
25277cd7
ZJS
213 if (identifier && shall_print(flags & OUTPUT_SHOW_ALL,
214 identifier, identifier_len)) {
4cd9a9d9
LP
215 printf(" %.*s", (int) identifier_len, identifier);
216 n += identifier_len + 1;
25277cd7
ZJS
217 } else if (comm && shall_print(flags & OUTPUT_SHOW_ALL,
218 comm, comm_len)) {
55d7bfc1
LP
219 printf(" %.*s", (int) comm_len, comm);
220 n += comm_len + 1;
b5936820
LP
221 } else
222 putchar(' ');
86aa7ba4 223
25277cd7 224 if (pid && shall_print(flags & OUTPUT_SHOW_ALL, pid, pid_len)) {
55d7bfc1
LP
225 printf("[%.*s]", (int) pid_len, pid);
226 n += pid_len + 2;
25277cd7
ZJS
227 } else if (fake_pid && shall_print(flags & OUTPUT_SHOW_ALL,
228 fake_pid, fake_pid_len)) {
6c1e6b98
LP
229 printf("[%.*s]", (int) fake_pid_len, fake_pid);
230 n += fake_pid_len + 2;
86aa7ba4
LP
231 }
232
49826187
LP
233 if (flags & OUTPUT_COLOR) {
234 if (p <= LOG_ERR) {
235 color_on = ANSI_HIGHLIGHT_RED_ON;
236 color_off = ANSI_HIGHLIGHT_OFF;
237 } else if (p <= LOG_NOTICE) {
238 color_on = ANSI_HIGHLIGHT_ON;
239 color_off = ANSI_HIGHLIGHT_OFF;
240 }
241 }
242
25277cd7 243 if (flags & OUTPUT_SHOW_ALL)
49826187 244 printf(": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
ba961854 245 else if (!utf8_is_printable_n(message, message_len)) {
e6acda19
LP
246 char bytes[FORMAT_BYTES_MAX];
247 printf(": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
92a1fd9e 248 } else if ((flags & OUTPUT_FULL_WIDTH) ||
193556b6 249 (message_len + n + 1 < n_columns))
49826187 250 printf(": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
b61a4660 251 else if (n < n_columns && n_columns - n - 2 >= 3) {
55d7bfc1
LP
252 char *e;
253
34a35ece 254 e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
55d7bfc1
LP
255
256 if (!e)
49826187 257 printf(": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
55d7bfc1 258 else
49826187 259 printf(": %s%s%s\n", color_on, e, color_off);
55d7bfc1
LP
260
261 free(e);
262 } else
263 fputs("\n", stdout);
264
265 r = 0;
266
267finish:
268 free(hostname);
4cd9a9d9 269 free(identifier);
55d7bfc1
LP
270 free(comm);
271 free(pid);
6c1e6b98 272 free(fake_pid);
55d7bfc1 273 free(message);
bf967366
LP
274 free(monotonic);
275 free(realtime);
64825d3c 276 free(priority);
86aa7ba4 277
55d7bfc1 278 return r;
86aa7ba4
LP
279}
280
25277cd7
ZJS
281static int output_short_realtime(sd_journal *j, unsigned line,
282 unsigned n_columns, OutputFlags flags) {
283 return output_short(j, line, n_columns, flags & ~OUTPUT_MONOTONIC_MODE);
67a12205
LP
284}
285
25277cd7
ZJS
286static int output_short_monotonic(sd_journal *j, unsigned line,
287 unsigned n_columns, OutputFlags flags) {
288 return output_short(j, line, n_columns, flags | OUTPUT_MONOTONIC_MODE);
67a12205
LP
289}
290
25277cd7
ZJS
291static int output_verbose(sd_journal *j, unsigned line,
292 unsigned n_columns, OutputFlags flags) {
86aa7ba4
LP
293 const void *data;
294 size_t length;
295 char *cursor;
296 uint64_t realtime;
297 char ts[FORMAT_TIMESTAMP_MAX];
298 int r;
299
300 assert(j);
301
302 r = sd_journal_get_realtime_usec(j, &realtime);
303 if (r < 0) {
304 log_error("Failed to get realtime timestamp: %s", strerror(-r));
305 return r;
306 }
307
308 r = sd_journal_get_cursor(j, &cursor);
309 if (r < 0) {
310 log_error("Failed to get cursor: %s", strerror(-r));
311 return r;
312 }
313
314 printf("%s [%s]\n",
315 format_timestamp(ts, sizeof(ts), realtime),
316 cursor);
317
318 free(cursor);
319
320 SD_JOURNAL_FOREACH_DATA(j, data, length) {
25277cd7 321 if (!(flags & OUTPUT_SHOW_ALL) && (length > PRINT_THRESHOLD ||
ba961854 322 !utf8_is_printable_n(data, length))) {
86aa7ba4 323 const char *c;
e6acda19 324 char bytes[FORMAT_BYTES_MAX];
86aa7ba4
LP
325
326 c = memchr(data, '=', length);
327 if (!c) {
328 log_error("Invalid field.");
329 return -EINVAL;
330 }
331
e6acda19 332 printf("\t%.*s=[%s blob data]\n",
86aa7ba4 333 (int) (c - (const char*) data),
e6acda19
LP
334 (const char*) data,
335 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
86aa7ba4
LP
336 } else
337 printf("\t%.*s\n", (int) length, (const char*) data);
338 }
339
340 return 0;
341}
342
25277cd7
ZJS
343static int output_export(sd_journal *j, unsigned line,
344 unsigned n_columns, OutputFlags flags) {
86aa7ba4
LP
345 sd_id128_t boot_id;
346 char sid[33];
347 int r;
348 usec_t realtime, monotonic;
349 char *cursor;
350 const void *data;
351 size_t length;
352
353 assert(j);
354
355 r = sd_journal_get_realtime_usec(j, &realtime);
356 if (r < 0) {
357 log_error("Failed to get realtime timestamp: %s", strerror(-r));
358 return r;
359 }
360
361 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
362 if (r < 0) {
363 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
364 return r;
365 }
366
367 r = sd_journal_get_cursor(j, &cursor);
368 if (r < 0) {
369 log_error("Failed to get cursor: %s", strerror(-r));
370 return r;
371 }
372
ba8d3790 373 printf("__CURSOR=%s\n"
ffa16db0
LP
374 "__REALTIME_TIMESTAMP=%llu\n"
375 "__MONOTONIC_TIMESTAMP=%llu\n"
112301ae 376 "_BOOT_ID=%s\n",
86aa7ba4
LP
377 cursor,
378 (unsigned long long) realtime,
379 (unsigned long long) monotonic,
380 sd_id128_to_string(boot_id, sid));
381
382 free(cursor);
383
384 SD_JOURNAL_FOREACH_DATA(j, data, length) {
385
112301ae
LP
386 /* We already printed the boot id, from the data in
387 * the header, hence let's suppress it here */
388 if (length >= 9 &&
389 memcmp(data, "_BOOT_ID=", 9) == 0)
390 continue;
391
ba961854 392 if (!utf8_is_printable_n(data, length)) {
86aa7ba4
LP
393 const char *c;
394 uint64_t le64;
395
396 c = memchr(data, '=', length);
397 if (!c) {
398 log_error("Invalid field.");
399 return -EINVAL;
400 }
401
402 fwrite(data, c - (const char*) data, 1, stdout);
403 fputc('\n', stdout);
404 le64 = htole64(length - (c - (const char*) data) - 1);
405 fwrite(&le64, sizeof(le64), 1, stdout);
406 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout);
407 } else
408 fwrite(data, length, 1, stdout);
409
410 fputc('\n', stdout);
411 }
412
413 fputc('\n', stdout);
414
415 return 0;
416}
417
418static void json_escape(const char* p, size_t l) {
ba961854 419 if (!utf8_is_printable_n(p, l)) {
86aa7ba4
LP
420 bool not_first = false;
421
422 fputs("[ ", stdout);
423
424 while (l > 0) {
425 if (not_first)
426 printf(", %u", (uint8_t) *p);
427 else {
428 not_first = true;
429 printf("%u", (uint8_t) *p);
430 }
431
432 p++;
433 l--;
434 }
435
436 fputs(" ]", stdout);
437 } else {
438 fputc('\"', stdout);
439
440 while (l > 0) {
441 if (*p == '"' || *p == '\\') {
442 fputc('\\', stdout);
443 fputc(*p, stdout);
444 } else
445 fputc(*p, stdout);
446
447 p++;
448 l--;
449 }
450
451 fputc('\"', stdout);
452 }
453}
454
25277cd7
ZJS
455static int output_json(sd_journal *j, unsigned line,
456 unsigned n_columns, OutputFlags flags) {
86aa7ba4
LP
457 uint64_t realtime, monotonic;
458 char *cursor;
459 const void *data;
460 size_t length;
461 sd_id128_t boot_id;
462 char sid[33];
463 int r;
464
465 assert(j);
466
467 r = sd_journal_get_realtime_usec(j, &realtime);
468 if (r < 0) {
469 log_error("Failed to get realtime timestamp: %s", strerror(-r));
470 return r;
471 }
472
473 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
474 if (r < 0) {
475 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
476 return r;
477 }
478
479 r = sd_journal_get_cursor(j, &cursor);
480 if (r < 0) {
481 log_error("Failed to get cursor: %s", strerror(-r));
482 return r;
483 }
484
485 if (line == 1)
486 fputc('\n', stdout);
487 else
488 fputs(",\n", stdout);
489
490 printf("{\n"
ba8d3790 491 "\t\"__CURSOR\" : \"%s\",\n"
ffa16db0
LP
492 "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
493 "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
112301ae 494 "\t\"_BOOT_ID\" : \"%s\"",
86aa7ba4
LP
495 cursor,
496 (unsigned long long) realtime,
497 (unsigned long long) monotonic,
498 sd_id128_to_string(boot_id, sid));
499
500 free(cursor);
501
502 SD_JOURNAL_FOREACH_DATA(j, data, length) {
503 const char *c;
504
112301ae
LP
505 /* We already printed the boot id, from the data in
506 * the header, hence let's suppress it here */
507 if (length >= 9 &&
508 memcmp(data, "_BOOT_ID=", 9) == 0)
509 continue;
510
86aa7ba4
LP
511 c = memchr(data, '=', length);
512 if (!c) {
513 log_error("Invalid field.");
514 return -EINVAL;
515 }
516
517 fputs(",\n\t", stdout);
518 json_escape(data, c - (const char*) data);
519 fputs(" : ", stdout);
520 json_escape(c + 1, length - (c - (const char*) data) - 1);
521 }
522
523 fputs("\n}", stdout);
86aa7ba4
LP
524
525 return 0;
526}
527
25277cd7
ZJS
528static int output_cat(sd_journal *j, unsigned line,
529 unsigned n_columns, OutputFlags flags) {
d3f2bdbf
LP
530 const void *data;
531 size_t l;
532 int r;
533
534 assert(j);
535
536 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
537 if (r < 0) {
538 log_error("Failed to get data: %s", strerror(-r));
539 return r;
540 }
541
542 assert(l >= 8);
543
544 fwrite((const char*) data + 8, 1, l - 8, stdout);
545 putchar('\n');
546
547 return 0;
548}
549
25277cd7
ZJS
550static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line,
551 unsigned n_columns, OutputFlags flags) = {
67a12205
LP
552 [OUTPUT_SHORT] = output_short_realtime,
553 [OUTPUT_SHORT_MONOTONIC] = output_short_monotonic,
86aa7ba4
LP
554 [OUTPUT_VERBOSE] = output_verbose,
555 [OUTPUT_EXPORT] = output_export,
d3f2bdbf
LP
556 [OUTPUT_JSON] = output_json,
557 [OUTPUT_CAT] = output_cat
86aa7ba4
LP
558};
559
25277cd7
ZJS
560int output_journal(sd_journal *j, OutputMode mode, unsigned line,
561 unsigned n_columns, OutputFlags flags) {
e268b81e 562 int ret;
df50185b 563 assert(mode >= 0);
86aa7ba4
LP
564 assert(mode < _OUTPUT_MODE_MAX);
565
34a35ece
LP
566 if (n_columns <= 0)
567 n_columns = columns();
568
e268b81e
BP
569 ret = output_funcs[mode](j, line, n_columns, flags);
570 fflush(stdout);
571 return ret;
86aa7ba4
LP
572}
573
df50185b
LP
574int show_journal_by_unit(
575 const char *unit,
576 OutputMode mode,
86aa7ba4
LP
577 unsigned n_columns,
578 usec_t not_before,
579 unsigned how_many,
085d7120 580 OutputFlags flags) {
86aa7ba4 581
1946b0bd 582 char *m1 = NULL, *m2 = NULL, *m3 = NULL;
7ea07dcd 583 sd_journal *j = NULL;
86aa7ba4 584 int r;
df50185b
LP
585 unsigned line = 0;
586 bool need_seek = false;
085d7120 587 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
86aa7ba4 588
df50185b
LP
589 assert(mode >= 0);
590 assert(mode < _OUTPUT_MODE_MAX);
591 assert(unit);
592
593 if (!endswith(unit, ".service") &&
594 !endswith(unit, ".socket") &&
595 !endswith(unit, ".mount") &&
596 !endswith(unit, ".swap"))
597 return 0;
86aa7ba4 598
df50185b 599 if (how_many <= 0)
f4fb21c1
LP
600 return 0;
601
1946b0bd
LP
602 if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
603 asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
604 asprintf(&m3, "UNIT=%s", unit) < 0) {
86aa7ba4
LP
605 r = -ENOMEM;
606 goto finish;
607 }
608
609 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
610 if (r < 0)
611 goto finish;
612
1946b0bd
LP
613 /* Look for messages from the service itself */
614 r = sd_journal_add_match(j, m1, 0);
86aa7ba4
LP
615 if (r < 0)
616 goto finish;
617
1946b0bd
LP
618 /* Look for coredumps of the service */
619 r = sd_journal_add_disjunction(j);
620 if (r < 0)
621 goto finish;
622 r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0);
623 if (r < 0)
624 goto finish;
625 r = sd_journal_add_match(j, m2, 0);
626 if (r < 0)
627 goto finish;
628
629 /* Look for messages from PID 1 about this service */
630 r = sd_journal_add_disjunction(j);
631 if (r < 0)
632 goto finish;
633 r = sd_journal_add_match(j, "_PID=1", 0);
634 if (r < 0)
635 goto finish;
636 r = sd_journal_add_match(j, m3, 0);
637 if (r < 0)
638 goto finish;
639
640 /* Seek to end */
86aa7ba4
LP
641 r = sd_journal_seek_tail(j);
642 if (r < 0)
643 goto finish;
644
df50185b
LP
645 r = sd_journal_previous_skip(j, how_many);
646 if (r < 0)
647 goto finish;
86aa7ba4 648
df50185b
LP
649 if (mode == OUTPUT_JSON) {
650 fputc('[', stdout);
651 fflush(stdout);
652 }
86aa7ba4 653
df50185b
LP
654 for (;;) {
655 for (;;) {
656 usec_t usec;
657
658 if (need_seek) {
659 r = sd_journal_next(j);
660 if (r < 0)
661 goto finish;
662 }
663
664 if (r == 0)
665 break;
86aa7ba4 666
df50185b
LP
667 need_seek = true;
668
669 if (not_before > 0) {
670 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
671
672 /* -ESTALE is returned if the
673 timestamp is not from this boot */
674 if (r == -ESTALE)
675 continue;
676 else if (r < 0)
677 goto finish;
678
679 if (usec < not_before)
680 continue;
681 }
682
683 line ++;
684
085d7120 685 r = output_journal(j, mode, line, n_columns, flags);
df50185b
LP
686 if (r < 0)
687 goto finish;
688 }
689
08984293
LP
690 if (warn_cutoff && line < how_many && not_before > 0) {
691 sd_id128_t boot_id;
692 usec_t cutoff;
693
694 /* Check whether the cutoff line is too early */
695
696 r = sd_id128_get_boot(&boot_id);
697 if (r < 0)
698 goto finish;
699
700 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
701 if (r < 0)
702 goto finish;
703
b59866ae 704 if (r > 0 && not_before < cutoff)
08984293
LP
705 printf("Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
706
707 warn_cutoff = false;
708 }
709
085d7120 710 if (!(flags & OUTPUT_FOLLOW))
86aa7ba4
LP
711 break;
712
e02d1cf7 713 r = sd_journal_wait(j, (usec_t) -1);
df50185b
LP
714 if (r < 0)
715 goto finish;
716
86aa7ba4
LP
717 }
718
df50185b
LP
719 if (mode == OUTPUT_JSON)
720 fputs("\n]\n", stdout);
721
86aa7ba4 722finish:
1946b0bd
LP
723 free(m1);
724 free(m2);
725 free(m3);
86aa7ba4
LP
726
727 if (j)
728 sd_journal_close(j);
729
730 return r;
731}
df50185b
LP
732
733static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
734 [OUTPUT_SHORT] = "short",
67a12205 735 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
df50185b
LP
736 [OUTPUT_VERBOSE] = "verbose",
737 [OUTPUT_EXPORT] = "export",
d3f2bdbf
LP
738 [OUTPUT_JSON] = "json",
739 [OUTPUT_CAT] = "cat"
df50185b
LP
740};
741
742DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);