]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journalctl.c
journald: be a bit more verbose when vacuuming
[thirdparty/systemd.git] / src / journal / journalctl.c
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <locale.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <stddef.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <time.h>
31 #include <getopt.h>
32 #include <signal.h>
33 #include <sys/stat.h>
34 #include <sys/ioctl.h>
35 #include <linux/fs.h>
36
37 #ifdef HAVE_ACL
38 #include <sys/acl.h>
39 #include "acl-util.h"
40 #endif
41
42 #include <systemd/sd-journal.h>
43
44 #include "log.h"
45 #include "logs-show.h"
46 #include "util.h"
47 #include "path-util.h"
48 #include "fileio.h"
49 #include "build.h"
50 #include "pager.h"
51 #include "logs-show.h"
52 #include "strv.h"
53 #include "journal-internal.h"
54 #include "journal-def.h"
55 #include "journal-verify.h"
56 #include "journal-authenticate.h"
57 #include "journal-qrcode.h"
58 #include "fsprg.h"
59 #include "unit-name.h"
60 #include "catalog.h"
61
62 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
63
64 static OutputMode arg_output = OUTPUT_SHORT;
65 static bool arg_pager_end = false;
66 static bool arg_follow = false;
67 static bool arg_full = false;
68 static bool arg_all = false;
69 static bool arg_no_pager = false;
70 static int arg_lines = -1;
71 static bool arg_no_tail = false;
72 static bool arg_quiet = false;
73 static bool arg_merge = false;
74 static bool arg_boot = false;
75 static char *arg_boot_descriptor = NULL;
76 static bool arg_dmesg = false;
77 static const char *arg_cursor = NULL;
78 static const char *arg_after_cursor = NULL;
79 static bool arg_show_cursor = false;
80 static const char *arg_directory = NULL;
81 static char **arg_file = NULL;
82 static int arg_priorities = 0xFF;
83 static const char *arg_verify_key = NULL;
84 #ifdef HAVE_GCRYPT
85 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
86 static bool arg_force = false;
87 #endif
88 static usec_t arg_since, arg_until;
89 static bool arg_since_set = false, arg_until_set = false;
90 static char **arg_system_units = NULL;
91 static char **arg_user_units = NULL;
92 static const char *arg_field = NULL;
93 static bool arg_catalog = false;
94 static bool arg_reverse = false;
95 static int arg_journal_type = 0;
96 static const char *arg_root = NULL;
97
98 static enum {
99 ACTION_SHOW,
100 ACTION_NEW_ID128,
101 ACTION_PRINT_HEADER,
102 ACTION_SETUP_KEYS,
103 ACTION_VERIFY,
104 ACTION_DISK_USAGE,
105 ACTION_LIST_CATALOG,
106 ACTION_DUMP_CATALOG,
107 ACTION_UPDATE_CATALOG
108 } arg_action = ACTION_SHOW;
109
110 typedef struct boot_id_t {
111 sd_id128_t id;
112 uint64_t timestamp;
113 } boot_id_t;
114
115 static int help(void) {
116
117 printf("%s [OPTIONS...] [MATCHES...]\n\n"
118 "Query the journal.\n\n"
119 "Flags:\n"
120 " --system Show only the system journal\n"
121 " --user Show only the user journal for current user\n"
122 " --since=DATE Start showing entries newer or of the specified date\n"
123 " --until=DATE Stop showing entries older or of the specified date\n"
124 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
125 " --after-cursor=CURSOR Start showing entries from specified cursor\n"
126 " --show-cursor Print the cursor after all the entries\n"
127 " -b --boot[=ID] Show data only from ID or current boot if unspecified\n"
128 " -k --dmesg Show kernel message log from current boot\n"
129 " -u --unit=UNIT Show data only from the specified unit\n"
130 " --user-unit=UNIT Show data only from the specified user session unit\n"
131 " -p --priority=RANGE Show only messages within the specified priority range\n"
132 " -e --pager-end Immediately jump to end of the journal in the pager\n"
133 " -f --follow Follow journal\n"
134 " -n --lines[=INTEGER] Number of journal entries to show\n"
135 " --no-tail Show all lines, even in follow mode\n"
136 " -r --reverse Show the newest entries first\n"
137 " -o --output=STRING Change journal output mode (short, short-iso,\n"
138 " short-precise, short-monotonic, verbose,\n"
139 " export, json, json-pretty, json-sse, cat)\n"
140 " -x --catalog Add message explanations where available\n"
141 " -l --full Do not ellipsize fields\n"
142 " -a --all Show all fields, including long and unprintable\n"
143 " -q --quiet Don't show privilege warning\n"
144 " --no-pager Do not pipe output into a pager\n"
145 " -m --merge Show entries from all available journals\n"
146 " -D --directory=PATH Show journal files from directory\n"
147 " --file=PATH Show journal file\n"
148 " --root=ROOT Operate on catalog files underneath the root ROOT\n"
149 #ifdef HAVE_GCRYPT
150 " --interval=TIME Time interval for changing the FSS sealing key\n"
151 " --verify-key=KEY Specify FSS verification key\n"
152 " --force Force overriding new FSS key pair with --setup-keys\n"
153 #endif
154 "\nCommands:\n"
155 " -h --help Show this help\n"
156 " --version Show package version\n"
157 " --new-id128 Generate a new 128 Bit ID\n"
158 " --header Show journal header information\n"
159 " --disk-usage Show total disk usage\n"
160 " -F --field=FIELD List all values a certain field takes\n"
161 " --list-catalog Show message IDs of all entries in the message catalog\n"
162 " --dump-catalog Show entries in the message catalog\n"
163 " --update-catalog Update the message catalog database\n"
164 #ifdef HAVE_GCRYPT
165 " --setup-keys Generate new FSS key pair\n"
166 " --verify Verify journal file consistency\n"
167 #endif
168 , program_invocation_short_name);
169
170 return 0;
171 }
172
173 static int parse_argv(int argc, char *argv[]) {
174
175 enum {
176 ARG_VERSION = 0x100,
177 ARG_NO_PAGER,
178 ARG_NO_TAIL,
179 ARG_NEW_ID128,
180 ARG_USER,
181 ARG_SYSTEM,
182 ARG_ROOT,
183 ARG_HEADER,
184 ARG_SETUP_KEYS,
185 ARG_FILE,
186 ARG_INTERVAL,
187 ARG_VERIFY,
188 ARG_VERIFY_KEY,
189 ARG_DISK_USAGE,
190 ARG_SINCE,
191 ARG_UNTIL,
192 ARG_AFTER_CURSOR,
193 ARG_SHOW_CURSOR,
194 ARG_USER_UNIT,
195 ARG_LIST_CATALOG,
196 ARG_DUMP_CATALOG,
197 ARG_UPDATE_CATALOG,
198 ARG_FORCE,
199 };
200
201 static const struct option options[] = {
202 { "help", no_argument, NULL, 'h' },
203 { "version" , no_argument, NULL, ARG_VERSION },
204 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
205 { "pager-end", no_argument, NULL, 'e' },
206 { "follow", no_argument, NULL, 'f' },
207 { "force", no_argument, NULL, ARG_FORCE },
208 { "output", required_argument, NULL, 'o' },
209 { "all", no_argument, NULL, 'a' },
210 { "full", no_argument, NULL, 'l' },
211 { "lines", optional_argument, NULL, 'n' },
212 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
213 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
214 { "quiet", no_argument, NULL, 'q' },
215 { "merge", no_argument, NULL, 'm' },
216 { "boot", optional_argument, NULL, 'b' },
217 { "this-boot", optional_argument, NULL, 'b' }, /* deprecated */
218 { "dmesg", no_argument, NULL, 'k' },
219 { "system", no_argument, NULL, ARG_SYSTEM },
220 { "user", no_argument, NULL, ARG_USER },
221 { "directory", required_argument, NULL, 'D' },
222 { "file", required_argument, NULL, ARG_FILE },
223 { "root", required_argument, NULL, ARG_ROOT },
224 { "header", no_argument, NULL, ARG_HEADER },
225 { "priority", required_argument, NULL, 'p' },
226 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
227 { "interval", required_argument, NULL, ARG_INTERVAL },
228 { "verify", no_argument, NULL, ARG_VERIFY },
229 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
230 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
231 { "cursor", required_argument, NULL, 'c' },
232 { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
233 { "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR },
234 { "since", required_argument, NULL, ARG_SINCE },
235 { "until", required_argument, NULL, ARG_UNTIL },
236 { "unit", required_argument, NULL, 'u' },
237 { "user-unit", required_argument, NULL, ARG_USER_UNIT },
238 { "field", required_argument, NULL, 'F' },
239 { "catalog", no_argument, NULL, 'x' },
240 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
241 { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
242 { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG },
243 { "reverse", no_argument, NULL, 'r' },
244 { NULL, 0, NULL, 0 }
245 };
246
247 int c, r;
248
249 assert(argc >= 0);
250 assert(argv);
251
252 while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:u:F:xr", options, NULL)) >= 0) {
253
254 switch (c) {
255
256 case 'h':
257 help();
258 return 0;
259
260 case ARG_VERSION:
261 puts(PACKAGE_STRING);
262 puts(SYSTEMD_FEATURES);
263 return 0;
264
265 case ARG_NO_PAGER:
266 arg_no_pager = true;
267 break;
268
269 case 'e':
270 arg_pager_end = true;
271
272 if (arg_lines < 0)
273 arg_lines = 1000;
274
275 break;
276
277 case 'f':
278 arg_follow = true;
279 break;
280
281 case 'o':
282 arg_output = output_mode_from_string(optarg);
283 if (arg_output < 0) {
284 log_error("Unknown output format '%s'.", optarg);
285 return -EINVAL;
286 }
287
288 if (arg_output == OUTPUT_EXPORT ||
289 arg_output == OUTPUT_JSON ||
290 arg_output == OUTPUT_JSON_PRETTY ||
291 arg_output == OUTPUT_JSON_SSE ||
292 arg_output == OUTPUT_CAT)
293 arg_quiet = true;
294
295 break;
296
297 case 'l':
298 arg_full = true;
299 break;
300
301 case 'a':
302 arg_all = true;
303 break;
304
305 case 'n':
306 if (optarg) {
307 r = safe_atoi(optarg, &arg_lines);
308 if (r < 0 || arg_lines < 0) {
309 log_error("Failed to parse lines '%s'", optarg);
310 return -EINVAL;
311 }
312 } else {
313 int n;
314
315 /* Hmm, no argument? Maybe the next
316 * word on the command line is
317 * supposed to be the argument? Let's
318 * see if there is one, and is
319 * parsable as a positive
320 * integer... */
321
322 if (optind < argc &&
323 safe_atoi(argv[optind], &n) >= 0 &&
324 n >= 0) {
325
326 arg_lines = n;
327 optind++;
328 } else
329 arg_lines = 10;
330 }
331
332 break;
333
334 case ARG_NO_TAIL:
335 arg_no_tail = true;
336 break;
337
338 case ARG_NEW_ID128:
339 arg_action = ACTION_NEW_ID128;
340 break;
341
342 case 'q':
343 arg_quiet = true;
344 break;
345
346 case 'm':
347 arg_merge = true;
348 break;
349
350 case 'b':
351 arg_boot = true;
352
353 if (optarg)
354 arg_boot_descriptor = optarg;
355 else if (optind < argc) {
356 int boot;
357
358 if (argv[optind][0] != '-' ||
359 safe_atoi(argv[optind], &boot) >= 0) {
360 arg_boot_descriptor = argv[optind];
361 optind++;
362 }
363 }
364
365 break;
366
367 case 'k':
368 arg_boot = arg_dmesg = true;
369 break;
370
371 case ARG_SYSTEM:
372 arg_journal_type |= SD_JOURNAL_SYSTEM;
373 break;
374
375 case ARG_USER:
376 arg_journal_type |= SD_JOURNAL_CURRENT_USER;
377 break;
378
379 case 'D':
380 arg_directory = optarg;
381 break;
382
383 case ARG_FILE:
384 r = glob_extend(&arg_file, optarg);
385 if (r < 0) {
386 log_error("Failed to add paths: %s", strerror(-r));
387 return r;
388 };
389 break;
390
391 case ARG_ROOT:
392 arg_root = optarg;
393 break;
394
395 case 'c':
396 arg_cursor = optarg;
397 break;
398
399 case ARG_AFTER_CURSOR:
400 arg_after_cursor = optarg;
401 break;
402
403 case ARG_SHOW_CURSOR:
404 arg_show_cursor = true;
405 break;
406
407 case ARG_HEADER:
408 arg_action = ACTION_PRINT_HEADER;
409 break;
410
411 case ARG_VERIFY:
412 arg_action = ACTION_VERIFY;
413 break;
414
415 case ARG_DISK_USAGE:
416 arg_action = ACTION_DISK_USAGE;
417 break;
418
419 #ifdef HAVE_GCRYPT
420 case ARG_FORCE:
421 arg_force = true;
422 break;
423
424 case ARG_SETUP_KEYS:
425 arg_action = ACTION_SETUP_KEYS;
426 break;
427
428
429 case ARG_VERIFY_KEY:
430 arg_action = ACTION_VERIFY;
431 arg_verify_key = optarg;
432 arg_merge = false;
433 break;
434
435 case ARG_INTERVAL:
436 r = parse_sec(optarg, &arg_interval);
437 if (r < 0 || arg_interval <= 0) {
438 log_error("Failed to parse sealing key change interval: %s", optarg);
439 return -EINVAL;
440 }
441 break;
442 #else
443 case ARG_SETUP_KEYS:
444 case ARG_VERIFY_KEY:
445 case ARG_INTERVAL:
446 case ARG_FORCE:
447 log_error("Forward-secure sealing not available.");
448 return -ENOTSUP;
449 #endif
450
451 case 'p': {
452 const char *dots;
453
454 dots = strstr(optarg, "..");
455 if (dots) {
456 char *a;
457 int from, to, i;
458
459 /* a range */
460 a = strndup(optarg, dots - optarg);
461 if (!a)
462 return log_oom();
463
464 from = log_level_from_string(a);
465 to = log_level_from_string(dots + 2);
466 free(a);
467
468 if (from < 0 || to < 0) {
469 log_error("Failed to parse log level range %s", optarg);
470 return -EINVAL;
471 }
472
473 arg_priorities = 0;
474
475 if (from < to) {
476 for (i = from; i <= to; i++)
477 arg_priorities |= 1 << i;
478 } else {
479 for (i = to; i <= from; i++)
480 arg_priorities |= 1 << i;
481 }
482
483 } else {
484 int p, i;
485
486 p = log_level_from_string(optarg);
487 if (p < 0) {
488 log_error("Unknown log level %s", optarg);
489 return -EINVAL;
490 }
491
492 arg_priorities = 0;
493
494 for (i = 0; i <= p; i++)
495 arg_priorities |= 1 << i;
496 }
497
498 break;
499 }
500
501 case ARG_SINCE:
502 r = parse_timestamp(optarg, &arg_since);
503 if (r < 0) {
504 log_error("Failed to parse timestamp: %s", optarg);
505 return -EINVAL;
506 }
507 arg_since_set = true;
508 break;
509
510 case ARG_UNTIL:
511 r = parse_timestamp(optarg, &arg_until);
512 if (r < 0) {
513 log_error("Failed to parse timestamp: %s", optarg);
514 return -EINVAL;
515 }
516 arg_until_set = true;
517 break;
518
519 case 'u':
520 r = strv_extend(&arg_system_units, optarg);
521 if (r < 0)
522 return log_oom();
523 break;
524
525 case ARG_USER_UNIT:
526 r = strv_extend(&arg_user_units, optarg);
527 if (r < 0)
528 return log_oom();
529 break;
530
531 case '?':
532 return -EINVAL;
533
534 case 'F':
535 arg_field = optarg;
536 break;
537
538 case 'x':
539 arg_catalog = true;
540 break;
541
542 case ARG_LIST_CATALOG:
543 arg_action = ACTION_LIST_CATALOG;
544 break;
545
546 case ARG_DUMP_CATALOG:
547 arg_action = ACTION_DUMP_CATALOG;
548 break;
549
550 case ARG_UPDATE_CATALOG:
551 arg_action = ACTION_UPDATE_CATALOG;
552 break;
553
554 case 'r':
555 arg_reverse = true;
556 break;
557
558 default:
559 log_error("Unknown option code %c", c);
560 return -EINVAL;
561 }
562 }
563
564 if (arg_follow && !arg_no_tail && arg_lines < 0)
565 arg_lines = 10;
566
567 if (arg_directory && arg_file) {
568 log_error("Please specify either -D/--directory= or --file=, not both.");
569 return -EINVAL;
570 }
571
572 if (arg_since_set && arg_until_set && arg_since > arg_until) {
573 log_error("--since= must be before --until=.");
574 return -EINVAL;
575 }
576
577 if (!!arg_cursor + !!arg_after_cursor + !!arg_since_set > 1) {
578 log_error("Please specify only one of --since=, --cursor=, and --after-cursor.");
579 return -EINVAL;
580 }
581
582 if (arg_follow && arg_reverse) {
583 log_error("Please specify either --reverse= or --follow=, not both.");
584 return -EINVAL;
585 }
586
587 return 1;
588 }
589
590 static int generate_new_id128(void) {
591 sd_id128_t id;
592 int r;
593 unsigned i;
594
595 r = sd_id128_randomize(&id);
596 if (r < 0) {
597 log_error("Failed to generate ID: %s", strerror(-r));
598 return r;
599 }
600
601 printf("As string:\n"
602 SD_ID128_FORMAT_STR "\n\n"
603 "As UUID:\n"
604 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
605 "As macro:\n"
606 "#define MESSAGE_XYZ SD_ID128_MAKE(",
607 SD_ID128_FORMAT_VAL(id),
608 SD_ID128_FORMAT_VAL(id));
609 for (i = 0; i < 16; i++)
610 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
611 fputs(")\n\n", stdout);
612
613 printf("As Python constant:\n"
614 ">>> import uuid\n"
615 ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
616 SD_ID128_FORMAT_VAL(id));
617
618 return 0;
619 }
620
621 static int add_matches(sd_journal *j, char **args) {
622 char **i;
623
624 assert(j);
625
626 STRV_FOREACH(i, args) {
627 int r;
628
629 if (streq(*i, "+"))
630 r = sd_journal_add_disjunction(j);
631 else if (path_is_absolute(*i)) {
632 _cleanup_free_ char *p, *t = NULL, *t2 = NULL;
633 const char *path;
634 _cleanup_free_ char *interpreter = NULL;
635 struct stat st;
636
637 p = canonicalize_file_name(*i);
638 path = p ? p : *i;
639
640 if (stat(path, &st) < 0) {
641 log_error("Couldn't stat file: %m");
642 return -errno;
643 }
644
645 if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) {
646 if (executable_is_script(path, &interpreter) > 0) {
647 _cleanup_free_ char *comm;
648
649 comm = strndup(path_get_file_name(path), 15);
650 if (!comm)
651 return log_oom();
652
653 t = strappend("_COMM=", comm);
654
655 /* Append _EXE only if the interpreter is not a link.
656 Otherwise it might be outdated often. */
657 if (lstat(interpreter, &st) == 0 &&
658 !S_ISLNK(st.st_mode)) {
659 t2 = strappend("_EXE=", interpreter);
660 if (!t2)
661 return log_oom();
662 }
663 } else
664 t = strappend("_EXE=", path);
665 } else if (S_ISCHR(st.st_mode))
666 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
667 else if (S_ISBLK(st.st_mode))
668 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
669 else {
670 log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
671 return -EINVAL;
672 }
673
674 if (!t)
675 return log_oom();
676
677 r = sd_journal_add_match(j, t, 0);
678 if (t2)
679 r = sd_journal_add_match(j, t2, 0);
680 } else
681 r = sd_journal_add_match(j, *i, 0);
682
683 if (r < 0) {
684 log_error("Failed to add match '%s': %s", *i, strerror(-r));
685 return r;
686 }
687 }
688
689 return 0;
690 }
691
692 static int boot_id_cmp(const void *a, const void *b) {
693 uint64_t _a, _b;
694
695 _a = ((const boot_id_t *)a)->timestamp;
696 _b = ((const boot_id_t *)b)->timestamp;
697
698 return _a < _b ? -1 : (_a > _b ? 1 : 0);
699 }
700
701 static int get_relative_boot_id(sd_journal *j, sd_id128_t *boot_id, int relative) {
702 int r;
703 const void *data;
704 unsigned int count = 0;
705 size_t length, allocated = 0;
706 boot_id_t ref_boot_id = {SD_ID128_NULL}, *id;
707 _cleanup_free_ boot_id_t *all_ids = NULL;
708
709 assert(j);
710 assert(boot_id);
711
712 if (relative == 0 && !sd_id128_equal(*boot_id, SD_ID128_NULL))
713 return 0;
714
715 r = sd_journal_query_unique(j, "_BOOT_ID");
716 if (r < 0)
717 return r;
718
719 SD_JOURNAL_FOREACH_UNIQUE(j, data, length) {
720 if (length < strlen("_BOOT_ID="))
721 continue;
722
723 if (!GREEDY_REALLOC(all_ids, allocated, count + 1))
724 return log_oom();
725
726 id = &all_ids[count];
727
728 r = sd_id128_from_string(((const char *)data) + strlen("_BOOT_ID="), &id->id);
729 if (r < 0)
730 continue;
731
732 r = sd_journal_add_match(j, data, length);
733 if (r < 0)
734 return r;
735
736 r = sd_journal_seek_head(j);
737 if (r < 0)
738 return r;
739
740 r = sd_journal_next(j);
741 if (r < 0)
742 return r;
743 else if (r == 0)
744 goto flush;
745
746 r = sd_journal_get_realtime_usec(j, &id->timestamp);
747 if (r < 0)
748 return r;
749
750 if (sd_id128_equal(id->id, *boot_id))
751 ref_boot_id = *id;
752
753 count++;
754 flush:
755 sd_journal_flush_matches(j);
756 }
757
758 qsort(all_ids, count, sizeof(boot_id_t), boot_id_cmp);
759
760 if (sd_id128_equal(*boot_id, SD_ID128_NULL)) {
761 if (relative > (int) count || relative <= -(int)count)
762 return -EADDRNOTAVAIL;
763
764 *boot_id = all_ids[(relative <= 0)*count + relative - 1].id;
765 } else {
766 id = bsearch(&ref_boot_id, all_ids, count, sizeof(boot_id_t), boot_id_cmp);
767
768 if (!id ||
769 relative <= 0 ? (id - all_ids) + relative < 0 :
770 (id - all_ids) + relative >= (int) count)
771 return -EADDRNOTAVAIL;
772
773 *boot_id = (id + relative)->id;
774 }
775
776 return 0;
777 }
778
779 static int add_boot(sd_journal *j) {
780 char match[9+32+1] = "_BOOT_ID=";
781 char *offset;
782 sd_id128_t boot_id = SD_ID128_NULL;
783 int r, relative = 0;
784
785 assert(j);
786
787 if (!arg_boot)
788 return 0;
789
790 if (!arg_boot_descriptor)
791 return add_match_this_boot(j);
792
793 if (strlen(arg_boot_descriptor) >= 32) {
794 char tmp = arg_boot_descriptor[32];
795 arg_boot_descriptor[32] = '\0';
796 r = sd_id128_from_string(arg_boot_descriptor, &boot_id);
797 arg_boot_descriptor[32] = tmp;
798
799 if (r < 0) {
800 log_error("Failed to parse boot ID '%.32s': %s",
801 arg_boot_descriptor, strerror(-r));
802 return r;
803 }
804
805 offset = arg_boot_descriptor + 32;
806
807 if (*offset && *offset != '-' && *offset != '+') {
808 log_error("Relative boot ID offset must start with a '+' or a '-', found '%s' ", offset);
809 return -EINVAL;
810 }
811 } else
812 offset = arg_boot_descriptor;
813
814 if (*offset) {
815 r = safe_atoi(offset, &relative);
816 if (r < 0) {
817 log_error("Failed to parse relative boot ID number '%s'", offset);
818 return -EINVAL;
819 }
820 }
821
822 r = get_relative_boot_id(j, &boot_id, relative);
823 if (r < 0) {
824 if (sd_id128_equal(boot_id, SD_ID128_NULL))
825 log_error("Failed to look up boot %+d: %s", relative, strerror(-r));
826 else
827 log_error("Failed to look up boot ID "SD_ID128_FORMAT_STR"%+d: %s",
828 SD_ID128_FORMAT_VAL(boot_id), relative, strerror(-r));
829 return r;
830 }
831
832 sd_id128_to_string(boot_id, match + 9);
833
834 r = sd_journal_add_match(j, match, sizeof(match) - 1);
835 if (r < 0) {
836 log_error("Failed to add match: %s", strerror(-r));
837 return r;
838 }
839
840 r = sd_journal_add_conjunction(j);
841 if (r < 0)
842 return r;
843
844 return 0;
845 }
846
847 static int add_dmesg(sd_journal *j) {
848 int r;
849 assert(j);
850
851 if (!arg_dmesg)
852 return 0;
853
854 r = sd_journal_add_match(j, "_TRANSPORT=kernel", strlen("_TRANSPORT=kernel"));
855 if (r < 0) {
856 log_error("Failed to add match: %s", strerror(-r));
857 return r;
858 }
859
860 r = sd_journal_add_conjunction(j);
861 if (r < 0)
862 return r;
863
864 return 0;
865 }
866
867 static int add_units(sd_journal *j) {
868 _cleanup_free_ char *u = NULL;
869 int r;
870 char **i;
871
872 assert(j);
873
874 STRV_FOREACH(i, arg_system_units) {
875 u = unit_name_mangle(*i);
876 if (!u)
877 return log_oom();
878 r = add_matches_for_unit(j, u);
879 if (r < 0)
880 return r;
881 r = sd_journal_add_disjunction(j);
882 if (r < 0)
883 return r;
884 }
885
886 STRV_FOREACH(i, arg_user_units) {
887 u = unit_name_mangle(*i);
888 if (!u)
889 return log_oom();
890
891 r = add_matches_for_user_unit(j, u, getuid());
892 if (r < 0)
893 return r;
894
895 r = sd_journal_add_disjunction(j);
896 if (r < 0)
897 return r;
898
899 }
900
901 r = sd_journal_add_conjunction(j);
902 if (r < 0)
903 return r;
904
905 return 0;
906 }
907
908 static int add_priorities(sd_journal *j) {
909 char match[] = "PRIORITY=0";
910 int i, r;
911 assert(j);
912
913 if (arg_priorities == 0xFF)
914 return 0;
915
916 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
917 if (arg_priorities & (1 << i)) {
918 match[sizeof(match)-2] = '0' + i;
919
920 r = sd_journal_add_match(j, match, strlen(match));
921 if (r < 0) {
922 log_error("Failed to add match: %s", strerror(-r));
923 return r;
924 }
925 }
926
927 r = sd_journal_add_conjunction(j);
928 if (r < 0)
929 return r;
930
931 return 0;
932 }
933
934 static int setup_keys(void) {
935 #ifdef HAVE_GCRYPT
936 size_t mpk_size, seed_size, state_size, i;
937 uint8_t *mpk, *seed, *state;
938 ssize_t l;
939 int fd = -1, r, attr = 0;
940 sd_id128_t machine, boot;
941 char *p = NULL, *k = NULL;
942 struct FSSHeader h;
943 uint64_t n;
944 struct stat st;
945
946 r = stat("/var/log/journal", &st);
947 if (r < 0 && errno != ENOENT && errno != ENOTDIR) {
948 log_error("stat(\"%s\") failed: %m", "/var/log/journal");
949 return -errno;
950 }
951
952 if (r < 0 || !S_ISDIR(st.st_mode)) {
953 log_error("%s is not a directory, must be using persistent logging for FSS.",
954 "/var/log/journal");
955 return r < 0 ? -errno : -ENOTDIR;
956 }
957
958 r = sd_id128_get_machine(&machine);
959 if (r < 0) {
960 log_error("Failed to get machine ID: %s", strerror(-r));
961 return r;
962 }
963
964 r = sd_id128_get_boot(&boot);
965 if (r < 0) {
966 log_error("Failed to get boot ID: %s", strerror(-r));
967 return r;
968 }
969
970 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
971 SD_ID128_FORMAT_VAL(machine)) < 0)
972 return log_oom();
973
974 if (access(p, F_OK) >= 0) {
975 if (arg_force) {
976 r = unlink(p);
977 if (r < 0) {
978 log_error("unlink(\"%s\") failed: %m", p);
979 r = -errno;
980 goto finish;
981 }
982 } else {
983 log_error("Sealing key file %s exists already. (--force to recreate)", p);
984 r = -EEXIST;
985 goto finish;
986 }
987 }
988
989 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
990 SD_ID128_FORMAT_VAL(machine)) < 0) {
991 r = log_oom();
992 goto finish;
993 }
994
995 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
996 mpk = alloca(mpk_size);
997
998 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
999 seed = alloca(seed_size);
1000
1001 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
1002 state = alloca(state_size);
1003
1004 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
1005 if (fd < 0) {
1006 log_error("Failed to open /dev/random: %m");
1007 r = -errno;
1008 goto finish;
1009 }
1010
1011 log_info("Generating seed...");
1012 l = loop_read(fd, seed, seed_size, true);
1013 if (l < 0 || (size_t) l != seed_size) {
1014 log_error("Failed to read random seed: %s", strerror(EIO));
1015 r = -EIO;
1016 goto finish;
1017 }
1018
1019 log_info("Generating key pair...");
1020 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
1021
1022 log_info("Generating sealing key...");
1023 FSPRG_GenState0(state, mpk, seed, seed_size);
1024
1025 assert(arg_interval > 0);
1026
1027 n = now(CLOCK_REALTIME);
1028 n /= arg_interval;
1029
1030 close_nointr_nofail(fd);
1031 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
1032 if (fd < 0) {
1033 log_error("Failed to open %s: %m", k);
1034 r = -errno;
1035 goto finish;
1036 }
1037
1038 /* Enable secure remove, exclusion from dump, synchronous
1039 * writing and in-place updating */
1040 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
1041 log_warning("FS_IOC_GETFLAGS failed: %m");
1042
1043 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
1044
1045 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
1046 log_warning("FS_IOC_SETFLAGS failed: %m");
1047
1048 zero(h);
1049 memcpy(h.signature, "KSHHRHLP", 8);
1050 h.machine_id = machine;
1051 h.boot_id = boot;
1052 h.header_size = htole64(sizeof(h));
1053 h.start_usec = htole64(n * arg_interval);
1054 h.interval_usec = htole64(arg_interval);
1055 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
1056 h.fsprg_state_size = htole64(state_size);
1057
1058 l = loop_write(fd, &h, sizeof(h), false);
1059 if (l < 0 || (size_t) l != sizeof(h)) {
1060 log_error("Failed to write header: %s", strerror(EIO));
1061 r = -EIO;
1062 goto finish;
1063 }
1064
1065 l = loop_write(fd, state, state_size, false);
1066 if (l < 0 || (size_t) l != state_size) {
1067 log_error("Failed to write state: %s", strerror(EIO));
1068 r = -EIO;
1069 goto finish;
1070 }
1071
1072 if (link(k, p) < 0) {
1073 log_error("Failed to link file: %m");
1074 r = -errno;
1075 goto finish;
1076 }
1077
1078 if (on_tty()) {
1079 fprintf(stderr,
1080 "\n"
1081 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
1082 "the following local file. This key file is automatically updated when the\n"
1083 "sealing key is advanced. It should not be used on multiple hosts.\n"
1084 "\n"
1085 "\t%s\n"
1086 "\n"
1087 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
1088 "at a safe location and should not be saved locally on disk.\n"
1089 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
1090 fflush(stderr);
1091 }
1092 for (i = 0; i < seed_size; i++) {
1093 if (i > 0 && i % 3 == 0)
1094 putchar('-');
1095 printf("%02x", ((uint8_t*) seed)[i]);
1096 }
1097
1098 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
1099
1100 if (on_tty()) {
1101 char tsb[FORMAT_TIMESPAN_MAX], *hn;
1102
1103 fprintf(stderr,
1104 ANSI_HIGHLIGHT_OFF "\n"
1105 "The sealing key is automatically changed every %s.\n",
1106 format_timespan(tsb, sizeof(tsb), arg_interval, 0));
1107
1108 hn = gethostname_malloc();
1109
1110 if (hn) {
1111 hostname_cleanup(hn, false);
1112 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
1113 } else
1114 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
1115
1116 #ifdef HAVE_QRENCODE
1117 /* If this is not an UTF-8 system don't print any QR codes */
1118 if (is_locale_utf8()) {
1119 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
1120 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
1121 }
1122 #endif
1123 free(hn);
1124 }
1125
1126 r = 0;
1127
1128 finish:
1129 if (fd >= 0)
1130 close_nointr_nofail(fd);
1131
1132 if (k) {
1133 unlink(k);
1134 free(k);
1135 }
1136
1137 free(p);
1138
1139 return r;
1140 #else
1141 log_error("Forward-secure sealing not available.");
1142 return -ENOTSUP;
1143 #endif
1144 }
1145
1146 static int verify(sd_journal *j) {
1147 int r = 0;
1148 Iterator i;
1149 JournalFile *f;
1150
1151 assert(j);
1152
1153 log_show_color(true);
1154
1155 HASHMAP_FOREACH(f, j->files, i) {
1156 int k;
1157 usec_t first, validated, last;
1158
1159 #ifdef HAVE_GCRYPT
1160 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
1161 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
1162 #endif
1163
1164 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
1165 if (k == -EINVAL) {
1166 /* If the key was invalid give up right-away. */
1167 return k;
1168 } else if (k < 0) {
1169 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
1170 r = k;
1171 } else {
1172 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
1173 log_info("PASS: %s", f->path);
1174
1175 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
1176 if (validated > 0) {
1177 log_info("=> Validated from %s to %s, final %s entries not sealed.",
1178 format_timestamp(a, sizeof(a), first),
1179 format_timestamp(b, sizeof(b), validated),
1180 format_timespan(c, sizeof(c), last > validated ? last - validated : 0, 0));
1181 } else if (last > 0)
1182 log_info("=> No sealing yet, %s of entries not sealed.",
1183 format_timespan(c, sizeof(c), last - first, 0));
1184 else
1185 log_info("=> No sealing yet, no entries in file.");
1186 }
1187 }
1188 }
1189
1190 return r;
1191 }
1192
1193 #ifdef HAVE_ACL
1194 static int access_check_var_log_journal(sd_journal *j) {
1195 _cleanup_strv_free_ char **g = NULL;
1196 bool have_access;
1197 int r;
1198
1199 assert(j);
1200
1201 have_access = in_group("systemd-journal") > 0;
1202
1203 if (!have_access) {
1204 /* Let's enumerate all groups from the default ACL of
1205 * the directory, which generally should allow access
1206 * to most journal files too */
1207 r = search_acl_groups(&g, "/var/log/journal/", &have_access);
1208 if (r < 0)
1209 return r;
1210 }
1211
1212 if (!have_access) {
1213
1214 if (strv_isempty(g))
1215 log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
1216 " Users in the 'systemd-journal' group can see all messages. Pass -q to\n"
1217 " turn off this notice.");
1218 else {
1219 _cleanup_free_ char *s = NULL;
1220
1221 r = strv_extend(&g, "systemd-journal");
1222 if (r < 0)
1223 return log_oom();
1224
1225 strv_sort(g);
1226 strv_uniq(g);
1227
1228 s = strv_join(g, "', '");
1229 if (!s)
1230 return log_oom();
1231
1232 log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
1233 " Users in the groups '%s' can see all messages.\n"
1234 " Pass -q to turn off this notice.", s);
1235 }
1236 }
1237
1238 return 0;
1239 }
1240 #endif
1241
1242 static int access_check(sd_journal *j) {
1243 Iterator it;
1244 void *code;
1245 int r = 0;
1246
1247 assert(j);
1248
1249 if (set_isempty(j->errors)) {
1250 if (hashmap_isempty(j->files))
1251 log_notice("No journal files were found.");
1252 return 0;
1253 }
1254
1255 if (set_contains(j->errors, INT_TO_PTR(-EACCES))) {
1256 #ifdef HAVE_ACL
1257 /* If /var/log/journal doesn't even exist,
1258 * unprivileged users have no access at all */
1259 if (access("/var/log/journal", F_OK) < 0 &&
1260 geteuid() != 0 &&
1261 in_group("systemd-journal") <= 0) {
1262 log_error("Unprivileged users cannot access messages, unless persistent log storage is\n"
1263 "enabled. Users in the 'systemd-journal' group may always access messages.");
1264 return -EACCES;
1265 }
1266
1267 /* If /var/log/journal exists, try to pring a nice
1268 notice if the user lacks access to it */
1269 if (!arg_quiet && geteuid() != 0) {
1270 r = access_check_var_log_journal(j);
1271 if (r < 0)
1272 return r;
1273 }
1274 #else
1275 if (geteuid() != 0 && in_group("systemd-journal") <= 0) {
1276 log_error("Unprivileged users cannot access messages. Users in the 'systemd-journal' group\n"
1277 "group may access messages.");
1278 return -EACCES;
1279 }
1280 #endif
1281
1282 if (hashmap_isempty(j->files)) {
1283 log_error("No journal files were opened due to insufficient permissions.");
1284 r = -EACCES;
1285 }
1286 }
1287
1288 SET_FOREACH(code, j->errors, it) {
1289 int err;
1290
1291 err = -PTR_TO_INT(code);
1292 assert(err > 0);
1293
1294 if (err != EACCES)
1295 log_warning("Error was encountered while opening journal files: %s",
1296 strerror(err));
1297 }
1298
1299 return r;
1300 }
1301
1302 int main(int argc, char *argv[]) {
1303 int r;
1304 _cleanup_journal_close_ sd_journal *j = NULL;
1305 bool need_seek = false;
1306 sd_id128_t previous_boot_id;
1307 bool previous_boot_id_valid = false, first_line = true;
1308 int n_shown = 0;
1309 bool ellipsized = false;
1310
1311 setlocale(LC_ALL, "");
1312 log_parse_environment();
1313 log_open();
1314
1315 r = parse_argv(argc, argv);
1316 if (r <= 0)
1317 goto finish;
1318
1319 signal(SIGWINCH, columns_lines_cache_reset);
1320
1321 if (arg_action == ACTION_NEW_ID128) {
1322 r = generate_new_id128();
1323 goto finish;
1324 }
1325
1326 if (arg_action == ACTION_SETUP_KEYS) {
1327 r = setup_keys();
1328 goto finish;
1329 }
1330
1331 if (arg_action == ACTION_UPDATE_CATALOG ||
1332 arg_action == ACTION_LIST_CATALOG ||
1333 arg_action == ACTION_DUMP_CATALOG) {
1334
1335 const char* database = CATALOG_DATABASE;
1336 _cleanup_free_ char *copy = NULL;
1337 if (arg_root) {
1338 copy = strjoin(arg_root, "/", CATALOG_DATABASE, NULL);
1339 if (!copy) {
1340 r = log_oom();
1341 goto finish;
1342 }
1343 path_kill_slashes(copy);
1344 database = copy;
1345 }
1346
1347 if (arg_action == ACTION_UPDATE_CATALOG) {
1348 r = catalog_update(database, arg_root, catalog_file_dirs);
1349 if (r < 0)
1350 log_error("Failed to list catalog: %s", strerror(-r));
1351 } else {
1352 bool oneline = arg_action == ACTION_LIST_CATALOG;
1353
1354 if (optind < argc)
1355 r = catalog_list_items(stdout, database,
1356 oneline, argv + optind);
1357 else
1358 r = catalog_list(stdout, database, oneline);
1359 if (r < 0)
1360 log_error("Failed to list catalog: %s", strerror(-r));
1361 }
1362
1363 goto finish;
1364 }
1365
1366 if (arg_directory)
1367 r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
1368 else if (arg_file)
1369 r = sd_journal_open_files(&j, (const char**) arg_file, 0);
1370 else
1371 r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
1372 if (r < 0) {
1373 log_error("Failed to open %s: %s",
1374 arg_directory ? arg_directory : arg_file ? "files" : "journal",
1375 strerror(-r));
1376 return EXIT_FAILURE;
1377 }
1378
1379 r = access_check(j);
1380 if (r < 0)
1381 return EXIT_FAILURE;
1382
1383 if (arg_action == ACTION_VERIFY) {
1384 r = verify(j);
1385 goto finish;
1386 }
1387
1388 if (arg_action == ACTION_PRINT_HEADER) {
1389 journal_print_header(j);
1390 return EXIT_SUCCESS;
1391 }
1392
1393 if (arg_action == ACTION_DISK_USAGE) {
1394 uint64_t bytes;
1395 char sbytes[FORMAT_BYTES_MAX];
1396
1397 r = sd_journal_get_usage(j, &bytes);
1398 if (r < 0)
1399 return EXIT_FAILURE;
1400
1401 printf("Journals take up %s on disk.\n",
1402 format_bytes(sbytes, sizeof(sbytes), bytes));
1403 return EXIT_SUCCESS;
1404 }
1405
1406 /* add_boot() must be called first!
1407 * It may need to seek the journal to find parent boot IDs. */
1408 r = add_boot(j);
1409 if (r < 0)
1410 return EXIT_FAILURE;
1411
1412 r = add_dmesg(j);
1413 if (r < 0)
1414 return EXIT_FAILURE;
1415
1416 r = add_units(j);
1417 strv_free(arg_system_units);
1418 strv_free(arg_user_units);
1419
1420 if (r < 0)
1421 return EXIT_FAILURE;
1422
1423 r = add_priorities(j);
1424 if (r < 0)
1425 return EXIT_FAILURE;
1426
1427 r = add_matches(j, argv + optind);
1428 if (r < 0)
1429 return EXIT_FAILURE;
1430
1431 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
1432 _cleanup_free_ char *filter;
1433
1434 filter = journal_make_match_string(j);
1435 log_debug("Journal filter: %s", filter);
1436 }
1437
1438 if (arg_field) {
1439 const void *data;
1440 size_t size;
1441
1442 r = sd_journal_set_data_threshold(j, 0);
1443 if (r < 0) {
1444 log_error("Failed to unset data size threshold");
1445 return EXIT_FAILURE;
1446 }
1447
1448 r = sd_journal_query_unique(j, arg_field);
1449 if (r < 0) {
1450 log_error("Failed to query unique data objects: %s", strerror(-r));
1451 return EXIT_FAILURE;
1452 }
1453
1454 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
1455 const void *eq;
1456
1457 if (arg_lines >= 0 && n_shown >= arg_lines)
1458 break;
1459
1460 eq = memchr(data, '=', size);
1461 if (eq)
1462 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
1463 else
1464 printf("%.*s\n", (int) size, (const char*) data);
1465
1466 n_shown ++;
1467 }
1468
1469 return EXIT_SUCCESS;
1470 }
1471
1472 /* Opening the fd now means the first sd_journal_wait() will actually wait */
1473 if (arg_follow) {
1474 r = sd_journal_get_fd(j);
1475 if (r < 0)
1476 return EXIT_FAILURE;
1477 }
1478
1479 if (arg_cursor || arg_after_cursor) {
1480 r = sd_journal_seek_cursor(j, arg_cursor ? arg_cursor : arg_after_cursor);
1481 if (r < 0) {
1482 log_error("Failed to seek to cursor: %s", strerror(-r));
1483 return EXIT_FAILURE;
1484 }
1485 if (!arg_reverse)
1486 r = sd_journal_next_skip(j, 1 + !!arg_after_cursor);
1487 else
1488 r = sd_journal_previous_skip(j, 1 + !!arg_after_cursor);
1489
1490 if (arg_after_cursor && r < 2 && !arg_follow)
1491 /* We couldn't find the next entry after the cursor. */
1492 arg_lines = 0;
1493
1494 } else if (arg_since_set && !arg_reverse) {
1495 r = sd_journal_seek_realtime_usec(j, arg_since);
1496 if (r < 0) {
1497 log_error("Failed to seek to date: %s", strerror(-r));
1498 return EXIT_FAILURE;
1499 }
1500 r = sd_journal_next(j);
1501
1502 } else if (arg_until_set && arg_reverse) {
1503 r = sd_journal_seek_realtime_usec(j, arg_until);
1504 if (r < 0) {
1505 log_error("Failed to seek to date: %s", strerror(-r));
1506 return EXIT_FAILURE;
1507 }
1508 r = sd_journal_previous(j);
1509
1510 } else if (arg_lines >= 0) {
1511 r = sd_journal_seek_tail(j);
1512 if (r < 0) {
1513 log_error("Failed to seek to tail: %s", strerror(-r));
1514 return EXIT_FAILURE;
1515 }
1516
1517 r = sd_journal_previous_skip(j, arg_lines);
1518
1519 } else if (arg_reverse) {
1520 r = sd_journal_seek_tail(j);
1521 if (r < 0) {
1522 log_error("Failed to seek to tail: %s", strerror(-r));
1523 return EXIT_FAILURE;
1524 }
1525
1526 r = sd_journal_previous(j);
1527
1528 } else {
1529 r = sd_journal_seek_head(j);
1530 if (r < 0) {
1531 log_error("Failed to seek to head: %s", strerror(-r));
1532 return EXIT_FAILURE;
1533 }
1534
1535 r = sd_journal_next(j);
1536 }
1537
1538 if (r < 0) {
1539 log_error("Failed to iterate through journal: %s", strerror(-r));
1540 return EXIT_FAILURE;
1541 }
1542
1543 if (!arg_no_pager && !arg_follow)
1544 pager_open(arg_pager_end);
1545
1546 if (!arg_quiet) {
1547 usec_t start, end;
1548 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
1549
1550 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
1551 if (r < 0) {
1552 log_error("Failed to get cutoff: %s", strerror(-r));
1553 goto finish;
1554 }
1555
1556 if (r > 0) {
1557 if (arg_follow)
1558 printf("-- Logs begin at %s. --\n",
1559 format_timestamp(start_buf, sizeof(start_buf), start));
1560 else
1561 printf("-- Logs begin at %s, end at %s. --\n",
1562 format_timestamp(start_buf, sizeof(start_buf), start),
1563 format_timestamp(end_buf, sizeof(end_buf), end));
1564 }
1565 }
1566
1567 for (;;) {
1568 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
1569 int flags;
1570
1571 if (need_seek) {
1572 if (!arg_reverse)
1573 r = sd_journal_next(j);
1574 else
1575 r = sd_journal_previous(j);
1576 if (r < 0) {
1577 log_error("Failed to iterate through journal: %s", strerror(-r));
1578 goto finish;
1579 }
1580 if (r == 0)
1581 break;
1582 }
1583
1584 if (arg_until_set && !arg_reverse) {
1585 usec_t usec;
1586
1587 r = sd_journal_get_realtime_usec(j, &usec);
1588 if (r < 0) {
1589 log_error("Failed to determine timestamp: %s", strerror(-r));
1590 goto finish;
1591 }
1592 if (usec > arg_until)
1593 goto finish;
1594 }
1595
1596 if (arg_since_set && arg_reverse) {
1597 usec_t usec;
1598
1599 r = sd_journal_get_realtime_usec(j, &usec);
1600 if (r < 0) {
1601 log_error("Failed to determine timestamp: %s", strerror(-r));
1602 goto finish;
1603 }
1604 if (usec < arg_since)
1605 goto finish;
1606 }
1607
1608 if (!arg_merge) {
1609 sd_id128_t boot_id;
1610
1611 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1612 if (r >= 0) {
1613 if (previous_boot_id_valid &&
1614 !sd_id128_equal(boot_id, previous_boot_id))
1615 printf("%s-- Reboot --%s\n",
1616 ansi_highlight(), ansi_highlight_off());
1617
1618 previous_boot_id = boot_id;
1619 previous_boot_id_valid = true;
1620 }
1621 }
1622
1623 flags =
1624 arg_all * OUTPUT_SHOW_ALL |
1625 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
1626 on_tty() * OUTPUT_COLOR |
1627 arg_catalog * OUTPUT_CATALOG;
1628
1629 r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized);
1630 need_seek = true;
1631 if (r == -EADDRNOTAVAIL)
1632 break;
1633 else if (r < 0 || ferror(stdout))
1634 goto finish;
1635
1636 n_shown++;
1637 }
1638
1639 if (!arg_follow) {
1640 if (arg_show_cursor) {
1641 _cleanup_free_ char *cursor = NULL;
1642
1643 r = sd_journal_get_cursor(j, &cursor);
1644 if (r < 0 && r != -EADDRNOTAVAIL)
1645 log_error("Failed to get cursor: %s", strerror(-r));
1646 else if (r >= 0)
1647 printf("-- cursor: %s\n", cursor);
1648 }
1649
1650 break;
1651 }
1652
1653 r = sd_journal_wait(j, (uint64_t) -1);
1654 if (r < 0) {
1655 log_error("Couldn't wait for journal event: %s", strerror(-r));
1656 goto finish;
1657 }
1658
1659 first_line = false;
1660 }
1661
1662 finish:
1663 pager_close();
1664
1665 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1666 }