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