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"
47 #define DEFAULT_FSPRG_INTERVAL_USEC (15*USEC_PER_MINUTE)
49 static OutputMode arg_output = OUTPUT_SHORT;
50 static bool arg_follow = false;
51 static bool arg_show_all = false;
52 static bool arg_no_pager = false;
53 static int arg_lines = -1;
54 static bool arg_no_tail = false;
55 static bool arg_quiet = false;
56 static bool arg_local = false;
57 static bool arg_this_boot = false;
58 static const char *arg_directory = NULL;
59 static int arg_priorities = 0xFF;
66 } arg_action = ACTION_SHOW;
68 static int help(void) {
70 printf("%s [OPTIONS...] [MATCH]\n\n"
71 "Send control commands to or query the journal.\n\n"
72 " -h --help Show this help\n"
73 " --version Show package version\n"
74 " --no-pager Do not pipe output into a pager\n"
75 " -a --all Show all fields, including long and unprintable\n"
76 " -f --follow Follow journal\n"
77 " -n --lines=INTEGER Journal entries to show\n"
78 " --no-tail Show all lines, even in follow mode\n"
79 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
80 " verbose, export, json, cat)\n"
81 " -q --quiet Don't show privilege warning\n"
82 " -l --local Only local entries\n"
83 " -b --this-boot Show data only from current boot\n"
84 " -D --directory=PATH Show journal files from directory\n"
85 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
87 " --new-id128 Generate a new 128 Bit ID\n"
88 " --header Show journal header information\n"
89 " --setup-keys Generate new FSPRG key pair\n",
90 program_invocation_short_name);
95 static int parse_argv(int argc, char *argv[]) {
106 static const struct option options[] = {
107 { "help", no_argument, NULL, 'h' },
108 { "version" , no_argument, NULL, ARG_VERSION },
109 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
110 { "follow", no_argument, NULL, 'f' },
111 { "output", required_argument, NULL, 'o' },
112 { "all", no_argument, NULL, 'a' },
113 { "lines", required_argument, NULL, 'n' },
114 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
115 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
116 { "quiet", no_argument, NULL, 'q' },
117 { "local", no_argument, NULL, 'l' },
118 { "this-boot", no_argument, NULL, 'b' },
119 { "directory", required_argument, NULL, 'D' },
120 { "header", no_argument, NULL, ARG_HEADER },
121 { "priority", no_argument, NULL, 'p' },
122 { "setup-keys",no_argument, NULL, ARG_SETUP_KEYS},
131 while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
140 puts(PACKAGE_STRING);
142 puts(SYSTEMD_FEATURES);
154 arg_output = output_mode_from_string(optarg);
155 if (arg_output < 0) {
156 log_error("Unknown output '%s'.", optarg);
167 r = safe_atoi(optarg, &arg_lines);
168 if (r < 0 || arg_lines < 0) {
169 log_error("Failed to parse lines '%s'", optarg);
179 arg_action = ACTION_NEW_ID128;
191 arg_this_boot = true;
195 arg_directory = optarg;
199 arg_action = ACTION_PRINT_HEADER;
203 arg_action = ACTION_SETUP_KEYS;
209 dots = strstr(optarg, "..");
215 a = strndup(optarg, dots - optarg);
219 from = log_level_from_string(a);
220 to = log_level_from_string(dots + 2);
223 if (from < 0 || to < 0) {
224 log_error("Failed to parse log level range %s", optarg);
231 for (i = from; i <= to; i++)
232 arg_priorities |= 1 << i;
234 for (i = to; i <= from; i++)
235 arg_priorities |= 1 << i;
241 p = log_level_from_string(optarg);
243 log_error("Unknown log level %s", optarg);
249 for (i = 0; i <= p; i++)
250 arg_priorities |= 1 << i;
260 log_error("Unknown option code %c", c);
265 if (arg_follow && !arg_no_tail && arg_lines < 0)
271 static bool on_tty(void) {
274 /* Note that this is invoked relatively early, before we start
275 * the pager. That means the value we return reflects whether
276 * we originally were started on a tty, not if we currently
277 * are. But this is intended, since we want colour and so on
278 * when run in our own pager. */
280 if (_unlikely_(t < 0))
281 t = isatty(STDOUT_FILENO) > 0;
286 static int generate_new_id128(void) {
291 r = sd_id128_randomize(&id);
293 log_error("Failed to generate ID: %s", strerror(-r));
297 printf("As string:\n"
298 SD_ID128_FORMAT_STR "\n\n"
300 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
302 "#define MESSAGE_XYZ SD_ID128_MAKE(",
303 SD_ID128_FORMAT_VAL(id),
304 SD_ID128_FORMAT_VAL(id));
306 for (i = 0; i < 16; i++)
307 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
309 fputs(")\n", stdout);
314 static int add_matches(sd_journal *j, char **args) {
320 STRV_FOREACH(i, args) {
323 r = sd_journal_add_disjunction(j);
324 else if (path_is_absolute(*i)) {
329 p = canonicalize_file_name(*i);
332 if (stat(path, &st) < 0) {
334 log_error("Couldn't stat file: %m");
338 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
339 t = strappend("_EXE=", path);
340 else if (S_ISCHR(st.st_mode))
341 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
342 else if (S_ISBLK(st.st_mode))
343 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
346 log_error("File is not a device node, regular file or is not executable: %s", *i);
355 r = sd_journal_add_match(j, t, 0);
358 r = sd_journal_add_match(j, *i, 0);
361 log_error("Failed to add match '%s': %s", *i, strerror(-r));
369 static int add_this_boot(sd_journal *j) {
370 char match[9+32+1] = "_BOOT_ID=";
379 r = sd_id128_get_boot(&boot_id);
381 log_error("Failed to get boot id: %s", strerror(-r));
385 sd_id128_to_string(boot_id, match + 9);
386 r = sd_journal_add_match(j, match, strlen(match));
388 log_error("Failed to add match: %s", strerror(-r));
395 static int add_priorities(sd_journal *j) {
396 char match[] = "PRIORITY=0";
401 if (arg_priorities == 0xFF)
404 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
405 if (arg_priorities & (1 << i)) {
406 match[sizeof(match)-2] = '0' + i;
408 log_info("adding match %s", match);
410 r = sd_journal_add_match(j, match, strlen(match));
412 log_error("Failed to add match: %s", strerror(-r));
420 static int setup_keys(void) {
422 size_t mpk_size, seed_size, state_size, i;
423 uint8_t *mpk, *seed, *state;
426 sd_id128_t machine, boot;
427 char *p = NULL, *k = NULL;
428 struct FSPRGHeader h;
429 uint64_t n, interval;
431 r = sd_id128_get_machine(&machine);
433 log_error("Failed to get machine ID: %s", strerror(-r));
437 r = sd_id128_get_boot(&boot);
439 log_error("Failed to get boot ID: %s", strerror(-r));
443 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg",
444 SD_ID128_FORMAT_VAL(machine)) < 0)
447 if (access(p, F_OK) >= 0) {
448 log_error("Evolving key file %s exists already.", p);
453 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg.tmp.XXXXXX",
454 SD_ID128_FORMAT_VAL(machine)) < 0) {
459 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
460 mpk = alloca(mpk_size);
462 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
463 seed = alloca(seed_size);
465 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
466 state = alloca(state_size);
468 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
470 log_error("Failed to open /dev/random: %m");
475 log_info("Generating seed...");
476 l = loop_read(fd, seed, seed_size, true);
477 if (l < 0 || (size_t) l != seed_size) {
478 log_error("Failed to read random seed: %s", strerror(EIO));
483 log_info("Generating key pair...");
484 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
486 log_info("Generating evolving key...");
487 FSPRG_GenState0(state, mpk, seed, seed_size);
489 interval = DEFAULT_FSPRG_INTERVAL_USEC;
490 n = now(CLOCK_REALTIME);
493 close_nointr_nofail(fd);
494 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
496 log_error("Failed to open %s: %m", k);
502 memcpy(h.signature, "KSHHRHLP", 8);
503 h.machine_id = machine;
505 h.header_size = htole64(sizeof(h));
506 h.fsprg_start_usec = htole64(n * interval);
507 h.fsprg_interval_usec = htole64(interval);
508 h.secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
509 h.state_size = htole64(state_size);
511 l = loop_write(fd, &h, sizeof(h), false);
512 if (l < 0 || (size_t) l != sizeof(h)) {
513 log_error("Failed to write header: %s", strerror(EIO));
518 l = loop_write(fd, state, state_size, false);
519 if (l < 0 || (size_t) l != state_size) {
520 log_error("Failed to write state: %s", strerror(EIO));
525 if (link(k, p) < 0) {
526 log_error("Failed to link file: %m");
531 if (isatty(STDOUT_FILENO)) {
534 "The new key pair has been generated. The evolving key has been written to the\n"
535 "following file. It will be used to protect local journal files. This file does\n"
536 "not need to be kept secret. It should not be used on multiple hosts.\n"
540 "Please write down the following " ANSI_HIGHLIGHT_ON "secret" ANSI_HIGHLIGHT_OFF " seed value. It should not be stored\n"
541 "locally on disk, and may be used to verify journal files from this host.\n"
542 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
545 for (i = 0; i < seed_size; i++) {
546 if (i > 0 && i % 3 == 0)
548 printf("%02x", ((uint8_t*) seed)[i]);
551 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) interval);
553 if (isatty(STDOUT_FILENO))
554 fputs(ANSI_HIGHLIGHT_OFF "\n", stderr);
560 close_nointr_nofail(fd);
571 log_error("Forward-secure journal verification not available.");
575 int main(int argc, char *argv[]) {
577 sd_journal *j = NULL;
579 bool need_seek = false;
580 sd_id128_t previous_boot_id;
581 bool previous_boot_id_valid = false;
584 log_parse_environment();
587 r = parse_argv(argc, argv);
591 if (arg_action == ACTION_NEW_ID128) {
592 r = generate_new_id128();
596 if (arg_action == ACTION_SETUP_KEYS) {
602 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
603 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
607 r = sd_journal_open_directory(&j, arg_directory, 0);
609 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
612 log_error("Failed to open journal: %s", strerror(-r));
616 if (arg_action == ACTION_PRINT_HEADER) {
617 journal_print_header(j);
622 r = add_this_boot(j);
626 r = add_matches(j, argv + optind);
630 r = add_priorities(j);
636 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
638 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
640 log_error("Failed to get cutoff: %s", strerror(-r));
646 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
648 printf("Logs begin at %s, end at %s.\n",
649 format_timestamp(start_buf, sizeof(start_buf), start),
650 format_timestamp(end_buf, sizeof(end_buf), end));
654 if (arg_lines >= 0) {
655 r = sd_journal_seek_tail(j);
657 log_error("Failed to seek to tail: %s", strerror(-r));
661 r = sd_journal_previous_skip(j, arg_lines);
663 r = sd_journal_seek_head(j);
665 log_error("Failed to seek to head: %s", strerror(-r));
669 r = sd_journal_next(j);
673 log_error("Failed to iterate through journal: %s", strerror(-r));
678 have_pager = !arg_no_pager && !arg_follow && pager_open();
680 if (arg_output == OUTPUT_JSON) {
689 arg_show_all * OUTPUT_SHOW_ALL |
690 have_pager * OUTPUT_FULL_WIDTH |
691 on_tty() * OUTPUT_COLOR;
694 r = sd_journal_next(j);
696 log_error("Failed to iterate through journal: %s", strerror(-r));
704 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
706 if (previous_boot_id_valid &&
707 !sd_id128_equal(boot_id, previous_boot_id))
708 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
710 previous_boot_id = boot_id;
711 previous_boot_id_valid = true;
716 r = output_journal(j, arg_output, line, 0, flags);
726 r = sd_journal_wait(j, (uint64_t) -1);
728 log_error("Couldn't wait for log event: %s", strerror(-r));
733 if (arg_output == OUTPUT_JSON)
734 fputs("\n]\n", stdout);
742 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;