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
);
200 signal(SIGWINCH
, columns_cache_reset
);
204 arg_output
= output_mode_from_string(optarg
);
205 if (arg_output
< 0) {
206 log_error("Unknown output format '%s'.", optarg
);
210 if (arg_output
== OUTPUT_EXPORT
||
211 arg_output
== OUTPUT_JSON
||
212 arg_output
== OUTPUT_JSON_PRETTY
||
213 arg_output
== OUTPUT_JSON_SSE
||
214 arg_output
== OUTPUT_CAT
)
225 r
= safe_atou(optarg
, &arg_lines
);
226 if (r
< 0 || arg_lines
<= 0) {
227 log_error("Failed to parse lines '%s'", optarg
);
240 arg_action
= ACTION_NEW_ID128
;
252 arg_this_boot
= true;
256 arg_directory
= optarg
;
264 arg_action
= ACTION_PRINT_HEADER
;
268 arg_action
= ACTION_VERIFY
;
272 arg_action
= ACTION_DISK_USAGE
;
277 arg_action
= ACTION_SETUP_KEYS
;
282 arg_action
= ACTION_VERIFY
;
283 arg_verify_key
= optarg
;
288 r
= parse_usec(optarg
, &arg_interval
);
289 if (r
< 0 || arg_interval
<= 0) {
290 log_error("Failed to parse sealing key change interval: %s", optarg
);
298 log_error("Forward-secure sealing not available.");
305 dots
= strstr(optarg
, "..");
311 a
= strndup(optarg
, dots
- optarg
);
315 from
= log_level_from_string(a
);
316 to
= log_level_from_string(dots
+ 2);
319 if (from
< 0 || to
< 0) {
320 log_error("Failed to parse log level range %s", optarg
);
327 for (i
= from
; i
<= to
; i
++)
328 arg_priorities
|= 1 << i
;
330 for (i
= to
; i
<= from
; i
++)
331 arg_priorities
|= 1 << i
;
337 p
= log_level_from_string(optarg
);
339 log_error("Unknown log level %s", optarg
);
345 for (i
= 0; i
<= p
; i
++)
346 arg_priorities
|= 1 << i
;
353 r
= parse_timestamp(optarg
, &arg_since
);
355 log_error("Failed to parse timestamp: %s", optarg
);
358 arg_since_set
= true;
362 r
= parse_timestamp(optarg
, &arg_until
);
364 log_error("Failed to parse timestamp: %s", optarg
);
367 arg_until_set
= true;
382 log_error("Unknown option code %c", c
);
387 if (arg_follow
&& !arg_no_tail
&& arg_lines
<= 0)
390 if (arg_since_set
&& arg_until_set
&& arg_since_set
> arg_until_set
) {
391 log_error("--since= must be before --until=.");
395 if (arg_cursor
&& arg_since_set
) {
396 log_error("Please specify either --since= or --cursor=, not both.");
403 static int generate_new_id128(void) {
408 r
= sd_id128_randomize(&id
);
410 log_error("Failed to generate ID: %s", strerror(-r
));
414 printf("As string:\n"
415 SD_ID128_FORMAT_STR
"\n\n"
417 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
419 "#define MESSAGE_XYZ SD_ID128_MAKE(",
420 SD_ID128_FORMAT_VAL(id
),
421 SD_ID128_FORMAT_VAL(id
));
423 for (i
= 0; i
< 16; i
++)
424 printf("%02x%s", id
.bytes
[i
], i
!= 15 ? "," : "");
426 fputs(")\n", stdout
);
431 static int add_matches(sd_journal
*j
, char **args
) {
437 STRV_FOREACH(i
, args
) {
440 r
= sd_journal_add_disjunction(j
);
441 else if (path_is_absolute(*i
)) {
446 p
= canonicalize_file_name(*i
);
449 if (stat(path
, &st
) < 0) {
451 log_error("Couldn't stat file: %m");
455 if (S_ISREG(st
.st_mode
) && (0111 & st
.st_mode
))
456 t
= strappend("_EXE=", path
);
457 else if (S_ISCHR(st
.st_mode
))
458 asprintf(&t
, "_KERNEL_DEVICE=c%u:%u", major(st
.st_rdev
), minor(st
.st_rdev
));
459 else if (S_ISBLK(st
.st_mode
))
460 asprintf(&t
, "_KERNEL_DEVICE=b%u:%u", major(st
.st_rdev
), minor(st
.st_rdev
));
463 log_error("File is not a device node, regular file or is not executable: %s", *i
);
472 r
= sd_journal_add_match(j
, t
, 0);
475 r
= sd_journal_add_match(j
, *i
, 0);
478 log_error("Failed to add match '%s': %s", *i
, strerror(-r
));
486 static int add_this_boot(sd_journal
*j
) {
487 char match
[9+32+1] = "_BOOT_ID=";
496 r
= sd_id128_get_boot(&boot_id
);
498 log_error("Failed to get boot id: %s", strerror(-r
));
502 sd_id128_to_string(boot_id
, match
+ 9);
503 r
= sd_journal_add_match(j
, match
, strlen(match
));
505 log_error("Failed to add match: %s", strerror(-r
));
512 static int add_unit(sd_journal
*j
) {
513 _cleanup_free_
char *m
= NULL
, *u
= NULL
;
518 if (isempty(arg_unit
))
521 u
= unit_name_mangle(arg_unit
);
525 m
= strappend("_SYSTEMD_UNIT=", u
);
529 r
= sd_journal_add_match(j
, m
, strlen(m
));
531 log_error("Failed to add match: %s", strerror(-r
));
538 static int add_priorities(sd_journal
*j
) {
539 char match
[] = "PRIORITY=0";
544 if (arg_priorities
== 0xFF)
547 for (i
= LOG_EMERG
; i
<= LOG_DEBUG
; i
++)
548 if (arg_priorities
& (1 << i
)) {
549 match
[sizeof(match
)-2] = '0' + i
;
551 log_info("adding match %s", match
);
553 r
= sd_journal_add_match(j
, match
, strlen(match
));
555 log_error("Failed to add match: %s", strerror(-r
));
563 static int setup_keys(void) {
565 size_t mpk_size
, seed_size
, state_size
, i
;
566 uint8_t *mpk
, *seed
, *state
;
568 int fd
= -1, r
, attr
= 0;
569 sd_id128_t machine
, boot
;
570 char *p
= NULL
, *k
= NULL
;
574 r
= sd_id128_get_machine(&machine
);
576 log_error("Failed to get machine ID: %s", strerror(-r
));
580 r
= sd_id128_get_boot(&boot
);
582 log_error("Failed to get boot ID: %s", strerror(-r
));
586 if (asprintf(&p
, "/var/log/journal/" SD_ID128_FORMAT_STR
"/fss",
587 SD_ID128_FORMAT_VAL(machine
)) < 0)
590 if (access(p
, F_OK
) >= 0) {
591 log_error("Sealing key file %s exists already.", p
);
596 if (asprintf(&k
, "/var/log/journal/" SD_ID128_FORMAT_STR
"/fss.tmp.XXXXXX",
597 SD_ID128_FORMAT_VAL(machine
)) < 0) {
602 mpk_size
= FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR
);
603 mpk
= alloca(mpk_size
);
605 seed_size
= FSPRG_RECOMMENDED_SEEDLEN
;
606 seed
= alloca(seed_size
);
608 state_size
= FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR
);
609 state
= alloca(state_size
);
611 fd
= open("/dev/random", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
613 log_error("Failed to open /dev/random: %m");
618 log_info("Generating seed...");
619 l
= loop_read(fd
, seed
, seed_size
, true);
620 if (l
< 0 || (size_t) l
!= seed_size
) {
621 log_error("Failed to read random seed: %s", strerror(EIO
));
626 log_info("Generating key pair...");
627 FSPRG_GenMK(NULL
, mpk
, seed
, seed_size
, FSPRG_RECOMMENDED_SECPAR
);
629 log_info("Generating sealing key...");
630 FSPRG_GenState0(state
, mpk
, seed
, seed_size
);
632 assert(arg_interval
> 0);
634 n
= now(CLOCK_REALTIME
);
637 close_nointr_nofail(fd
);
638 fd
= mkostemp(k
, O_WRONLY
|O_CLOEXEC
|O_NOCTTY
);
640 log_error("Failed to open %s: %m", k
);
645 /* Enable secure remove, exclusion from dump, synchronous
646 * writing and in-place updating */
647 if (ioctl(fd
, FS_IOC_GETFLAGS
, &attr
) < 0)
648 log_warning("FS_IOC_GETFLAGS failed: %m");
650 attr
|= FS_SECRM_FL
|FS_NODUMP_FL
|FS_SYNC_FL
|FS_NOCOW_FL
;
652 if (ioctl(fd
, FS_IOC_SETFLAGS
, &attr
) < 0)
653 log_warning("FS_IOC_SETFLAGS failed: %m");
656 memcpy(h
.signature
, "KSHHRHLP", 8);
657 h
.machine_id
= machine
;
659 h
.header_size
= htole64(sizeof(h
));
660 h
.start_usec
= htole64(n
* arg_interval
);
661 h
.interval_usec
= htole64(arg_interval
);
662 h
.fsprg_secpar
= htole16(FSPRG_RECOMMENDED_SECPAR
);
663 h
.fsprg_state_size
= htole64(state_size
);
665 l
= loop_write(fd
, &h
, sizeof(h
), false);
666 if (l
< 0 || (size_t) l
!= sizeof(h
)) {
667 log_error("Failed to write header: %s", strerror(EIO
));
672 l
= loop_write(fd
, state
, state_size
, false);
673 if (l
< 0 || (size_t) l
!= state_size
) {
674 log_error("Failed to write state: %s", strerror(EIO
));
679 if (link(k
, p
) < 0) {
680 log_error("Failed to link file: %m");
688 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON
"secret sealing key" ANSI_HIGHLIGHT_OFF
" has been written to\n"
689 "the following local file. This key file is automatically updated when the\n"
690 "sealing key is advanced. It should not be used on multiple hosts.\n"
694 "Please write down the following " ANSI_HIGHLIGHT_ON
"secret verification key" ANSI_HIGHLIGHT_OFF
". It should be stored\n"
695 "at a safe location and should not be saved locally on disk.\n"
696 "\n\t" ANSI_HIGHLIGHT_RED_ON
, p
);
699 for (i
= 0; i
< seed_size
; i
++) {
700 if (i
> 0 && i
% 3 == 0)
702 printf("%02x", ((uint8_t*) seed
)[i
]);
705 printf("/%llx-%llx\n", (unsigned long long) n
, (unsigned long long) arg_interval
);
708 char tsb
[FORMAT_TIMESPAN_MAX
], *hn
;
711 ANSI_HIGHLIGHT_OFF
"\n"
712 "The sealing key is automatically changed every %s.\n",
713 format_timespan(tsb
, sizeof(tsb
), arg_interval
));
715 hn
= gethostname_malloc();
718 hostname_cleanup(hn
);
719 fprintf(stderr
, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR
".\n", hn
, SD_ID128_FORMAT_VAL(machine
));
721 fprintf(stderr
, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR
".\n", SD_ID128_FORMAT_VAL(machine
));
724 /* If this is not an UTF-8 system don't print any QR codes */
725 setlocale(LC_CTYPE
, "");
727 if (streq_ptr(nl_langinfo(CODESET
), "UTF-8")) {
728 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr
);
729 print_qr_code(stderr
, seed
, seed_size
, n
, arg_interval
, hn
, machine
);
739 close_nointr_nofail(fd
);
750 log_error("Forward-secure sealing not available.");
755 static int verify(sd_journal
*j
) {
762 log_show_color(true);
764 HASHMAP_FOREACH(f
, j
->files
, i
) {
766 usec_t first
, validated
, last
;
769 if (!arg_verify_key
&& JOURNAL_HEADER_SEALED(f
->header
))
770 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f
->path
);
773 k
= journal_file_verify(f
, arg_verify_key
, &first
, &validated
, &last
, true);
775 /* If the key was invalid give up right-away. */
778 log_warning("FAIL: %s (%s)", f
->path
, strerror(-k
));
781 char a
[FORMAT_TIMESTAMP_MAX
], b
[FORMAT_TIMESTAMP_MAX
], c
[FORMAT_TIMESPAN_MAX
];
782 log_info("PASS: %s", f
->path
);
784 if (arg_verify_key
&& JOURNAL_HEADER_SEALED(f
->header
)) {
786 log_info("=> Validated from %s to %s, final %s entries not sealed.",
787 format_timestamp(a
, sizeof(a
), first
),
788 format_timestamp(b
, sizeof(b
), validated
),
789 format_timespan(c
, sizeof(c
), last
> validated
? last
- validated
: 0));
791 log_info("=> No sealing yet, %s of entries not sealed.",
792 format_timespan(c
, sizeof(c
), last
- first
));
794 log_info("=> No sealing yet, no entries in file.");
802 static int access_check(void) {
805 if (access("/var/log/journal", F_OK
) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
806 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
810 if (!arg_quiet
&& geteuid() != 0 && in_group("adm") <= 0)
811 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
813 if (geteuid() != 0 && in_group("adm") <= 0) {
814 log_error("No access to messages. Only users in the group 'adm' can see messages.");
822 int main(int argc
, char *argv
[]) {
824 sd_journal
*j
= NULL
;
825 bool need_seek
= false;
826 sd_id128_t previous_boot_id
;
827 bool previous_boot_id_valid
= false;
828 unsigned n_shown
= 0;
830 log_parse_environment();
833 r
= parse_argv(argc
, argv
);
837 if (arg_action
== ACTION_NEW_ID128
) {
838 r
= generate_new_id128();
842 if (arg_action
== ACTION_SETUP_KEYS
) {
852 r
= sd_journal_open_directory(&j
, arg_directory
, 0);
854 r
= sd_journal_open(&j
, arg_merge
? 0 : SD_JOURNAL_LOCAL_ONLY
);
856 log_error("Failed to open journal: %s", strerror(-r
));
860 if (arg_action
== ACTION_VERIFY
) {
865 if (arg_action
== ACTION_PRINT_HEADER
) {
866 journal_print_header(j
);
871 if (arg_action
== ACTION_DISK_USAGE
) {
873 char sbytes
[FORMAT_BYTES_MAX
];
875 r
= sd_journal_get_usage(j
, &bytes
);
879 printf("Journals take up %s on disk.\n", format_bytes(sbytes
, sizeof(sbytes
), bytes
));
884 r
= add_this_boot(j
);
892 r
= add_matches(j
, argv
+ optind
);
896 r
= add_priorities(j
);
904 r
= sd_journal_query_unique(j
, arg_field
);
906 log_error("Failed to query unique data objects: %s", strerror(-r
));
910 SD_JOURNAL_FOREACH_UNIQUE(j
, data
, size
) {
913 if (arg_lines
> 0 && n_shown
>= arg_lines
)
916 eq
= memchr(data
, '=', size
);
918 printf("%.*s\n", (int) (size
- ((const uint8_t*) eq
- (const uint8_t*) data
+ 1)), (const char*) eq
+ 1);
920 printf("%.*s\n", (int) size
, (const char*) data
);
930 r
= sd_journal_seek_cursor(j
, arg_cursor
);
932 log_error("Failed to seek to cursor: %s", strerror(-r
));
936 r
= sd_journal_next(j
);
938 } else if (arg_since_set
) {
939 r
= sd_journal_seek_realtime_usec(j
, arg_since
);
941 log_error("Failed to seek to date: %s", strerror(-r
));
944 r
= sd_journal_next(j
);
946 } else if (arg_lines
> 0) {
947 r
= sd_journal_seek_tail(j
);
949 log_error("Failed to seek to tail: %s", strerror(-r
));
953 r
= sd_journal_previous_skip(j
, arg_lines
);
956 r
= sd_journal_seek_head(j
);
958 log_error("Failed to seek to head: %s", strerror(-r
));
962 r
= sd_journal_next(j
);
966 log_error("Failed to iterate through journal: %s", strerror(-r
));
970 if (!arg_no_pager
&& !arg_follow
)
975 char start_buf
[FORMAT_TIMESTAMP_MAX
], end_buf
[FORMAT_TIMESTAMP_MAX
];
977 r
= sd_journal_get_cutoff_realtime_usec(j
, &start
, &end
);
979 log_error("Failed to get cutoff: %s", strerror(-r
));
985 printf("-- Logs begin at %s. --\n",
986 format_timestamp(start_buf
, sizeof(start_buf
), start
));
988 printf("-- Logs begin at %s, end at %s. --\n",
989 format_timestamp(start_buf
, sizeof(start_buf
), start
),
990 format_timestamp(end_buf
, sizeof(end_buf
), end
));
995 while (arg_lines
== 0 || arg_follow
|| n_shown
< arg_lines
) {
999 r
= sd_journal_next(j
);
1001 log_error("Failed to iterate through journal: %s", strerror(-r
));
1009 if (arg_until_set
) {
1012 r
= sd_journal_get_realtime_usec(j
, &usec
);
1014 log_error("Failed to determine timestamp: %s", strerror(-r
));
1022 r
= sd_journal_get_monotonic_usec(j
, NULL
, &boot_id
);
1024 if (previous_boot_id_valid
&&
1025 !sd_id128_equal(boot_id
, previous_boot_id
))
1026 printf(ANSI_HIGHLIGHT_ON
"-- Reboot --" ANSI_HIGHLIGHT_OFF
"\n");
1028 previous_boot_id
= boot_id
;
1029 previous_boot_id_valid
= true;
1034 arg_all
* OUTPUT_SHOW_ALL
|
1035 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH
|
1036 on_tty() * OUTPUT_COLOR
;
1038 r
= output_journal(stdout
, j
, arg_output
, 0, flags
);
1049 r
= sd_journal_wait(j
, (uint64_t) -1);
1051 log_error("Couldn't wait for journal event: %s", strerror(-r
));
1058 sd_journal_close(j
);
1062 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;