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;
77 static const char *arg_field = NULL;
86 } arg_action = ACTION_SHOW;
88 static int help(void) {
90 printf("%s [OPTIONS...] [MATCH]\n\n"
91 "Send control commands to or query the journal.\n\n"
92 " -h --help Show this help\n"
93 " --version Show package version\n"
94 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
95 " --since=DATE Start showing entries newer or of the specified date\n"
96 " --until=DATE Stop showing entries older or of the specified date\n"
97 " -b --this-boot Show data only from current boot\n"
98 " -u --unit=UNIT Show data only from the specified unit\n"
99 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
100 " -f --follow Follow journal\n"
101 " -n --lines[=INTEGER] Number of journal entries to show\n"
102 " --no-tail Show all lines, even in follow mode\n"
103 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
104 " verbose, export, json, json-pretty, json-sse, cat)\n"
105 " -a --all Show all fields, including long and unprintable\n"
106 " -q --quiet Don't show privilege warning\n"
107 " --no-pager Do not pipe output into a pager\n"
108 " -m --merge Show entries from all available journals\n"
109 " -D --directory=PATH Show journal files from directory\n"
111 " --new-id128 Generate a new 128 Bit ID\n"
112 " --header Show journal header information\n"
113 " --disk-usage Show total disk usage\n"
115 " --setup-keys Generate new FSS key pair\n"
116 " --interval=TIME Time interval for changing the FSS sealing key\n"
117 " --verify Verify journal file consistency\n"
118 " --verify-key=KEY Specify FSS verification key\n"
120 , program_invocation_short_name);
125 static int parse_argv(int argc, char *argv[]) {
142 static const struct option options[] = {
143 { "help", no_argument, NULL, 'h' },
144 { "version" , no_argument, NULL, ARG_VERSION },
145 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
146 { "follow", no_argument, NULL, 'f' },
147 { "output", required_argument, NULL, 'o' },
148 { "all", no_argument, NULL, 'a' },
149 { "lines", optional_argument, NULL, 'n' },
150 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
151 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
152 { "quiet", no_argument, NULL, 'q' },
153 { "merge", no_argument, NULL, 'm' },
154 { "this-boot", no_argument, NULL, 'b' },
155 { "directory", required_argument, NULL, 'D' },
156 { "header", no_argument, NULL, ARG_HEADER },
157 { "priority", no_argument, NULL, 'p' },
158 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
159 { "interval", required_argument, NULL, ARG_INTERVAL },
160 { "verify", no_argument, NULL, ARG_VERIFY },
161 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
162 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
163 { "cursor", required_argument, NULL, 'c' },
164 { "since", required_argument, NULL, ARG_SINCE },
165 { "until", required_argument, NULL, ARG_UNTIL },
166 { "unit", required_argument, NULL, 'u' },
175 while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:u:", options, NULL)) >= 0) {
184 puts(PACKAGE_STRING);
186 puts(SYSTEMD_FEATURES);
195 signal(SIGWINCH, columns_cache_reset);
199 arg_output = output_mode_from_string(optarg);
200 if (arg_output < 0) {
201 log_error("Unknown output format '%s'.", optarg);
205 if (arg_output == OUTPUT_EXPORT ||
206 arg_output == OUTPUT_JSON ||
207 arg_output == OUTPUT_JSON_PRETTY ||
208 arg_output == OUTPUT_JSON_SSE ||
209 arg_output == OUTPUT_CAT)
220 r = safe_atou(optarg, &arg_lines);
221 if (r < 0 || arg_lines <= 0) {
222 log_error("Failed to parse lines '%s'", optarg);
235 arg_action = ACTION_NEW_ID128;
247 arg_this_boot = true;
251 arg_directory = optarg;
259 arg_action = ACTION_PRINT_HEADER;
263 arg_action = ACTION_VERIFY;
267 arg_action = ACTION_DISK_USAGE;
272 arg_action = ACTION_SETUP_KEYS;
277 arg_action = ACTION_VERIFY;
278 arg_verify_key = optarg;
283 r = parse_usec(optarg, &arg_interval);
284 if (r < 0 || arg_interval <= 0) {
285 log_error("Failed to parse sealing key change interval: %s", optarg);
293 log_error("Forward-secure sealing not available.");
300 dots = strstr(optarg, "..");
306 a = strndup(optarg, dots - optarg);
310 from = log_level_from_string(a);
311 to = log_level_from_string(dots + 2);
314 if (from < 0 || to < 0) {
315 log_error("Failed to parse log level range %s", optarg);
322 for (i = from; i <= to; i++)
323 arg_priorities |= 1 << i;
325 for (i = to; i <= from; i++)
326 arg_priorities |= 1 << i;
332 p = log_level_from_string(optarg);
334 log_error("Unknown log level %s", optarg);
340 for (i = 0; i <= p; i++)
341 arg_priorities |= 1 << i;
348 r = parse_timestamp(optarg, &arg_since);
350 log_error("Failed to parse timestamp: %s", optarg);
353 arg_since_set = true;
357 r = parse_timestamp(optarg, &arg_until);
359 log_error("Failed to parse timestamp: %s", optarg);
362 arg_until_set = true;
373 log_error("Unknown option code %c", c);
378 if (arg_follow && !arg_no_tail && arg_lines <= 0)
381 if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) {
382 log_error("--since= must be before --until=.");
386 if (arg_cursor && arg_since_set) {
387 log_error("Please specify either --since= or --cursor=, not both.");
394 static bool on_tty(void) {
397 /* Note that this is invoked relatively early, before we start
398 * the pager. That means the value we return reflects whether
399 * we originally were started on a tty, not if we currently
400 * are. But this is intended, since we want colour and so on
401 * when run in our own pager. */
403 if (_unlikely_(t < 0))
404 t = isatty(STDOUT_FILENO) > 0;
409 static int generate_new_id128(void) {
414 r = sd_id128_randomize(&id);
416 log_error("Failed to generate ID: %s", strerror(-r));
420 printf("As string:\n"
421 SD_ID128_FORMAT_STR "\n\n"
423 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
425 "#define MESSAGE_XYZ SD_ID128_MAKE(",
426 SD_ID128_FORMAT_VAL(id),
427 SD_ID128_FORMAT_VAL(id));
429 for (i = 0; i < 16; i++)
430 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
432 fputs(")\n", stdout);
437 static int add_matches(sd_journal *j, char **args) {
443 STRV_FOREACH(i, args) {
446 r = sd_journal_add_disjunction(j);
447 else if (path_is_absolute(*i)) {
452 p = canonicalize_file_name(*i);
455 if (stat(path, &st) < 0) {
457 log_error("Couldn't stat file: %m");
461 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
462 t = strappend("_EXE=", path);
463 else if (S_ISCHR(st.st_mode))
464 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
465 else if (S_ISBLK(st.st_mode))
466 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
469 log_error("File is not a device node, regular file or is not executable: %s", *i);
478 r = sd_journal_add_match(j, t, 0);
481 r = sd_journal_add_match(j, *i, 0);
484 log_error("Failed to add match '%s': %s", *i, strerror(-r));
492 static int add_this_boot(sd_journal *j) {
493 char match[9+32+1] = "_BOOT_ID=";
502 r = sd_id128_get_boot(&boot_id);
504 log_error("Failed to get boot id: %s", strerror(-r));
508 sd_id128_to_string(boot_id, match + 9);
509 r = sd_journal_add_match(j, match, strlen(match));
511 log_error("Failed to add match: %s", strerror(-r));
518 static int add_unit(sd_journal *j) {
519 _cleanup_free_ char *m = NULL, *u = NULL;
524 if (isempty(arg_unit))
527 u = unit_name_mangle(arg_unit);
531 m = strappend("_SYSTEMD_UNIT=", u);
535 r = sd_journal_add_match(j, m, strlen(m));
537 log_error("Failed to add match: %s", strerror(-r));
544 static int add_priorities(sd_journal *j) {
545 char match[] = "PRIORITY=0";
550 if (arg_priorities == 0xFF)
553 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
554 if (arg_priorities & (1 << i)) {
555 match[sizeof(match)-2] = '0' + i;
557 log_info("adding match %s", match);
559 r = sd_journal_add_match(j, match, strlen(match));
561 log_error("Failed to add match: %s", strerror(-r));
569 static int setup_keys(void) {
571 size_t mpk_size, seed_size, state_size, i;
572 uint8_t *mpk, *seed, *state;
574 int fd = -1, r, attr = 0;
575 sd_id128_t machine, boot;
576 char *p = NULL, *k = NULL;
580 r = sd_id128_get_machine(&machine);
582 log_error("Failed to get machine ID: %s", strerror(-r));
586 r = sd_id128_get_boot(&boot);
588 log_error("Failed to get boot ID: %s", strerror(-r));
592 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
593 SD_ID128_FORMAT_VAL(machine)) < 0)
596 if (access(p, F_OK) >= 0) {
597 log_error("Sealing key file %s exists already.", p);
602 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
603 SD_ID128_FORMAT_VAL(machine)) < 0) {
608 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
609 mpk = alloca(mpk_size);
611 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
612 seed = alloca(seed_size);
614 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
615 state = alloca(state_size);
617 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
619 log_error("Failed to open /dev/random: %m");
624 log_info("Generating seed...");
625 l = loop_read(fd, seed, seed_size, true);
626 if (l < 0 || (size_t) l != seed_size) {
627 log_error("Failed to read random seed: %s", strerror(EIO));
632 log_info("Generating key pair...");
633 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
635 log_info("Generating sealing key...");
636 FSPRG_GenState0(state, mpk, seed, seed_size);
638 assert(arg_interval > 0);
640 n = now(CLOCK_REALTIME);
643 close_nointr_nofail(fd);
644 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
646 log_error("Failed to open %s: %m", k);
651 /* Enable secure remove, exclusion from dump, synchronous
652 * writing and in-place updating */
653 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
654 log_warning("FS_IOC_GETFLAGS failed: %m");
656 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
658 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
659 log_warning("FS_IOC_SETFLAGS failed: %m");
662 memcpy(h.signature, "KSHHRHLP", 8);
663 h.machine_id = machine;
665 h.header_size = htole64(sizeof(h));
666 h.start_usec = htole64(n * arg_interval);
667 h.interval_usec = htole64(arg_interval);
668 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
669 h.fsprg_state_size = htole64(state_size);
671 l = loop_write(fd, &h, sizeof(h), false);
672 if (l < 0 || (size_t) l != sizeof(h)) {
673 log_error("Failed to write header: %s", strerror(EIO));
678 l = loop_write(fd, state, state_size, false);
679 if (l < 0 || (size_t) l != state_size) {
680 log_error("Failed to write state: %s", strerror(EIO));
685 if (link(k, p) < 0) {
686 log_error("Failed to link file: %m");
691 if (isatty(STDOUT_FILENO)) {
694 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
695 "the following local file. This key file is automatically updated when the\n"
696 "sealing key is advanced. It should not be used on multiple hosts.\n"
700 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
701 "at a safe location and should not be saved locally on disk.\n"
702 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
705 for (i = 0; i < seed_size; i++) {
706 if (i > 0 && i % 3 == 0)
708 printf("%02x", ((uint8_t*) seed)[i]);
711 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
713 if (isatty(STDOUT_FILENO)) {
714 char tsb[FORMAT_TIMESPAN_MAX], *hn;
717 ANSI_HIGHLIGHT_OFF "\n"
718 "The sealing key is automatically changed every %s.\n",
719 format_timespan(tsb, sizeof(tsb), arg_interval));
721 hn = gethostname_malloc();
724 hostname_cleanup(hn);
725 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
727 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
730 /* If this is not an UTF-8 system don't print any QR codes */
731 setlocale(LC_CTYPE, "");
733 if (streq_ptr(nl_langinfo(CODESET), "UTF-8")) {
734 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
735 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
745 close_nointr_nofail(fd);
756 log_error("Forward-secure sealing not available.");
761 static int verify(sd_journal *j) {
768 log_show_color(true);
770 HASHMAP_FOREACH(f, j->files, i) {
772 usec_t first, validated, last;
775 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
776 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
779 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
781 /* If the key was invalid give up right-away. */
784 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
787 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
788 log_info("PASS: %s", f->path);
790 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
792 log_info("=> Validated from %s to %s, final %s entries not sealed.",
793 format_timestamp(a, sizeof(a), first),
794 format_timestamp(b, sizeof(b), validated),
795 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
797 log_info("=> No sealing yet, %s of entries not sealed.",
798 format_timespan(c, sizeof(c), last - first));
800 log_info("=> No sealing yet, no entries in file.");
808 int main(int argc, char *argv[]) {
810 sd_journal *j = NULL;
811 bool need_seek = false;
812 sd_id128_t previous_boot_id;
813 bool previous_boot_id_valid = false;
815 unsigned n_shown = 0;
817 log_parse_environment();
820 r = parse_argv(argc, argv);
824 if (arg_action == ACTION_NEW_ID128) {
825 r = generate_new_id128();
829 if (arg_action == ACTION_SETUP_KEYS) {
835 r = sd_journal_open_directory(&j, arg_directory, 0);
837 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
840 log_error("Failed to open journal: %s", strerror(-r));
844 if (arg_action == ACTION_VERIFY) {
849 if (arg_action == ACTION_PRINT_HEADER) {
850 journal_print_header(j);
855 if (arg_action == ACTION_DISK_USAGE) {
857 char sbytes[FORMAT_BYTES_MAX];
859 r = sd_journal_get_usage(j, &bytes);
863 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
869 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
870 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
875 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
876 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
878 if (geteuid() != 0 && in_group("adm") <= 0) {
879 log_error("No access to messages. Only users in the group 'adm' can see messages.");
885 r = add_this_boot(j);
893 r = add_matches(j, argv + optind);
897 r = add_priorities(j);
902 r = sd_journal_seek_cursor(j, arg_cursor);
904 log_error("Failed to seek to cursor: %s", strerror(-r));
908 r = sd_journal_next(j);
910 } else if (arg_since_set) {
911 r = sd_journal_seek_realtime_usec(j, arg_since);
913 log_error("Failed to seek to date: %s", strerror(-r));
916 r = sd_journal_next(j);
918 } else if (arg_lines > 0) {
919 r = sd_journal_seek_tail(j);
921 log_error("Failed to seek to tail: %s", strerror(-r));
925 r = sd_journal_previous_skip(j, arg_lines);
928 r = sd_journal_seek_head(j);
930 log_error("Failed to seek to head: %s", strerror(-r));
934 r = sd_journal_next(j);
938 log_error("Failed to iterate through journal: %s", strerror(-r));
943 have_pager = !arg_no_pager && !arg_follow && pager_open();
947 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
949 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
951 log_error("Failed to get cutoff: %s", strerror(-r));
957 printf("-- Logs begin at %s. --\n",
958 format_timestamp(start_buf, sizeof(start_buf), start));
960 printf("-- Logs begin at %s, end at %s. --\n",
961 format_timestamp(start_buf, sizeof(start_buf), start),
962 format_timestamp(end_buf, sizeof(end_buf), end));
967 while (arg_lines == 0 || arg_follow || n_shown < arg_lines) {
971 r = sd_journal_next(j);
973 log_error("Failed to iterate through journal: %s", strerror(-r));
984 r = sd_journal_get_realtime_usec(j, &usec);
986 log_error("Failed to determine timestamp: %s", strerror(-r));
994 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
996 if (previous_boot_id_valid &&
997 !sd_id128_equal(boot_id, previous_boot_id))
998 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
1000 previous_boot_id = boot_id;
1001 previous_boot_id_valid = true;
1006 arg_show_all * OUTPUT_SHOW_ALL |
1007 have_pager * OUTPUT_FULL_WIDTH |
1008 on_tty() * OUTPUT_COLOR;
1010 r = output_journal(stdout, j, arg_output, 0, flags);
1021 r = sd_journal_wait(j, (uint64_t) -1);
1023 log_error("Couldn't wait for journal event: %s", strerror(-r));
1030 sd_journal_close(j);
1034 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;