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