]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journalctl.c
catalog: make sure strings are terminated
[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 907 if (strv_isempty(g))
3ac251b8
LP
908 log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
909 " Users in the 'systemd-journal' group can see all messages. Pass -q to\n"
910 " turn off this notice.");
6fe391c5
ZJS
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"
3ac251b8
LP
926 " Users in the groups '%s' can see all messages.\n"
927 " Pass -q to turn off this notice.", s);
4468addc 928 }
6fe391c5 929 }
4468addc 930
6fe391c5
ZJS
931 return 0;
932}
933#endif
4468addc 934
6fe391c5 935static int access_check(sd_journal *j) {
6fe391c5 936 Iterator it;
3ac251b8 937 void *code;
6fe391c5 938 int r = 0;
4468addc 939
6fe391c5 940 assert(j);
4468addc 941
6fe391c5
ZJS
942 if (set_isempty(j->errors)) {
943 if (hashmap_isempty(j->files))
3ac251b8 944 log_notice("No journal files were found.");
6fe391c5
ZJS
945 return 0;
946 }
4468addc 947
3ac251b8 948 if (set_contains(j->errors, INT_TO_PTR(-EACCES))) {
6fe391c5
ZJS
949#ifdef HAVE_ACL
950 /* If /var/log/journal doesn't even exist,
3ac251b8 951 * unprivileged users have no access at all */
6fe391c5
ZJS
952 if (access("/var/log/journal", F_OK) < 0 &&
953 geteuid() != 0 &&
954 in_group("systemd-journal") <= 0) {
3ac251b8
LP
955 log_error("Unprivileged users cannot access messages, unless persistent log storage is\n"
956 "enabled. Users in the 'systemd-journal' group may always access messages.");
6fe391c5
ZJS
957 return -EACCES;
958 }
4468addc 959
6fe391c5
ZJS
960 /* If /var/log/journal exists, try to pring a nice
961 notice if the user lacks access to it */
962 if (!arg_quiet && geteuid() != 0) {
963 r = access_check_var_log_journal(j);
964 if (r < 0)
965 return r;
4468addc 966 }
15804ceb 967#else
3ac251b8
LP
968 if (geteuid() != 0 && in_group("systemd-journal") <= 0) {
969 log_error("Unprivileged users cannot access messages. Users in the 'systemd-journal' group\n"
970 "group may access messages.");
971 return -EACCES;
972 }
15804ceb 973#endif
3ac251b8 974
6fe391c5 975 if (hashmap_isempty(j->files)) {
3ac251b8 976 log_error("No journal files were opened due to insufficient permissions.");
6fe391c5
ZJS
977 r = -EACCES;
978 }
979 }
15804ceb 980
6fe391c5 981 SET_FOREACH(code, j->errors, it) {
3ac251b8
LP
982 int err;
983
984 err = -PTR_TO_INT(code);
6fe391c5 985 assert(err > 0);
3ac251b8 986
6fe391c5
ZJS
987 if (err != EACCES)
988 log_warning("Error was encountered while opening journal files: %s",
989 strerror(err));
990 }
991
6fe391c5 992 return r;
15804ceb
LP
993}
994
a963990f
LP
995int main(int argc, char *argv[]) {
996 int r;
763c7aa2 997 sd_journal _cleanup_journal_close_ *j = NULL;
a963990f 998 bool need_seek = false;
14a65d65 999 sd_id128_t previous_boot_id;
67e04a48
ZJS
1000 bool previous_boot_id_valid = false, first_line = true;
1001 int n_shown = 0;
a963990f 1002
a9cdc94f 1003 setlocale(LC_ALL, "");
a963990f
LP
1004 log_parse_environment();
1005 log_open();
1006
1007 r = parse_argv(argc, argv);
1008 if (r <= 0)
1009 goto finish;
1010
ed757c0c
LP
1011 signal(SIGWINCH, columns_lines_cache_reset);
1012
7560fffc 1013 if (arg_action == ACTION_NEW_ID128) {
a963990f
LP
1014 r = generate_new_id128();
1015 goto finish;
1016 }
1017
7560fffc
LP
1018 if (arg_action == ACTION_SETUP_KEYS) {
1019 r = setup_keys();
1020 goto finish;
1021 }
1022
54b7254c
ZJS
1023 if (arg_action == ACTION_LIST_CATALOG ||
1024 arg_action == ACTION_DUMP_CATALOG) {
1025 bool oneline = arg_action == ACTION_LIST_CATALOG;
1026 if (optind < argc)
1027 r = catalog_list_items(stdout, oneline, argv + optind);
1028 else
1029 r = catalog_list(stdout, oneline);
83f6936a
LP
1030 if (r < 0)
1031 log_error("Failed to list catalog: %s", strerror(-r));
d4205751
LP
1032 goto finish;
1033 }
1034
1035 if (arg_action == ACTION_UPDATE_CATALOG) {
1036 r = catalog_update();
1037 goto finish;
1038 }
1039
a963990f
LP
1040 if (arg_directory)
1041 r = sd_journal_open_directory(&j, arg_directory, 0);
1042 else
9e8a535f 1043 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
a963990f
LP
1044 if (r < 0) {
1045 log_error("Failed to open journal: %s", strerror(-r));
763c7aa2 1046 return EXIT_FAILURE;
a963990f
LP
1047 }
1048
6fe391c5
ZJS
1049 r = access_check(j);
1050 if (r < 0)
1051 return EXIT_FAILURE;
1052
beec0085
LP
1053 if (arg_action == ACTION_VERIFY) {
1054 r = verify(j);
1055 goto finish;
1056 }
1057
7560fffc 1058 if (arg_action == ACTION_PRINT_HEADER) {
dca6219e 1059 journal_print_header(j);
763c7aa2 1060 return EXIT_SUCCESS;
dca6219e
LP
1061 }
1062
a1a03e30
LP
1063 if (arg_action == ACTION_DISK_USAGE) {
1064 uint64_t bytes;
1065 char sbytes[FORMAT_BYTES_MAX];
1066
1067 r = sd_journal_get_usage(j, &bytes);
1068 if (r < 0)
763c7aa2 1069 return EXIT_FAILURE;
a1a03e30 1070
763c7aa2
ZJS
1071 printf("Journals take up %s on disk.\n",
1072 format_bytes(sbytes, sizeof(sbytes), bytes));
1073 return EXIT_SUCCESS;
a1a03e30
LP
1074 }
1075
a963990f
LP
1076 r = add_this_boot(j);
1077 if (r < 0)
763c7aa2 1078 return EXIT_FAILURE;
a963990f 1079
c3f60ec5
LP
1080 r = add_unit(j);
1081 if (r < 0)
763c7aa2 1082 return EXIT_FAILURE;
c3f60ec5 1083
a963990f
LP
1084 r = add_matches(j, argv + optind);
1085 if (r < 0)
763c7aa2 1086 return EXIT_FAILURE;
a963990f 1087
941e990d
LP
1088 r = add_priorities(j);
1089 if (r < 0)
763c7aa2 1090 return EXIT_FAILURE;
941e990d 1091
67e04a48
ZJS
1092 /* Opening the fd now means the first sd_journal_wait() will actually wait */
1093 r = sd_journal_get_fd(j);
1094 if (r < 0)
763c7aa2 1095 return EXIT_FAILURE;
67e04a48 1096
15119c16
LP
1097 if (arg_field) {
1098 const void *data;
1099 size_t size;
1100
1101 r = sd_journal_query_unique(j, arg_field);
1102 if (r < 0) {
1103 log_error("Failed to query unique data objects: %s", strerror(-r));
763c7aa2 1104 return EXIT_FAILURE;
15119c16
LP
1105 }
1106
1107 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
1108 const void *eq;
1109
67e04a48 1110 if (arg_lines >= 0 && n_shown >= arg_lines)
fd6e8875
LP
1111 break;
1112
15119c16
LP
1113 eq = memchr(data, '=', size);
1114 if (eq)
1115 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
1116 else
1117 printf("%.*s\n", (int) size, (const char*) data);
fd6e8875
LP
1118
1119 n_shown ++;
15119c16
LP
1120 }
1121
763c7aa2 1122 return EXIT_SUCCESS;
15119c16
LP
1123 }
1124
cfbc22ab
LP
1125 if (arg_cursor) {
1126 r = sd_journal_seek_cursor(j, arg_cursor);
08984293 1127 if (r < 0) {
cfbc22ab 1128 log_error("Failed to seek to cursor: %s", strerror(-r));
763c7aa2 1129 return EXIT_FAILURE;
08984293 1130 }
d89d6c86
LN
1131 if (!arg_reverse)
1132 r = sd_journal_next(j);
1133 else
1134 r = sd_journal_previous(j);
08984293 1135
d89d6c86 1136 } else if (arg_since_set && !arg_reverse) {
cfbc22ab 1137 r = sd_journal_seek_realtime_usec(j, arg_since);
8f14c832 1138 if (r < 0) {
cfbc22ab 1139 log_error("Failed to seek to date: %s", strerror(-r));
763c7aa2 1140 return EXIT_FAILURE;
8f14c832 1141 }
8f14c832
LP
1142 r = sd_journal_next(j);
1143
d89d6c86
LN
1144 } else if (arg_until_set && arg_reverse) {
1145 r = sd_journal_seek_realtime_usec(j, arg_until);
1146 if (r < 0) {
1147 log_error("Failed to seek to date: %s", strerror(-r));
763c7aa2 1148 return EXIT_FAILURE;
d89d6c86
LN
1149 }
1150 r = sd_journal_previous(j);
1151
67e04a48 1152 } else if (arg_lines >= 0) {
2100675e
LP
1153 r = sd_journal_seek_tail(j);
1154 if (r < 0) {
1155 log_error("Failed to seek to tail: %s", strerror(-r));
763c7aa2 1156 return EXIT_FAILURE;
2100675e
LP
1157 }
1158
1159 r = sd_journal_previous_skip(j, arg_lines);
8f14c832 1160
d89d6c86
LN
1161 } else if (arg_reverse) {
1162 r = sd_journal_seek_tail(j);
1163 if (r < 0) {
1164 log_error("Failed to seek to tail: %s", strerror(-r));
763c7aa2 1165 return EXIT_FAILURE;
d89d6c86
LN
1166 }
1167
1168 r = sd_journal_previous(j);
1169
2100675e
LP
1170 } else {
1171 r = sd_journal_seek_head(j);
1172 if (r < 0) {
1173 log_error("Failed to seek to head: %s", strerror(-r));
763c7aa2 1174 return EXIT_FAILURE;
2100675e 1175 }
6f003b43
LP
1176
1177 r = sd_journal_next(j);
1178 }
1179
1180 if (r < 0) {
1181 log_error("Failed to iterate through journal: %s", strerror(-r));
763c7aa2 1182 return EXIT_FAILURE;
50f20cfd 1183 }
87d2c1ff 1184
f89a3b6f 1185 if (!arg_no_pager && !arg_follow)
1b12a7b5 1186 pager_open(arg_pager_end);
0d43c694 1187
cfbc22ab
LP
1188 if (!arg_quiet) {
1189 usec_t start, end;
1190 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
1191
1192 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
1193 if (r < 0) {
1194 log_error("Failed to get cutoff: %s", strerror(-r));
1195 goto finish;
1196 }
1197
1198 if (r > 0) {
1199 if (arg_follow)
9048b11f
LP
1200 printf("-- Logs begin at %s. --\n",
1201 format_timestamp(start_buf, sizeof(start_buf), start));
cfbc22ab 1202 else
9048b11f 1203 printf("-- Logs begin at %s, end at %s. --\n",
cfbc22ab
LP
1204 format_timestamp(start_buf, sizeof(start_buf), start),
1205 format_timestamp(end_buf, sizeof(end_buf), end));
1206 }
1207 }
1208
50f20cfd 1209 for (;;) {
67e04a48 1210 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
cfbc22ab
LP
1211 int flags;
1212
6f003b43 1213 if (need_seek) {
99613ec5 1214 if (!arg_reverse)
d89d6c86
LN
1215 r = sd_journal_next(j);
1216 else
1217 r = sd_journal_previous(j);
6f003b43
LP
1218 if (r < 0) {
1219 log_error("Failed to iterate through journal: %s", strerror(-r));
1220 goto finish;
1221 }
0d43c694
LP
1222 }
1223
1224 if (r == 0)
1225 break;
1226
d89d6c86 1227 if (arg_until_set && !arg_reverse) {
cfbc22ab
LP
1228 usec_t usec;
1229
1230 r = sd_journal_get_realtime_usec(j, &usec);
1231 if (r < 0) {
1232 log_error("Failed to determine timestamp: %s", strerror(-r));
1233 goto finish;
1234 }
3ba09ee8
PF
1235 if (usec > arg_until)
1236 goto finish;
cfbc22ab
LP
1237 }
1238
d89d6c86
LN
1239 if (arg_since_set && arg_reverse) {
1240 usec_t usec;
1241
1242 r = sd_journal_get_realtime_usec(j, &usec);
1243 if (r < 0) {
1244 log_error("Failed to determine timestamp: %s", strerror(-r));
1245 goto finish;
1246 }
1247 if (usec < arg_since)
1248 goto finish;
1249 }
1250
cd931c0a
LP
1251 if (!arg_merge) {
1252 sd_id128_t boot_id;
14a65d65 1253
cd931c0a
LP
1254 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1255 if (r >= 0) {
1256 if (previous_boot_id_valid &&
1257 !sd_id128_equal(boot_id, previous_boot_id))
9048b11f 1258 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
cd931c0a
LP
1259
1260 previous_boot_id = boot_id;
1261 previous_boot_id_valid = true;
1262 }
14a65d65
LP
1263 }
1264
cfbc22ab 1265 flags =
cd4b13e0 1266 arg_all * OUTPUT_SHOW_ALL |
e3657ecd 1267 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
d4205751
LP
1268 on_tty() * OUTPUT_COLOR |
1269 arg_catalog * OUTPUT_CATALOG;
cfbc22ab 1270
08ace05b 1271 r = output_journal(stdout, j, arg_output, 0, flags);
244692cb 1272 if (r < 0 || ferror(stdout))
72f59706 1273 goto finish;
6f003b43
LP
1274
1275 need_seek = true;
cfbc22ab 1276 n_shown++;
87d2c1ff
LP
1277 }
1278
50f20cfd
LP
1279 if (!arg_follow)
1280 break;
1281
e02d1cf7 1282 r = sd_journal_wait(j, (uint64_t) -1);
50f20cfd 1283 if (r < 0) {
7a69007a 1284 log_error("Couldn't wait for journal event: %s", strerror(-r));
50f20cfd
LP
1285 goto finish;
1286 }
67e04a48
ZJS
1287
1288 first_line = false;
de190aef 1289 }
87d2c1ff
LP
1290
1291finish:
0d43c694
LP
1292 pager_close();
1293
3fbf9cbb 1294 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
87d2c1ff 1295}