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