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