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;
67 } arg_action = ACTION_SHOW;
69 static int help(void) {
71 printf("%s [OPTIONS...] [MATCH]\n\n"
72 "Send control commands to or query the journal.\n\n"
73 " -h --help Show this help\n"
74 " --version Show package version\n"
75 " --no-pager Do not pipe output into a pager\n"
76 " -a --all Show all fields, including long and unprintable\n"
77 " -f --follow Follow journal\n"
78 " -n --lines=INTEGER Journal entries to show\n"
79 " --no-tail Show all lines, even in follow mode\n"
80 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
81 " verbose, export, json, cat)\n"
82 " -q --quiet Don't show privilege warning\n"
83 " -l --local Only local entries\n"
84 " -b --this-boot Show data only from current boot\n"
85 " -D --directory=PATH Show journal files from directory\n"
86 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
88 " --new-id128 Generate a new 128 Bit ID\n"
89 " --header Show journal header information\n"
90 " --setup-keys Generate new FSPRG key pair\n"
91 " --verify Verify journal file consistency\n",
92 program_invocation_short_name);
97 static int parse_argv(int argc, char *argv[]) {
109 static const struct option options[] = {
110 { "help", no_argument, NULL, 'h' },
111 { "version" , no_argument, NULL, ARG_VERSION },
112 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
113 { "follow", no_argument, NULL, 'f' },
114 { "output", required_argument, NULL, 'o' },
115 { "all", no_argument, NULL, 'a' },
116 { "lines", required_argument, NULL, 'n' },
117 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
118 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
119 { "quiet", no_argument, NULL, 'q' },
120 { "local", no_argument, NULL, 'l' },
121 { "this-boot", no_argument, NULL, 'b' },
122 { "directory", required_argument, NULL, 'D' },
123 { "header", no_argument, NULL, ARG_HEADER },
124 { "priority", no_argument, NULL, 'p' },
125 { "setup-keys",no_argument, NULL, ARG_SETUP_KEYS},
126 { "verify", no_argument, NULL, ARG_VERIFY },
135 while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
144 puts(PACKAGE_STRING);
146 puts(SYSTEMD_FEATURES);
158 arg_output = output_mode_from_string(optarg);
159 if (arg_output < 0) {
160 log_error("Unknown output '%s'.", optarg);
171 r = safe_atoi(optarg, &arg_lines);
172 if (r < 0 || arg_lines < 0) {
173 log_error("Failed to parse lines '%s'", optarg);
183 arg_action = ACTION_NEW_ID128;
195 arg_this_boot = true;
199 arg_directory = optarg;
203 arg_action = ACTION_PRINT_HEADER;
207 arg_action = ACTION_SETUP_KEYS;
211 arg_action = ACTION_VERIFY;
217 dots = strstr(optarg, "..");
223 a = strndup(optarg, dots - optarg);
227 from = log_level_from_string(a);
228 to = log_level_from_string(dots + 2);
231 if (from < 0 || to < 0) {
232 log_error("Failed to parse log level range %s", optarg);
239 for (i = from; i <= to; i++)
240 arg_priorities |= 1 << i;
242 for (i = to; i <= from; i++)
243 arg_priorities |= 1 << i;
249 p = log_level_from_string(optarg);
251 log_error("Unknown log level %s", optarg);
257 for (i = 0; i <= p; i++)
258 arg_priorities |= 1 << i;
268 log_error("Unknown option code %c", c);
273 if (arg_follow && !arg_no_tail && arg_lines < 0)
279 static bool on_tty(void) {
282 /* Note that this is invoked relatively early, before we start
283 * the pager. That means the value we return reflects whether
284 * we originally were started on a tty, not if we currently
285 * are. But this is intended, since we want colour and so on
286 * when run in our own pager. */
288 if (_unlikely_(t < 0))
289 t = isatty(STDOUT_FILENO) > 0;
294 static int generate_new_id128(void) {
299 r = sd_id128_randomize(&id);
301 log_error("Failed to generate ID: %s", strerror(-r));
305 printf("As string:\n"
306 SD_ID128_FORMAT_STR "\n\n"
308 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
310 "#define MESSAGE_XYZ SD_ID128_MAKE(",
311 SD_ID128_FORMAT_VAL(id),
312 SD_ID128_FORMAT_VAL(id));
314 for (i = 0; i < 16; i++)
315 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
317 fputs(")\n", stdout);
322 static int add_matches(sd_journal *j, char **args) {
328 STRV_FOREACH(i, args) {
331 r = sd_journal_add_disjunction(j);
332 else if (path_is_absolute(*i)) {
337 p = canonicalize_file_name(*i);
340 if (stat(path, &st) < 0) {
342 log_error("Couldn't stat file: %m");
346 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
347 t = strappend("_EXE=", path);
348 else if (S_ISCHR(st.st_mode))
349 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
350 else if (S_ISBLK(st.st_mode))
351 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
354 log_error("File is not a device node, regular file or is not executable: %s", *i);
363 r = sd_journal_add_match(j, t, 0);
366 r = sd_journal_add_match(j, *i, 0);
369 log_error("Failed to add match '%s': %s", *i, strerror(-r));
377 static int add_this_boot(sd_journal *j) {
378 char match[9+32+1] = "_BOOT_ID=";
387 r = sd_id128_get_boot(&boot_id);
389 log_error("Failed to get boot id: %s", strerror(-r));
393 sd_id128_to_string(boot_id, match + 9);
394 r = sd_journal_add_match(j, match, strlen(match));
396 log_error("Failed to add match: %s", strerror(-r));
403 static int add_priorities(sd_journal *j) {
404 char match[] = "PRIORITY=0";
409 if (arg_priorities == 0xFF)
412 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
413 if (arg_priorities & (1 << i)) {
414 match[sizeof(match)-2] = '0' + i;
416 log_info("adding match %s", match);
418 r = sd_journal_add_match(j, match, strlen(match));
420 log_error("Failed to add match: %s", strerror(-r));
428 static int setup_keys(void) {
430 size_t mpk_size, seed_size, state_size, i;
431 uint8_t *mpk, *seed, *state;
434 sd_id128_t machine, boot;
435 char *p = NULL, *k = NULL;
436 struct FSPRGHeader h;
437 uint64_t n, interval;
439 r = sd_id128_get_machine(&machine);
441 log_error("Failed to get machine ID: %s", strerror(-r));
445 r = sd_id128_get_boot(&boot);
447 log_error("Failed to get boot ID: %s", strerror(-r));
451 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg",
452 SD_ID128_FORMAT_VAL(machine)) < 0)
455 if (access(p, F_OK) >= 0) {
456 log_error("Evolving key file %s exists already.", p);
461 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg.tmp.XXXXXX",
462 SD_ID128_FORMAT_VAL(machine)) < 0) {
467 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
468 mpk = alloca(mpk_size);
470 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
471 seed = alloca(seed_size);
473 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
474 state = alloca(state_size);
476 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
478 log_error("Failed to open /dev/random: %m");
483 log_info("Generating seed...");
484 l = loop_read(fd, seed, seed_size, true);
485 if (l < 0 || (size_t) l != seed_size) {
486 log_error("Failed to read random seed: %s", strerror(EIO));
491 log_info("Generating key pair...");
492 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
494 log_info("Generating evolving key...");
495 FSPRG_GenState0(state, mpk, seed, seed_size);
497 interval = DEFAULT_FSPRG_INTERVAL_USEC;
498 n = now(CLOCK_REALTIME);
501 close_nointr_nofail(fd);
502 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
504 log_error("Failed to open %s: %m", k);
510 memcpy(h.signature, "KSHHRHLP", 8);
511 h.machine_id = machine;
513 h.header_size = htole64(sizeof(h));
514 h.fsprg_start_usec = htole64(n * interval);
515 h.fsprg_interval_usec = htole64(interval);
516 h.secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
517 h.state_size = htole64(state_size);
519 l = loop_write(fd, &h, sizeof(h), false);
520 if (l < 0 || (size_t) l != sizeof(h)) {
521 log_error("Failed to write header: %s", strerror(EIO));
526 l = loop_write(fd, state, state_size, false);
527 if (l < 0 || (size_t) l != state_size) {
528 log_error("Failed to write state: %s", strerror(EIO));
533 if (link(k, p) < 0) {
534 log_error("Failed to link file: %m");
539 if (isatty(STDOUT_FILENO)) {
542 "The new key pair has been generated. The evolving key has been written to the\n"
543 "following file. It will be used to protect local journal files. This file does\n"
544 "not need to be kept secret. It should not be used on multiple hosts.\n"
548 "Please write down the following " ANSI_HIGHLIGHT_ON "secret" ANSI_HIGHLIGHT_OFF " seed value. It should not be stored\n"
549 "locally on disk, and may be used to verify journal files from this host.\n"
550 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
553 for (i = 0; i < seed_size; i++) {
554 if (i > 0 && i % 3 == 0)
556 printf("%02x", ((uint8_t*) seed)[i]);
559 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) interval);
561 if (isatty(STDOUT_FILENO))
562 fputs(ANSI_HIGHLIGHT_OFF "\n", stderr);
568 close_nointr_nofail(fd);
579 log_error("Forward-secure journal verification not available.");
583 static int verify(sd_journal *j) {
590 HASHMAP_FOREACH(f, j->files, i) {
593 k = journal_file_verify(f, NULL);
595 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
598 log_info("PASS: %s", f->path);
604 int main(int argc, char *argv[]) {
606 sd_journal *j = NULL;
608 bool need_seek = false;
609 sd_id128_t previous_boot_id;
610 bool previous_boot_id_valid = false;
613 log_parse_environment();
616 r = parse_argv(argc, argv);
620 if (arg_action == ACTION_NEW_ID128) {
621 r = generate_new_id128();
625 if (arg_action == ACTION_SETUP_KEYS) {
631 r = sd_journal_open_directory(&j, arg_directory, 0);
633 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
636 log_error("Failed to open journal: %s", strerror(-r));
640 if (arg_action == ACTION_VERIFY) {
645 if (arg_action == ACTION_PRINT_HEADER) {
646 journal_print_header(j);
652 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
653 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
656 r = add_this_boot(j);
660 r = add_matches(j, argv + optind);
664 r = add_priorities(j);
670 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
672 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
674 log_error("Failed to get cutoff: %s", strerror(-r));
680 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
682 printf("Logs begin at %s, end at %s.\n",
683 format_timestamp(start_buf, sizeof(start_buf), start),
684 format_timestamp(end_buf, sizeof(end_buf), end));
688 if (arg_lines >= 0) {
689 r = sd_journal_seek_tail(j);
691 log_error("Failed to seek to tail: %s", strerror(-r));
695 r = sd_journal_previous_skip(j, arg_lines);
697 r = sd_journal_seek_head(j);
699 log_error("Failed to seek to head: %s", strerror(-r));
703 r = sd_journal_next(j);
707 log_error("Failed to iterate through journal: %s", strerror(-r));
712 have_pager = !arg_no_pager && !arg_follow && pager_open();
714 if (arg_output == OUTPUT_JSON) {
723 arg_show_all * OUTPUT_SHOW_ALL |
724 have_pager * OUTPUT_FULL_WIDTH |
725 on_tty() * OUTPUT_COLOR;
728 r = sd_journal_next(j);
730 log_error("Failed to iterate through journal: %s", strerror(-r));
738 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
740 if (previous_boot_id_valid &&
741 !sd_id128_equal(boot_id, previous_boot_id))
742 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
744 previous_boot_id = boot_id;
745 previous_boot_id_valid = true;
750 r = output_journal(j, arg_output, line, 0, flags);
760 r = sd_journal_wait(j, (uint64_t) -1);
762 log_error("Couldn't wait for log event: %s", strerror(-r));
767 if (arg_output == OUTPUT_JSON)
768 fputs("\n]\n", stdout);
776 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;