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