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