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