]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journalctl.c
journalctl: replace --local by --merge, i.e. don't interleave remote journals by...
[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;
9e8a535f 61static bool arg_merge = 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"
a6e87e90 90 " verbose, export, json, json-pretty, cat)\n"
baed47c3 91 " -q --quiet Don't show privilege warning\n"
9e8a535f 92 " -m --merge Show entries from all available journals\n"
baed47c3
LP
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' },
9e8a535f 135 { "merge", no_argument, NULL, 'm' },
baed47c3
LP
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
9e8a535f 152 while ((c = getopt_long(argc, argv, "hfo:an:qmbD: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
9e8a535f
LP
207 case 'm':
208 arg_merge = true;
2bd3c38a
LP
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;
9e8a535f 236 arg_merge = false;
4da416aa
LP
237 break;
238
baed47c3
LP
239 case ARG_INTERVAL:
240 r = parse_usec(optarg, &arg_interval);
241 if (r < 0 || arg_interval <= 0) {
242 log_error("Failed to parse sealing key change interval: %s", optarg);
14d10188
LP
243 return -EINVAL;
244 }
245 break;
feb12d3e
LP
246#else
247 case ARG_SETUP_KEYS:
248 case ARG_VERIFY_KEY:
249 case ARG_INTERVAL:
250 log_error("Forward-secure sealing not available.");
251 return -ENOTSUP;
252#endif
14d10188 253
941e990d
LP
254 case 'p': {
255 const char *dots;
256
257 dots = strstr(optarg, "..");
258 if (dots) {
259 char *a;
260 int from, to, i;
261
262 /* a range */
263 a = strndup(optarg, dots - optarg);
264 if (!a)
265 return log_oom();
266
267 from = log_level_from_string(a);
268 to = log_level_from_string(dots + 2);
269 free(a);
270
271 if (from < 0 || to < 0) {
272 log_error("Failed to parse log level range %s", optarg);
273 return -EINVAL;
274 }
275
276 arg_priorities = 0;
277
278 if (from < to) {
279 for (i = from; i <= to; i++)
280 arg_priorities |= 1 << i;
281 } else {
282 for (i = to; i <= from; i++)
283 arg_priorities |= 1 << i;
284 }
285
286 } else {
287 int p, i;
288
289 p = log_level_from_string(optarg);
290 if (p < 0) {
291 log_error("Unknown log level %s", optarg);
292 return -EINVAL;
293 }
294
295 arg_priorities = 0;
296
297 for (i = 0; i <= p; i++)
298 arg_priorities |= 1 << i;
299 }
300
301 break;
302 }
303
0d43c694
LP
304 case '?':
305 return -EINVAL;
306
307 default:
308 log_error("Unknown option code %c", c);
309 return -EINVAL;
310 }
311 }
312
62f21ec9 313 if (arg_follow && !arg_no_tail && arg_lines < 0)
e91af489
LP
314 arg_lines = 10;
315
0d43c694
LP
316 return 1;
317}
318
49826187
LP
319static bool on_tty(void) {
320 static int t = -1;
321
322 /* Note that this is invoked relatively early, before we start
323 * the pager. That means the value we return reflects whether
324 * we originally were started on a tty, not if we currently
325 * are. But this is intended, since we want colour and so on
326 * when run in our own pager. */
327
328 if (_unlikely_(t < 0))
329 t = isatty(STDOUT_FILENO) > 0;
330
331 return t;
332}
333
39f7f5c1 334static int generate_new_id128(void) {
55ee336c
LP
335 sd_id128_t id;
336 int r;
337 unsigned i;
338
339 r = sd_id128_randomize(&id);
340 if (r < 0) {
341 log_error("Failed to generate ID: %s", strerror(-r));
342 return r;
343 }
344
345 printf("As string:\n"
346 SD_ID128_FORMAT_STR "\n\n"
347 "As UUID:\n"
348 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
349 "As macro:\n"
350 "#define MESSAGE_XYZ SD_ID128_MAKE(",
351 SD_ID128_FORMAT_VAL(id),
352 SD_ID128_FORMAT_VAL(id));
353
354 for (i = 0; i < 16; i++)
355 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
356
357 fputs(")\n", stdout);
358
359 return 0;
360}
361
a963990f
LP
362static int add_matches(sd_journal *j, char **args) {
363 char **i;
364 int r;
59cea26a 365
a963990f 366 assert(j);
59cea26a 367
a963990f 368 STRV_FOREACH(i, args) {
59cea26a 369
cbdca852
LP
370 if (streq(*i, "+"))
371 r = sd_journal_add_disjunction(j);
372 else if (path_is_absolute(*i)) {
b6a34514 373 char *p, *t = NULL;
e5124088 374 const char *path;
a963990f 375 struct stat st;
e5124088 376
a963990f
LP
377 p = canonicalize_file_name(*i);
378 path = p ? p : *i;
e5124088
LP
379
380 if (stat(path, &st) < 0) {
381 free(p);
382 log_error("Couldn't stat file: %m");
a963990f 383 return -errno;
e5124088
LP
384 }
385
b6a34514 386 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
e5124088 387 t = strappend("_EXE=", path);
b6a34514
LP
388 else if (S_ISCHR(st.st_mode))
389 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
390 else if (S_ISBLK(st.st_mode))
391 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
392 else {
e5124088 393 free(p);
b6a34514 394 log_error("File is not a device node, regular file or is not executable: %s", *i);
a963990f 395 return -EINVAL;
50940700 396 }
e5124088
LP
397
398 free(p);
b6a34514
LP
399
400 if (!t)
401 return log_oom();
402
403 r = sd_journal_add_match(j, t, 0);
404 free(t);
e5124088 405 } else
cbdca852 406 r = sd_journal_add_match(j, *i, 0);
e5124088 407
de7b95cd 408 if (r < 0) {
cbdca852 409 log_error("Failed to add match '%s': %s", *i, strerror(-r));
a963990f 410 return r;
de7b95cd
LP
411 }
412 }
413
a963990f
LP
414 return 0;
415}
416
417static int add_this_boot(sd_journal *j) {
418 char match[9+32+1] = "_BOOT_ID=";
419 sd_id128_t boot_id;
420 int r;
421
941e990d
LP
422 assert(j);
423
a963990f
LP
424 if (!arg_this_boot)
425 return 0;
426
427 r = sd_id128_get_boot(&boot_id);
428 if (r < 0) {
429 log_error("Failed to get boot id: %s", strerror(-r));
430 return r;
431 }
432
433 sd_id128_to_string(boot_id, match + 9);
434 r = sd_journal_add_match(j, match, strlen(match));
435 if (r < 0) {
436 log_error("Failed to add match: %s", strerror(-r));
437 return r;
438 }
439
440 return 0;
441}
442
941e990d
LP
443static int add_priorities(sd_journal *j) {
444 char match[] = "PRIORITY=0";
445 int i, r;
446
447 assert(j);
448
449 if (arg_priorities == 0xFF)
450 return 0;
451
452 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
453 if (arg_priorities & (1 << i)) {
454 match[sizeof(match)-2] = '0' + i;
455
456 log_info("adding match %s", match);
457
458 r = sd_journal_add_match(j, match, strlen(match));
459 if (r < 0) {
460 log_error("Failed to add match: %s", strerror(-r));
461 return r;
462 }
463 }
464
465 return 0;
466}
467
7560fffc
LP
468static int setup_keys(void) {
469#ifdef HAVE_GCRYPT
470 size_t mpk_size, seed_size, state_size, i;
471 uint8_t *mpk, *seed, *state;
472 ssize_t l;
f982e6f7 473 int fd = -1, r, attr = 0;
7560fffc
LP
474 sd_id128_t machine, boot;
475 char *p = NULL, *k = NULL;
baed47c3 476 struct FSSHeader h;
14d10188 477 uint64_t n;
7560fffc
LP
478
479 r = sd_id128_get_machine(&machine);
480 if (r < 0) {
481 log_error("Failed to get machine ID: %s", strerror(-r));
482 return r;
483 }
484
485 r = sd_id128_get_boot(&boot);
486 if (r < 0) {
487 log_error("Failed to get boot ID: %s", strerror(-r));
488 return r;
489 }
490
baed47c3 491 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
7560fffc
LP
492 SD_ID128_FORMAT_VAL(machine)) < 0)
493 return log_oom();
494
495 if (access(p, F_OK) >= 0) {
f7fab8a5 496 log_error("Sealing key file %s exists already.", p);
7560fffc
LP
497 r = -EEXIST;
498 goto finish;
499 }
500
baed47c3 501 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
7560fffc
LP
502 SD_ID128_FORMAT_VAL(machine)) < 0) {
503 r = log_oom();
504 goto finish;
505 }
506
507 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
508 mpk = alloca(mpk_size);
509
510 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
511 seed = alloca(seed_size);
512
513 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
514 state = alloca(state_size);
515
516 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
517 if (fd < 0) {
518 log_error("Failed to open /dev/random: %m");
519 r = -errno;
520 goto finish;
521 }
522
523 log_info("Generating seed...");
524 l = loop_read(fd, seed, seed_size, true);
525 if (l < 0 || (size_t) l != seed_size) {
526 log_error("Failed to read random seed: %s", strerror(EIO));
527 r = -EIO;
528 goto finish;
529 }
530
531 log_info("Generating key pair...");
532 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
533
baed47c3 534 log_info("Generating sealing key...");
7560fffc
LP
535 FSPRG_GenState0(state, mpk, seed, seed_size);
536
baed47c3
LP
537 assert(arg_interval > 0);
538
7560fffc 539 n = now(CLOCK_REALTIME);
baed47c3 540 n /= arg_interval;
7560fffc
LP
541
542 close_nointr_nofail(fd);
543 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
544 if (fd < 0) {
545 log_error("Failed to open %s: %m", k);
546 r = -errno;
547 goto finish;
548 }
549
f982e6f7
LP
550 /* Enable secure remove, exclusion from dump, synchronous
551 * writing and in-place updating */
552 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
553 log_warning("FS_IOC_GETFLAGS failed: %m");
554
555 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
556
557 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
558 log_warning("FS_IOC_SETFLAGS failed: %m");
559
7560fffc
LP
560 zero(h);
561 memcpy(h.signature, "KSHHRHLP", 8);
562 h.machine_id = machine;
563 h.boot_id = boot;
564 h.header_size = htole64(sizeof(h));
baed47c3
LP
565 h.start_usec = htole64(n * arg_interval);
566 h.interval_usec = htole64(arg_interval);
567 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
568 h.fsprg_state_size = htole64(state_size);
7560fffc
LP
569
570 l = loop_write(fd, &h, sizeof(h), false);
571 if (l < 0 || (size_t) l != sizeof(h)) {
572 log_error("Failed to write header: %s", strerror(EIO));
573 r = -EIO;
574 goto finish;
575 }
576
577 l = loop_write(fd, state, state_size, false);
578 if (l < 0 || (size_t) l != state_size) {
579 log_error("Failed to write state: %s", strerror(EIO));
580 r = -EIO;
581 goto finish;
582 }
583
584 if (link(k, p) < 0) {
585 log_error("Failed to link file: %m");
586 r = -errno;
587 goto finish;
588 }
589
590 if (isatty(STDOUT_FILENO)) {
591 fprintf(stderr,
592 "\n"
baed47c3 593 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
c05276f2
LP
594 "the following local file. This key file is automatically updated when the\n"
595 "sealing key is advanced. It should not be used on multiple hosts.\n"
7560fffc
LP
596 "\n"
597 "\t%s\n"
598 "\n"
baed47c3
LP
599 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
600 "at a safe location and should not be saved locally on disk.\n"
7560fffc
LP
601 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
602 fflush(stderr);
603 }
604 for (i = 0; i < seed_size; i++) {
605 if (i > 0 && i % 3 == 0)
606 putchar('-');
607 printf("%02x", ((uint8_t*) seed)[i]);
608 }
609
baed47c3
LP
610 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
611
612 if (isatty(STDOUT_FILENO)) {
f6a971bc 613 char tsb[FORMAT_TIMESPAN_MAX], *hn;
7560fffc 614
baed47c3
LP
615 fprintf(stderr,
616 ANSI_HIGHLIGHT_OFF "\n"
617 "The sealing key is automatically changed every %s.\n",
618 format_timespan(tsb, sizeof(tsb), arg_interval));
f6a971bc
LP
619
620 hn = gethostname_malloc();
621
622 if (hn) {
623 hostname_cleanup(hn);
adac1c93 624 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
f6a971bc 625 } else
adac1c93 626 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
f6a971bc
LP
627
628#ifdef HAVE_QRENCODE
adac1c93 629 fprintf(stderr, "\nTo transfer the verification key to your phone please scan the QR code below:\n\n");
f6a971bc
LP
630 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
631#endif
632 free(hn);
baed47c3 633 }
7560fffc
LP
634
635 r = 0;
636
637finish:
638 if (fd >= 0)
639 close_nointr_nofail(fd);
640
641 if (k) {
642 unlink(k);
643 free(k);
644 }
645
646 free(p);
647
648 return r;
649#else
feb12d3e
LP
650 log_error("Forward-secure sealing not available.");
651 return -ENOTSUP;
7560fffc
LP
652#endif
653}
654
beec0085
LP
655static int verify(sd_journal *j) {
656 int r = 0;
657 Iterator i;
658 JournalFile *f;
659
660 assert(j);
661
cedb42bb
LP
662 log_show_color(true);
663
beec0085
LP
664 HASHMAP_FOREACH(f, j->files, i) {
665 int k;
6c7be122 666 usec_t from, to, total;
beec0085 667
56e81f7c 668#ifdef HAVE_GCRYPT
feb12d3e 669 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
cedb42bb 670 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
56e81f7c 671#endif
4da416aa 672
b72631e5 673 k = journal_file_verify(f, arg_verify_key, &from, &to, &total, true);
56e81f7c 674 if (k == -EINVAL) {
baed47c3 675 /* If the key was invalid give up right-away. */
56e81f7c
LP
676 return k;
677 } else if (k < 0) {
beec0085 678 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
56e81f7c 679 r = k;
6c7be122
LP
680 } else {
681 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
beec0085 682 log_info("PASS: %s", f->path);
6c7be122 683
c0ca7aee
LP
684 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
685 if (from > 0) {
686 log_info("=> Validated from %s to %s, final %s entries not sealed.",
687 format_timestamp(a, sizeof(a), from),
688 format_timestamp(b, sizeof(b), to),
689 format_timespan(c, sizeof(c), total > to ? total - to : 0));
690 } else if (total > 0)
691 log_info("=> No sealing yet, %s of entries not sealed.",
692 format_timespan(c, sizeof(c), total));
693 else
694 log_info("=> No sealing yet, no entries in file.");
695 }
6c7be122 696 }
beec0085
LP
697 }
698
699 return r;
700}
701
a963990f
LP
702int main(int argc, char *argv[]) {
703 int r;
704 sd_journal *j = NULL;
705 unsigned line = 0;
706 bool need_seek = false;
14a65d65
LP
707 sd_id128_t previous_boot_id;
708 bool previous_boot_id_valid = false;
92a1fd9e 709 bool have_pager;
a963990f
LP
710
711 log_parse_environment();
712 log_open();
713
714 r = parse_argv(argc, argv);
715 if (r <= 0)
716 goto finish;
717
7560fffc 718 if (arg_action == ACTION_NEW_ID128) {
a963990f
LP
719 r = generate_new_id128();
720 goto finish;
721 }
722
7560fffc
LP
723 if (arg_action == ACTION_SETUP_KEYS) {
724 r = setup_keys();
725 goto finish;
726 }
727
a963990f
LP
728 if (arg_directory)
729 r = sd_journal_open_directory(&j, arg_directory, 0);
730 else
9e8a535f 731 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
a963990f
LP
732
733 if (r < 0) {
734 log_error("Failed to open journal: %s", strerror(-r));
735 goto finish;
736 }
737
beec0085
LP
738 if (arg_action == ACTION_VERIFY) {
739 r = verify(j);
740 goto finish;
741 }
742
7560fffc 743 if (arg_action == ACTION_PRINT_HEADER) {
dca6219e
LP
744 journal_print_header(j);
745 r = 0;
746 goto finish;
747 }
748
beec0085 749#ifdef HAVE_ACL
99add6fd
LP
750 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
751 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
752 r = -EACCES;
753 goto finish;
754 }
755
beec0085 756 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
99add6fd
LP
757 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
758#else
759 if (geteuid() != 0 && in_group("adm") <= 0) {
760 log_error("No access to messages. Only users in the group 'adm' can see messages.");
761 r = -EACCES;
762 goto finish;
763 }
beec0085
LP
764#endif
765
a963990f
LP
766 r = add_this_boot(j);
767 if (r < 0)
768 goto finish;
769
770 r = add_matches(j, argv + optind);
771 if (r < 0)
772 goto finish;
773
941e990d
LP
774 r = add_priorities(j);
775 if (r < 0)
776 goto finish;
777
08984293
LP
778 if (!arg_quiet) {
779 usec_t start, end;
780 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
781
782 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
783 if (r < 0) {
784 log_error("Failed to get cutoff: %s", strerror(-r));
785 goto finish;
786 }
787
788 if (r > 0) {
789 if (arg_follow)
790 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
791 else
792 printf("Logs begin at %s, end at %s.\n",
793 format_timestamp(start_buf, sizeof(start_buf), start),
794 format_timestamp(end_buf, sizeof(end_buf), end));
795 }
796 }
797
2100675e
LP
798 if (arg_lines >= 0) {
799 r = sd_journal_seek_tail(j);
800 if (r < 0) {
801 log_error("Failed to seek to tail: %s", strerror(-r));
802 goto finish;
803 }
804
805 r = sd_journal_previous_skip(j, arg_lines);
2100675e
LP
806 } else {
807 r = sd_journal_seek_head(j);
808 if (r < 0) {
809 log_error("Failed to seek to head: %s", strerror(-r));
810 goto finish;
811 }
6f003b43
LP
812
813 r = sd_journal_next(j);
814 }
815
816 if (r < 0) {
817 log_error("Failed to iterate through journal: %s", strerror(-r));
818 goto finish;
50f20cfd 819 }
87d2c1ff 820
49826187 821 on_tty();
fafb6ecc 822 have_pager = !arg_no_pager && !arg_follow && pager_open();
0d43c694 823
50f20cfd 824 for (;;) {
0d43c694 825 for (;;) {
14a65d65 826 sd_id128_t boot_id;
49826187
LP
827 int flags =
828 arg_show_all * OUTPUT_SHOW_ALL |
829 have_pager * OUTPUT_FULL_WIDTH |
830 on_tty() * OUTPUT_COLOR;
14a65d65 831
6f003b43
LP
832 if (need_seek) {
833 r = sd_journal_next(j);
834 if (r < 0) {
835 log_error("Failed to iterate through journal: %s", strerror(-r));
836 goto finish;
837 }
0d43c694
LP
838 }
839
840 if (r == 0)
841 break;
842
14a65d65
LP
843 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
844 if (r >= 0) {
845 if (previous_boot_id_valid &&
846 !sd_id128_equal(boot_id, previous_boot_id))
847 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
848
849 previous_boot_id = boot_id;
850 previous_boot_id_valid = true;
851 }
852
72f59706 853 line ++;
50f20cfd 854
92a1fd9e 855 r = output_journal(j, arg_output, line, 0, flags);
72f59706
LP
856 if (r < 0)
857 goto finish;
6f003b43
LP
858
859 need_seek = true;
87d2c1ff
LP
860 }
861
50f20cfd
LP
862 if (!arg_follow)
863 break;
864
e02d1cf7 865 r = sd_journal_wait(j, (uint64_t) -1);
50f20cfd 866 if (r < 0) {
e02d1cf7 867 log_error("Couldn't wait for log event: %s", strerror(-r));
50f20cfd
LP
868 goto finish;
869 }
de190aef 870 }
87d2c1ff
LP
871
872finish:
3fbf9cbb
LP
873 if (j)
874 sd_journal_close(j);
87d2c1ff 875
0d43c694
LP
876 pager_close();
877
3fbf9cbb 878 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
87d2c1ff 879}