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 format '%s'.", optarg);
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)
215 r = safe_atou(optarg, &arg_lines);
216 if (r < 0 || arg_lines <= 0) {
217 log_error("Failed to parse lines '%s'", optarg);
230 arg_action = ACTION_NEW_ID128;
242 arg_this_boot = true;
246 arg_directory = optarg;
254 arg_action = ACTION_PRINT_HEADER;
258 arg_action = ACTION_VERIFY;
262 arg_action = ACTION_DISK_USAGE;
267 arg_action = ACTION_SETUP_KEYS;
272 arg_action = ACTION_VERIFY;
273 arg_verify_key = optarg;
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);
288 log_error("Forward-secure sealing not available.");
295 dots = strstr(optarg, "..");
301 a = strndup(optarg, dots - optarg);
305 from = log_level_from_string(a);
306 to = log_level_from_string(dots + 2);
309 if (from < 0 || to < 0) {
310 log_error("Failed to parse log level range %s", optarg);
317 for (i = from; i <= to; i++)
318 arg_priorities |= 1 << i;
320 for (i = to; i <= from; i++)
321 arg_priorities |= 1 << i;
327 p = log_level_from_string(optarg);
329 log_error("Unknown log level %s", optarg);
335 for (i = 0; i <= p; i++)
336 arg_priorities |= 1 << i;
343 r = parse_timestamp(optarg, &arg_since);
345 log_error("Failed to parse timestamp: %s", optarg);
348 arg_since_set = true;
352 r = parse_timestamp(optarg, &arg_until);
354 log_error("Failed to parse timestamp: %s", optarg);
357 arg_until_set = true;
364 log_error("Unknown option code %c", c);
369 if (arg_follow && !arg_no_tail && arg_lines <= 0)
372 if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) {
373 log_error("--since= must be before --until=.");
377 if (arg_cursor && arg_since_set) {
378 log_error("Please specify either --since= or --cursor=, not both.");
385 static bool on_tty(void) {
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. */
394 if (_unlikely_(t < 0))
395 t = isatty(STDOUT_FILENO) > 0;
400 static int generate_new_id128(void) {
405 r = sd_id128_randomize(&id);
407 log_error("Failed to generate ID: %s", strerror(-r));
411 printf("As string:\n"
412 SD_ID128_FORMAT_STR "\n\n"
414 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
416 "#define MESSAGE_XYZ SD_ID128_MAKE(",
417 SD_ID128_FORMAT_VAL(id),
418 SD_ID128_FORMAT_VAL(id));
420 for (i = 0; i < 16; i++)
421 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
423 fputs(")\n", stdout);
428 static int add_matches(sd_journal *j, char **args) {
434 STRV_FOREACH(i, args) {
437 r = sd_journal_add_disjunction(j);
438 else if (path_is_absolute(*i)) {
443 p = canonicalize_file_name(*i);
446 if (stat(path, &st) < 0) {
448 log_error("Couldn't stat file: %m");
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));
460 log_error("File is not a device node, regular file or is not executable: %s", *i);
469 r = sd_journal_add_match(j, t, 0);
472 r = sd_journal_add_match(j, *i, 0);
475 log_error("Failed to add match '%s': %s", *i, strerror(-r));
483 static int add_this_boot(sd_journal *j) {
484 char match[9+32+1] = "_BOOT_ID=";
493 r = sd_id128_get_boot(&boot_id);
495 log_error("Failed to get boot id: %s", strerror(-r));
499 sd_id128_to_string(boot_id, match + 9);
500 r = sd_journal_add_match(j, match, strlen(match));
502 log_error("Failed to add match: %s", strerror(-r));
509 static int add_priorities(sd_journal *j) {
510 char match[] = "PRIORITY=0";
515 if (arg_priorities == 0xFF)
518 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
519 if (arg_priorities & (1 << i)) {
520 match[sizeof(match)-2] = '0' + i;
522 log_info("adding match %s", match);
524 r = sd_journal_add_match(j, match, strlen(match));
526 log_error("Failed to add match: %s", strerror(-r));
534 static int setup_keys(void) {
536 size_t mpk_size, seed_size, state_size, i;
537 uint8_t *mpk, *seed, *state;
539 int fd = -1, r, attr = 0;
540 sd_id128_t machine, boot;
541 char *p = NULL, *k = NULL;
545 r = sd_id128_get_machine(&machine);
547 log_error("Failed to get machine ID: %s", strerror(-r));
551 r = sd_id128_get_boot(&boot);
553 log_error("Failed to get boot ID: %s", strerror(-r));
557 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
558 SD_ID128_FORMAT_VAL(machine)) < 0)
561 if (access(p, F_OK) >= 0) {
562 log_error("Sealing key file %s exists already.", p);
567 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
568 SD_ID128_FORMAT_VAL(machine)) < 0) {
573 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
574 mpk = alloca(mpk_size);
576 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
577 seed = alloca(seed_size);
579 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
580 state = alloca(state_size);
582 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
584 log_error("Failed to open /dev/random: %m");
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));
597 log_info("Generating key pair...");
598 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
600 log_info("Generating sealing key...");
601 FSPRG_GenState0(state, mpk, seed, seed_size);
603 assert(arg_interval > 0);
605 n = now(CLOCK_REALTIME);
608 close_nointr_nofail(fd);
609 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
611 log_error("Failed to open %s: %m", k);
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");
621 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
623 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
624 log_warning("FS_IOC_SETFLAGS failed: %m");
627 memcpy(h.signature, "KSHHRHLP", 8);
628 h.machine_id = machine;
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);
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));
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));
650 if (link(k, p) < 0) {
651 log_error("Failed to link file: %m");
656 if (isatty(STDOUT_FILENO)) {
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"
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);
670 for (i = 0; i < seed_size; i++) {
671 if (i > 0 && i % 3 == 0)
673 printf("%02x", ((uint8_t*) seed)[i]);
676 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
678 if (isatty(STDOUT_FILENO)) {
679 char tsb[FORMAT_TIMESPAN_MAX], *hn;
682 ANSI_HIGHLIGHT_OFF "\n"
683 "The sealing key is automatically changed every %s.\n",
684 format_timespan(tsb, sizeof(tsb), arg_interval));
686 hn = gethostname_malloc();
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));
692 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
695 /* If this is not an UTF-8 system don't print any QR codes */
696 setlocale(LC_CTYPE, "");
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);
710 close_nointr_nofail(fd);
721 log_error("Forward-secure sealing not available.");
726 static int verify(sd_journal *j) {
733 log_show_color(true);
735 HASHMAP_FOREACH(f, j->files, i) {
737 usec_t first, validated, last;
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);
744 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
746 /* If the key was invalid give up right-away. */
749 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
752 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
753 log_info("PASS: %s", f->path);
755 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
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));
762 log_info("=> No sealing yet, %s of entries not sealed.",
763 format_timespan(c, sizeof(c), last - first));
765 log_info("=> No sealing yet, no entries in file.");
773 int main(int argc, char *argv[]) {
775 sd_journal *j = NULL;
776 bool need_seek = false;
777 sd_id128_t previous_boot_id;
778 bool previous_boot_id_valid = false;
780 unsigned n_shown = 0;
782 log_parse_environment();
785 r = parse_argv(argc, argv);
789 if (arg_action == ACTION_NEW_ID128) {
790 r = generate_new_id128();
794 if (arg_action == ACTION_SETUP_KEYS) {
800 r = sd_journal_open_directory(&j, arg_directory, 0);
802 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
805 log_error("Failed to open journal: %s", strerror(-r));
809 if (arg_action == ACTION_VERIFY) {
814 if (arg_action == ACTION_PRINT_HEADER) {
815 journal_print_header(j);
820 if (arg_action == ACTION_DISK_USAGE) {
822 char sbytes[FORMAT_BYTES_MAX];
824 r = sd_journal_get_usage(j, &bytes);
828 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
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.");
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.");
843 if (geteuid() != 0 && in_group("adm") <= 0) {
844 log_error("No access to messages. Only users in the group 'adm' can see messages.");
850 r = add_this_boot(j);
854 r = add_matches(j, argv + optind);
858 r = add_priorities(j);
863 r = sd_journal_seek_cursor(j, arg_cursor);
865 log_error("Failed to seek to cursor: %s", strerror(-r));
869 r = sd_journal_next(j);
871 } else if (arg_since_set) {
872 r = sd_journal_seek_realtime_usec(j, arg_since);
874 log_error("Failed to seek to date: %s", strerror(-r));
877 r = sd_journal_next(j);
879 } else if (arg_lines > 0) {
880 r = sd_journal_seek_tail(j);
882 log_error("Failed to seek to tail: %s", strerror(-r));
886 r = sd_journal_previous_skip(j, arg_lines);
889 r = sd_journal_seek_head(j);
891 log_error("Failed to seek to head: %s", strerror(-r));
895 r = sd_journal_next(j);
899 log_error("Failed to iterate through journal: %s", strerror(-r));
904 have_pager = !arg_no_pager && !arg_follow && pager_open();
908 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
910 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
912 log_error("Failed to get cutoff: %s", strerror(-r));
918 printf("---- Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
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));
927 while (arg_lines == 0 || arg_follow || n_shown < arg_lines) {
931 r = sd_journal_next(j);
933 log_error("Failed to iterate through journal: %s", strerror(-r));
944 r = sd_journal_get_realtime_usec(j, &usec);
946 log_error("Failed to determine timestamp: %s", strerror(-r));
954 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
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");
960 previous_boot_id = boot_id;
961 previous_boot_id_valid = true;
966 arg_show_all * OUTPUT_SHOW_ALL |
967 have_pager * OUTPUT_FULL_WIDTH |
968 on_tty() * OUTPUT_COLOR;
970 r = output_journal(stdout, j, arg_output, 0, flags);
981 r = sd_journal_wait(j, (uint64_t) -1);
983 log_error("Couldn't wait for journal event: %s", strerror(-r));
994 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;