chiark / gitweb /
0bbf4a045d3b289bdb2d84f480ad660b2ade2aeb
[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         HASHMAP_FOREACH(f, j->files, i) {
663                 int k;
664                 usec_t from, to, total;
665
666 #ifdef HAVE_GCRYPT
667                 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
668                         log_warning("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
669 #endif
670
671                 k = journal_file_verify(f, arg_verify_key, &from, &to, &total, true);
672                 if (k == -EINVAL) {
673                         /* If the key was invalid give up right-away. */
674                         return k;
675                 } else if (k < 0) {
676                         log_warning("FAIL: %s (%s)", f->path, strerror(-k));
677                         r = k;
678                 } else {
679                         char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
680                         log_info("PASS: %s", f->path);
681
682                         if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
683                                 log_info("=> Validated from %s to %s, %s missing",
684                                          format_timestamp(a, sizeof(a), from),
685                                          format_timestamp(b, sizeof(b), to),
686                                          format_timespan(c, sizeof(c), total > to ? total - to : 0));
687                 }
688         }
689
690         return r;
691 }
692
693 int main(int argc, char *argv[]) {
694         int r;
695         sd_journal *j = NULL;
696         unsigned line = 0;
697         bool need_seek = false;
698         sd_id128_t previous_boot_id;
699         bool previous_boot_id_valid = false;
700         bool have_pager;
701
702         log_parse_environment();
703         log_open();
704
705         r = parse_argv(argc, argv);
706         if (r <= 0)
707                 goto finish;
708
709         if (arg_action == ACTION_NEW_ID128) {
710                 r = generate_new_id128();
711                 goto finish;
712         }
713
714         if (arg_action == ACTION_SETUP_KEYS) {
715                 r = setup_keys();
716                 goto finish;
717         }
718
719         if (arg_directory)
720                 r = sd_journal_open_directory(&j, arg_directory, 0);
721         else
722                 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
723
724         if (r < 0) {
725                 log_error("Failed to open journal: %s", strerror(-r));
726                 goto finish;
727         }
728
729         if (arg_action == ACTION_VERIFY) {
730                 r = verify(j);
731                 goto finish;
732         }
733
734         if (arg_action == ACTION_PRINT_HEADER) {
735                 journal_print_header(j);
736                 r = 0;
737                 goto finish;
738         }
739
740 #ifdef HAVE_ACL
741         if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
742                 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
743 #endif
744
745         r = add_this_boot(j);
746         if (r < 0)
747                 goto finish;
748
749         r = add_matches(j, argv + optind);
750         if (r < 0)
751                 goto finish;
752
753         r = add_priorities(j);
754         if (r < 0)
755                 goto finish;
756
757         if (!arg_quiet) {
758                 usec_t start, end;
759                 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
760
761                 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
762                 if (r < 0) {
763                         log_error("Failed to get cutoff: %s", strerror(-r));
764                         goto finish;
765                 }
766
767                 if (r > 0) {
768                         if (arg_follow)
769                                 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
770                         else
771                                 printf("Logs begin at %s, end at %s.\n",
772                                        format_timestamp(start_buf, sizeof(start_buf), start),
773                                        format_timestamp(end_buf, sizeof(end_buf), end));
774                 }
775         }
776
777         if (arg_lines >= 0) {
778                 r = sd_journal_seek_tail(j);
779                 if (r < 0) {
780                         log_error("Failed to seek to tail: %s", strerror(-r));
781                         goto finish;
782                 }
783
784                 r = sd_journal_previous_skip(j, arg_lines);
785         } else {
786                 r = sd_journal_seek_head(j);
787                 if (r < 0) {
788                         log_error("Failed to seek to head: %s", strerror(-r));
789                         goto finish;
790                 }
791
792                 r = sd_journal_next(j);
793         }
794
795         if (r < 0) {
796                 log_error("Failed to iterate through journal: %s", strerror(-r));
797                 goto finish;
798         }
799
800         on_tty();
801         have_pager = !arg_no_pager && !arg_follow && pager_open();
802
803         if (arg_output == OUTPUT_JSON) {
804                 fputc('[', stdout);
805                 fflush(stdout);
806         }
807
808         for (;;) {
809                 for (;;) {
810                         sd_id128_t boot_id;
811                         int flags =
812                                 arg_show_all * OUTPUT_SHOW_ALL |
813                                 have_pager * OUTPUT_FULL_WIDTH |
814                                 on_tty() * OUTPUT_COLOR;
815
816                         if (need_seek) {
817                                 r = sd_journal_next(j);
818                                 if (r < 0) {
819                                         log_error("Failed to iterate through journal: %s", strerror(-r));
820                                         goto finish;
821                                 }
822                         }
823
824                         if (r == 0)
825                                 break;
826
827                         r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
828                         if (r >= 0) {
829                                 if (previous_boot_id_valid &&
830                                     !sd_id128_equal(boot_id, previous_boot_id))
831                                         printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
832
833                                 previous_boot_id = boot_id;
834                                 previous_boot_id_valid = true;
835                         }
836
837                         line ++;
838
839                         r = output_journal(j, arg_output, line, 0, flags);
840                         if (r < 0)
841                                 goto finish;
842
843                         need_seek = true;
844                 }
845
846                 if (!arg_follow)
847                         break;
848
849                 r = sd_journal_wait(j, (uint64_t) -1);
850                 if (r < 0) {
851                         log_error("Couldn't wait for log event: %s", strerror(-r));
852                         goto finish;
853                 }
854         }
855
856         if (arg_output == OUTPUT_JSON)
857                 fputs("\n]\n", stdout);
858
859 finish:
860         if (j)
861                 sd_journal_close(j);
862
863         pager_close();
864
865         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
866 }