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] Number of 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", optional_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);
195 r = safe_atoi(optarg, &arg_lines);
196 if (r < 0 || arg_lines < 0) {
197 log_error("Failed to parse lines '%s'", optarg);
210 arg_action = ACTION_NEW_ID128;
222 arg_this_boot = true;
226 arg_directory = optarg;
230 arg_action = ACTION_PRINT_HEADER;
234 arg_action = ACTION_VERIFY;
238 arg_action = ACTION_DISK_USAGE;
243 arg_action = ACTION_SETUP_KEYS;
248 arg_action = ACTION_VERIFY;
249 arg_verify_key = optarg;
254 r = parse_usec(optarg, &arg_interval);
255 if (r < 0 || arg_interval <= 0) {
256 log_error("Failed to parse sealing key change interval: %s", optarg);
264 log_error("Forward-secure sealing not available.");
271 dots = strstr(optarg, "..");
277 a = strndup(optarg, dots - optarg);
281 from = log_level_from_string(a);
282 to = log_level_from_string(dots + 2);
285 if (from < 0 || to < 0) {
286 log_error("Failed to parse log level range %s", optarg);
293 for (i = from; i <= to; i++)
294 arg_priorities |= 1 << i;
296 for (i = to; i <= from; i++)
297 arg_priorities |= 1 << i;
303 p = log_level_from_string(optarg);
305 log_error("Unknown log level %s", optarg);
311 for (i = 0; i <= p; i++)
312 arg_priorities |= 1 << i;
322 log_error("Unknown option code %c", c);
327 if (arg_follow && !arg_no_tail && arg_lines < 0)
333 static bool on_tty(void) {
336 /* Note that this is invoked relatively early, before we start
337 * the pager. That means the value we return reflects whether
338 * we originally were started on a tty, not if we currently
339 * are. But this is intended, since we want colour and so on
340 * when run in our own pager. */
342 if (_unlikely_(t < 0))
343 t = isatty(STDOUT_FILENO) > 0;
348 static int generate_new_id128(void) {
353 r = sd_id128_randomize(&id);
355 log_error("Failed to generate ID: %s", strerror(-r));
359 printf("As string:\n"
360 SD_ID128_FORMAT_STR "\n\n"
362 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
364 "#define MESSAGE_XYZ SD_ID128_MAKE(",
365 SD_ID128_FORMAT_VAL(id),
366 SD_ID128_FORMAT_VAL(id));
368 for (i = 0; i < 16; i++)
369 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
371 fputs(")\n", stdout);
376 static int add_matches(sd_journal *j, char **args) {
382 STRV_FOREACH(i, args) {
385 r = sd_journal_add_disjunction(j);
386 else if (path_is_absolute(*i)) {
391 p = canonicalize_file_name(*i);
394 if (stat(path, &st) < 0) {
396 log_error("Couldn't stat file: %m");
400 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
401 t = strappend("_EXE=", path);
402 else if (S_ISCHR(st.st_mode))
403 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
404 else if (S_ISBLK(st.st_mode))
405 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
408 log_error("File is not a device node, regular file or is not executable: %s", *i);
417 r = sd_journal_add_match(j, t, 0);
420 r = sd_journal_add_match(j, *i, 0);
423 log_error("Failed to add match '%s': %s", *i, strerror(-r));
431 static int add_this_boot(sd_journal *j) {
432 char match[9+32+1] = "_BOOT_ID=";
441 r = sd_id128_get_boot(&boot_id);
443 log_error("Failed to get boot id: %s", strerror(-r));
447 sd_id128_to_string(boot_id, match + 9);
448 r = sd_journal_add_match(j, match, strlen(match));
450 log_error("Failed to add match: %s", strerror(-r));
457 static int add_priorities(sd_journal *j) {
458 char match[] = "PRIORITY=0";
463 if (arg_priorities == 0xFF)
466 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
467 if (arg_priorities & (1 << i)) {
468 match[sizeof(match)-2] = '0' + i;
470 log_info("adding match %s", match);
472 r = sd_journal_add_match(j, match, strlen(match));
474 log_error("Failed to add match: %s", strerror(-r));
482 static int setup_keys(void) {
484 size_t mpk_size, seed_size, state_size, i;
485 uint8_t *mpk, *seed, *state;
487 int fd = -1, r, attr = 0;
488 sd_id128_t machine, boot;
489 char *p = NULL, *k = NULL;
493 r = sd_id128_get_machine(&machine);
495 log_error("Failed to get machine ID: %s", strerror(-r));
499 r = sd_id128_get_boot(&boot);
501 log_error("Failed to get boot ID: %s", strerror(-r));
505 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
506 SD_ID128_FORMAT_VAL(machine)) < 0)
509 if (access(p, F_OK) >= 0) {
510 log_error("Sealing key file %s exists already.", p);
515 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
516 SD_ID128_FORMAT_VAL(machine)) < 0) {
521 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
522 mpk = alloca(mpk_size);
524 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
525 seed = alloca(seed_size);
527 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
528 state = alloca(state_size);
530 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
532 log_error("Failed to open /dev/random: %m");
537 log_info("Generating seed...");
538 l = loop_read(fd, seed, seed_size, true);
539 if (l < 0 || (size_t) l != seed_size) {
540 log_error("Failed to read random seed: %s", strerror(EIO));
545 log_info("Generating key pair...");
546 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
548 log_info("Generating sealing key...");
549 FSPRG_GenState0(state, mpk, seed, seed_size);
551 assert(arg_interval > 0);
553 n = now(CLOCK_REALTIME);
556 close_nointr_nofail(fd);
557 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
559 log_error("Failed to open %s: %m", k);
564 /* Enable secure remove, exclusion from dump, synchronous
565 * writing and in-place updating */
566 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
567 log_warning("FS_IOC_GETFLAGS failed: %m");
569 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
571 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
572 log_warning("FS_IOC_SETFLAGS failed: %m");
575 memcpy(h.signature, "KSHHRHLP", 8);
576 h.machine_id = machine;
578 h.header_size = htole64(sizeof(h));
579 h.start_usec = htole64(n * arg_interval);
580 h.interval_usec = htole64(arg_interval);
581 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
582 h.fsprg_state_size = htole64(state_size);
584 l = loop_write(fd, &h, sizeof(h), false);
585 if (l < 0 || (size_t) l != sizeof(h)) {
586 log_error("Failed to write header: %s", strerror(EIO));
591 l = loop_write(fd, state, state_size, false);
592 if (l < 0 || (size_t) l != state_size) {
593 log_error("Failed to write state: %s", strerror(EIO));
598 if (link(k, p) < 0) {
599 log_error("Failed to link file: %m");
604 if (isatty(STDOUT_FILENO)) {
607 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
608 "the following local file. This key file is automatically updated when the\n"
609 "sealing key is advanced. It should not be used on multiple hosts.\n"
613 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
614 "at a safe location and should not be saved locally on disk.\n"
615 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
618 for (i = 0; i < seed_size; i++) {
619 if (i > 0 && i % 3 == 0)
621 printf("%02x", ((uint8_t*) seed)[i]);
624 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
626 if (isatty(STDOUT_FILENO)) {
627 char tsb[FORMAT_TIMESPAN_MAX], *hn;
630 ANSI_HIGHLIGHT_OFF "\n"
631 "The sealing key is automatically changed every %s.\n",
632 format_timespan(tsb, sizeof(tsb), arg_interval));
634 hn = gethostname_malloc();
637 hostname_cleanup(hn);
638 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
640 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
643 /* If this is not an UTF-8 system don't print any QR codes */
644 setlocale(LC_CTYPE, "");
646 if (streq_ptr(nl_langinfo(CODESET), "UTF-8")) {
647 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
648 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
658 close_nointr_nofail(fd);
669 log_error("Forward-secure sealing not available.");
674 static int verify(sd_journal *j) {
681 log_show_color(true);
683 HASHMAP_FOREACH(f, j->files, i) {
685 usec_t from, to, total;
688 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
689 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
692 k = journal_file_verify(f, arg_verify_key, &from, &to, &total, true);
694 /* If the key was invalid give up right-away. */
697 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
700 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
701 log_info("PASS: %s", f->path);
703 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
705 log_info("=> Validated from %s to %s, final %s entries not sealed.",
706 format_timestamp(a, sizeof(a), from),
707 format_timestamp(b, sizeof(b), to),
708 format_timespan(c, sizeof(c), total > to ? total - to : 0));
709 } else if (total > 0)
710 log_info("=> No sealing yet, %s of entries not sealed.",
711 format_timespan(c, sizeof(c), total));
713 log_info("=> No sealing yet, no entries in file.");
721 int main(int argc, char *argv[]) {
723 sd_journal *j = NULL;
725 bool need_seek = false;
726 sd_id128_t previous_boot_id;
727 bool previous_boot_id_valid = false;
730 log_parse_environment();
733 r = parse_argv(argc, argv);
737 if (arg_action == ACTION_NEW_ID128) {
738 r = generate_new_id128();
742 if (arg_action == ACTION_SETUP_KEYS) {
748 r = sd_journal_open_directory(&j, arg_directory, 0);
750 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
753 log_error("Failed to open journal: %s", strerror(-r));
757 if (arg_action == ACTION_VERIFY) {
762 if (arg_action == ACTION_PRINT_HEADER) {
763 journal_print_header(j);
768 if (arg_action == ACTION_DISK_USAGE) {
770 char sbytes[FORMAT_BYTES_MAX];
772 r = sd_journal_get_usage(j, &bytes);
776 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
782 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
783 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
788 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
789 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
791 if (geteuid() != 0 && in_group("adm") <= 0) {
792 log_error("No access to messages. Only users in the group 'adm' can see messages.");
798 r = add_this_boot(j);
802 r = add_matches(j, argv + optind);
806 r = add_priorities(j);
812 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
814 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
816 log_error("Failed to get cutoff: %s", strerror(-r));
822 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
824 printf("Logs begin at %s, end at %s.\n",
825 format_timestamp(start_buf, sizeof(start_buf), start),
826 format_timestamp(end_buf, sizeof(end_buf), end));
830 if (arg_lines >= 0) {
831 r = sd_journal_seek_tail(j);
833 log_error("Failed to seek to tail: %s", strerror(-r));
837 r = sd_journal_previous_skip(j, arg_lines);
839 r = sd_journal_seek_head(j);
841 log_error("Failed to seek to head: %s", strerror(-r));
845 r = sd_journal_next(j);
849 log_error("Failed to iterate through journal: %s", strerror(-r));
854 have_pager = !arg_no_pager && !arg_follow && pager_open();
859 arg_show_all * OUTPUT_SHOW_ALL |
860 have_pager * OUTPUT_FULL_WIDTH |
861 on_tty() * OUTPUT_COLOR;
864 r = sd_journal_next(j);
866 log_error("Failed to iterate through journal: %s", strerror(-r));
877 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
879 if (previous_boot_id_valid &&
880 !sd_id128_equal(boot_id, previous_boot_id))
881 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
883 previous_boot_id = boot_id;
884 previous_boot_id_valid = true;
890 r = output_journal(j, arg_output, line, 0, flags);
900 r = sd_journal_wait(j, (uint64_t) -1);
902 log_error("Couldn't wait for log event: %s", strerror(-r));
913 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;