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