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