1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
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.
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.
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/>.
35 #include <sys/ioctl.h>
38 #include <systemd/sd-journal.h>
41 #include "logs-show.h"
43 #include "path-util.h"
46 #include "logs-show.h"
48 #include "journal-internal.h"
49 #include "journal-def.h"
50 #include "journal-verify.h"
51 #include "journal-authenticate.h"
52 #include "journal-qrcode.h"
54 #include "unit-name.h"
57 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
59 static OutputMode arg_output = OUTPUT_SHORT;
60 static bool arg_pager_end = false;
61 static bool arg_follow = false;
62 static bool arg_full = false;
63 static bool arg_all = false;
64 static bool arg_no_pager = false;
65 static int arg_lines = -1;
66 static bool arg_no_tail = false;
67 static bool arg_quiet = false;
68 static bool arg_merge = false;
69 static bool arg_this_boot = false;
70 static const char *arg_cursor = NULL;
71 static const char *arg_directory = NULL;
72 static int arg_priorities = 0xFF;
73 static const char *arg_verify_key = NULL;
75 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
77 static usec_t arg_since, arg_until;
78 static bool arg_since_set = false, arg_until_set = false;
79 static const char *arg_unit = NULL;
80 static bool arg_unit_system;
81 static const char *arg_field = NULL;
82 static bool arg_catalog = false;
83 static bool arg_reverse = false;
95 } arg_action = ACTION_SHOW;
97 static int help(void) {
99 printf("%s [OPTIONS...] [MATCHES...]\n\n"
100 "Query the journal.\n\n"
102 " --since=DATE Start showing entries newer or of the specified date\n"
103 " --until=DATE Stop showing entries older or of the specified date\n"
104 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
105 " -b --this-boot Show data only from current boot\n"
106 " -u --unit=UNIT Show data only from the specified unit\n"
107 " --user-unit=UNIT Show data only from the specified user session unit\n"
108 " -p --priority=RANGE Show only messages within the specified priority range\n"
109 " -e --pager-end Immediately jump to end of the journal in the pager\n"
110 " -f --follow Follow journal\n"
111 " -n --lines[=INTEGER] Number of journal entries to show\n"
112 " --no-tail Show all lines, even in follow mode\n"
113 " -r --reverse Show the newest entries first\n"
114 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
115 " verbose, export, json, json-pretty, json-sse, cat)\n"
116 " -x --catalog Add message explanations where available\n"
117 " --full Do not ellipsize fields\n"
118 " -a --all Show all fields, including long and unprintable\n"
119 " -q --quiet Don't show privilege warning\n"
120 " --no-pager Do not pipe output into a pager\n"
121 " -m --merge Show entries from all available journals\n"
122 " -D --directory=PATH Show journal files from directory\n"
124 " --interval=TIME Time interval for changing the FSS sealing key\n"
125 " --verify-key=KEY Specify FSS verification key\n"
128 " -h --help Show this help\n"
129 " --version Show package version\n"
130 " --new-id128 Generate a new 128 Bit ID\n"
131 " --header Show journal header information\n"
132 " --disk-usage Show total disk usage\n"
133 " -F --field=FIELD List all values a certain field takes\n"
134 " --list-catalog Show message IDs of all entries in the message catalog\n"
135 " --dump-catalog Show entries in the message catalog\n"
136 " --update-catalog Update the message catalog database\n"
138 " --setup-keys Generate new FSS key pair\n"
139 " --verify Verify journal file consistency\n"
141 , program_invocation_short_name);
146 static int parse_argv(int argc, char *argv[]) {
168 static const struct option options[] = {
169 { "help", no_argument, NULL, 'h' },
170 { "version" , no_argument, NULL, ARG_VERSION },
171 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
172 { "pager-end", no_argument, NULL, 'e' },
173 { "follow", no_argument, NULL, 'f' },
174 { "output", required_argument, NULL, 'o' },
175 { "all", no_argument, NULL, 'a' },
176 { "full", no_argument, NULL, ARG_FULL },
177 { "lines", optional_argument, NULL, 'n' },
178 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
179 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
180 { "quiet", no_argument, NULL, 'q' },
181 { "merge", no_argument, NULL, 'm' },
182 { "this-boot", no_argument, NULL, 'b' },
183 { "directory", required_argument, NULL, 'D' },
184 { "header", no_argument, NULL, ARG_HEADER },
185 { "priority", required_argument, NULL, 'p' },
186 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
187 { "interval", required_argument, NULL, ARG_INTERVAL },
188 { "verify", no_argument, NULL, ARG_VERIFY },
189 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
190 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
191 { "cursor", required_argument, NULL, 'c' },
192 { "since", required_argument, NULL, ARG_SINCE },
193 { "until", required_argument, NULL, ARG_UNTIL },
194 { "unit", required_argument, NULL, 'u' },
195 { "user-unit", required_argument, NULL, ARG_USER_UNIT },
196 { "field", required_argument, NULL, 'F' },
197 { "catalog", no_argument, NULL, 'x' },
198 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
199 { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
200 { "update-catalog",no_argument, NULL, ARG_UPDATE_CATALOG },
201 { "reverse", no_argument, NULL, 'r' },
210 while ((c = getopt_long(argc, argv, "hefo:an::qmbD:p:c:u:F:xr", options, NULL)) >= 0) {
219 puts(PACKAGE_STRING);
220 puts(SYSTEMD_FEATURES);
228 arg_pager_end = true;
240 arg_output = output_mode_from_string(optarg);
241 if (arg_output < 0) {
242 log_error("Unknown output format '%s'.", optarg);
246 if (arg_output == OUTPUT_EXPORT ||
247 arg_output == OUTPUT_JSON ||
248 arg_output == OUTPUT_JSON_PRETTY ||
249 arg_output == OUTPUT_JSON_SSE ||
250 arg_output == OUTPUT_CAT)
265 r = safe_atoi(optarg, &arg_lines);
266 if (r < 0 || arg_lines < 0) {
267 log_error("Failed to parse lines '%s'", optarg);
273 /* Hmm, no argument? Maybe the next
274 * word on the command line is
275 * supposed to be the argument? Let's
276 * see if there is one, and is
277 * parsable as a positive
281 safe_atoi(argv[optind], &n) >= 0 &&
297 arg_action = ACTION_NEW_ID128;
309 arg_this_boot = true;
313 arg_directory = optarg;
321 arg_action = ACTION_PRINT_HEADER;
325 arg_action = ACTION_VERIFY;
329 arg_action = ACTION_DISK_USAGE;
334 arg_action = ACTION_SETUP_KEYS;
339 arg_action = ACTION_VERIFY;
340 arg_verify_key = optarg;
345 r = parse_usec(optarg, &arg_interval);
346 if (r < 0 || arg_interval <= 0) {
347 log_error("Failed to parse sealing key change interval: %s", optarg);
355 log_error("Forward-secure sealing not available.");
362 dots = strstr(optarg, "..");
368 a = strndup(optarg, dots - optarg);
372 from = log_level_from_string(a);
373 to = log_level_from_string(dots + 2);
376 if (from < 0 || to < 0) {
377 log_error("Failed to parse log level range %s", optarg);
384 for (i = from; i <= to; i++)
385 arg_priorities |= 1 << i;
387 for (i = to; i <= from; i++)
388 arg_priorities |= 1 << i;
394 p = log_level_from_string(optarg);
396 log_error("Unknown log level %s", optarg);
402 for (i = 0; i <= p; i++)
403 arg_priorities |= 1 << i;
410 r = parse_timestamp(optarg, &arg_since);
412 log_error("Failed to parse timestamp: %s", optarg);
415 arg_since_set = true;
419 r = parse_timestamp(optarg, &arg_until);
421 log_error("Failed to parse timestamp: %s", optarg);
424 arg_until_set = true;
429 arg_unit_system = true;
434 arg_unit_system = false;
448 case ARG_LIST_CATALOG:
449 arg_action = ACTION_LIST_CATALOG;
452 case ARG_DUMP_CATALOG:
453 arg_action = ACTION_DUMP_CATALOG;
456 case ARG_UPDATE_CATALOG:
457 arg_action = ACTION_UPDATE_CATALOG;
465 log_error("Unknown option code %c", c);
470 if (arg_follow && !arg_no_tail && arg_lines < 0)
473 if (arg_since_set && arg_until_set && arg_since > arg_until) {
474 log_error("--since= must be before --until=.");
478 if (arg_cursor && arg_since_set) {
479 log_error("Please specify either --since= or --cursor=, not both.");
483 if (arg_follow && arg_reverse) {
484 log_error("Please specify either --reverse= or --follow=, not both.");
491 static int generate_new_id128(void) {
496 r = sd_id128_randomize(&id);
498 log_error("Failed to generate ID: %s", strerror(-r));
502 printf("As string:\n"
503 SD_ID128_FORMAT_STR "\n\n"
505 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
507 "#define MESSAGE_XYZ SD_ID128_MAKE(",
508 SD_ID128_FORMAT_VAL(id),
509 SD_ID128_FORMAT_VAL(id));
510 for (i = 0; i < 16; i++)
511 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
512 fputs(")\n\n", stdout);
514 printf("As Python constant:\n"
516 ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
517 SD_ID128_FORMAT_VAL(id));
522 static int add_matches(sd_journal *j, char **args) {
527 STRV_FOREACH(i, args) {
531 r = sd_journal_add_disjunction(j);
532 else if (path_is_absolute(*i)) {
533 char _cleanup_free_ *p, *t = NULL;
537 p = canonicalize_file_name(*i);
540 if (stat(path, &st) < 0) {
541 log_error("Couldn't stat file: %m");
545 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
546 t = strappend("_EXE=", path);
547 else if (S_ISCHR(st.st_mode))
548 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
549 else if (S_ISBLK(st.st_mode))
550 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
552 log_error("File is not a device node, regular file or is not executable: %s", *i);
559 r = sd_journal_add_match(j, t, 0);
561 r = sd_journal_add_match(j, *i, 0);
564 log_error("Failed to add match '%s': %s", *i, strerror(-r));
572 static int add_this_boot(sd_journal *j) {
573 char match[9+32+1] = "_BOOT_ID=";
582 r = sd_id128_get_boot(&boot_id);
584 log_error("Failed to get boot id: %s", strerror(-r));
588 sd_id128_to_string(boot_id, match + 9);
589 r = sd_journal_add_match(j, match, strlen(match));
591 log_error("Failed to add match: %s", strerror(-r));
598 static int add_unit(sd_journal *j) {
599 _cleanup_free_ char *u = NULL;
604 if (isempty(arg_unit))
607 u = unit_name_mangle(arg_unit);
612 r = add_matches_for_unit(j, u);
614 r = add_matches_for_user_unit(j, u, getuid());
621 static int add_priorities(sd_journal *j) {
622 char match[] = "PRIORITY=0";
627 if (arg_priorities == 0xFF)
630 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
631 if (arg_priorities & (1 << i)) {
632 match[sizeof(match)-2] = '0' + i;
634 r = sd_journal_add_match(j, match, strlen(match));
636 log_error("Failed to add match: %s", strerror(-r));
644 static int setup_keys(void) {
646 size_t mpk_size, seed_size, state_size, i;
647 uint8_t *mpk, *seed, *state;
649 int fd = -1, r, attr = 0;
650 sd_id128_t machine, boot;
651 char *p = NULL, *k = NULL;
655 r = sd_id128_get_machine(&machine);
657 log_error("Failed to get machine ID: %s", strerror(-r));
661 r = sd_id128_get_boot(&boot);
663 log_error("Failed to get boot ID: %s", strerror(-r));
667 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
668 SD_ID128_FORMAT_VAL(machine)) < 0)
671 if (access(p, F_OK) >= 0) {
672 log_error("Sealing key file %s exists already.", p);
677 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
678 SD_ID128_FORMAT_VAL(machine)) < 0) {
683 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
684 mpk = alloca(mpk_size);
686 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
687 seed = alloca(seed_size);
689 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
690 state = alloca(state_size);
692 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
694 log_error("Failed to open /dev/random: %m");
699 log_info("Generating seed...");
700 l = loop_read(fd, seed, seed_size, true);
701 if (l < 0 || (size_t) l != seed_size) {
702 log_error("Failed to read random seed: %s", strerror(EIO));
707 log_info("Generating key pair...");
708 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
710 log_info("Generating sealing key...");
711 FSPRG_GenState0(state, mpk, seed, seed_size);
713 assert(arg_interval > 0);
715 n = now(CLOCK_REALTIME);
718 close_nointr_nofail(fd);
719 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
721 log_error("Failed to open %s: %m", k);
726 /* Enable secure remove, exclusion from dump, synchronous
727 * writing and in-place updating */
728 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
729 log_warning("FS_IOC_GETFLAGS failed: %m");
731 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
733 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
734 log_warning("FS_IOC_SETFLAGS failed: %m");
737 memcpy(h.signature, "KSHHRHLP", 8);
738 h.machine_id = machine;
740 h.header_size = htole64(sizeof(h));
741 h.start_usec = htole64(n * arg_interval);
742 h.interval_usec = htole64(arg_interval);
743 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
744 h.fsprg_state_size = htole64(state_size);
746 l = loop_write(fd, &h, sizeof(h), false);
747 if (l < 0 || (size_t) l != sizeof(h)) {
748 log_error("Failed to write header: %s", strerror(EIO));
753 l = loop_write(fd, state, state_size, false);
754 if (l < 0 || (size_t) l != state_size) {
755 log_error("Failed to write state: %s", strerror(EIO));
760 if (link(k, p) < 0) {
761 log_error("Failed to link file: %m");
769 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
770 "the following local file. This key file is automatically updated when the\n"
771 "sealing key is advanced. It should not be used on multiple hosts.\n"
775 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
776 "at a safe location and should not be saved locally on disk.\n"
777 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
780 for (i = 0; i < seed_size; i++) {
781 if (i > 0 && i % 3 == 0)
783 printf("%02x", ((uint8_t*) seed)[i]);
786 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
789 char tsb[FORMAT_TIMESPAN_MAX], *hn;
792 ANSI_HIGHLIGHT_OFF "\n"
793 "The sealing key is automatically changed every %s.\n",
794 format_timespan(tsb, sizeof(tsb), arg_interval));
796 hn = gethostname_malloc();
799 hostname_cleanup(hn);
800 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
802 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
805 /* If this is not an UTF-8 system don't print any QR codes */
806 if (is_locale_utf8()) {
807 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
808 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
818 close_nointr_nofail(fd);
829 log_error("Forward-secure sealing not available.");
834 static int verify(sd_journal *j) {
841 log_show_color(true);
843 HASHMAP_FOREACH(f, j->files, i) {
845 usec_t first, validated, last;
848 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
849 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
852 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
854 /* If the key was invalid give up right-away. */
857 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
860 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
861 log_info("PASS: %s", f->path);
863 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
865 log_info("=> Validated from %s to %s, final %s entries not sealed.",
866 format_timestamp(a, sizeof(a), first),
867 format_timestamp(b, sizeof(b), validated),
868 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
870 log_info("=> No sealing yet, %s of entries not sealed.",
871 format_timespan(c, sizeof(c), last - first));
873 log_info("=> No sealing yet, no entries in file.");
881 static int access_check(void) {
884 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("systemd-journal") <= 0) {
885 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'systemd-journal' can always see messages.");
889 if (!arg_quiet && geteuid() != 0 && in_group("systemd-journal") <= 0)
890 log_warning("Showing user generated messages only. Users in the group 'systemd-journal' can see all messages. Pass -q to turn this notice off.");
892 if (geteuid() != 0 && in_group("systemd-journal") <= 0) {
893 log_error("No access to messages. Only users in the group 'systemd-journal' can see messages.");
901 int main(int argc, char *argv[]) {
903 sd_journal _cleanup_journal_close_ *j = NULL;
904 bool need_seek = false;
905 sd_id128_t previous_boot_id;
906 bool previous_boot_id_valid = false, first_line = true;
909 setlocale(LC_ALL, "");
910 log_parse_environment();
913 r = parse_argv(argc, argv);
917 signal(SIGWINCH, columns_lines_cache_reset);
919 if (arg_action == ACTION_NEW_ID128) {
920 r = generate_new_id128();
924 if (arg_action == ACTION_SETUP_KEYS) {
929 if (arg_action == ACTION_LIST_CATALOG ||
930 arg_action == ACTION_DUMP_CATALOG) {
931 bool oneline = arg_action == ACTION_LIST_CATALOG;
933 r = catalog_list_items(stdout, oneline, argv + optind);
935 r = catalog_list(stdout, oneline);
937 log_error("Failed to list catalog: %s", strerror(-r));
941 if (arg_action == ACTION_UPDATE_CATALOG) {
942 r = catalog_update();
951 r = sd_journal_open_directory(&j, arg_directory, 0);
953 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
955 log_error("Failed to open journal: %s", strerror(-r));
959 if (arg_action == ACTION_VERIFY) {
964 if (arg_action == ACTION_PRINT_HEADER) {
965 journal_print_header(j);
969 if (arg_action == ACTION_DISK_USAGE) {
971 char sbytes[FORMAT_BYTES_MAX];
973 r = sd_journal_get_usage(j, &bytes);
977 printf("Journals take up %s on disk.\n",
978 format_bytes(sbytes, sizeof(sbytes), bytes));
982 r = add_this_boot(j);
990 r = add_matches(j, argv + optind);
994 r = add_priorities(j);
998 /* Opening the fd now means the first sd_journal_wait() will actually wait */
999 r = sd_journal_get_fd(j);
1001 return EXIT_FAILURE;
1007 r = sd_journal_query_unique(j, arg_field);
1009 log_error("Failed to query unique data objects: %s", strerror(-r));
1010 return EXIT_FAILURE;
1013 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
1016 if (arg_lines >= 0 && n_shown >= arg_lines)
1019 eq = memchr(data, '=', size);
1021 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
1023 printf("%.*s\n", (int) size, (const char*) data);
1028 return EXIT_SUCCESS;
1032 r = sd_journal_seek_cursor(j, arg_cursor);
1034 log_error("Failed to seek to cursor: %s", strerror(-r));
1035 return EXIT_FAILURE;
1038 r = sd_journal_next(j);
1040 r = sd_journal_previous(j);
1042 } else if (arg_since_set && !arg_reverse) {
1043 r = sd_journal_seek_realtime_usec(j, arg_since);
1045 log_error("Failed to seek to date: %s", strerror(-r));
1046 return EXIT_FAILURE;
1048 r = sd_journal_next(j);
1050 } else if (arg_until_set && arg_reverse) {
1051 r = sd_journal_seek_realtime_usec(j, arg_until);
1053 log_error("Failed to seek to date: %s", strerror(-r));
1054 return EXIT_FAILURE;
1056 r = sd_journal_previous(j);
1058 } else if (arg_lines >= 0) {
1059 r = sd_journal_seek_tail(j);
1061 log_error("Failed to seek to tail: %s", strerror(-r));
1062 return EXIT_FAILURE;
1065 r = sd_journal_previous_skip(j, arg_lines);
1067 } else if (arg_reverse) {
1068 r = sd_journal_seek_tail(j);
1070 log_error("Failed to seek to tail: %s", strerror(-r));
1071 return EXIT_FAILURE;
1074 r = sd_journal_previous(j);
1077 r = sd_journal_seek_head(j);
1079 log_error("Failed to seek to head: %s", strerror(-r));
1080 return EXIT_FAILURE;
1083 r = sd_journal_next(j);
1087 log_error("Failed to iterate through journal: %s", strerror(-r));
1088 return EXIT_FAILURE;
1091 if (!arg_no_pager && !arg_follow)
1092 pager_open(arg_pager_end);
1096 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
1098 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
1100 log_error("Failed to get cutoff: %s", strerror(-r));
1106 printf("-- Logs begin at %s. --\n",
1107 format_timestamp(start_buf, sizeof(start_buf), start));
1109 printf("-- Logs begin at %s, end at %s. --\n",
1110 format_timestamp(start_buf, sizeof(start_buf), start),
1111 format_timestamp(end_buf, sizeof(end_buf), end));
1116 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
1121 r = sd_journal_next(j);
1123 r = sd_journal_previous(j);
1125 log_error("Failed to iterate through journal: %s", strerror(-r));
1133 if (arg_until_set && !arg_reverse) {
1136 r = sd_journal_get_realtime_usec(j, &usec);
1138 log_error("Failed to determine timestamp: %s", strerror(-r));
1141 if (usec > arg_until)
1145 if (arg_since_set && arg_reverse) {
1148 r = sd_journal_get_realtime_usec(j, &usec);
1150 log_error("Failed to determine timestamp: %s", strerror(-r));
1153 if (usec < arg_since)
1160 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1162 if (previous_boot_id_valid &&
1163 !sd_id128_equal(boot_id, previous_boot_id))
1164 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
1166 previous_boot_id = boot_id;
1167 previous_boot_id_valid = true;
1172 arg_all * OUTPUT_SHOW_ALL |
1173 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
1174 on_tty() * OUTPUT_COLOR |
1175 arg_catalog * OUTPUT_CATALOG;
1177 r = output_journal(stdout, j, arg_output, 0, flags);
1178 if (r < 0 || ferror(stdout))
1188 r = sd_journal_wait(j, (uint64_t) -1);
1190 log_error("Couldn't wait for journal event: %s", strerror(-r));
1200 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;