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