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