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;
94 } arg_action = ACTION_SHOW;
96 static int help(void) {
98 printf("%s [OPTIONS...] [MATCHES...]\n\n"
99 "Query the journal.\n\n"
101 " --since=DATE Start showing entries newer or of the specified date\n"
102 " --until=DATE Stop showing entries older or of the specified date\n"
103 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
104 " -b --this-boot Show data only from current boot\n"
105 " -u --unit=UNIT Show data only from the specified unit\n"
106 " --user-unit=UNIT Show data only from the specified user session unit\n"
107 " -p --priority=RANGE Show only messages within the specified priority range\n"
108 " -e --pager-end Immediately jump to end of the journal in the pager\n"
109 " -f --follow Follow journal\n"
110 " -n --lines[=INTEGER] Number of journal entries to show\n"
111 " --no-tail Show all lines, even in follow mode\n"
112 " -r --reverse Show the newest entries first\n"
113 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
114 " verbose, export, json, json-pretty, json-sse, cat)\n"
115 " -x --catalog Add message explanations where available\n"
116 " --full Do not ellipsize fields\n"
117 " -a --all Show all fields, including long and unprintable\n"
118 " -q --quiet Don't show privilege warning\n"
119 " --no-pager Do not pipe output into a pager\n"
120 " -m --merge Show entries from all available journals\n"
121 " -D --directory=PATH Show journal files from directory\n"
123 " --interval=TIME Time interval for changing the FSS sealing key\n"
124 " --verify-key=KEY Specify FSS verification key\n"
127 " -h --help Show this help\n"
128 " --version Show package version\n"
129 " --new-id128 Generate a new 128 Bit ID\n"
130 " --header Show journal header information\n"
131 " --disk-usage Show total disk usage\n"
132 " -F --field=FIELD List all values a certain field takes\n"
133 " --list-catalog Show message IDs of all entries in the message catalog\n"
134 " --update-catalog Update the message catalog database\n"
136 " --setup-keys Generate new FSS key pair\n"
137 " --verify Verify journal file consistency\n"
139 , program_invocation_short_name);
144 static int parse_argv(int argc, char *argv[]) {
165 static const struct option options[] = {
166 { "help", no_argument, NULL, 'h' },
167 { "version" , no_argument, NULL, ARG_VERSION },
168 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
169 { "pager-end", no_argument, NULL, 'e' },
170 { "follow", no_argument, NULL, 'f' },
171 { "output", required_argument, NULL, 'o' },
172 { "all", no_argument, NULL, 'a' },
173 { "full", no_argument, NULL, ARG_FULL },
174 { "lines", optional_argument, NULL, 'n' },
175 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
176 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
177 { "quiet", no_argument, NULL, 'q' },
178 { "merge", no_argument, NULL, 'm' },
179 { "this-boot", no_argument, NULL, 'b' },
180 { "directory", required_argument, NULL, 'D' },
181 { "header", no_argument, NULL, ARG_HEADER },
182 { "priority", required_argument, NULL, 'p' },
183 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
184 { "interval", required_argument, NULL, ARG_INTERVAL },
185 { "verify", no_argument, NULL, ARG_VERIFY },
186 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
187 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
188 { "cursor", required_argument, NULL, 'c' },
189 { "since", required_argument, NULL, ARG_SINCE },
190 { "until", required_argument, NULL, ARG_UNTIL },
191 { "unit", required_argument, NULL, 'u' },
192 { "user-unit", required_argument, NULL, ARG_USER_UNIT },
193 { "field", required_argument, NULL, 'F' },
194 { "catalog", no_argument, NULL, 'x' },
195 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
196 { "update-catalog",no_argument, NULL, ARG_UPDATE_CATALOG },
197 { "reverse", no_argument, NULL, 'r' },
206 while ((c = getopt_long(argc, argv, "hefo:an::qmbD:p:c:u:F:xr", options, NULL)) >= 0) {
215 puts(PACKAGE_STRING);
216 puts(SYSTEMD_FEATURES);
224 arg_pager_end = true;
236 arg_output = output_mode_from_string(optarg);
237 if (arg_output < 0) {
238 log_error("Unknown output format '%s'.", optarg);
242 if (arg_output == OUTPUT_EXPORT ||
243 arg_output == OUTPUT_JSON ||
244 arg_output == OUTPUT_JSON_PRETTY ||
245 arg_output == OUTPUT_JSON_SSE ||
246 arg_output == OUTPUT_CAT)
261 r = safe_atoi(optarg, &arg_lines);
262 if (r < 0 || arg_lines < 0) {
263 log_error("Failed to parse lines '%s'", optarg);
269 /* Hmm, no argument? Maybe the next
270 * word on the command line is
271 * supposed to be the argument? Let's
272 * see if there is one, and is
273 * parsable as a positive
277 safe_atoi(argv[optind], &n) >= 0 &&
293 arg_action = ACTION_NEW_ID128;
305 arg_this_boot = true;
309 arg_directory = optarg;
317 arg_action = ACTION_PRINT_HEADER;
321 arg_action = ACTION_VERIFY;
325 arg_action = ACTION_DISK_USAGE;
330 arg_action = ACTION_SETUP_KEYS;
335 arg_action = ACTION_VERIFY;
336 arg_verify_key = optarg;
341 r = parse_usec(optarg, &arg_interval);
342 if (r < 0 || arg_interval <= 0) {
343 log_error("Failed to parse sealing key change interval: %s", optarg);
351 log_error("Forward-secure sealing not available.");
358 dots = strstr(optarg, "..");
364 a = strndup(optarg, dots - optarg);
368 from = log_level_from_string(a);
369 to = log_level_from_string(dots + 2);
372 if (from < 0 || to < 0) {
373 log_error("Failed to parse log level range %s", optarg);
380 for (i = from; i <= to; i++)
381 arg_priorities |= 1 << i;
383 for (i = to; i <= from; i++)
384 arg_priorities |= 1 << i;
390 p = log_level_from_string(optarg);
392 log_error("Unknown log level %s", optarg);
398 for (i = 0; i <= p; i++)
399 arg_priorities |= 1 << i;
406 r = parse_timestamp(optarg, &arg_since);
408 log_error("Failed to parse timestamp: %s", optarg);
411 arg_since_set = true;
415 r = parse_timestamp(optarg, &arg_until);
417 log_error("Failed to parse timestamp: %s", optarg);
420 arg_until_set = true;
425 arg_unit_system = true;
430 arg_unit_system = false;
444 case ARG_LIST_CATALOG:
445 arg_action = ACTION_LIST_CATALOG;
448 case ARG_UPDATE_CATALOG:
449 arg_action = ACTION_UPDATE_CATALOG;
457 log_error("Unknown option code %c", c);
462 if (arg_follow && !arg_no_tail && arg_lines < 0)
465 if (arg_since_set && arg_until_set && arg_since > arg_until) {
466 log_error("--since= must be before --until=.");
470 if (arg_cursor && arg_since_set) {
471 log_error("Please specify either --since= or --cursor=, not both.");
475 if (arg_follow && arg_reverse) {
476 log_error("Please specify either --reverse= or --follow=, not both.");
483 static int generate_new_id128(void) {
488 r = sd_id128_randomize(&id);
490 log_error("Failed to generate ID: %s", strerror(-r));
494 printf("As string:\n"
495 SD_ID128_FORMAT_STR "\n\n"
497 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
499 "#define MESSAGE_XYZ SD_ID128_MAKE(",
500 SD_ID128_FORMAT_VAL(id),
501 SD_ID128_FORMAT_VAL(id));
502 for (i = 0; i < 16; i++)
503 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
504 fputs(")\n\n", stdout);
506 printf("As Python constant:\n"
508 ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
509 SD_ID128_FORMAT_VAL(id));
514 static int add_matches(sd_journal *j, char **args) {
519 STRV_FOREACH(i, args) {
523 r = sd_journal_add_disjunction(j);
524 else if (path_is_absolute(*i)) {
525 char _cleanup_free_ *p, *t = NULL;
529 p = canonicalize_file_name(*i);
532 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));
544 log_error("File is not a device node, regular file or is not executable: %s", *i);
551 r = sd_journal_add_match(j, t, 0);
553 r = sd_journal_add_match(j, *i, 0);
556 log_error("Failed to add match '%s': %s", *i, strerror(-r));
564 static int add_this_boot(sd_journal *j) {
565 char match[9+32+1] = "_BOOT_ID=";
574 r = sd_id128_get_boot(&boot_id);
576 log_error("Failed to get boot id: %s", strerror(-r));
580 sd_id128_to_string(boot_id, match + 9);
581 r = sd_journal_add_match(j, match, strlen(match));
583 log_error("Failed to add match: %s", strerror(-r));
590 static int add_unit(sd_journal *j) {
591 _cleanup_free_ char *m = NULL, *u = NULL;
596 if (isempty(arg_unit))
599 u = unit_name_mangle(arg_unit);
604 r = add_matches_for_unit(j, u);
606 r = add_matches_for_user_unit(j, u, getuid());
613 static int add_priorities(sd_journal *j) {
614 char match[] = "PRIORITY=0";
619 if (arg_priorities == 0xFF)
622 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
623 if (arg_priorities & (1 << i)) {
624 match[sizeof(match)-2] = '0' + i;
626 r = sd_journal_add_match(j, match, strlen(match));
628 log_error("Failed to add match: %s", strerror(-r));
636 static int setup_keys(void) {
638 size_t mpk_size, seed_size, state_size, i;
639 uint8_t *mpk, *seed, *state;
641 int fd = -1, r, attr = 0;
642 sd_id128_t machine, boot;
643 char *p = NULL, *k = NULL;
647 r = sd_id128_get_machine(&machine);
649 log_error("Failed to get machine ID: %s", strerror(-r));
653 r = sd_id128_get_boot(&boot);
655 log_error("Failed to get boot ID: %s", strerror(-r));
659 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
660 SD_ID128_FORMAT_VAL(machine)) < 0)
663 if (access(p, F_OK) >= 0) {
664 log_error("Sealing key file %s exists already.", p);
669 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
670 SD_ID128_FORMAT_VAL(machine)) < 0) {
675 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
676 mpk = alloca(mpk_size);
678 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
679 seed = alloca(seed_size);
681 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
682 state = alloca(state_size);
684 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
686 log_error("Failed to open /dev/random: %m");
691 log_info("Generating seed...");
692 l = loop_read(fd, seed, seed_size, true);
693 if (l < 0 || (size_t) l != seed_size) {
694 log_error("Failed to read random seed: %s", strerror(EIO));
699 log_info("Generating key pair...");
700 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
702 log_info("Generating sealing key...");
703 FSPRG_GenState0(state, mpk, seed, seed_size);
705 assert(arg_interval > 0);
707 n = now(CLOCK_REALTIME);
710 close_nointr_nofail(fd);
711 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
713 log_error("Failed to open %s: %m", k);
718 /* Enable secure remove, exclusion from dump, synchronous
719 * writing and in-place updating */
720 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
721 log_warning("FS_IOC_GETFLAGS failed: %m");
723 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
725 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
726 log_warning("FS_IOC_SETFLAGS failed: %m");
729 memcpy(h.signature, "KSHHRHLP", 8);
730 h.machine_id = machine;
732 h.header_size = htole64(sizeof(h));
733 h.start_usec = htole64(n * arg_interval);
734 h.interval_usec = htole64(arg_interval);
735 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
736 h.fsprg_state_size = htole64(state_size);
738 l = loop_write(fd, &h, sizeof(h), false);
739 if (l < 0 || (size_t) l != sizeof(h)) {
740 log_error("Failed to write header: %s", strerror(EIO));
745 l = loop_write(fd, state, state_size, false);
746 if (l < 0 || (size_t) l != state_size) {
747 log_error("Failed to write state: %s", strerror(EIO));
752 if (link(k, p) < 0) {
753 log_error("Failed to link file: %m");
761 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
762 "the following local file. This key file is automatically updated when the\n"
763 "sealing key is advanced. It should not be used on multiple hosts.\n"
767 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
768 "at a safe location and should not be saved locally on disk.\n"
769 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
772 for (i = 0; i < seed_size; i++) {
773 if (i > 0 && i % 3 == 0)
775 printf("%02x", ((uint8_t*) seed)[i]);
778 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
781 char tsb[FORMAT_TIMESPAN_MAX], *hn;
784 ANSI_HIGHLIGHT_OFF "\n"
785 "The sealing key is automatically changed every %s.\n",
786 format_timespan(tsb, sizeof(tsb), arg_interval));
788 hn = gethostname_malloc();
791 hostname_cleanup(hn);
792 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
794 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
797 /* If this is not an UTF-8 system don't print any QR codes */
798 if (is_locale_utf8()) {
799 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
800 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
810 close_nointr_nofail(fd);
821 log_error("Forward-secure sealing not available.");
826 static int verify(sd_journal *j) {
833 log_show_color(true);
835 HASHMAP_FOREACH(f, j->files, i) {
837 usec_t first, validated, last;
840 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
841 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
844 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
846 /* If the key was invalid give up right-away. */
849 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
852 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
853 log_info("PASS: %s", f->path);
855 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
857 log_info("=> Validated from %s to %s, final %s entries not sealed.",
858 format_timestamp(a, sizeof(a), first),
859 format_timestamp(b, sizeof(b), validated),
860 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
862 log_info("=> No sealing yet, %s of entries not sealed.",
863 format_timespan(c, sizeof(c), last - first));
865 log_info("=> No sealing yet, no entries in file.");
873 static int access_check(void) {
876 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("systemd-journal") <= 0) {
877 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'systemd-journal' can always see messages.");
881 if (!arg_quiet && geteuid() != 0 && in_group("systemd-journal") <= 0)
882 log_warning("Showing user generated messages only. Users in the group 'systemd-journal' can see all messages. Pass -q to turn this notice off.");
884 if (geteuid() != 0 && in_group("systemd-journal") <= 0) {
885 log_error("No access to messages. Only users in the group 'systemd-journal' can see messages.");
893 int main(int argc, char *argv[]) {
895 sd_journal _cleanup_journal_close_ *j = NULL;
896 bool need_seek = false;
897 sd_id128_t previous_boot_id;
898 bool previous_boot_id_valid = false, first_line = true;
901 setlocale(LC_ALL, "");
902 log_parse_environment();
905 r = parse_argv(argc, argv);
909 signal(SIGWINCH, columns_lines_cache_reset);
911 if (arg_action == ACTION_NEW_ID128) {
912 r = generate_new_id128();
916 if (arg_action == ACTION_SETUP_KEYS) {
921 if (arg_action == ACTION_LIST_CATALOG) {
922 r = catalog_list(stdout);
924 log_error("Failed to list catalog: %s", strerror(-r));
928 if (arg_action == ACTION_UPDATE_CATALOG) {
929 r = catalog_update();
938 r = sd_journal_open_directory(&j, arg_directory, 0);
940 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
942 log_error("Failed to open journal: %s", strerror(-r));
946 if (arg_action == ACTION_VERIFY) {
951 if (arg_action == ACTION_PRINT_HEADER) {
952 journal_print_header(j);
956 if (arg_action == ACTION_DISK_USAGE) {
958 char sbytes[FORMAT_BYTES_MAX];
960 r = sd_journal_get_usage(j, &bytes);
964 printf("Journals take up %s on disk.\n",
965 format_bytes(sbytes, sizeof(sbytes), bytes));
969 r = add_this_boot(j);
977 r = add_matches(j, argv + optind);
981 r = add_priorities(j);
985 /* Opening the fd now means the first sd_journal_wait() will actually wait */
986 r = sd_journal_get_fd(j);
994 r = sd_journal_query_unique(j, arg_field);
996 log_error("Failed to query unique data objects: %s", strerror(-r));
1000 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
1003 if (arg_lines >= 0 && n_shown >= arg_lines)
1006 eq = memchr(data, '=', size);
1008 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
1010 printf("%.*s\n", (int) size, (const char*) data);
1015 return EXIT_SUCCESS;
1019 r = sd_journal_seek_cursor(j, arg_cursor);
1021 log_error("Failed to seek to cursor: %s", strerror(-r));
1022 return EXIT_FAILURE;
1025 r = sd_journal_next(j);
1027 r = sd_journal_previous(j);
1029 } else if (arg_since_set && !arg_reverse) {
1030 r = sd_journal_seek_realtime_usec(j, arg_since);
1032 log_error("Failed to seek to date: %s", strerror(-r));
1033 return EXIT_FAILURE;
1035 r = sd_journal_next(j);
1037 } else if (arg_until_set && arg_reverse) {
1038 r = sd_journal_seek_realtime_usec(j, arg_until);
1040 log_error("Failed to seek to date: %s", strerror(-r));
1041 return EXIT_FAILURE;
1043 r = sd_journal_previous(j);
1045 } else if (arg_lines >= 0) {
1046 r = sd_journal_seek_tail(j);
1048 log_error("Failed to seek to tail: %s", strerror(-r));
1049 return EXIT_FAILURE;
1052 r = sd_journal_previous_skip(j, arg_lines);
1054 } else if (arg_reverse) {
1055 r = sd_journal_seek_tail(j);
1057 log_error("Failed to seek to tail: %s", strerror(-r));
1058 return EXIT_FAILURE;
1061 r = sd_journal_previous(j);
1064 r = sd_journal_seek_head(j);
1066 log_error("Failed to seek to head: %s", strerror(-r));
1067 return EXIT_FAILURE;
1070 r = sd_journal_next(j);
1074 log_error("Failed to iterate through journal: %s", strerror(-r));
1075 return EXIT_FAILURE;
1078 if (!arg_no_pager && !arg_follow)
1079 pager_open(arg_pager_end);
1083 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
1085 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
1087 log_error("Failed to get cutoff: %s", strerror(-r));
1093 printf("-- Logs begin at %s. --\n",
1094 format_timestamp(start_buf, sizeof(start_buf), start));
1096 printf("-- Logs begin at %s, end at %s. --\n",
1097 format_timestamp(start_buf, sizeof(start_buf), start),
1098 format_timestamp(end_buf, sizeof(end_buf), end));
1103 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
1108 r = sd_journal_next(j);
1110 r = sd_journal_previous(j);
1112 log_error("Failed to iterate through journal: %s", strerror(-r));
1120 if (arg_until_set && !arg_reverse) {
1123 r = sd_journal_get_realtime_usec(j, &usec);
1125 log_error("Failed to determine timestamp: %s", strerror(-r));
1128 if (usec > arg_until)
1132 if (arg_since_set && arg_reverse) {
1135 r = sd_journal_get_realtime_usec(j, &usec);
1137 log_error("Failed to determine timestamp: %s", strerror(-r));
1140 if (usec < arg_since)
1147 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1149 if (previous_boot_id_valid &&
1150 !sd_id128_equal(boot_id, previous_boot_id))
1151 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
1153 previous_boot_id = boot_id;
1154 previous_boot_id_valid = true;
1159 arg_all * OUTPUT_SHOW_ALL |
1160 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
1161 on_tty() * OUTPUT_COLOR |
1162 arg_catalog * OUTPUT_CATALOG;
1164 r = output_journal(stdout, j, arg_output, 0, flags);
1165 if (r < 0 || ferror(stdout))
1175 r = sd_journal_wait(j, (uint64_t) -1);
1177 log_error("Couldn't wait for journal event: %s", strerror(-r));
1187 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;