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_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...] [MATCHES...]\n\n"
91 "Query the journal.\n\n"
93 " --since=DATE Start showing entries newer or of the specified date\n"
94 " --until=DATE Stop showing entries older or of the specified date\n"
95 " -c --cursor=CURSOR Start showing entries from specified cursor\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"
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);
203 arg_output = output_mode_from_string(optarg);
204 if (arg_output < 0) {
205 log_error("Unknown output format '%s'.", optarg);
209 if (arg_output == OUTPUT_EXPORT ||
210 arg_output == OUTPUT_JSON ||
211 arg_output == OUTPUT_JSON_PRETTY ||
212 arg_output == OUTPUT_JSON_SSE ||
213 arg_output == OUTPUT_CAT)
224 r = safe_atou(optarg, &arg_lines);
225 if (r < 0 || arg_lines <= 0) {
226 log_error("Failed to parse lines '%s'", optarg);
239 arg_action = ACTION_NEW_ID128;
251 arg_this_boot = true;
255 arg_directory = optarg;
263 arg_action = ACTION_PRINT_HEADER;
267 arg_action = ACTION_VERIFY;
271 arg_action = ACTION_DISK_USAGE;
276 arg_action = ACTION_SETUP_KEYS;
281 arg_action = ACTION_VERIFY;
282 arg_verify_key = optarg;
287 r = parse_usec(optarg, &arg_interval);
288 if (r < 0 || arg_interval <= 0) {
289 log_error("Failed to parse sealing key change interval: %s", optarg);
297 log_error("Forward-secure sealing not available.");
304 dots = strstr(optarg, "..");
310 a = strndup(optarg, dots - optarg);
314 from = log_level_from_string(a);
315 to = log_level_from_string(dots + 2);
318 if (from < 0 || to < 0) {
319 log_error("Failed to parse log level range %s", optarg);
326 for (i = from; i <= to; i++)
327 arg_priorities |= 1 << i;
329 for (i = to; i <= from; i++)
330 arg_priorities |= 1 << i;
336 p = log_level_from_string(optarg);
338 log_error("Unknown log level %s", optarg);
344 for (i = 0; i <= p; i++)
345 arg_priorities |= 1 << i;
352 r = parse_timestamp(optarg, &arg_since);
354 log_error("Failed to parse timestamp: %s", optarg);
357 arg_since_set = true;
361 r = parse_timestamp(optarg, &arg_until);
363 log_error("Failed to parse timestamp: %s", optarg);
366 arg_until_set = true;
381 log_error("Unknown option code %c", c);
386 if (arg_follow && !arg_no_tail && arg_lines <= 0)
389 if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) {
390 log_error("--since= must be before --until=.");
394 if (arg_cursor && arg_since_set) {
395 log_error("Please specify either --since= or --cursor=, not both.");
402 static int generate_new_id128(void) {
407 r = sd_id128_randomize(&id);
409 log_error("Failed to generate ID: %s", strerror(-r));
413 printf("As string:\n"
414 SD_ID128_FORMAT_STR "\n\n"
416 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
418 "#define MESSAGE_XYZ SD_ID128_MAKE(",
419 SD_ID128_FORMAT_VAL(id),
420 SD_ID128_FORMAT_VAL(id));
422 for (i = 0; i < 16; i++)
423 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
425 fputs(")\n", stdout);
430 static int add_matches(sd_journal *j, char **args) {
436 STRV_FOREACH(i, args) {
439 r = sd_journal_add_disjunction(j);
440 else if (path_is_absolute(*i)) {
445 p = canonicalize_file_name(*i);
448 if (stat(path, &st) < 0) {
450 log_error("Couldn't stat file: %m");
454 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
455 t = strappend("_EXE=", path);
456 else if (S_ISCHR(st.st_mode))
457 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
458 else if (S_ISBLK(st.st_mode))
459 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
462 log_error("File is not a device node, regular file or is not executable: %s", *i);
471 r = sd_journal_add_match(j, t, 0);
474 r = sd_journal_add_match(j, *i, 0);
477 log_error("Failed to add match '%s': %s", *i, strerror(-r));
485 static int add_this_boot(sd_journal *j) {
486 char match[9+32+1] = "_BOOT_ID=";
495 r = sd_id128_get_boot(&boot_id);
497 log_error("Failed to get boot id: %s", strerror(-r));
501 sd_id128_to_string(boot_id, match + 9);
502 r = sd_journal_add_match(j, match, strlen(match));
504 log_error("Failed to add match: %s", strerror(-r));
511 static int add_unit(sd_journal *j) {
512 _cleanup_free_ char *m = NULL, *u = NULL;
517 if (isempty(arg_unit))
520 u = unit_name_mangle(arg_unit);
524 m = strappend("_SYSTEMD_UNIT=", u);
528 r = sd_journal_add_match(j, m, strlen(m));
530 log_error("Failed to add match: %s", strerror(-r));
537 static int add_priorities(sd_journal *j) {
538 char match[] = "PRIORITY=0";
543 if (arg_priorities == 0xFF)
546 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
547 if (arg_priorities & (1 << i)) {
548 match[sizeof(match)-2] = '0' + i;
550 log_info("adding match %s", match);
552 r = sd_journal_add_match(j, match, strlen(match));
554 log_error("Failed to add match: %s", strerror(-r));
562 static int setup_keys(void) {
564 size_t mpk_size, seed_size, state_size, i;
565 uint8_t *mpk, *seed, *state;
567 int fd = -1, r, attr = 0;
568 sd_id128_t machine, boot;
569 char *p = NULL, *k = NULL;
573 r = sd_id128_get_machine(&machine);
575 log_error("Failed to get machine ID: %s", strerror(-r));
579 r = sd_id128_get_boot(&boot);
581 log_error("Failed to get boot ID: %s", strerror(-r));
585 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
586 SD_ID128_FORMAT_VAL(machine)) < 0)
589 if (access(p, F_OK) >= 0) {
590 log_error("Sealing key file %s exists already.", p);
595 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
596 SD_ID128_FORMAT_VAL(machine)) < 0) {
601 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
602 mpk = alloca(mpk_size);
604 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
605 seed = alloca(seed_size);
607 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
608 state = alloca(state_size);
610 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
612 log_error("Failed to open /dev/random: %m");
617 log_info("Generating seed...");
618 l = loop_read(fd, seed, seed_size, true);
619 if (l < 0 || (size_t) l != seed_size) {
620 log_error("Failed to read random seed: %s", strerror(EIO));
625 log_info("Generating key pair...");
626 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
628 log_info("Generating sealing key...");
629 FSPRG_GenState0(state, mpk, seed, seed_size);
631 assert(arg_interval > 0);
633 n = now(CLOCK_REALTIME);
636 close_nointr_nofail(fd);
637 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
639 log_error("Failed to open %s: %m", k);
644 /* Enable secure remove, exclusion from dump, synchronous
645 * writing and in-place updating */
646 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
647 log_warning("FS_IOC_GETFLAGS failed: %m");
649 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
651 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
652 log_warning("FS_IOC_SETFLAGS failed: %m");
655 memcpy(h.signature, "KSHHRHLP", 8);
656 h.machine_id = machine;
658 h.header_size = htole64(sizeof(h));
659 h.start_usec = htole64(n * arg_interval);
660 h.interval_usec = htole64(arg_interval);
661 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
662 h.fsprg_state_size = htole64(state_size);
664 l = loop_write(fd, &h, sizeof(h), false);
665 if (l < 0 || (size_t) l != sizeof(h)) {
666 log_error("Failed to write header: %s", strerror(EIO));
671 l = loop_write(fd, state, state_size, false);
672 if (l < 0 || (size_t) l != state_size) {
673 log_error("Failed to write state: %s", strerror(EIO));
678 if (link(k, p) < 0) {
679 log_error("Failed to link file: %m");
687 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
688 "the following local file. This key file is automatically updated when the\n"
689 "sealing key is advanced. It should not be used on multiple hosts.\n"
693 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
694 "at a safe location and should not be saved locally on disk.\n"
695 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
698 for (i = 0; i < seed_size; i++) {
699 if (i > 0 && i % 3 == 0)
701 printf("%02x", ((uint8_t*) seed)[i]);
704 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
707 char tsb[FORMAT_TIMESPAN_MAX], *hn;
710 ANSI_HIGHLIGHT_OFF "\n"
711 "The sealing key is automatically changed every %s.\n",
712 format_timespan(tsb, sizeof(tsb), arg_interval));
714 hn = gethostname_malloc();
717 hostname_cleanup(hn);
718 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
720 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
723 /* If this is not an UTF-8 system don't print any QR codes */
724 setlocale(LC_CTYPE, "");
726 if (streq_ptr(nl_langinfo(CODESET), "UTF-8")) {
727 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
728 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
738 close_nointr_nofail(fd);
749 log_error("Forward-secure sealing not available.");
754 static int verify(sd_journal *j) {
761 log_show_color(true);
763 HASHMAP_FOREACH(f, j->files, i) {
765 usec_t first, validated, last;
768 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
769 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
772 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
774 /* If the key was invalid give up right-away. */
777 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
780 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
781 log_info("PASS: %s", f->path);
783 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
785 log_info("=> Validated from %s to %s, final %s entries not sealed.",
786 format_timestamp(a, sizeof(a), first),
787 format_timestamp(b, sizeof(b), validated),
788 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
790 log_info("=> No sealing yet, %s of entries not sealed.",
791 format_timespan(c, sizeof(c), last - first));
793 log_info("=> No sealing yet, no entries in file.");
801 static int access_check(void) {
804 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
805 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
809 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
810 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
812 if (geteuid() != 0 && in_group("adm") <= 0) {
813 log_error("No access to messages. Only users in the group 'adm' can see messages.");
821 int main(int argc, char *argv[]) {
823 sd_journal *j = NULL;
824 bool need_seek = false;
825 sd_id128_t previous_boot_id;
826 bool previous_boot_id_valid = false;
827 unsigned n_shown = 0;
829 log_parse_environment();
832 r = parse_argv(argc, argv);
836 signal(SIGWINCH, columns_lines_cache_reset);
838 if (arg_action == ACTION_NEW_ID128) {
839 r = generate_new_id128();
843 if (arg_action == ACTION_SETUP_KEYS) {
853 r = sd_journal_open_directory(&j, arg_directory, 0);
855 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
857 log_error("Failed to open journal: %s", strerror(-r));
861 if (arg_action == ACTION_VERIFY) {
866 if (arg_action == ACTION_PRINT_HEADER) {
867 journal_print_header(j);
872 if (arg_action == ACTION_DISK_USAGE) {
874 char sbytes[FORMAT_BYTES_MAX];
876 r = sd_journal_get_usage(j, &bytes);
880 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
885 r = add_this_boot(j);
893 r = add_matches(j, argv + optind);
897 r = add_priorities(j);
905 r = sd_journal_query_unique(j, arg_field);
907 log_error("Failed to query unique data objects: %s", strerror(-r));
911 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
914 if (arg_lines > 0 && n_shown >= arg_lines)
917 eq = memchr(data, '=', size);
919 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
921 printf("%.*s\n", (int) size, (const char*) data);
931 r = sd_journal_seek_cursor(j, arg_cursor);
933 log_error("Failed to seek to cursor: %s", strerror(-r));
937 r = sd_journal_next(j);
939 } else if (arg_since_set) {
940 r = sd_journal_seek_realtime_usec(j, arg_since);
942 log_error("Failed to seek to date: %s", strerror(-r));
945 r = sd_journal_next(j);
947 } else if (arg_lines > 0) {
948 r = sd_journal_seek_tail(j);
950 log_error("Failed to seek to tail: %s", strerror(-r));
954 r = sd_journal_previous_skip(j, arg_lines);
957 r = sd_journal_seek_head(j);
959 log_error("Failed to seek to head: %s", strerror(-r));
963 r = sd_journal_next(j);
967 log_error("Failed to iterate through journal: %s", strerror(-r));
971 if (!arg_no_pager && !arg_follow)
976 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
978 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
980 log_error("Failed to get cutoff: %s", strerror(-r));
986 printf("-- Logs begin at %s. --\n",
987 format_timestamp(start_buf, sizeof(start_buf), start));
989 printf("-- Logs begin at %s, end at %s. --\n",
990 format_timestamp(start_buf, sizeof(start_buf), start),
991 format_timestamp(end_buf, sizeof(end_buf), end));
996 while (arg_lines == 0 || arg_follow || n_shown < arg_lines) {
1000 r = sd_journal_next(j);
1002 log_error("Failed to iterate through journal: %s", strerror(-r));
1010 if (arg_until_set) {
1013 r = sd_journal_get_realtime_usec(j, &usec);
1015 log_error("Failed to determine timestamp: %s", strerror(-r));
1023 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1025 if (previous_boot_id_valid &&
1026 !sd_id128_equal(boot_id, previous_boot_id))
1027 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
1029 previous_boot_id = boot_id;
1030 previous_boot_id_valid = true;
1035 arg_all * OUTPUT_SHOW_ALL |
1036 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
1037 on_tty() * OUTPUT_COLOR;
1039 r = output_journal(stdout, j, arg_output, 0, flags);
1050 r = sd_journal_wait(j, (uint64_t) -1);
1052 log_error("Couldn't wait for journal event: %s", strerror(-r));
1059 sd_journal_close(j);
1063 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;