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"
54 #include "unit-name.h"
56 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
58 static OutputMode arg_output = OUTPUT_SHORT;
59 static bool arg_follow = false;
60 static bool arg_show_all = false;
61 static bool arg_no_pager = false;
62 static unsigned arg_lines = 0;
63 static bool arg_no_tail = false;
64 static bool arg_quiet = false;
65 static bool arg_merge = false;
66 static bool arg_this_boot = false;
67 static const char *arg_cursor = NULL;
68 static const char *arg_directory = NULL;
69 static int arg_priorities = 0xFF;
70 static const char *arg_verify_key = NULL;
72 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
74 static usec_t arg_since, arg_until;
75 static bool arg_since_set = false, arg_until_set = false;
76 static const char *arg_unit = NULL;
85 } arg_action = ACTION_SHOW;
87 static int help(void) {
89 printf("%s [OPTIONS...] [MATCH]\n\n"
90 "Send control commands to or query the journal.\n\n"
91 " -h --help Show this help\n"
92 " --version Show package version\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 " -b --this-boot Show data only from current boot\n"
97 " -u --unit=UNIT Show data only from the specified unit\n"
98 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
99 " -f --follow Follow journal\n"
100 " -n --lines[=INTEGER] Number of journal entries to show\n"
101 " --no-tail Show all lines, even in follow mode\n"
102 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
103 " verbose, export, json, json-pretty, json-sse, cat)\n"
104 " -a --all Show all fields, including long and unprintable\n"
105 " -q --quiet Don't show privilege warning\n"
106 " --no-pager Do not pipe output into a pager\n"
107 " -m --merge Show entries from all available journals\n"
108 " -D --directory=PATH Show journal files from directory\n"
110 " --new-id128 Generate a new 128 Bit ID\n"
111 " --header Show journal header information\n"
112 " --disk-usage Show total disk usage\n"
114 " --setup-keys Generate new FSS key pair\n"
115 " --interval=TIME Time interval for changing the FSS sealing key\n"
116 " --verify Verify journal file consistency\n"
117 " --verify-key=KEY Specify FSS verification key\n"
119 , program_invocation_short_name);
124 static int parse_argv(int argc, char *argv[]) {
141 static const struct option options[] = {
142 { "help", no_argument, NULL, 'h' },
143 { "version" , no_argument, NULL, ARG_VERSION },
144 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
145 { "follow", no_argument, NULL, 'f' },
146 { "output", required_argument, NULL, 'o' },
147 { "all", no_argument, NULL, 'a' },
148 { "lines", optional_argument, NULL, 'n' },
149 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
150 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
151 { "quiet", no_argument, NULL, 'q' },
152 { "merge", no_argument, NULL, 'm' },
153 { "this-boot", no_argument, NULL, 'b' },
154 { "directory", required_argument, NULL, 'D' },
155 { "header", no_argument, NULL, ARG_HEADER },
156 { "priority", no_argument, NULL, 'p' },
157 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
158 { "interval", required_argument, NULL, ARG_INTERVAL },
159 { "verify", no_argument, NULL, ARG_VERIFY },
160 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
161 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
162 { "cursor", required_argument, NULL, 'c' },
163 { "since", required_argument, NULL, ARG_SINCE },
164 { "until", required_argument, NULL, ARG_UNTIL },
165 { "unit", required_argument, NULL, 'u' },
174 while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:u:", options, NULL)) >= 0) {
183 puts(PACKAGE_STRING);
185 puts(SYSTEMD_FEATURES);
194 signal(SIGWINCH, columns_cache_reset);
198 arg_output = output_mode_from_string(optarg);
199 if (arg_output < 0) {
200 log_error("Unknown output format '%s'.", optarg);
204 if (arg_output == OUTPUT_EXPORT ||
205 arg_output == OUTPUT_JSON ||
206 arg_output == OUTPUT_JSON_PRETTY ||
207 arg_output == OUTPUT_JSON_SSE ||
208 arg_output == OUTPUT_CAT)
219 r = safe_atou(optarg, &arg_lines);
220 if (r < 0 || arg_lines <= 0) {
221 log_error("Failed to parse lines '%s'", optarg);
234 arg_action = ACTION_NEW_ID128;
246 arg_this_boot = true;
250 arg_directory = optarg;
258 arg_action = ACTION_PRINT_HEADER;
262 arg_action = ACTION_VERIFY;
266 arg_action = ACTION_DISK_USAGE;
271 arg_action = ACTION_SETUP_KEYS;
276 arg_action = ACTION_VERIFY;
277 arg_verify_key = optarg;
282 r = parse_usec(optarg, &arg_interval);
283 if (r < 0 || arg_interval <= 0) {
284 log_error("Failed to parse sealing key change interval: %s", optarg);
292 log_error("Forward-secure sealing not available.");
299 dots = strstr(optarg, "..");
305 a = strndup(optarg, dots - optarg);
309 from = log_level_from_string(a);
310 to = log_level_from_string(dots + 2);
313 if (from < 0 || to < 0) {
314 log_error("Failed to parse log level range %s", optarg);
321 for (i = from; i <= to; i++)
322 arg_priorities |= 1 << i;
324 for (i = to; i <= from; i++)
325 arg_priorities |= 1 << i;
331 p = log_level_from_string(optarg);
333 log_error("Unknown log level %s", optarg);
339 for (i = 0; i <= p; i++)
340 arg_priorities |= 1 << i;
347 r = parse_timestamp(optarg, &arg_since);
349 log_error("Failed to parse timestamp: %s", optarg);
352 arg_since_set = true;
356 r = parse_timestamp(optarg, &arg_until);
358 log_error("Failed to parse timestamp: %s", optarg);
361 arg_until_set = true;
372 log_error("Unknown option code %c", c);
377 if (arg_follow && !arg_no_tail && arg_lines <= 0)
380 if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) {
381 log_error("--since= must be before --until=.");
385 if (arg_cursor && arg_since_set) {
386 log_error("Please specify either --since= or --cursor=, not both.");
393 static bool on_tty(void) {
396 /* Note that this is invoked relatively early, before we start
397 * the pager. That means the value we return reflects whether
398 * we originally were started on a tty, not if we currently
399 * are. But this is intended, since we want colour and so on
400 * when run in our own pager. */
402 if (_unlikely_(t < 0))
403 t = isatty(STDOUT_FILENO) > 0;
408 static int generate_new_id128(void) {
413 r = sd_id128_randomize(&id);
415 log_error("Failed to generate ID: %s", strerror(-r));
419 printf("As string:\n"
420 SD_ID128_FORMAT_STR "\n\n"
422 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
424 "#define MESSAGE_XYZ SD_ID128_MAKE(",
425 SD_ID128_FORMAT_VAL(id),
426 SD_ID128_FORMAT_VAL(id));
428 for (i = 0; i < 16; i++)
429 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
431 fputs(")\n", stdout);
436 static int add_matches(sd_journal *j, char **args) {
442 STRV_FOREACH(i, args) {
445 r = sd_journal_add_disjunction(j);
446 else if (path_is_absolute(*i)) {
451 p = canonicalize_file_name(*i);
454 if (stat(path, &st) < 0) {
456 log_error("Couldn't stat file: %m");
460 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
461 t = strappend("_EXE=", path);
462 else if (S_ISCHR(st.st_mode))
463 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
464 else if (S_ISBLK(st.st_mode))
465 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
468 log_error("File is not a device node, regular file or is not executable: %s", *i);
477 r = sd_journal_add_match(j, t, 0);
480 r = sd_journal_add_match(j, *i, 0);
483 log_error("Failed to add match '%s': %s", *i, strerror(-r));
491 static int add_this_boot(sd_journal *j) {
492 char match[9+32+1] = "_BOOT_ID=";
501 r = sd_id128_get_boot(&boot_id);
503 log_error("Failed to get boot id: %s", strerror(-r));
507 sd_id128_to_string(boot_id, match + 9);
508 r = sd_journal_add_match(j, match, strlen(match));
510 log_error("Failed to add match: %s", strerror(-r));
517 static int add_unit(sd_journal *j) {
518 _cleanup_free_ char *m = NULL, *u = NULL;
523 if (isempty(arg_unit))
526 u = unit_name_mangle(arg_unit);
530 m = strappend("_SYSTEMD_UNIT=", u);
534 r = sd_journal_add_match(j, m, strlen(m));
536 log_error("Failed to add match: %s", strerror(-r));
543 static int add_priorities(sd_journal *j) {
544 char match[] = "PRIORITY=0";
549 if (arg_priorities == 0xFF)
552 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
553 if (arg_priorities & (1 << i)) {
554 match[sizeof(match)-2] = '0' + i;
556 log_info("adding match %s", match);
558 r = sd_journal_add_match(j, match, strlen(match));
560 log_error("Failed to add match: %s", strerror(-r));
568 static int setup_keys(void) {
570 size_t mpk_size, seed_size, state_size, i;
571 uint8_t *mpk, *seed, *state;
573 int fd = -1, r, attr = 0;
574 sd_id128_t machine, boot;
575 char *p = NULL, *k = NULL;
579 r = sd_id128_get_machine(&machine);
581 log_error("Failed to get machine ID: %s", strerror(-r));
585 r = sd_id128_get_boot(&boot);
587 log_error("Failed to get boot ID: %s", strerror(-r));
591 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
592 SD_ID128_FORMAT_VAL(machine)) < 0)
595 if (access(p, F_OK) >= 0) {
596 log_error("Sealing key file %s exists already.", p);
601 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
602 SD_ID128_FORMAT_VAL(machine)) < 0) {
607 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
608 mpk = alloca(mpk_size);
610 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
611 seed = alloca(seed_size);
613 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
614 state = alloca(state_size);
616 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
618 log_error("Failed to open /dev/random: %m");
623 log_info("Generating seed...");
624 l = loop_read(fd, seed, seed_size, true);
625 if (l < 0 || (size_t) l != seed_size) {
626 log_error("Failed to read random seed: %s", strerror(EIO));
631 log_info("Generating key pair...");
632 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
634 log_info("Generating sealing key...");
635 FSPRG_GenState0(state, mpk, seed, seed_size);
637 assert(arg_interval > 0);
639 n = now(CLOCK_REALTIME);
642 close_nointr_nofail(fd);
643 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
645 log_error("Failed to open %s: %m", k);
650 /* Enable secure remove, exclusion from dump, synchronous
651 * writing and in-place updating */
652 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
653 log_warning("FS_IOC_GETFLAGS failed: %m");
655 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
657 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
658 log_warning("FS_IOC_SETFLAGS failed: %m");
661 memcpy(h.signature, "KSHHRHLP", 8);
662 h.machine_id = machine;
664 h.header_size = htole64(sizeof(h));
665 h.start_usec = htole64(n * arg_interval);
666 h.interval_usec = htole64(arg_interval);
667 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
668 h.fsprg_state_size = htole64(state_size);
670 l = loop_write(fd, &h, sizeof(h), false);
671 if (l < 0 || (size_t) l != sizeof(h)) {
672 log_error("Failed to write header: %s", strerror(EIO));
677 l = loop_write(fd, state, state_size, false);
678 if (l < 0 || (size_t) l != state_size) {
679 log_error("Failed to write state: %s", strerror(EIO));
684 if (link(k, p) < 0) {
685 log_error("Failed to link file: %m");
690 if (isatty(STDOUT_FILENO)) {
693 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
694 "the following local file. This key file is automatically updated when the\n"
695 "sealing key is advanced. It should not be used on multiple hosts.\n"
699 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
700 "at a safe location and should not be saved locally on disk.\n"
701 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
704 for (i = 0; i < seed_size; i++) {
705 if (i > 0 && i % 3 == 0)
707 printf("%02x", ((uint8_t*) seed)[i]);
710 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
712 if (isatty(STDOUT_FILENO)) {
713 char tsb[FORMAT_TIMESPAN_MAX], *hn;
716 ANSI_HIGHLIGHT_OFF "\n"
717 "The sealing key is automatically changed every %s.\n",
718 format_timespan(tsb, sizeof(tsb), arg_interval));
720 hn = gethostname_malloc();
723 hostname_cleanup(hn);
724 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
726 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
729 /* If this is not an UTF-8 system don't print any QR codes */
730 setlocale(LC_CTYPE, "");
732 if (streq_ptr(nl_langinfo(CODESET), "UTF-8")) {
733 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
734 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
744 close_nointr_nofail(fd);
755 log_error("Forward-secure sealing not available.");
760 static int verify(sd_journal *j) {
767 log_show_color(true);
769 HASHMAP_FOREACH(f, j->files, i) {
771 usec_t first, validated, last;
774 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
775 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
778 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
780 /* If the key was invalid give up right-away. */
783 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
786 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
787 log_info("PASS: %s", f->path);
789 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
791 log_info("=> Validated from %s to %s, final %s entries not sealed.",
792 format_timestamp(a, sizeof(a), first),
793 format_timestamp(b, sizeof(b), validated),
794 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
796 log_info("=> No sealing yet, %s of entries not sealed.",
797 format_timespan(c, sizeof(c), last - first));
799 log_info("=> No sealing yet, no entries in file.");
807 int main(int argc, char *argv[]) {
809 sd_journal *j = NULL;
810 bool need_seek = false;
811 sd_id128_t previous_boot_id;
812 bool previous_boot_id_valid = false;
814 unsigned n_shown = 0;
816 log_parse_environment();
819 r = parse_argv(argc, argv);
823 if (arg_action == ACTION_NEW_ID128) {
824 r = generate_new_id128();
828 if (arg_action == ACTION_SETUP_KEYS) {
834 r = sd_journal_open_directory(&j, arg_directory, 0);
836 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
839 log_error("Failed to open journal: %s", strerror(-r));
843 if (arg_action == ACTION_VERIFY) {
848 if (arg_action == ACTION_PRINT_HEADER) {
849 journal_print_header(j);
854 if (arg_action == ACTION_DISK_USAGE) {
856 char sbytes[FORMAT_BYTES_MAX];
858 r = sd_journal_get_usage(j, &bytes);
862 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
868 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
869 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
874 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
875 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
877 if (geteuid() != 0 && in_group("adm") <= 0) {
878 log_error("No access to messages. Only users in the group 'adm' can see messages.");
884 r = add_this_boot(j);
892 r = add_matches(j, argv + optind);
896 r = add_priorities(j);
901 r = sd_journal_seek_cursor(j, arg_cursor);
903 log_error("Failed to seek to cursor: %s", strerror(-r));
907 r = sd_journal_next(j);
909 } else if (arg_since_set) {
910 r = sd_journal_seek_realtime_usec(j, arg_since);
912 log_error("Failed to seek to date: %s", strerror(-r));
915 r = sd_journal_next(j);
917 } else if (arg_lines > 0) {
918 r = sd_journal_seek_tail(j);
920 log_error("Failed to seek to tail: %s", strerror(-r));
924 r = sd_journal_previous_skip(j, arg_lines);
927 r = sd_journal_seek_head(j);
929 log_error("Failed to seek to head: %s", strerror(-r));
933 r = sd_journal_next(j);
937 log_error("Failed to iterate through journal: %s", strerror(-r));
942 have_pager = !arg_no_pager && !arg_follow && pager_open();
946 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
948 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
950 log_error("Failed to get cutoff: %s", strerror(-r));
956 printf("-- Logs begin at %s. --\n",
957 format_timestamp(start_buf, sizeof(start_buf), start));
959 printf("-- Logs begin at %s, end at %s. --\n",
960 format_timestamp(start_buf, sizeof(start_buf), start),
961 format_timestamp(end_buf, sizeof(end_buf), end));
966 while (arg_lines == 0 || arg_follow || n_shown < arg_lines) {
970 r = sd_journal_next(j);
972 log_error("Failed to iterate through journal: %s", strerror(-r));
983 r = sd_journal_get_realtime_usec(j, &usec);
985 log_error("Failed to determine timestamp: %s", strerror(-r));
993 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
995 if (previous_boot_id_valid &&
996 !sd_id128_equal(boot_id, previous_boot_id))
997 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
999 previous_boot_id = boot_id;
1000 previous_boot_id_valid = true;
1005 arg_show_all * OUTPUT_SHOW_ALL |
1006 have_pager * OUTPUT_FULL_WIDTH |
1007 on_tty() * OUTPUT_COLOR;
1009 r = output_journal(stdout, j, arg_output, 0, flags);
1020 r = sd_journal_wait(j, (uint64_t) -1);
1022 log_error("Couldn't wait for journal event: %s", strerror(-r));
1029 sd_journal_close(j);
1033 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;