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