1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
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.
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.
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/>.
33 #include <sys/ioctl.h>
36 #include <systemd/sd-journal.h>
40 #include "path-util.h"
43 #include "logs-show.h"
45 #include "journal-internal.h"
46 #include "journal-def.h"
47 #include "journal-verify.h"
48 #include "journal-authenticate.h"
51 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
53 static OutputMode arg_output = OUTPUT_SHORT;
54 static bool arg_follow = false;
55 static bool arg_show_all = false;
56 static bool arg_no_pager = false;
57 static int arg_lines = -1;
58 static bool arg_no_tail = false;
59 static bool arg_quiet = false;
60 static bool arg_local = false;
61 static bool arg_this_boot = false;
62 static const char *arg_directory = NULL;
63 static int arg_priorities = 0xFF;
64 static const char *arg_verify_key = NULL;
66 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
75 } arg_action = ACTION_SHOW;
77 static int help(void) {
79 printf("%s [OPTIONS...] [MATCH]\n\n"
80 "Send control commands to or query the journal.\n\n"
81 " -h --help Show this help\n"
82 " --version Show package version\n"
83 " --no-pager Do not pipe output into a pager\n"
84 " -a --all Show all fields, including long and unprintable\n"
85 " -f --follow Follow journal\n"
86 " -n --lines=INTEGER Journal entries to show\n"
87 " --no-tail Show all lines, even in follow mode\n"
88 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
89 " verbose, export, json, cat)\n"
90 " -q --quiet Don't show privilege warning\n"
91 " -l --local Only local entries\n"
92 " -b --this-boot Show data only from current boot\n"
93 " -D --directory=PATH Show journal files from directory\n"
94 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
96 " --new-id128 Generate a new 128 Bit ID\n"
97 " --header Show journal header information\n"
99 " --setup-keys Generate new FSS key pair\n"
100 " --interval=TIME Time interval for changing the FSS sealing key\n"
101 " --verify Verify journal file consistency\n"
102 " --verify-key=KEY Specify FSS verification key\n"
104 , program_invocation_short_name);
109 static int parse_argv(int argc, char *argv[]) {
123 static const struct option options[] = {
124 { "help", no_argument, NULL, 'h' },
125 { "version" , no_argument, NULL, ARG_VERSION },
126 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
127 { "follow", no_argument, NULL, 'f' },
128 { "output", required_argument, NULL, 'o' },
129 { "all", no_argument, NULL, 'a' },
130 { "lines", required_argument, NULL, 'n' },
131 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
132 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
133 { "quiet", no_argument, NULL, 'q' },
134 { "local", no_argument, NULL, 'l' },
135 { "this-boot", no_argument, NULL, 'b' },
136 { "directory", required_argument, NULL, 'D' },
137 { "header", no_argument, NULL, ARG_HEADER },
138 { "priority", no_argument, NULL, 'p' },
139 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
140 { "interval", required_argument, NULL, ARG_INTERVAL },
141 { "verify", no_argument, NULL, ARG_VERIFY },
142 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
151 while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
160 puts(PACKAGE_STRING);
162 puts(SYSTEMD_FEATURES);
174 arg_output = output_mode_from_string(optarg);
175 if (arg_output < 0) {
176 log_error("Unknown output '%s'.", optarg);
187 r = safe_atoi(optarg, &arg_lines);
188 if (r < 0 || arg_lines < 0) {
189 log_error("Failed to parse lines '%s'", optarg);
199 arg_action = ACTION_NEW_ID128;
211 arg_this_boot = true;
215 arg_directory = optarg;
219 arg_action = ACTION_PRINT_HEADER;
223 arg_action = ACTION_VERIFY;
228 arg_action = ACTION_SETUP_KEYS;
233 arg_action = ACTION_VERIFY;
234 arg_verify_key = optarg;
238 r = parse_usec(optarg, &arg_interval);
239 if (r < 0 || arg_interval <= 0) {
240 log_error("Failed to parse sealing key change interval: %s", optarg);
248 log_error("Forward-secure sealing not available.");
255 dots = strstr(optarg, "..");
261 a = strndup(optarg, dots - optarg);
265 from = log_level_from_string(a);
266 to = log_level_from_string(dots + 2);
269 if (from < 0 || to < 0) {
270 log_error("Failed to parse log level range %s", optarg);
277 for (i = from; i <= to; i++)
278 arg_priorities |= 1 << i;
280 for (i = to; i <= from; i++)
281 arg_priorities |= 1 << i;
287 p = log_level_from_string(optarg);
289 log_error("Unknown log level %s", optarg);
295 for (i = 0; i <= p; i++)
296 arg_priorities |= 1 << i;
306 log_error("Unknown option code %c", c);
311 if (arg_follow && !arg_no_tail && arg_lines < 0)
317 static bool on_tty(void) {
320 /* Note that this is invoked relatively early, before we start
321 * the pager. That means the value we return reflects whether
322 * we originally were started on a tty, not if we currently
323 * are. But this is intended, since we want colour and so on
324 * when run in our own pager. */
326 if (_unlikely_(t < 0))
327 t = isatty(STDOUT_FILENO) > 0;
332 static int generate_new_id128(void) {
337 r = sd_id128_randomize(&id);
339 log_error("Failed to generate ID: %s", strerror(-r));
343 printf("As string:\n"
344 SD_ID128_FORMAT_STR "\n\n"
346 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
348 "#define MESSAGE_XYZ SD_ID128_MAKE(",
349 SD_ID128_FORMAT_VAL(id),
350 SD_ID128_FORMAT_VAL(id));
352 for (i = 0; i < 16; i++)
353 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
355 fputs(")\n", stdout);
360 static int add_matches(sd_journal *j, char **args) {
366 STRV_FOREACH(i, args) {
369 r = sd_journal_add_disjunction(j);
370 else if (path_is_absolute(*i)) {
375 p = canonicalize_file_name(*i);
378 if (stat(path, &st) < 0) {
380 log_error("Couldn't stat file: %m");
384 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
385 t = strappend("_EXE=", path);
386 else if (S_ISCHR(st.st_mode))
387 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
388 else if (S_ISBLK(st.st_mode))
389 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
392 log_error("File is not a device node, regular file or is not executable: %s", *i);
401 r = sd_journal_add_match(j, t, 0);
404 r = sd_journal_add_match(j, *i, 0);
407 log_error("Failed to add match '%s': %s", *i, strerror(-r));
415 static int add_this_boot(sd_journal *j) {
416 char match[9+32+1] = "_BOOT_ID=";
425 r = sd_id128_get_boot(&boot_id);
427 log_error("Failed to get boot id: %s", strerror(-r));
431 sd_id128_to_string(boot_id, match + 9);
432 r = sd_journal_add_match(j, match, strlen(match));
434 log_error("Failed to add match: %s", strerror(-r));
441 static int add_priorities(sd_journal *j) {
442 char match[] = "PRIORITY=0";
447 if (arg_priorities == 0xFF)
450 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
451 if (arg_priorities & (1 << i)) {
452 match[sizeof(match)-2] = '0' + i;
454 log_info("adding match %s", match);
456 r = sd_journal_add_match(j, match, strlen(match));
458 log_error("Failed to add match: %s", strerror(-r));
466 static int setup_keys(void) {
468 size_t mpk_size, seed_size, state_size, i;
469 uint8_t *mpk, *seed, *state;
471 int fd = -1, r, attr = 0;
472 sd_id128_t machine, boot;
473 char *p = NULL, *k = NULL;
477 r = sd_id128_get_machine(&machine);
479 log_error("Failed to get machine ID: %s", strerror(-r));
483 r = sd_id128_get_boot(&boot);
485 log_error("Failed to get boot ID: %s", strerror(-r));
489 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
490 SD_ID128_FORMAT_VAL(machine)) < 0)
493 if (access(p, F_OK) >= 0) {
494 log_error("Sealing key file %s exists already.", p);
499 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
500 SD_ID128_FORMAT_VAL(machine)) < 0) {
505 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
506 mpk = alloca(mpk_size);
508 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
509 seed = alloca(seed_size);
511 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
512 state = alloca(state_size);
514 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
516 log_error("Failed to open /dev/random: %m");
521 log_info("Generating seed...");
522 l = loop_read(fd, seed, seed_size, true);
523 if (l < 0 || (size_t) l != seed_size) {
524 log_error("Failed to read random seed: %s", strerror(EIO));
529 log_info("Generating key pair...");
530 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
532 log_info("Generating sealing key...");
533 FSPRG_GenState0(state, mpk, seed, seed_size);
535 assert(arg_interval > 0);
537 n = now(CLOCK_REALTIME);
540 close_nointr_nofail(fd);
541 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
543 log_error("Failed to open %s: %m", k);
548 /* Enable secure remove, exclusion from dump, synchronous
549 * writing and in-place updating */
550 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
551 log_warning("FS_IOC_GETFLAGS failed: %m");
553 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
555 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
556 log_warning("FS_IOC_SETFLAGS failed: %m");
559 memcpy(h.signature, "KSHHRHLP", 8);
560 h.machine_id = machine;
562 h.header_size = htole64(sizeof(h));
563 h.start_usec = htole64(n * arg_interval);
564 h.interval_usec = htole64(arg_interval);
565 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
566 h.fsprg_state_size = htole64(state_size);
568 l = loop_write(fd, &h, sizeof(h), false);
569 if (l < 0 || (size_t) l != sizeof(h)) {
570 log_error("Failed to write header: %s", strerror(EIO));
575 l = loop_write(fd, state, state_size, false);
576 if (l < 0 || (size_t) l != state_size) {
577 log_error("Failed to write state: %s", strerror(EIO));
582 if (link(k, p) < 0) {
583 log_error("Failed to link file: %m");
588 if (isatty(STDOUT_FILENO)) {
591 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
592 "the following local file. It should not be used on multiple hosts.\n"
596 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
597 "at a safe location and should not be saved locally on disk.\n"
598 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
601 for (i = 0; i < seed_size; i++) {
602 if (i > 0 && i % 3 == 0)
604 printf("%02x", ((uint8_t*) seed)[i]);
607 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
609 if (isatty(STDOUT_FILENO)) {
610 char tsb[FORMAT_TIMESPAN_MAX];
613 ANSI_HIGHLIGHT_OFF "\n"
614 "The sealing key is automatically changed every %s.\n",
615 format_timespan(tsb, sizeof(tsb), arg_interval));
622 close_nointr_nofail(fd);
633 log_error("Forward-secure sealing not available.");
638 static int verify(sd_journal *j) {
645 HASHMAP_FOREACH(f, j->files, i) {
647 usec_t from, to, total;
650 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
651 log_warning("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
654 k = journal_file_verify(f, arg_verify_key, &from, &to, &total, true);
656 /* If the key was invalid give up right-away. */
659 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
662 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
663 log_info("PASS: %s", f->path);
665 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
666 log_info("=> Validated from %s to %s, %s missing",
667 format_timestamp(a, sizeof(a), from),
668 format_timestamp(b, sizeof(b), to),
669 format_timespan(c, sizeof(c), total > to ? total - to : 0));
676 int main(int argc, char *argv[]) {
678 sd_journal *j = NULL;
680 bool need_seek = false;
681 sd_id128_t previous_boot_id;
682 bool previous_boot_id_valid = false;
685 log_parse_environment();
688 r = parse_argv(argc, argv);
692 if (arg_action == ACTION_NEW_ID128) {
693 r = generate_new_id128();
697 if (arg_action == ACTION_SETUP_KEYS) {
703 r = sd_journal_open_directory(&j, arg_directory, 0);
705 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
708 log_error("Failed to open journal: %s", strerror(-r));
712 if (arg_action == ACTION_VERIFY) {
717 if (arg_action == ACTION_PRINT_HEADER) {
718 journal_print_header(j);
724 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
725 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
728 r = add_this_boot(j);
732 r = add_matches(j, argv + optind);
736 r = add_priorities(j);
742 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
744 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
746 log_error("Failed to get cutoff: %s", strerror(-r));
752 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
754 printf("Logs begin at %s, end at %s.\n",
755 format_timestamp(start_buf, sizeof(start_buf), start),
756 format_timestamp(end_buf, sizeof(end_buf), end));
760 if (arg_lines >= 0) {
761 r = sd_journal_seek_tail(j);
763 log_error("Failed to seek to tail: %s", strerror(-r));
767 r = sd_journal_previous_skip(j, arg_lines);
769 r = sd_journal_seek_head(j);
771 log_error("Failed to seek to head: %s", strerror(-r));
775 r = sd_journal_next(j);
779 log_error("Failed to iterate through journal: %s", strerror(-r));
784 have_pager = !arg_no_pager && !arg_follow && pager_open();
786 if (arg_output == OUTPUT_JSON) {
795 arg_show_all * OUTPUT_SHOW_ALL |
796 have_pager * OUTPUT_FULL_WIDTH |
797 on_tty() * OUTPUT_COLOR;
800 r = sd_journal_next(j);
802 log_error("Failed to iterate through journal: %s", strerror(-r));
810 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
812 if (previous_boot_id_valid &&
813 !sd_id128_equal(boot_id, previous_boot_id))
814 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
816 previous_boot_id = boot_id;
817 previous_boot_id_valid = true;
822 r = output_journal(j, arg_output, line, 0, flags);
832 r = sd_journal_wait(j, (uint64_t) -1);
834 log_error("Couldn't wait for log event: %s", strerror(-r));
839 if (arg_output == OUTPUT_JSON)
840 fputs("\n]\n", stdout);
848 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;