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