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