]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journalctl.c
dbus: Do send out "replies" to signals
[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
a9cdc94f 22#include <locale.h>
87d2c1ff
LP
23#include <fcntl.h>
24#include <errno.h>
25#include <stddef.h>
3fbf9cbb
LP
26#include <string.h>
27#include <stdio.h>
28#include <unistd.h>
29#include <stdlib.h>
50f20cfd 30#include <sys/poll.h>
72f59706 31#include <time.h>
0d43c694 32#include <getopt.h>
585314e8 33#include <signal.h>
50940700 34#include <sys/stat.h>
f982e6f7
LP
35#include <sys/ioctl.h>
36#include <linux/fs.h>
87d2c1ff 37
4468addc
LP
38#ifdef HAVE_ACL
39#include <sys/acl.h>
478c8269 40#include "acl-util.h"
4468addc
LP
41#endif
42
81527be1
LP
43#include <systemd/sd-journal.h>
44
3fbf9cbb 45#include "log.h"
7199aa96 46#include "logs-show.h"
72f59706 47#include "util.h"
e5124088 48#include "path-util.h"
0d43c694
LP
49#include "build.h"
50#include "pager.h"
86aa7ba4 51#include "logs-show.h"
a963990f 52#include "strv.h"
dca6219e 53#include "journal-internal.h"
7560fffc 54#include "journal-def.h"
0284adc6 55#include "journal-verify.h"
4da416aa 56#include "journal-authenticate.h"
f6a971bc 57#include "journal-qrcode.h"
4da416aa 58#include "fsprg.h"
c3f60ec5 59#include "unit-name.h"
d4205751 60#include "catalog.h"
7560fffc 61
baed47c3 62#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
250d54b5 63
df50185b 64static OutputMode arg_output = OUTPUT_SHORT;
1b12a7b5 65static bool arg_pager_end = false;
72f59706 66static bool arg_follow = false;
e3657ecd 67static bool arg_full = false;
cd4b13e0 68static bool arg_all = false;
0d43c694 69static bool arg_no_pager = false;
67e04a48 70static int arg_lines = -1;
e91af489 71static bool arg_no_tail = false;
43673799 72static bool arg_quiet = false;
9e8a535f 73static bool arg_merge = false;
59cea26a 74static bool arg_this_boot = false;
8f14c832 75static const char *arg_cursor = NULL;
a963990f 76static const char *arg_directory = NULL;
941e990d 77static int arg_priorities = 0xFF;
baed47c3 78static const char *arg_verify_key = NULL;
feb12d3e 79#ifdef HAVE_GCRYPT
baed47c3 80static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
feb12d3e 81#endif
cfbc22ab
LP
82static usec_t arg_since, arg_until;
83static bool arg_since_set = false, arg_until_set = false;
c3f60ec5 84static const char *arg_unit = NULL;
7199aa96 85static bool arg_unit_system;
3c1668da 86static const char *arg_field = NULL;
d4205751 87static bool arg_catalog = false;
d89d6c86 88static bool arg_reverse = false;
50f20cfd 89
7560fffc
LP
90static enum {
91 ACTION_SHOW,
92 ACTION_NEW_ID128,
93 ACTION_PRINT_HEADER,
beec0085 94 ACTION_SETUP_KEYS,
a1a03e30
LP
95 ACTION_VERIFY,
96 ACTION_DISK_USAGE,
d4205751 97 ACTION_LIST_CATALOG,
54b7254c 98 ACTION_DUMP_CATALOG,
d4205751 99 ACTION_UPDATE_CATALOG
7560fffc
LP
100} arg_action = ACTION_SHOW;
101
0d43c694
LP
102static int help(void) {
103
cd4b13e0 104 printf("%s [OPTIONS...] [MATCHES...]\n\n"
15119c16
LP
105 "Query the journal.\n\n"
106 "Flags:\n"
cfbc22ab
LP
107 " --since=DATE Start showing entries newer or of the specified date\n"
108 " --until=DATE Stop showing entries older or of the specified date\n"
cd4b13e0 109 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
c3f60ec5
LP
110 " -b --this-boot Show data only from current boot\n"
111 " -u --unit=UNIT Show data only from the specified unit\n"
ffa7cd15 112 " --user-unit=UNIT Show data only from the specified user session unit\n"
cd4b13e0 113 " -p --priority=RANGE Show only messages within the specified priority range\n"
1b12a7b5 114 " -e --pager-end Immediately jump to end of the journal in the pager\n"
baed47c3 115 " -f --follow Follow journal\n"
1705594f 116 " -n --lines[=INTEGER] Number of journal entries to show\n"
baed47c3 117 " --no-tail Show all lines, even in follow mode\n"
d89d6c86 118 " -r --reverse Show the newest entries first\n"
baed47c3 119 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
48383c25 120 " verbose, export, json, json-pretty, json-sse, cat)\n"
d4205751 121 " -x --catalog Add message explanations where available\n"
e3657ecd 122 " --full Do not ellipsize fields\n"
c3f60ec5 123 " -a --all Show all fields, including long and unprintable\n"
baed47c3 124 " -q --quiet Don't show privilege warning\n"
c3f60ec5 125 " --no-pager Do not pipe output into a pager\n"
9e8a535f 126 " -m --merge Show entries from all available journals\n"
baed47c3 127 " -D --directory=PATH Show journal files from directory\n"
15119c16
LP
128#ifdef HAVE_GCRYPT
129 " --interval=TIME Time interval for changing the FSS sealing key\n"
130 " --verify-key=KEY Specify FSS verification key\n"
131#endif
132 "\nCommands:\n"
133 " -h --help Show this help\n"
134 " --version Show package version\n"
baed47c3
LP
135 " --new-id128 Generate a new 128 Bit ID\n"
136 " --header Show journal header information\n"
a1a03e30 137 " --disk-usage Show total disk usage\n"
15119c16 138 " -F --field=FIELD List all values a certain field takes\n"
d4205751 139 " --list-catalog Show message IDs of all entries in the message catalog\n"
54b7254c 140 " --dump-catalog Show entries in the message catalog\n"
d4205751 141 " --update-catalog Update the message catalog database\n"
feb12d3e 142#ifdef HAVE_GCRYPT
baed47c3 143 " --setup-keys Generate new FSS key pair\n"
baed47c3 144 " --verify Verify journal file consistency\n"
feb12d3e
LP
145#endif
146 , program_invocation_short_name);
0d43c694
LP
147
148 return 0;
149}
150
151static int parse_argv(int argc, char *argv[]) {
152
153 enum {
154 ARG_VERSION = 0x100,
e91af489 155 ARG_NO_PAGER,
55ee336c 156 ARG_NO_TAIL,
dca6219e 157 ARG_NEW_ID128,
7560fffc 158 ARG_HEADER,
e3657ecd 159 ARG_FULL,
beec0085 160 ARG_SETUP_KEYS,
baed47c3 161 ARG_INTERVAL,
4da416aa 162 ARG_VERIFY,
a1a03e30 163 ARG_VERIFY_KEY,
cfbc22ab
LP
164 ARG_DISK_USAGE,
165 ARG_SINCE,
d4205751 166 ARG_UNTIL,
ffa7cd15 167 ARG_USER_UNIT,
d4205751 168 ARG_LIST_CATALOG,
54b7254c 169 ARG_DUMP_CATALOG,
d4205751 170 ARG_UPDATE_CATALOG
0d43c694
LP
171 };
172
173 static const struct option options[] = {
baed47c3
LP
174 { "help", no_argument, NULL, 'h' },
175 { "version" , no_argument, NULL, ARG_VERSION },
176 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1b12a7b5 177 { "pager-end", no_argument, NULL, 'e' },
baed47c3
LP
178 { "follow", no_argument, NULL, 'f' },
179 { "output", required_argument, NULL, 'o' },
180 { "all", no_argument, NULL, 'a' },
e3657ecd 181 { "full", no_argument, NULL, ARG_FULL },
1705594f 182 { "lines", optional_argument, NULL, 'n' },
baed47c3
LP
183 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
184 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
185 { "quiet", no_argument, NULL, 'q' },
9e8a535f 186 { "merge", no_argument, NULL, 'm' },
baed47c3
LP
187 { "this-boot", no_argument, NULL, 'b' },
188 { "directory", required_argument, NULL, 'D' },
189 { "header", no_argument, NULL, ARG_HEADER },
71c01596 190 { "priority", required_argument, NULL, 'p' },
baed47c3
LP
191 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
192 { "interval", required_argument, NULL, ARG_INTERVAL },
193 { "verify", no_argument, NULL, ARG_VERIFY },
194 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
a1a03e30 195 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
ad9eafab 196 { "cursor", required_argument, NULL, 'c' },
cfbc22ab
LP
197 { "since", required_argument, NULL, ARG_SINCE },
198 { "until", required_argument, NULL, ARG_UNTIL },
c3f60ec5 199 { "unit", required_argument, NULL, 'u' },
7199aa96 200 { "user-unit", required_argument, NULL, ARG_USER_UNIT },
15119c16 201 { "field", required_argument, NULL, 'F' },
d4205751
LP
202 { "catalog", no_argument, NULL, 'x' },
203 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
54b7254c 204 { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
d4205751 205 { "update-catalog",no_argument, NULL, ARG_UPDATE_CATALOG },
d89d6c86 206 { "reverse", no_argument, NULL, 'r' },
baed47c3 207 { NULL, 0, NULL, 0 }
0d43c694
LP
208 };
209
2100675e 210 int c, r;
0d43c694
LP
211
212 assert(argc >= 0);
213 assert(argv);
214
1b12a7b5 215 while ((c = getopt_long(argc, argv, "hefo:an::qmbD:p:c:u:F:xr", options, NULL)) >= 0) {
0d43c694
LP
216
217 switch (c) {
218
219 case 'h':
220 help();
221 return 0;
222
223 case ARG_VERSION:
224 puts(PACKAGE_STRING);
0d43c694
LP
225 puts(SYSTEMD_FEATURES);
226 return 0;
227
228 case ARG_NO_PAGER:
229 arg_no_pager = true;
230 break;
231
1b12a7b5
HH
232 case 'e':
233 arg_pager_end = true;
fe59e38b
LP
234
235 if (arg_lines < 0)
236 arg_lines = 1000;
237
1b12a7b5
HH
238 break;
239
0d43c694
LP
240 case 'f':
241 arg_follow = true;
242 break;
243
244 case 'o':
1705594f 245 arg_output = output_mode_from_string(optarg);
df50185b 246 if (arg_output < 0) {
edfb521a 247 log_error("Unknown output format '%s'.", optarg);
0d43c694
LP
248 return -EINVAL;
249 }
df50185b 250
edfb521a
ZJS
251 if (arg_output == OUTPUT_EXPORT ||
252 arg_output == OUTPUT_JSON ||
253 arg_output == OUTPUT_JSON_PRETTY ||
254 arg_output == OUTPUT_JSON_SSE ||
255 arg_output == OUTPUT_CAT)
256 arg_quiet = true;
257
0d43c694
LP
258 break;
259
e3657ecd
ZJS
260 case ARG_FULL:
261 arg_full = true;
262 break;
263
0d43c694 264 case 'a':
cd4b13e0 265 arg_all = true;
0d43c694
LP
266 break;
267
2100675e 268 case 'n':
1705594f 269 if (optarg) {
67e04a48
ZJS
270 r = safe_atoi(optarg, &arg_lines);
271 if (r < 0 || arg_lines < 0) {
1705594f
LP
272 log_error("Failed to parse lines '%s'", optarg);
273 return -EINVAL;
274 }
96088db0
LP
275 } else {
276 int n;
277
278 /* Hmm, no argument? Maybe the next
279 * word on the command line is
280 * supposed to be the argument? Let's
281 * see if there is one, and is
282 * parsable as a positive
283 * integer... */
284
285 if (optind < argc &&
286 safe_atoi(argv[optind], &n) >= 0 &&
287 n >= 0) {
288
289 arg_lines = n;
290 optind++;
291 } else
292 arg_lines = 10;
293 }
1705594f 294
2100675e
LP
295 break;
296
e91af489
LP
297 case ARG_NO_TAIL:
298 arg_no_tail = true;
299 break;
300
39f7f5c1 301 case ARG_NEW_ID128:
7560fffc 302 arg_action = ACTION_NEW_ID128;
55ee336c
LP
303 break;
304
43673799
LP
305 case 'q':
306 arg_quiet = true;
490e567d 307 break;
43673799 308
9e8a535f
LP
309 case 'm':
310 arg_merge = true;
2bd3c38a
LP
311 break;
312
59cea26a
LP
313 case 'b':
314 arg_this_boot = true;
315 break;
316
a963990f
LP
317 case 'D':
318 arg_directory = optarg;
319 break;
320
8f14c832
LP
321 case 'c':
322 arg_cursor = optarg;
323 break;
324
dca6219e 325 case ARG_HEADER:
7560fffc
LP
326 arg_action = ACTION_PRINT_HEADER;
327 break;
328
feb12d3e
LP
329 case ARG_VERIFY:
330 arg_action = ACTION_VERIFY;
331 break;
332
a1a03e30
LP
333 case ARG_DISK_USAGE:
334 arg_action = ACTION_DISK_USAGE;
335 break;
336
feb12d3e 337#ifdef HAVE_GCRYPT
7560fffc
LP
338 case ARG_SETUP_KEYS:
339 arg_action = ACTION_SETUP_KEYS;
dca6219e
LP
340 break;
341
beec0085 342
baed47c3 343 case ARG_VERIFY_KEY:
4da416aa 344 arg_action = ACTION_VERIFY;
baed47c3 345 arg_verify_key = optarg;
9e8a535f 346 arg_merge = false;
4da416aa
LP
347 break;
348
baed47c3
LP
349 case ARG_INTERVAL:
350 r = parse_usec(optarg, &arg_interval);
351 if (r < 0 || arg_interval <= 0) {
352 log_error("Failed to parse sealing key change interval: %s", optarg);
14d10188
LP
353 return -EINVAL;
354 }
355 break;
feb12d3e
LP
356#else
357 case ARG_SETUP_KEYS:
358 case ARG_VERIFY_KEY:
359 case ARG_INTERVAL:
360 log_error("Forward-secure sealing not available.");
361 return -ENOTSUP;
362#endif
14d10188 363
941e990d
LP
364 case 'p': {
365 const char *dots;
366
367 dots = strstr(optarg, "..");
368 if (dots) {
369 char *a;
370 int from, to, i;
371
372 /* a range */
373 a = strndup(optarg, dots - optarg);
374 if (!a)
375 return log_oom();
376
377 from = log_level_from_string(a);
378 to = log_level_from_string(dots + 2);
379 free(a);
380
381 if (from < 0 || to < 0) {
382 log_error("Failed to parse log level range %s", optarg);
383 return -EINVAL;
384 }
385
386 arg_priorities = 0;
387
388 if (from < to) {
389 for (i = from; i <= to; i++)
390 arg_priorities |= 1 << i;
391 } else {
392 for (i = to; i <= from; i++)
393 arg_priorities |= 1 << i;
394 }
395
396 } else {
397 int p, i;
398
399 p = log_level_from_string(optarg);
400 if (p < 0) {
401 log_error("Unknown log level %s", optarg);
402 return -EINVAL;
403 }
404
405 arg_priorities = 0;
406
407 for (i = 0; i <= p; i++)
408 arg_priorities |= 1 << i;
409 }
410
411 break;
412 }
413
cfbc22ab
LP
414 case ARG_SINCE:
415 r = parse_timestamp(optarg, &arg_since);
416 if (r < 0) {
417 log_error("Failed to parse timestamp: %s", optarg);
418 return -EINVAL;
419 }
420 arg_since_set = true;
421 break;
422
423 case ARG_UNTIL:
424 r = parse_timestamp(optarg, &arg_until);
425 if (r < 0) {
426 log_error("Failed to parse timestamp: %s", optarg);
427 return -EINVAL;
428 }
429 arg_until_set = true;
430 break;
431
7199aa96 432 case 'u':
ffa7cd15 433 arg_unit = optarg;
7199aa96 434 arg_unit_system = true;
ffa7cd15
DW
435 break;
436
7199aa96 437 case ARG_USER_UNIT:
c3f60ec5 438 arg_unit = optarg;
7199aa96 439 arg_unit_system = false;
c3f60ec5
LP
440 break;
441
0d43c694
LP
442 case '?':
443 return -EINVAL;
444
15119c16
LP
445 case 'F':
446 arg_field = optarg;
447 break;
448
d4205751
LP
449 case 'x':
450 arg_catalog = true;
451 break;
452
453 case ARG_LIST_CATALOG:
454 arg_action = ACTION_LIST_CATALOG;
455 break;
456
54b7254c
ZJS
457 case ARG_DUMP_CATALOG:
458 arg_action = ACTION_DUMP_CATALOG;
459 break;
460
d4205751
LP
461 case ARG_UPDATE_CATALOG:
462 arg_action = ACTION_UPDATE_CATALOG;
463 break;
464
d89d6c86
LN
465 case 'r':
466 arg_reverse = true;
467 break;
468
0d43c694
LP
469 default:
470 log_error("Unknown option code %c", c);
471 return -EINVAL;
472 }
473 }
474
67e04a48 475 if (arg_follow && !arg_no_tail && arg_lines < 0)
e91af489
LP
476 arg_lines = 10;
477
3ba09ee8 478 if (arg_since_set && arg_until_set && arg_since > arg_until) {
cfbc22ab
LP
479 log_error("--since= must be before --until=.");
480 return -EINVAL;
481 }
482
483 if (arg_cursor && arg_since_set) {
484 log_error("Please specify either --since= or --cursor=, not both.");
485 return -EINVAL;
486 }
487
d89d6c86
LN
488 if (arg_follow && arg_reverse) {
489 log_error("Please specify either --reverse= or --follow=, not both.");
490 return -EINVAL;
491 }
492
0d43c694
LP
493 return 1;
494}
495
39f7f5c1 496static int generate_new_id128(void) {
55ee336c
LP
497 sd_id128_t id;
498 int r;
499 unsigned i;
500
501 r = sd_id128_randomize(&id);
502 if (r < 0) {
503 log_error("Failed to generate ID: %s", strerror(-r));
504 return r;
505 }
506
507 printf("As string:\n"
508 SD_ID128_FORMAT_STR "\n\n"
509 "As UUID:\n"
510 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
511 "As macro:\n"
d489071f 512 "#define MESSAGE_XYZ SD_ID128_MAKE(",
55ee336c
LP
513 SD_ID128_FORMAT_VAL(id),
514 SD_ID128_FORMAT_VAL(id));
55ee336c
LP
515 for (i = 0; i < 16; i++)
516 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
d489071f 517 fputs(")\n\n", stdout);
55ee336c 518
d489071f
ZJS
519 printf("As Python constant:\n"
520 ">>> import uuid\n"
521 ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
522 SD_ID128_FORMAT_VAL(id));
55ee336c
LP
523
524 return 0;
525}
526
a963990f
LP
527static int add_matches(sd_journal *j, char **args) {
528 char **i;
59cea26a 529
a963990f 530 assert(j);
59cea26a 531
a963990f 532 STRV_FOREACH(i, args) {
52aeb63c 533 int r;
59cea26a 534
cbdca852
LP
535 if (streq(*i, "+"))
536 r = sd_journal_add_disjunction(j);
537 else if (path_is_absolute(*i)) {
52aeb63c 538 char _cleanup_free_ *p, *t = NULL;
e5124088 539 const char *path;
a963990f 540 struct stat st;
e5124088 541
a963990f
LP
542 p = canonicalize_file_name(*i);
543 path = p ? p : *i;
e5124088
LP
544
545 if (stat(path, &st) < 0) {
e5124088 546 log_error("Couldn't stat file: %m");
a963990f 547 return -errno;
e5124088
LP
548 }
549
b6a34514 550 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
e5124088 551 t = strappend("_EXE=", path);
b6a34514
LP
552 else if (S_ISCHR(st.st_mode))
553 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
554 else if (S_ISBLK(st.st_mode))
555 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
556 else {
b6a34514 557 log_error("File is not a device node, regular file or is not executable: %s", *i);
a963990f 558 return -EINVAL;
50940700 559 }
e5124088 560
b6a34514
LP
561 if (!t)
562 return log_oom();
563
564 r = sd_journal_add_match(j, t, 0);
e5124088 565 } else
cbdca852 566 r = sd_journal_add_match(j, *i, 0);
e5124088 567
de7b95cd 568 if (r < 0) {
cbdca852 569 log_error("Failed to add match '%s': %s", *i, strerror(-r));
a963990f 570 return r;
de7b95cd
LP
571 }
572 }
573
a963990f
LP
574 return 0;
575}
576
577static int add_this_boot(sd_journal *j) {
578 char match[9+32+1] = "_BOOT_ID=";
579 sd_id128_t boot_id;
580 int r;
581
941e990d
LP
582 assert(j);
583
a963990f
LP
584 if (!arg_this_boot)
585 return 0;
586
587 r = sd_id128_get_boot(&boot_id);
588 if (r < 0) {
589 log_error("Failed to get boot id: %s", strerror(-r));
590 return r;
591 }
592
593 sd_id128_to_string(boot_id, match + 9);
594 r = sd_journal_add_match(j, match, strlen(match));
595 if (r < 0) {
596 log_error("Failed to add match: %s", strerror(-r));
597 return r;
598 }
599
600 return 0;
601}
602
c3f60ec5 603static int add_unit(sd_journal *j) {
4750fade 604 _cleanup_free_ char *u = NULL;
c3f60ec5
LP
605 int r;
606
607 assert(j);
608
609 if (isempty(arg_unit))
610 return 0;
611
612 u = unit_name_mangle(arg_unit);
613 if (!u)
614 return log_oom();
615
7199aa96
ZJS
616 if (arg_unit_system)
617 r = add_matches_for_unit(j, u);
618 else
619 r = add_matches_for_user_unit(j, u, getuid());
620 if (r < 0)
c3f60ec5 621 return r;
c3f60ec5
LP
622
623 return 0;
624}
625
941e990d
LP
626static int add_priorities(sd_journal *j) {
627 char match[] = "PRIORITY=0";
628 int i, r;
629
630 assert(j);
631
632 if (arg_priorities == 0xFF)
633 return 0;
634
635 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
636 if (arg_priorities & (1 << i)) {
637 match[sizeof(match)-2] = '0' + i;
638
941e990d
LP
639 r = sd_journal_add_match(j, match, strlen(match));
640 if (r < 0) {
641 log_error("Failed to add match: %s", strerror(-r));
642 return r;
643 }
644 }
645
646 return 0;
647}
648
7560fffc
LP
649static int setup_keys(void) {
650#ifdef HAVE_GCRYPT
651 size_t mpk_size, seed_size, state_size, i;
652 uint8_t *mpk, *seed, *state;
653 ssize_t l;
f982e6f7 654 int fd = -1, r, attr = 0;
7560fffc
LP
655 sd_id128_t machine, boot;
656 char *p = NULL, *k = NULL;
baed47c3 657 struct FSSHeader h;
14d10188 658 uint64_t n;
7560fffc
LP
659
660 r = sd_id128_get_machine(&machine);
661 if (r < 0) {
662 log_error("Failed to get machine ID: %s", strerror(-r));
663 return r;
664 }
665
666 r = sd_id128_get_boot(&boot);
667 if (r < 0) {
668 log_error("Failed to get boot ID: %s", strerror(-r));
669 return r;
670 }
671
baed47c3 672 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
7560fffc
LP
673 SD_ID128_FORMAT_VAL(machine)) < 0)
674 return log_oom();
675
676 if (access(p, F_OK) >= 0) {
f7fab8a5 677 log_error("Sealing key file %s exists already.", p);
7560fffc
LP
678 r = -EEXIST;
679 goto finish;
680 }
681
baed47c3 682 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
7560fffc
LP
683 SD_ID128_FORMAT_VAL(machine)) < 0) {
684 r = log_oom();
685 goto finish;
686 }
687
688 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
689 mpk = alloca(mpk_size);
690
691 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
692 seed = alloca(seed_size);
693
694 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
695 state = alloca(state_size);
696
697 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
698 if (fd < 0) {
699 log_error("Failed to open /dev/random: %m");
700 r = -errno;
701 goto finish;
702 }
703
704 log_info("Generating seed...");
705 l = loop_read(fd, seed, seed_size, true);
706 if (l < 0 || (size_t) l != seed_size) {
707 log_error("Failed to read random seed: %s", strerror(EIO));
708 r = -EIO;
709 goto finish;
710 }
711
712 log_info("Generating key pair...");
713 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
714
baed47c3 715 log_info("Generating sealing key...");
7560fffc
LP
716 FSPRG_GenState0(state, mpk, seed, seed_size);
717
baed47c3
LP
718 assert(arg_interval > 0);
719
7560fffc 720 n = now(CLOCK_REALTIME);
baed47c3 721 n /= arg_interval;
7560fffc
LP
722
723 close_nointr_nofail(fd);
724 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
725 if (fd < 0) {
726 log_error("Failed to open %s: %m", k);
727 r = -errno;
728 goto finish;
729 }
730
f982e6f7
LP
731 /* Enable secure remove, exclusion from dump, synchronous
732 * writing and in-place updating */
733 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
734 log_warning("FS_IOC_GETFLAGS failed: %m");
735
736 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
737
738 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
739 log_warning("FS_IOC_SETFLAGS failed: %m");
740
7560fffc
LP
741 zero(h);
742 memcpy(h.signature, "KSHHRHLP", 8);
743 h.machine_id = machine;
744 h.boot_id = boot;
745 h.header_size = htole64(sizeof(h));
baed47c3
LP
746 h.start_usec = htole64(n * arg_interval);
747 h.interval_usec = htole64(arg_interval);
748 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
749 h.fsprg_state_size = htole64(state_size);
7560fffc
LP
750
751 l = loop_write(fd, &h, sizeof(h), false);
752 if (l < 0 || (size_t) l != sizeof(h)) {
753 log_error("Failed to write header: %s", strerror(EIO));
754 r = -EIO;
755 goto finish;
756 }
757
758 l = loop_write(fd, state, state_size, false);
759 if (l < 0 || (size_t) l != state_size) {
760 log_error("Failed to write state: %s", strerror(EIO));
761 r = -EIO;
762 goto finish;
763 }
764
765 if (link(k, p) < 0) {
766 log_error("Failed to link file: %m");
767 r = -errno;
768 goto finish;
769 }
770
8481248b 771 if (on_tty()) {
7560fffc
LP
772 fprintf(stderr,
773 "\n"
baed47c3 774 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
c05276f2
LP
775 "the following local file. This key file is automatically updated when the\n"
776 "sealing key is advanced. It should not be used on multiple hosts.\n"
7560fffc
LP
777 "\n"
778 "\t%s\n"
779 "\n"
baed47c3
LP
780 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
781 "at a safe location and should not be saved locally on disk.\n"
7560fffc
LP
782 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
783 fflush(stderr);
784 }
785 for (i = 0; i < seed_size; i++) {
786 if (i > 0 && i % 3 == 0)
787 putchar('-');
788 printf("%02x", ((uint8_t*) seed)[i]);
789 }
790
baed47c3
LP
791 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
792
8481248b 793 if (on_tty()) {
f6a971bc 794 char tsb[FORMAT_TIMESPAN_MAX], *hn;
7560fffc 795
baed47c3
LP
796 fprintf(stderr,
797 ANSI_HIGHLIGHT_OFF "\n"
798 "The sealing key is automatically changed every %s.\n",
799 format_timespan(tsb, sizeof(tsb), arg_interval));
f6a971bc
LP
800
801 hn = gethostname_malloc();
802
803 if (hn) {
804 hostname_cleanup(hn);
adac1c93 805 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
f6a971bc 806 } else
adac1c93 807 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
f6a971bc
LP
808
809#ifdef HAVE_QRENCODE
cf5a3432 810 /* If this is not an UTF-8 system don't print any QR codes */
09017585 811 if (is_locale_utf8()) {
cf5a3432
LP
812 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
813 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
814 }
f6a971bc
LP
815#endif
816 free(hn);
baed47c3 817 }
7560fffc
LP
818
819 r = 0;
820
821finish:
822 if (fd >= 0)
823 close_nointr_nofail(fd);
824
825 if (k) {
826 unlink(k);
827 free(k);
828 }
829
830 free(p);
831
832 return r;
833#else
feb12d3e
LP
834 log_error("Forward-secure sealing not available.");
835 return -ENOTSUP;
7560fffc
LP
836#endif
837}
838
beec0085
LP
839static int verify(sd_journal *j) {
840 int r = 0;
841 Iterator i;
842 JournalFile *f;
843
844 assert(j);
845
cedb42bb
LP
846 log_show_color(true);
847
beec0085
LP
848 HASHMAP_FOREACH(f, j->files, i) {
849 int k;
2a7b539a 850 usec_t first, validated, last;
beec0085 851
56e81f7c 852#ifdef HAVE_GCRYPT
feb12d3e 853 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
cedb42bb 854 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
56e81f7c 855#endif
4da416aa 856
2a7b539a 857 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
56e81f7c 858 if (k == -EINVAL) {
baed47c3 859 /* If the key was invalid give up right-away. */
56e81f7c
LP
860 return k;
861 } else if (k < 0) {
beec0085 862 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
56e81f7c 863 r = k;
6c7be122
LP
864 } else {
865 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
beec0085 866 log_info("PASS: %s", f->path);
6c7be122 867
c0ca7aee 868 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
2a7b539a 869 if (validated > 0) {
c0ca7aee 870 log_info("=> Validated from %s to %s, final %s entries not sealed.",
2a7b539a
LP
871 format_timestamp(a, sizeof(a), first),
872 format_timestamp(b, sizeof(b), validated),
873 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
874 } else if (last > 0)
c0ca7aee 875 log_info("=> No sealing yet, %s of entries not sealed.",
2a7b539a 876 format_timespan(c, sizeof(c), last - first));
c0ca7aee
LP
877 else
878 log_info("=> No sealing yet, no entries in file.");
879 }
6c7be122 880 }
beec0085
LP
881 }
882
883 return r;
884}
885
15804ceb 886#ifdef HAVE_ACL
6fe391c5
ZJS
887static int access_check_var_log_journal(sd_journal *j) {
888 _cleanup_strv_free_ char **g = NULL;
889 bool have_access;
890 int r;
891
892 assert(j);
893
894 have_access = in_group("systemd-journal") > 0;
895
896 if (!have_access) {
897 /* Let's enumerate all groups from the default ACL of
898 * the directory, which generally should allow access
899 * to most journal files too */
900 r = search_acl_groups(&g, "/var/log/journal/", &have_access);
901 if (r < 0)
902 return r;
15804ceb
LP
903 }
904
6fe391c5 905 if (!have_access) {
4468addc 906
6fe391c5
ZJS
907 if (strv_isempty(g))
908 log_notice("Hint: You are currently not seeing messages from other users and\n"
909 "the system. Users in the group 'systemd-journal' can see all messages.\n"
910 "Pass -q to turn this notice off.");
911 else {
912 _cleanup_free_ char *s = NULL;
4468addc 913
6fe391c5 914 r = strv_extend(&g, "systemd-journal");
478c8269 915 if (r < 0)
6fe391c5
ZJS
916 return log_oom();
917
918 strv_sort(g);
919 strv_uniq(g);
920
921 s = strv_join(g, "', '");
922 if (!s)
923 return log_oom();
924
925 log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
926 "Users in the groups '%s' can see all messages.\n"
927 "Pass -q to turn this notice off.", s);
4468addc 928 }
6fe391c5 929 }
4468addc 930
6fe391c5
ZJS
931 return 0;
932}
933#endif
4468addc 934
6fe391c5
ZJS
935static int access_check(sd_journal *j) {
936 uint64_t eacces = EACCES, *code;
937 Iterator it;
938 int r = 0;
4468addc 939
6fe391c5
ZJS
940 assert(j);
941 assert(j->errors);
942 assert(j->files);
4468addc 943
6fe391c5
ZJS
944 if (set_isempty(j->errors)) {
945 if (hashmap_isempty(j->files))
946 log_info("No journal files were found.");
947 return 0;
948 }
4468addc 949
6fe391c5
ZJS
950 if (!set_contains(j->errors, &eacces)) {
951#ifdef HAVE_ACL
952 /* If /var/log/journal doesn't even exist,
953 unprivileged users have no access at all */
954 if (access("/var/log/journal", F_OK) < 0 &&
955 geteuid() != 0 &&
956 in_group("systemd-journal") <= 0) {
957 log_error("Unprivileged users can't see messages unless persistent log storage\n"
958 "is enabled. Users in the group 'systemd-journal' can always see messages.");
959 return -EACCES;
960 }
4468addc 961
6fe391c5
ZJS
962 /* If /var/log/journal exists, try to pring a nice
963 notice if the user lacks access to it */
964 if (!arg_quiet && geteuid() != 0) {
965 r = access_check_var_log_journal(j);
966 if (r < 0)
967 return r;
4468addc 968 }
15804ceb 969#else
6fe391c5
ZJS
970 if (geteuid() != 0 && in_group("systemd-journal") <= 0)
971 log_error("No access to messages.\n"
972 "Users in the group 'systemd-journal' can see messages.");
15804ceb 973#endif
6fe391c5
ZJS
974 if (hashmap_isempty(j->files)) {
975 log_error("No journal files were opened, due to insufficient permissions.");
976 r = -EACCES;
977 }
978 }
15804ceb 979
6fe391c5
ZJS
980 SET_FOREACH(code, j->errors, it) {
981 int err = -PTR_TO_INT(code);
982 assert(err > 0);
983 if (err != EACCES)
984 log_warning("Error was encountered while opening journal files: %s",
985 strerror(err));
986 }
987
988 log_notice("Hint: run journalctl in debug mode: SYSTEMD_LOG_LEVEL=debug journalct ...");
989
990 return r;
15804ceb
LP
991}
992
a963990f
LP
993int main(int argc, char *argv[]) {
994 int r;
763c7aa2 995 sd_journal _cleanup_journal_close_ *j = NULL;
a963990f 996 bool need_seek = false;
14a65d65 997 sd_id128_t previous_boot_id;
67e04a48
ZJS
998 bool previous_boot_id_valid = false, first_line = true;
999 int n_shown = 0;
a963990f 1000
a9cdc94f 1001 setlocale(LC_ALL, "");
a963990f
LP
1002 log_parse_environment();
1003 log_open();
1004
1005 r = parse_argv(argc, argv);
1006 if (r <= 0)
1007 goto finish;
1008
ed757c0c
LP
1009 signal(SIGWINCH, columns_lines_cache_reset);
1010
7560fffc 1011 if (arg_action == ACTION_NEW_ID128) {
a963990f
LP
1012 r = generate_new_id128();
1013 goto finish;
1014 }
1015
7560fffc
LP
1016 if (arg_action == ACTION_SETUP_KEYS) {
1017 r = setup_keys();
1018 goto finish;
1019 }
1020
54b7254c
ZJS
1021 if (arg_action == ACTION_LIST_CATALOG ||
1022 arg_action == ACTION_DUMP_CATALOG) {
1023 bool oneline = arg_action == ACTION_LIST_CATALOG;
1024 if (optind < argc)
1025 r = catalog_list_items(stdout, oneline, argv + optind);
1026 else
1027 r = catalog_list(stdout, oneline);
83f6936a
LP
1028 if (r < 0)
1029 log_error("Failed to list catalog: %s", strerror(-r));
d4205751
LP
1030 goto finish;
1031 }
1032
1033 if (arg_action == ACTION_UPDATE_CATALOG) {
1034 r = catalog_update();
1035 goto finish;
1036 }
1037
a963990f
LP
1038 if (arg_directory)
1039 r = sd_journal_open_directory(&j, arg_directory, 0);
1040 else
9e8a535f 1041 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
a963990f
LP
1042 if (r < 0) {
1043 log_error("Failed to open journal: %s", strerror(-r));
763c7aa2 1044 return EXIT_FAILURE;
a963990f
LP
1045 }
1046
6fe391c5
ZJS
1047 r = access_check(j);
1048 if (r < 0)
1049 return EXIT_FAILURE;
1050
beec0085
LP
1051 if (arg_action == ACTION_VERIFY) {
1052 r = verify(j);
1053 goto finish;
1054 }
1055
7560fffc 1056 if (arg_action == ACTION_PRINT_HEADER) {
dca6219e 1057 journal_print_header(j);
763c7aa2 1058 return EXIT_SUCCESS;
dca6219e
LP
1059 }
1060
a1a03e30
LP
1061 if (arg_action == ACTION_DISK_USAGE) {
1062 uint64_t bytes;
1063 char sbytes[FORMAT_BYTES_MAX];
1064
1065 r = sd_journal_get_usage(j, &bytes);
1066 if (r < 0)
763c7aa2 1067 return EXIT_FAILURE;
a1a03e30 1068
763c7aa2
ZJS
1069 printf("Journals take up %s on disk.\n",
1070 format_bytes(sbytes, sizeof(sbytes), bytes));
1071 return EXIT_SUCCESS;
a1a03e30
LP
1072 }
1073
a963990f
LP
1074 r = add_this_boot(j);
1075 if (r < 0)
763c7aa2 1076 return EXIT_FAILURE;
a963990f 1077
c3f60ec5
LP
1078 r = add_unit(j);
1079 if (r < 0)
763c7aa2 1080 return EXIT_FAILURE;
c3f60ec5 1081
a963990f
LP
1082 r = add_matches(j, argv + optind);
1083 if (r < 0)
763c7aa2 1084 return EXIT_FAILURE;
a963990f 1085
941e990d
LP
1086 r = add_priorities(j);
1087 if (r < 0)
763c7aa2 1088 return EXIT_FAILURE;
941e990d 1089
67e04a48
ZJS
1090 /* Opening the fd now means the first sd_journal_wait() will actually wait */
1091 r = sd_journal_get_fd(j);
1092 if (r < 0)
763c7aa2 1093 return EXIT_FAILURE;
67e04a48 1094
15119c16
LP
1095 if (arg_field) {
1096 const void *data;
1097 size_t size;
1098
1099 r = sd_journal_query_unique(j, arg_field);
1100 if (r < 0) {
1101 log_error("Failed to query unique data objects: %s", strerror(-r));
763c7aa2 1102 return EXIT_FAILURE;
15119c16
LP
1103 }
1104
1105 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
1106 const void *eq;
1107
67e04a48 1108 if (arg_lines >= 0 && n_shown >= arg_lines)
fd6e8875
LP
1109 break;
1110
15119c16
LP
1111 eq = memchr(data, '=', size);
1112 if (eq)
1113 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
1114 else
1115 printf("%.*s\n", (int) size, (const char*) data);
fd6e8875
LP
1116
1117 n_shown ++;
15119c16
LP
1118 }
1119
763c7aa2 1120 return EXIT_SUCCESS;
15119c16
LP
1121 }
1122
cfbc22ab
LP
1123 if (arg_cursor) {
1124 r = sd_journal_seek_cursor(j, arg_cursor);
08984293 1125 if (r < 0) {
cfbc22ab 1126 log_error("Failed to seek to cursor: %s", strerror(-r));
763c7aa2 1127 return EXIT_FAILURE;
08984293 1128 }
d89d6c86
LN
1129 if (!arg_reverse)
1130 r = sd_journal_next(j);
1131 else
1132 r = sd_journal_previous(j);
08984293 1133
d89d6c86 1134 } else if (arg_since_set && !arg_reverse) {
cfbc22ab 1135 r = sd_journal_seek_realtime_usec(j, arg_since);
8f14c832 1136 if (r < 0) {
cfbc22ab 1137 log_error("Failed to seek to date: %s", strerror(-r));
763c7aa2 1138 return EXIT_FAILURE;
8f14c832 1139 }
8f14c832
LP
1140 r = sd_journal_next(j);
1141
d89d6c86
LN
1142 } else if (arg_until_set && arg_reverse) {
1143 r = sd_journal_seek_realtime_usec(j, arg_until);
1144 if (r < 0) {
1145 log_error("Failed to seek to date: %s", strerror(-r));
763c7aa2 1146 return EXIT_FAILURE;
d89d6c86
LN
1147 }
1148 r = sd_journal_previous(j);
1149
67e04a48 1150 } else if (arg_lines >= 0) {
2100675e
LP
1151 r = sd_journal_seek_tail(j);
1152 if (r < 0) {
1153 log_error("Failed to seek to tail: %s", strerror(-r));
763c7aa2 1154 return EXIT_FAILURE;
2100675e
LP
1155 }
1156
1157 r = sd_journal_previous_skip(j, arg_lines);
8f14c832 1158
d89d6c86
LN
1159 } else if (arg_reverse) {
1160 r = sd_journal_seek_tail(j);
1161 if (r < 0) {
1162 log_error("Failed to seek to tail: %s", strerror(-r));
763c7aa2 1163 return EXIT_FAILURE;
d89d6c86
LN
1164 }
1165
1166 r = sd_journal_previous(j);
1167
2100675e
LP
1168 } else {
1169 r = sd_journal_seek_head(j);
1170 if (r < 0) {
1171 log_error("Failed to seek to head: %s", strerror(-r));
763c7aa2 1172 return EXIT_FAILURE;
2100675e 1173 }
6f003b43
LP
1174
1175 r = sd_journal_next(j);
1176 }
1177
1178 if (r < 0) {
1179 log_error("Failed to iterate through journal: %s", strerror(-r));
763c7aa2 1180 return EXIT_FAILURE;
50f20cfd 1181 }
87d2c1ff 1182
f89a3b6f 1183 if (!arg_no_pager && !arg_follow)
1b12a7b5 1184 pager_open(arg_pager_end);
0d43c694 1185
cfbc22ab
LP
1186 if (!arg_quiet) {
1187 usec_t start, end;
1188 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
1189
1190 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
1191 if (r < 0) {
1192 log_error("Failed to get cutoff: %s", strerror(-r));
1193 goto finish;
1194 }
1195
1196 if (r > 0) {
1197 if (arg_follow)
9048b11f
LP
1198 printf("-- Logs begin at %s. --\n",
1199 format_timestamp(start_buf, sizeof(start_buf), start));
cfbc22ab 1200 else
9048b11f 1201 printf("-- Logs begin at %s, end at %s. --\n",
cfbc22ab
LP
1202 format_timestamp(start_buf, sizeof(start_buf), start),
1203 format_timestamp(end_buf, sizeof(end_buf), end));
1204 }
1205 }
1206
50f20cfd 1207 for (;;) {
67e04a48 1208 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
cfbc22ab
LP
1209 int flags;
1210
6f003b43 1211 if (need_seek) {
99613ec5 1212 if (!arg_reverse)
d89d6c86
LN
1213 r = sd_journal_next(j);
1214 else
1215 r = sd_journal_previous(j);
6f003b43
LP
1216 if (r < 0) {
1217 log_error("Failed to iterate through journal: %s", strerror(-r));
1218 goto finish;
1219 }
0d43c694
LP
1220 }
1221
1222 if (r == 0)
1223 break;
1224
d89d6c86 1225 if (arg_until_set && !arg_reverse) {
cfbc22ab
LP
1226 usec_t usec;
1227
1228 r = sd_journal_get_realtime_usec(j, &usec);
1229 if (r < 0) {
1230 log_error("Failed to determine timestamp: %s", strerror(-r));
1231 goto finish;
1232 }
3ba09ee8
PF
1233 if (usec > arg_until)
1234 goto finish;
cfbc22ab
LP
1235 }
1236
d89d6c86
LN
1237 if (arg_since_set && arg_reverse) {
1238 usec_t usec;
1239
1240 r = sd_journal_get_realtime_usec(j, &usec);
1241 if (r < 0) {
1242 log_error("Failed to determine timestamp: %s", strerror(-r));
1243 goto finish;
1244 }
1245 if (usec < arg_since)
1246 goto finish;
1247 }
1248
cd931c0a
LP
1249 if (!arg_merge) {
1250 sd_id128_t boot_id;
14a65d65 1251
cd931c0a
LP
1252 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1253 if (r >= 0) {
1254 if (previous_boot_id_valid &&
1255 !sd_id128_equal(boot_id, previous_boot_id))
9048b11f 1256 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
cd931c0a
LP
1257
1258 previous_boot_id = boot_id;
1259 previous_boot_id_valid = true;
1260 }
14a65d65
LP
1261 }
1262
cfbc22ab 1263 flags =
cd4b13e0 1264 arg_all * OUTPUT_SHOW_ALL |
e3657ecd 1265 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
d4205751
LP
1266 on_tty() * OUTPUT_COLOR |
1267 arg_catalog * OUTPUT_CATALOG;
cfbc22ab 1268
08ace05b 1269 r = output_journal(stdout, j, arg_output, 0, flags);
244692cb 1270 if (r < 0 || ferror(stdout))
72f59706 1271 goto finish;
6f003b43
LP
1272
1273 need_seek = true;
cfbc22ab 1274 n_shown++;
87d2c1ff
LP
1275 }
1276
50f20cfd
LP
1277 if (!arg_follow)
1278 break;
1279
e02d1cf7 1280 r = sd_journal_wait(j, (uint64_t) -1);
50f20cfd 1281 if (r < 0) {
7a69007a 1282 log_error("Couldn't wait for journal event: %s", strerror(-r));
50f20cfd
LP
1283 goto finish;
1284 }
67e04a48
ZJS
1285
1286 first_line = false;
de190aef 1287 }
87d2c1ff
LP
1288
1289finish:
0d43c694
LP
1290 pager_close();
1291
3fbf9cbb 1292 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
87d2c1ff 1293}