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>
38 #include <systemd/sd-journal.h>
42 #include "path-util.h"
45 #include "logs-show.h"
47 #include "journal-internal.h"
48 #include "journal-def.h"
49 #include "journal-verify.h"
50 #include "journal-authenticate.h"
51 #include "journal-qrcode.h"
54 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
56 static OutputMode arg_output = OUTPUT_SHORT;
57 static bool arg_follow = false;
58 static bool arg_show_all = false;
59 static bool arg_no_pager = false;
60 static int arg_lines = -1;
61 static bool arg_no_tail = false;
62 static bool arg_quiet = false;
63 static bool arg_merge = false;
64 static bool arg_this_boot = false;
65 static const char *arg_directory = NULL;
66 static int arg_priorities = 0xFF;
67 static const char *arg_verify_key = NULL;
69 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
79 } arg_action = ACTION_SHOW;
81 static int help(void) {
83 printf("%s [OPTIONS...] [MATCH]\n\n"
84 "Send control commands to or query the journal.\n\n"
85 " -h --help Show this help\n"
86 " --version Show package version\n"
87 " --no-pager Do not pipe output into a pager\n"
88 " -a --all Show all fields, including long and unprintable\n"
89 " -f --follow Follow journal\n"
90 " -n --lines=INTEGER Journal entries to show\n"
91 " --no-tail Show all lines, even in follow mode\n"
92 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
93 " verbose, export, json, json-pretty, cat)\n"
94 " -q --quiet Don't show privilege warning\n"
95 " -m --merge Show entries from all available journals\n"
96 " -b --this-boot Show data only from current boot\n"
97 " -D --directory=PATH Show journal files from directory\n"
98 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
100 " --new-id128 Generate a new 128 Bit ID\n"
101 " --header Show journal header information\n"
102 " --disk-usage Show total disk usage\n"
104 " --setup-keys Generate new FSS key pair\n"
105 " --interval=TIME Time interval for changing the FSS sealing key\n"
106 " --verify Verify journal file consistency\n"
107 " --verify-key=KEY Specify FSS verification key\n"
109 , program_invocation_short_name);
114 static int parse_argv(int argc, char *argv[]) {
129 static const struct option options[] = {
130 { "help", no_argument, NULL, 'h' },
131 { "version" , no_argument, NULL, ARG_VERSION },
132 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
133 { "follow", no_argument, NULL, 'f' },
134 { "output", required_argument, NULL, 'o' },
135 { "all", no_argument, NULL, 'a' },
136 { "lines", required_argument, NULL, 'n' },
137 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
138 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
139 { "quiet", no_argument, NULL, 'q' },
140 { "merge", no_argument, NULL, 'm' },
141 { "this-boot", no_argument, NULL, 'b' },
142 { "directory", required_argument, NULL, 'D' },
143 { "header", no_argument, NULL, ARG_HEADER },
144 { "priority", no_argument, NULL, 'p' },
145 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
146 { "interval", required_argument, NULL, ARG_INTERVAL },
147 { "verify", no_argument, NULL, ARG_VERIFY },
148 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
149 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
158 while ((c = getopt_long(argc, argv, "hfo:an:qmbD:p:", options, NULL)) >= 0) {
167 puts(PACKAGE_STRING);
169 puts(SYSTEMD_FEATURES);
181 arg_output = output_mode_from_string(optarg);
182 if (arg_output < 0) {
183 log_error("Unknown output '%s'.", optarg);
194 r = safe_atoi(optarg, &arg_lines);
195 if (r < 0 || arg_lines < 0) {
196 log_error("Failed to parse lines '%s'", optarg);
206 arg_action = ACTION_NEW_ID128;
218 arg_this_boot = true;
222 arg_directory = optarg;
226 arg_action = ACTION_PRINT_HEADER;
230 arg_action = ACTION_VERIFY;
234 arg_action = ACTION_DISK_USAGE;
239 arg_action = ACTION_SETUP_KEYS;
244 arg_action = ACTION_VERIFY;
245 arg_verify_key = optarg;
250 r = parse_usec(optarg, &arg_interval);
251 if (r < 0 || arg_interval <= 0) {
252 log_error("Failed to parse sealing key change interval: %s", optarg);
260 log_error("Forward-secure sealing not available.");
267 dots = strstr(optarg, "..");
273 a = strndup(optarg, dots - optarg);
277 from = log_level_from_string(a);
278 to = log_level_from_string(dots + 2);
281 if (from < 0 || to < 0) {
282 log_error("Failed to parse log level range %s", optarg);
289 for (i = from; i <= to; i++)
290 arg_priorities |= 1 << i;
292 for (i = to; i <= from; i++)
293 arg_priorities |= 1 << i;
299 p = log_level_from_string(optarg);
301 log_error("Unknown log level %s", optarg);
307 for (i = 0; i <= p; i++)
308 arg_priorities |= 1 << i;
318 log_error("Unknown option code %c", c);
323 if (arg_follow && !arg_no_tail && arg_lines < 0)
329 static bool on_tty(void) {
332 /* Note that this is invoked relatively early, before we start
333 * the pager. That means the value we return reflects whether
334 * we originally were started on a tty, not if we currently
335 * are. But this is intended, since we want colour and so on
336 * when run in our own pager. */
338 if (_unlikely_(t < 0))
339 t = isatty(STDOUT_FILENO) > 0;
344 static int generate_new_id128(void) {
349 r = sd_id128_randomize(&id);
351 log_error("Failed to generate ID: %s", strerror(-r));
355 printf("As string:\n"
356 SD_ID128_FORMAT_STR "\n\n"
358 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
360 "#define MESSAGE_XYZ SD_ID128_MAKE(",
361 SD_ID128_FORMAT_VAL(id),
362 SD_ID128_FORMAT_VAL(id));
364 for (i = 0; i < 16; i++)
365 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
367 fputs(")\n", stdout);
372 static int add_matches(sd_journal *j, char **args) {
378 STRV_FOREACH(i, args) {
381 r = sd_journal_add_disjunction(j);
382 else if (path_is_absolute(*i)) {
387 p = canonicalize_file_name(*i);
390 if (stat(path, &st) < 0) {
392 log_error("Couldn't stat file: %m");
396 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
397 t = strappend("_EXE=", path);
398 else if (S_ISCHR(st.st_mode))
399 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
400 else if (S_ISBLK(st.st_mode))
401 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
404 log_error("File is not a device node, regular file or is not executable: %s", *i);
413 r = sd_journal_add_match(j, t, 0);
416 r = sd_journal_add_match(j, *i, 0);
419 log_error("Failed to add match '%s': %s", *i, strerror(-r));
427 static int add_this_boot(sd_journal *j) {
428 char match[9+32+1] = "_BOOT_ID=";
437 r = sd_id128_get_boot(&boot_id);
439 log_error("Failed to get boot id: %s", strerror(-r));
443 sd_id128_to_string(boot_id, match + 9);
444 r = sd_journal_add_match(j, match, strlen(match));
446 log_error("Failed to add match: %s", strerror(-r));
453 static int add_priorities(sd_journal *j) {
454 char match[] = "PRIORITY=0";
459 if (arg_priorities == 0xFF)
462 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
463 if (arg_priorities & (1 << i)) {
464 match[sizeof(match)-2] = '0' + i;
466 log_info("adding match %s", match);
468 r = sd_journal_add_match(j, match, strlen(match));
470 log_error("Failed to add match: %s", strerror(-r));
478 static int setup_keys(void) {
480 size_t mpk_size, seed_size, state_size, i;
481 uint8_t *mpk, *seed, *state;
483 int fd = -1, r, attr = 0;
484 sd_id128_t machine, boot;
485 char *p = NULL, *k = NULL;
489 r = sd_id128_get_machine(&machine);
491 log_error("Failed to get machine ID: %s", strerror(-r));
495 r = sd_id128_get_boot(&boot);
497 log_error("Failed to get boot ID: %s", strerror(-r));
501 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
502 SD_ID128_FORMAT_VAL(machine)) < 0)
505 if (access(p, F_OK) >= 0) {
506 log_error("Sealing key file %s exists already.", p);
511 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
512 SD_ID128_FORMAT_VAL(machine)) < 0) {
517 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
518 mpk = alloca(mpk_size);
520 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
521 seed = alloca(seed_size);
523 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
524 state = alloca(state_size);
526 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
528 log_error("Failed to open /dev/random: %m");
533 log_info("Generating seed...");
534 l = loop_read(fd, seed, seed_size, true);
535 if (l < 0 || (size_t) l != seed_size) {
536 log_error("Failed to read random seed: %s", strerror(EIO));
541 log_info("Generating key pair...");
542 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
544 log_info("Generating sealing key...");
545 FSPRG_GenState0(state, mpk, seed, seed_size);
547 assert(arg_interval > 0);
549 n = now(CLOCK_REALTIME);
552 close_nointr_nofail(fd);
553 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
555 log_error("Failed to open %s: %m", k);
560 /* Enable secure remove, exclusion from dump, synchronous
561 * writing and in-place updating */
562 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
563 log_warning("FS_IOC_GETFLAGS failed: %m");
565 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
567 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
568 log_warning("FS_IOC_SETFLAGS failed: %m");
571 memcpy(h.signature, "KSHHRHLP", 8);
572 h.machine_id = machine;
574 h.header_size = htole64(sizeof(h));
575 h.start_usec = htole64(n * arg_interval);
576 h.interval_usec = htole64(arg_interval);
577 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
578 h.fsprg_state_size = htole64(state_size);
580 l = loop_write(fd, &h, sizeof(h), false);
581 if (l < 0 || (size_t) l != sizeof(h)) {
582 log_error("Failed to write header: %s", strerror(EIO));
587 l = loop_write(fd, state, state_size, false);
588 if (l < 0 || (size_t) l != state_size) {
589 log_error("Failed to write state: %s", strerror(EIO));
594 if (link(k, p) < 0) {
595 log_error("Failed to link file: %m");
600 if (isatty(STDOUT_FILENO)) {
603 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
604 "the following local file. This key file is automatically updated when the\n"
605 "sealing key is advanced. It should not be used on multiple hosts.\n"
609 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
610 "at a safe location and should not be saved locally on disk.\n"
611 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
614 for (i = 0; i < seed_size; i++) {
615 if (i > 0 && i % 3 == 0)
617 printf("%02x", ((uint8_t*) seed)[i]);
620 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
622 if (isatty(STDOUT_FILENO)) {
623 char tsb[FORMAT_TIMESPAN_MAX], *hn;
626 ANSI_HIGHLIGHT_OFF "\n"
627 "The sealing key is automatically changed every %s.\n",
628 format_timespan(tsb, sizeof(tsb), arg_interval));
630 hn = gethostname_malloc();
633 hostname_cleanup(hn);
634 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
636 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
639 /* If this is not an UTF-8 system don't print any QR codes */
640 setlocale(LC_CTYPE, "");
642 if (streq_ptr(nl_langinfo(CODESET), "UTF-8")) {
643 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
644 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
654 close_nointr_nofail(fd);
665 log_error("Forward-secure sealing not available.");
670 static int verify(sd_journal *j) {
677 log_show_color(true);
679 HASHMAP_FOREACH(f, j->files, i) {
681 usec_t from, to, total;
684 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
685 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
688 k = journal_file_verify(f, arg_verify_key, &from, &to, &total, true);
690 /* If the key was invalid give up right-away. */
693 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
696 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
697 log_info("PASS: %s", f->path);
699 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
701 log_info("=> Validated from %s to %s, final %s entries not sealed.",
702 format_timestamp(a, sizeof(a), from),
703 format_timestamp(b, sizeof(b), to),
704 format_timespan(c, sizeof(c), total > to ? total - to : 0));
705 } else if (total > 0)
706 log_info("=> No sealing yet, %s of entries not sealed.",
707 format_timespan(c, sizeof(c), total));
709 log_info("=> No sealing yet, no entries in file.");
717 int main(int argc, char *argv[]) {
719 sd_journal *j = NULL;
721 bool need_seek = false;
722 sd_id128_t previous_boot_id;
723 bool previous_boot_id_valid = false;
726 log_parse_environment();
729 r = parse_argv(argc, argv);
733 if (arg_action == ACTION_NEW_ID128) {
734 r = generate_new_id128();
738 if (arg_action == ACTION_SETUP_KEYS) {
744 r = sd_journal_open_directory(&j, arg_directory, 0);
746 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
749 log_error("Failed to open journal: %s", strerror(-r));
753 if (arg_action == ACTION_VERIFY) {
758 if (arg_action == ACTION_PRINT_HEADER) {
759 journal_print_header(j);
764 if (arg_action == ACTION_DISK_USAGE) {
766 char sbytes[FORMAT_BYTES_MAX];
768 r = sd_journal_get_usage(j, &bytes);
772 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
778 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
779 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
784 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
785 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
787 if (geteuid() != 0 && in_group("adm") <= 0) {
788 log_error("No access to messages. Only users in the group 'adm' can see messages.");
794 r = add_this_boot(j);
798 r = add_matches(j, argv + optind);
802 r = add_priorities(j);
808 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
810 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
812 log_error("Failed to get cutoff: %s", strerror(-r));
818 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
820 printf("Logs begin at %s, end at %s.\n",
821 format_timestamp(start_buf, sizeof(start_buf), start),
822 format_timestamp(end_buf, sizeof(end_buf), end));
826 if (arg_lines >= 0) {
827 r = sd_journal_seek_tail(j);
829 log_error("Failed to seek to tail: %s", strerror(-r));
833 r = sd_journal_previous_skip(j, arg_lines);
835 r = sd_journal_seek_head(j);
837 log_error("Failed to seek to head: %s", strerror(-r));
841 r = sd_journal_next(j);
845 log_error("Failed to iterate through journal: %s", strerror(-r));
850 have_pager = !arg_no_pager && !arg_follow && pager_open();
855 arg_show_all * OUTPUT_SHOW_ALL |
856 have_pager * OUTPUT_FULL_WIDTH |
857 on_tty() * OUTPUT_COLOR;
860 r = sd_journal_next(j);
862 log_error("Failed to iterate through journal: %s", strerror(-r));
873 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
875 if (previous_boot_id_valid &&
876 !sd_id128_equal(boot_id, previous_boot_id))
877 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
879 previous_boot_id = boot_id;
880 previous_boot_id_valid = true;
886 r = output_journal(j, arg_output, line, 0, flags);
896 r = sd_journal_wait(j, (uint64_t) -1);
898 log_error("Couldn't wait for log event: %s", strerror(-r));
909 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;