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