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