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