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_follow = false;
60 static bool arg_full = false;
61 static bool arg_all = false;
62 static bool arg_no_pager = false;
63 static int arg_lines = -1;
64 static bool arg_no_tail = false;
65 static bool arg_quiet = false;
66 static bool arg_merge = false;
67 static bool arg_this_boot = false;
68 static const char *arg_cursor = NULL;
69 static const char *arg_directory = NULL;
70 static int arg_priorities = 0xFF;
71 static const char *arg_verify_key = NULL;
73 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
75 static usec_t arg_since, arg_until;
76 static bool arg_since_set = false, arg_until_set = false;
77 static const char *arg_unit = NULL;
78 static const char *arg_field = NULL;
79 static bool arg_catalog = false;
80 static bool arg_reverse = false;
91 } arg_action = ACTION_SHOW;
93 static int help(void) {
95 printf("%s [OPTIONS...] [MATCHES...]\n\n"
96 "Query the journal.\n\n"
98 " --since=DATE Start showing entries newer or of the specified date\n"
99 " --until=DATE Stop showing entries older or of the specified date\n"
100 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
101 " -b --this-boot Show data only from current boot\n"
102 " -u --unit=UNIT Show data only from the specified unit\n"
103 " -p --priority=RANGE Show only messages within the specified priority range\n"
104 " -f --follow Follow journal\n"
105 " -n --lines[=INTEGER] Number of journal entries to show\n"
106 " --no-tail Show all lines, even in follow mode\n"
107 " -r --reverse Show the newest entries first\n"
108 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
109 " verbose, export, json, json-pretty, json-sse, cat)\n"
110 " -x --catalog Add message explanations where available\n"
111 " --full Do not ellipsize fields\n"
112 " -a --all Show all fields, including long and unprintable\n"
113 " -q --quiet Don't show privilege warning\n"
114 " --no-pager Do not pipe output into a pager\n"
115 " -m --merge Show entries from all available journals\n"
116 " -D --directory=PATH Show journal files from directory\n"
118 " --interval=TIME Time interval for changing the FSS sealing key\n"
119 " --verify-key=KEY Specify FSS verification key\n"
122 " -h --help Show this help\n"
123 " --version Show package version\n"
124 " --new-id128 Generate a new 128 Bit ID\n"
125 " --header Show journal header information\n"
126 " --disk-usage Show total disk usage\n"
127 " -F --field=FIELD List all values a certain field takes\n"
128 " --list-catalog Show message IDs of all entries in the message catalog\n"
129 " --update-catalog Update the message catalog database\n"
131 " --setup-keys Generate new FSS key pair\n"
132 " --verify Verify journal file consistency\n"
134 , program_invocation_short_name);
139 static int parse_argv(int argc, char *argv[]) {
159 static const struct option options[] = {
160 { "help", no_argument, NULL, 'h' },
161 { "version" , no_argument, NULL, ARG_VERSION },
162 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
163 { "follow", no_argument, NULL, 'f' },
164 { "output", required_argument, NULL, 'o' },
165 { "all", no_argument, NULL, 'a' },
166 { "full", no_argument, NULL, ARG_FULL },
167 { "lines", optional_argument, NULL, 'n' },
168 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
169 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
170 { "quiet", no_argument, NULL, 'q' },
171 { "merge", no_argument, NULL, 'm' },
172 { "this-boot", no_argument, NULL, 'b' },
173 { "directory", required_argument, NULL, 'D' },
174 { "header", no_argument, NULL, ARG_HEADER },
175 { "priority", required_argument, NULL, 'p' },
176 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
177 { "interval", required_argument, NULL, ARG_INTERVAL },
178 { "verify", no_argument, NULL, ARG_VERIFY },
179 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
180 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
181 { "cursor", required_argument, NULL, 'c' },
182 { "since", required_argument, NULL, ARG_SINCE },
183 { "until", required_argument, NULL, ARG_UNTIL },
184 { "unit", required_argument, NULL, 'u' },
185 { "field", required_argument, NULL, 'F' },
186 { "catalog", no_argument, NULL, 'x' },
187 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
188 { "update-catalog",no_argument, NULL, ARG_UPDATE_CATALOG },
189 { "reverse", no_argument, NULL, 'r' },
198 while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:u:F:xr", options, NULL)) >= 0) {
207 puts(PACKAGE_STRING);
208 puts(SYSTEMD_FEATURES);
220 arg_output = output_mode_from_string(optarg);
221 if (arg_output < 0) {
222 log_error("Unknown output format '%s'.", optarg);
226 if (arg_output == OUTPUT_EXPORT ||
227 arg_output == OUTPUT_JSON ||
228 arg_output == OUTPUT_JSON_PRETTY ||
229 arg_output == OUTPUT_JSON_SSE ||
230 arg_output == OUTPUT_CAT)
245 r = safe_atoi(optarg, &arg_lines);
246 if (r < 0 || arg_lines < 0) {
247 log_error("Failed to parse lines '%s'", optarg);
253 /* Hmm, no argument? Maybe the next
254 * word on the command line is
255 * supposed to be the argument? Let's
256 * see if there is one, and is
257 * parsable as a positive
261 safe_atoi(argv[optind], &n) >= 0 &&
277 arg_action = ACTION_NEW_ID128;
289 arg_this_boot = true;
293 arg_directory = optarg;
301 arg_action = ACTION_PRINT_HEADER;
305 arg_action = ACTION_VERIFY;
309 arg_action = ACTION_DISK_USAGE;
314 arg_action = ACTION_SETUP_KEYS;
319 arg_action = ACTION_VERIFY;
320 arg_verify_key = optarg;
325 r = parse_usec(optarg, &arg_interval);
326 if (r < 0 || arg_interval <= 0) {
327 log_error("Failed to parse sealing key change interval: %s", optarg);
335 log_error("Forward-secure sealing not available.");
342 dots = strstr(optarg, "..");
348 a = strndup(optarg, dots - optarg);
352 from = log_level_from_string(a);
353 to = log_level_from_string(dots + 2);
356 if (from < 0 || to < 0) {
357 log_error("Failed to parse log level range %s", optarg);
364 for (i = from; i <= to; i++)
365 arg_priorities |= 1 << i;
367 for (i = to; i <= from; i++)
368 arg_priorities |= 1 << i;
374 p = log_level_from_string(optarg);
376 log_error("Unknown log level %s", optarg);
382 for (i = 0; i <= p; i++)
383 arg_priorities |= 1 << i;
390 r = parse_timestamp(optarg, &arg_since);
392 log_error("Failed to parse timestamp: %s", optarg);
395 arg_since_set = true;
399 r = parse_timestamp(optarg, &arg_until);
401 log_error("Failed to parse timestamp: %s", optarg);
404 arg_until_set = true;
422 case ARG_LIST_CATALOG:
423 arg_action = ACTION_LIST_CATALOG;
426 case ARG_UPDATE_CATALOG:
427 arg_action = ACTION_UPDATE_CATALOG;
435 log_error("Unknown option code %c", c);
440 if (arg_follow && !arg_no_tail && arg_lines < 0)
443 if (arg_since_set && arg_until_set && arg_since > arg_until) {
444 log_error("--since= must be before --until=.");
448 if (arg_cursor && arg_since_set) {
449 log_error("Please specify either --since= or --cursor=, not both.");
453 if (arg_follow && arg_reverse) {
454 log_error("Please specify either --reverse= or --follow=, not both.");
461 static int generate_new_id128(void) {
466 r = sd_id128_randomize(&id);
468 log_error("Failed to generate ID: %s", strerror(-r));
472 printf("As string:\n"
473 SD_ID128_FORMAT_STR "\n\n"
475 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
477 "#define MESSAGE_XYZ SD_ID128_MAKE(",
478 SD_ID128_FORMAT_VAL(id),
479 SD_ID128_FORMAT_VAL(id));
480 for (i = 0; i < 16; i++)
481 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
482 fputs(")\n\n", stdout);
484 printf("As Python constant:\n"
486 ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
487 SD_ID128_FORMAT_VAL(id));
492 static int add_matches(sd_journal *j, char **args) {
498 STRV_FOREACH(i, args) {
501 r = sd_journal_add_disjunction(j);
502 else if (path_is_absolute(*i)) {
507 p = canonicalize_file_name(*i);
510 if (stat(path, &st) < 0) {
512 log_error("Couldn't stat file: %m");
516 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
517 t = strappend("_EXE=", path);
518 else if (S_ISCHR(st.st_mode))
519 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
520 else if (S_ISBLK(st.st_mode))
521 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
524 log_error("File is not a device node, regular file or is not executable: %s", *i);
533 r = sd_journal_add_match(j, t, 0);
536 r = sd_journal_add_match(j, *i, 0);
539 log_error("Failed to add match '%s': %s", *i, strerror(-r));
547 static int add_this_boot(sd_journal *j) {
548 char match[9+32+1] = "_BOOT_ID=";
557 r = sd_id128_get_boot(&boot_id);
559 log_error("Failed to get boot id: %s", strerror(-r));
563 sd_id128_to_string(boot_id, match + 9);
564 r = sd_journal_add_match(j, match, strlen(match));
566 log_error("Failed to add match: %s", strerror(-r));
573 static int add_unit(sd_journal *j) {
574 _cleanup_free_ char *m = NULL, *u = NULL;
579 if (isempty(arg_unit))
582 u = unit_name_mangle(arg_unit);
586 m = strappend("_SYSTEMD_UNIT=", u);
590 r = sd_journal_add_match(j, m, strlen(m));
592 log_error("Failed to add match: %s", strerror(-r));
599 static int add_priorities(sd_journal *j) {
600 char match[] = "PRIORITY=0";
605 if (arg_priorities == 0xFF)
608 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
609 if (arg_priorities & (1 << i)) {
610 match[sizeof(match)-2] = '0' + i;
612 r = sd_journal_add_match(j, match, strlen(match));
614 log_error("Failed to add match: %s", strerror(-r));
622 static int setup_keys(void) {
624 size_t mpk_size, seed_size, state_size, i;
625 uint8_t *mpk, *seed, *state;
627 int fd = -1, r, attr = 0;
628 sd_id128_t machine, boot;
629 char *p = NULL, *k = NULL;
633 r = sd_id128_get_machine(&machine);
635 log_error("Failed to get machine ID: %s", strerror(-r));
639 r = sd_id128_get_boot(&boot);
641 log_error("Failed to get boot ID: %s", strerror(-r));
645 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
646 SD_ID128_FORMAT_VAL(machine)) < 0)
649 if (access(p, F_OK) >= 0) {
650 log_error("Sealing key file %s exists already.", p);
655 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
656 SD_ID128_FORMAT_VAL(machine)) < 0) {
661 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
662 mpk = alloca(mpk_size);
664 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
665 seed = alloca(seed_size);
667 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
668 state = alloca(state_size);
670 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
672 log_error("Failed to open /dev/random: %m");
677 log_info("Generating seed...");
678 l = loop_read(fd, seed, seed_size, true);
679 if (l < 0 || (size_t) l != seed_size) {
680 log_error("Failed to read random seed: %s", strerror(EIO));
685 log_info("Generating key pair...");
686 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
688 log_info("Generating sealing key...");
689 FSPRG_GenState0(state, mpk, seed, seed_size);
691 assert(arg_interval > 0);
693 n = now(CLOCK_REALTIME);
696 close_nointr_nofail(fd);
697 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
699 log_error("Failed to open %s: %m", k);
704 /* Enable secure remove, exclusion from dump, synchronous
705 * writing and in-place updating */
706 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
707 log_warning("FS_IOC_GETFLAGS failed: %m");
709 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
711 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
712 log_warning("FS_IOC_SETFLAGS failed: %m");
715 memcpy(h.signature, "KSHHRHLP", 8);
716 h.machine_id = machine;
718 h.header_size = htole64(sizeof(h));
719 h.start_usec = htole64(n * arg_interval);
720 h.interval_usec = htole64(arg_interval);
721 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
722 h.fsprg_state_size = htole64(state_size);
724 l = loop_write(fd, &h, sizeof(h), false);
725 if (l < 0 || (size_t) l != sizeof(h)) {
726 log_error("Failed to write header: %s", strerror(EIO));
731 l = loop_write(fd, state, state_size, false);
732 if (l < 0 || (size_t) l != state_size) {
733 log_error("Failed to write state: %s", strerror(EIO));
738 if (link(k, p) < 0) {
739 log_error("Failed to link file: %m");
747 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
748 "the following local file. This key file is automatically updated when the\n"
749 "sealing key is advanced. It should not be used on multiple hosts.\n"
753 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
754 "at a safe location and should not be saved locally on disk.\n"
755 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
758 for (i = 0; i < seed_size; i++) {
759 if (i > 0 && i % 3 == 0)
761 printf("%02x", ((uint8_t*) seed)[i]);
764 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
767 char tsb[FORMAT_TIMESPAN_MAX], *hn;
770 ANSI_HIGHLIGHT_OFF "\n"
771 "The sealing key is automatically changed every %s.\n",
772 format_timespan(tsb, sizeof(tsb), arg_interval));
774 hn = gethostname_malloc();
777 hostname_cleanup(hn);
778 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
780 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
783 /* If this is not an UTF-8 system don't print any QR codes */
784 if (is_locale_utf8()) {
785 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
786 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
796 close_nointr_nofail(fd);
807 log_error("Forward-secure sealing not available.");
812 static int verify(sd_journal *j) {
819 log_show_color(true);
821 HASHMAP_FOREACH(f, j->files, i) {
823 usec_t first, validated, last;
826 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
827 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
830 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
832 /* If the key was invalid give up right-away. */
835 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
838 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
839 log_info("PASS: %s", f->path);
841 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
843 log_info("=> Validated from %s to %s, final %s entries not sealed.",
844 format_timestamp(a, sizeof(a), first),
845 format_timestamp(b, sizeof(b), validated),
846 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
848 log_info("=> No sealing yet, %s of entries not sealed.",
849 format_timespan(c, sizeof(c), last - first));
851 log_info("=> No sealing yet, no entries in file.");
859 static int access_check(void) {
862 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) {
863 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages.");
867 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
868 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off.");
870 if (geteuid() != 0 && in_group("adm") <= 0) {
871 log_error("No access to messages. Only users in the group 'adm' can see messages.");
879 int main(int argc, char *argv[]) {
881 sd_journal *j = NULL;
882 bool need_seek = false;
883 sd_id128_t previous_boot_id;
884 bool previous_boot_id_valid = false, first_line = true;
887 setlocale(LC_ALL, "");
888 log_parse_environment();
891 r = parse_argv(argc, argv);
895 signal(SIGWINCH, columns_lines_cache_reset);
897 if (arg_action == ACTION_NEW_ID128) {
898 r = generate_new_id128();
902 if (arg_action == ACTION_SETUP_KEYS) {
907 if (arg_action == ACTION_LIST_CATALOG) {
908 r = catalog_list(stdout);
910 log_error("Failed to list catalog: %s", strerror(-r));
914 if (arg_action == ACTION_UPDATE_CATALOG) {
915 r = catalog_update();
924 r = sd_journal_open_directory(&j, arg_directory, 0);
926 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
928 log_error("Failed to open journal: %s", strerror(-r));
932 if (arg_action == ACTION_VERIFY) {
937 if (arg_action == ACTION_PRINT_HEADER) {
938 journal_print_header(j);
943 if (arg_action == ACTION_DISK_USAGE) {
945 char sbytes[FORMAT_BYTES_MAX];
947 r = sd_journal_get_usage(j, &bytes);
951 printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes));
956 r = add_this_boot(j);
964 r = add_matches(j, argv + optind);
968 r = add_priorities(j);
972 /* Opening the fd now means the first sd_journal_wait() will actually wait */
973 r = sd_journal_get_fd(j);
981 r = sd_journal_query_unique(j, arg_field);
983 log_error("Failed to query unique data objects: %s", strerror(-r));
987 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
990 if (arg_lines >= 0 && n_shown >= arg_lines)
993 eq = memchr(data, '=', size);
995 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
997 printf("%.*s\n", (int) size, (const char*) data);
1007 r = sd_journal_seek_cursor(j, arg_cursor);
1009 log_error("Failed to seek to cursor: %s", strerror(-r));
1013 r = sd_journal_next(j);
1015 r = sd_journal_previous(j);
1017 } else if (arg_since_set && !arg_reverse) {
1018 r = sd_journal_seek_realtime_usec(j, arg_since);
1020 log_error("Failed to seek to date: %s", strerror(-r));
1023 r = sd_journal_next(j);
1025 } else if (arg_until_set && arg_reverse) {
1026 r = sd_journal_seek_realtime_usec(j, arg_until);
1028 log_error("Failed to seek to date: %s", strerror(-r));
1031 r = sd_journal_previous(j);
1033 } else if (arg_lines >= 0) {
1034 r = sd_journal_seek_tail(j);
1036 log_error("Failed to seek to tail: %s", strerror(-r));
1040 r = sd_journal_previous_skip(j, arg_lines);
1042 } else if (arg_reverse) {
1043 r = sd_journal_seek_tail(j);
1045 log_error("Failed to seek to tail: %s", strerror(-r));
1049 r = sd_journal_previous(j);
1052 r = sd_journal_seek_head(j);
1054 log_error("Failed to seek to head: %s", strerror(-r));
1058 r = sd_journal_next(j);
1062 log_error("Failed to iterate through journal: %s", strerror(-r));
1066 if (!arg_no_pager && !arg_follow)
1071 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
1073 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
1075 log_error("Failed to get cutoff: %s", strerror(-r));
1081 printf("-- Logs begin at %s. --\n",
1082 format_timestamp(start_buf, sizeof(start_buf), start));
1084 printf("-- Logs begin at %s, end at %s. --\n",
1085 format_timestamp(start_buf, sizeof(start_buf), start),
1086 format_timestamp(end_buf, sizeof(end_buf), end));
1091 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
1096 r = sd_journal_next(j);
1098 r = sd_journal_previous(j);
1100 log_error("Failed to iterate through journal: %s", strerror(-r));
1108 if (arg_until_set && !arg_reverse) {
1111 r = sd_journal_get_realtime_usec(j, &usec);
1113 log_error("Failed to determine timestamp: %s", strerror(-r));
1116 if (usec > arg_until)
1120 if (arg_since_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_since)
1135 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1137 if (previous_boot_id_valid &&
1138 !sd_id128_equal(boot_id, previous_boot_id))
1139 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
1141 previous_boot_id = boot_id;
1142 previous_boot_id_valid = true;
1147 arg_all * OUTPUT_SHOW_ALL |
1148 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
1149 on_tty() * OUTPUT_COLOR |
1150 arg_catalog * OUTPUT_CATALOG;
1152 r = output_journal(stdout, j, arg_output, 0, flags);
1153 if (r < 0 || ferror(stdout))
1163 r = sd_journal_wait(j, (uint64_t) -1);
1165 log_error("Couldn't wait for journal event: %s", strerror(-r));
1174 sd_journal_close(j);
1178 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;