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 <systemd/sd-journal.h>
38 #include "path-util.h"
41 #include "logs-show.h"
43 #include "journal-internal.h"
44 #include "journal-def.h"
45 #include "journal-verify.h"
46 #include "journal-authenticate.h"
49 #define DEFAULT_FSPRG_INTERVAL_USEC (15*USEC_PER_MINUTE)
51 static OutputMode arg_output = OUTPUT_SHORT;
52 static bool arg_follow = false;
53 static bool arg_show_all = false;
54 static bool arg_no_pager = false;
55 static int arg_lines = -1;
56 static bool arg_no_tail = false;
57 static bool arg_quiet = false;
58 static bool arg_local = false;
59 static bool arg_this_boot = false;
60 static const char *arg_directory = NULL;
61 static int arg_priorities = 0xFF;
62 static const char *arg_verify_seed = NULL;
70 } arg_action = ACTION_SHOW;
72 static int help(void) {
74 printf("%s [OPTIONS...] [MATCH]\n\n"
75 "Send control commands to or query the journal.\n\n"
76 " -h --help Show this help\n"
77 " --version Show package version\n"
78 " --no-pager Do not pipe output into a pager\n"
79 " -a --all Show all fields, including long and unprintable\n"
80 " -f --follow Follow journal\n"
81 " -n --lines=INTEGER Journal entries to show\n"
82 " --no-tail Show all lines, even in follow mode\n"
83 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
84 " verbose, export, json, cat)\n"
85 " -q --quiet Don't show privilege warning\n"
86 " -l --local Only local entries\n"
87 " -b --this-boot Show data only from current boot\n"
88 " -D --directory=PATH Show journal files from directory\n"
89 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
91 " --new-id128 Generate a new 128 Bit ID\n"
92 " --header Show journal header information\n"
93 " --verify Verify journal file consistency\n"
94 " --verify-seed=SEED Specify FSPRG seed for verification\n"
95 " --setup-keys Generate new FSPRG key and seed\n",
96 program_invocation_short_name);
101 static int parse_argv(int argc, char *argv[]) {
114 static const struct option options[] = {
115 { "help", no_argument, NULL, 'h' },
116 { "version" , no_argument, NULL, ARG_VERSION },
117 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
118 { "follow", no_argument, NULL, 'f' },
119 { "output", required_argument, NULL, 'o' },
120 { "all", no_argument, NULL, 'a' },
121 { "lines", required_argument, NULL, 'n' },
122 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
123 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
124 { "quiet", no_argument, NULL, 'q' },
125 { "local", no_argument, NULL, 'l' },
126 { "this-boot", no_argument, NULL, 'b' },
127 { "directory", required_argument, NULL, 'D' },
128 { "header", no_argument, NULL, ARG_HEADER },
129 { "priority", no_argument, NULL, 'p' },
130 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
131 { "verify", no_argument, NULL, ARG_VERIFY },
132 { "verify-seed", required_argument, NULL, ARG_VERIFY_SEED },
141 while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
150 puts(PACKAGE_STRING);
152 puts(SYSTEMD_FEATURES);
164 arg_output = output_mode_from_string(optarg);
165 if (arg_output < 0) {
166 log_error("Unknown output '%s'.", optarg);
177 r = safe_atoi(optarg, &arg_lines);
178 if (r < 0 || arg_lines < 0) {
179 log_error("Failed to parse lines '%s'", optarg);
189 arg_action = ACTION_NEW_ID128;
201 arg_this_boot = true;
205 arg_directory = optarg;
209 arg_action = ACTION_PRINT_HEADER;
213 arg_action = ACTION_SETUP_KEYS;
217 arg_action = ACTION_VERIFY;
220 case ARG_VERIFY_SEED:
221 arg_action = ACTION_VERIFY;
222 arg_verify_seed = optarg;
228 dots = strstr(optarg, "..");
234 a = strndup(optarg, dots - optarg);
238 from = log_level_from_string(a);
239 to = log_level_from_string(dots + 2);
242 if (from < 0 || to < 0) {
243 log_error("Failed to parse log level range %s", optarg);
250 for (i = from; i <= to; i++)
251 arg_priorities |= 1 << i;
253 for (i = to; i <= from; i++)
254 arg_priorities |= 1 << i;
260 p = log_level_from_string(optarg);
262 log_error("Unknown log level %s", optarg);
268 for (i = 0; i <= p; i++)
269 arg_priorities |= 1 << i;
279 log_error("Unknown option code %c", c);
284 if (arg_follow && !arg_no_tail && arg_lines < 0)
290 static bool on_tty(void) {
293 /* Note that this is invoked relatively early, before we start
294 * the pager. That means the value we return reflects whether
295 * we originally were started on a tty, not if we currently
296 * are. But this is intended, since we want colour and so on
297 * when run in our own pager. */
299 if (_unlikely_(t < 0))
300 t = isatty(STDOUT_FILENO) > 0;
305 static int generate_new_id128(void) {
310 r = sd_id128_randomize(&id);
312 log_error("Failed to generate ID: %s", strerror(-r));
316 printf("As string:\n"
317 SD_ID128_FORMAT_STR "\n\n"
319 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
321 "#define MESSAGE_XYZ SD_ID128_MAKE(",
322 SD_ID128_FORMAT_VAL(id),
323 SD_ID128_FORMAT_VAL(id));
325 for (i = 0; i < 16; i++)
326 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
328 fputs(")\n", stdout);
333 static int add_matches(sd_journal *j, char **args) {
339 STRV_FOREACH(i, args) {
342 r = sd_journal_add_disjunction(j);
343 else if (path_is_absolute(*i)) {
348 p = canonicalize_file_name(*i);
351 if (stat(path, &st) < 0) {
353 log_error("Couldn't stat file: %m");
357 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
358 t = strappend("_EXE=", path);
359 else if (S_ISCHR(st.st_mode))
360 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
361 else if (S_ISBLK(st.st_mode))
362 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
365 log_error("File is not a device node, regular file or is not executable: %s", *i);
374 r = sd_journal_add_match(j, t, 0);
377 r = sd_journal_add_match(j, *i, 0);
380 log_error("Failed to add match '%s': %s", *i, strerror(-r));
388 static int add_this_boot(sd_journal *j) {
389 char match[9+32+1] = "_BOOT_ID=";
398 r = sd_id128_get_boot(&boot_id);
400 log_error("Failed to get boot id: %s", strerror(-r));
404 sd_id128_to_string(boot_id, match + 9);
405 r = sd_journal_add_match(j, match, strlen(match));
407 log_error("Failed to add match: %s", strerror(-r));
414 static int add_priorities(sd_journal *j) {
415 char match[] = "PRIORITY=0";
420 if (arg_priorities == 0xFF)
423 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
424 if (arg_priorities & (1 << i)) {
425 match[sizeof(match)-2] = '0' + i;
427 log_info("adding match %s", match);
429 r = sd_journal_add_match(j, match, strlen(match));
431 log_error("Failed to add match: %s", strerror(-r));
439 static int setup_keys(void) {
441 size_t mpk_size, seed_size, state_size, i;
442 uint8_t *mpk, *seed, *state;
445 sd_id128_t machine, boot;
446 char *p = NULL, *k = NULL;
447 struct FSPRGHeader h;
448 uint64_t n, interval;
450 r = sd_id128_get_machine(&machine);
452 log_error("Failed to get machine ID: %s", strerror(-r));
456 r = sd_id128_get_boot(&boot);
458 log_error("Failed to get boot ID: %s", strerror(-r));
462 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg",
463 SD_ID128_FORMAT_VAL(machine)) < 0)
466 if (access(p, F_OK) >= 0) {
467 log_error("Evolving key file %s exists already.", p);
472 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg.tmp.XXXXXX",
473 SD_ID128_FORMAT_VAL(machine)) < 0) {
478 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
479 mpk = alloca(mpk_size);
481 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
482 seed = alloca(seed_size);
484 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
485 state = alloca(state_size);
487 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
489 log_error("Failed to open /dev/random: %m");
494 log_info("Generating seed...");
495 l = loop_read(fd, seed, seed_size, true);
496 if (l < 0 || (size_t) l != seed_size) {
497 log_error("Failed to read random seed: %s", strerror(EIO));
502 log_info("Generating key pair...");
503 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
505 log_info("Generating evolving key...");
506 FSPRG_GenState0(state, mpk, seed, seed_size);
508 interval = DEFAULT_FSPRG_INTERVAL_USEC;
509 n = now(CLOCK_REALTIME);
512 close_nointr_nofail(fd);
513 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
515 log_error("Failed to open %s: %m", k);
521 memcpy(h.signature, "KSHHRHLP", 8);
522 h.machine_id = machine;
524 h.header_size = htole64(sizeof(h));
525 h.fsprg_start_usec = htole64(n * interval);
526 h.fsprg_interval_usec = htole64(interval);
527 h.secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
528 h.state_size = htole64(state_size);
530 l = loop_write(fd, &h, sizeof(h), false);
531 if (l < 0 || (size_t) l != sizeof(h)) {
532 log_error("Failed to write header: %s", strerror(EIO));
537 l = loop_write(fd, state, state_size, false);
538 if (l < 0 || (size_t) l != state_size) {
539 log_error("Failed to write state: %s", strerror(EIO));
544 if (link(k, p) < 0) {
545 log_error("Failed to link file: %m");
550 if (isatty(STDOUT_FILENO)) {
553 "The new key pair has been generated. The evolving key has been written to the\n"
554 "following file. It will be used to protect local journal files. This file\n"
555 "should be kept secret. It should not be used on multiple hosts.\n"
559 "Please write down the following " ANSI_HIGHLIGHT_ON "secret" ANSI_HIGHLIGHT_OFF " seed value. It should not be stored\n"
560 "locally on disk, and may be used to verify journal files from this host.\n"
561 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
564 for (i = 0; i < seed_size; i++) {
565 if (i > 0 && i % 3 == 0)
567 printf("%02x", ((uint8_t*) seed)[i]);
570 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) interval);
572 if (isatty(STDOUT_FILENO))
573 fputs(ANSI_HIGHLIGHT_OFF "\n", stderr);
579 close_nointr_nofail(fd);
590 log_error("Forward-secure journal verification not available.");
594 static int verify(sd_journal *j) {
601 HASHMAP_FOREACH(f, j->files, i) {
604 if (!arg_verify_seed && journal_file_fsprg_enabled(f))
605 log_warning("Journal file %s has authentication enabled but verification seed has not been passed using --verify-seed=.", f->path);
607 k = journal_file_verify(f, arg_verify_seed);
609 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
612 log_info("PASS: %s", f->path);
618 int main(int argc, char *argv[]) {
620 sd_journal *j = NULL;
622 bool need_seek = false;
623 sd_id128_t previous_boot_id;
624 bool previous_boot_id_valid = false;
627 log_parse_environment();
630 r = parse_argv(argc, argv);
634 if (arg_action == ACTION_NEW_ID128) {
635 r = generate_new_id128();
639 if (arg_action == ACTION_SETUP_KEYS) {
645 r = sd_journal_open_directory(&j, arg_directory, 0);
647 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
650 log_error("Failed to open journal: %s", strerror(-r));
654 if (arg_action == ACTION_VERIFY) {
659 if (arg_action == ACTION_PRINT_HEADER) {
660 journal_print_header(j);
666 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
667 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
670 r = add_this_boot(j);
674 r = add_matches(j, argv + optind);
678 r = add_priorities(j);
684 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
686 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
688 log_error("Failed to get cutoff: %s", strerror(-r));
694 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
696 printf("Logs begin at %s, end at %s.\n",
697 format_timestamp(start_buf, sizeof(start_buf), start),
698 format_timestamp(end_buf, sizeof(end_buf), end));
702 if (arg_lines >= 0) {
703 r = sd_journal_seek_tail(j);
705 log_error("Failed to seek to tail: %s", strerror(-r));
709 r = sd_journal_previous_skip(j, arg_lines);
711 r = sd_journal_seek_head(j);
713 log_error("Failed to seek to head: %s", strerror(-r));
717 r = sd_journal_next(j);
721 log_error("Failed to iterate through journal: %s", strerror(-r));
726 have_pager = !arg_no_pager && !arg_follow && pager_open();
728 if (arg_output == OUTPUT_JSON) {
737 arg_show_all * OUTPUT_SHOW_ALL |
738 have_pager * OUTPUT_FULL_WIDTH |
739 on_tty() * OUTPUT_COLOR;
742 r = sd_journal_next(j);
744 log_error("Failed to iterate through journal: %s", strerror(-r));
752 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
754 if (previous_boot_id_valid &&
755 !sd_id128_equal(boot_id, previous_boot_id))
756 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
758 previous_boot_id = boot_id;
759 previous_boot_id_valid = true;
764 r = output_journal(j, arg_output, line, 0, flags);
774 r = sd_journal_wait(j, (uint64_t) -1);
776 log_error("Couldn't wait for log event: %s", strerror(-r));
781 if (arg_output == OUTPUT_JSON)
782 fputs("\n]\n", stdout);
790 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;