]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/logs-show.c
update TODO -- hackfest edition
[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);
524 fflush(stdout);
525
526 return 0;
527}
528
25277cd7
ZJS
529static int output_cat(sd_journal *j, unsigned line,
530 unsigned n_columns, OutputFlags flags) {
d3f2bdbf
LP
531 const void *data;
532 size_t l;
533 int r;
534
535 assert(j);
536
537 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
538 if (r < 0) {
539 log_error("Failed to get data: %s", strerror(-r));
540 return r;
541 }
542
543 assert(l >= 8);
544
545 fwrite((const char*) data + 8, 1, l - 8, stdout);
546 putchar('\n');
547
548 return 0;
549}
550
25277cd7
ZJS
551static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line,
552 unsigned n_columns, OutputFlags flags) = {
67a12205
LP
553 [OUTPUT_SHORT] = output_short_realtime,
554 [OUTPUT_SHORT_MONOTONIC] = output_short_monotonic,
86aa7ba4
LP
555 [OUTPUT_VERBOSE] = output_verbose,
556 [OUTPUT_EXPORT] = output_export,
d3f2bdbf
LP
557 [OUTPUT_JSON] = output_json,
558 [OUTPUT_CAT] = output_cat
86aa7ba4
LP
559};
560
25277cd7
ZJS
561int output_journal(sd_journal *j, OutputMode mode, unsigned line,
562 unsigned n_columns, OutputFlags flags) {
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
25277cd7 569 return output_funcs[mode](j, line, n_columns, flags);
86aa7ba4
LP
570}
571
df50185b
LP
572int show_journal_by_unit(
573 const char *unit,
574 OutputMode mode,
86aa7ba4
LP
575 unsigned n_columns,
576 usec_t not_before,
577 unsigned how_many,
085d7120 578 OutputFlags flags) {
86aa7ba4 579
1946b0bd 580 char *m1 = NULL, *m2 = NULL, *m3 = NULL;
7ea07dcd 581 sd_journal *j = NULL;
86aa7ba4 582 int r;
df50185b
LP
583 unsigned line = 0;
584 bool need_seek = false;
085d7120 585 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
86aa7ba4 586
df50185b
LP
587 assert(mode >= 0);
588 assert(mode < _OUTPUT_MODE_MAX);
589 assert(unit);
590
591 if (!endswith(unit, ".service") &&
592 !endswith(unit, ".socket") &&
593 !endswith(unit, ".mount") &&
594 !endswith(unit, ".swap"))
595 return 0;
86aa7ba4 596
df50185b 597 if (how_many <= 0)
f4fb21c1
LP
598 return 0;
599
1946b0bd
LP
600 if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
601 asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
602 asprintf(&m3, "UNIT=%s", unit) < 0) {
86aa7ba4
LP
603 r = -ENOMEM;
604 goto finish;
605 }
606
607 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
608 if (r < 0)
609 goto finish;
610
1946b0bd
LP
611 /* Look for messages from the service itself */
612 r = sd_journal_add_match(j, m1, 0);
86aa7ba4
LP
613 if (r < 0)
614 goto finish;
615
1946b0bd
LP
616 /* Look for coredumps of the service */
617 r = sd_journal_add_disjunction(j);
618 if (r < 0)
619 goto finish;
620 r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0);
621 if (r < 0)
622 goto finish;
623 r = sd_journal_add_match(j, m2, 0);
624 if (r < 0)
625 goto finish;
626
627 /* Look for messages from PID 1 about this service */
628 r = sd_journal_add_disjunction(j);
629 if (r < 0)
630 goto finish;
631 r = sd_journal_add_match(j, "_PID=1", 0);
632 if (r < 0)
633 goto finish;
634 r = sd_journal_add_match(j, m3, 0);
635 if (r < 0)
636 goto finish;
637
638 /* Seek to end */
86aa7ba4
LP
639 r = sd_journal_seek_tail(j);
640 if (r < 0)
641 goto finish;
642
df50185b
LP
643 r = sd_journal_previous_skip(j, how_many);
644 if (r < 0)
645 goto finish;
86aa7ba4 646
df50185b
LP
647 if (mode == OUTPUT_JSON) {
648 fputc('[', stdout);
649 fflush(stdout);
650 }
86aa7ba4 651
df50185b
LP
652 for (;;) {
653 for (;;) {
654 usec_t usec;
655
656 if (need_seek) {
657 r = sd_journal_next(j);
658 if (r < 0)
659 goto finish;
660 }
661
662 if (r == 0)
663 break;
86aa7ba4 664
df50185b
LP
665 need_seek = true;
666
667 if (not_before > 0) {
668 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
669
670 /* -ESTALE is returned if the
671 timestamp is not from this boot */
672 if (r == -ESTALE)
673 continue;
674 else if (r < 0)
675 goto finish;
676
677 if (usec < not_before)
678 continue;
679 }
680
681 line ++;
682
085d7120 683 r = output_journal(j, mode, line, n_columns, flags);
df50185b
LP
684 if (r < 0)
685 goto finish;
686 }
687
08984293
LP
688 if (warn_cutoff && line < how_many && not_before > 0) {
689 sd_id128_t boot_id;
690 usec_t cutoff;
691
692 /* Check whether the cutoff line is too early */
693
694 r = sd_id128_get_boot(&boot_id);
695 if (r < 0)
696 goto finish;
697
698 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
699 if (r < 0)
700 goto finish;
701
b59866ae 702 if (r > 0 && not_before < cutoff)
08984293
LP
703 printf("Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
704
705 warn_cutoff = false;
706 }
707
085d7120 708 if (!(flags & OUTPUT_FOLLOW))
86aa7ba4
LP
709 break;
710
e02d1cf7 711 r = sd_journal_wait(j, (usec_t) -1);
df50185b
LP
712 if (r < 0)
713 goto finish;
714
86aa7ba4
LP
715 }
716
df50185b
LP
717 if (mode == OUTPUT_JSON)
718 fputs("\n]\n", stdout);
719
86aa7ba4 720finish:
1946b0bd
LP
721 free(m1);
722 free(m2);
723 free(m3);
86aa7ba4
LP
724
725 if (j)
726 sd_journal_close(j);
727
728 return r;
729}
df50185b
LP
730
731static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
732 [OUTPUT_SHORT] = "short",
67a12205 733 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
df50185b
LP
734 [OUTPUT_VERBOSE] = "verbose",
735 [OUTPUT_EXPORT] = "export",
d3f2bdbf
LP
736 [OUTPUT_JSON] = "json",
737 [OUTPUT_CAT] = "cat"
df50185b
LP
738};
739
740DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);