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