]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journalctl.c
Revert "completion: don't offer completions for journalctl -b"
[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
a9cdc94f 22#include <locale.h>
87d2c1ff
LP
23#include <fcntl.h>
24#include <errno.h>
25#include <stddef.h>
3fbf9cbb
LP
26#include <string.h>
27#include <stdio.h>
28#include <unistd.h>
29#include <stdlib.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>
87d2c1ff 36
4468addc
LP
37#ifdef HAVE_ACL
38#include <sys/acl.h>
478c8269 39#include "acl-util.h"
4468addc
LP
40#endif
41
81527be1
LP
42#include <systemd/sd-journal.h>
43
3fbf9cbb 44#include "log.h"
7199aa96 45#include "logs-show.h"
72f59706 46#include "util.h"
e5124088 47#include "path-util.h"
68fee104 48#include "fileio.h"
0d43c694
LP
49#include "build.h"
50#include "pager.h"
a963990f 51#include "strv.h"
dca6219e 52#include "journal-internal.h"
7560fffc 53#include "journal-def.h"
0284adc6 54#include "journal-verify.h"
4da416aa 55#include "journal-authenticate.h"
f6a971bc 56#include "journal-qrcode.h"
4da416aa 57#include "fsprg.h"
c3f60ec5 58#include "unit-name.h"
d4205751 59#include "catalog.h"
7560fffc 60
baed47c3 61#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
250d54b5 62
df50185b 63static OutputMode arg_output = OUTPUT_SHORT;
1b12a7b5 64static bool arg_pager_end = false;
72f59706 65static bool arg_follow = false;
2b8f6883 66static bool arg_full = true;
cd4b13e0 67static bool arg_all = false;
0d43c694 68static bool arg_no_pager = false;
67e04a48 69static int arg_lines = -1;
e91af489 70static bool arg_no_tail = false;
43673799 71static bool arg_quiet = false;
9e8a535f 72static bool arg_merge = false;
d121b396
ZJS
73static bool arg_boot = false;
74static char *arg_boot_descriptor = NULL;
99271804 75static bool arg_dmesg = false;
8f14c832 76static const char *arg_cursor = NULL;
248fc619
ZJS
77static const char *arg_after_cursor = NULL;
78static bool arg_show_cursor = false;
a963990f 79static const char *arg_directory = NULL;
8d98da3f 80static char **arg_file = NULL;
941e990d 81static int arg_priorities = 0xFF;
baed47c3 82static const char *arg_verify_key = NULL;
feb12d3e 83#ifdef HAVE_GCRYPT
baed47c3 84static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
b8547c10 85static bool arg_force = false;
feb12d3e 86#endif
cfbc22ab
LP
87static usec_t arg_since, arg_until;
88static bool arg_since_set = false, arg_until_set = false;
b9e40524
HH
89static char **arg_system_units = NULL;
90static char **arg_user_units = NULL;
3c1668da 91static const char *arg_field = NULL;
d4205751 92static bool arg_catalog = false;
d89d6c86 93static bool arg_reverse = false;
3f3a438f 94static int arg_journal_type = 0;
13cbf3a5 95static const char *arg_root = NULL;
b6741478 96static const char *arg_machine = NULL;
50f20cfd 97
7560fffc
LP
98static enum {
99 ACTION_SHOW,
100 ACTION_NEW_ID128,
101 ACTION_PRINT_HEADER,
beec0085 102 ACTION_SETUP_KEYS,
a1a03e30
LP
103 ACTION_VERIFY,
104 ACTION_DISK_USAGE,
d4205751 105 ACTION_LIST_CATALOG,
54b7254c 106 ACTION_DUMP_CATALOG,
f1188074
ZJS
107 ACTION_UPDATE_CATALOG,
108 ACTION_LIST_BOOTS,
7560fffc
LP
109} arg_action = ACTION_SHOW;
110
a331b5e6
JJ
111typedef struct boot_id_t {
112 sd_id128_t id;
f1188074
ZJS
113 uint64_t first;
114 uint64_t last;
a331b5e6
JJ
115} boot_id_t;
116
faf5077f
DH
117static void pager_open_if_enabled(void) {
118
119 if (arg_no_pager)
120 return;
121
122 pager_open(arg_pager_end);
123}
124
0d43c694
LP
125static int help(void) {
126
faf5077f
DH
127 pager_open_if_enabled();
128
cd4b13e0 129 printf("%s [OPTIONS...] [MATCHES...]\n\n"
15119c16
LP
130 "Query the journal.\n\n"
131 "Flags:\n"
248fc619 132 " --system Show only the system journal\n"
c736283b 133 " --user Show only the user journal for the current user\n"
b6741478 134 " -M --machine=CONTAINER Operate on local container\n"
c736283b
JSJ
135 " --since=DATE Start showing entries on or newer than the specified date\n"
136 " --until=DATE Stop showing entries on or older than the specified date\n"
137 " -c --cursor=CURSOR Start showing entries from the specified cursor\n"
138 " --after-cursor=CURSOR Start showing entries from after the specified cursor\n"
248fc619 139 " --show-cursor Print the cursor after all the entries\n"
c736283b 140 " -b --boot[=ID] Show data only from ID or, if unspecified, the current boot\n"
f1188074 141 " --list-boots Show terse information about recorded boots\n"
c736283b 142 " -k --dmesg Show kernel message log from the current boot\n"
248fc619
ZJS
143 " -u --unit=UNIT Show data only from the specified unit\n"
144 " --user-unit=UNIT Show data only from the specified user session unit\n"
145 " -p --priority=RANGE Show only messages within the specified priority range\n"
146 " -e --pager-end Immediately jump to end of the journal in the pager\n"
c736283b 147 " -f --follow Follow the journal\n"
248fc619
ZJS
148 " -n --lines[=INTEGER] Number of journal entries to show\n"
149 " --no-tail Show all lines, even in follow mode\n"
150 " -r --reverse Show the newest entries first\n"
f02d8367 151 " -o --output=STRING Change journal output mode (short, short-iso,\n"
c736283b
JSJ
152 " short-precise, short-monotonic, verbose,\n"
153 " export, json, json-pretty, json-sse, cat)\n"
248fc619 154 " -x --catalog Add message explanations where available\n"
2b8f6883 155 " --no-full Ellipsize fields\n"
248fc619 156 " -a --all Show all fields, including long and unprintable\n"
c736283b 157 " -q --quiet Do not show privilege warning\n"
248fc619
ZJS
158 " --no-pager Do not pipe output into a pager\n"
159 " -m --merge Show entries from all available journals\n"
160 " -D --directory=PATH Show journal files from directory\n"
161 " --file=PATH Show journal file\n"
162 " --root=ROOT Operate on catalog files underneath the root ROOT\n"
15119c16 163#ifdef HAVE_GCRYPT
248fc619
ZJS
164 " --interval=TIME Time interval for changing the FSS sealing key\n"
165 " --verify-key=KEY Specify FSS verification key\n"
c736283b 166 " --force Force overriding of the FSS key pair with --setup-keys\n"
15119c16
LP
167#endif
168 "\nCommands:\n"
c736283b 169 " -h --help Show this help text\n"
248fc619 170 " --version Show package version\n"
c736283b 171 " --new-id128 Generate a new 128-bit ID\n"
248fc619 172 " --header Show journal header information\n"
c736283b
JSJ
173 " --disk-usage Show total disk usage of all journal files\n"
174 " -F --field=FIELD List all values that a specified field takes\n"
248fc619
ZJS
175 " --list-catalog Show message IDs of all entries in the message catalog\n"
176 " --dump-catalog Show entries in the message catalog\n"
177 " --update-catalog Update the message catalog database\n"
feb12d3e 178#ifdef HAVE_GCRYPT
c736283b 179 " --setup-keys Generate a new FSS key pair\n"
248fc619 180 " --verify Verify journal file consistency\n"
feb12d3e
LP
181#endif
182 , program_invocation_short_name);
0d43c694
LP
183
184 return 0;
185}
186
187static int parse_argv(int argc, char *argv[]) {
188
189 enum {
190 ARG_VERSION = 0x100,
e91af489 191 ARG_NO_PAGER,
2b8f6883 192 ARG_NO_FULL,
55ee336c 193 ARG_NO_TAIL,
dca6219e 194 ARG_NEW_ID128,
f1188074 195 ARG_LIST_BOOTS,
3f3a438f
ZJS
196 ARG_USER,
197 ARG_SYSTEM,
13cbf3a5 198 ARG_ROOT,
7560fffc 199 ARG_HEADER,
beec0085 200 ARG_SETUP_KEYS,
8d98da3f 201 ARG_FILE,
baed47c3 202 ARG_INTERVAL,
4da416aa 203 ARG_VERIFY,
a1a03e30 204 ARG_VERIFY_KEY,
cfbc22ab
LP
205 ARG_DISK_USAGE,
206 ARG_SINCE,
d4205751 207 ARG_UNTIL,
248fc619
ZJS
208 ARG_AFTER_CURSOR,
209 ARG_SHOW_CURSOR,
ffa7cd15 210 ARG_USER_UNIT,
d4205751 211 ARG_LIST_CATALOG,
54b7254c 212 ARG_DUMP_CATALOG,
3f3a438f 213 ARG_UPDATE_CATALOG,
b8547c10 214 ARG_FORCE,
0d43c694
LP
215 };
216
217 static const struct option options[] = {
248fc619
ZJS
218 { "help", no_argument, NULL, 'h' },
219 { "version" , no_argument, NULL, ARG_VERSION },
220 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
221 { "pager-end", no_argument, NULL, 'e' },
222 { "follow", no_argument, NULL, 'f' },
223 { "force", no_argument, NULL, ARG_FORCE },
224 { "output", required_argument, NULL, 'o' },
225 { "all", no_argument, NULL, 'a' },
226 { "full", no_argument, NULL, 'l' },
2b8f6883 227 { "no-full", no_argument, NULL, ARG_NO_FULL },
248fc619
ZJS
228 { "lines", optional_argument, NULL, 'n' },
229 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
230 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
231 { "quiet", no_argument, NULL, 'q' },
232 { "merge", no_argument, NULL, 'm' },
233 { "boot", optional_argument, NULL, 'b' },
f1188074 234 { "list-boots", no_argument, NULL, ARG_LIST_BOOTS },
248fc619
ZJS
235 { "this-boot", optional_argument, NULL, 'b' }, /* deprecated */
236 { "dmesg", no_argument, NULL, 'k' },
237 { "system", no_argument, NULL, ARG_SYSTEM },
238 { "user", no_argument, NULL, ARG_USER },
239 { "directory", required_argument, NULL, 'D' },
240 { "file", required_argument, NULL, ARG_FILE },
241 { "root", required_argument, NULL, ARG_ROOT },
242 { "header", no_argument, NULL, ARG_HEADER },
243 { "priority", required_argument, NULL, 'p' },
244 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
245 { "interval", required_argument, NULL, ARG_INTERVAL },
246 { "verify", no_argument, NULL, ARG_VERIFY },
247 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
248 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
249 { "cursor", required_argument, NULL, 'c' },
250 { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
251 { "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR },
252 { "since", required_argument, NULL, ARG_SINCE },
253 { "until", required_argument, NULL, ARG_UNTIL },
254 { "unit", required_argument, NULL, 'u' },
255 { "user-unit", required_argument, NULL, ARG_USER_UNIT },
256 { "field", required_argument, NULL, 'F' },
257 { "catalog", no_argument, NULL, 'x' },
258 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
259 { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
260 { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG },
261 { "reverse", no_argument, NULL, 'r' },
b6741478 262 { "machine", required_argument, NULL, 'M' },
eb9da376 263 {}
0d43c694
LP
264 };
265
2100675e 266 int c, r;
0d43c694
LP
267
268 assert(argc >= 0);
269 assert(argv);
270
b6741478 271 while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:u:F:xrM:", options, NULL)) >= 0) {
0d43c694
LP
272
273 switch (c) {
274
275 case 'h':
eb9da376 276 return help();
0d43c694
LP
277
278 case ARG_VERSION:
279 puts(PACKAGE_STRING);
0d43c694
LP
280 puts(SYSTEMD_FEATURES);
281 return 0;
282
283 case ARG_NO_PAGER:
284 arg_no_pager = true;
285 break;
286
1b12a7b5
HH
287 case 'e':
288 arg_pager_end = true;
fe59e38b
LP
289
290 if (arg_lines < 0)
291 arg_lines = 1000;
292
1b12a7b5
HH
293 break;
294
0d43c694
LP
295 case 'f':
296 arg_follow = true;
297 break;
298
299 case 'o':
1705594f 300 arg_output = output_mode_from_string(optarg);
df50185b 301 if (arg_output < 0) {
edfb521a 302 log_error("Unknown output format '%s'.", optarg);
0d43c694
LP
303 return -EINVAL;
304 }
df50185b 305
edfb521a
ZJS
306 if (arg_output == OUTPUT_EXPORT ||
307 arg_output == OUTPUT_JSON ||
308 arg_output == OUTPUT_JSON_PRETTY ||
309 arg_output == OUTPUT_JSON_SSE ||
310 arg_output == OUTPUT_CAT)
311 arg_quiet = true;
312
0d43c694
LP
313 break;
314
98a6e132 315 case 'l':
e3657ecd
ZJS
316 arg_full = true;
317 break;
318
2b8f6883
ZJS
319 case ARG_NO_FULL:
320 arg_full = false;
321 break;
322
0d43c694 323 case 'a':
cd4b13e0 324 arg_all = true;
0d43c694
LP
325 break;
326
2100675e 327 case 'n':
1705594f 328 if (optarg) {
67e04a48
ZJS
329 r = safe_atoi(optarg, &arg_lines);
330 if (r < 0 || arg_lines < 0) {
1705594f
LP
331 log_error("Failed to parse lines '%s'", optarg);
332 return -EINVAL;
333 }
96088db0
LP
334 } else {
335 int n;
336
337 /* Hmm, no argument? Maybe the next
338 * word on the command line is
339 * supposed to be the argument? Let's
340 * see if there is one, and is
341 * parsable as a positive
342 * integer... */
343
344 if (optind < argc &&
345 safe_atoi(argv[optind], &n) >= 0 &&
346 n >= 0) {
347
348 arg_lines = n;
349 optind++;
350 } else
351 arg_lines = 10;
352 }
1705594f 353
2100675e
LP
354 break;
355
e91af489
LP
356 case ARG_NO_TAIL:
357 arg_no_tail = true;
358 break;
359
39f7f5c1 360 case ARG_NEW_ID128:
7560fffc 361 arg_action = ACTION_NEW_ID128;
55ee336c
LP
362 break;
363
43673799
LP
364 case 'q':
365 arg_quiet = true;
490e567d 366 break;
43673799 367
9e8a535f
LP
368 case 'm':
369 arg_merge = true;
2bd3c38a
LP
370 break;
371
59cea26a 372 case 'b':
d121b396 373 arg_boot = true;
6cebe83c
LP
374
375 if (optarg)
376 arg_boot_descriptor = optarg;
377 else if (optind < argc) {
378 int boot;
379
380 if (argv[optind][0] != '-' ||
381 safe_atoi(argv[optind], &boot) >= 0) {
382 arg_boot_descriptor = argv[optind];
383 optind++;
384 }
385 }
d121b396 386
59cea26a
LP
387 break;
388
f1188074
ZJS
389 case ARG_LIST_BOOTS:
390 arg_action = ACTION_LIST_BOOTS;
391 break;
392
99271804 393 case 'k':
d121b396 394 arg_boot = arg_dmesg = true;
99271804
ZJS
395 break;
396
3f3a438f
ZJS
397 case ARG_SYSTEM:
398 arg_journal_type |= SD_JOURNAL_SYSTEM;
399 break;
400
401 case ARG_USER:
402 arg_journal_type |= SD_JOURNAL_CURRENT_USER;
403 break;
404
b6741478
LP
405 case 'M':
406 arg_machine = optarg;
407 break;
408
a963990f
LP
409 case 'D':
410 arg_directory = optarg;
411 break;
412
8d98da3f
ZJS
413 case ARG_FILE:
414 r = glob_extend(&arg_file, optarg);
415 if (r < 0) {
416 log_error("Failed to add paths: %s", strerror(-r));
417 return r;
418 };
419 break;
420
13cbf3a5
ZJS
421 case ARG_ROOT:
422 arg_root = optarg;
423 break;
424
8f14c832
LP
425 case 'c':
426 arg_cursor = optarg;
427 break;
428
248fc619
ZJS
429 case ARG_AFTER_CURSOR:
430 arg_after_cursor = optarg;
431 break;
432
433 case ARG_SHOW_CURSOR:
434 arg_show_cursor = true;
435 break;
436
dca6219e 437 case ARG_HEADER:
7560fffc
LP
438 arg_action = ACTION_PRINT_HEADER;
439 break;
440
feb12d3e
LP
441 case ARG_VERIFY:
442 arg_action = ACTION_VERIFY;
443 break;
444
a1a03e30
LP
445 case ARG_DISK_USAGE:
446 arg_action = ACTION_DISK_USAGE;
447 break;
448
feb12d3e 449#ifdef HAVE_GCRYPT
b8547c10
SL
450 case ARG_FORCE:
451 arg_force = true;
452 break;
453
7560fffc
LP
454 case ARG_SETUP_KEYS:
455 arg_action = ACTION_SETUP_KEYS;
dca6219e
LP
456 break;
457
beec0085 458
baed47c3 459 case ARG_VERIFY_KEY:
4da416aa 460 arg_action = ACTION_VERIFY;
baed47c3 461 arg_verify_key = optarg;
9e8a535f 462 arg_merge = false;
4da416aa
LP
463 break;
464
baed47c3 465 case ARG_INTERVAL:
7f602784 466 r = parse_sec(optarg, &arg_interval);
baed47c3
LP
467 if (r < 0 || arg_interval <= 0) {
468 log_error("Failed to parse sealing key change interval: %s", optarg);
14d10188
LP
469 return -EINVAL;
470 }
471 break;
feb12d3e
LP
472#else
473 case ARG_SETUP_KEYS:
474 case ARG_VERIFY_KEY:
475 case ARG_INTERVAL:
b8547c10 476 case ARG_FORCE:
feb12d3e
LP
477 log_error("Forward-secure sealing not available.");
478 return -ENOTSUP;
479#endif
14d10188 480
941e990d
LP
481 case 'p': {
482 const char *dots;
483
484 dots = strstr(optarg, "..");
485 if (dots) {
486 char *a;
487 int from, to, i;
488
489 /* a range */
490 a = strndup(optarg, dots - optarg);
491 if (!a)
492 return log_oom();
493
494 from = log_level_from_string(a);
495 to = log_level_from_string(dots + 2);
496 free(a);
497
498 if (from < 0 || to < 0) {
499 log_error("Failed to parse log level range %s", optarg);
500 return -EINVAL;
501 }
502
503 arg_priorities = 0;
504
505 if (from < to) {
506 for (i = from; i <= to; i++)
507 arg_priorities |= 1 << i;
508 } else {
509 for (i = to; i <= from; i++)
510 arg_priorities |= 1 << i;
511 }
512
513 } else {
514 int p, i;
515
516 p = log_level_from_string(optarg);
517 if (p < 0) {
518 log_error("Unknown log level %s", optarg);
519 return -EINVAL;
520 }
521
522 arg_priorities = 0;
523
524 for (i = 0; i <= p; i++)
525 arg_priorities |= 1 << i;
526 }
527
528 break;
529 }
530
cfbc22ab
LP
531 case ARG_SINCE:
532 r = parse_timestamp(optarg, &arg_since);
533 if (r < 0) {
534 log_error("Failed to parse timestamp: %s", optarg);
535 return -EINVAL;
536 }
537 arg_since_set = true;
538 break;
539
540 case ARG_UNTIL:
541 r = parse_timestamp(optarg, &arg_until);
542 if (r < 0) {
543 log_error("Failed to parse timestamp: %s", optarg);
544 return -EINVAL;
545 }
546 arg_until_set = true;
547 break;
548
7199aa96 549 case 'u':
b9e40524
HH
550 r = strv_extend(&arg_system_units, optarg);
551 if (r < 0)
552 return log_oom();
ffa7cd15
DW
553 break;
554
7199aa96 555 case ARG_USER_UNIT:
b9e40524
HH
556 r = strv_extend(&arg_user_units, optarg);
557 if (r < 0)
558 return log_oom();
c3f60ec5
LP
559 break;
560
15119c16
LP
561 case 'F':
562 arg_field = optarg;
563 break;
564
d4205751
LP
565 case 'x':
566 arg_catalog = true;
567 break;
568
569 case ARG_LIST_CATALOG:
570 arg_action = ACTION_LIST_CATALOG;
571 break;
572
54b7254c
ZJS
573 case ARG_DUMP_CATALOG:
574 arg_action = ACTION_DUMP_CATALOG;
575 break;
576
d4205751
LP
577 case ARG_UPDATE_CATALOG:
578 arg_action = ACTION_UPDATE_CATALOG;
579 break;
580
d89d6c86
LN
581 case 'r':
582 arg_reverse = true;
583 break;
584
eb9da376 585 case '?':
0d43c694 586 return -EINVAL;
eb9da376
LP
587
588 default:
589 assert_not_reached("Unhandled option");
0d43c694
LP
590 }
591 }
592
67e04a48 593 if (arg_follow && !arg_no_tail && arg_lines < 0)
e91af489
LP
594 arg_lines = 10;
595
b6741478
LP
596 if (!!arg_directory + !!arg_file + !!arg_machine > 1) {
597 log_error("Please specify either -D/--directory= or --file= or -M/--machine=, not more than one.");
8d98da3f
ZJS
598 return -EINVAL;
599 }
600
3ba09ee8 601 if (arg_since_set && arg_until_set && arg_since > arg_until) {
cfbc22ab
LP
602 log_error("--since= must be before --until=.");
603 return -EINVAL;
604 }
605
248fc619
ZJS
606 if (!!arg_cursor + !!arg_after_cursor + !!arg_since_set > 1) {
607 log_error("Please specify only one of --since=, --cursor=, and --after-cursor.");
cfbc22ab
LP
608 return -EINVAL;
609 }
610
d89d6c86
LN
611 if (arg_follow && arg_reverse) {
612 log_error("Please specify either --reverse= or --follow=, not both.");
613 return -EINVAL;
614 }
615
0d43c694
LP
616 return 1;
617}
618
39f7f5c1 619static int generate_new_id128(void) {
55ee336c
LP
620 sd_id128_t id;
621 int r;
622 unsigned i;
623
624 r = sd_id128_randomize(&id);
625 if (r < 0) {
626 log_error("Failed to generate ID: %s", strerror(-r));
627 return r;
628 }
629
630 printf("As string:\n"
631 SD_ID128_FORMAT_STR "\n\n"
632 "As UUID:\n"
633 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
634 "As macro:\n"
d489071f 635 "#define MESSAGE_XYZ SD_ID128_MAKE(",
55ee336c
LP
636 SD_ID128_FORMAT_VAL(id),
637 SD_ID128_FORMAT_VAL(id));
55ee336c
LP
638 for (i = 0; i < 16; i++)
639 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
d489071f 640 fputs(")\n\n", stdout);
55ee336c 641
d489071f
ZJS
642 printf("As Python constant:\n"
643 ">>> import uuid\n"
644 ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
645 SD_ID128_FORMAT_VAL(id));
55ee336c
LP
646
647 return 0;
648}
649
a963990f
LP
650static int add_matches(sd_journal *j, char **args) {
651 char **i;
59cea26a 652
a963990f 653 assert(j);
59cea26a 654
a963990f 655 STRV_FOREACH(i, args) {
52aeb63c 656 int r;
59cea26a 657
cbdca852
LP
658 if (streq(*i, "+"))
659 r = sd_journal_add_disjunction(j);
660 else if (path_is_absolute(*i)) {
68fee104 661 _cleanup_free_ char *p, *t = NULL, *t2 = NULL;
e5124088 662 const char *path;
68fee104 663 _cleanup_free_ char *interpreter = NULL;
a963990f 664 struct stat st;
e5124088 665
a963990f
LP
666 p = canonicalize_file_name(*i);
667 path = p ? p : *i;
e5124088
LP
668
669 if (stat(path, &st) < 0) {
e5124088 670 log_error("Couldn't stat file: %m");
a963990f 671 return -errno;
e5124088
LP
672 }
673
68fee104
ZJS
674 if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) {
675 if (executable_is_script(path, &interpreter) > 0) {
676 _cleanup_free_ char *comm;
677
2b6bf07d 678 comm = strndup(basename(path), 15);
68fee104
ZJS
679 if (!comm)
680 return log_oom();
681
682 t = strappend("_COMM=", comm);
683
684 /* Append _EXE only if the interpreter is not a link.
685 Otherwise it might be outdated often. */
686 if (lstat(interpreter, &st) == 0 &&
687 !S_ISLNK(st.st_mode)) {
688 t2 = strappend("_EXE=", interpreter);
689 if (!t2)
690 return log_oom();
691 }
692 } else
693 t = strappend("_EXE=", path);
694 } else if (S_ISCHR(st.st_mode))
b6a34514
LP
695 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
696 else if (S_ISBLK(st.st_mode))
697 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
698 else {
fb93cf73 699 log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
a963990f 700 return -EINVAL;
50940700 701 }
e5124088 702
b6a34514
LP
703 if (!t)
704 return log_oom();
705
706 r = sd_journal_add_match(j, t, 0);
68fee104
ZJS
707 if (t2)
708 r = sd_journal_add_match(j, t2, 0);
e5124088 709 } else
cbdca852 710 r = sd_journal_add_match(j, *i, 0);
e5124088 711
de7b95cd 712 if (r < 0) {
cbdca852 713 log_error("Failed to add match '%s': %s", *i, strerror(-r));
a963990f 714 return r;
de7b95cd
LP
715 }
716 }
717
a963990f
LP
718 return 0;
719}
720
a331b5e6
JJ
721static int boot_id_cmp(const void *a, const void *b) {
722 uint64_t _a, _b;
723
f1188074
ZJS
724 _a = ((const boot_id_t *)a)->first;
725 _b = ((const boot_id_t *)b)->first;
a331b5e6
JJ
726
727 return _a < _b ? -1 : (_a > _b ? 1 : 0);
728}
729
f1188074
ZJS
730static int list_boots(sd_journal *j) {
731 int r;
732 const void *data;
733 unsigned int count = 0;
734 int w, i;
735 size_t length, allocated = 0;
736 boot_id_t *id;
737 _cleanup_free_ boot_id_t *all_ids = NULL;
738
739 r = sd_journal_query_unique(j, "_BOOT_ID");
740 if (r < 0)
741 return r;
742
743 SD_JOURNAL_FOREACH_UNIQUE(j, data, length) {
744 if (length < strlen("_BOOT_ID="))
745 continue;
746
747 if (!GREEDY_REALLOC(all_ids, allocated, count + 1))
748 return log_oom();
749
750 id = &all_ids[count];
751
752 r = sd_id128_from_string(((const char *)data) + strlen("_BOOT_ID="), &id->id);
753 if (r < 0)
754 continue;
755
756 r = sd_journal_add_match(j, data, length);
757 if (r < 0)
758 return r;
759
760 r = sd_journal_seek_head(j);
761 if (r < 0)
762 return r;
763
764 r = sd_journal_next(j);
765 if (r < 0)
766 return r;
767 else if (r == 0)
768 goto flush;
769
770 r = sd_journal_get_realtime_usec(j, &id->first);
771 if (r < 0)
772 return r;
773
774 r = sd_journal_seek_tail(j);
775 if (r < 0)
776 return r;
777
778 r = sd_journal_previous(j);
779 if (r < 0)
780 return r;
781 else if (r == 0)
782 goto flush;
783
784 r = sd_journal_get_realtime_usec(j, &id->last);
785 if (r < 0)
786 return r;
787
788 count++;
789 flush:
790 sd_journal_flush_matches(j);
791 }
792
793 qsort_safe(all_ids, count, sizeof(boot_id_t), boot_id_cmp);
794
795 /* numbers are one less, but we need an extra char for the sign */
796 w = DECIMAL_STR_WIDTH(count - 1) + 1;
797
798 for (id = all_ids, i = 0; id < all_ids + count; id++, i++) {
799 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
800
801 printf("% *i " SD_ID128_FORMAT_STR " %s—%s\n",
802 w, i - count + 1,
803 SD_ID128_FORMAT_VAL(id->id),
804 format_timestamp(a, sizeof(a), id->first),
805 format_timestamp(b, sizeof(b), id->last));
806 }
807
808 return 0;
809}
810
a331b5e6
JJ
811static int get_relative_boot_id(sd_journal *j, sd_id128_t *boot_id, int relative) {
812 int r;
813 const void *data;
d121b396 814 unsigned int count = 0;
a331b5e6 815 size_t length, allocated = 0;
d121b396 816 boot_id_t ref_boot_id = {SD_ID128_NULL}, *id;
a331b5e6 817 _cleanup_free_ boot_id_t *all_ids = NULL;
a331b5e6
JJ
818
819 assert(j);
820 assert(boot_id);
821
d121b396 822 if (relative == 0 && !sd_id128_equal(*boot_id, SD_ID128_NULL))
a331b5e6
JJ
823 return 0;
824
a331b5e6
JJ
825 r = sd_journal_query_unique(j, "_BOOT_ID");
826 if (r < 0)
827 return r;
828
829 SD_JOURNAL_FOREACH_UNIQUE(j, data, length) {
830 if (length < strlen("_BOOT_ID="))
831 continue;
832
d121b396 833 if (!GREEDY_REALLOC(all_ids, allocated, count + 1))
a331b5e6
JJ
834 return log_oom();
835
d121b396 836 id = &all_ids[count];
a331b5e6
JJ
837
838 r = sd_id128_from_string(((const char *)data) + strlen("_BOOT_ID="), &id->id);
d121b396 839 if (r < 0)
a331b5e6 840 continue;
a331b5e6 841
a331b5e6
JJ
842 r = sd_journal_add_match(j, data, length);
843 if (r < 0)
d121b396 844 return r;
a331b5e6
JJ
845
846 r = sd_journal_seek_head(j);
847 if (r < 0)
d121b396 848 return r;
a331b5e6
JJ
849
850 r = sd_journal_next(j);
d121b396
ZJS
851 if (r < 0)
852 return r;
853 else if (r == 0)
854 goto flush;
a331b5e6 855
f1188074 856 r = sd_journal_get_realtime_usec(j, &id->first);
a331b5e6 857 if (r < 0)
d121b396 858 return r;
a331b5e6 859
d121b396 860 if (sd_id128_equal(id->id, *boot_id))
a331b5e6 861 ref_boot_id = *id;
a331b5e6 862
d121b396
ZJS
863 count++;
864 flush:
865 sd_journal_flush_matches(j);
a331b5e6
JJ
866 }
867
7ff7394d 868 qsort_safe(all_ids, count, sizeof(boot_id_t), boot_id_cmp);
a331b5e6 869
d121b396
ZJS
870 if (sd_id128_equal(*boot_id, SD_ID128_NULL)) {
871 if (relative > (int) count || relative <= -(int)count)
872 return -EADDRNOTAVAIL;
a331b5e6 873
d121b396
ZJS
874 *boot_id = all_ids[(relative <= 0)*count + relative - 1].id;
875 } else {
876 id = bsearch(&ref_boot_id, all_ids, count, sizeof(boot_id_t), boot_id_cmp);
a331b5e6 877
d121b396
ZJS
878 if (!id ||
879 relative <= 0 ? (id - all_ids) + relative < 0 :
a54e3b3d 880 (id - all_ids) + relative >= (int) count)
d121b396
ZJS
881 return -EADDRNOTAVAIL;
882
883 *boot_id = (id + relative)->id;
884 }
a963990f 885
a331b5e6
JJ
886 return 0;
887}
888
889static int add_boot(sd_journal *j) {
890 char match[9+32+1] = "_BOOT_ID=";
d121b396
ZJS
891 char *offset;
892 sd_id128_t boot_id = SD_ID128_NULL;
a331b5e6
JJ
893 int r, relative = 0;
894
895 assert(j);
896
d121b396 897 if (!arg_boot)
a331b5e6
JJ
898 return 0;
899
d121b396 900 if (!arg_boot_descriptor)
b6741478 901 return add_match_this_boot(j, arg_machine);
a331b5e6 902
d121b396
ZJS
903 if (strlen(arg_boot_descriptor) >= 32) {
904 char tmp = arg_boot_descriptor[32];
905 arg_boot_descriptor[32] = '\0';
906 r = sd_id128_from_string(arg_boot_descriptor, &boot_id);
907 arg_boot_descriptor[32] = tmp;
908
909 if (r < 0) {
910 log_error("Failed to parse boot ID '%.32s': %s",
911 arg_boot_descriptor, strerror(-r));
912 return r;
a331b5e6 913 }
a331b5e6 914
d121b396
ZJS
915 offset = arg_boot_descriptor + 32;
916
a54e3b3d 917 if (*offset && *offset != '-' && *offset != '+') {
d121b396
ZJS
918 log_error("Relative boot ID offset must start with a '+' or a '-', found '%s' ", offset);
919 return -EINVAL;
a331b5e6 920 }
d121b396
ZJS
921 } else
922 offset = arg_boot_descriptor;
923
924 if (*offset) {
925 r = safe_atoi(offset, &relative);
a331b5e6 926 if (r < 0) {
d121b396
ZJS
927 log_error("Failed to parse relative boot ID number '%s'", offset);
928 return -EINVAL;
a331b5e6
JJ
929 }
930 }
931
932 r = get_relative_boot_id(j, &boot_id, relative);
933 if (r < 0) {
d121b396
ZJS
934 if (sd_id128_equal(boot_id, SD_ID128_NULL))
935 log_error("Failed to look up boot %+d: %s", relative, strerror(-r));
936 else
937 log_error("Failed to look up boot ID "SD_ID128_FORMAT_STR"%+d: %s",
938 SD_ID128_FORMAT_VAL(boot_id), relative, strerror(-r));
a331b5e6 939 return r;
a331b5e6
JJ
940 }
941
942 sd_id128_to_string(boot_id, match + 9);
d121b396
ZJS
943
944 r = sd_journal_add_match(j, match, sizeof(match) - 1);
a331b5e6
JJ
945 if (r < 0) {
946 log_error("Failed to add match: %s", strerror(-r));
947 return r;
948 }
949
950 r = sd_journal_add_conjunction(j);
951 if (r < 0)
952 return r;
953
954 return 0;
a963990f
LP
955}
956
99271804
ZJS
957static int add_dmesg(sd_journal *j) {
958 int r;
959 assert(j);
960
961 if (!arg_dmesg)
962 return 0;
963
964 r = sd_journal_add_match(j, "_TRANSPORT=kernel", strlen("_TRANSPORT=kernel"));
965 if (r < 0) {
966 log_error("Failed to add match: %s", strerror(-r));
967 return r;
968 }
969
970 r = sd_journal_add_conjunction(j);
971 if (r < 0)
972 return r;
973
974 return 0;
975}
976
b9e40524 977static int add_units(sd_journal *j) {
4750fade 978 _cleanup_free_ char *u = NULL;
c3f60ec5 979 int r;
b9e40524 980 char **i;
c3f60ec5
LP
981
982 assert(j);
983
b9e40524
HH
984 STRV_FOREACH(i, arg_system_units) {
985 u = unit_name_mangle(*i);
986 if (!u)
987 return log_oom();
988 r = add_matches_for_unit(j, u);
989 if (r < 0)
990 return r;
991 r = sd_journal_add_disjunction(j);
992 if (r < 0)
993 return r;
994 }
c3f60ec5 995
b9e40524
HH
996 STRV_FOREACH(i, arg_user_units) {
997 u = unit_name_mangle(*i);
998 if (!u)
999 return log_oom();
c3f60ec5 1000
7199aa96 1001 r = add_matches_for_user_unit(j, u, getuid());
b9e40524
HH
1002 if (r < 0)
1003 return r;
1004
1005 r = sd_journal_add_disjunction(j);
1006 if (r < 0)
1007 return r;
1008
1009 }
c3f60ec5 1010
cd34b3c6
HH
1011 r = sd_journal_add_conjunction(j);
1012 if (r < 0)
1013 return r;
1014
c3f60ec5
LP
1015 return 0;
1016}
1017
941e990d
LP
1018static int add_priorities(sd_journal *j) {
1019 char match[] = "PRIORITY=0";
1020 int i, r;
941e990d
LP
1021 assert(j);
1022
1023 if (arg_priorities == 0xFF)
1024 return 0;
1025
1026 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
1027 if (arg_priorities & (1 << i)) {
1028 match[sizeof(match)-2] = '0' + i;
1029
941e990d
LP
1030 r = sd_journal_add_match(j, match, strlen(match));
1031 if (r < 0) {
1032 log_error("Failed to add match: %s", strerror(-r));
1033 return r;
1034 }
1035 }
1036
cd34b3c6
HH
1037 r = sd_journal_add_conjunction(j);
1038 if (r < 0)
1039 return r;
1040
941e990d
LP
1041 return 0;
1042}
1043
7560fffc
LP
1044static int setup_keys(void) {
1045#ifdef HAVE_GCRYPT
1046 size_t mpk_size, seed_size, state_size, i;
1047 uint8_t *mpk, *seed, *state;
1048 ssize_t l;
f982e6f7 1049 int fd = -1, r, attr = 0;
7560fffc
LP
1050 sd_id128_t machine, boot;
1051 char *p = NULL, *k = NULL;
baed47c3 1052 struct FSSHeader h;
14d10188 1053 uint64_t n;
b98e3866
SL
1054 struct stat st;
1055
1056 r = stat("/var/log/journal", &st);
1057 if (r < 0 && errno != ENOENT && errno != ENOTDIR) {
1058 log_error("stat(\"%s\") failed: %m", "/var/log/journal");
1059 return -errno;
1060 }
1061
1062 if (r < 0 || !S_ISDIR(st.st_mode)) {
1063 log_error("%s is not a directory, must be using persistent logging for FSS.",
1064 "/var/log/journal");
1065 return r < 0 ? -errno : -ENOTDIR;
1066 }
7560fffc
LP
1067
1068 r = sd_id128_get_machine(&machine);
1069 if (r < 0) {
1070 log_error("Failed to get machine ID: %s", strerror(-r));
1071 return r;
1072 }
1073
1074 r = sd_id128_get_boot(&boot);
1075 if (r < 0) {
1076 log_error("Failed to get boot ID: %s", strerror(-r));
1077 return r;
1078 }
1079
baed47c3 1080 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
7560fffc
LP
1081 SD_ID128_FORMAT_VAL(machine)) < 0)
1082 return log_oom();
1083
1084 if (access(p, F_OK) >= 0) {
b8547c10
SL
1085 if (arg_force) {
1086 r = unlink(p);
1087 if (r < 0) {
1088 log_error("unlink(\"%s\") failed: %m", p);
1089 r = -errno;
1090 goto finish;
1091 }
1092 } else {
1093 log_error("Sealing key file %s exists already. (--force to recreate)", p);
1094 r = -EEXIST;
1095 goto finish;
1096 }
7560fffc
LP
1097 }
1098
baed47c3 1099 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
7560fffc
LP
1100 SD_ID128_FORMAT_VAL(machine)) < 0) {
1101 r = log_oom();
1102 goto finish;
1103 }
1104
1105 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
1106 mpk = alloca(mpk_size);
1107
1108 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
1109 seed = alloca(seed_size);
1110
1111 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
1112 state = alloca(state_size);
1113
1114 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
1115 if (fd < 0) {
1116 log_error("Failed to open /dev/random: %m");
1117 r = -errno;
1118 goto finish;
1119 }
1120
1121 log_info("Generating seed...");
1122 l = loop_read(fd, seed, seed_size, true);
1123 if (l < 0 || (size_t) l != seed_size) {
1124 log_error("Failed to read random seed: %s", strerror(EIO));
1125 r = -EIO;
1126 goto finish;
1127 }
1128
1129 log_info("Generating key pair...");
1130 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
1131
baed47c3 1132 log_info("Generating sealing key...");
7560fffc
LP
1133 FSPRG_GenState0(state, mpk, seed, seed_size);
1134
baed47c3
LP
1135 assert(arg_interval > 0);
1136
7560fffc 1137 n = now(CLOCK_REALTIME);
baed47c3 1138 n /= arg_interval;
7560fffc
LP
1139
1140 close_nointr_nofail(fd);
1141 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
1142 if (fd < 0) {
1143 log_error("Failed to open %s: %m", k);
1144 r = -errno;
1145 goto finish;
1146 }
1147
f982e6f7
LP
1148 /* Enable secure remove, exclusion from dump, synchronous
1149 * writing and in-place updating */
1150 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
1151 log_warning("FS_IOC_GETFLAGS failed: %m");
1152
1153 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
1154
1155 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
1156 log_warning("FS_IOC_SETFLAGS failed: %m");
1157
7560fffc
LP
1158 zero(h);
1159 memcpy(h.signature, "KSHHRHLP", 8);
1160 h.machine_id = machine;
1161 h.boot_id = boot;
1162 h.header_size = htole64(sizeof(h));
baed47c3
LP
1163 h.start_usec = htole64(n * arg_interval);
1164 h.interval_usec = htole64(arg_interval);
1165 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
1166 h.fsprg_state_size = htole64(state_size);
7560fffc
LP
1167
1168 l = loop_write(fd, &h, sizeof(h), false);
1169 if (l < 0 || (size_t) l != sizeof(h)) {
1170 log_error("Failed to write header: %s", strerror(EIO));
1171 r = -EIO;
1172 goto finish;
1173 }
1174
1175 l = loop_write(fd, state, state_size, false);
1176 if (l < 0 || (size_t) l != state_size) {
1177 log_error("Failed to write state: %s", strerror(EIO));
1178 r = -EIO;
1179 goto finish;
1180 }
1181
1182 if (link(k, p) < 0) {
1183 log_error("Failed to link file: %m");
1184 r = -errno;
1185 goto finish;
1186 }
1187
8481248b 1188 if (on_tty()) {
7560fffc
LP
1189 fprintf(stderr,
1190 "\n"
baed47c3 1191 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
c05276f2
LP
1192 "the following local file. This key file is automatically updated when the\n"
1193 "sealing key is advanced. It should not be used on multiple hosts.\n"
7560fffc
LP
1194 "\n"
1195 "\t%s\n"
1196 "\n"
baed47c3
LP
1197 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
1198 "at a safe location and should not be saved locally on disk.\n"
7560fffc
LP
1199 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
1200 fflush(stderr);
1201 }
1202 for (i = 0; i < seed_size; i++) {
1203 if (i > 0 && i % 3 == 0)
1204 putchar('-');
1205 printf("%02x", ((uint8_t*) seed)[i]);
1206 }
1207
baed47c3
LP
1208 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
1209
8481248b 1210 if (on_tty()) {
f6a971bc 1211 char tsb[FORMAT_TIMESPAN_MAX], *hn;
7560fffc 1212
baed47c3
LP
1213 fprintf(stderr,
1214 ANSI_HIGHLIGHT_OFF "\n"
1215 "The sealing key is automatically changed every %s.\n",
2fa4092c 1216 format_timespan(tsb, sizeof(tsb), arg_interval, 0));
f6a971bc
LP
1217
1218 hn = gethostname_malloc();
1219
1220 if (hn) {
e724b063 1221 hostname_cleanup(hn, false);
adac1c93 1222 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
f6a971bc 1223 } else
adac1c93 1224 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
f6a971bc
LP
1225
1226#ifdef HAVE_QRENCODE
cf5a3432 1227 /* If this is not an UTF-8 system don't print any QR codes */
09017585 1228 if (is_locale_utf8()) {
cf5a3432
LP
1229 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
1230 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
1231 }
f6a971bc
LP
1232#endif
1233 free(hn);
baed47c3 1234 }
7560fffc
LP
1235
1236 r = 0;
1237
1238finish:
1239 if (fd >= 0)
1240 close_nointr_nofail(fd);
1241
1242 if (k) {
1243 unlink(k);
1244 free(k);
1245 }
1246
1247 free(p);
1248
1249 return r;
1250#else
feb12d3e
LP
1251 log_error("Forward-secure sealing not available.");
1252 return -ENOTSUP;
7560fffc
LP
1253#endif
1254}
1255
beec0085
LP
1256static int verify(sd_journal *j) {
1257 int r = 0;
1258 Iterator i;
1259 JournalFile *f;
1260
1261 assert(j);
1262
cedb42bb
LP
1263 log_show_color(true);
1264
beec0085
LP
1265 HASHMAP_FOREACH(f, j->files, i) {
1266 int k;
2a7b539a 1267 usec_t first, validated, last;
beec0085 1268
56e81f7c 1269#ifdef HAVE_GCRYPT
feb12d3e 1270 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
cedb42bb 1271 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
56e81f7c 1272#endif
4da416aa 1273
2a7b539a 1274 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
56e81f7c 1275 if (k == -EINVAL) {
baed47c3 1276 /* If the key was invalid give up right-away. */
56e81f7c
LP
1277 return k;
1278 } else if (k < 0) {
beec0085 1279 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
56e81f7c 1280 r = k;
6c7be122
LP
1281 } else {
1282 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
beec0085 1283 log_info("PASS: %s", f->path);
6c7be122 1284
c0ca7aee 1285 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
2a7b539a 1286 if (validated > 0) {
c0ca7aee 1287 log_info("=> Validated from %s to %s, final %s entries not sealed.",
2a7b539a
LP
1288 format_timestamp(a, sizeof(a), first),
1289 format_timestamp(b, sizeof(b), validated),
2fa4092c 1290 format_timespan(c, sizeof(c), last > validated ? last - validated : 0, 0));
2a7b539a 1291 } else if (last > 0)
c0ca7aee 1292 log_info("=> No sealing yet, %s of entries not sealed.",
2fa4092c 1293 format_timespan(c, sizeof(c), last - first, 0));
c0ca7aee
LP
1294 else
1295 log_info("=> No sealing yet, no entries in file.");
1296 }
6c7be122 1297 }
beec0085
LP
1298 }
1299
1300 return r;
1301}
1302
15804ceb 1303#ifdef HAVE_ACL
6fe391c5
ZJS
1304static int access_check_var_log_journal(sd_journal *j) {
1305 _cleanup_strv_free_ char **g = NULL;
1306 bool have_access;
1307 int r;
1308
1309 assert(j);
1310
1311 have_access = in_group("systemd-journal") > 0;
1312
1313 if (!have_access) {
1314 /* Let's enumerate all groups from the default ACL of
1315 * the directory, which generally should allow access
1316 * to most journal files too */
1317 r = search_acl_groups(&g, "/var/log/journal/", &have_access);
1318 if (r < 0)
1319 return r;
15804ceb
LP
1320 }
1321
6fe391c5 1322 if (!have_access) {
4468addc 1323
6fe391c5 1324 if (strv_isempty(g))
3ac251b8
LP
1325 log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
1326 " Users in the 'systemd-journal' group can see all messages. Pass -q to\n"
1327 " turn off this notice.");
6fe391c5
ZJS
1328 else {
1329 _cleanup_free_ char *s = NULL;
4468addc 1330
6fe391c5 1331 r = strv_extend(&g, "systemd-journal");
478c8269 1332 if (r < 0)
6fe391c5
ZJS
1333 return log_oom();
1334
1335 strv_sort(g);
1336 strv_uniq(g);
1337
1338 s = strv_join(g, "', '");
1339 if (!s)
1340 return log_oom();
1341
1342 log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
3ac251b8
LP
1343 " Users in the groups '%s' can see all messages.\n"
1344 " Pass -q to turn off this notice.", s);
4468addc 1345 }
6fe391c5 1346 }
4468addc 1347
6fe391c5
ZJS
1348 return 0;
1349}
1350#endif
4468addc 1351
6fe391c5 1352static int access_check(sd_journal *j) {
6fe391c5 1353 Iterator it;
3ac251b8 1354 void *code;
6fe391c5 1355 int r = 0;
4468addc 1356
6fe391c5 1357 assert(j);
4468addc 1358
6fe391c5
ZJS
1359 if (set_isempty(j->errors)) {
1360 if (hashmap_isempty(j->files))
3ac251b8 1361 log_notice("No journal files were found.");
6fe391c5
ZJS
1362 return 0;
1363 }
4468addc 1364
3ac251b8 1365 if (set_contains(j->errors, INT_TO_PTR(-EACCES))) {
6fe391c5
ZJS
1366#ifdef HAVE_ACL
1367 /* If /var/log/journal doesn't even exist,
3ac251b8 1368 * unprivileged users have no access at all */
6fe391c5
ZJS
1369 if (access("/var/log/journal", F_OK) < 0 &&
1370 geteuid() != 0 &&
1371 in_group("systemd-journal") <= 0) {
3ac251b8
LP
1372 log_error("Unprivileged users cannot access messages, unless persistent log storage is\n"
1373 "enabled. Users in the 'systemd-journal' group may always access messages.");
6fe391c5
ZJS
1374 return -EACCES;
1375 }
4468addc 1376
6fe391c5
ZJS
1377 /* If /var/log/journal exists, try to pring a nice
1378 notice if the user lacks access to it */
1379 if (!arg_quiet && geteuid() != 0) {
1380 r = access_check_var_log_journal(j);
1381 if (r < 0)
1382 return r;
4468addc 1383 }
15804ceb 1384#else
3ac251b8
LP
1385 if (geteuid() != 0 && in_group("systemd-journal") <= 0) {
1386 log_error("Unprivileged users cannot access messages. Users in the 'systemd-journal' group\n"
1387 "group may access messages.");
1388 return -EACCES;
1389 }
15804ceb 1390#endif
3ac251b8 1391
6fe391c5 1392 if (hashmap_isempty(j->files)) {
3ac251b8 1393 log_error("No journal files were opened due to insufficient permissions.");
6fe391c5
ZJS
1394 r = -EACCES;
1395 }
1396 }
15804ceb 1397
6fe391c5 1398 SET_FOREACH(code, j->errors, it) {
3ac251b8
LP
1399 int err;
1400
1401 err = -PTR_TO_INT(code);
6fe391c5 1402 assert(err > 0);
3ac251b8 1403
6fe391c5
ZJS
1404 if (err != EACCES)
1405 log_warning("Error was encountered while opening journal files: %s",
1406 strerror(err));
1407 }
1408
6fe391c5 1409 return r;
15804ceb
LP
1410}
1411
a963990f
LP
1412int main(int argc, char *argv[]) {
1413 int r;
289f910e 1414 _cleanup_journal_close_ sd_journal *j = NULL;
a963990f 1415 bool need_seek = false;
14a65d65 1416 sd_id128_t previous_boot_id;
67e04a48
ZJS
1417 bool previous_boot_id_valid = false, first_line = true;
1418 int n_shown = 0;
94e0bd7d 1419 bool ellipsized = false;
a963990f 1420
a9cdc94f 1421 setlocale(LC_ALL, "");
a963990f
LP
1422 log_parse_environment();
1423 log_open();
1424
1425 r = parse_argv(argc, argv);
1426 if (r <= 0)
1427 goto finish;
1428
ed757c0c
LP
1429 signal(SIGWINCH, columns_lines_cache_reset);
1430
7560fffc 1431 if (arg_action == ACTION_NEW_ID128) {
a963990f
LP
1432 r = generate_new_id128();
1433 goto finish;
1434 }
1435
7560fffc
LP
1436 if (arg_action == ACTION_SETUP_KEYS) {
1437 r = setup_keys();
1438 goto finish;
1439 }
1440
844ec79b
ZJS
1441 if (arg_action == ACTION_UPDATE_CATALOG ||
1442 arg_action == ACTION_LIST_CATALOG ||
1443 arg_action == ACTION_DUMP_CATALOG) {
1444
0db80948 1445 const char* database = CATALOG_DATABASE;
7fd1b19b 1446 _cleanup_free_ char *copy = NULL;
0db80948
ZJS
1447 if (arg_root) {
1448 copy = strjoin(arg_root, "/", CATALOG_DATABASE, NULL);
3a819b1b 1449 if (!copy) {
0db80948
ZJS
1450 r = log_oom();
1451 goto finish;
1452 }
1453 path_kill_slashes(copy);
1454 database = copy;
13cbf3a5
ZJS
1455 }
1456
844ec79b 1457 if (arg_action == ACTION_UPDATE_CATALOG) {
13cbf3a5 1458 r = catalog_update(database, arg_root, catalog_file_dirs);
844ec79b
ZJS
1459 if (r < 0)
1460 log_error("Failed to list catalog: %s", strerror(-r));
1461 } else {
1462 bool oneline = arg_action == ACTION_LIST_CATALOG;
1463
1464 if (optind < argc)
13cbf3a5 1465 r = catalog_list_items(stdout, database,
844ec79b
ZJS
1466 oneline, argv + optind);
1467 else
13cbf3a5 1468 r = catalog_list(stdout, database, oneline);
844ec79b
ZJS
1469 if (r < 0)
1470 log_error("Failed to list catalog: %s", strerror(-r));
1471 }
d4205751 1472
d4205751
LP
1473 goto finish;
1474 }
1475
a963990f 1476 if (arg_directory)
3f3a438f 1477 r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
8d98da3f
ZJS
1478 else if (arg_file)
1479 r = sd_journal_open_files(&j, (const char**) arg_file, 0);
b6741478
LP
1480 else if (arg_machine)
1481 r = sd_journal_open_container(&j, arg_machine, 0);
a963990f 1482 else
3f3a438f 1483 r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
a963990f 1484 if (r < 0) {
8d98da3f
ZJS
1485 log_error("Failed to open %s: %s",
1486 arg_directory ? arg_directory : arg_file ? "files" : "journal",
1487 strerror(-r));
763c7aa2 1488 return EXIT_FAILURE;
a963990f
LP
1489 }
1490
6fe391c5
ZJS
1491 r = access_check(j);
1492 if (r < 0)
1493 return EXIT_FAILURE;
1494
beec0085
LP
1495 if (arg_action == ACTION_VERIFY) {
1496 r = verify(j);
1497 goto finish;
1498 }
1499
7560fffc 1500 if (arg_action == ACTION_PRINT_HEADER) {
dca6219e 1501 journal_print_header(j);
763c7aa2 1502 return EXIT_SUCCESS;
dca6219e
LP
1503 }
1504
a1a03e30
LP
1505 if (arg_action == ACTION_DISK_USAGE) {
1506 uint64_t bytes;
1507 char sbytes[FORMAT_BYTES_MAX];
1508
1509 r = sd_journal_get_usage(j, &bytes);
1510 if (r < 0)
763c7aa2 1511 return EXIT_FAILURE;
a1a03e30 1512
763c7aa2
ZJS
1513 printf("Journals take up %s on disk.\n",
1514 format_bytes(sbytes, sizeof(sbytes), bytes));
1515 return EXIT_SUCCESS;
a1a03e30
LP
1516 }
1517
f1188074
ZJS
1518 if (arg_action == ACTION_LIST_BOOTS) {
1519 r = list_boots(j);
1520 goto finish;
1521 }
1522
a331b5e6
JJ
1523 /* add_boot() must be called first!
1524 * It may need to seek the journal to find parent boot IDs. */
1525 r = add_boot(j);
a963990f 1526 if (r < 0)
763c7aa2 1527 return EXIT_FAILURE;
a963990f 1528
99271804
ZJS
1529 r = add_dmesg(j);
1530 if (r < 0)
1531 return EXIT_FAILURE;
1532
b9e40524
HH
1533 r = add_units(j);
1534 strv_free(arg_system_units);
1535 strv_free(arg_user_units);
1536
c3f60ec5 1537 if (r < 0)
763c7aa2 1538 return EXIT_FAILURE;
c3f60ec5 1539
cd34b3c6 1540 r = add_priorities(j);
a963990f 1541 if (r < 0)
763c7aa2 1542 return EXIT_FAILURE;
a963990f 1543
cd34b3c6 1544 r = add_matches(j, argv + optind);
941e990d 1545 if (r < 0)
763c7aa2 1546 return EXIT_FAILURE;
941e990d 1547
4ad16808
ZJS
1548 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
1549 _cleanup_free_ char *filter;
1550
1551 filter = journal_make_match_string(j);
1552 log_debug("Journal filter: %s", filter);
1553 }
67e04a48 1554
15119c16
LP
1555 if (arg_field) {
1556 const void *data;
1557 size_t size;
1558
21ae4593
ZJS
1559 r = sd_journal_set_data_threshold(j, 0);
1560 if (r < 0) {
1561 log_error("Failed to unset data size threshold");
1562 return EXIT_FAILURE;
1563 }
1564
15119c16
LP
1565 r = sd_journal_query_unique(j, arg_field);
1566 if (r < 0) {
1567 log_error("Failed to query unique data objects: %s", strerror(-r));
763c7aa2 1568 return EXIT_FAILURE;
15119c16
LP
1569 }
1570
1571 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
1572 const void *eq;
1573
67e04a48 1574 if (arg_lines >= 0 && n_shown >= arg_lines)
fd6e8875
LP
1575 break;
1576
15119c16
LP
1577 eq = memchr(data, '=', size);
1578 if (eq)
1579 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
1580 else
1581 printf("%.*s\n", (int) size, (const char*) data);
fd6e8875
LP
1582
1583 n_shown ++;
15119c16
LP
1584 }
1585
763c7aa2 1586 return EXIT_SUCCESS;
15119c16
LP
1587 }
1588
8d98da3f
ZJS
1589 /* Opening the fd now means the first sd_journal_wait() will actually wait */
1590 if (arg_follow) {
1591 r = sd_journal_get_fd(j);
1592 if (r < 0)
1593 return EXIT_FAILURE;
1594 }
1595
248fc619
ZJS
1596 if (arg_cursor || arg_after_cursor) {
1597 r = sd_journal_seek_cursor(j, arg_cursor ? arg_cursor : arg_after_cursor);
08984293 1598 if (r < 0) {
cfbc22ab 1599 log_error("Failed to seek to cursor: %s", strerror(-r));
763c7aa2 1600 return EXIT_FAILURE;
08984293 1601 }
d89d6c86 1602 if (!arg_reverse)
248fc619 1603 r = sd_journal_next_skip(j, 1 + !!arg_after_cursor);
d89d6c86 1604 else
248fc619
ZJS
1605 r = sd_journal_previous_skip(j, 1 + !!arg_after_cursor);
1606
1607 if (arg_after_cursor && r < 2 && !arg_follow)
1608 /* We couldn't find the next entry after the cursor. */
1609 arg_lines = 0;
08984293 1610
d89d6c86 1611 } else if (arg_since_set && !arg_reverse) {
cfbc22ab 1612 r = sd_journal_seek_realtime_usec(j, arg_since);
8f14c832 1613 if (r < 0) {
cfbc22ab 1614 log_error("Failed to seek to date: %s", strerror(-r));
763c7aa2 1615 return EXIT_FAILURE;
8f14c832 1616 }
8f14c832
LP
1617 r = sd_journal_next(j);
1618
d89d6c86
LN
1619 } else if (arg_until_set && arg_reverse) {
1620 r = sd_journal_seek_realtime_usec(j, arg_until);
1621 if (r < 0) {
1622 log_error("Failed to seek to date: %s", strerror(-r));
763c7aa2 1623 return EXIT_FAILURE;
d89d6c86
LN
1624 }
1625 r = sd_journal_previous(j);
1626
67e04a48 1627 } else if (arg_lines >= 0) {
2100675e
LP
1628 r = sd_journal_seek_tail(j);
1629 if (r < 0) {
1630 log_error("Failed to seek to tail: %s", strerror(-r));
763c7aa2 1631 return EXIT_FAILURE;
2100675e
LP
1632 }
1633
1634 r = sd_journal_previous_skip(j, arg_lines);
8f14c832 1635
d89d6c86
LN
1636 } else if (arg_reverse) {
1637 r = sd_journal_seek_tail(j);
1638 if (r < 0) {
1639 log_error("Failed to seek to tail: %s", strerror(-r));
763c7aa2 1640 return EXIT_FAILURE;
d89d6c86
LN
1641 }
1642
1643 r = sd_journal_previous(j);
1644
2100675e
LP
1645 } else {
1646 r = sd_journal_seek_head(j);
1647 if (r < 0) {
1648 log_error("Failed to seek to head: %s", strerror(-r));
763c7aa2 1649 return EXIT_FAILURE;
2100675e 1650 }
6f003b43
LP
1651
1652 r = sd_journal_next(j);
1653 }
1654
1655 if (r < 0) {
1656 log_error("Failed to iterate through journal: %s", strerror(-r));
763c7aa2 1657 return EXIT_FAILURE;
50f20cfd 1658 }
87d2c1ff 1659
faf5077f
DH
1660 if (!arg_follow)
1661 pager_open_if_enabled();
0d43c694 1662
cfbc22ab
LP
1663 if (!arg_quiet) {
1664 usec_t start, end;
1665 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
1666
1667 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
1668 if (r < 0) {
1669 log_error("Failed to get cutoff: %s", strerror(-r));
1670 goto finish;
1671 }
1672
1673 if (r > 0) {
1674 if (arg_follow)
9048b11f
LP
1675 printf("-- Logs begin at %s. --\n",
1676 format_timestamp(start_buf, sizeof(start_buf), start));
cfbc22ab 1677 else
9048b11f 1678 printf("-- Logs begin at %s, end at %s. --\n",
cfbc22ab
LP
1679 format_timestamp(start_buf, sizeof(start_buf), start),
1680 format_timestamp(end_buf, sizeof(end_buf), end));
1681 }
1682 }
1683
50f20cfd 1684 for (;;) {
67e04a48 1685 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
cfbc22ab
LP
1686 int flags;
1687
6f003b43 1688 if (need_seek) {
99613ec5 1689 if (!arg_reverse)
d89d6c86
LN
1690 r = sd_journal_next(j);
1691 else
1692 r = sd_journal_previous(j);
6f003b43
LP
1693 if (r < 0) {
1694 log_error("Failed to iterate through journal: %s", strerror(-r));
1695 goto finish;
1696 }
a72b6353
ZJS
1697 if (r == 0)
1698 break;
0d43c694
LP
1699 }
1700
d89d6c86 1701 if (arg_until_set && !arg_reverse) {
cfbc22ab
LP
1702 usec_t usec;
1703
1704 r = sd_journal_get_realtime_usec(j, &usec);
1705 if (r < 0) {
1706 log_error("Failed to determine timestamp: %s", strerror(-r));
1707 goto finish;
1708 }
3ba09ee8
PF
1709 if (usec > arg_until)
1710 goto finish;
cfbc22ab
LP
1711 }
1712
d89d6c86
LN
1713 if (arg_since_set && arg_reverse) {
1714 usec_t usec;
1715
1716 r = sd_journal_get_realtime_usec(j, &usec);
1717 if (r < 0) {
1718 log_error("Failed to determine timestamp: %s", strerror(-r));
1719 goto finish;
1720 }
1721 if (usec < arg_since)
1722 goto finish;
1723 }
1724
cd931c0a
LP
1725 if (!arg_merge) {
1726 sd_id128_t boot_id;
14a65d65 1727
cd931c0a
LP
1728 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1729 if (r >= 0) {
1730 if (previous_boot_id_valid &&
1731 !sd_id128_equal(boot_id, previous_boot_id))
0b5a519c
DS
1732 printf("%s-- Reboot --%s\n",
1733 ansi_highlight(), ansi_highlight_off());
cd931c0a
LP
1734
1735 previous_boot_id = boot_id;
1736 previous_boot_id_valid = true;
1737 }
14a65d65
LP
1738 }
1739
cfbc22ab 1740 flags =
cd4b13e0 1741 arg_all * OUTPUT_SHOW_ALL |
2b8f6883 1742 arg_full * OUTPUT_FULL_WIDTH |
d4205751
LP
1743 on_tty() * OUTPUT_COLOR |
1744 arg_catalog * OUTPUT_CATALOG;
cfbc22ab 1745
94e0bd7d 1746 r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized);
a72b6353
ZJS
1747 need_seek = true;
1748 if (r == -EADDRNOTAVAIL)
1749 break;
1750 else if (r < 0 || ferror(stdout))
72f59706 1751 goto finish;
6f003b43 1752
cfbc22ab 1753 n_shown++;
87d2c1ff
LP
1754 }
1755
248fc619
ZJS
1756 if (!arg_follow) {
1757 if (arg_show_cursor) {
1758 _cleanup_free_ char *cursor = NULL;
1759
1760 r = sd_journal_get_cursor(j, &cursor);
1761 if (r < 0 && r != -EADDRNOTAVAIL)
1762 log_error("Failed to get cursor: %s", strerror(-r));
1763 else if (r >= 0)
1764 printf("-- cursor: %s\n", cursor);
1765 }
1766
50f20cfd 1767 break;
248fc619 1768 }
50f20cfd 1769
e02d1cf7 1770 r = sd_journal_wait(j, (uint64_t) -1);
50f20cfd 1771 if (r < 0) {
7a69007a 1772 log_error("Couldn't wait for journal event: %s", strerror(-r));
50f20cfd
LP
1773 goto finish;
1774 }
67e04a48
ZJS
1775
1776 first_line = false;
de190aef 1777 }
87d2c1ff
LP
1778
1779finish:
0d43c694
LP
1780 pager_close();
1781
3fbf9cbb 1782 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
87d2c1ff 1783}