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