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"
45 #include "journal-def.h"
46 #include "journal-verify.h"
48 #define DEFAULT_FSPRG_INTERVAL_USEC (15*USEC_PER_MINUTE)
50 static OutputMode arg_output = OUTPUT_SHORT;
51 static bool arg_follow = false;
52 static bool arg_show_all = false;
53 static bool arg_no_pager = false;
54 static int arg_lines = -1;
55 static bool arg_no_tail = false;
56 static bool arg_quiet = false;
57 static bool arg_local = false;
58 static bool arg_this_boot = false;
59 static const char *arg_directory = NULL;
60 static int arg_priorities = 0xFF;
68 } arg_action = ACTION_SHOW;
70 static int help(void) {
72 printf("%s [OPTIONS...] [MATCH]\n\n"
73 "Send control commands to or query the journal.\n\n"
74 " -h --help Show this help\n"
75 " --version Show package version\n"
76 " --no-pager Do not pipe output into a pager\n"
77 " -a --all Show all fields, including long and unprintable\n"
78 " -f --follow Follow journal\n"
79 " -n --lines=INTEGER Journal entries to show\n"
80 " --no-tail Show all lines, even in follow mode\n"
81 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
82 " verbose, export, json, cat)\n"
83 " -q --quiet Don't show privilege warning\n"
84 " -l --local Only local entries\n"
85 " -b --this-boot Show data only from current boot\n"
86 " -D --directory=PATH Show journal files from directory\n"
87 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
89 " --new-id128 Generate a new 128 Bit ID\n"
90 " --header Show journal header information\n"
91 " --setup-keys Generate new FSPRG key pair\n"
92 " --verify Verify journal file consistency\n",
93 program_invocation_short_name);
98 static int parse_argv(int argc, char *argv[]) {
110 static const struct option options[] = {
111 { "help", no_argument, NULL, 'h' },
112 { "version" , no_argument, NULL, ARG_VERSION },
113 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
114 { "follow", no_argument, NULL, 'f' },
115 { "output", required_argument, NULL, 'o' },
116 { "all", no_argument, NULL, 'a' },
117 { "lines", required_argument, NULL, 'n' },
118 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
119 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
120 { "quiet", no_argument, NULL, 'q' },
121 { "local", no_argument, NULL, 'l' },
122 { "this-boot", no_argument, NULL, 'b' },
123 { "directory", required_argument, NULL, 'D' },
124 { "header", no_argument, NULL, ARG_HEADER },
125 { "priority", no_argument, NULL, 'p' },
126 { "setup-keys",no_argument, NULL, ARG_SETUP_KEYS},
127 { "verify", no_argument, NULL, ARG_VERIFY },
136 while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
145 puts(PACKAGE_STRING);
147 puts(SYSTEMD_FEATURES);
159 arg_output = output_mode_from_string(optarg);
160 if (arg_output < 0) {
161 log_error("Unknown output '%s'.", optarg);
172 r = safe_atoi(optarg, &arg_lines);
173 if (r < 0 || arg_lines < 0) {
174 log_error("Failed to parse lines '%s'", optarg);
184 arg_action = ACTION_NEW_ID128;
196 arg_this_boot = true;
200 arg_directory = optarg;
204 arg_action = ACTION_PRINT_HEADER;
208 arg_action = ACTION_SETUP_KEYS;
212 arg_action = ACTION_VERIFY;
218 dots = strstr(optarg, "..");
224 a = strndup(optarg, dots - optarg);
228 from = log_level_from_string(a);
229 to = log_level_from_string(dots + 2);
232 if (from < 0 || to < 0) {
233 log_error("Failed to parse log level range %s", optarg);
240 for (i = from; i <= to; i++)
241 arg_priorities |= 1 << i;
243 for (i = to; i <= from; i++)
244 arg_priorities |= 1 << i;
250 p = log_level_from_string(optarg);
252 log_error("Unknown log level %s", optarg);
258 for (i = 0; i <= p; i++)
259 arg_priorities |= 1 << i;
269 log_error("Unknown option code %c", c);
274 if (arg_follow && !arg_no_tail && arg_lines < 0)
280 static bool on_tty(void) {
283 /* Note that this is invoked relatively early, before we start
284 * the pager. That means the value we return reflects whether
285 * we originally were started on a tty, not if we currently
286 * are. But this is intended, since we want colour and so on
287 * when run in our own pager. */
289 if (_unlikely_(t < 0))
290 t = isatty(STDOUT_FILENO) > 0;
295 static int generate_new_id128(void) {
300 r = sd_id128_randomize(&id);
302 log_error("Failed to generate ID: %s", strerror(-r));
306 printf("As string:\n"
307 SD_ID128_FORMAT_STR "\n\n"
309 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
311 "#define MESSAGE_XYZ SD_ID128_MAKE(",
312 SD_ID128_FORMAT_VAL(id),
313 SD_ID128_FORMAT_VAL(id));
315 for (i = 0; i < 16; i++)
316 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
318 fputs(")\n", stdout);
323 static int add_matches(sd_journal *j, char **args) {
329 STRV_FOREACH(i, args) {
332 r = sd_journal_add_disjunction(j);
333 else if (path_is_absolute(*i)) {
338 p = canonicalize_file_name(*i);
341 if (stat(path, &st) < 0) {
343 log_error("Couldn't stat file: %m");
347 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
348 t = strappend("_EXE=", path);
349 else if (S_ISCHR(st.st_mode))
350 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
351 else if (S_ISBLK(st.st_mode))
352 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
355 log_error("File is not a device node, regular file or is not executable: %s", *i);
364 r = sd_journal_add_match(j, t, 0);
367 r = sd_journal_add_match(j, *i, 0);
370 log_error("Failed to add match '%s': %s", *i, strerror(-r));
378 static int add_this_boot(sd_journal *j) {
379 char match[9+32+1] = "_BOOT_ID=";
388 r = sd_id128_get_boot(&boot_id);
390 log_error("Failed to get boot id: %s", strerror(-r));
394 sd_id128_to_string(boot_id, match + 9);
395 r = sd_journal_add_match(j, match, strlen(match));
397 log_error("Failed to add match: %s", strerror(-r));
404 static int add_priorities(sd_journal *j) {
405 char match[] = "PRIORITY=0";
410 if (arg_priorities == 0xFF)
413 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
414 if (arg_priorities & (1 << i)) {
415 match[sizeof(match)-2] = '0' + i;
417 log_info("adding match %s", match);
419 r = sd_journal_add_match(j, match, strlen(match));
421 log_error("Failed to add match: %s", strerror(-r));
429 static int setup_keys(void) {
431 size_t mpk_size, seed_size, state_size, i;
432 uint8_t *mpk, *seed, *state;
435 sd_id128_t machine, boot;
436 char *p = NULL, *k = NULL;
437 struct FSPRGHeader h;
438 uint64_t n, interval;
440 r = sd_id128_get_machine(&machine);
442 log_error("Failed to get machine ID: %s", strerror(-r));
446 r = sd_id128_get_boot(&boot);
448 log_error("Failed to get boot ID: %s", strerror(-r));
452 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg",
453 SD_ID128_FORMAT_VAL(machine)) < 0)
456 if (access(p, F_OK) >= 0) {
457 log_error("Evolving key file %s exists already.", p);
462 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg.tmp.XXXXXX",
463 SD_ID128_FORMAT_VAL(machine)) < 0) {
468 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
469 mpk = alloca(mpk_size);
471 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
472 seed = alloca(seed_size);
474 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
475 state = alloca(state_size);
477 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
479 log_error("Failed to open /dev/random: %m");
484 log_info("Generating seed...");
485 l = loop_read(fd, seed, seed_size, true);
486 if (l < 0 || (size_t) l != seed_size) {
487 log_error("Failed to read random seed: %s", strerror(EIO));
492 log_info("Generating key pair...");
493 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
495 log_info("Generating evolving key...");
496 FSPRG_GenState0(state, mpk, seed, seed_size);
498 interval = DEFAULT_FSPRG_INTERVAL_USEC;
499 n = now(CLOCK_REALTIME);
502 close_nointr_nofail(fd);
503 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
505 log_error("Failed to open %s: %m", k);
511 memcpy(h.signature, "KSHHRHLP", 8);
512 h.machine_id = machine;
514 h.header_size = htole64(sizeof(h));
515 h.fsprg_start_usec = htole64(n * interval);
516 h.fsprg_interval_usec = htole64(interval);
517 h.secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
518 h.state_size = htole64(state_size);
520 l = loop_write(fd, &h, sizeof(h), false);
521 if (l < 0 || (size_t) l != sizeof(h)) {
522 log_error("Failed to write header: %s", strerror(EIO));
527 l = loop_write(fd, state, state_size, false);
528 if (l < 0 || (size_t) l != state_size) {
529 log_error("Failed to write state: %s", strerror(EIO));
534 if (link(k, p) < 0) {
535 log_error("Failed to link file: %m");
540 if (isatty(STDOUT_FILENO)) {
543 "The new key pair has been generated. The evolving key has been written to the\n"
544 "following file. It will be used to protect local journal files. This file does\n"
545 "not need to be kept secret. It should not be used on multiple hosts.\n"
549 "Please write down the following " ANSI_HIGHLIGHT_ON "secret" ANSI_HIGHLIGHT_OFF " seed value. It should not be stored\n"
550 "locally on disk, and may be used to verify journal files from this host.\n"
551 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
554 for (i = 0; i < seed_size; i++) {
555 if (i > 0 && i % 3 == 0)
557 printf("%02x", ((uint8_t*) seed)[i]);
560 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) interval);
562 if (isatty(STDOUT_FILENO))
563 fputs(ANSI_HIGHLIGHT_OFF "\n", stderr);
569 close_nointr_nofail(fd);
580 log_error("Forward-secure journal verification not available.");
584 static int verify(sd_journal *j) {
591 HASHMAP_FOREACH(f, j->files, i) {
594 k = journal_file_verify(f, NULL);
596 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
599 log_info("PASS: %s", f->path);
605 int main(int argc, char *argv[]) {
607 sd_journal *j = NULL;
609 bool need_seek = false;
610 sd_id128_t previous_boot_id;
611 bool previous_boot_id_valid = false;
614 log_parse_environment();
617 r = parse_argv(argc, argv);
621 if (arg_action == ACTION_NEW_ID128) {
622 r = generate_new_id128();
626 if (arg_action == ACTION_SETUP_KEYS) {
632 r = sd_journal_open_directory(&j, arg_directory, 0);
634 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
637 log_error("Failed to open journal: %s", strerror(-r));
641 if (arg_action == ACTION_VERIFY) {
646 if (arg_action == ACTION_PRINT_HEADER) {
647 journal_print_header(j);
653 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
654 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
657 r = add_this_boot(j);
661 r = add_matches(j, argv + optind);
665 r = add_priorities(j);
671 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
673 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
675 log_error("Failed to get cutoff: %s", strerror(-r));
681 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
683 printf("Logs begin at %s, end at %s.\n",
684 format_timestamp(start_buf, sizeof(start_buf), start),
685 format_timestamp(end_buf, sizeof(end_buf), end));
689 if (arg_lines >= 0) {
690 r = sd_journal_seek_tail(j);
692 log_error("Failed to seek to tail: %s", strerror(-r));
696 r = sd_journal_previous_skip(j, arg_lines);
698 r = sd_journal_seek_head(j);
700 log_error("Failed to seek to head: %s", strerror(-r));
704 r = sd_journal_next(j);
708 log_error("Failed to iterate through journal: %s", strerror(-r));
713 have_pager = !arg_no_pager && !arg_follow && pager_open();
715 if (arg_output == OUTPUT_JSON) {
724 arg_show_all * OUTPUT_SHOW_ALL |
725 have_pager * OUTPUT_FULL_WIDTH |
726 on_tty() * OUTPUT_COLOR;
729 r = sd_journal_next(j);
731 log_error("Failed to iterate through journal: %s", strerror(-r));
739 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
741 if (previous_boot_id_valid &&
742 !sd_id128_equal(boot_id, previous_boot_id))
743 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
745 previous_boot_id = boot_id;
746 previous_boot_id_valid = true;
751 r = output_journal(j, arg_output, line, 0, flags);
761 r = sd_journal_wait(j, (uint64_t) -1);
763 log_error("Couldn't wait for log event: %s", strerror(-r));
768 if (arg_output == OUTPUT_JSON)
769 fputs("\n]\n", stdout);
777 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;