]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/logs-show.c
journal: properly serialize fields with multiple values into JSON
[thirdparty/systemd.git] / src / shared / logs-show.c
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
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>
25 #include <sys/poll.h>
26 #include <string.h>
27
28 #include "logs-show.h"
29 #include "log.h"
30 #include "util.h"
31 #include "utf8.h"
32 #include "hashmap.h"
33
34 #define PRINT_THRESHOLD 128
35 #define JSON_THRESHOLD 4096
36
37 static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
38 size_t fl, nl;
39 void *buf;
40
41 assert(data);
42 assert(field);
43 assert(target);
44 assert(target_size);
45
46 fl = strlen(field);
47 if (length < fl)
48 return 0;
49
50 if (memcmp(data, field, fl))
51 return 0;
52
53 nl = length - fl;
54 buf = malloc(nl+1);
55 if (!buf)
56 return log_oom();
57
58 memcpy(buf, (const char*) data + fl, nl);
59 ((char*)buf)[nl] = 0;
60
61 free(*target);
62 *target = buf;
63 *target_size = nl;
64
65 return 1;
66 }
67
68 static bool shall_print(const char *p, size_t l, OutputFlags flags) {
69 assert(p);
70
71 if (flags & OUTPUT_SHOW_ALL)
72 return true;
73
74 if (l > PRINT_THRESHOLD)
75 return false;
76
77 if (!utf8_is_printable_n(p, l))
78 return false;
79
80 return true;
81 }
82
83 static int output_short(
84 FILE *f,
85 sd_journal *j,
86 OutputMode mode,
87 unsigned n_columns,
88 OutputFlags flags) {
89
90 int r;
91 const void *data;
92 size_t length;
93 size_t n = 0;
94 _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
95 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;
96 int p = LOG_INFO;
97 const char *color_on = "", *color_off = "";
98
99 assert(f);
100 assert(j);
101
102 SD_JOURNAL_FOREACH_DATA(j, data, length) {
103
104 r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
105 if (r < 0)
106 return r;
107 else if (r > 0)
108 continue;
109
110 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
111 if (r < 0)
112 return r;
113 else if (r > 0)
114 continue;
115
116 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
117 if (r < 0)
118 return r;
119 else if (r > 0)
120 continue;
121
122 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
123 if (r < 0)
124 return r;
125 else if (r > 0)
126 continue;
127
128 r = parse_field(data, length, "_PID=", &pid, &pid_len);
129 if (r < 0)
130 return r;
131 else if (r > 0)
132 continue;
133
134 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
135 if (r < 0)
136 return r;
137 else if (r > 0)
138 continue;
139
140 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
141 if (r < 0)
142 return r;
143 else if (r > 0)
144 continue;
145
146 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
147 if (r < 0)
148 return r;
149 else if (r > 0)
150 continue;
151
152 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
153 if (r < 0)
154 return r;
155 }
156
157 if (!message)
158 return 0;
159
160 if (priority_len == 1 && *priority >= '0' && *priority <= '7')
161 p = *priority - '0';
162
163 if (mode == OUTPUT_SHORT_MONOTONIC) {
164 uint64_t t;
165 sd_id128_t boot_id;
166
167 r = -ENOENT;
168
169 if (monotonic)
170 r = safe_atou64(monotonic, &t);
171
172 if (r < 0)
173 r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
174
175 if (r < 0) {
176 log_error("Failed to get monotonic: %s", strerror(-r));
177 return r;
178 }
179
180 fprintf(f, "[%5llu.%06llu]",
181 (unsigned long long) (t / USEC_PER_SEC),
182 (unsigned long long) (t % USEC_PER_SEC));
183
184 n += 1 + 5 + 1 + 6 + 1;
185
186 } else {
187 char buf[64];
188 uint64_t x;
189 time_t t;
190 struct tm tm;
191
192 r = -ENOENT;
193
194 if (realtime)
195 r = safe_atou64(realtime, &x);
196
197 if (r < 0)
198 r = sd_journal_get_realtime_usec(j, &x);
199
200 if (r < 0) {
201 log_error("Failed to get realtime: %s", strerror(-r));
202 return r;
203 }
204
205 t = (time_t) (x / USEC_PER_SEC);
206 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
207 log_error("Failed to format time.");
208 return r;
209 }
210
211 fputs(buf, f);
212 n += strlen(buf);
213 }
214
215 if (hostname && shall_print(hostname, hostname_len, flags)) {
216 fprintf(f, " %.*s", (int) hostname_len, hostname);
217 n += hostname_len + 1;
218 }
219
220 if (identifier && shall_print(identifier, identifier_len, flags)) {
221 fprintf(f, " %.*s", (int) identifier_len, identifier);
222 n += identifier_len + 1;
223 } else if (comm && shall_print(comm, comm_len, flags)) {
224 fprintf(f, " %.*s", (int) comm_len, comm);
225 n += comm_len + 1;
226 } else
227 fputc(' ', f);
228
229 if (pid && shall_print(pid, pid_len, flags)) {
230 fprintf(f, "[%.*s]", (int) pid_len, pid);
231 n += pid_len + 2;
232 } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
233 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
234 n += fake_pid_len + 2;
235 }
236
237 if (flags & OUTPUT_COLOR) {
238 if (p <= LOG_ERR) {
239 color_on = ANSI_HIGHLIGHT_RED_ON;
240 color_off = ANSI_HIGHLIGHT_OFF;
241 } else if (p <= LOG_NOTICE) {
242 color_on = ANSI_HIGHLIGHT_ON;
243 color_off = ANSI_HIGHLIGHT_OFF;
244 }
245 }
246
247 if (flags & OUTPUT_SHOW_ALL)
248 fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
249 else if (!utf8_is_printable_n(message, message_len)) {
250 char bytes[FORMAT_BYTES_MAX];
251 fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
252 } else if ((flags & OUTPUT_FULL_WIDTH) || (message_len + n + 1 < n_columns))
253 fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
254 else if (n < n_columns && n_columns - n - 2 >= 3) {
255 char *e;
256
257 e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
258
259 if (!e)
260 fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
261 else
262 fprintf(f, ": %s%s%s\n", color_on, e, color_off);
263
264 free(e);
265 } else
266 fputs("\n", f);
267
268 return 0;
269 }
270
271 static int output_verbose(
272 FILE *f,
273 sd_journal *j,
274 OutputMode mode,
275 unsigned n_columns,
276 OutputFlags flags) {
277
278 const void *data;
279 size_t length;
280 char *cursor;
281 uint64_t realtime;
282 char ts[FORMAT_TIMESTAMP_MAX];
283 int r;
284
285 assert(f);
286 assert(j);
287
288 r = sd_journal_get_realtime_usec(j, &realtime);
289 if (r < 0) {
290 log_error("Failed to get realtime timestamp: %s", strerror(-r));
291 return r;
292 }
293
294 r = sd_journal_get_cursor(j, &cursor);
295 if (r < 0) {
296 log_error("Failed to get cursor: %s", strerror(-r));
297 return r;
298 }
299
300 fprintf(f, "%s [%s]\n",
301 format_timestamp(ts, sizeof(ts), realtime),
302 cursor);
303
304 free(cursor);
305
306 SD_JOURNAL_FOREACH_DATA(j, data, length) {
307 if (!shall_print(data, length, flags)) {
308 const char *c;
309 char bytes[FORMAT_BYTES_MAX];
310
311 c = memchr(data, '=', length);
312 if (!c) {
313 log_error("Invalid field.");
314 return -EINVAL;
315 }
316
317 fprintf(f, "\t%.*s=[%s blob data]\n",
318 (int) (c - (const char*) data),
319 (const char*) data,
320 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
321 } else
322 fprintf(f, "\t%.*s\n", (int) length, (const char*) data);
323 }
324
325 return 0;
326 }
327
328 static int output_export(
329 FILE *f,
330 sd_journal *j,
331 OutputMode mode,
332 unsigned n_columns,
333 OutputFlags flags) {
334
335 sd_id128_t boot_id;
336 char sid[33];
337 int r;
338 usec_t realtime, monotonic;
339 char *cursor;
340 const void *data;
341 size_t length;
342
343 assert(j);
344
345 r = sd_journal_get_realtime_usec(j, &realtime);
346 if (r < 0) {
347 log_error("Failed to get realtime timestamp: %s", strerror(-r));
348 return r;
349 }
350
351 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
352 if (r < 0) {
353 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
354 return r;
355 }
356
357 r = sd_journal_get_cursor(j, &cursor);
358 if (r < 0) {
359 log_error("Failed to get cursor: %s", strerror(-r));
360 return r;
361 }
362
363 fprintf(f,
364 "__CURSOR=%s\n"
365 "__REALTIME_TIMESTAMP=%llu\n"
366 "__MONOTONIC_TIMESTAMP=%llu\n"
367 "_BOOT_ID=%s\n",
368 cursor,
369 (unsigned long long) realtime,
370 (unsigned long long) monotonic,
371 sd_id128_to_string(boot_id, sid));
372
373 free(cursor);
374
375 SD_JOURNAL_FOREACH_DATA(j, data, length) {
376
377 /* We already printed the boot id, from the data in
378 * the header, hence let's suppress it here */
379 if (length >= 9 &&
380 memcmp(data, "_BOOT_ID=", 9) == 0)
381 continue;
382
383 if (!utf8_is_printable_n(data, length)) {
384 const char *c;
385 uint64_t le64;
386
387 c = memchr(data, '=', length);
388 if (!c) {
389 log_error("Invalid field.");
390 return -EINVAL;
391 }
392
393 fwrite(data, c - (const char*) data, 1, f);
394 fputc('\n', f);
395 le64 = htole64(length - (c - (const char*) data) - 1);
396 fwrite(&le64, sizeof(le64), 1, f);
397 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
398 } else
399 fwrite(data, length, 1, f);
400
401 fputc('\n', f);
402 }
403
404 fputc('\n', f);
405
406 return 0;
407 }
408
409 void json_escape(
410 FILE *f,
411 const char* p,
412 size_t l,
413 OutputFlags flags) {
414
415 assert(f);
416 assert(p);
417
418 if (!(flags & OUTPUT_SHOW_ALL) && l > JSON_THRESHOLD)
419
420 fputs("null", f);
421
422 else if (!utf8_is_printable_n(p, l)) {
423 bool not_first = false;
424
425 fputs("[ ", f);
426
427 while (l > 0) {
428 if (not_first)
429 fprintf(f, ", %u", (uint8_t) *p);
430 else {
431 not_first = true;
432 fprintf(f, "%u", (uint8_t) *p);
433 }
434
435 p++;
436 l--;
437 }
438
439 fputs(" ]", f);
440 } else {
441 fputc('\"', f);
442
443 while (l > 0) {
444 if (*p == '"' || *p == '\\') {
445 fputc('\\', f);
446 fputc(*p, f);
447 } else if (*p < ' ')
448 fprintf(f, "\\u%04x", *p);
449 else
450 fputc(*p, f);
451
452 p++;
453 l--;
454 }
455
456 fputc('\"', f);
457 }
458 }
459
460 static int output_json(
461 FILE *f,
462 sd_journal *j,
463 OutputMode mode,
464 unsigned n_columns,
465 OutputFlags flags) {
466
467 uint64_t realtime, monotonic;
468 char *cursor, *k;
469 const void *data;
470 size_t length;
471 sd_id128_t boot_id;
472 char sid[33];
473 int r;
474 Hashmap *h = NULL;
475 bool done, separator;
476
477 assert(j);
478
479 r = sd_journal_get_realtime_usec(j, &realtime);
480 if (r < 0) {
481 log_error("Failed to get realtime timestamp: %s", strerror(-r));
482 return r;
483 }
484
485 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
486 if (r < 0) {
487 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
488 return r;
489 }
490
491 r = sd_journal_get_cursor(j, &cursor);
492 if (r < 0) {
493 log_error("Failed to get cursor: %s", strerror(-r));
494 return r;
495 }
496
497 if (mode == OUTPUT_JSON_PRETTY)
498 fprintf(f,
499 "{\n"
500 "\t\"__CURSOR\" : \"%s\",\n"
501 "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
502 "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
503 "\t\"_BOOT_ID\" : \"%s\"",
504 cursor,
505 (unsigned long long) realtime,
506 (unsigned long long) monotonic,
507 sd_id128_to_string(boot_id, sid));
508 else {
509 if (mode == OUTPUT_JSON_SSE)
510 fputs("data: ", f);
511
512 fprintf(f,
513 "{ \"__CURSOR\" : \"%s\", "
514 "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
515 "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
516 "\"_BOOT_ID\" : \"%s\"",
517 cursor,
518 (unsigned long long) realtime,
519 (unsigned long long) monotonic,
520 sd_id128_to_string(boot_id, sid));
521 }
522 free(cursor);
523
524 h = hashmap_new(string_hash_func, string_compare_func);
525 if (!h)
526 return -ENOMEM;
527
528 /* First round, iterate through the entry and count how often each field appears */
529 SD_JOURNAL_FOREACH_DATA(j, data, length) {
530 const char *eq;
531 char *n;
532 unsigned u;
533
534 if (length >= 9 &&
535 memcmp(data, "_BOOT_ID=", 9) == 0)
536 continue;
537
538 eq = memchr(data, '=', length);
539 if (!eq)
540 continue;
541
542 n = strndup(data, eq - (const char*) data);
543 if (!n) {
544 r = -ENOMEM;
545 goto finish;
546 }
547
548 u = PTR_TO_UINT(hashmap_get(h, n));
549 if (u == 0) {
550 r = hashmap_put(h, n, UINT_TO_PTR(1));
551 if (r < 0) {
552 free(n);
553 goto finish;
554 }
555 } else {
556 r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
557 free(n);
558 if (r < 0)
559 goto finish;
560 }
561 }
562
563 separator = true;
564 do {
565 done = true;
566
567 SD_JOURNAL_FOREACH_DATA(j, data, length) {
568 const char *eq;
569 char *kk, *n;
570 size_t m;
571 unsigned u;
572
573 /* We already printed the boot id, from the data in
574 * the header, hence let's suppress it here */
575 if (length >= 9 &&
576 memcmp(data, "_BOOT_ID=", 9) == 0)
577 continue;
578
579 eq = memchr(data, '=', length);
580 if (!eq)
581 continue;
582
583 if (separator) {
584 if (mode == OUTPUT_JSON_PRETTY)
585 fputs(",\n\t", f);
586 else
587 fputs(", ", f);
588 }
589
590 m = eq - (const char*) data;
591
592 n = strndup(data, m);
593 if (!n) {
594 r = -ENOMEM;
595 goto finish;
596 }
597
598 u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
599 if (u == 0) {
600 /* We already printed this, let's jump to the next */
601 free(n);
602 separator = false;
603
604 continue;
605 } else if (u == 1) {
606 /* Field only appears once, output it directly */
607
608 json_escape(f, data, m, flags);
609 fputs(" : ", f);
610
611 json_escape(f, eq + 1, length - m - 1, flags);
612
613 hashmap_remove(h, n);
614 free(kk);
615 free(n);
616
617 separator = true;
618
619 continue;
620
621 } else {
622 /* Field appears multiple times, output it as array */
623 json_escape(f, data, m, flags);
624 fputs(" : [ ", f);
625 json_escape(f, eq + 1, length - m - 1, flags);
626
627 /* Iterate through the end of the list */
628
629 while (sd_journal_enumerate_data(j, &data, &length) > 0) {
630 if (length < m + 1)
631 continue;
632
633 if (memcmp(data, n, m) != 0)
634 continue;
635
636 if (((const char*) data)[m] != '=')
637 continue;
638
639 fputs(", ", f);
640 json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
641 }
642
643 fputs(" ]", f);
644
645 hashmap_remove(h, n);
646 free(kk);
647 free(n);
648
649 /* Iterate data fields form the beginning */
650 done = false;
651 separator = true;
652
653 break;
654 }
655 }
656
657 } while (!done);
658
659 if (mode == OUTPUT_JSON_PRETTY)
660 fputs("\n}\n", f);
661 else if (mode == OUTPUT_JSON_SSE)
662 fputs("}\n\n", f);
663 else
664 fputs(" }\n", f);
665
666 r = 0;
667
668 finish:
669 while ((k = hashmap_steal_first_key(h)))
670 free(k);
671
672 hashmap_free(h);
673
674 return r;
675 }
676
677 static int output_cat(
678 FILE *f,
679 sd_journal *j,
680 OutputMode mode,
681 unsigned n_columns,
682 OutputFlags flags) {
683
684 const void *data;
685 size_t l;
686 int r;
687
688 assert(j);
689 assert(f);
690
691 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
692 if (r < 0) {
693 /* An entry without MESSAGE=? */
694 if (r == -ENOENT)
695 return 0;
696
697 log_error("Failed to get data: %s", strerror(-r));
698 return r;
699 }
700
701 assert(l >= 8);
702
703 fwrite((const char*) data + 8, 1, l - 8, f);
704 fputc('\n', f);
705
706 return 0;
707 }
708
709 static int (*output_funcs[_OUTPUT_MODE_MAX])(
710 FILE *f,
711 sd_journal*j,
712 OutputMode mode,
713 unsigned n_columns,
714 OutputFlags flags) = {
715
716 [OUTPUT_SHORT] = output_short,
717 [OUTPUT_SHORT_MONOTONIC] = output_short,
718 [OUTPUT_VERBOSE] = output_verbose,
719 [OUTPUT_EXPORT] = output_export,
720 [OUTPUT_JSON] = output_json,
721 [OUTPUT_JSON_PRETTY] = output_json,
722 [OUTPUT_JSON_SSE] = output_json,
723 [OUTPUT_CAT] = output_cat
724 };
725
726 int output_journal(
727 FILE *f,
728 sd_journal *j,
729 OutputMode mode,
730 unsigned n_columns,
731 OutputFlags flags) {
732
733 int ret;
734 assert(mode >= 0);
735 assert(mode < _OUTPUT_MODE_MAX);
736
737 if (n_columns <= 0)
738 n_columns = columns();
739
740 ret = output_funcs[mode](f, j, mode, n_columns, flags);
741 fflush(stdout);
742 return ret;
743 }
744
745 int show_journal_by_unit(
746 FILE *f,
747 const char *unit,
748 OutputMode mode,
749 unsigned n_columns,
750 usec_t not_before,
751 unsigned how_many,
752 OutputFlags flags) {
753
754 _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL;
755 sd_journal *j = NULL;
756 int r;
757 unsigned line = 0;
758 bool need_seek = false;
759 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
760
761 assert(mode >= 0);
762 assert(mode < _OUTPUT_MODE_MAX);
763 assert(unit);
764
765 if (!endswith(unit, ".service") &&
766 !endswith(unit, ".socket") &&
767 !endswith(unit, ".mount") &&
768 !endswith(unit, ".swap"))
769 return 0;
770
771 if (how_many <= 0)
772 return 0;
773
774 if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
775 asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
776 asprintf(&m3, "UNIT=%s", unit) < 0) {
777 r = -ENOMEM;
778 goto finish;
779 }
780
781 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
782 if (r < 0)
783 goto finish;
784
785 /* Look for messages from the service itself */
786 r = sd_journal_add_match(j, m1, 0);
787 if (r < 0)
788 goto finish;
789
790 /* Look for coredumps of the service */
791 r = sd_journal_add_disjunction(j);
792 if (r < 0)
793 goto finish;
794 r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0);
795 if (r < 0)
796 goto finish;
797 r = sd_journal_add_match(j, m2, 0);
798 if (r < 0)
799 goto finish;
800
801 /* Look for messages from PID 1 about this service */
802 r = sd_journal_add_disjunction(j);
803 if (r < 0)
804 goto finish;
805 r = sd_journal_add_match(j, "_PID=1", 0);
806 if (r < 0)
807 goto finish;
808 r = sd_journal_add_match(j, m3, 0);
809 if (r < 0)
810 goto finish;
811
812 /* Seek to end */
813 r = sd_journal_seek_tail(j);
814 if (r < 0)
815 goto finish;
816
817 r = sd_journal_previous_skip(j, how_many);
818 if (r < 0)
819 goto finish;
820
821 for (;;) {
822 for (;;) {
823 usec_t usec;
824
825 if (need_seek) {
826 r = sd_journal_next(j);
827 if (r < 0)
828 goto finish;
829 }
830
831 if (r == 0)
832 break;
833
834 need_seek = true;
835
836 if (not_before > 0) {
837 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
838
839 /* -ESTALE is returned if the
840 timestamp is not from this boot */
841 if (r == -ESTALE)
842 continue;
843 else if (r < 0)
844 goto finish;
845
846 if (usec < not_before)
847 continue;
848 }
849
850 line ++;
851
852 r = output_journal(f, j, mode, n_columns, flags);
853 if (r < 0)
854 goto finish;
855 }
856
857 if (warn_cutoff && line < how_many && not_before > 0) {
858 sd_id128_t boot_id;
859 usec_t cutoff;
860
861 /* Check whether the cutoff line is too early */
862
863 r = sd_id128_get_boot(&boot_id);
864 if (r < 0)
865 goto finish;
866
867 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
868 if (r < 0)
869 goto finish;
870
871 if (r > 0 && not_before < cutoff)
872 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
873
874 warn_cutoff = false;
875 }
876
877 if (!(flags & OUTPUT_FOLLOW))
878 break;
879
880 r = sd_journal_wait(j, (usec_t) -1);
881 if (r < 0)
882 goto finish;
883
884 }
885
886 finish:
887 if (j)
888 sd_journal_close(j);
889
890 return r;
891 }
892
893 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
894 [OUTPUT_SHORT] = "short",
895 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
896 [OUTPUT_VERBOSE] = "verbose",
897 [OUTPUT_EXPORT] = "export",
898 [OUTPUT_JSON] = "json",
899 [OUTPUT_JSON_PRETTY] = "json-pretty",
900 [OUTPUT_JSON_SSE] = "json-sse",
901 [OUTPUT_CAT] = "cat"
902 };
903
904 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);