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