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