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 "Query the journal.\n\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 " --interval=TIME Time interval for changing the FSS sealing key\n"
111 " --verify-key=KEY Specify FSS verification key\n"
114 " -h --help Show this help\n"
115 " --version Show package version\n"
116 " --new-id128 Generate a new 128 Bit ID\n"
117 " --header Show journal header information\n"
118 " --disk-usage Show total disk usage\n"
119 " -F --field=FIELD List all values a certain field takes\n"
121 " --setup-keys Generate new FSS key pair\n"
122 " --verify Verify journal file consistency\n"
124 , program_invocation_short_name);
129 static int parse_argv(int argc, char *argv[]) {
146 static const struct option options[] = {
147 { "help", no_argument, NULL, 'h' },
148 { "version" , no_argument, NULL, ARG_VERSION },
149 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
150 { "follow", no_argument, NULL, 'f' },
151 { "output", required_argument, NULL, 'o' },
152 { "all", no_argument, NULL, 'a' },
153 { "lines", optional_argument, NULL, 'n' },
154 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
155 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
156 { "quiet", no_argument, NULL, 'q' },
157 { "merge", no_argument, NULL, 'm' },
158 { "this-boot", no_argument, NULL, 'b' },
159 { "directory", required_argument, NULL, 'D' },
160 { "header", no_argument, NULL, ARG_HEADER },
161 { "priority", no_argument, NULL, 'p' },
162 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
163 { "interval", required_argument, NULL, ARG_INTERVAL },
164 { "verify", no_argument, NULL, ARG_VERIFY },
165 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
166 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
167 { "cursor", required_argument, NULL, 'c' },
168 { "since", required_argument, NULL, ARG_SINCE },
169 { "until", required_argument, NULL, ARG_UNTIL },
170 { "unit", required_argument, NULL, 'u' },
171 { "field", required_argument, NULL, 'F' },
180 while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:u:F:", options, NULL)) >= 0) {
189 puts(PACKAGE_STRING);
191 puts(SYSTEMD_FEATURES);
200 signal(SIGWINCH, columns_cache_reset);
204 arg_output = output_mode_from_string(optarg);
205 if (arg_output < 0) {
206 log_error("Unknown output format '%s'.", optarg);
210 if (arg_output == OUTPUT_EXPORT ||
211 arg_output == OUTPUT_JSON ||
212 arg_output == OUTPUT_JSON_PRETTY ||
213 arg_output == OUTPUT_JSON_SSE ||
214 arg_output == OUTPUT_CAT)
225 r = safe_atou(optarg, &arg_lines);
226 if (r < 0 || arg_lines <= 0) {
227 log_error("Failed to parse lines '%s'", optarg);
240 arg_action = ACTION_NEW_ID128;
252 arg_this_boot = true;
256 arg_directory = optarg;
264 arg_action = ACTION_PRINT_HEADER;
268 arg_action = ACTION_VERIFY;
272 arg_action = ACTION_DISK_USAGE;
277 arg_action = ACTION_SETUP_KEYS;
282 arg_action = ACTION_VERIFY;
283 arg_verify_key = optarg;
288 r = parse_usec(optarg, &arg_interval);
289 if (r < 0 || arg_interval <= 0) {
290 log_error("Failed to parse sealing key change interval: %s", optarg);
298 log_error("Forward-secure sealing not available.");
305 dots = strstr(optarg, "..");
311 a = strndup(optarg, dots - optarg);
315 from = log_level_from_string(a);
316 to = log_level_from_string(dots + 2);
319 if (from < 0 || to < 0) {
320 log_error("Failed to parse log level range %s", optarg);
327 for (i = from; i <= to; i++)
328 arg_priorities |= 1 << i;
330 for (i = to; i <= from; i++)
331 arg_priorities |= 1 << i;
337 p = log_level_from_string(optarg);
339 log_error("Unknown log level %s", optarg);
345 for (i = 0; i <= p; i++)
346 arg_priorities |= 1 << i;
353 r = parse_timestamp(optarg, &arg_since);
355 log_error("Failed to parse timestamp: %s", optarg);
358 arg_since_set = true;
362 r = parse_timestamp(optarg, &arg_until);
364 log_error("Failed to parse timestamp: %s", optarg);
367 arg_until_set = true;
382 log_error("Unknown option code %c", c);
387 if (arg_follow && !arg_no_tail && arg_lines <= 0)
390 if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) {
391 log_error("--since= must be before --until=.");
395 if (arg_cursor && arg_since_set) {
396 log_error("Please specify either --since= or --cursor=, not both.");
403 static bool on_tty(void) {
406 /* Note that this is invoked relatively early, before we start
407 * the pager. That means the value we return reflects whether
408 * we originally were started on a tty, not if we currently
409 * are. But this is intended, since we want colour and so on
410 * when run in our own pager. */
412 if (_unlikely_(t < 0))
413 t = isatty(STDOUT_FILENO) > 0;
418 static int generate_new_id128(void) {
423 r = sd_id128_randomize(&id);
425 log_error("Failed to generate ID: %s", strerror(-r));
429 printf("As string:\n"
430 SD_ID128_FORMAT_STR "\n\n"
432 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
434 "#define MESSAGE_XYZ SD_ID128_MAKE(",
435 SD_ID128_FORMAT_VAL(id),
436 SD_ID128_FORMAT_VAL(id));
438 for (i = 0; i < 16; i++)
439 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
441 fputs(")\n", stdout);
446 static int add_matches(sd_journal *j, char **args) {
452 STRV_FOREACH(i, args) {
455 r = sd_journal_add_disjunction(j);
456 else if (path_is_absolute(*i)) {
461 p = canonicalize_file_name(*i);
464 if (stat(path, &st) < 0) {
466 log_error("Couldn't stat file: %m");
470 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
471 t = strappend("_EXE=", path);
472 else if (S_ISCHR(st.st_mode))
473 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
474 else if (S_ISBLK(st.st_mode))
475 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
478 log_error("File is not a device node, regular file or is not executable: %s", *i);
487 r = sd_journal_add_match(j, t, 0);
490 r = sd_journal_add_match(j, *i, 0);
493 log_error("Failed to add match '%s': %s", *i, strerror(-r));
501 static int add_this_boot(sd_journal *j) {
502 char match[9+32+1] = "_BOOT_ID=";
511 r = sd_id128_get_boot(&boot_id);
513 log_error("Failed to get boot id: %s", strerror(-r));
517 sd_id128_to_string(boot_id, match + 9);
518 r = sd_journal_add_match(j, match, strlen(match));
520 log_error("Failed to add match: %s", strerror(-r));
527 static int add_unit(sd_journal *j) {
528 _cleanup_free_ char *m = NULL, *u = NULL;
533 if (isempty(arg_unit))
536 u = unit_name_mangle(arg_unit);
540 m = strappend("_SYSTEMD_UNIT=", u);
544 r = sd_journal_add_match(j, m, strlen(m));
546 log_error("Failed to add match: %s", strerror(-r));
553 static int add_priorities(sd_journal *j) {
554 char match[] = "PRIORITY=0";
559 if (arg_priorities == 0xFF)
562 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
563 if (arg_priorities & (1 << i)) {
564 match[sizeof(match)-2] = '0' + i;
566 log_info("adding match %s", match);
568 r = sd_journal_add_match(j, match, strlen(match));
570 log_error("Failed to add match: %s", strerror(-r));
578 static int setup_keys(void) {
580 size_t mpk_size, seed_size, state_size, i;
581 uint8_t *mpk, *seed, *state;
583 int fd = -1, r, attr = 0;
584 sd_id128_t machine, boot;
585 char *p = NULL, *k = NULL;
589 r = sd_id128_get_machine(&machine);
591 log_error("Failed to get machine ID: %s", strerror(-r));
595 r = sd_id128_get_boot(&boot);
597 log_error("Failed to get boot ID: %s", strerror(-r));
601 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
602 SD_ID128_FORMAT_VAL(machine)) < 0)
605 if (access(p, F_OK) >= 0) {
606 log_error("Sealing key file %s exists already.", p);
611 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
612 SD_ID128_FORMAT_VAL(machine)) < 0) {
617 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
618 mpk = alloca(mpk_size);
620 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
621 seed = alloca(seed_size);
623 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
624 state = alloca(state_size);
626 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
628 log_error("Failed to open /dev/random: %m");
633 log_info("Generating seed...");
634 l = loop_read(fd, seed, seed_size, true);
635 if (l < 0 || (size_t) l != seed_size) {
636 log_error("Failed to read random seed: %s", strerror(EIO));
641 log_info("Generating key pair...");
642 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
644 log_info("Generating sealing key...");
645 FSPRG_GenState0(state, mpk, seed, seed_size);
647 assert(arg_interval > 0);
649 n = now(CLOCK_REALTIME);
652 close_nointr_nofail(fd);
653 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
655 log_error("Failed to open %s: %m", k);
660 /* Enable secure remove, exclusion from dump, synchronous
661 * writing and in-place updating */
662 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
663 log_warning("FS_IOC_GETFLAGS failed: %m");
665 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
667 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
668 log_warning("FS_IOC_SETFLAGS failed: %m");
671 memcpy(h.signature, "KSHHRHLP", 8);
672 h.machine_id = machine;
674 h.header_size = htole64(sizeof(h));
675 h.start_usec = htole64(n * arg_interval);
676 h.interval_usec = htole64(arg_interval);
677 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
678 h.fsprg_state_size = htole64(state_size);
680 l = loop_write(fd, &h, sizeof(h), false);
681 if (l < 0 || (size_t) l != sizeof(h)) {
682 log_error("Failed to write header: %s", strerror(EIO));
687 l = loop_write(fd, state, state_size, false);
688 if (l < 0 || (size_t) l != state_size) {
689 log_error("Failed to write state: %s", strerror(EIO));
694 if (link(k, p) < 0) {
695 log_error("Failed to link file: %m");
700 if (isatty(STDOUT_FILENO)) {
703 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
704 "the following local file. This key file is automatically updated when the\n"
705 "sealing key is advanced. It should not be used on multiple hosts.\n"
709 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
710 "at a safe location and should not be saved locally on disk.\n"
711 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
714 for (i = 0; i < seed_size; i++) {
715 if (i > 0 && i % 3 == 0)
717 printf("%02x", ((uint8_t*) seed)[i]);
720 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
722 if (isatty(STDOUT_FILENO)) {
723 char tsb[FORMAT_TIMESPAN_MAX], *hn;
726 ANSI_HIGHLIGHT_OFF "\n"
727 "The sealing key is automatically changed every %s.\n",
728 format_timespan(tsb, sizeof(tsb), arg_interval));
730 hn = gethostname_malloc();
733 hostname_cleanup(hn);
734 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
736 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
739 /* If this is not an UTF-8 system don't print any QR codes */
740 setlocale(LC_CTYPE, "");
742 if (streq_ptr(nl_langinfo(CODESET), "UTF-8")) {
743 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
744 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
754 close_nointr_nofail(fd);
765 log_error("Forward-secure sealing not available.");
770 static int verify(sd_journal *j) {
777 log_show_color(true);
779 HASHMAP_FOREACH(f, j->files, i) {
781 usec_t first, validated, last;
784 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
785 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
788 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
790 /* If the key was invalid give up right-away. */
793 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
796 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
797 log_info("PASS: %s", f->path);
799 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
801 log_info("=> Validated from %s to %s, final %s entries not sealed.",
802 format_timestamp(a, sizeof(a), first),
803 format_timestamp(b, sizeof(b), validated),
804 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
806 log_info("=> No sealing yet, %s of entries not sealed.",
807 format_timespan(c, sizeof(c), last - first));
809 log_info("=> No sealing yet, no entries in file.");
817 static int access_check(void) {
820 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
821 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
825 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
826 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
828 if (geteuid() != 0 && in_group("adm") <= 0) {
829 log_error("No access to messages. Only users in the group 'adm' can see messages.");
837 int main(int argc, char *argv[]) {
839 sd_journal *j = NULL;
840 bool need_seek = false;
841 sd_id128_t previous_boot_id;
842 bool previous_boot_id_valid = false;
844 unsigned n_shown = 0;
846 log_parse_environment();
849 r = parse_argv(argc, argv);
853 if (arg_action == ACTION_NEW_ID128) {
854 r = generate_new_id128();
858 if (arg_action == ACTION_SETUP_KEYS) {
868 r = sd_journal_open_directory(&j, arg_directory, 0);
870 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
872 log_error("Failed to open journal: %s", strerror(-r));
876 if (arg_action == ACTION_VERIFY) {
881 if (arg_action == ACTION_PRINT_HEADER) {
882 journal_print_header(j);
887 if (arg_action == ACTION_DISK_USAGE) {
889 char sbytes[FORMAT_BYTES_MAX];
891 r = sd_journal_get_usage(j, &bytes);
895 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
900 r = add_this_boot(j);
908 r = add_matches(j, argv + optind);
912 r = add_priorities(j);
920 r = sd_journal_query_unique(j, arg_field);
922 log_error("Failed to query unique data objects: %s", strerror(-r));
926 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
929 eq = memchr(data, '=', size);
931 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
933 printf("%.*s\n", (int) size, (const char*) data);
941 r = sd_journal_seek_cursor(j, arg_cursor);
943 log_error("Failed to seek to cursor: %s", strerror(-r));
947 r = sd_journal_next(j);
949 } else if (arg_since_set) {
950 r = sd_journal_seek_realtime_usec(j, arg_since);
952 log_error("Failed to seek to date: %s", strerror(-r));
955 r = sd_journal_next(j);
957 } else if (arg_lines > 0) {
958 r = sd_journal_seek_tail(j);
960 log_error("Failed to seek to tail: %s", strerror(-r));
964 r = sd_journal_previous_skip(j, arg_lines);
967 r = sd_journal_seek_head(j);
969 log_error("Failed to seek to head: %s", strerror(-r));
973 r = sd_journal_next(j);
977 log_error("Failed to iterate through journal: %s", strerror(-r));
982 have_pager = !arg_no_pager && !arg_follow && pager_open();
986 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
988 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
990 log_error("Failed to get cutoff: %s", strerror(-r));
996 printf("-- Logs begin at %s. --\n",
997 format_timestamp(start_buf, sizeof(start_buf), start));
999 printf("-- Logs begin at %s, end at %s. --\n",
1000 format_timestamp(start_buf, sizeof(start_buf), start),
1001 format_timestamp(end_buf, sizeof(end_buf), end));
1006 while (arg_lines == 0 || arg_follow || n_shown < arg_lines) {
1010 r = sd_journal_next(j);
1012 log_error("Failed to iterate through journal: %s", strerror(-r));
1020 if (arg_until_set) {
1023 r = sd_journal_get_realtime_usec(j, &usec);
1025 log_error("Failed to determine timestamp: %s", strerror(-r));
1033 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1035 if (previous_boot_id_valid &&
1036 !sd_id128_equal(boot_id, previous_boot_id))
1037 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
1039 previous_boot_id = boot_id;
1040 previous_boot_id_valid = true;
1045 arg_show_all * OUTPUT_SHOW_ALL |
1046 have_pager * OUTPUT_FULL_WIDTH |
1047 on_tty() * OUTPUT_COLOR;
1049 r = output_journal(stdout, j, arg_output, 0, flags);
1060 r = sd_journal_wait(j, (uint64_t) -1);
1062 log_error("Couldn't wait for journal event: %s", strerror(-r));
1069 sd_journal_close(j);
1073 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;