]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journalctl.c
journal: after verification output validated time range
[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
22#include <fcntl.h>
23#include <errno.h>
24#include <stddef.h>
3fbf9cbb
LP
25#include <string.h>
26#include <stdio.h>
27#include <unistd.h>
28#include <stdlib.h>
50f20cfd 29#include <sys/poll.h>
72f59706 30#include <time.h>
0d43c694 31#include <getopt.h>
50940700 32#include <sys/stat.h>
87d2c1ff 33
81527be1
LP
34#include <systemd/sd-journal.h>
35
3fbf9cbb 36#include "log.h"
72f59706 37#include "util.h"
e5124088 38#include "path-util.h"
0d43c694
LP
39#include "build.h"
40#include "pager.h"
86aa7ba4 41#include "logs-show.h"
a963990f 42#include "strv.h"
dca6219e 43#include "journal-internal.h"
7560fffc 44#include "journal-def.h"
0284adc6 45#include "journal-verify.h"
4da416aa
LP
46#include "journal-authenticate.h"
47#include "fsprg.h"
7560fffc 48
baed47c3 49#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
250d54b5 50
df50185b 51static OutputMode arg_output = OUTPUT_SHORT;
72f59706
LP
52static bool arg_follow = false;
53static bool arg_show_all = false;
0d43c694 54static bool arg_no_pager = false;
2100675e 55static int arg_lines = -1;
e91af489 56static bool arg_no_tail = false;
43673799 57static bool arg_quiet = false;
2bd3c38a 58static bool arg_local = false;
59cea26a 59static bool arg_this_boot = false;
a963990f 60static const char *arg_directory = NULL;
941e990d 61static int arg_priorities = 0xFF;
baed47c3
LP
62static const char *arg_verify_key = NULL;
63static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
50f20cfd 64
7560fffc
LP
65static enum {
66 ACTION_SHOW,
67 ACTION_NEW_ID128,
68 ACTION_PRINT_HEADER,
beec0085
LP
69 ACTION_SETUP_KEYS,
70 ACTION_VERIFY
7560fffc
LP
71} arg_action = ACTION_SHOW;
72
0d43c694
LP
73static int help(void) {
74
89834a7c 75 printf("%s [OPTIONS...] [MATCH]\n\n"
df50185b 76 "Send control commands to or query the journal.\n\n"
baed47c3
LP
77 " -h --help Show this help\n"
78 " --version Show package version\n"
79 " --no-pager Do not pipe output into a pager\n"
80 " -a --all Show all fields, including long and unprintable\n"
81 " -f --follow Follow journal\n"
82 " -n --lines=INTEGER Journal entries to show\n"
83 " --no-tail Show all lines, even in follow mode\n"
84 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
85 " verbose, export, json, cat)\n"
86 " -q --quiet Don't show privilege warning\n"
87 " -l --local Only local entries\n"
88 " -b --this-boot Show data only from current boot\n"
89 " -D --directory=PATH Show journal files from directory\n"
90 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
7560fffc 91 "Commands:\n"
baed47c3
LP
92 " --new-id128 Generate a new 128 Bit ID\n"
93 " --header Show journal header information\n"
94 " --setup-keys Generate new FSS key pair\n"
95 " --interval=TIME Time interval for changing the FSS sealing key\n"
96 " --verify Verify journal file consistency\n"
97 " --verify-key=KEY Specify FSS verification key\n",
0d43c694
LP
98 program_invocation_short_name);
99
100 return 0;
101}
102
103static int parse_argv(int argc, char *argv[]) {
104
105 enum {
106 ARG_VERSION = 0x100,
e91af489 107 ARG_NO_PAGER,
55ee336c 108 ARG_NO_TAIL,
dca6219e 109 ARG_NEW_ID128,
7560fffc 110 ARG_HEADER,
beec0085 111 ARG_SETUP_KEYS,
baed47c3 112 ARG_INTERVAL,
4da416aa 113 ARG_VERIFY,
baed47c3 114 ARG_VERIFY_KEY
0d43c694
LP
115 };
116
117 static const struct option options[] = {
baed47c3
LP
118 { "help", no_argument, NULL, 'h' },
119 { "version" , no_argument, NULL, ARG_VERSION },
120 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
121 { "follow", no_argument, NULL, 'f' },
122 { "output", required_argument, NULL, 'o' },
123 { "all", no_argument, NULL, 'a' },
124 { "lines", required_argument, NULL, 'n' },
125 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
126 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
127 { "quiet", no_argument, NULL, 'q' },
128 { "local", no_argument, NULL, 'l' },
129 { "this-boot", no_argument, NULL, 'b' },
130 { "directory", required_argument, NULL, 'D' },
131 { "header", no_argument, NULL, ARG_HEADER },
132 { "priority", no_argument, NULL, 'p' },
133 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
134 { "interval", required_argument, NULL, ARG_INTERVAL },
135 { "verify", no_argument, NULL, ARG_VERIFY },
136 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
137 { NULL, 0, NULL, 0 }
0d43c694
LP
138 };
139
2100675e 140 int c, r;
0d43c694
LP
141
142 assert(argc >= 0);
143 assert(argv);
144
941e990d 145 while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
0d43c694
LP
146
147 switch (c) {
148
149 case 'h':
150 help();
151 return 0;
152
153 case ARG_VERSION:
154 puts(PACKAGE_STRING);
155 puts(DISTRIBUTION);
156 puts(SYSTEMD_FEATURES);
157 return 0;
158
159 case ARG_NO_PAGER:
160 arg_no_pager = true;
161 break;
162
163 case 'f':
164 arg_follow = true;
165 break;
166
167 case 'o':
df50185b
LP
168 arg_output = output_mode_from_string(optarg);
169 if (arg_output < 0) {
0d43c694
LP
170 log_error("Unknown output '%s'.", optarg);
171 return -EINVAL;
172 }
df50185b 173
0d43c694
LP
174 break;
175
176 case 'a':
177 arg_show_all = true;
178 break;
179
2100675e
LP
180 case 'n':
181 r = safe_atoi(optarg, &arg_lines);
e91af489 182 if (r < 0 || arg_lines < 0) {
2100675e
LP
183 log_error("Failed to parse lines '%s'", optarg);
184 return -EINVAL;
185 }
186 break;
187
e91af489
LP
188 case ARG_NO_TAIL:
189 arg_no_tail = true;
190 break;
191
39f7f5c1 192 case ARG_NEW_ID128:
7560fffc 193 arg_action = ACTION_NEW_ID128;
55ee336c
LP
194 break;
195
43673799
LP
196 case 'q':
197 arg_quiet = true;
490e567d 198 break;
43673799 199
2bd3c38a
LP
200 case 'l':
201 arg_local = true;
202 break;
203
59cea26a
LP
204 case 'b':
205 arg_this_boot = true;
206 break;
207
a963990f
LP
208 case 'D':
209 arg_directory = optarg;
210 break;
211
dca6219e 212 case ARG_HEADER:
7560fffc
LP
213 arg_action = ACTION_PRINT_HEADER;
214 break;
215
216 case ARG_SETUP_KEYS:
217 arg_action = ACTION_SETUP_KEYS;
dca6219e
LP
218 break;
219
beec0085
LP
220 case ARG_VERIFY:
221 arg_action = ACTION_VERIFY;
222 break;
223
baed47c3 224 case ARG_VERIFY_KEY:
4da416aa 225 arg_action = ACTION_VERIFY;
baed47c3 226 arg_verify_key = optarg;
4da416aa
LP
227 break;
228
baed47c3
LP
229 case ARG_INTERVAL:
230 r = parse_usec(optarg, &arg_interval);
231 if (r < 0 || arg_interval <= 0) {
232 log_error("Failed to parse sealing key change interval: %s", optarg);
14d10188
LP
233 return -EINVAL;
234 }
235 break;
236
941e990d
LP
237 case 'p': {
238 const char *dots;
239
240 dots = strstr(optarg, "..");
241 if (dots) {
242 char *a;
243 int from, to, i;
244
245 /* a range */
246 a = strndup(optarg, dots - optarg);
247 if (!a)
248 return log_oom();
249
250 from = log_level_from_string(a);
251 to = log_level_from_string(dots + 2);
252 free(a);
253
254 if (from < 0 || to < 0) {
255 log_error("Failed to parse log level range %s", optarg);
256 return -EINVAL;
257 }
258
259 arg_priorities = 0;
260
261 if (from < to) {
262 for (i = from; i <= to; i++)
263 arg_priorities |= 1 << i;
264 } else {
265 for (i = to; i <= from; i++)
266 arg_priorities |= 1 << i;
267 }
268
269 } else {
270 int p, i;
271
272 p = log_level_from_string(optarg);
273 if (p < 0) {
274 log_error("Unknown log level %s", optarg);
275 return -EINVAL;
276 }
277
278 arg_priorities = 0;
279
280 for (i = 0; i <= p; i++)
281 arg_priorities |= 1 << i;
282 }
283
284 break;
285 }
286
0d43c694
LP
287 case '?':
288 return -EINVAL;
289
290 default:
291 log_error("Unknown option code %c", c);
292 return -EINVAL;
293 }
294 }
295
62f21ec9 296 if (arg_follow && !arg_no_tail && arg_lines < 0)
e91af489
LP
297 arg_lines = 10;
298
0d43c694
LP
299 return 1;
300}
301
49826187
LP
302static bool on_tty(void) {
303 static int t = -1;
304
305 /* Note that this is invoked relatively early, before we start
306 * the pager. That means the value we return reflects whether
307 * we originally were started on a tty, not if we currently
308 * are. But this is intended, since we want colour and so on
309 * when run in our own pager. */
310
311 if (_unlikely_(t < 0))
312 t = isatty(STDOUT_FILENO) > 0;
313
314 return t;
315}
316
39f7f5c1 317static int generate_new_id128(void) {
55ee336c
LP
318 sd_id128_t id;
319 int r;
320 unsigned i;
321
322 r = sd_id128_randomize(&id);
323 if (r < 0) {
324 log_error("Failed to generate ID: %s", strerror(-r));
325 return r;
326 }
327
328 printf("As string:\n"
329 SD_ID128_FORMAT_STR "\n\n"
330 "As UUID:\n"
331 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
332 "As macro:\n"
333 "#define MESSAGE_XYZ SD_ID128_MAKE(",
334 SD_ID128_FORMAT_VAL(id),
335 SD_ID128_FORMAT_VAL(id));
336
337 for (i = 0; i < 16; i++)
338 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
339
340 fputs(")\n", stdout);
341
342 return 0;
343}
344
a963990f
LP
345static int add_matches(sd_journal *j, char **args) {
346 char **i;
347 int r;
59cea26a 348
a963990f 349 assert(j);
59cea26a 350
a963990f 351 STRV_FOREACH(i, args) {
59cea26a 352
cbdca852
LP
353 if (streq(*i, "+"))
354 r = sd_journal_add_disjunction(j);
355 else if (path_is_absolute(*i)) {
b6a34514 356 char *p, *t = NULL;
e5124088 357 const char *path;
a963990f 358 struct stat st;
e5124088 359
a963990f
LP
360 p = canonicalize_file_name(*i);
361 path = p ? p : *i;
e5124088
LP
362
363 if (stat(path, &st) < 0) {
364 free(p);
365 log_error("Couldn't stat file: %m");
a963990f 366 return -errno;
e5124088
LP
367 }
368
b6a34514 369 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
e5124088 370 t = strappend("_EXE=", path);
b6a34514
LP
371 else if (S_ISCHR(st.st_mode))
372 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
373 else if (S_ISBLK(st.st_mode))
374 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
375 else {
e5124088 376 free(p);
b6a34514 377 log_error("File is not a device node, regular file or is not executable: %s", *i);
a963990f 378 return -EINVAL;
50940700 379 }
e5124088
LP
380
381 free(p);
b6a34514
LP
382
383 if (!t)
384 return log_oom();
385
386 r = sd_journal_add_match(j, t, 0);
387 free(t);
e5124088 388 } else
cbdca852 389 r = sd_journal_add_match(j, *i, 0);
e5124088 390
de7b95cd 391 if (r < 0) {
cbdca852 392 log_error("Failed to add match '%s': %s", *i, strerror(-r));
a963990f 393 return r;
de7b95cd
LP
394 }
395 }
396
a963990f
LP
397 return 0;
398}
399
400static int add_this_boot(sd_journal *j) {
401 char match[9+32+1] = "_BOOT_ID=";
402 sd_id128_t boot_id;
403 int r;
404
941e990d
LP
405 assert(j);
406
a963990f
LP
407 if (!arg_this_boot)
408 return 0;
409
410 r = sd_id128_get_boot(&boot_id);
411 if (r < 0) {
412 log_error("Failed to get boot id: %s", strerror(-r));
413 return r;
414 }
415
416 sd_id128_to_string(boot_id, match + 9);
417 r = sd_journal_add_match(j, match, strlen(match));
418 if (r < 0) {
419 log_error("Failed to add match: %s", strerror(-r));
420 return r;
421 }
422
423 return 0;
424}
425
941e990d
LP
426static int add_priorities(sd_journal *j) {
427 char match[] = "PRIORITY=0";
428 int i, r;
429
430 assert(j);
431
432 if (arg_priorities == 0xFF)
433 return 0;
434
435 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
436 if (arg_priorities & (1 << i)) {
437 match[sizeof(match)-2] = '0' + i;
438
439 log_info("adding match %s", match);
440
441 r = sd_journal_add_match(j, match, strlen(match));
442 if (r < 0) {
443 log_error("Failed to add match: %s", strerror(-r));
444 return r;
445 }
446 }
447
448 return 0;
449}
450
7560fffc
LP
451static int setup_keys(void) {
452#ifdef HAVE_GCRYPT
453 size_t mpk_size, seed_size, state_size, i;
454 uint8_t *mpk, *seed, *state;
455 ssize_t l;
456 int fd = -1, r;
457 sd_id128_t machine, boot;
458 char *p = NULL, *k = NULL;
baed47c3 459 struct FSSHeader h;
14d10188 460 uint64_t n;
7560fffc
LP
461
462 r = sd_id128_get_machine(&machine);
463 if (r < 0) {
464 log_error("Failed to get machine ID: %s", strerror(-r));
465 return r;
466 }
467
468 r = sd_id128_get_boot(&boot);
469 if (r < 0) {
470 log_error("Failed to get boot ID: %s", strerror(-r));
471 return r;
472 }
473
baed47c3 474 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
7560fffc
LP
475 SD_ID128_FORMAT_VAL(machine)) < 0)
476 return log_oom();
477
478 if (access(p, F_OK) >= 0) {
479 log_error("Evolving key file %s exists already.", p);
480 r = -EEXIST;
481 goto finish;
482 }
483
baed47c3 484 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
7560fffc
LP
485 SD_ID128_FORMAT_VAL(machine)) < 0) {
486 r = log_oom();
487 goto finish;
488 }
489
490 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
491 mpk = alloca(mpk_size);
492
493 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
494 seed = alloca(seed_size);
495
496 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
497 state = alloca(state_size);
498
499 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
500 if (fd < 0) {
501 log_error("Failed to open /dev/random: %m");
502 r = -errno;
503 goto finish;
504 }
505
506 log_info("Generating seed...");
507 l = loop_read(fd, seed, seed_size, true);
508 if (l < 0 || (size_t) l != seed_size) {
509 log_error("Failed to read random seed: %s", strerror(EIO));
510 r = -EIO;
511 goto finish;
512 }
513
514 log_info("Generating key pair...");
515 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
516
baed47c3 517 log_info("Generating sealing key...");
7560fffc
LP
518 FSPRG_GenState0(state, mpk, seed, seed_size);
519
baed47c3
LP
520 assert(arg_interval > 0);
521
7560fffc 522 n = now(CLOCK_REALTIME);
baed47c3 523 n /= arg_interval;
7560fffc
LP
524
525 close_nointr_nofail(fd);
526 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
527 if (fd < 0) {
528 log_error("Failed to open %s: %m", k);
529 r = -errno;
530 goto finish;
531 }
532
533 zero(h);
534 memcpy(h.signature, "KSHHRHLP", 8);
535 h.machine_id = machine;
536 h.boot_id = boot;
537 h.header_size = htole64(sizeof(h));
baed47c3
LP
538 h.start_usec = htole64(n * arg_interval);
539 h.interval_usec = htole64(arg_interval);
540 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
541 h.fsprg_state_size = htole64(state_size);
7560fffc
LP
542
543 l = loop_write(fd, &h, sizeof(h), false);
544 if (l < 0 || (size_t) l != sizeof(h)) {
545 log_error("Failed to write header: %s", strerror(EIO));
546 r = -EIO;
547 goto finish;
548 }
549
550 l = loop_write(fd, state, state_size, false);
551 if (l < 0 || (size_t) l != state_size) {
552 log_error("Failed to write state: %s", strerror(EIO));
553 r = -EIO;
554 goto finish;
555 }
556
557 if (link(k, p) < 0) {
558 log_error("Failed to link file: %m");
559 r = -errno;
560 goto finish;
561 }
562
563 if (isatty(STDOUT_FILENO)) {
564 fprintf(stderr,
565 "\n"
baed47c3
LP
566 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
567 "the following local file. It should not be used on multiple hosts.\n"
7560fffc
LP
568 "\n"
569 "\t%s\n"
570 "\n"
baed47c3
LP
571 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
572 "at a safe location and should not be saved locally on disk.\n"
7560fffc
LP
573 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
574 fflush(stderr);
575 }
576 for (i = 0; i < seed_size; i++) {
577 if (i > 0 && i % 3 == 0)
578 putchar('-');
579 printf("%02x", ((uint8_t*) seed)[i]);
580 }
581
baed47c3
LP
582 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
583
584 if (isatty(STDOUT_FILENO)) {
585 char tsb[FORMAT_TIMESPAN_MAX];
7560fffc 586
baed47c3
LP
587 fprintf(stderr,
588 ANSI_HIGHLIGHT_OFF "\n"
589 "The sealing key is automatically changed every %s.\n",
590 format_timespan(tsb, sizeof(tsb), arg_interval));
591 }
7560fffc
LP
592
593 r = 0;
594
595finish:
596 if (fd >= 0)
597 close_nointr_nofail(fd);
598
599 if (k) {
600 unlink(k);
601 free(k);
602 }
603
604 free(p);
605
606 return r;
607#else
608 log_error("Forward-secure journal verification not available.");
609#endif
610}
611
beec0085
LP
612static int verify(sd_journal *j) {
613 int r = 0;
614 Iterator i;
615 JournalFile *f;
616
617 assert(j);
618
619 HASHMAP_FOREACH(f, j->files, i) {
620 int k;
6c7be122 621 usec_t from, to, total;
beec0085 622
56e81f7c 623#ifdef HAVE_GCRYPT
baed47c3
LP
624 if (!arg_verify_key && journal_file_fss_enabled(f))
625 log_warning("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
56e81f7c 626#endif
4da416aa 627
6c7be122 628 k = journal_file_verify(f, arg_verify_key, &from, &to, &total);
56e81f7c 629 if (k == -EINVAL) {
baed47c3 630 /* If the key was invalid give up right-away. */
56e81f7c
LP
631 return k;
632 } else if (k < 0) {
beec0085 633 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
56e81f7c 634 r = k;
6c7be122
LP
635 } else {
636 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
beec0085 637 log_info("PASS: %s", f->path);
6c7be122
LP
638
639 if (journal_file_fss_enabled(f))
640 log_info("=> Validated from %s to %s, %s missing",
641 format_timestamp(a, sizeof(a), from),
642 format_timestamp(b, sizeof(b), to),
643 format_timespan(c, sizeof(c), total > to ? total - to : 0));
644 }
beec0085
LP
645 }
646
647 return r;
648}
649
a963990f
LP
650int main(int argc, char *argv[]) {
651 int r;
652 sd_journal *j = NULL;
653 unsigned line = 0;
654 bool need_seek = false;
14a65d65
LP
655 sd_id128_t previous_boot_id;
656 bool previous_boot_id_valid = false;
92a1fd9e 657 bool have_pager;
a963990f
LP
658
659 log_parse_environment();
660 log_open();
661
662 r = parse_argv(argc, argv);
663 if (r <= 0)
664 goto finish;
665
7560fffc 666 if (arg_action == ACTION_NEW_ID128) {
a963990f
LP
667 r = generate_new_id128();
668 goto finish;
669 }
670
7560fffc
LP
671 if (arg_action == ACTION_SETUP_KEYS) {
672 r = setup_keys();
673 goto finish;
674 }
675
a963990f
LP
676 if (arg_directory)
677 r = sd_journal_open_directory(&j, arg_directory, 0);
678 else
679 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
680
681 if (r < 0) {
682 log_error("Failed to open journal: %s", strerror(-r));
683 goto finish;
684 }
685
beec0085
LP
686 if (arg_action == ACTION_VERIFY) {
687 r = verify(j);
688 goto finish;
689 }
690
7560fffc 691 if (arg_action == ACTION_PRINT_HEADER) {
dca6219e
LP
692 journal_print_header(j);
693 r = 0;
694 goto finish;
695 }
696
beec0085
LP
697#ifdef HAVE_ACL
698 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
699 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
700#endif
701
a963990f
LP
702 r = add_this_boot(j);
703 if (r < 0)
704 goto finish;
705
706 r = add_matches(j, argv + optind);
707 if (r < 0)
708 goto finish;
709
941e990d
LP
710 r = add_priorities(j);
711 if (r < 0)
712 goto finish;
713
08984293
LP
714 if (!arg_quiet) {
715 usec_t start, end;
716 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
717
718 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
719 if (r < 0) {
720 log_error("Failed to get cutoff: %s", strerror(-r));
721 goto finish;
722 }
723
724 if (r > 0) {
725 if (arg_follow)
726 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
727 else
728 printf("Logs begin at %s, end at %s.\n",
729 format_timestamp(start_buf, sizeof(start_buf), start),
730 format_timestamp(end_buf, sizeof(end_buf), end));
731 }
732 }
733
2100675e
LP
734 if (arg_lines >= 0) {
735 r = sd_journal_seek_tail(j);
736 if (r < 0) {
737 log_error("Failed to seek to tail: %s", strerror(-r));
738 goto finish;
739 }
740
741 r = sd_journal_previous_skip(j, arg_lines);
2100675e
LP
742 } else {
743 r = sd_journal_seek_head(j);
744 if (r < 0) {
745 log_error("Failed to seek to head: %s", strerror(-r));
746 goto finish;
747 }
6f003b43
LP
748
749 r = sd_journal_next(j);
750 }
751
752 if (r < 0) {
753 log_error("Failed to iterate through journal: %s", strerror(-r));
754 goto finish;
50f20cfd 755 }
87d2c1ff 756
49826187 757 on_tty();
fafb6ecc 758 have_pager = !arg_no_pager && !arg_follow && pager_open();
0d43c694
LP
759
760 if (arg_output == OUTPUT_JSON) {
72f59706 761 fputc('[', stdout);
0d43c694
LP
762 fflush(stdout);
763 }
72f59706 764
50f20cfd 765 for (;;) {
0d43c694 766 for (;;) {
14a65d65 767 sd_id128_t boot_id;
49826187
LP
768 int flags =
769 arg_show_all * OUTPUT_SHOW_ALL |
770 have_pager * OUTPUT_FULL_WIDTH |
771 on_tty() * OUTPUT_COLOR;
14a65d65 772
6f003b43
LP
773 if (need_seek) {
774 r = sd_journal_next(j);
775 if (r < 0) {
776 log_error("Failed to iterate through journal: %s", strerror(-r));
777 goto finish;
778 }
0d43c694
LP
779 }
780
781 if (r == 0)
782 break;
783
14a65d65
LP
784 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
785 if (r >= 0) {
786 if (previous_boot_id_valid &&
787 !sd_id128_equal(boot_id, previous_boot_id))
788 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
789
790 previous_boot_id = boot_id;
791 previous_boot_id_valid = true;
792 }
793
72f59706 794 line ++;
50f20cfd 795
92a1fd9e 796 r = output_journal(j, arg_output, line, 0, flags);
72f59706
LP
797 if (r < 0)
798 goto finish;
6f003b43
LP
799
800 need_seek = true;
87d2c1ff
LP
801 }
802
50f20cfd
LP
803 if (!arg_follow)
804 break;
805
e02d1cf7 806 r = sd_journal_wait(j, (uint64_t) -1);
50f20cfd 807 if (r < 0) {
e02d1cf7 808 log_error("Couldn't wait for log event: %s", strerror(-r));
50f20cfd
LP
809 goto finish;
810 }
de190aef 811 }
87d2c1ff 812
72f59706
LP
813 if (arg_output == OUTPUT_JSON)
814 fputs("\n]\n", stdout);
815
87d2c1ff 816finish:
3fbf9cbb
LP
817 if (j)
818 sd_journal_close(j);
87d2c1ff 819
0d43c694
LP
820 pager_close();
821
3fbf9cbb 822 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
87d2c1ff 823}