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/>.
34 #include <sys/ioctl.h>
39 #include <systemd/sd-journal.h>
43 #include "path-util.h"
46 #include "logs-show.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"
55 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
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;
71 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
73 static usec_t arg_since, arg_until;
74 static bool arg_since_set = false, arg_until_set = false;
83 } arg_action = ACTION_SHOW;
85 static int help(void) {
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"
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"
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"
116 , program_invocation_short_name);
121 static int parse_argv(int argc, char *argv[]) {
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 },
170 while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:", options, NULL)) >= 0) {
179 puts(PACKAGE_STRING);
181 puts(SYSTEMD_FEATURES);
190 signal(SIGWINCH, columns_cache_reset);
194 arg_output = output_mode_from_string(optarg);
195 if (arg_output < 0) {
196 log_error("Unknown output '%s'.", optarg);
208 r = safe_atou(optarg, &arg_lines);
209 if (r < 0 || arg_lines <= 0) {
210 log_error("Failed to parse lines '%s'", optarg);
223 arg_action = ACTION_NEW_ID128;
235 arg_this_boot = true;
239 arg_directory = optarg;
247 arg_action = ACTION_PRINT_HEADER;
251 arg_action = ACTION_VERIFY;
255 arg_action = ACTION_DISK_USAGE;
260 arg_action = ACTION_SETUP_KEYS;
265 arg_action = ACTION_VERIFY;
266 arg_verify_key = optarg;
271 r = parse_usec(optarg, &arg_interval);
272 if (r < 0 || arg_interval <= 0) {
273 log_error("Failed to parse sealing key change interval: %s", optarg);
281 log_error("Forward-secure sealing not available.");
288 dots = strstr(optarg, "..");
294 a = strndup(optarg, dots - optarg);
298 from = log_level_from_string(a);
299 to = log_level_from_string(dots + 2);
302 if (from < 0 || to < 0) {
303 log_error("Failed to parse log level range %s", optarg);
310 for (i = from; i <= to; i++)
311 arg_priorities |= 1 << i;
313 for (i = to; i <= from; i++)
314 arg_priorities |= 1 << i;
320 p = log_level_from_string(optarg);
322 log_error("Unknown log level %s", optarg);
328 for (i = 0; i <= p; i++)
329 arg_priorities |= 1 << i;
336 r = parse_timestamp(optarg, &arg_since);
338 log_error("Failed to parse timestamp: %s", optarg);
341 arg_since_set = true;
345 r = parse_timestamp(optarg, &arg_until);
347 log_error("Failed to parse timestamp: %s", optarg);
350 arg_until_set = true;
357 log_error("Unknown option code %c", c);
362 if (arg_follow && !arg_no_tail && arg_lines <= 0)
365 if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) {
366 log_error("--since= must be before --until=.");
370 if (arg_cursor && arg_since_set) {
371 log_error("Please specify either --since= or --cursor=, not both.");
378 static bool on_tty(void) {
381 /* Note that this is invoked relatively early, before we start
382 * the pager. That means the value we return reflects whether
383 * we originally were started on a tty, not if we currently
384 * are. But this is intended, since we want colour and so on
385 * when run in our own pager. */
387 if (_unlikely_(t < 0))
388 t = isatty(STDOUT_FILENO) > 0;
393 static int generate_new_id128(void) {
398 r = sd_id128_randomize(&id);
400 log_error("Failed to generate ID: %s", strerror(-r));
404 printf("As string:\n"
405 SD_ID128_FORMAT_STR "\n\n"
407 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
409 "#define MESSAGE_XYZ SD_ID128_MAKE(",
410 SD_ID128_FORMAT_VAL(id),
411 SD_ID128_FORMAT_VAL(id));
413 for (i = 0; i < 16; i++)
414 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
416 fputs(")\n", stdout);
421 static int add_matches(sd_journal *j, char **args) {
427 STRV_FOREACH(i, args) {
430 r = sd_journal_add_disjunction(j);
431 else if (path_is_absolute(*i)) {
436 p = canonicalize_file_name(*i);
439 if (stat(path, &st) < 0) {
441 log_error("Couldn't stat file: %m");
445 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
446 t = strappend("_EXE=", path);
447 else if (S_ISCHR(st.st_mode))
448 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
449 else if (S_ISBLK(st.st_mode))
450 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
453 log_error("File is not a device node, regular file or is not executable: %s", *i);
462 r = sd_journal_add_match(j, t, 0);
465 r = sd_journal_add_match(j, *i, 0);
468 log_error("Failed to add match '%s': %s", *i, strerror(-r));
476 static int add_this_boot(sd_journal *j) {
477 char match[9+32+1] = "_BOOT_ID=";
486 r = sd_id128_get_boot(&boot_id);
488 log_error("Failed to get boot id: %s", strerror(-r));
492 sd_id128_to_string(boot_id, match + 9);
493 r = sd_journal_add_match(j, match, strlen(match));
495 log_error("Failed to add match: %s", strerror(-r));
502 static int add_priorities(sd_journal *j) {
503 char match[] = "PRIORITY=0";
508 if (arg_priorities == 0xFF)
511 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
512 if (arg_priorities & (1 << i)) {
513 match[sizeof(match)-2] = '0' + i;
515 log_info("adding match %s", match);
517 r = sd_journal_add_match(j, match, strlen(match));
519 log_error("Failed to add match: %s", strerror(-r));
527 static int setup_keys(void) {
529 size_t mpk_size, seed_size, state_size, i;
530 uint8_t *mpk, *seed, *state;
532 int fd = -1, r, attr = 0;
533 sd_id128_t machine, boot;
534 char *p = NULL, *k = NULL;
538 r = sd_id128_get_machine(&machine);
540 log_error("Failed to get machine ID: %s", strerror(-r));
544 r = sd_id128_get_boot(&boot);
546 log_error("Failed to get boot ID: %s", strerror(-r));
550 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
551 SD_ID128_FORMAT_VAL(machine)) < 0)
554 if (access(p, F_OK) >= 0) {
555 log_error("Sealing key file %s exists already.", p);
560 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
561 SD_ID128_FORMAT_VAL(machine)) < 0) {
566 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
567 mpk = alloca(mpk_size);
569 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
570 seed = alloca(seed_size);
572 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
573 state = alloca(state_size);
575 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
577 log_error("Failed to open /dev/random: %m");
582 log_info("Generating seed...");
583 l = loop_read(fd, seed, seed_size, true);
584 if (l < 0 || (size_t) l != seed_size) {
585 log_error("Failed to read random seed: %s", strerror(EIO));
590 log_info("Generating key pair...");
591 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
593 log_info("Generating sealing key...");
594 FSPRG_GenState0(state, mpk, seed, seed_size);
596 assert(arg_interval > 0);
598 n = now(CLOCK_REALTIME);
601 close_nointr_nofail(fd);
602 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
604 log_error("Failed to open %s: %m", k);
609 /* Enable secure remove, exclusion from dump, synchronous
610 * writing and in-place updating */
611 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
612 log_warning("FS_IOC_GETFLAGS failed: %m");
614 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
616 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
617 log_warning("FS_IOC_SETFLAGS failed: %m");
620 memcpy(h.signature, "KSHHRHLP", 8);
621 h.machine_id = machine;
623 h.header_size = htole64(sizeof(h));
624 h.start_usec = htole64(n * arg_interval);
625 h.interval_usec = htole64(arg_interval);
626 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
627 h.fsprg_state_size = htole64(state_size);
629 l = loop_write(fd, &h, sizeof(h), false);
630 if (l < 0 || (size_t) l != sizeof(h)) {
631 log_error("Failed to write header: %s", strerror(EIO));
636 l = loop_write(fd, state, state_size, false);
637 if (l < 0 || (size_t) l != state_size) {
638 log_error("Failed to write state: %s", strerror(EIO));
643 if (link(k, p) < 0) {
644 log_error("Failed to link file: %m");
649 if (isatty(STDOUT_FILENO)) {
652 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
653 "the following local file. This key file is automatically updated when the\n"
654 "sealing key is advanced. It should not be used on multiple hosts.\n"
658 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
659 "at a safe location and should not be saved locally on disk.\n"
660 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
663 for (i = 0; i < seed_size; i++) {
664 if (i > 0 && i % 3 == 0)
666 printf("%02x", ((uint8_t*) seed)[i]);
669 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
671 if (isatty(STDOUT_FILENO)) {
672 char tsb[FORMAT_TIMESPAN_MAX], *hn;
675 ANSI_HIGHLIGHT_OFF "\n"
676 "The sealing key is automatically changed every %s.\n",
677 format_timespan(tsb, sizeof(tsb), arg_interval));
679 hn = gethostname_malloc();
682 hostname_cleanup(hn);
683 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
685 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
688 /* If this is not an UTF-8 system don't print any QR codes */
689 setlocale(LC_CTYPE, "");
691 if (streq_ptr(nl_langinfo(CODESET), "UTF-8")) {
692 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
693 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
703 close_nointr_nofail(fd);
714 log_error("Forward-secure sealing not available.");
719 static int verify(sd_journal *j) {
726 log_show_color(true);
728 HASHMAP_FOREACH(f, j->files, i) {
730 usec_t first, validated, last;
733 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
734 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
737 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
739 /* If the key was invalid give up right-away. */
742 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
745 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
746 log_info("PASS: %s", f->path);
748 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
750 log_info("=> Validated from %s to %s, final %s entries not sealed.",
751 format_timestamp(a, sizeof(a), first),
752 format_timestamp(b, sizeof(b), validated),
753 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
755 log_info("=> No sealing yet, %s of entries not sealed.",
756 format_timespan(c, sizeof(c), last - first));
758 log_info("=> No sealing yet, no entries in file.");
766 int main(int argc, char *argv[]) {
768 sd_journal *j = NULL;
769 bool need_seek = false;
770 sd_id128_t previous_boot_id;
771 bool previous_boot_id_valid = false;
773 unsigned n_shown = 0;
775 log_parse_environment();
778 r = parse_argv(argc, argv);
782 if (arg_action == ACTION_NEW_ID128) {
783 r = generate_new_id128();
787 if (arg_action == ACTION_SETUP_KEYS) {
793 r = sd_journal_open_directory(&j, arg_directory, 0);
795 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
798 log_error("Failed to open journal: %s", strerror(-r));
802 if (arg_action == ACTION_VERIFY) {
807 if (arg_action == ACTION_PRINT_HEADER) {
808 journal_print_header(j);
813 if (arg_action == ACTION_DISK_USAGE) {
815 char sbytes[FORMAT_BYTES_MAX];
817 r = sd_journal_get_usage(j, &bytes);
821 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
827 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
828 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
833 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
834 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
836 if (geteuid() != 0 && in_group("adm") <= 0) {
837 log_error("No access to messages. Only users in the group 'adm' can see messages.");
843 r = add_this_boot(j);
847 r = add_matches(j, argv + optind);
851 r = add_priorities(j);
856 r = sd_journal_seek_cursor(j, arg_cursor);
858 log_error("Failed to seek to cursor: %s", strerror(-r));
862 r = sd_journal_next(j);
864 } else if (arg_since_set) {
865 r = sd_journal_seek_realtime_usec(j, arg_since);
867 log_error("Failed to seek to date: %s", strerror(-r));
870 r = sd_journal_next(j);
872 } else if (arg_lines > 0) {
873 r = sd_journal_seek_tail(j);
875 log_error("Failed to seek to tail: %s", strerror(-r));
879 r = sd_journal_previous_skip(j, arg_lines);
882 r = sd_journal_seek_head(j);
884 log_error("Failed to seek to head: %s", strerror(-r));
888 r = sd_journal_next(j);
892 log_error("Failed to iterate through journal: %s", strerror(-r));
897 have_pager = !arg_no_pager && !arg_follow && pager_open();
901 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
903 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
905 log_error("Failed to get cutoff: %s", strerror(-r));
911 printf("---- Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
913 printf("---- Logs begin at %s, end at %s.\n",
914 format_timestamp(start_buf, sizeof(start_buf), start),
915 format_timestamp(end_buf, sizeof(end_buf), end));
920 while (arg_lines == 0 || arg_follow || n_shown < arg_lines) {
924 r = sd_journal_next(j);
926 log_error("Failed to iterate through journal: %s", strerror(-r));
937 r = sd_journal_get_realtime_usec(j, &usec);
939 log_error("Failed to determine timestamp: %s", strerror(-r));
947 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
949 if (previous_boot_id_valid &&
950 !sd_id128_equal(boot_id, previous_boot_id))
951 printf(ANSI_HIGHLIGHT_ON "---- Reboot ----" ANSI_HIGHLIGHT_OFF "\n");
953 previous_boot_id = boot_id;
954 previous_boot_id_valid = true;
959 arg_show_all * OUTPUT_SHOW_ALL |
960 have_pager * OUTPUT_FULL_WIDTH |
961 on_tty() * OUTPUT_COLOR;
963 r = output_journal(stdout, j, arg_output, 0, flags);
974 r = sd_journal_wait(j, (uint64_t) -1);
976 log_error("Couldn't wait for journal event: %s", strerror(-r));
987 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;