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>
42 #include "path-util.h"
45 #include "logs-show.h"
47 #include "journal-internal.h"
48 #include "journal-def.h"
49 #include "journal-verify.h"
50 #include "journal-authenticate.h"
51 #include "journal-qrcode.h"
53 #include "unit-name.h"
56 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
58 static OutputMode arg_output = OUTPUT_SHORT;
59 static bool arg_pager_end = false;
60 static bool arg_follow = false;
61 static bool arg_full = false;
62 static bool arg_all = false;
63 static bool arg_no_pager = false;
64 static int arg_lines = -1;
65 static bool arg_no_tail = false;
66 static bool arg_quiet = false;
67 static bool arg_merge = false;
68 static bool arg_this_boot = false;
69 static const char *arg_cursor = NULL;
70 static const char *arg_directory = NULL;
71 static int arg_priorities = 0xFF;
72 static const char *arg_verify_key = NULL;
74 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
76 static usec_t arg_since, arg_until;
77 static bool arg_since_set = false, arg_until_set = false;
78 static const char *arg_unit = NULL;
79 static const char *arg_unit_type = NULL;
80 static const char *arg_field = NULL;
81 static bool arg_catalog = false;
82 static bool arg_reverse = false;
93 } arg_action = ACTION_SHOW;
95 static int help(void) {
97 printf("%s [OPTIONS...] [MATCHES...]\n\n"
98 "Query the journal.\n\n"
100 " --since=DATE Start showing entries newer or of the specified date\n"
101 " --until=DATE Stop showing entries older or of the specified date\n"
102 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
103 " -b --this-boot Show data only from current boot\n"
104 " -u --unit=UNIT Show data only from the specified unit\n"
105 " --user-unit=UNIT Show data only from the specified user session unit\n"
106 " -p --priority=RANGE Show only messages within the specified priority range\n"
107 " -e --pager-end Immediately jump to end of the journal in the pager\n"
108 " -f --follow Follow journal\n"
109 " -n --lines[=INTEGER] Number of journal entries to show\n"
110 " --no-tail Show all lines, even in follow mode\n"
111 " -r --reverse Show the newest entries first\n"
112 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
113 " verbose, export, json, json-pretty, json-sse, cat)\n"
114 " -x --catalog Add message explanations where available\n"
115 " --full Do not ellipsize fields\n"
116 " -a --all Show all fields, including long and unprintable\n"
117 " -q --quiet Don't show privilege warning\n"
118 " --no-pager Do not pipe output into a pager\n"
119 " -m --merge Show entries from all available journals\n"
120 " -D --directory=PATH Show journal files from directory\n"
122 " --interval=TIME Time interval for changing the FSS sealing key\n"
123 " --verify-key=KEY Specify FSS verification key\n"
126 " -h --help Show this help\n"
127 " --version Show package version\n"
128 " --new-id128 Generate a new 128 Bit ID\n"
129 " --header Show journal header information\n"
130 " --disk-usage Show total disk usage\n"
131 " -F --field=FIELD List all values a certain field takes\n"
132 " --list-catalog Show message IDs of all entries in the message catalog\n"
133 " --update-catalog Update the message catalog database\n"
135 " --setup-keys Generate new FSS key pair\n"
136 " --verify Verify journal file consistency\n"
138 , program_invocation_short_name);
143 static int parse_argv(int argc, char *argv[]) {
164 static const struct option options[] = {
165 { "help", no_argument, NULL, 'h' },
166 { "version" , no_argument, NULL, ARG_VERSION },
167 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
168 { "pager-end", no_argument, NULL, 'e' },
169 { "follow", no_argument, NULL, 'f' },
170 { "output", required_argument, NULL, 'o' },
171 { "all", no_argument, NULL, 'a' },
172 { "full", no_argument, NULL, ARG_FULL },
173 { "lines", optional_argument, NULL, 'n' },
174 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
175 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
176 { "quiet", no_argument, NULL, 'q' },
177 { "merge", no_argument, NULL, 'm' },
178 { "this-boot", no_argument, NULL, 'b' },
179 { "directory", required_argument, NULL, 'D' },
180 { "header", no_argument, NULL, ARG_HEADER },
181 { "priority", required_argument, NULL, 'p' },
182 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
183 { "interval", required_argument, NULL, ARG_INTERVAL },
184 { "verify", no_argument, NULL, ARG_VERIFY },
185 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
186 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
187 { "cursor", required_argument, NULL, 'c' },
188 { "since", required_argument, NULL, ARG_SINCE },
189 { "until", required_argument, NULL, ARG_UNTIL },
190 { "user-unit", required_argument, NULL, ARG_USER_UNIT },
191 { "unit", required_argument, NULL, 'u' },
192 { "field", required_argument, NULL, 'F' },
193 { "catalog", no_argument, NULL, 'x' },
194 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
195 { "update-catalog",no_argument, NULL, ARG_UPDATE_CATALOG },
196 { "reverse", no_argument, NULL, 'r' },
205 while ((c = getopt_long(argc, argv, "hefo:an::qmbD:p:c:u:F:xr", options, NULL)) >= 0) {
214 puts(PACKAGE_STRING);
215 puts(SYSTEMD_FEATURES);
223 arg_pager_end = true;
235 arg_output = output_mode_from_string(optarg);
236 if (arg_output < 0) {
237 log_error("Unknown output format '%s'.", optarg);
241 if (arg_output == OUTPUT_EXPORT ||
242 arg_output == OUTPUT_JSON ||
243 arg_output == OUTPUT_JSON_PRETTY ||
244 arg_output == OUTPUT_JSON_SSE ||
245 arg_output == OUTPUT_CAT)
260 r = safe_atoi(optarg, &arg_lines);
261 if (r < 0 || arg_lines < 0) {
262 log_error("Failed to parse lines '%s'", optarg);
268 /* Hmm, no argument? Maybe the next
269 * word on the command line is
270 * supposed to be the argument? Let's
271 * see if there is one, and is
272 * parsable as a positive
276 safe_atoi(argv[optind], &n) >= 0 &&
292 arg_action = ACTION_NEW_ID128;
304 arg_this_boot = true;
308 arg_directory = optarg;
316 arg_action = ACTION_PRINT_HEADER;
320 arg_action = ACTION_VERIFY;
324 arg_action = ACTION_DISK_USAGE;
329 arg_action = ACTION_SETUP_KEYS;
334 arg_action = ACTION_VERIFY;
335 arg_verify_key = optarg;
340 r = parse_usec(optarg, &arg_interval);
341 if (r < 0 || arg_interval <= 0) {
342 log_error("Failed to parse sealing key change interval: %s", optarg);
350 log_error("Forward-secure sealing not available.");
357 dots = strstr(optarg, "..");
363 a = strndup(optarg, dots - optarg);
367 from = log_level_from_string(a);
368 to = log_level_from_string(dots + 2);
371 if (from < 0 || to < 0) {
372 log_error("Failed to parse log level range %s", optarg);
379 for (i = from; i <= to; i++)
380 arg_priorities |= 1 << i;
382 for (i = to; i <= from; i++)
383 arg_priorities |= 1 << i;
389 p = log_level_from_string(optarg);
391 log_error("Unknown log level %s", optarg);
397 for (i = 0; i <= p; i++)
398 arg_priorities |= 1 << i;
405 r = parse_timestamp(optarg, &arg_since);
407 log_error("Failed to parse timestamp: %s", optarg);
410 arg_since_set = true;
414 r = parse_timestamp(optarg, &arg_until);
416 log_error("Failed to parse timestamp: %s", optarg);
419 arg_until_set = true;
424 arg_unit_type = "_SYSTEMD_USER_UNIT=";
429 arg_unit_type = "_SYSTEMD_UNIT=";
443 case ARG_LIST_CATALOG:
444 arg_action = ACTION_LIST_CATALOG;
447 case ARG_UPDATE_CATALOG:
448 arg_action = ACTION_UPDATE_CATALOG;
456 log_error("Unknown option code %c", c);
461 if (arg_follow && !arg_no_tail && arg_lines < 0)
464 if (arg_since_set && arg_until_set && arg_since > arg_until) {
465 log_error("--since= must be before --until=.");
469 if (arg_cursor && arg_since_set) {
470 log_error("Please specify either --since= or --cursor=, not both.");
474 if (arg_follow && arg_reverse) {
475 log_error("Please specify either --reverse= or --follow=, not both.");
482 static int generate_new_id128(void) {
487 r = sd_id128_randomize(&id);
489 log_error("Failed to generate ID: %s", strerror(-r));
493 printf("As string:\n"
494 SD_ID128_FORMAT_STR "\n\n"
496 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
498 "#define MESSAGE_XYZ SD_ID128_MAKE(",
499 SD_ID128_FORMAT_VAL(id),
500 SD_ID128_FORMAT_VAL(id));
501 for (i = 0; i < 16; i++)
502 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
503 fputs(")\n\n", stdout);
505 printf("As Python constant:\n"
507 ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
508 SD_ID128_FORMAT_VAL(id));
513 static int add_matches(sd_journal *j, char **args) {
519 STRV_FOREACH(i, args) {
522 r = sd_journal_add_disjunction(j);
523 else if (path_is_absolute(*i)) {
528 p = canonicalize_file_name(*i);
531 if (stat(path, &st) < 0) {
533 log_error("Couldn't stat file: %m");
537 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
538 t = strappend("_EXE=", path);
539 else if (S_ISCHR(st.st_mode))
540 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
541 else if (S_ISBLK(st.st_mode))
542 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
545 log_error("File is not a device node, regular file or is not executable: %s", *i);
554 r = sd_journal_add_match(j, t, 0);
557 r = sd_journal_add_match(j, *i, 0);
560 log_error("Failed to add match '%s': %s", *i, strerror(-r));
568 static int add_this_boot(sd_journal *j) {
569 char match[9+32+1] = "_BOOT_ID=";
578 r = sd_id128_get_boot(&boot_id);
580 log_error("Failed to get boot id: %s", strerror(-r));
584 sd_id128_to_string(boot_id, match + 9);
585 r = sd_journal_add_match(j, match, strlen(match));
587 log_error("Failed to add match: %s", strerror(-r));
594 static int add_unit(sd_journal *j) {
595 _cleanup_free_ char *m = NULL, *u = NULL;
600 if (isempty(arg_unit))
603 u = unit_name_mangle(arg_unit);
607 m = strappend(arg_unit_type, u);
612 r = sd_journal_add_match(j, m, strlen(m));
614 log_error("Failed to add match: %s", strerror(-r));
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 *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 r = catalog_list(stdout);
932 log_error("Failed to list catalog: %s", strerror(-r));
936 if (arg_action == ACTION_UPDATE_CATALOG) {
937 r = catalog_update();
946 r = sd_journal_open_directory(&j, arg_directory, 0);
948 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
950 log_error("Failed to open journal: %s", strerror(-r));
954 if (arg_action == ACTION_VERIFY) {
959 if (arg_action == ACTION_PRINT_HEADER) {
960 journal_print_header(j);
965 if (arg_action == ACTION_DISK_USAGE) {
967 char sbytes[FORMAT_BYTES_MAX];
969 r = sd_journal_get_usage(j, &bytes);
973 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
978 r = add_this_boot(j);
986 r = add_matches(j, argv + optind);
990 r = add_priorities(j);
994 /* Opening the fd now means the first sd_journal_wait() will actually wait */
995 r = sd_journal_get_fd(j);
1003 r = sd_journal_query_unique(j, arg_field);
1005 log_error("Failed to query unique data objects: %s", strerror(-r));
1009 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
1012 if (arg_lines >= 0 && n_shown >= arg_lines)
1015 eq = memchr(data, '=', size);
1017 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
1019 printf("%.*s\n", (int) size, (const char*) data);
1029 r = sd_journal_seek_cursor(j, arg_cursor);
1031 log_error("Failed to seek to cursor: %s", strerror(-r));
1035 r = sd_journal_next(j);
1037 r = sd_journal_previous(j);
1039 } else if (arg_since_set && !arg_reverse) {
1040 r = sd_journal_seek_realtime_usec(j, arg_since);
1042 log_error("Failed to seek to date: %s", strerror(-r));
1045 r = sd_journal_next(j);
1047 } else if (arg_until_set && arg_reverse) {
1048 r = sd_journal_seek_realtime_usec(j, arg_until);
1050 log_error("Failed to seek to date: %s", strerror(-r));
1053 r = sd_journal_previous(j);
1055 } else if (arg_lines >= 0) {
1056 r = sd_journal_seek_tail(j);
1058 log_error("Failed to seek to tail: %s", strerror(-r));
1062 r = sd_journal_previous_skip(j, arg_lines);
1064 } else if (arg_reverse) {
1065 r = sd_journal_seek_tail(j);
1067 log_error("Failed to seek to tail: %s", strerror(-r));
1071 r = sd_journal_previous(j);
1074 r = sd_journal_seek_head(j);
1076 log_error("Failed to seek to head: %s", strerror(-r));
1080 r = sd_journal_next(j);
1084 log_error("Failed to iterate through journal: %s", strerror(-r));
1088 if (!arg_no_pager && !arg_follow)
1089 pager_open(arg_pager_end);
1093 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
1095 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
1097 log_error("Failed to get cutoff: %s", strerror(-r));
1103 printf("-- Logs begin at %s. --\n",
1104 format_timestamp(start_buf, sizeof(start_buf), start));
1106 printf("-- Logs begin at %s, end at %s. --\n",
1107 format_timestamp(start_buf, sizeof(start_buf), start),
1108 format_timestamp(end_buf, sizeof(end_buf), end));
1113 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
1118 r = sd_journal_next(j);
1120 r = sd_journal_previous(j);
1122 log_error("Failed to iterate through journal: %s", strerror(-r));
1130 if (arg_until_set && !arg_reverse) {
1133 r = sd_journal_get_realtime_usec(j, &usec);
1135 log_error("Failed to determine timestamp: %s", strerror(-r));
1138 if (usec > arg_until)
1142 if (arg_since_set && arg_reverse) {
1145 r = sd_journal_get_realtime_usec(j, &usec);
1147 log_error("Failed to determine timestamp: %s", strerror(-r));
1150 if (usec < arg_since)
1157 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1159 if (previous_boot_id_valid &&
1160 !sd_id128_equal(boot_id, previous_boot_id))
1161 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
1163 previous_boot_id = boot_id;
1164 previous_boot_id_valid = true;
1169 arg_all * OUTPUT_SHOW_ALL |
1170 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
1171 on_tty() * OUTPUT_COLOR |
1172 arg_catalog * OUTPUT_CATALOG;
1174 r = output_journal(stdout, j, arg_output, 0, flags);
1175 if (r < 0 || ferror(stdout))
1185 r = sd_journal_wait(j, (uint64_t) -1);
1187 log_error("Couldn't wait for journal event: %s", strerror(-r));
1196 sd_journal_close(j);
1200 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;