chiark / gitweb /
journalctl: be more friendly when informing about seal verification
[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                         arg_local = true;
237                         break;
238
239                 case ARG_INTERVAL:
240                         r = parse_usec(optarg, &arg_interval);
241                         if (r < 0 || arg_interval <= 0) {
242                                 log_error("Failed to parse sealing key change interval: %s", optarg);
243                                 return -EINVAL;
244                         }
245                         break;
246 #else
247                 case ARG_SETUP_KEYS:
248                 case ARG_VERIFY_KEY:
249                 case ARG_INTERVAL:
250                         log_error("Forward-secure sealing not available.");
251                         return -ENOTSUP;
252 #endif
253
254                 case 'p': {
255                         const char *dots;
256
257                         dots = strstr(optarg, "..");
258                         if (dots) {
259                                 char *a;
260                                 int from, to, i;
261
262                                 /* a range */
263                                 a = strndup(optarg, dots - optarg);
264                                 if (!a)
265                                         return log_oom();
266
267                                 from = log_level_from_string(a);
268                                 to = log_level_from_string(dots + 2);
269                                 free(a);
270
271                                 if (from < 0 || to < 0) {
272                                         log_error("Failed to parse log level range %s", optarg);
273                                         return -EINVAL;
274                                 }
275
276                                 arg_priorities = 0;
277
278                                 if (from < to) {
279                                         for (i = from; i <= to; i++)
280                                                 arg_priorities |= 1 << i;
281                                 } else {
282                                         for (i = to; i <= from; i++)
283                                                 arg_priorities |= 1 << i;
284                                 }
285
286                         } else {
287                                 int p, i;
288
289                                 p = log_level_from_string(optarg);
290                                 if (p < 0) {
291                                         log_error("Unknown log level %s", optarg);
292                                         return -EINVAL;
293                                 }
294
295                                 arg_priorities = 0;
296
297                                 for (i = 0; i <= p; i++)
298                                         arg_priorities |= 1 << i;
299                         }
300
301                         break;
302                 }
303
304                 case '?':
305                         return -EINVAL;
306
307                 default:
308                         log_error("Unknown option code %c", c);
309                         return -EINVAL;
310                 }
311         }
312
313         if (arg_follow && !arg_no_tail && arg_lines < 0)
314                 arg_lines = 10;
315
316         return 1;
317 }
318
319 static bool on_tty(void) {
320         static int t = -1;
321
322         /* Note that this is invoked relatively early, before we start
323          * the pager. That means the value we return reflects whether
324          * we originally were started on a tty, not if we currently
325          * are. But this is intended, since we want colour and so on
326          * when run in our own pager. */
327
328         if (_unlikely_(t < 0))
329                 t = isatty(STDOUT_FILENO) > 0;
330
331         return t;
332 }
333
334 static int generate_new_id128(void) {
335         sd_id128_t id;
336         int r;
337         unsigned i;
338
339         r = sd_id128_randomize(&id);
340         if (r < 0) {
341                 log_error("Failed to generate ID: %s", strerror(-r));
342                 return r;
343         }
344
345         printf("As string:\n"
346                SD_ID128_FORMAT_STR "\n\n"
347                "As UUID:\n"
348                "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
349                "As macro:\n"
350               "#define MESSAGE_XYZ SD_ID128_MAKE(",
351                SD_ID128_FORMAT_VAL(id),
352                SD_ID128_FORMAT_VAL(id));
353
354         for (i = 0; i < 16; i++)
355                 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
356
357         fputs(")\n", stdout);
358
359         return 0;
360 }
361
362 static int add_matches(sd_journal *j, char **args) {
363         char **i;
364         int r;
365
366         assert(j);
367
368         STRV_FOREACH(i, args) {
369
370                 if (streq(*i, "+"))
371                         r = sd_journal_add_disjunction(j);
372                 else if (path_is_absolute(*i)) {
373                         char *p, *t = NULL;
374                         const char *path;
375                         struct stat st;
376
377                         p = canonicalize_file_name(*i);
378                         path = p ? p : *i;
379
380                         if (stat(path, &st) < 0)  {
381                                 free(p);
382                                 log_error("Couldn't stat file: %m");
383                                 return -errno;
384                         }
385
386                         if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
387                                 t = strappend("_EXE=", path);
388                         else if (S_ISCHR(st.st_mode))
389                                 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
390                         else if (S_ISBLK(st.st_mode))
391                                 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
392                         else {
393                                 free(p);
394                                 log_error("File is not a device node, regular file or is not executable: %s", *i);
395                                 return -EINVAL;
396                         }
397
398                         free(p);
399
400                         if (!t)
401                                 return log_oom();
402
403                         r = sd_journal_add_match(j, t, 0);
404                         free(t);
405                 } else
406                         r = sd_journal_add_match(j, *i, 0);
407
408                 if (r < 0) {
409                         log_error("Failed to add match '%s': %s", *i, strerror(-r));
410                         return r;
411                 }
412         }
413
414         return 0;
415 }
416
417 static int add_this_boot(sd_journal *j) {
418         char match[9+32+1] = "_BOOT_ID=";
419         sd_id128_t boot_id;
420         int r;
421
422         assert(j);
423
424         if (!arg_this_boot)
425                 return 0;
426
427         r = sd_id128_get_boot(&boot_id);
428         if (r < 0) {
429                 log_error("Failed to get boot id: %s", strerror(-r));
430                 return r;
431         }
432
433         sd_id128_to_string(boot_id, match + 9);
434         r = sd_journal_add_match(j, match, strlen(match));
435         if (r < 0) {
436                 log_error("Failed to add match: %s", strerror(-r));
437                 return r;
438         }
439
440         return 0;
441 }
442
443 static int add_priorities(sd_journal *j) {
444         char match[] = "PRIORITY=0";
445         int i, r;
446
447         assert(j);
448
449         if (arg_priorities == 0xFF)
450                 return 0;
451
452         for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
453                 if (arg_priorities & (1 << i)) {
454                         match[sizeof(match)-2] = '0' + i;
455
456                         log_info("adding match %s", match);
457
458                         r = sd_journal_add_match(j, match, strlen(match));
459                         if (r < 0) {
460                                 log_error("Failed to add match: %s", strerror(-r));
461                                 return r;
462                         }
463                 }
464
465         return 0;
466 }
467
468 static int setup_keys(void) {
469 #ifdef HAVE_GCRYPT
470         size_t mpk_size, seed_size, state_size, i;
471         uint8_t *mpk, *seed, *state;
472         ssize_t l;
473         int fd = -1, r, attr = 0;
474         sd_id128_t machine, boot;
475         char *p = NULL, *k = NULL;
476         struct FSSHeader h;
477         uint64_t n;
478
479         r = sd_id128_get_machine(&machine);
480         if (r < 0) {
481                 log_error("Failed to get machine ID: %s", strerror(-r));
482                 return r;
483         }
484
485         r = sd_id128_get_boot(&boot);
486         if (r < 0) {
487                 log_error("Failed to get boot ID: %s", strerror(-r));
488                 return r;
489         }
490
491         if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
492                      SD_ID128_FORMAT_VAL(machine)) < 0)
493                 return log_oom();
494
495         if (access(p, F_OK) >= 0) {
496                 log_error("Sealing key file %s exists already.", p);
497                 r = -EEXIST;
498                 goto finish;
499         }
500
501         if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
502                      SD_ID128_FORMAT_VAL(machine)) < 0) {
503                 r = log_oom();
504                 goto finish;
505         }
506
507         mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
508         mpk = alloca(mpk_size);
509
510         seed_size = FSPRG_RECOMMENDED_SEEDLEN;
511         seed = alloca(seed_size);
512
513         state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
514         state = alloca(state_size);
515
516         fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
517         if (fd < 0) {
518                 log_error("Failed to open /dev/random: %m");
519                 r = -errno;
520                 goto finish;
521         }
522
523         log_info("Generating seed...");
524         l = loop_read(fd, seed, seed_size, true);
525         if (l < 0 || (size_t) l != seed_size) {
526                 log_error("Failed to read random seed: %s", strerror(EIO));
527                 r = -EIO;
528                 goto finish;
529         }
530
531         log_info("Generating key pair...");
532         FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
533
534         log_info("Generating sealing key...");
535         FSPRG_GenState0(state, mpk, seed, seed_size);
536
537         assert(arg_interval > 0);
538
539         n = now(CLOCK_REALTIME);
540         n /= arg_interval;
541
542         close_nointr_nofail(fd);
543         fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
544         if (fd < 0) {
545                 log_error("Failed to open %s: %m", k);
546                 r = -errno;
547                 goto finish;
548         }
549
550         /* Enable secure remove, exclusion from dump, synchronous
551          * writing and in-place updating */
552         if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
553                 log_warning("FS_IOC_GETFLAGS failed: %m");
554
555         attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
556
557         if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
558                 log_warning("FS_IOC_SETFLAGS failed: %m");
559
560         zero(h);
561         memcpy(h.signature, "KSHHRHLP", 8);
562         h.machine_id = machine;
563         h.boot_id = boot;
564         h.header_size = htole64(sizeof(h));
565         h.start_usec = htole64(n * arg_interval);
566         h.interval_usec = htole64(arg_interval);
567         h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
568         h.fsprg_state_size = htole64(state_size);
569
570         l = loop_write(fd, &h, sizeof(h), false);
571         if (l < 0 || (size_t) l != sizeof(h)) {
572                 log_error("Failed to write header: %s", strerror(EIO));
573                 r = -EIO;
574                 goto finish;
575         }
576
577         l = loop_write(fd, state, state_size, false);
578         if (l < 0 || (size_t) l != state_size) {
579                 log_error("Failed to write state: %s", strerror(EIO));
580                 r = -EIO;
581                 goto finish;
582         }
583
584         if (link(k, p) < 0) {
585                 log_error("Failed to link file: %m");
586                 r = -errno;
587                 goto finish;
588         }
589
590         if (isatty(STDOUT_FILENO)) {
591                 fprintf(stderr,
592                         "\n"
593                         "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
594                         "the following local file. This key file is automatically updated when the\n"
595                         "sealing key is advanced. It should not be used on multiple hosts.\n"
596                         "\n"
597                         "\t%s\n"
598                         "\n"
599                         "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
600                         "at a safe location and should not be saved locally on disk.\n"
601                         "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
602                 fflush(stderr);
603         }
604         for (i = 0; i < seed_size; i++) {
605                 if (i > 0 && i % 3 == 0)
606                         putchar('-');
607                 printf("%02x", ((uint8_t*) seed)[i]);
608         }
609
610         printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
611
612         if (isatty(STDOUT_FILENO)) {
613                 char tsb[FORMAT_TIMESPAN_MAX], *hn;
614
615                 fprintf(stderr,
616                         ANSI_HIGHLIGHT_OFF "\n"
617                         "The sealing key is automatically changed every %s.\n",
618                         format_timespan(tsb, sizeof(tsb), arg_interval));
619
620                 hn = gethostname_malloc();
621
622                 if (hn) {
623                         hostname_cleanup(hn);
624                         fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
625                 } else
626                         fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
627
628 #ifdef HAVE_QRENCODE
629                 fprintf(stderr, "\nTo transfer the verification key to your phone please scan the QR code below:\n\n");
630                 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
631 #endif
632                 free(hn);
633         }
634
635         r = 0;
636
637 finish:
638         if (fd >= 0)
639                 close_nointr_nofail(fd);
640
641         if (k) {
642                 unlink(k);
643                 free(k);
644         }
645
646         free(p);
647
648         return r;
649 #else
650         log_error("Forward-secure sealing not available.");
651         return -ENOTSUP;
652 #endif
653 }
654
655 static int verify(sd_journal *j) {
656         int r = 0;
657         Iterator i;
658         JournalFile *f;
659
660         assert(j);
661
662         log_show_color(true);
663
664         HASHMAP_FOREACH(f, j->files, i) {
665                 int k;
666                 usec_t from, to, total;
667
668 #ifdef HAVE_GCRYPT
669                 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
670                         log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
671 #endif
672
673                 k = journal_file_verify(f, arg_verify_key, &from, &to, &total, true);
674                 if (k == -EINVAL) {
675                         /* If the key was invalid give up right-away. */
676                         return k;
677                 } else if (k < 0) {
678                         log_warning("FAIL: %s (%s)", f->path, strerror(-k));
679                         r = k;
680                 } else {
681                         char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
682                         log_info("PASS: %s", f->path);
683
684                         if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
685                                 if (from > 0) {
686                                         log_info("=> Validated from %s to %s, final %s entries not sealed.",
687                                                  format_timestamp(a, sizeof(a), from),
688                                                  format_timestamp(b, sizeof(b), to),
689                                                  format_timespan(c, sizeof(c), total > to ? total - to : 0));
690                                 } else if (total > 0)
691                                         log_info("=> No sealing yet, %s of entries not sealed.",
692                                                  format_timespan(c, sizeof(c), total));
693                                 else
694                                         log_info("=> No sealing yet, no entries in file.");
695                         }
696                 }
697         }
698
699         return r;
700 }
701
702 int main(int argc, char *argv[]) {
703         int r;
704         sd_journal *j = NULL;
705         unsigned line = 0;
706         bool need_seek = false;
707         sd_id128_t previous_boot_id;
708         bool previous_boot_id_valid = false;
709         bool have_pager;
710
711         log_parse_environment();
712         log_open();
713
714         r = parse_argv(argc, argv);
715         if (r <= 0)
716                 goto finish;
717
718         if (arg_action == ACTION_NEW_ID128) {
719                 r = generate_new_id128();
720                 goto finish;
721         }
722
723         if (arg_action == ACTION_SETUP_KEYS) {
724                 r = setup_keys();
725                 goto finish;
726         }
727
728         if (arg_directory)
729                 r = sd_journal_open_directory(&j, arg_directory, 0);
730         else
731                 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
732
733         if (r < 0) {
734                 log_error("Failed to open journal: %s", strerror(-r));
735                 goto finish;
736         }
737
738         if (arg_action == ACTION_VERIFY) {
739                 r = verify(j);
740                 goto finish;
741         }
742
743         if (arg_action == ACTION_PRINT_HEADER) {
744                 journal_print_header(j);
745                 r = 0;
746                 goto finish;
747         }
748
749 #ifdef HAVE_ACL
750         if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
751                 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
752 #endif
753
754         r = add_this_boot(j);
755         if (r < 0)
756                 goto finish;
757
758         r = add_matches(j, argv + optind);
759         if (r < 0)
760                 goto finish;
761
762         r = add_priorities(j);
763         if (r < 0)
764                 goto finish;
765
766         if (!arg_quiet) {
767                 usec_t start, end;
768                 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
769
770                 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
771                 if (r < 0) {
772                         log_error("Failed to get cutoff: %s", strerror(-r));
773                         goto finish;
774                 }
775
776                 if (r > 0) {
777                         if (arg_follow)
778                                 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
779                         else
780                                 printf("Logs begin at %s, end at %s.\n",
781                                        format_timestamp(start_buf, sizeof(start_buf), start),
782                                        format_timestamp(end_buf, sizeof(end_buf), end));
783                 }
784         }
785
786         if (arg_lines >= 0) {
787                 r = sd_journal_seek_tail(j);
788                 if (r < 0) {
789                         log_error("Failed to seek to tail: %s", strerror(-r));
790                         goto finish;
791                 }
792
793                 r = sd_journal_previous_skip(j, arg_lines);
794         } else {
795                 r = sd_journal_seek_head(j);
796                 if (r < 0) {
797                         log_error("Failed to seek to head: %s", strerror(-r));
798                         goto finish;
799                 }
800
801                 r = sd_journal_next(j);
802         }
803
804         if (r < 0) {
805                 log_error("Failed to iterate through journal: %s", strerror(-r));
806                 goto finish;
807         }
808
809         on_tty();
810         have_pager = !arg_no_pager && !arg_follow && pager_open();
811
812         if (arg_output == OUTPUT_JSON) {
813                 fputc('[', stdout);
814                 fflush(stdout);
815         }
816
817         for (;;) {
818                 for (;;) {
819                         sd_id128_t boot_id;
820                         int flags =
821                                 arg_show_all * OUTPUT_SHOW_ALL |
822                                 have_pager * OUTPUT_FULL_WIDTH |
823                                 on_tty() * OUTPUT_COLOR;
824
825                         if (need_seek) {
826                                 r = sd_journal_next(j);
827                                 if (r < 0) {
828                                         log_error("Failed to iterate through journal: %s", strerror(-r));
829                                         goto finish;
830                                 }
831                         }
832
833                         if (r == 0)
834                                 break;
835
836                         r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
837                         if (r >= 0) {
838                                 if (previous_boot_id_valid &&
839                                     !sd_id128_equal(boot_id, previous_boot_id))
840                                         printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
841
842                                 previous_boot_id = boot_id;
843                                 previous_boot_id_valid = true;
844                         }
845
846                         line ++;
847
848                         r = output_journal(j, arg_output, line, 0, flags);
849                         if (r < 0)
850                                 goto finish;
851
852                         need_seek = true;
853                 }
854
855                 if (!arg_follow)
856                         break;
857
858                 r = sd_journal_wait(j, (uint64_t) -1);
859                 if (r < 0) {
860                         log_error("Couldn't wait for log event: %s", strerror(-r));
861                         goto finish;
862                 }
863         }
864
865         if (arg_output == OUTPUT_JSON)
866                 fputs("\n]\n", stdout);
867
868 finish:
869         if (j)
870                 sd_journal_close(j);
871
872         pager_close();
873
874         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
875 }