]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journalctl.c
journal: fix tag ordering check
[thirdparty/systemd.git] / src / journal / journalctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2011 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 <fcntl.h>
23 #include <errno.h>
24 #include <stddef.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <sys/poll.h>
30 #include <time.h>
31 #include <getopt.h>
32 #include <sys/stat.h>
33 #include <sys/ioctl.h>
34 #include <linux/fs.h>
35
36 #include <systemd/sd-journal.h>
37
38 #include "log.h"
39 #include "util.h"
40 #include "path-util.h"
41 #include "build.h"
42 #include "pager.h"
43 #include "logs-show.h"
44 #include "strv.h"
45 #include "journal-internal.h"
46 #include "journal-def.h"
47 #include "journal-verify.h"
48 #include "journal-authenticate.h"
49 #include "fsprg.h"
50
51 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
52
53 static OutputMode arg_output = OUTPUT_SHORT;
54 static bool arg_follow = false;
55 static bool arg_show_all = false;
56 static bool arg_no_pager = false;
57 static int arg_lines = -1;
58 static bool arg_no_tail = false;
59 static bool arg_quiet = false;
60 static bool arg_local = false;
61 static bool arg_this_boot = false;
62 static const char *arg_directory = NULL;
63 static int arg_priorities = 0xFF;
64 static const char *arg_verify_key = NULL;
65 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
66
67 static enum {
68 ACTION_SHOW,
69 ACTION_NEW_ID128,
70 ACTION_PRINT_HEADER,
71 ACTION_SETUP_KEYS,
72 ACTION_VERIFY
73 } arg_action = ACTION_SHOW;
74
75 static int help(void) {
76
77 printf("%s [OPTIONS...] [MATCH]\n\n"
78 "Send control commands to or query the journal.\n\n"
79 " -h --help Show this help\n"
80 " --version Show package version\n"
81 " --no-pager Do not pipe output into a pager\n"
82 " -a --all Show all fields, including long and unprintable\n"
83 " -f --follow Follow journal\n"
84 " -n --lines=INTEGER Journal entries to show\n"
85 " --no-tail Show all lines, even in follow mode\n"
86 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
87 " verbose, export, json, cat)\n"
88 " -q --quiet Don't show privilege warning\n"
89 " -l --local Only local entries\n"
90 " -b --this-boot Show data only from current boot\n"
91 " -D --directory=PATH Show journal files from directory\n"
92 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
93 "Commands:\n"
94 " --new-id128 Generate a new 128 Bit ID\n"
95 " --header Show journal header information\n"
96 " --setup-keys Generate new FSS key pair\n"
97 " --interval=TIME Time interval for changing the FSS sealing key\n"
98 " --verify Verify journal file consistency\n"
99 " --verify-key=KEY Specify FSS verification key\n",
100 program_invocation_short_name);
101
102 return 0;
103 }
104
105 static int parse_argv(int argc, char *argv[]) {
106
107 enum {
108 ARG_VERSION = 0x100,
109 ARG_NO_PAGER,
110 ARG_NO_TAIL,
111 ARG_NEW_ID128,
112 ARG_HEADER,
113 ARG_SETUP_KEYS,
114 ARG_INTERVAL,
115 ARG_VERIFY,
116 ARG_VERIFY_KEY
117 };
118
119 static const struct option options[] = {
120 { "help", no_argument, NULL, 'h' },
121 { "version" , no_argument, NULL, ARG_VERSION },
122 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
123 { "follow", no_argument, NULL, 'f' },
124 { "output", required_argument, NULL, 'o' },
125 { "all", no_argument, NULL, 'a' },
126 { "lines", required_argument, NULL, 'n' },
127 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
128 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
129 { "quiet", no_argument, NULL, 'q' },
130 { "local", no_argument, NULL, 'l' },
131 { "this-boot", no_argument, NULL, 'b' },
132 { "directory", required_argument, NULL, 'D' },
133 { "header", no_argument, NULL, ARG_HEADER },
134 { "priority", no_argument, NULL, 'p' },
135 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
136 { "interval", required_argument, NULL, ARG_INTERVAL },
137 { "verify", no_argument, NULL, ARG_VERIFY },
138 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
139 { NULL, 0, NULL, 0 }
140 };
141
142 int c, r;
143
144 assert(argc >= 0);
145 assert(argv);
146
147 while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
148
149 switch (c) {
150
151 case 'h':
152 help();
153 return 0;
154
155 case ARG_VERSION:
156 puts(PACKAGE_STRING);
157 puts(DISTRIBUTION);
158 puts(SYSTEMD_FEATURES);
159 return 0;
160
161 case ARG_NO_PAGER:
162 arg_no_pager = true;
163 break;
164
165 case 'f':
166 arg_follow = true;
167 break;
168
169 case 'o':
170 arg_output = output_mode_from_string(optarg);
171 if (arg_output < 0) {
172 log_error("Unknown output '%s'.", optarg);
173 return -EINVAL;
174 }
175
176 break;
177
178 case 'a':
179 arg_show_all = true;
180 break;
181
182 case 'n':
183 r = safe_atoi(optarg, &arg_lines);
184 if (r < 0 || arg_lines < 0) {
185 log_error("Failed to parse lines '%s'", optarg);
186 return -EINVAL;
187 }
188 break;
189
190 case ARG_NO_TAIL:
191 arg_no_tail = true;
192 break;
193
194 case ARG_NEW_ID128:
195 arg_action = ACTION_NEW_ID128;
196 break;
197
198 case 'q':
199 arg_quiet = true;
200 break;
201
202 case 'l':
203 arg_local = true;
204 break;
205
206 case 'b':
207 arg_this_boot = true;
208 break;
209
210 case 'D':
211 arg_directory = optarg;
212 break;
213
214 case ARG_HEADER:
215 arg_action = ACTION_PRINT_HEADER;
216 break;
217
218 case ARG_SETUP_KEYS:
219 arg_action = ACTION_SETUP_KEYS;
220 break;
221
222 case ARG_VERIFY:
223 arg_action = ACTION_VERIFY;
224 break;
225
226 case ARG_VERIFY_KEY:
227 arg_action = ACTION_VERIFY;
228 arg_verify_key = optarg;
229 break;
230
231 case ARG_INTERVAL:
232 r = parse_usec(optarg, &arg_interval);
233 if (r < 0 || arg_interval <= 0) {
234 log_error("Failed to parse sealing key change interval: %s", optarg);
235 return -EINVAL;
236 }
237 break;
238
239 case 'p': {
240 const char *dots;
241
242 dots = strstr(optarg, "..");
243 if (dots) {
244 char *a;
245 int from, to, i;
246
247 /* a range */
248 a = strndup(optarg, dots - optarg);
249 if (!a)
250 return log_oom();
251
252 from = log_level_from_string(a);
253 to = log_level_from_string(dots + 2);
254 free(a);
255
256 if (from < 0 || to < 0) {
257 log_error("Failed to parse log level range %s", optarg);
258 return -EINVAL;
259 }
260
261 arg_priorities = 0;
262
263 if (from < to) {
264 for (i = from; i <= to; i++)
265 arg_priorities |= 1 << i;
266 } else {
267 for (i = to; i <= from; i++)
268 arg_priorities |= 1 << i;
269 }
270
271 } else {
272 int p, i;
273
274 p = log_level_from_string(optarg);
275 if (p < 0) {
276 log_error("Unknown log level %s", optarg);
277 return -EINVAL;
278 }
279
280 arg_priorities = 0;
281
282 for (i = 0; i <= p; i++)
283 arg_priorities |= 1 << i;
284 }
285
286 break;
287 }
288
289 case '?':
290 return -EINVAL;
291
292 default:
293 log_error("Unknown option code %c", c);
294 return -EINVAL;
295 }
296 }
297
298 if (arg_follow && !arg_no_tail && arg_lines < 0)
299 arg_lines = 10;
300
301 return 1;
302 }
303
304 static bool on_tty(void) {
305 static int t = -1;
306
307 /* Note that this is invoked relatively early, before we start
308 * the pager. That means the value we return reflects whether
309 * we originally were started on a tty, not if we currently
310 * are. But this is intended, since we want colour and so on
311 * when run in our own pager. */
312
313 if (_unlikely_(t < 0))
314 t = isatty(STDOUT_FILENO) > 0;
315
316 return t;
317 }
318
319 static int generate_new_id128(void) {
320 sd_id128_t id;
321 int r;
322 unsigned i;
323
324 r = sd_id128_randomize(&id);
325 if (r < 0) {
326 log_error("Failed to generate ID: %s", strerror(-r));
327 return r;
328 }
329
330 printf("As string:\n"
331 SD_ID128_FORMAT_STR "\n\n"
332 "As UUID:\n"
333 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
334 "As macro:\n"
335 "#define MESSAGE_XYZ SD_ID128_MAKE(",
336 SD_ID128_FORMAT_VAL(id),
337 SD_ID128_FORMAT_VAL(id));
338
339 for (i = 0; i < 16; i++)
340 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
341
342 fputs(")\n", stdout);
343
344 return 0;
345 }
346
347 static int add_matches(sd_journal *j, char **args) {
348 char **i;
349 int r;
350
351 assert(j);
352
353 STRV_FOREACH(i, args) {
354
355 if (streq(*i, "+"))
356 r = sd_journal_add_disjunction(j);
357 else if (path_is_absolute(*i)) {
358 char *p, *t = NULL;
359 const char *path;
360 struct stat st;
361
362 p = canonicalize_file_name(*i);
363 path = p ? p : *i;
364
365 if (stat(path, &st) < 0) {
366 free(p);
367 log_error("Couldn't stat file: %m");
368 return -errno;
369 }
370
371 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
372 t = strappend("_EXE=", path);
373 else if (S_ISCHR(st.st_mode))
374 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
375 else if (S_ISBLK(st.st_mode))
376 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
377 else {
378 free(p);
379 log_error("File is not a device node, regular file or is not executable: %s", *i);
380 return -EINVAL;
381 }
382
383 free(p);
384
385 if (!t)
386 return log_oom();
387
388 r = sd_journal_add_match(j, t, 0);
389 free(t);
390 } else
391 r = sd_journal_add_match(j, *i, 0);
392
393 if (r < 0) {
394 log_error("Failed to add match '%s': %s", *i, strerror(-r));
395 return r;
396 }
397 }
398
399 return 0;
400 }
401
402 static int add_this_boot(sd_journal *j) {
403 char match[9+32+1] = "_BOOT_ID=";
404 sd_id128_t boot_id;
405 int r;
406
407 assert(j);
408
409 if (!arg_this_boot)
410 return 0;
411
412 r = sd_id128_get_boot(&boot_id);
413 if (r < 0) {
414 log_error("Failed to get boot id: %s", strerror(-r));
415 return r;
416 }
417
418 sd_id128_to_string(boot_id, match + 9);
419 r = sd_journal_add_match(j, match, strlen(match));
420 if (r < 0) {
421 log_error("Failed to add match: %s", strerror(-r));
422 return r;
423 }
424
425 return 0;
426 }
427
428 static int add_priorities(sd_journal *j) {
429 char match[] = "PRIORITY=0";
430 int i, r;
431
432 assert(j);
433
434 if (arg_priorities == 0xFF)
435 return 0;
436
437 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
438 if (arg_priorities & (1 << i)) {
439 match[sizeof(match)-2] = '0' + i;
440
441 log_info("adding match %s", match);
442
443 r = sd_journal_add_match(j, match, strlen(match));
444 if (r < 0) {
445 log_error("Failed to add match: %s", strerror(-r));
446 return r;
447 }
448 }
449
450 return 0;
451 }
452
453 static int setup_keys(void) {
454 #ifdef HAVE_GCRYPT
455 size_t mpk_size, seed_size, state_size, i;
456 uint8_t *mpk, *seed, *state;
457 ssize_t l;
458 int fd = -1, r, attr = 0;
459 sd_id128_t machine, boot;
460 char *p = NULL, *k = NULL;
461 struct FSSHeader h;
462 uint64_t n;
463
464 r = sd_id128_get_machine(&machine);
465 if (r < 0) {
466 log_error("Failed to get machine ID: %s", strerror(-r));
467 return r;
468 }
469
470 r = sd_id128_get_boot(&boot);
471 if (r < 0) {
472 log_error("Failed to get boot ID: %s", strerror(-r));
473 return r;
474 }
475
476 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
477 SD_ID128_FORMAT_VAL(machine)) < 0)
478 return log_oom();
479
480 if (access(p, F_OK) >= 0) {
481 log_error("Sealing key file %s exists already.", p);
482 r = -EEXIST;
483 goto finish;
484 }
485
486 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
487 SD_ID128_FORMAT_VAL(machine)) < 0) {
488 r = log_oom();
489 goto finish;
490 }
491
492 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
493 mpk = alloca(mpk_size);
494
495 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
496 seed = alloca(seed_size);
497
498 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
499 state = alloca(state_size);
500
501 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
502 if (fd < 0) {
503 log_error("Failed to open /dev/random: %m");
504 r = -errno;
505 goto finish;
506 }
507
508 log_info("Generating seed...");
509 l = loop_read(fd, seed, seed_size, true);
510 if (l < 0 || (size_t) l != seed_size) {
511 log_error("Failed to read random seed: %s", strerror(EIO));
512 r = -EIO;
513 goto finish;
514 }
515
516 log_info("Generating key pair...");
517 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
518
519 log_info("Generating sealing key...");
520 FSPRG_GenState0(state, mpk, seed, seed_size);
521
522 assert(arg_interval > 0);
523
524 n = now(CLOCK_REALTIME);
525 n /= arg_interval;
526
527 close_nointr_nofail(fd);
528 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
529 if (fd < 0) {
530 log_error("Failed to open %s: %m", k);
531 r = -errno;
532 goto finish;
533 }
534
535 /* Enable secure remove, exclusion from dump, synchronous
536 * writing and in-place updating */
537 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
538 log_warning("FS_IOC_GETFLAGS failed: %m");
539
540 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
541
542 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
543 log_warning("FS_IOC_SETFLAGS failed: %m");
544
545 zero(h);
546 memcpy(h.signature, "KSHHRHLP", 8);
547 h.machine_id = machine;
548 h.boot_id = boot;
549 h.header_size = htole64(sizeof(h));
550 h.start_usec = htole64(n * arg_interval);
551 h.interval_usec = htole64(arg_interval);
552 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
553 h.fsprg_state_size = htole64(state_size);
554
555 l = loop_write(fd, &h, sizeof(h), false);
556 if (l < 0 || (size_t) l != sizeof(h)) {
557 log_error("Failed to write header: %s", strerror(EIO));
558 r = -EIO;
559 goto finish;
560 }
561
562 l = loop_write(fd, state, state_size, false);
563 if (l < 0 || (size_t) l != state_size) {
564 log_error("Failed to write state: %s", strerror(EIO));
565 r = -EIO;
566 goto finish;
567 }
568
569 if (link(k, p) < 0) {
570 log_error("Failed to link file: %m");
571 r = -errno;
572 goto finish;
573 }
574
575 if (isatty(STDOUT_FILENO)) {
576 fprintf(stderr,
577 "\n"
578 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
579 "the following local file. It should not be used on multiple hosts.\n"
580 "\n"
581 "\t%s\n"
582 "\n"
583 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
584 "at a safe location and should not be saved locally on disk.\n"
585 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
586 fflush(stderr);
587 }
588 for (i = 0; i < seed_size; i++) {
589 if (i > 0 && i % 3 == 0)
590 putchar('-');
591 printf("%02x", ((uint8_t*) seed)[i]);
592 }
593
594 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
595
596 if (isatty(STDOUT_FILENO)) {
597 char tsb[FORMAT_TIMESPAN_MAX];
598
599 fprintf(stderr,
600 ANSI_HIGHLIGHT_OFF "\n"
601 "The sealing key is automatically changed every %s.\n",
602 format_timespan(tsb, sizeof(tsb), arg_interval));
603 }
604
605 r = 0;
606
607 finish:
608 if (fd >= 0)
609 close_nointr_nofail(fd);
610
611 if (k) {
612 unlink(k);
613 free(k);
614 }
615
616 free(p);
617
618 return r;
619 #else
620 log_error("Forward-secure journal verification not available.");
621 #endif
622 }
623
624 static int verify(sd_journal *j) {
625 int r = 0;
626 Iterator i;
627 JournalFile *f;
628
629 assert(j);
630
631 HASHMAP_FOREACH(f, j->files, i) {
632 int k;
633 usec_t from, to, total;
634
635 #ifdef HAVE_GCRYPT
636 if (!arg_verify_key && journal_file_fss_enabled(f))
637 log_warning("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
638 #endif
639
640 k = journal_file_verify(f, arg_verify_key, &from, &to, &total, true);
641 if (k == -EINVAL) {
642 /* If the key was invalid give up right-away. */
643 return k;
644 } else if (k < 0) {
645 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
646 r = k;
647 } else {
648 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
649 log_info("PASS: %s", f->path);
650
651 if (arg_verify_key && journal_file_fss_enabled(f))
652 log_info("=> Validated from %s to %s, %s missing",
653 format_timestamp(a, sizeof(a), from),
654 format_timestamp(b, sizeof(b), to),
655 format_timespan(c, sizeof(c), total > to ? total - to : 0));
656 }
657 }
658
659 return r;
660 }
661
662 int main(int argc, char *argv[]) {
663 int r;
664 sd_journal *j = NULL;
665 unsigned line = 0;
666 bool need_seek = false;
667 sd_id128_t previous_boot_id;
668 bool previous_boot_id_valid = false;
669 bool have_pager;
670
671 log_parse_environment();
672 log_open();
673
674 r = parse_argv(argc, argv);
675 if (r <= 0)
676 goto finish;
677
678 if (arg_action == ACTION_NEW_ID128) {
679 r = generate_new_id128();
680 goto finish;
681 }
682
683 if (arg_action == ACTION_SETUP_KEYS) {
684 r = setup_keys();
685 goto finish;
686 }
687
688 if (arg_directory)
689 r = sd_journal_open_directory(&j, arg_directory, 0);
690 else
691 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
692
693 if (r < 0) {
694 log_error("Failed to open journal: %s", strerror(-r));
695 goto finish;
696 }
697
698 if (arg_action == ACTION_VERIFY) {
699 r = verify(j);
700 goto finish;
701 }
702
703 if (arg_action == ACTION_PRINT_HEADER) {
704 journal_print_header(j);
705 r = 0;
706 goto finish;
707 }
708
709 #ifdef HAVE_ACL
710 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
711 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
712 #endif
713
714 r = add_this_boot(j);
715 if (r < 0)
716 goto finish;
717
718 r = add_matches(j, argv + optind);
719 if (r < 0)
720 goto finish;
721
722 r = add_priorities(j);
723 if (r < 0)
724 goto finish;
725
726 if (!arg_quiet) {
727 usec_t start, end;
728 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
729
730 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
731 if (r < 0) {
732 log_error("Failed to get cutoff: %s", strerror(-r));
733 goto finish;
734 }
735
736 if (r > 0) {
737 if (arg_follow)
738 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
739 else
740 printf("Logs begin at %s, end at %s.\n",
741 format_timestamp(start_buf, sizeof(start_buf), start),
742 format_timestamp(end_buf, sizeof(end_buf), end));
743 }
744 }
745
746 if (arg_lines >= 0) {
747 r = sd_journal_seek_tail(j);
748 if (r < 0) {
749 log_error("Failed to seek to tail: %s", strerror(-r));
750 goto finish;
751 }
752
753 r = sd_journal_previous_skip(j, arg_lines);
754 } else {
755 r = sd_journal_seek_head(j);
756 if (r < 0) {
757 log_error("Failed to seek to head: %s", strerror(-r));
758 goto finish;
759 }
760
761 r = sd_journal_next(j);
762 }
763
764 if (r < 0) {
765 log_error("Failed to iterate through journal: %s", strerror(-r));
766 goto finish;
767 }
768
769 on_tty();
770 have_pager = !arg_no_pager && !arg_follow && pager_open();
771
772 if (arg_output == OUTPUT_JSON) {
773 fputc('[', stdout);
774 fflush(stdout);
775 }
776
777 for (;;) {
778 for (;;) {
779 sd_id128_t boot_id;
780 int flags =
781 arg_show_all * OUTPUT_SHOW_ALL |
782 have_pager * OUTPUT_FULL_WIDTH |
783 on_tty() * OUTPUT_COLOR;
784
785 if (need_seek) {
786 r = sd_journal_next(j);
787 if (r < 0) {
788 log_error("Failed to iterate through journal: %s", strerror(-r));
789 goto finish;
790 }
791 }
792
793 if (r == 0)
794 break;
795
796 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
797 if (r >= 0) {
798 if (previous_boot_id_valid &&
799 !sd_id128_equal(boot_id, previous_boot_id))
800 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
801
802 previous_boot_id = boot_id;
803 previous_boot_id_valid = true;
804 }
805
806 line ++;
807
808 r = output_journal(j, arg_output, line, 0, flags);
809 if (r < 0)
810 goto finish;
811
812 need_seek = true;
813 }
814
815 if (!arg_follow)
816 break;
817
818 r = sd_journal_wait(j, (uint64_t) -1);
819 if (r < 0) {
820 log_error("Couldn't wait for log event: %s", strerror(-r));
821 goto finish;
822 }
823 }
824
825 if (arg_output == OUTPUT_JSON)
826 fputs("\n]\n", stdout);
827
828 finish:
829 if (j)
830 sd_journal_close(j);
831
832 pager_close();
833
834 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
835 }