]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journalctl.c
configure.ac: Add a --without-python option.
[thirdparty/systemd.git] / src / journal / journalctl.c
CommitLineData
87d2c1ff
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
87d2c1ff
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
87d2c1ff 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
87d2c1ff
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <fcntl.h>
23#include <errno.h>
24#include <stddef.h>
3fbf9cbb
LP
25#include <string.h>
26#include <stdio.h>
27#include <unistd.h>
28#include <stdlib.h>
50f20cfd 29#include <sys/poll.h>
72f59706 30#include <time.h>
0d43c694 31#include <getopt.h>
585314e8 32#include <signal.h>
50940700 33#include <sys/stat.h>
f982e6f7
LP
34#include <sys/ioctl.h>
35#include <linux/fs.h>
cf5a3432
LP
36#include <locale.h>
37#include <langinfo.h>
87d2c1ff 38
81527be1
LP
39#include <systemd/sd-journal.h>
40
3fbf9cbb 41#include "log.h"
72f59706 42#include "util.h"
e5124088 43#include "path-util.h"
0d43c694
LP
44#include "build.h"
45#include "pager.h"
86aa7ba4 46#include "logs-show.h"
a963990f 47#include "strv.h"
dca6219e 48#include "journal-internal.h"
7560fffc 49#include "journal-def.h"
0284adc6 50#include "journal-verify.h"
4da416aa 51#include "journal-authenticate.h"
f6a971bc 52#include "journal-qrcode.h"
4da416aa 53#include "fsprg.h"
7560fffc 54
baed47c3 55#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
250d54b5 56
df50185b 57static OutputMode arg_output = OUTPUT_SHORT;
72f59706
LP
58static bool arg_follow = false;
59static bool arg_show_all = false;
0d43c694 60static bool arg_no_pager = false;
cfbc22ab 61static unsigned arg_lines = 0;
e91af489 62static bool arg_no_tail = false;
43673799 63static bool arg_quiet = false;
9e8a535f 64static bool arg_merge = false;
59cea26a 65static bool arg_this_boot = false;
8f14c832 66static const char *arg_cursor = NULL;
a963990f 67static const char *arg_directory = NULL;
941e990d 68static int arg_priorities = 0xFF;
baed47c3 69static const char *arg_verify_key = NULL;
feb12d3e 70#ifdef HAVE_GCRYPT
baed47c3 71static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
feb12d3e 72#endif
cfbc22ab
LP
73static usec_t arg_since, arg_until;
74static bool arg_since_set = false, arg_until_set = false;
50f20cfd 75
7560fffc
LP
76static enum {
77 ACTION_SHOW,
78 ACTION_NEW_ID128,
79 ACTION_PRINT_HEADER,
beec0085 80 ACTION_SETUP_KEYS,
a1a03e30
LP
81 ACTION_VERIFY,
82 ACTION_DISK_USAGE,
7560fffc
LP
83} arg_action = ACTION_SHOW;
84
0d43c694
LP
85static int help(void) {
86
89834a7c 87 printf("%s [OPTIONS...] [MATCH]\n\n"
df50185b 88 "Send control commands to or query the journal.\n\n"
baed47c3
LP
89 " -h --help Show this help\n"
90 " --version Show package version\n"
91 " --no-pager Do not pipe output into a pager\n"
92 " -a --all Show all fields, including long and unprintable\n"
cfbc22ab
LP
93 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
94 " --since=DATE Start showing entries newer or of the specified date\n"
95 " --until=DATE Stop showing entries older or of the specified date\n"
baed47c3 96 " -f --follow Follow journal\n"
1705594f 97 " -n --lines[=INTEGER] Number of journal entries to show\n"
baed47c3
LP
98 " --no-tail Show all lines, even in follow mode\n"
99 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
48383c25 100 " verbose, export, json, json-pretty, json-sse, cat)\n"
baed47c3 101 " -q --quiet Don't show privilege warning\n"
9e8a535f 102 " -m --merge Show entries from all available journals\n"
baed47c3
LP
103 " -b --this-boot Show data only from current boot\n"
104 " -D --directory=PATH Show journal files from directory\n"
105 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
7560fffc 106 "Commands:\n"
baed47c3
LP
107 " --new-id128 Generate a new 128 Bit ID\n"
108 " --header Show journal header information\n"
a1a03e30 109 " --disk-usage Show total disk usage\n"
feb12d3e 110#ifdef HAVE_GCRYPT
baed47c3
LP
111 " --setup-keys Generate new FSS key pair\n"
112 " --interval=TIME Time interval for changing the FSS sealing key\n"
113 " --verify Verify journal file consistency\n"
feb12d3e
LP
114 " --verify-key=KEY Specify FSS verification key\n"
115#endif
116 , program_invocation_short_name);
0d43c694
LP
117
118 return 0;
119}
120
121static int parse_argv(int argc, char *argv[]) {
122
123 enum {
124 ARG_VERSION = 0x100,
e91af489 125 ARG_NO_PAGER,
55ee336c 126 ARG_NO_TAIL,
dca6219e 127 ARG_NEW_ID128,
7560fffc 128 ARG_HEADER,
beec0085 129 ARG_SETUP_KEYS,
baed47c3 130 ARG_INTERVAL,
4da416aa 131 ARG_VERIFY,
a1a03e30 132 ARG_VERIFY_KEY,
cfbc22ab
LP
133 ARG_DISK_USAGE,
134 ARG_SINCE,
135 ARG_UNTIL
0d43c694
LP
136 };
137
138 static const struct option options[] = {
baed47c3
LP
139 { "help", no_argument, NULL, 'h' },
140 { "version" , no_argument, NULL, ARG_VERSION },
141 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
142 { "follow", no_argument, NULL, 'f' },
143 { "output", required_argument, NULL, 'o' },
144 { "all", no_argument, NULL, 'a' },
1705594f 145 { "lines", optional_argument, NULL, 'n' },
baed47c3
LP
146 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
147 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
148 { "quiet", no_argument, NULL, 'q' },
9e8a535f 149 { "merge", no_argument, NULL, 'm' },
baed47c3
LP
150 { "this-boot", no_argument, NULL, 'b' },
151 { "directory", required_argument, NULL, 'D' },
152 { "header", no_argument, NULL, ARG_HEADER },
153 { "priority", no_argument, NULL, 'p' },
154 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
155 { "interval", required_argument, NULL, ARG_INTERVAL },
156 { "verify", no_argument, NULL, ARG_VERIFY },
157 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
a1a03e30 158 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
ad9eafab 159 { "cursor", required_argument, NULL, 'c' },
cfbc22ab
LP
160 { "since", required_argument, NULL, ARG_SINCE },
161 { "until", required_argument, NULL, ARG_UNTIL },
baed47c3 162 { NULL, 0, NULL, 0 }
0d43c694
LP
163 };
164
2100675e 165 int c, r;
0d43c694
LP
166
167 assert(argc >= 0);
168 assert(argv);
169
8f14c832 170 while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:", options, NULL)) >= 0) {
0d43c694
LP
171
172 switch (c) {
173
174 case 'h':
175 help();
176 return 0;
177
178 case ARG_VERSION:
179 puts(PACKAGE_STRING);
180 puts(DISTRIBUTION);
181 puts(SYSTEMD_FEATURES);
182 return 0;
183
184 case ARG_NO_PAGER:
185 arg_no_pager = true;
186 break;
187
188 case 'f':
189 arg_follow = true;
585314e8 190 signal(SIGWINCH, columns_cache_reset);
0d43c694
LP
191 break;
192
193 case 'o':
1705594f 194 arg_output = output_mode_from_string(optarg);
df50185b 195 if (arg_output < 0) {
edfb521a 196 log_error("Unknown output format '%s'.", optarg);
0d43c694
LP
197 return -EINVAL;
198 }
df50185b 199
edfb521a
ZJS
200 if (arg_output == OUTPUT_EXPORT ||
201 arg_output == OUTPUT_JSON ||
202 arg_output == OUTPUT_JSON_PRETTY ||
203 arg_output == OUTPUT_JSON_SSE ||
204 arg_output == OUTPUT_CAT)
205 arg_quiet = true;
206
0d43c694
LP
207 break;
208
209 case 'a':
210 arg_show_all = true;
211 break;
212
2100675e 213 case 'n':
1705594f 214 if (optarg) {
cfbc22ab
LP
215 r = safe_atou(optarg, &arg_lines);
216 if (r < 0 || arg_lines <= 0) {
1705594f
LP
217 log_error("Failed to parse lines '%s'", optarg);
218 return -EINVAL;
219 }
220 } else
221 arg_lines = 10;
222
2100675e
LP
223 break;
224
e91af489
LP
225 case ARG_NO_TAIL:
226 arg_no_tail = true;
227 break;
228
39f7f5c1 229 case ARG_NEW_ID128:
7560fffc 230 arg_action = ACTION_NEW_ID128;
55ee336c
LP
231 break;
232
43673799
LP
233 case 'q':
234 arg_quiet = true;
490e567d 235 break;
43673799 236
9e8a535f
LP
237 case 'm':
238 arg_merge = true;
2bd3c38a
LP
239 break;
240
59cea26a
LP
241 case 'b':
242 arg_this_boot = true;
243 break;
244
a963990f
LP
245 case 'D':
246 arg_directory = optarg;
247 break;
248
8f14c832
LP
249 case 'c':
250 arg_cursor = optarg;
251 break;
252
dca6219e 253 case ARG_HEADER:
7560fffc
LP
254 arg_action = ACTION_PRINT_HEADER;
255 break;
256
feb12d3e
LP
257 case ARG_VERIFY:
258 arg_action = ACTION_VERIFY;
259 break;
260
a1a03e30
LP
261 case ARG_DISK_USAGE:
262 arg_action = ACTION_DISK_USAGE;
263 break;
264
feb12d3e 265#ifdef HAVE_GCRYPT
7560fffc
LP
266 case ARG_SETUP_KEYS:
267 arg_action = ACTION_SETUP_KEYS;
dca6219e
LP
268 break;
269
beec0085 270
baed47c3 271 case ARG_VERIFY_KEY:
4da416aa 272 arg_action = ACTION_VERIFY;
baed47c3 273 arg_verify_key = optarg;
9e8a535f 274 arg_merge = false;
4da416aa
LP
275 break;
276
baed47c3
LP
277 case ARG_INTERVAL:
278 r = parse_usec(optarg, &arg_interval);
279 if (r < 0 || arg_interval <= 0) {
280 log_error("Failed to parse sealing key change interval: %s", optarg);
14d10188
LP
281 return -EINVAL;
282 }
283 break;
feb12d3e
LP
284#else
285 case ARG_SETUP_KEYS:
286 case ARG_VERIFY_KEY:
287 case ARG_INTERVAL:
288 log_error("Forward-secure sealing not available.");
289 return -ENOTSUP;
290#endif
14d10188 291
941e990d
LP
292 case 'p': {
293 const char *dots;
294
295 dots = strstr(optarg, "..");
296 if (dots) {
297 char *a;
298 int from, to, i;
299
300 /* a range */
301 a = strndup(optarg, dots - optarg);
302 if (!a)
303 return log_oom();
304
305 from = log_level_from_string(a);
306 to = log_level_from_string(dots + 2);
307 free(a);
308
309 if (from < 0 || to < 0) {
310 log_error("Failed to parse log level range %s", optarg);
311 return -EINVAL;
312 }
313
314 arg_priorities = 0;
315
316 if (from < to) {
317 for (i = from; i <= to; i++)
318 arg_priorities |= 1 << i;
319 } else {
320 for (i = to; i <= from; i++)
321 arg_priorities |= 1 << i;
322 }
323
324 } else {
325 int p, i;
326
327 p = log_level_from_string(optarg);
328 if (p < 0) {
329 log_error("Unknown log level %s", optarg);
330 return -EINVAL;
331 }
332
333 arg_priorities = 0;
334
335 for (i = 0; i <= p; i++)
336 arg_priorities |= 1 << i;
337 }
338
339 break;
340 }
341
cfbc22ab
LP
342 case ARG_SINCE:
343 r = parse_timestamp(optarg, &arg_since);
344 if (r < 0) {
345 log_error("Failed to parse timestamp: %s", optarg);
346 return -EINVAL;
347 }
348 arg_since_set = true;
349 break;
350
351 case ARG_UNTIL:
352 r = parse_timestamp(optarg, &arg_until);
353 if (r < 0) {
354 log_error("Failed to parse timestamp: %s", optarg);
355 return -EINVAL;
356 }
357 arg_until_set = true;
358 break;
359
0d43c694
LP
360 case '?':
361 return -EINVAL;
362
363 default:
364 log_error("Unknown option code %c", c);
365 return -EINVAL;
366 }
367 }
368
cfbc22ab 369 if (arg_follow && !arg_no_tail && arg_lines <= 0)
e91af489
LP
370 arg_lines = 10;
371
cfbc22ab
LP
372 if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) {
373 log_error("--since= must be before --until=.");
374 return -EINVAL;
375 }
376
377 if (arg_cursor && arg_since_set) {
378 log_error("Please specify either --since= or --cursor=, not both.");
379 return -EINVAL;
380 }
381
0d43c694
LP
382 return 1;
383}
384
49826187
LP
385static bool on_tty(void) {
386 static int t = -1;
387
388 /* Note that this is invoked relatively early, before we start
389 * the pager. That means the value we return reflects whether
390 * we originally were started on a tty, not if we currently
391 * are. But this is intended, since we want colour and so on
392 * when run in our own pager. */
393
394 if (_unlikely_(t < 0))
395 t = isatty(STDOUT_FILENO) > 0;
396
397 return t;
398}
399
39f7f5c1 400static int generate_new_id128(void) {
55ee336c
LP
401 sd_id128_t id;
402 int r;
403 unsigned i;
404
405 r = sd_id128_randomize(&id);
406 if (r < 0) {
407 log_error("Failed to generate ID: %s", strerror(-r));
408 return r;
409 }
410
411 printf("As string:\n"
412 SD_ID128_FORMAT_STR "\n\n"
413 "As UUID:\n"
414 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
415 "As macro:\n"
416 "#define MESSAGE_XYZ SD_ID128_MAKE(",
417 SD_ID128_FORMAT_VAL(id),
418 SD_ID128_FORMAT_VAL(id));
419
420 for (i = 0; i < 16; i++)
421 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
422
423 fputs(")\n", stdout);
424
425 return 0;
426}
427
a963990f
LP
428static int add_matches(sd_journal *j, char **args) {
429 char **i;
430 int r;
59cea26a 431
a963990f 432 assert(j);
59cea26a 433
a963990f 434 STRV_FOREACH(i, args) {
59cea26a 435
cbdca852
LP
436 if (streq(*i, "+"))
437 r = sd_journal_add_disjunction(j);
438 else if (path_is_absolute(*i)) {
b6a34514 439 char *p, *t = NULL;
e5124088 440 const char *path;
a963990f 441 struct stat st;
e5124088 442
a963990f
LP
443 p = canonicalize_file_name(*i);
444 path = p ? p : *i;
e5124088
LP
445
446 if (stat(path, &st) < 0) {
447 free(p);
448 log_error("Couldn't stat file: %m");
a963990f 449 return -errno;
e5124088
LP
450 }
451
b6a34514 452 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
e5124088 453 t = strappend("_EXE=", path);
b6a34514
LP
454 else if (S_ISCHR(st.st_mode))
455 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
456 else if (S_ISBLK(st.st_mode))
457 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
458 else {
e5124088 459 free(p);
b6a34514 460 log_error("File is not a device node, regular file or is not executable: %s", *i);
a963990f 461 return -EINVAL;
50940700 462 }
e5124088
LP
463
464 free(p);
b6a34514
LP
465
466 if (!t)
467 return log_oom();
468
469 r = sd_journal_add_match(j, t, 0);
470 free(t);
e5124088 471 } else
cbdca852 472 r = sd_journal_add_match(j, *i, 0);
e5124088 473
de7b95cd 474 if (r < 0) {
cbdca852 475 log_error("Failed to add match '%s': %s", *i, strerror(-r));
a963990f 476 return r;
de7b95cd
LP
477 }
478 }
479
a963990f
LP
480 return 0;
481}
482
483static int add_this_boot(sd_journal *j) {
484 char match[9+32+1] = "_BOOT_ID=";
485 sd_id128_t boot_id;
486 int r;
487
941e990d
LP
488 assert(j);
489
a963990f
LP
490 if (!arg_this_boot)
491 return 0;
492
493 r = sd_id128_get_boot(&boot_id);
494 if (r < 0) {
495 log_error("Failed to get boot id: %s", strerror(-r));
496 return r;
497 }
498
499 sd_id128_to_string(boot_id, match + 9);
500 r = sd_journal_add_match(j, match, strlen(match));
501 if (r < 0) {
502 log_error("Failed to add match: %s", strerror(-r));
503 return r;
504 }
505
506 return 0;
507}
508
941e990d
LP
509static int add_priorities(sd_journal *j) {
510 char match[] = "PRIORITY=0";
511 int i, r;
512
513 assert(j);
514
515 if (arg_priorities == 0xFF)
516 return 0;
517
518 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
519 if (arg_priorities & (1 << i)) {
520 match[sizeof(match)-2] = '0' + i;
521
522 log_info("adding match %s", match);
523
524 r = sd_journal_add_match(j, match, strlen(match));
525 if (r < 0) {
526 log_error("Failed to add match: %s", strerror(-r));
527 return r;
528 }
529 }
530
531 return 0;
532}
533
7560fffc
LP
534static int setup_keys(void) {
535#ifdef HAVE_GCRYPT
536 size_t mpk_size, seed_size, state_size, i;
537 uint8_t *mpk, *seed, *state;
538 ssize_t l;
f982e6f7 539 int fd = -1, r, attr = 0;
7560fffc
LP
540 sd_id128_t machine, boot;
541 char *p = NULL, *k = NULL;
baed47c3 542 struct FSSHeader h;
14d10188 543 uint64_t n;
7560fffc
LP
544
545 r = sd_id128_get_machine(&machine);
546 if (r < 0) {
547 log_error("Failed to get machine ID: %s", strerror(-r));
548 return r;
549 }
550
551 r = sd_id128_get_boot(&boot);
552 if (r < 0) {
553 log_error("Failed to get boot ID: %s", strerror(-r));
554 return r;
555 }
556
baed47c3 557 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
7560fffc
LP
558 SD_ID128_FORMAT_VAL(machine)) < 0)
559 return log_oom();
560
561 if (access(p, F_OK) >= 0) {
f7fab8a5 562 log_error("Sealing key file %s exists already.", p);
7560fffc
LP
563 r = -EEXIST;
564 goto finish;
565 }
566
baed47c3 567 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
7560fffc
LP
568 SD_ID128_FORMAT_VAL(machine)) < 0) {
569 r = log_oom();
570 goto finish;
571 }
572
573 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
574 mpk = alloca(mpk_size);
575
576 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
577 seed = alloca(seed_size);
578
579 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
580 state = alloca(state_size);
581
582 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
583 if (fd < 0) {
584 log_error("Failed to open /dev/random: %m");
585 r = -errno;
586 goto finish;
587 }
588
589 log_info("Generating seed...");
590 l = loop_read(fd, seed, seed_size, true);
591 if (l < 0 || (size_t) l != seed_size) {
592 log_error("Failed to read random seed: %s", strerror(EIO));
593 r = -EIO;
594 goto finish;
595 }
596
597 log_info("Generating key pair...");
598 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
599
baed47c3 600 log_info("Generating sealing key...");
7560fffc
LP
601 FSPRG_GenState0(state, mpk, seed, seed_size);
602
baed47c3
LP
603 assert(arg_interval > 0);
604
7560fffc 605 n = now(CLOCK_REALTIME);
baed47c3 606 n /= arg_interval;
7560fffc
LP
607
608 close_nointr_nofail(fd);
609 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
610 if (fd < 0) {
611 log_error("Failed to open %s: %m", k);
612 r = -errno;
613 goto finish;
614 }
615
f982e6f7
LP
616 /* Enable secure remove, exclusion from dump, synchronous
617 * writing and in-place updating */
618 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
619 log_warning("FS_IOC_GETFLAGS failed: %m");
620
621 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
622
623 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
624 log_warning("FS_IOC_SETFLAGS failed: %m");
625
7560fffc
LP
626 zero(h);
627 memcpy(h.signature, "KSHHRHLP", 8);
628 h.machine_id = machine;
629 h.boot_id = boot;
630 h.header_size = htole64(sizeof(h));
baed47c3
LP
631 h.start_usec = htole64(n * arg_interval);
632 h.interval_usec = htole64(arg_interval);
633 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
634 h.fsprg_state_size = htole64(state_size);
7560fffc
LP
635
636 l = loop_write(fd, &h, sizeof(h), false);
637 if (l < 0 || (size_t) l != sizeof(h)) {
638 log_error("Failed to write header: %s", strerror(EIO));
639 r = -EIO;
640 goto finish;
641 }
642
643 l = loop_write(fd, state, state_size, false);
644 if (l < 0 || (size_t) l != state_size) {
645 log_error("Failed to write state: %s", strerror(EIO));
646 r = -EIO;
647 goto finish;
648 }
649
650 if (link(k, p) < 0) {
651 log_error("Failed to link file: %m");
652 r = -errno;
653 goto finish;
654 }
655
656 if (isatty(STDOUT_FILENO)) {
657 fprintf(stderr,
658 "\n"
baed47c3 659 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
c05276f2
LP
660 "the following local file. This key file is automatically updated when the\n"
661 "sealing key is advanced. It should not be used on multiple hosts.\n"
7560fffc
LP
662 "\n"
663 "\t%s\n"
664 "\n"
baed47c3
LP
665 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
666 "at a safe location and should not be saved locally on disk.\n"
7560fffc
LP
667 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
668 fflush(stderr);
669 }
670 for (i = 0; i < seed_size; i++) {
671 if (i > 0 && i % 3 == 0)
672 putchar('-');
673 printf("%02x", ((uint8_t*) seed)[i]);
674 }
675
baed47c3
LP
676 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
677
678 if (isatty(STDOUT_FILENO)) {
f6a971bc 679 char tsb[FORMAT_TIMESPAN_MAX], *hn;
7560fffc 680
baed47c3
LP
681 fprintf(stderr,
682 ANSI_HIGHLIGHT_OFF "\n"
683 "The sealing key is automatically changed every %s.\n",
684 format_timespan(tsb, sizeof(tsb), arg_interval));
f6a971bc
LP
685
686 hn = gethostname_malloc();
687
688 if (hn) {
689 hostname_cleanup(hn);
adac1c93 690 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
f6a971bc 691 } else
adac1c93 692 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
f6a971bc
LP
693
694#ifdef HAVE_QRENCODE
cf5a3432
LP
695 /* If this is not an UTF-8 system don't print any QR codes */
696 setlocale(LC_CTYPE, "");
697
698 if (streq_ptr(nl_langinfo(CODESET), "UTF-8")) {
699 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
700 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
701 }
f6a971bc
LP
702#endif
703 free(hn);
baed47c3 704 }
7560fffc
LP
705
706 r = 0;
707
708finish:
709 if (fd >= 0)
710 close_nointr_nofail(fd);
711
712 if (k) {
713 unlink(k);
714 free(k);
715 }
716
717 free(p);
718
719 return r;
720#else
feb12d3e
LP
721 log_error("Forward-secure sealing not available.");
722 return -ENOTSUP;
7560fffc
LP
723#endif
724}
725
beec0085
LP
726static int verify(sd_journal *j) {
727 int r = 0;
728 Iterator i;
729 JournalFile *f;
730
731 assert(j);
732
cedb42bb
LP
733 log_show_color(true);
734
beec0085
LP
735 HASHMAP_FOREACH(f, j->files, i) {
736 int k;
2a7b539a 737 usec_t first, validated, last;
beec0085 738
56e81f7c 739#ifdef HAVE_GCRYPT
feb12d3e 740 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
cedb42bb 741 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
56e81f7c 742#endif
4da416aa 743
2a7b539a 744 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
56e81f7c 745 if (k == -EINVAL) {
baed47c3 746 /* If the key was invalid give up right-away. */
56e81f7c
LP
747 return k;
748 } else if (k < 0) {
beec0085 749 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
56e81f7c 750 r = k;
6c7be122
LP
751 } else {
752 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
beec0085 753 log_info("PASS: %s", f->path);
6c7be122 754
c0ca7aee 755 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
2a7b539a 756 if (validated > 0) {
c0ca7aee 757 log_info("=> Validated from %s to %s, final %s entries not sealed.",
2a7b539a
LP
758 format_timestamp(a, sizeof(a), first),
759 format_timestamp(b, sizeof(b), validated),
760 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
761 } else if (last > 0)
c0ca7aee 762 log_info("=> No sealing yet, %s of entries not sealed.",
2a7b539a 763 format_timespan(c, sizeof(c), last - first));
c0ca7aee
LP
764 else
765 log_info("=> No sealing yet, no entries in file.");
766 }
6c7be122 767 }
beec0085
LP
768 }
769
770 return r;
771}
772
a963990f
LP
773int main(int argc, char *argv[]) {
774 int r;
775 sd_journal *j = NULL;
a963990f 776 bool need_seek = false;
14a65d65
LP
777 sd_id128_t previous_boot_id;
778 bool previous_boot_id_valid = false;
92a1fd9e 779 bool have_pager;
cfbc22ab 780 unsigned n_shown = 0;
a963990f
LP
781
782 log_parse_environment();
783 log_open();
784
785 r = parse_argv(argc, argv);
786 if (r <= 0)
787 goto finish;
788
7560fffc 789 if (arg_action == ACTION_NEW_ID128) {
a963990f
LP
790 r = generate_new_id128();
791 goto finish;
792 }
793
7560fffc
LP
794 if (arg_action == ACTION_SETUP_KEYS) {
795 r = setup_keys();
796 goto finish;
797 }
798
a963990f
LP
799 if (arg_directory)
800 r = sd_journal_open_directory(&j, arg_directory, 0);
801 else
9e8a535f 802 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
a963990f
LP
803
804 if (r < 0) {
805 log_error("Failed to open journal: %s", strerror(-r));
806 goto finish;
807 }
808
beec0085
LP
809 if (arg_action == ACTION_VERIFY) {
810 r = verify(j);
811 goto finish;
812 }
813
7560fffc 814 if (arg_action == ACTION_PRINT_HEADER) {
dca6219e
LP
815 journal_print_header(j);
816 r = 0;
817 goto finish;
818 }
819
a1a03e30
LP
820 if (arg_action == ACTION_DISK_USAGE) {
821 uint64_t bytes;
822 char sbytes[FORMAT_BYTES_MAX];
823
824 r = sd_journal_get_usage(j, &bytes);
825 if (r < 0)
826 goto finish;
827
828 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
829 r = 0;
830 goto finish;
831 }
832
beec0085 833#ifdef HAVE_ACL
99add6fd
LP
834 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
835 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
836 r = -EACCES;
837 goto finish;
838 }
839
beec0085 840 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
99add6fd
LP
841 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
842#else
843 if (geteuid() != 0 && in_group("adm") <= 0) {
844 log_error("No access to messages. Only users in the group 'adm' can see messages.");
845 r = -EACCES;
846 goto finish;
847 }
beec0085
LP
848#endif
849
a963990f
LP
850 r = add_this_boot(j);
851 if (r < 0)
852 goto finish;
853
854 r = add_matches(j, argv + optind);
855 if (r < 0)
856 goto finish;
857
941e990d
LP
858 r = add_priorities(j);
859 if (r < 0)
860 goto finish;
861
cfbc22ab
LP
862 if (arg_cursor) {
863 r = sd_journal_seek_cursor(j, arg_cursor);
08984293 864 if (r < 0) {
cfbc22ab 865 log_error("Failed to seek to cursor: %s", strerror(-r));
08984293
LP
866 goto finish;
867 }
868
cfbc22ab 869 r = sd_journal_next(j);
08984293 870
cfbc22ab
LP
871 } else if (arg_since_set) {
872 r = sd_journal_seek_realtime_usec(j, arg_since);
8f14c832 873 if (r < 0) {
cfbc22ab 874 log_error("Failed to seek to date: %s", strerror(-r));
8f14c832
LP
875 goto finish;
876 }
8f14c832
LP
877 r = sd_journal_next(j);
878
cfbc22ab 879 } else if (arg_lines > 0) {
2100675e
LP
880 r = sd_journal_seek_tail(j);
881 if (r < 0) {
882 log_error("Failed to seek to tail: %s", strerror(-r));
883 goto finish;
884 }
885
886 r = sd_journal_previous_skip(j, arg_lines);
8f14c832 887
2100675e
LP
888 } else {
889 r = sd_journal_seek_head(j);
890 if (r < 0) {
891 log_error("Failed to seek to head: %s", strerror(-r));
892 goto finish;
893 }
6f003b43
LP
894
895 r = sd_journal_next(j);
896 }
897
898 if (r < 0) {
899 log_error("Failed to iterate through journal: %s", strerror(-r));
900 goto finish;
50f20cfd 901 }
87d2c1ff 902
49826187 903 on_tty();
fafb6ecc 904 have_pager = !arg_no_pager && !arg_follow && pager_open();
0d43c694 905
cfbc22ab
LP
906 if (!arg_quiet) {
907 usec_t start, end;
908 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
909
910 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
911 if (r < 0) {
912 log_error("Failed to get cutoff: %s", strerror(-r));
913 goto finish;
914 }
915
916 if (r > 0) {
917 if (arg_follow)
9048b11f
LP
918 printf("-- Logs begin at %s. --\n",
919 format_timestamp(start_buf, sizeof(start_buf), start));
cfbc22ab 920 else
9048b11f 921 printf("-- Logs begin at %s, end at %s. --\n",
cfbc22ab
LP
922 format_timestamp(start_buf, sizeof(start_buf), start),
923 format_timestamp(end_buf, sizeof(end_buf), end));
924 }
925 }
926
50f20cfd 927 for (;;) {
c3eba2ab 928 while (arg_lines == 0 || arg_follow || n_shown < arg_lines) {
cfbc22ab
LP
929 int flags;
930
6f003b43
LP
931 if (need_seek) {
932 r = sd_journal_next(j);
933 if (r < 0) {
934 log_error("Failed to iterate through journal: %s", strerror(-r));
935 goto finish;
936 }
0d43c694
LP
937 }
938
939 if (r == 0)
940 break;
941
cfbc22ab
LP
942 if (arg_until_set) {
943 usec_t usec;
944
945 r = sd_journal_get_realtime_usec(j, &usec);
946 if (r < 0) {
947 log_error("Failed to determine timestamp: %s", strerror(-r));
948 goto finish;
949 }
950 }
951
cd931c0a
LP
952 if (!arg_merge) {
953 sd_id128_t boot_id;
14a65d65 954
cd931c0a
LP
955 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
956 if (r >= 0) {
957 if (previous_boot_id_valid &&
958 !sd_id128_equal(boot_id, previous_boot_id))
9048b11f 959 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
cd931c0a
LP
960
961 previous_boot_id = boot_id;
962 previous_boot_id_valid = true;
963 }
14a65d65
LP
964 }
965
cfbc22ab
LP
966 flags =
967 arg_show_all * OUTPUT_SHOW_ALL |
968 have_pager * OUTPUT_FULL_WIDTH |
969 on_tty() * OUTPUT_COLOR;
970
08ace05b 971 r = output_journal(stdout, j, arg_output, 0, flags);
72f59706
LP
972 if (r < 0)
973 goto finish;
6f003b43
LP
974
975 need_seek = true;
cfbc22ab 976 n_shown++;
87d2c1ff
LP
977 }
978
50f20cfd
LP
979 if (!arg_follow)
980 break;
981
e02d1cf7 982 r = sd_journal_wait(j, (uint64_t) -1);
50f20cfd 983 if (r < 0) {
7a69007a 984 log_error("Couldn't wait for journal event: %s", strerror(-r));
50f20cfd
LP
985 goto finish;
986 }
de190aef 987 }
87d2c1ff
LP
988
989finish:
3fbf9cbb
LP
990 if (j)
991 sd_journal_close(j);
87d2c1ff 992
0d43c694
LP
993 pager_close();
994
3fbf9cbb 995 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
87d2c1ff 996}