chiark / gitweb /
journalctl: immeidately terminate on invalid seed
[elogind.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
34 #include <systemd/sd-journal.h>
35
36 #include "log.h"
37 #include "util.h"
38 #include "path-util.h"
39 #include "build.h"
40 #include "pager.h"
41 #include "logs-show.h"
42 #include "strv.h"
43 #include "journal-internal.h"
44 #include "journal-def.h"
45 #include "journal-verify.h"
46 #include "journal-authenticate.h"
47 #include "fsprg.h"
48
49 #define DEFAULT_FSPRG_INTERVAL_USEC (15*USEC_PER_MINUTE)
50
51 static OutputMode arg_output = OUTPUT_SHORT;
52 static bool arg_follow = false;
53 static bool arg_show_all = false;
54 static bool arg_no_pager = false;
55 static int arg_lines = -1;
56 static bool arg_no_tail = false;
57 static bool arg_quiet = false;
58 static bool arg_local = false;
59 static bool arg_this_boot = false;
60 static const char *arg_directory = NULL;
61 static int arg_priorities = 0xFF;
62 static const char *arg_verify_seed = NULL;
63
64 static enum {
65         ACTION_SHOW,
66         ACTION_NEW_ID128,
67         ACTION_PRINT_HEADER,
68         ACTION_SETUP_KEYS,
69         ACTION_VERIFY
70 } arg_action = ACTION_SHOW;
71
72 static int help(void) {
73
74         printf("%s [OPTIONS...] [MATCH]\n\n"
75                "Send control commands to or query the journal.\n\n"
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"
90                "Commands:\n"
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",
96                program_invocation_short_name);
97
98         return 0;
99 }
100
101 static int parse_argv(int argc, char *argv[]) {
102
103         enum {
104                 ARG_VERSION = 0x100,
105                 ARG_NO_PAGER,
106                 ARG_NO_TAIL,
107                 ARG_NEW_ID128,
108                 ARG_HEADER,
109                 ARG_SETUP_KEYS,
110                 ARG_VERIFY,
111                 ARG_VERIFY_SEED
112         };
113
114         static const struct option options[] = {
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               }
134         };
135
136         int c, r;
137
138         assert(argc >= 0);
139         assert(argv);
140
141         while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
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':
164                         arg_output =  output_mode_from_string(optarg);
165                         if (arg_output < 0) {
166                                 log_error("Unknown output '%s'.", optarg);
167                                 return -EINVAL;
168                         }
169
170                         break;
171
172                 case 'a':
173                         arg_show_all = true;
174                         break;
175
176                 case 'n':
177                         r = safe_atoi(optarg, &arg_lines);
178                         if (r < 0 || arg_lines < 0) {
179                                 log_error("Failed to parse lines '%s'", optarg);
180                                 return -EINVAL;
181                         }
182                         break;
183
184                 case ARG_NO_TAIL:
185                         arg_no_tail = true;
186                         break;
187
188                 case ARG_NEW_ID128:
189                         arg_action = ACTION_NEW_ID128;
190                         break;
191
192                 case 'q':
193                         arg_quiet = true;
194                         break;
195
196                 case 'l':
197                         arg_local = true;
198                         break;
199
200                 case 'b':
201                         arg_this_boot = true;
202                         break;
203
204                 case 'D':
205                         arg_directory = optarg;
206                         break;
207
208                 case ARG_HEADER:
209                         arg_action = ACTION_PRINT_HEADER;
210                         break;
211
212                 case ARG_SETUP_KEYS:
213                         arg_action = ACTION_SETUP_KEYS;
214                         break;
215
216                 case ARG_VERIFY:
217                         arg_action = ACTION_VERIFY;
218                         break;
219
220                 case ARG_VERIFY_SEED:
221                         arg_action = ACTION_VERIFY;
222                         arg_verify_seed = optarg;
223                         break;
224
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
275                 case '?':
276                         return -EINVAL;
277
278                 default:
279                         log_error("Unknown option code %c", c);
280                         return -EINVAL;
281                 }
282         }
283
284         if (arg_follow && !arg_no_tail && arg_lines < 0)
285                 arg_lines = 10;
286
287         return 1;
288 }
289
290 static 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
305 static int generate_new_id128(void) {
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
333 static int add_matches(sd_journal *j, char **args) {
334         char **i;
335         int r;
336
337         assert(j);
338
339         STRV_FOREACH(i, args) {
340
341                 if (streq(*i, "+"))
342                         r = sd_journal_add_disjunction(j);
343                 else if (path_is_absolute(*i)) {
344                         char *p, *t = NULL;
345                         const char *path;
346                         struct stat st;
347
348                         p = canonicalize_file_name(*i);
349                         path = p ? p : *i;
350
351                         if (stat(path, &st) < 0)  {
352                                 free(p);
353                                 log_error("Couldn't stat file: %m");
354                                 return -errno;
355                         }
356
357                         if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
358                                 t = strappend("_EXE=", path);
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 {
364                                 free(p);
365                                 log_error("File is not a device node, regular file or is not executable: %s", *i);
366                                 return -EINVAL;
367                         }
368
369                         free(p);
370
371                         if (!t)
372                                 return log_oom();
373
374                         r = sd_journal_add_match(j, t, 0);
375                         free(t);
376                 } else
377                         r = sd_journal_add_match(j, *i, 0);
378
379                 if (r < 0) {
380                         log_error("Failed to add match '%s': %s", *i, strerror(-r));
381                         return r;
382                 }
383         }
384
385         return 0;
386 }
387
388 static 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
393         assert(j);
394
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
414 static 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
439 static 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"
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"
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
577 finish:
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
594 static 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
604 #ifdef HAVE_GCRYPT
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);
607 #endif
608
609                 k = journal_file_verify(f, arg_verify_seed);
610                 if (k == -EINVAL) {
611                         /* If the seed was invalid give up right-away. */
612                         return k;
613                 } else if (k < 0) {
614                         log_warning("FAIL: %s (%s)", f->path, strerror(-k));
615                         r = k;
616                 } else
617                         log_info("PASS: %s", f->path);
618         }
619
620         return r;
621 }
622
623 int main(int argc, char *argv[]) {
624         int r;
625         sd_journal *j = NULL;
626         unsigned line = 0;
627         bool need_seek = false;
628         sd_id128_t previous_boot_id;
629         bool previous_boot_id_valid = false;
630         bool have_pager;
631
632         log_parse_environment();
633         log_open();
634
635         r = parse_argv(argc, argv);
636         if (r <= 0)
637                 goto finish;
638
639         if (arg_action == ACTION_NEW_ID128) {
640                 r = generate_new_id128();
641                 goto finish;
642         }
643
644         if (arg_action == ACTION_SETUP_KEYS) {
645                 r = setup_keys();
646                 goto finish;
647         }
648
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
659         if (arg_action == ACTION_VERIFY) {
660                 r = verify(j);
661                 goto finish;
662         }
663
664         if (arg_action == ACTION_PRINT_HEADER) {
665                 journal_print_header(j);
666                 r = 0;
667                 goto finish;
668         }
669
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
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
683         r = add_priorities(j);
684         if (r < 0)
685                 goto finish;
686
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
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);
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                 }
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;
728         }
729
730         on_tty();
731         have_pager = !arg_no_pager && !arg_follow && pager_open();
732
733         if (arg_output == OUTPUT_JSON) {
734                 fputc('[', stdout);
735                 fflush(stdout);
736         }
737
738         for (;;) {
739                 for (;;) {
740                         sd_id128_t boot_id;
741                         int flags =
742                                 arg_show_all * OUTPUT_SHOW_ALL |
743                                 have_pager * OUTPUT_FULL_WIDTH |
744                                 on_tty() * OUTPUT_COLOR;
745
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                                 }
752                         }
753
754                         if (r == 0)
755                                 break;
756
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
767                         line ++;
768
769                         r = output_journal(j, arg_output, line, 0, flags);
770                         if (r < 0)
771                                 goto finish;
772
773                         need_seek = true;
774                 }
775
776                 if (!arg_follow)
777                         break;
778
779                 r = sd_journal_wait(j, (uint64_t) -1);
780                 if (r < 0) {
781                         log_error("Couldn't wait for log event: %s", strerror(-r));
782                         goto finish;
783                 }
784         }
785
786         if (arg_output == OUTPUT_JSON)
787                 fputs("\n]\n", stdout);
788
789 finish:
790         if (j)
791                 sd_journal_close(j);
792
793         pager_close();
794
795         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
796 }