]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journalctl.c
build-sys: fix gtkdocize check
[thirdparty/systemd.git] / src / journal / journalctl.c
CommitLineData
87d2c1ff
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
87d2c1ff
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
87d2c1ff 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
87d2c1ff
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
a9cdc94f 22#include <locale.h>
87d2c1ff
LP
23#include <fcntl.h>
24#include <errno.h>
25#include <stddef.h>
3fbf9cbb
LP
26#include <string.h>
27#include <stdio.h>
28#include <unistd.h>
29#include <stdlib.h>
72f59706 30#include <time.h>
0d43c694 31#include <getopt.h>
585314e8 32#include <signal.h>
50940700 33#include <sys/stat.h>
f982e6f7
LP
34#include <sys/ioctl.h>
35#include <linux/fs.h>
87d2c1ff 36
4468addc
LP
37#ifdef HAVE_ACL
38#include <sys/acl.h>
478c8269 39#include "acl-util.h"
4468addc
LP
40#endif
41
81527be1
LP
42#include <systemd/sd-journal.h>
43
3fbf9cbb 44#include "log.h"
7199aa96 45#include "logs-show.h"
72f59706 46#include "util.h"
e5124088 47#include "path-util.h"
0d43c694
LP
48#include "build.h"
49#include "pager.h"
86aa7ba4 50#include "logs-show.h"
a963990f 51#include "strv.h"
dca6219e 52#include "journal-internal.h"
7560fffc 53#include "journal-def.h"
0284adc6 54#include "journal-verify.h"
4da416aa 55#include "journal-authenticate.h"
f6a971bc 56#include "journal-qrcode.h"
4da416aa 57#include "fsprg.h"
c3f60ec5 58#include "unit-name.h"
d4205751 59#include "catalog.h"
7560fffc 60
baed47c3 61#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
250d54b5 62
df50185b 63static OutputMode arg_output = OUTPUT_SHORT;
1b12a7b5 64static bool arg_pager_end = false;
72f59706 65static bool arg_follow = false;
e3657ecd 66static bool arg_full = false;
cd4b13e0 67static bool arg_all = false;
0d43c694 68static bool arg_no_pager = false;
67e04a48 69static int arg_lines = -1;
e91af489 70static bool arg_no_tail = false;
43673799 71static bool arg_quiet = false;
9e8a535f 72static bool arg_merge = false;
59cea26a 73static bool arg_this_boot = false;
8f14c832 74static const char *arg_cursor = NULL;
a963990f 75static const char *arg_directory = NULL;
941e990d 76static int arg_priorities = 0xFF;
baed47c3 77static const char *arg_verify_key = NULL;
feb12d3e 78#ifdef HAVE_GCRYPT
baed47c3 79static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
feb12d3e 80#endif
cfbc22ab
LP
81static usec_t arg_since, arg_until;
82static bool arg_since_set = false, arg_until_set = false;
b9e40524
HH
83static char **arg_system_units = NULL;
84static char **arg_user_units = NULL;
3c1668da 85static const char *arg_field = NULL;
d4205751 86static bool arg_catalog = false;
d89d6c86 87static bool arg_reverse = false;
13cbf3a5 88static const char *arg_root = NULL;
50f20cfd 89
7560fffc
LP
90static enum {
91 ACTION_SHOW,
92 ACTION_NEW_ID128,
93 ACTION_PRINT_HEADER,
beec0085 94 ACTION_SETUP_KEYS,
a1a03e30
LP
95 ACTION_VERIFY,
96 ACTION_DISK_USAGE,
d4205751 97 ACTION_LIST_CATALOG,
54b7254c 98 ACTION_DUMP_CATALOG,
d4205751 99 ACTION_UPDATE_CATALOG
7560fffc
LP
100} arg_action = ACTION_SHOW;
101
0d43c694
LP
102static int help(void) {
103
cd4b13e0 104 printf("%s [OPTIONS...] [MATCHES...]\n\n"
15119c16
LP
105 "Query the journal.\n\n"
106 "Flags:\n"
cfbc22ab
LP
107 " --since=DATE Start showing entries newer or of the specified date\n"
108 " --until=DATE Stop showing entries older or of the specified date\n"
cd4b13e0 109 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
c3f60ec5
LP
110 " -b --this-boot Show data only from current boot\n"
111 " -u --unit=UNIT Show data only from the specified unit\n"
ffa7cd15 112 " --user-unit=UNIT Show data only from the specified user session unit\n"
cd4b13e0 113 " -p --priority=RANGE Show only messages within the specified priority range\n"
1b12a7b5 114 " -e --pager-end Immediately jump to end of the journal in the pager\n"
baed47c3 115 " -f --follow Follow journal\n"
1705594f 116 " -n --lines[=INTEGER] Number of journal entries to show\n"
baed47c3 117 " --no-tail Show all lines, even in follow mode\n"
d89d6c86 118 " -r --reverse Show the newest entries first\n"
baed47c3 119 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
48383c25 120 " verbose, export, json, json-pretty, json-sse, cat)\n"
d4205751 121 " -x --catalog Add message explanations where available\n"
e3657ecd 122 " --full Do not ellipsize fields\n"
c3f60ec5 123 " -a --all Show all fields, including long and unprintable\n"
baed47c3 124 " -q --quiet Don't show privilege warning\n"
c3f60ec5 125 " --no-pager Do not pipe output into a pager\n"
9e8a535f 126 " -m --merge Show entries from all available journals\n"
baed47c3 127 " -D --directory=PATH Show journal files from directory\n"
13cbf3a5 128 " --root=ROOT Operate on catalog files underneath the root ROOT\n"
15119c16
LP
129#ifdef HAVE_GCRYPT
130 " --interval=TIME Time interval for changing the FSS sealing key\n"
131 " --verify-key=KEY Specify FSS verification key\n"
132#endif
133 "\nCommands:\n"
134 " -h --help Show this help\n"
135 " --version Show package version\n"
baed47c3
LP
136 " --new-id128 Generate a new 128 Bit ID\n"
137 " --header Show journal header information\n"
a1a03e30 138 " --disk-usage Show total disk usage\n"
15119c16 139 " -F --field=FIELD List all values a certain field takes\n"
d4205751 140 " --list-catalog Show message IDs of all entries in the message catalog\n"
54b7254c 141 " --dump-catalog Show entries in the message catalog\n"
d4205751 142 " --update-catalog Update the message catalog database\n"
feb12d3e 143#ifdef HAVE_GCRYPT
baed47c3 144 " --setup-keys Generate new FSS key pair\n"
baed47c3 145 " --verify Verify journal file consistency\n"
feb12d3e
LP
146#endif
147 , program_invocation_short_name);
0d43c694
LP
148
149 return 0;
150}
151
152static int parse_argv(int argc, char *argv[]) {
153
154 enum {
155 ARG_VERSION = 0x100,
e91af489 156 ARG_NO_PAGER,
55ee336c 157 ARG_NO_TAIL,
dca6219e 158 ARG_NEW_ID128,
13cbf3a5 159 ARG_ROOT,
7560fffc 160 ARG_HEADER,
e3657ecd 161 ARG_FULL,
beec0085 162 ARG_SETUP_KEYS,
baed47c3 163 ARG_INTERVAL,
4da416aa 164 ARG_VERIFY,
a1a03e30 165 ARG_VERIFY_KEY,
cfbc22ab
LP
166 ARG_DISK_USAGE,
167 ARG_SINCE,
d4205751 168 ARG_UNTIL,
ffa7cd15 169 ARG_USER_UNIT,
d4205751 170 ARG_LIST_CATALOG,
54b7254c 171 ARG_DUMP_CATALOG,
d4205751 172 ARG_UPDATE_CATALOG
0d43c694
LP
173 };
174
175 static const struct option options[] = {
baed47c3
LP
176 { "help", no_argument, NULL, 'h' },
177 { "version" , no_argument, NULL, ARG_VERSION },
178 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1b12a7b5 179 { "pager-end", no_argument, NULL, 'e' },
baed47c3
LP
180 { "follow", no_argument, NULL, 'f' },
181 { "output", required_argument, NULL, 'o' },
182 { "all", no_argument, NULL, 'a' },
e3657ecd 183 { "full", no_argument, NULL, ARG_FULL },
1705594f 184 { "lines", optional_argument, NULL, 'n' },
baed47c3
LP
185 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
186 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
187 { "quiet", no_argument, NULL, 'q' },
9e8a535f 188 { "merge", no_argument, NULL, 'm' },
baed47c3
LP
189 { "this-boot", no_argument, NULL, 'b' },
190 { "directory", required_argument, NULL, 'D' },
13cbf3a5 191 { "root", required_argument, NULL, ARG_ROOT },
baed47c3 192 { "header", no_argument, NULL, ARG_HEADER },
71c01596 193 { "priority", required_argument, NULL, 'p' },
baed47c3
LP
194 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
195 { "interval", required_argument, NULL, ARG_INTERVAL },
196 { "verify", no_argument, NULL, ARG_VERIFY },
197 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
a1a03e30 198 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
ad9eafab 199 { "cursor", required_argument, NULL, 'c' },
cfbc22ab
LP
200 { "since", required_argument, NULL, ARG_SINCE },
201 { "until", required_argument, NULL, ARG_UNTIL },
c3f60ec5 202 { "unit", required_argument, NULL, 'u' },
7199aa96 203 { "user-unit", required_argument, NULL, ARG_USER_UNIT },
15119c16 204 { "field", required_argument, NULL, 'F' },
d4205751
LP
205 { "catalog", no_argument, NULL, 'x' },
206 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
54b7254c 207 { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
d4205751 208 { "update-catalog",no_argument, NULL, ARG_UPDATE_CATALOG },
d89d6c86 209 { "reverse", no_argument, NULL, 'r' },
baed47c3 210 { NULL, 0, NULL, 0 }
0d43c694
LP
211 };
212
2100675e 213 int c, r;
0d43c694
LP
214
215 assert(argc >= 0);
216 assert(argv);
217
1b12a7b5 218 while ((c = getopt_long(argc, argv, "hefo:an::qmbD:p:c:u:F:xr", options, NULL)) >= 0) {
0d43c694
LP
219
220 switch (c) {
221
222 case 'h':
223 help();
224 return 0;
225
226 case ARG_VERSION:
227 puts(PACKAGE_STRING);
0d43c694
LP
228 puts(SYSTEMD_FEATURES);
229 return 0;
230
231 case ARG_NO_PAGER:
232 arg_no_pager = true;
233 break;
234
1b12a7b5
HH
235 case 'e':
236 arg_pager_end = true;
fe59e38b
LP
237
238 if (arg_lines < 0)
239 arg_lines = 1000;
240
1b12a7b5
HH
241 break;
242
0d43c694
LP
243 case 'f':
244 arg_follow = true;
245 break;
246
247 case 'o':
1705594f 248 arg_output = output_mode_from_string(optarg);
df50185b 249 if (arg_output < 0) {
edfb521a 250 log_error("Unknown output format '%s'.", optarg);
0d43c694
LP
251 return -EINVAL;
252 }
df50185b 253
edfb521a
ZJS
254 if (arg_output == OUTPUT_EXPORT ||
255 arg_output == OUTPUT_JSON ||
256 arg_output == OUTPUT_JSON_PRETTY ||
257 arg_output == OUTPUT_JSON_SSE ||
258 arg_output == OUTPUT_CAT)
259 arg_quiet = true;
260
0d43c694
LP
261 break;
262
e3657ecd
ZJS
263 case ARG_FULL:
264 arg_full = true;
265 break;
266
0d43c694 267 case 'a':
cd4b13e0 268 arg_all = true;
0d43c694
LP
269 break;
270
2100675e 271 case 'n':
1705594f 272 if (optarg) {
67e04a48
ZJS
273 r = safe_atoi(optarg, &arg_lines);
274 if (r < 0 || arg_lines < 0) {
1705594f
LP
275 log_error("Failed to parse lines '%s'", optarg);
276 return -EINVAL;
277 }
96088db0
LP
278 } else {
279 int n;
280
281 /* Hmm, no argument? Maybe the next
282 * word on the command line is
283 * supposed to be the argument? Let's
284 * see if there is one, and is
285 * parsable as a positive
286 * integer... */
287
288 if (optind < argc &&
289 safe_atoi(argv[optind], &n) >= 0 &&
290 n >= 0) {
291
292 arg_lines = n;
293 optind++;
294 } else
295 arg_lines = 10;
296 }
1705594f 297
2100675e
LP
298 break;
299
e91af489
LP
300 case ARG_NO_TAIL:
301 arg_no_tail = true;
302 break;
303
39f7f5c1 304 case ARG_NEW_ID128:
7560fffc 305 arg_action = ACTION_NEW_ID128;
55ee336c
LP
306 break;
307
43673799
LP
308 case 'q':
309 arg_quiet = true;
490e567d 310 break;
43673799 311
9e8a535f
LP
312 case 'm':
313 arg_merge = true;
2bd3c38a
LP
314 break;
315
59cea26a
LP
316 case 'b':
317 arg_this_boot = true;
318 break;
319
a963990f
LP
320 case 'D':
321 arg_directory = optarg;
322 break;
323
13cbf3a5
ZJS
324 case ARG_ROOT:
325 arg_root = optarg;
326 break;
327
8f14c832
LP
328 case 'c':
329 arg_cursor = optarg;
330 break;
331
dca6219e 332 case ARG_HEADER:
7560fffc
LP
333 arg_action = ACTION_PRINT_HEADER;
334 break;
335
feb12d3e
LP
336 case ARG_VERIFY:
337 arg_action = ACTION_VERIFY;
338 break;
339
a1a03e30
LP
340 case ARG_DISK_USAGE:
341 arg_action = ACTION_DISK_USAGE;
342 break;
343
feb12d3e 344#ifdef HAVE_GCRYPT
7560fffc
LP
345 case ARG_SETUP_KEYS:
346 arg_action = ACTION_SETUP_KEYS;
dca6219e
LP
347 break;
348
beec0085 349
baed47c3 350 case ARG_VERIFY_KEY:
4da416aa 351 arg_action = ACTION_VERIFY;
baed47c3 352 arg_verify_key = optarg;
9e8a535f 353 arg_merge = false;
4da416aa
LP
354 break;
355
baed47c3 356 case ARG_INTERVAL:
7f602784 357 r = parse_sec(optarg, &arg_interval);
baed47c3
LP
358 if (r < 0 || arg_interval <= 0) {
359 log_error("Failed to parse sealing key change interval: %s", optarg);
14d10188
LP
360 return -EINVAL;
361 }
362 break;
feb12d3e
LP
363#else
364 case ARG_SETUP_KEYS:
365 case ARG_VERIFY_KEY:
366 case ARG_INTERVAL:
367 log_error("Forward-secure sealing not available.");
368 return -ENOTSUP;
369#endif
14d10188 370
941e990d
LP
371 case 'p': {
372 const char *dots;
373
374 dots = strstr(optarg, "..");
375 if (dots) {
376 char *a;
377 int from, to, i;
378
379 /* a range */
380 a = strndup(optarg, dots - optarg);
381 if (!a)
382 return log_oom();
383
384 from = log_level_from_string(a);
385 to = log_level_from_string(dots + 2);
386 free(a);
387
388 if (from < 0 || to < 0) {
389 log_error("Failed to parse log level range %s", optarg);
390 return -EINVAL;
391 }
392
393 arg_priorities = 0;
394
395 if (from < to) {
396 for (i = from; i <= to; i++)
397 arg_priorities |= 1 << i;
398 } else {
399 for (i = to; i <= from; i++)
400 arg_priorities |= 1 << i;
401 }
402
403 } else {
404 int p, i;
405
406 p = log_level_from_string(optarg);
407 if (p < 0) {
408 log_error("Unknown log level %s", optarg);
409 return -EINVAL;
410 }
411
412 arg_priorities = 0;
413
414 for (i = 0; i <= p; i++)
415 arg_priorities |= 1 << i;
416 }
417
418 break;
419 }
420
cfbc22ab
LP
421 case ARG_SINCE:
422 r = parse_timestamp(optarg, &arg_since);
423 if (r < 0) {
424 log_error("Failed to parse timestamp: %s", optarg);
425 return -EINVAL;
426 }
427 arg_since_set = true;
428 break;
429
430 case ARG_UNTIL:
431 r = parse_timestamp(optarg, &arg_until);
432 if (r < 0) {
433 log_error("Failed to parse timestamp: %s", optarg);
434 return -EINVAL;
435 }
436 arg_until_set = true;
437 break;
438
7199aa96 439 case 'u':
b9e40524
HH
440 r = strv_extend(&arg_system_units, optarg);
441 if (r < 0)
442 return log_oom();
ffa7cd15
DW
443 break;
444
7199aa96 445 case ARG_USER_UNIT:
b9e40524
HH
446 r = strv_extend(&arg_user_units, optarg);
447 if (r < 0)
448 return log_oom();
c3f60ec5
LP
449 break;
450
0d43c694
LP
451 case '?':
452 return -EINVAL;
453
15119c16
LP
454 case 'F':
455 arg_field = optarg;
456 break;
457
d4205751
LP
458 case 'x':
459 arg_catalog = true;
460 break;
461
462 case ARG_LIST_CATALOG:
463 arg_action = ACTION_LIST_CATALOG;
464 break;
465
54b7254c
ZJS
466 case ARG_DUMP_CATALOG:
467 arg_action = ACTION_DUMP_CATALOG;
468 break;
469
d4205751
LP
470 case ARG_UPDATE_CATALOG:
471 arg_action = ACTION_UPDATE_CATALOG;
472 break;
473
d89d6c86
LN
474 case 'r':
475 arg_reverse = true;
476 break;
477
0d43c694
LP
478 default:
479 log_error("Unknown option code %c", c);
480 return -EINVAL;
481 }
482 }
483
67e04a48 484 if (arg_follow && !arg_no_tail && arg_lines < 0)
e91af489
LP
485 arg_lines = 10;
486
3ba09ee8 487 if (arg_since_set && arg_until_set && arg_since > arg_until) {
cfbc22ab
LP
488 log_error("--since= must be before --until=.");
489 return -EINVAL;
490 }
491
492 if (arg_cursor && arg_since_set) {
493 log_error("Please specify either --since= or --cursor=, not both.");
494 return -EINVAL;
495 }
496
d89d6c86
LN
497 if (arg_follow && arg_reverse) {
498 log_error("Please specify either --reverse= or --follow=, not both.");
499 return -EINVAL;
500 }
501
0d43c694
LP
502 return 1;
503}
504
39f7f5c1 505static int generate_new_id128(void) {
55ee336c
LP
506 sd_id128_t id;
507 int r;
508 unsigned i;
509
510 r = sd_id128_randomize(&id);
511 if (r < 0) {
512 log_error("Failed to generate ID: %s", strerror(-r));
513 return r;
514 }
515
516 printf("As string:\n"
517 SD_ID128_FORMAT_STR "\n\n"
518 "As UUID:\n"
519 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
520 "As macro:\n"
d489071f 521 "#define MESSAGE_XYZ SD_ID128_MAKE(",
55ee336c
LP
522 SD_ID128_FORMAT_VAL(id),
523 SD_ID128_FORMAT_VAL(id));
55ee336c
LP
524 for (i = 0; i < 16; i++)
525 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
d489071f 526 fputs(")\n\n", stdout);
55ee336c 527
d489071f
ZJS
528 printf("As Python constant:\n"
529 ">>> import uuid\n"
530 ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
531 SD_ID128_FORMAT_VAL(id));
55ee336c
LP
532
533 return 0;
534}
535
a963990f
LP
536static int add_matches(sd_journal *j, char **args) {
537 char **i;
59cea26a 538
a963990f 539 assert(j);
59cea26a 540
a963990f 541 STRV_FOREACH(i, args) {
52aeb63c 542 int r;
59cea26a 543
cbdca852
LP
544 if (streq(*i, "+"))
545 r = sd_journal_add_disjunction(j);
546 else if (path_is_absolute(*i)) {
7fd1b19b 547 _cleanup_free_ char *p, *t = NULL;
e5124088 548 const char *path;
a963990f 549 struct stat st;
e5124088 550
a963990f
LP
551 p = canonicalize_file_name(*i);
552 path = p ? p : *i;
e5124088
LP
553
554 if (stat(path, &st) < 0) {
e5124088 555 log_error("Couldn't stat file: %m");
a963990f 556 return -errno;
e5124088
LP
557 }
558
b6a34514 559 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
e5124088 560 t = strappend("_EXE=", path);
b6a34514
LP
561 else if (S_ISCHR(st.st_mode))
562 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
563 else if (S_ISBLK(st.st_mode))
564 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
565 else {
fb93cf73 566 log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
a963990f 567 return -EINVAL;
50940700 568 }
e5124088 569
b6a34514
LP
570 if (!t)
571 return log_oom();
572
573 r = sd_journal_add_match(j, t, 0);
e5124088 574 } else
cbdca852 575 r = sd_journal_add_match(j, *i, 0);
e5124088 576
de7b95cd 577 if (r < 0) {
cbdca852 578 log_error("Failed to add match '%s': %s", *i, strerror(-r));
a963990f 579 return r;
de7b95cd
LP
580 }
581 }
582
a963990f
LP
583 return 0;
584}
585
586static int add_this_boot(sd_journal *j) {
587 char match[9+32+1] = "_BOOT_ID=";
588 sd_id128_t boot_id;
589 int r;
590
941e990d
LP
591 assert(j);
592
a963990f
LP
593 if (!arg_this_boot)
594 return 0;
595
596 r = sd_id128_get_boot(&boot_id);
597 if (r < 0) {
598 log_error("Failed to get boot id: %s", strerror(-r));
599 return r;
600 }
601
602 sd_id128_to_string(boot_id, match + 9);
603 r = sd_journal_add_match(j, match, strlen(match));
604 if (r < 0) {
605 log_error("Failed to add match: %s", strerror(-r));
606 return r;
607 }
608
cd34b3c6
HH
609 r = sd_journal_add_conjunction(j);
610 if (r < 0)
611 return r;
612
a963990f
LP
613 return 0;
614}
615
b9e40524 616static int add_units(sd_journal *j) {
4750fade 617 _cleanup_free_ char *u = NULL;
c3f60ec5 618 int r;
b9e40524 619 char **i;
c3f60ec5
LP
620
621 assert(j);
622
b9e40524
HH
623 STRV_FOREACH(i, arg_system_units) {
624 u = unit_name_mangle(*i);
625 if (!u)
626 return log_oom();
627 r = add_matches_for_unit(j, u);
628 if (r < 0)
629 return r;
630 r = sd_journal_add_disjunction(j);
631 if (r < 0)
632 return r;
633 }
c3f60ec5 634
b9e40524
HH
635 STRV_FOREACH(i, arg_user_units) {
636 u = unit_name_mangle(*i);
637 if (!u)
638 return log_oom();
c3f60ec5 639
7199aa96 640 r = add_matches_for_user_unit(j, u, getuid());
b9e40524
HH
641 if (r < 0)
642 return r;
643
644 r = sd_journal_add_disjunction(j);
645 if (r < 0)
646 return r;
647
648 }
c3f60ec5 649
cd34b3c6
HH
650 r = sd_journal_add_conjunction(j);
651 if (r < 0)
652 return r;
653
c3f60ec5
LP
654 return 0;
655}
656
941e990d
LP
657static int add_priorities(sd_journal *j) {
658 char match[] = "PRIORITY=0";
659 int i, r;
941e990d
LP
660 assert(j);
661
662 if (arg_priorities == 0xFF)
663 return 0;
664
665 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
666 if (arg_priorities & (1 << i)) {
667 match[sizeof(match)-2] = '0' + i;
668
941e990d
LP
669 r = sd_journal_add_match(j, match, strlen(match));
670 if (r < 0) {
671 log_error("Failed to add match: %s", strerror(-r));
672 return r;
673 }
674 }
675
cd34b3c6
HH
676 r = sd_journal_add_conjunction(j);
677 if (r < 0)
678 return r;
679
941e990d
LP
680 return 0;
681}
682
7560fffc
LP
683static int setup_keys(void) {
684#ifdef HAVE_GCRYPT
685 size_t mpk_size, seed_size, state_size, i;
686 uint8_t *mpk, *seed, *state;
687 ssize_t l;
f982e6f7 688 int fd = -1, r, attr = 0;
7560fffc
LP
689 sd_id128_t machine, boot;
690 char *p = NULL, *k = NULL;
baed47c3 691 struct FSSHeader h;
14d10188 692 uint64_t n;
7560fffc
LP
693
694 r = sd_id128_get_machine(&machine);
695 if (r < 0) {
696 log_error("Failed to get machine ID: %s", strerror(-r));
697 return r;
698 }
699
700 r = sd_id128_get_boot(&boot);
701 if (r < 0) {
702 log_error("Failed to get boot ID: %s", strerror(-r));
703 return r;
704 }
705
baed47c3 706 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
7560fffc
LP
707 SD_ID128_FORMAT_VAL(machine)) < 0)
708 return log_oom();
709
710 if (access(p, F_OK) >= 0) {
f7fab8a5 711 log_error("Sealing key file %s exists already.", p);
7560fffc
LP
712 r = -EEXIST;
713 goto finish;
714 }
715
baed47c3 716 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
7560fffc
LP
717 SD_ID128_FORMAT_VAL(machine)) < 0) {
718 r = log_oom();
719 goto finish;
720 }
721
722 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
723 mpk = alloca(mpk_size);
724
725 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
726 seed = alloca(seed_size);
727
728 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
729 state = alloca(state_size);
730
731 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
732 if (fd < 0) {
733 log_error("Failed to open /dev/random: %m");
734 r = -errno;
735 goto finish;
736 }
737
738 log_info("Generating seed...");
739 l = loop_read(fd, seed, seed_size, true);
740 if (l < 0 || (size_t) l != seed_size) {
741 log_error("Failed to read random seed: %s", strerror(EIO));
742 r = -EIO;
743 goto finish;
744 }
745
746 log_info("Generating key pair...");
747 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
748
baed47c3 749 log_info("Generating sealing key...");
7560fffc
LP
750 FSPRG_GenState0(state, mpk, seed, seed_size);
751
baed47c3
LP
752 assert(arg_interval > 0);
753
7560fffc 754 n = now(CLOCK_REALTIME);
baed47c3 755 n /= arg_interval;
7560fffc
LP
756
757 close_nointr_nofail(fd);
758 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
759 if (fd < 0) {
760 log_error("Failed to open %s: %m", k);
761 r = -errno;
762 goto finish;
763 }
764
f982e6f7
LP
765 /* Enable secure remove, exclusion from dump, synchronous
766 * writing and in-place updating */
767 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
768 log_warning("FS_IOC_GETFLAGS failed: %m");
769
770 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
771
772 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
773 log_warning("FS_IOC_SETFLAGS failed: %m");
774
7560fffc
LP
775 zero(h);
776 memcpy(h.signature, "KSHHRHLP", 8);
777 h.machine_id = machine;
778 h.boot_id = boot;
779 h.header_size = htole64(sizeof(h));
baed47c3
LP
780 h.start_usec = htole64(n * arg_interval);
781 h.interval_usec = htole64(arg_interval);
782 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
783 h.fsprg_state_size = htole64(state_size);
7560fffc
LP
784
785 l = loop_write(fd, &h, sizeof(h), false);
786 if (l < 0 || (size_t) l != sizeof(h)) {
787 log_error("Failed to write header: %s", strerror(EIO));
788 r = -EIO;
789 goto finish;
790 }
791
792 l = loop_write(fd, state, state_size, false);
793 if (l < 0 || (size_t) l != state_size) {
794 log_error("Failed to write state: %s", strerror(EIO));
795 r = -EIO;
796 goto finish;
797 }
798
799 if (link(k, p) < 0) {
800 log_error("Failed to link file: %m");
801 r = -errno;
802 goto finish;
803 }
804
8481248b 805 if (on_tty()) {
7560fffc
LP
806 fprintf(stderr,
807 "\n"
baed47c3 808 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
c05276f2
LP
809 "the following local file. This key file is automatically updated when the\n"
810 "sealing key is advanced. It should not be used on multiple hosts.\n"
7560fffc
LP
811 "\n"
812 "\t%s\n"
813 "\n"
baed47c3
LP
814 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
815 "at a safe location and should not be saved locally on disk.\n"
7560fffc
LP
816 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
817 fflush(stderr);
818 }
819 for (i = 0; i < seed_size; i++) {
820 if (i > 0 && i % 3 == 0)
821 putchar('-');
822 printf("%02x", ((uint8_t*) seed)[i]);
823 }
824
baed47c3
LP
825 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
826
8481248b 827 if (on_tty()) {
f6a971bc 828 char tsb[FORMAT_TIMESPAN_MAX], *hn;
7560fffc 829
baed47c3
LP
830 fprintf(stderr,
831 ANSI_HIGHLIGHT_OFF "\n"
832 "The sealing key is automatically changed every %s.\n",
2fa4092c 833 format_timespan(tsb, sizeof(tsb), arg_interval, 0));
f6a971bc
LP
834
835 hn = gethostname_malloc();
836
837 if (hn) {
e724b063 838 hostname_cleanup(hn, false);
adac1c93 839 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
f6a971bc 840 } else
adac1c93 841 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
f6a971bc
LP
842
843#ifdef HAVE_QRENCODE
cf5a3432 844 /* If this is not an UTF-8 system don't print any QR codes */
09017585 845 if (is_locale_utf8()) {
cf5a3432
LP
846 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
847 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
848 }
f6a971bc
LP
849#endif
850 free(hn);
baed47c3 851 }
7560fffc
LP
852
853 r = 0;
854
855finish:
856 if (fd >= 0)
857 close_nointr_nofail(fd);
858
859 if (k) {
860 unlink(k);
861 free(k);
862 }
863
864 free(p);
865
866 return r;
867#else
feb12d3e
LP
868 log_error("Forward-secure sealing not available.");
869 return -ENOTSUP;
7560fffc
LP
870#endif
871}
872
beec0085
LP
873static int verify(sd_journal *j) {
874 int r = 0;
875 Iterator i;
876 JournalFile *f;
877
878 assert(j);
879
cedb42bb
LP
880 log_show_color(true);
881
beec0085
LP
882 HASHMAP_FOREACH(f, j->files, i) {
883 int k;
2a7b539a 884 usec_t first, validated, last;
beec0085 885
56e81f7c 886#ifdef HAVE_GCRYPT
feb12d3e 887 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
cedb42bb 888 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
56e81f7c 889#endif
4da416aa 890
2a7b539a 891 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
56e81f7c 892 if (k == -EINVAL) {
baed47c3 893 /* If the key was invalid give up right-away. */
56e81f7c
LP
894 return k;
895 } else if (k < 0) {
beec0085 896 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
56e81f7c 897 r = k;
6c7be122
LP
898 } else {
899 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
beec0085 900 log_info("PASS: %s", f->path);
6c7be122 901
c0ca7aee 902 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
2a7b539a 903 if (validated > 0) {
c0ca7aee 904 log_info("=> Validated from %s to %s, final %s entries not sealed.",
2a7b539a
LP
905 format_timestamp(a, sizeof(a), first),
906 format_timestamp(b, sizeof(b), validated),
2fa4092c 907 format_timespan(c, sizeof(c), last > validated ? last - validated : 0, 0));
2a7b539a 908 } else if (last > 0)
c0ca7aee 909 log_info("=> No sealing yet, %s of entries not sealed.",
2fa4092c 910 format_timespan(c, sizeof(c), last - first, 0));
c0ca7aee
LP
911 else
912 log_info("=> No sealing yet, no entries in file.");
913 }
6c7be122 914 }
beec0085
LP
915 }
916
917 return r;
918}
919
15804ceb 920#ifdef HAVE_ACL
6fe391c5
ZJS
921static int access_check_var_log_journal(sd_journal *j) {
922 _cleanup_strv_free_ char **g = NULL;
923 bool have_access;
924 int r;
925
926 assert(j);
927
928 have_access = in_group("systemd-journal") > 0;
929
930 if (!have_access) {
931 /* Let's enumerate all groups from the default ACL of
932 * the directory, which generally should allow access
933 * to most journal files too */
934 r = search_acl_groups(&g, "/var/log/journal/", &have_access);
935 if (r < 0)
936 return r;
15804ceb
LP
937 }
938
6fe391c5 939 if (!have_access) {
4468addc 940
6fe391c5 941 if (strv_isempty(g))
3ac251b8
LP
942 log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
943 " Users in the 'systemd-journal' group can see all messages. Pass -q to\n"
944 " turn off this notice.");
6fe391c5
ZJS
945 else {
946 _cleanup_free_ char *s = NULL;
4468addc 947
6fe391c5 948 r = strv_extend(&g, "systemd-journal");
478c8269 949 if (r < 0)
6fe391c5
ZJS
950 return log_oom();
951
952 strv_sort(g);
953 strv_uniq(g);
954
955 s = strv_join(g, "', '");
956 if (!s)
957 return log_oom();
958
959 log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
3ac251b8
LP
960 " Users in the groups '%s' can see all messages.\n"
961 " Pass -q to turn off this notice.", s);
4468addc 962 }
6fe391c5 963 }
4468addc 964
6fe391c5
ZJS
965 return 0;
966}
967#endif
4468addc 968
6fe391c5 969static int access_check(sd_journal *j) {
6fe391c5 970 Iterator it;
3ac251b8 971 void *code;
6fe391c5 972 int r = 0;
4468addc 973
6fe391c5 974 assert(j);
4468addc 975
6fe391c5
ZJS
976 if (set_isempty(j->errors)) {
977 if (hashmap_isempty(j->files))
3ac251b8 978 log_notice("No journal files were found.");
6fe391c5
ZJS
979 return 0;
980 }
4468addc 981
3ac251b8 982 if (set_contains(j->errors, INT_TO_PTR(-EACCES))) {
6fe391c5
ZJS
983#ifdef HAVE_ACL
984 /* If /var/log/journal doesn't even exist,
3ac251b8 985 * unprivileged users have no access at all */
6fe391c5
ZJS
986 if (access("/var/log/journal", F_OK) < 0 &&
987 geteuid() != 0 &&
988 in_group("systemd-journal") <= 0) {
3ac251b8
LP
989 log_error("Unprivileged users cannot access messages, unless persistent log storage is\n"
990 "enabled. Users in the 'systemd-journal' group may always access messages.");
6fe391c5
ZJS
991 return -EACCES;
992 }
4468addc 993
6fe391c5
ZJS
994 /* If /var/log/journal exists, try to pring a nice
995 notice if the user lacks access to it */
996 if (!arg_quiet && geteuid() != 0) {
997 r = access_check_var_log_journal(j);
998 if (r < 0)
999 return r;
4468addc 1000 }
15804ceb 1001#else
3ac251b8
LP
1002 if (geteuid() != 0 && in_group("systemd-journal") <= 0) {
1003 log_error("Unprivileged users cannot access messages. Users in the 'systemd-journal' group\n"
1004 "group may access messages.");
1005 return -EACCES;
1006 }
15804ceb 1007#endif
3ac251b8 1008
6fe391c5 1009 if (hashmap_isempty(j->files)) {
3ac251b8 1010 log_error("No journal files were opened due to insufficient permissions.");
6fe391c5
ZJS
1011 r = -EACCES;
1012 }
1013 }
15804ceb 1014
6fe391c5 1015 SET_FOREACH(code, j->errors, it) {
3ac251b8
LP
1016 int err;
1017
1018 err = -PTR_TO_INT(code);
6fe391c5 1019 assert(err > 0);
3ac251b8 1020
6fe391c5
ZJS
1021 if (err != EACCES)
1022 log_warning("Error was encountered while opening journal files: %s",
1023 strerror(err));
1024 }
1025
6fe391c5 1026 return r;
15804ceb
LP
1027}
1028
a963990f
LP
1029int main(int argc, char *argv[]) {
1030 int r;
7fd1b19b 1031 _cleanup_journal_close_ sd_journal*j = NULL;
a963990f 1032 bool need_seek = false;
14a65d65 1033 sd_id128_t previous_boot_id;
67e04a48
ZJS
1034 bool previous_boot_id_valid = false, first_line = true;
1035 int n_shown = 0;
a963990f 1036
a9cdc94f 1037 setlocale(LC_ALL, "");
a963990f
LP
1038 log_parse_environment();
1039 log_open();
1040
1041 r = parse_argv(argc, argv);
1042 if (r <= 0)
1043 goto finish;
1044
ed757c0c
LP
1045 signal(SIGWINCH, columns_lines_cache_reset);
1046
7560fffc 1047 if (arg_action == ACTION_NEW_ID128) {
a963990f
LP
1048 r = generate_new_id128();
1049 goto finish;
1050 }
1051
7560fffc
LP
1052 if (arg_action == ACTION_SETUP_KEYS) {
1053 r = setup_keys();
1054 goto finish;
1055 }
1056
844ec79b
ZJS
1057 if (arg_action == ACTION_UPDATE_CATALOG ||
1058 arg_action == ACTION_LIST_CATALOG ||
1059 arg_action == ACTION_DUMP_CATALOG) {
1060
0db80948 1061 const char* database = CATALOG_DATABASE;
7fd1b19b 1062 _cleanup_free_ char *copy = NULL;
0db80948
ZJS
1063 if (arg_root) {
1064 copy = strjoin(arg_root, "/", CATALOG_DATABASE, NULL);
3a819b1b 1065 if (!copy) {
0db80948
ZJS
1066 r = log_oom();
1067 goto finish;
1068 }
1069 path_kill_slashes(copy);
1070 database = copy;
13cbf3a5
ZJS
1071 }
1072
844ec79b 1073 if (arg_action == ACTION_UPDATE_CATALOG) {
13cbf3a5 1074 r = catalog_update(database, arg_root, catalog_file_dirs);
844ec79b
ZJS
1075 if (r < 0)
1076 log_error("Failed to list catalog: %s", strerror(-r));
1077 } else {
1078 bool oneline = arg_action == ACTION_LIST_CATALOG;
1079
1080 if (optind < argc)
13cbf3a5 1081 r = catalog_list_items(stdout, database,
844ec79b
ZJS
1082 oneline, argv + optind);
1083 else
13cbf3a5 1084 r = catalog_list(stdout, database, oneline);
844ec79b
ZJS
1085 if (r < 0)
1086 log_error("Failed to list catalog: %s", strerror(-r));
1087 }
d4205751 1088
d4205751
LP
1089 goto finish;
1090 }
1091
a963990f
LP
1092 if (arg_directory)
1093 r = sd_journal_open_directory(&j, arg_directory, 0);
1094 else
9e8a535f 1095 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
a963990f
LP
1096 if (r < 0) {
1097 log_error("Failed to open journal: %s", strerror(-r));
763c7aa2 1098 return EXIT_FAILURE;
a963990f
LP
1099 }
1100
6fe391c5
ZJS
1101 r = access_check(j);
1102 if (r < 0)
1103 return EXIT_FAILURE;
1104
beec0085
LP
1105 if (arg_action == ACTION_VERIFY) {
1106 r = verify(j);
1107 goto finish;
1108 }
1109
7560fffc 1110 if (arg_action == ACTION_PRINT_HEADER) {
dca6219e 1111 journal_print_header(j);
763c7aa2 1112 return EXIT_SUCCESS;
dca6219e
LP
1113 }
1114
a1a03e30
LP
1115 if (arg_action == ACTION_DISK_USAGE) {
1116 uint64_t bytes;
1117 char sbytes[FORMAT_BYTES_MAX];
1118
1119 r = sd_journal_get_usage(j, &bytes);
1120 if (r < 0)
763c7aa2 1121 return EXIT_FAILURE;
a1a03e30 1122
763c7aa2
ZJS
1123 printf("Journals take up %s on disk.\n",
1124 format_bytes(sbytes, sizeof(sbytes), bytes));
1125 return EXIT_SUCCESS;
a1a03e30
LP
1126 }
1127
a963990f
LP
1128 r = add_this_boot(j);
1129 if (r < 0)
763c7aa2 1130 return EXIT_FAILURE;
a963990f 1131
b9e40524
HH
1132 r = add_units(j);
1133 strv_free(arg_system_units);
1134 strv_free(arg_user_units);
1135
c3f60ec5 1136 if (r < 0)
763c7aa2 1137 return EXIT_FAILURE;
c3f60ec5 1138
cd34b3c6 1139 r = add_priorities(j);
a963990f 1140 if (r < 0)
763c7aa2 1141 return EXIT_FAILURE;
a963990f 1142
cd34b3c6 1143 r = add_matches(j, argv + optind);
941e990d 1144 if (r < 0)
763c7aa2 1145 return EXIT_FAILURE;
941e990d 1146
67e04a48
ZJS
1147 /* Opening the fd now means the first sd_journal_wait() will actually wait */
1148 r = sd_journal_get_fd(j);
1149 if (r < 0)
763c7aa2 1150 return EXIT_FAILURE;
67e04a48 1151
15119c16
LP
1152 if (arg_field) {
1153 const void *data;
1154 size_t size;
1155
21ae4593
ZJS
1156 r = sd_journal_set_data_threshold(j, 0);
1157 if (r < 0) {
1158 log_error("Failed to unset data size threshold");
1159 return EXIT_FAILURE;
1160 }
1161
15119c16
LP
1162 r = sd_journal_query_unique(j, arg_field);
1163 if (r < 0) {
1164 log_error("Failed to query unique data objects: %s", strerror(-r));
763c7aa2 1165 return EXIT_FAILURE;
15119c16
LP
1166 }
1167
1168 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
1169 const void *eq;
1170
67e04a48 1171 if (arg_lines >= 0 && n_shown >= arg_lines)
fd6e8875
LP
1172 break;
1173
15119c16
LP
1174 eq = memchr(data, '=', size);
1175 if (eq)
1176 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
1177 else
1178 printf("%.*s\n", (int) size, (const char*) data);
fd6e8875
LP
1179
1180 n_shown ++;
15119c16
LP
1181 }
1182
763c7aa2 1183 return EXIT_SUCCESS;
15119c16
LP
1184 }
1185
cfbc22ab
LP
1186 if (arg_cursor) {
1187 r = sd_journal_seek_cursor(j, arg_cursor);
08984293 1188 if (r < 0) {
cfbc22ab 1189 log_error("Failed to seek to cursor: %s", strerror(-r));
763c7aa2 1190 return EXIT_FAILURE;
08984293 1191 }
d89d6c86
LN
1192 if (!arg_reverse)
1193 r = sd_journal_next(j);
1194 else
1195 r = sd_journal_previous(j);
08984293 1196
d89d6c86 1197 } else if (arg_since_set && !arg_reverse) {
cfbc22ab 1198 r = sd_journal_seek_realtime_usec(j, arg_since);
8f14c832 1199 if (r < 0) {
cfbc22ab 1200 log_error("Failed to seek to date: %s", strerror(-r));
763c7aa2 1201 return EXIT_FAILURE;
8f14c832 1202 }
8f14c832
LP
1203 r = sd_journal_next(j);
1204
d89d6c86
LN
1205 } else if (arg_until_set && arg_reverse) {
1206 r = sd_journal_seek_realtime_usec(j, arg_until);
1207 if (r < 0) {
1208 log_error("Failed to seek to date: %s", strerror(-r));
763c7aa2 1209 return EXIT_FAILURE;
d89d6c86
LN
1210 }
1211 r = sd_journal_previous(j);
1212
67e04a48 1213 } else if (arg_lines >= 0) {
2100675e
LP
1214 r = sd_journal_seek_tail(j);
1215 if (r < 0) {
1216 log_error("Failed to seek to tail: %s", strerror(-r));
763c7aa2 1217 return EXIT_FAILURE;
2100675e
LP
1218 }
1219
1220 r = sd_journal_previous_skip(j, arg_lines);
8f14c832 1221
d89d6c86
LN
1222 } else if (arg_reverse) {
1223 r = sd_journal_seek_tail(j);
1224 if (r < 0) {
1225 log_error("Failed to seek to tail: %s", strerror(-r));
763c7aa2 1226 return EXIT_FAILURE;
d89d6c86
LN
1227 }
1228
1229 r = sd_journal_previous(j);
1230
2100675e
LP
1231 } else {
1232 r = sd_journal_seek_head(j);
1233 if (r < 0) {
1234 log_error("Failed to seek to head: %s", strerror(-r));
763c7aa2 1235 return EXIT_FAILURE;
2100675e 1236 }
6f003b43
LP
1237
1238 r = sd_journal_next(j);
1239 }
1240
1241 if (r < 0) {
1242 log_error("Failed to iterate through journal: %s", strerror(-r));
763c7aa2 1243 return EXIT_FAILURE;
50f20cfd 1244 }
87d2c1ff 1245
f89a3b6f 1246 if (!arg_no_pager && !arg_follow)
1b12a7b5 1247 pager_open(arg_pager_end);
0d43c694 1248
cfbc22ab
LP
1249 if (!arg_quiet) {
1250 usec_t start, end;
1251 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
1252
1253 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
1254 if (r < 0) {
1255 log_error("Failed to get cutoff: %s", strerror(-r));
1256 goto finish;
1257 }
1258
1259 if (r > 0) {
1260 if (arg_follow)
9048b11f
LP
1261 printf("-- Logs begin at %s. --\n",
1262 format_timestamp(start_buf, sizeof(start_buf), start));
cfbc22ab 1263 else
9048b11f 1264 printf("-- Logs begin at %s, end at %s. --\n",
cfbc22ab
LP
1265 format_timestamp(start_buf, sizeof(start_buf), start),
1266 format_timestamp(end_buf, sizeof(end_buf), end));
1267 }
1268 }
1269
50f20cfd 1270 for (;;) {
67e04a48 1271 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
cfbc22ab
LP
1272 int flags;
1273
6f003b43 1274 if (need_seek) {
99613ec5 1275 if (!arg_reverse)
d89d6c86
LN
1276 r = sd_journal_next(j);
1277 else
1278 r = sd_journal_previous(j);
6f003b43
LP
1279 if (r < 0) {
1280 log_error("Failed to iterate through journal: %s", strerror(-r));
1281 goto finish;
1282 }
0d43c694
LP
1283 }
1284
1285 if (r == 0)
1286 break;
1287
d89d6c86 1288 if (arg_until_set && !arg_reverse) {
cfbc22ab
LP
1289 usec_t usec;
1290
1291 r = sd_journal_get_realtime_usec(j, &usec);
1292 if (r < 0) {
1293 log_error("Failed to determine timestamp: %s", strerror(-r));
1294 goto finish;
1295 }
3ba09ee8
PF
1296 if (usec > arg_until)
1297 goto finish;
cfbc22ab
LP
1298 }
1299
d89d6c86
LN
1300 if (arg_since_set && arg_reverse) {
1301 usec_t usec;
1302
1303 r = sd_journal_get_realtime_usec(j, &usec);
1304 if (r < 0) {
1305 log_error("Failed to determine timestamp: %s", strerror(-r));
1306 goto finish;
1307 }
1308 if (usec < arg_since)
1309 goto finish;
1310 }
1311
cd931c0a
LP
1312 if (!arg_merge) {
1313 sd_id128_t boot_id;
14a65d65 1314
cd931c0a
LP
1315 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1316 if (r >= 0) {
1317 if (previous_boot_id_valid &&
1318 !sd_id128_equal(boot_id, previous_boot_id))
9048b11f 1319 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
cd931c0a
LP
1320
1321 previous_boot_id = boot_id;
1322 previous_boot_id_valid = true;
1323 }
14a65d65
LP
1324 }
1325
cfbc22ab 1326 flags =
cd4b13e0 1327 arg_all * OUTPUT_SHOW_ALL |
e3657ecd 1328 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
d4205751
LP
1329 on_tty() * OUTPUT_COLOR |
1330 arg_catalog * OUTPUT_CATALOG;
cfbc22ab 1331
08ace05b 1332 r = output_journal(stdout, j, arg_output, 0, flags);
244692cb 1333 if (r < 0 || ferror(stdout))
72f59706 1334 goto finish;
6f003b43
LP
1335
1336 need_seek = true;
cfbc22ab 1337 n_shown++;
87d2c1ff
LP
1338 }
1339
50f20cfd
LP
1340 if (!arg_follow)
1341 break;
1342
e02d1cf7 1343 r = sd_journal_wait(j, (uint64_t) -1);
50f20cfd 1344 if (r < 0) {
7a69007a 1345 log_error("Couldn't wait for journal event: %s", strerror(-r));
50f20cfd
LP
1346 goto finish;
1347 }
67e04a48
ZJS
1348
1349 first_line = false;
de190aef 1350 }
87d2c1ff
LP
1351
1352finish:
0d43c694
LP
1353 pager_close();
1354
3fbf9cbb 1355 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
87d2c1ff 1356}