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