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