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>
42 #include <systemd/sd-journal.h>
45 #include "logs-show.h"
47 #include "path-util.h"
50 #include "logs-show.h"
52 #include "journal-internal.h"
53 #include "journal-def.h"
54 #include "journal-verify.h"
55 #include "journal-authenticate.h"
56 #include "journal-qrcode.h"
58 #include "unit-name.h"
61 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
63 static OutputMode arg_output = OUTPUT_SHORT;
64 static bool arg_pager_end = false;
65 static bool arg_follow = false;
66 static bool arg_full = false;
67 static bool arg_all = false;
68 static bool arg_no_pager = false;
69 static int arg_lines = -1;
70 static bool arg_no_tail = false;
71 static bool arg_quiet = false;
72 static bool arg_merge = false;
73 static bool arg_this_boot = false;
74 static const char *arg_cursor = NULL;
75 static const char *arg_directory = NULL;
76 static int arg_priorities = 0xFF;
77 static const char *arg_verify_key = NULL;
79 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
81 static usec_t arg_since, arg_until;
82 static bool arg_since_set = false, arg_until_set = false;
83 static const char *arg_unit = NULL;
84 static bool arg_unit_system;
85 static const char *arg_field = NULL;
86 static bool arg_catalog = false;
87 static bool arg_reverse = false;
99 } arg_action = ACTION_SHOW;
101 static int help(void) {
103 printf("%s [OPTIONS...] [MATCHES...]\n\n"
104 "Query the journal.\n\n"
106 " --since=DATE Start showing entries newer or of the specified date\n"
107 " --until=DATE Stop showing entries older or of the specified date\n"
108 " -c --cursor=CURSOR Start showing entries from specified cursor\n"
109 " -b --this-boot Show data only from current boot\n"
110 " -u --unit=UNIT Show data only from the specified unit\n"
111 " --user-unit=UNIT Show data only from the specified user session unit\n"
112 " -p --priority=RANGE Show only messages within the specified priority range\n"
113 " -e --pager-end Immediately jump to end of the journal in the pager\n"
114 " -f --follow Follow journal\n"
115 " -n --lines[=INTEGER] Number of journal entries to show\n"
116 " --no-tail Show all lines, even in follow mode\n"
117 " -r --reverse Show the newest entries first\n"
118 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
119 " verbose, export, json, json-pretty, json-sse, cat)\n"
120 " -x --catalog Add message explanations where available\n"
121 " --full Do not ellipsize fields\n"
122 " -a --all Show all fields, including long and unprintable\n"
123 " -q --quiet Don't show privilege warning\n"
124 " --no-pager Do not pipe output into a pager\n"
125 " -m --merge Show entries from all available journals\n"
126 " -D --directory=PATH Show journal files from directory\n"
128 " --interval=TIME Time interval for changing the FSS sealing key\n"
129 " --verify-key=KEY Specify FSS verification key\n"
132 " -h --help Show this help\n"
133 " --version Show package version\n"
134 " --new-id128 Generate a new 128 Bit ID\n"
135 " --header Show journal header information\n"
136 " --disk-usage Show total disk usage\n"
137 " -F --field=FIELD List all values a certain field takes\n"
138 " --list-catalog Show message IDs of all entries in the message catalog\n"
139 " --dump-catalog Show entries in the message catalog\n"
140 " --update-catalog Update the message catalog database\n"
142 " --setup-keys Generate new FSS key pair\n"
143 " --verify Verify journal file consistency\n"
145 , program_invocation_short_name);
150 static int parse_argv(int argc, char *argv[]) {
172 static const struct option options[] = {
173 { "help", no_argument, NULL, 'h' },
174 { "version" , no_argument, NULL, ARG_VERSION },
175 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
176 { "pager-end", no_argument, NULL, 'e' },
177 { "follow", no_argument, NULL, 'f' },
178 { "output", required_argument, NULL, 'o' },
179 { "all", no_argument, NULL, 'a' },
180 { "full", no_argument, NULL, ARG_FULL },
181 { "lines", optional_argument, NULL, 'n' },
182 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
183 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
184 { "quiet", no_argument, NULL, 'q' },
185 { "merge", no_argument, NULL, 'm' },
186 { "this-boot", no_argument, NULL, 'b' },
187 { "directory", required_argument, NULL, 'D' },
188 { "header", no_argument, NULL, ARG_HEADER },
189 { "priority", required_argument, NULL, 'p' },
190 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
191 { "interval", required_argument, NULL, ARG_INTERVAL },
192 { "verify", no_argument, NULL, ARG_VERIFY },
193 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
194 { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
195 { "cursor", required_argument, NULL, 'c' },
196 { "since", required_argument, NULL, ARG_SINCE },
197 { "until", required_argument, NULL, ARG_UNTIL },
198 { "unit", required_argument, NULL, 'u' },
199 { "user-unit", required_argument, NULL, ARG_USER_UNIT },
200 { "field", required_argument, NULL, 'F' },
201 { "catalog", no_argument, NULL, 'x' },
202 { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
203 { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
204 { "update-catalog",no_argument, NULL, ARG_UPDATE_CATALOG },
205 { "reverse", no_argument, NULL, 'r' },
214 while ((c = getopt_long(argc, argv, "hefo:an::qmbD:p:c:u:F:xr", options, NULL)) >= 0) {
223 puts(PACKAGE_STRING);
224 puts(SYSTEMD_FEATURES);
232 arg_pager_end = true;
244 arg_output = output_mode_from_string(optarg);
245 if (arg_output < 0) {
246 log_error("Unknown output format '%s'.", optarg);
250 if (arg_output == OUTPUT_EXPORT ||
251 arg_output == OUTPUT_JSON ||
252 arg_output == OUTPUT_JSON_PRETTY ||
253 arg_output == OUTPUT_JSON_SSE ||
254 arg_output == OUTPUT_CAT)
269 r = safe_atoi(optarg, &arg_lines);
270 if (r < 0 || arg_lines < 0) {
271 log_error("Failed to parse lines '%s'", optarg);
277 /* Hmm, no argument? Maybe the next
278 * word on the command line is
279 * supposed to be the argument? Let's
280 * see if there is one, and is
281 * parsable as a positive
285 safe_atoi(argv[optind], &n) >= 0 &&
301 arg_action = ACTION_NEW_ID128;
313 arg_this_boot = true;
317 arg_directory = optarg;
325 arg_action = ACTION_PRINT_HEADER;
329 arg_action = ACTION_VERIFY;
333 arg_action = ACTION_DISK_USAGE;
338 arg_action = ACTION_SETUP_KEYS;
343 arg_action = ACTION_VERIFY;
344 arg_verify_key = optarg;
349 r = parse_usec(optarg, &arg_interval);
350 if (r < 0 || arg_interval <= 0) {
351 log_error("Failed to parse sealing key change interval: %s", optarg);
359 log_error("Forward-secure sealing not available.");
366 dots = strstr(optarg, "..");
372 a = strndup(optarg, dots - optarg);
376 from = log_level_from_string(a);
377 to = log_level_from_string(dots + 2);
380 if (from < 0 || to < 0) {
381 log_error("Failed to parse log level range %s", optarg);
388 for (i = from; i <= to; i++)
389 arg_priorities |= 1 << i;
391 for (i = to; i <= from; i++)
392 arg_priorities |= 1 << i;
398 p = log_level_from_string(optarg);
400 log_error("Unknown log level %s", optarg);
406 for (i = 0; i <= p; i++)
407 arg_priorities |= 1 << i;
414 r = parse_timestamp(optarg, &arg_since);
416 log_error("Failed to parse timestamp: %s", optarg);
419 arg_since_set = true;
423 r = parse_timestamp(optarg, &arg_until);
425 log_error("Failed to parse timestamp: %s", optarg);
428 arg_until_set = true;
433 arg_unit_system = true;
438 arg_unit_system = false;
452 case ARG_LIST_CATALOG:
453 arg_action = ACTION_LIST_CATALOG;
456 case ARG_DUMP_CATALOG:
457 arg_action = ACTION_DUMP_CATALOG;
460 case ARG_UPDATE_CATALOG:
461 arg_action = ACTION_UPDATE_CATALOG;
469 log_error("Unknown option code %c", c);
474 if (arg_follow && !arg_no_tail && arg_lines < 0)
477 if (arg_since_set && arg_until_set && arg_since > arg_until) {
478 log_error("--since= must be before --until=.");
482 if (arg_cursor && arg_since_set) {
483 log_error("Please specify either --since= or --cursor=, not both.");
487 if (arg_follow && arg_reverse) {
488 log_error("Please specify either --reverse= or --follow=, not both.");
495 static int generate_new_id128(void) {
500 r = sd_id128_randomize(&id);
502 log_error("Failed to generate ID: %s", strerror(-r));
506 printf("As string:\n"
507 SD_ID128_FORMAT_STR "\n\n"
509 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
511 "#define MESSAGE_XYZ SD_ID128_MAKE(",
512 SD_ID128_FORMAT_VAL(id),
513 SD_ID128_FORMAT_VAL(id));
514 for (i = 0; i < 16; i++)
515 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
516 fputs(")\n\n", stdout);
518 printf("As Python constant:\n"
520 ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
521 SD_ID128_FORMAT_VAL(id));
526 static int add_matches(sd_journal *j, char **args) {
531 STRV_FOREACH(i, args) {
535 r = sd_journal_add_disjunction(j);
536 else if (path_is_absolute(*i)) {
537 char _cleanup_free_ *p, *t = NULL;
541 p = canonicalize_file_name(*i);
544 if (stat(path, &st) < 0) {
545 log_error("Couldn't stat file: %m");
549 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
550 t = strappend("_EXE=", path);
551 else if (S_ISCHR(st.st_mode))
552 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
553 else if (S_ISBLK(st.st_mode))
554 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
556 log_error("File is not a device node, regular file or is not executable: %s", *i);
563 r = sd_journal_add_match(j, t, 0);
565 r = sd_journal_add_match(j, *i, 0);
568 log_error("Failed to add match '%s': %s", *i, strerror(-r));
576 static int add_this_boot(sd_journal *j) {
577 char match[9+32+1] = "_BOOT_ID=";
586 r = sd_id128_get_boot(&boot_id);
588 log_error("Failed to get boot id: %s", strerror(-r));
592 sd_id128_to_string(boot_id, match + 9);
593 r = sd_journal_add_match(j, match, strlen(match));
595 log_error("Failed to add match: %s", strerror(-r));
602 static int add_unit(sd_journal *j) {
603 _cleanup_free_ char *u = NULL;
608 if (isempty(arg_unit))
611 u = unit_name_mangle(arg_unit);
616 r = add_matches_for_unit(j, u);
618 r = add_matches_for_user_unit(j, u, getuid());
625 static int add_priorities(sd_journal *j) {
626 char match[] = "PRIORITY=0";
631 if (arg_priorities == 0xFF)
634 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
635 if (arg_priorities & (1 << i)) {
636 match[sizeof(match)-2] = '0' + i;
638 r = sd_journal_add_match(j, match, strlen(match));
640 log_error("Failed to add match: %s", strerror(-r));
648 static int setup_keys(void) {
650 size_t mpk_size, seed_size, state_size, i;
651 uint8_t *mpk, *seed, *state;
653 int fd = -1, r, attr = 0;
654 sd_id128_t machine, boot;
655 char *p = NULL, *k = NULL;
659 r = sd_id128_get_machine(&machine);
661 log_error("Failed to get machine ID: %s", strerror(-r));
665 r = sd_id128_get_boot(&boot);
667 log_error("Failed to get boot ID: %s", strerror(-r));
671 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
672 SD_ID128_FORMAT_VAL(machine)) < 0)
675 if (access(p, F_OK) >= 0) {
676 log_error("Sealing key file %s exists already.", p);
681 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
682 SD_ID128_FORMAT_VAL(machine)) < 0) {
687 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
688 mpk = alloca(mpk_size);
690 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
691 seed = alloca(seed_size);
693 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
694 state = alloca(state_size);
696 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
698 log_error("Failed to open /dev/random: %m");
703 log_info("Generating seed...");
704 l = loop_read(fd, seed, seed_size, true);
705 if (l < 0 || (size_t) l != seed_size) {
706 log_error("Failed to read random seed: %s", strerror(EIO));
711 log_info("Generating key pair...");
712 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
714 log_info("Generating sealing key...");
715 FSPRG_GenState0(state, mpk, seed, seed_size);
717 assert(arg_interval > 0);
719 n = now(CLOCK_REALTIME);
722 close_nointr_nofail(fd);
723 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
725 log_error("Failed to open %s: %m", k);
730 /* Enable secure remove, exclusion from dump, synchronous
731 * writing and in-place updating */
732 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
733 log_warning("FS_IOC_GETFLAGS failed: %m");
735 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
737 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
738 log_warning("FS_IOC_SETFLAGS failed: %m");
741 memcpy(h.signature, "KSHHRHLP", 8);
742 h.machine_id = machine;
744 h.header_size = htole64(sizeof(h));
745 h.start_usec = htole64(n * arg_interval);
746 h.interval_usec = htole64(arg_interval);
747 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
748 h.fsprg_state_size = htole64(state_size);
750 l = loop_write(fd, &h, sizeof(h), false);
751 if (l < 0 || (size_t) l != sizeof(h)) {
752 log_error("Failed to write header: %s", strerror(EIO));
757 l = loop_write(fd, state, state_size, false);
758 if (l < 0 || (size_t) l != state_size) {
759 log_error("Failed to write state: %s", strerror(EIO));
764 if (link(k, p) < 0) {
765 log_error("Failed to link file: %m");
773 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
774 "the following local file. This key file is automatically updated when the\n"
775 "sealing key is advanced. It should not be used on multiple hosts.\n"
779 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
780 "at a safe location and should not be saved locally on disk.\n"
781 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
784 for (i = 0; i < seed_size; i++) {
785 if (i > 0 && i % 3 == 0)
787 printf("%02x", ((uint8_t*) seed)[i]);
790 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
793 char tsb[FORMAT_TIMESPAN_MAX], *hn;
796 ANSI_HIGHLIGHT_OFF "\n"
797 "The sealing key is automatically changed every %s.\n",
798 format_timespan(tsb, sizeof(tsb), arg_interval));
800 hn = gethostname_malloc();
803 hostname_cleanup(hn);
804 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
806 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
809 /* If this is not an UTF-8 system don't print any QR codes */
810 if (is_locale_utf8()) {
811 fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
812 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
822 close_nointr_nofail(fd);
833 log_error("Forward-secure sealing not available.");
838 static int verify(sd_journal *j) {
845 log_show_color(true);
847 HASHMAP_FOREACH(f, j->files, i) {
849 usec_t first, validated, last;
852 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
853 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
856 k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
858 /* If the key was invalid give up right-away. */
861 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
864 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
865 log_info("PASS: %s", f->path);
867 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
869 log_info("=> Validated from %s to %s, final %s entries not sealed.",
870 format_timestamp(a, sizeof(a), first),
871 format_timestamp(b, sizeof(b), validated),
872 format_timespan(c, sizeof(c), last > validated ? last - validated : 0));
874 log_info("=> No sealing yet, %s of entries not sealed.",
875 format_timespan(c, sizeof(c), last - first));
877 log_info("=> No sealing yet, no entries in file.");
885 static int access_check(void) {
888 /* If /var/log/journal doesn't even exist, unprivileged users have no access at all */
889 if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("systemd-journal") <= 0) {
890 log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'systemd-journal' can always see messages.");
894 /* If /var/log/journal exists, try to pring a nice notice if the user lacks access to it */
895 if (!arg_quiet && geteuid() != 0) {
896 _cleanup_strv_free_ char **g = NULL;
901 have_access = in_group("systemd-journal") > 0;
904 /* Let's enumerate all groups from the default
905 * ACL of the directory, which generally
906 * should allow access to most journal
909 acl = acl_get_file("/var/log/journal/", ACL_TYPE_DEFAULT);
913 r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
919 r = acl_get_tag_type(entry, &tag);
923 if (tag != ACL_GROUP)
926 gid = acl_get_qualifier(entry);
930 if (in_gid(*gid) > 0) {
935 name = gid_to_name(*gid);
941 r = strv_push(&g, name);
949 r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
959 log_notice("Hint: You are currently not seeing messages from other users and the system. Users in the group 'systemd-journal' can see all messages. Pass -q to turn this notice off.");
961 _cleanup_free_ char *s = NULL;
963 r = strv_extend(&g, "systemd-journal");
970 s = strv_join(g, "', '");
974 log_notice("Hint: You are currently not seeing messages from other users and the system. Users in the groups '%s' can see all messages. Pass -q to turn this notice off.", s);
979 if (geteuid() != 0 && in_group("systemd-journal") <= 0) {
980 log_error("No access to messages. Only users in the group 'systemd-journal' can see messages.");
988 int main(int argc, char *argv[]) {
990 sd_journal _cleanup_journal_close_ *j = NULL;
991 bool need_seek = false;
992 sd_id128_t previous_boot_id;
993 bool previous_boot_id_valid = false, first_line = true;
996 setlocale(LC_ALL, "");
997 log_parse_environment();
1000 r = parse_argv(argc, argv);
1004 signal(SIGWINCH, columns_lines_cache_reset);
1006 if (arg_action == ACTION_NEW_ID128) {
1007 r = generate_new_id128();
1011 if (arg_action == ACTION_SETUP_KEYS) {
1016 if (arg_action == ACTION_LIST_CATALOG ||
1017 arg_action == ACTION_DUMP_CATALOG) {
1018 bool oneline = arg_action == ACTION_LIST_CATALOG;
1020 r = catalog_list_items(stdout, oneline, argv + optind);
1022 r = catalog_list(stdout, oneline);
1024 log_error("Failed to list catalog: %s", strerror(-r));
1028 if (arg_action == ACTION_UPDATE_CATALOG) {
1029 r = catalog_update();
1035 return EXIT_FAILURE;
1038 r = sd_journal_open_directory(&j, arg_directory, 0);
1040 r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY);
1042 log_error("Failed to open journal: %s", strerror(-r));
1043 return EXIT_FAILURE;
1046 if (arg_action == ACTION_VERIFY) {
1051 if (arg_action == ACTION_PRINT_HEADER) {
1052 journal_print_header(j);
1053 return EXIT_SUCCESS;
1056 if (arg_action == ACTION_DISK_USAGE) {
1058 char sbytes[FORMAT_BYTES_MAX];
1060 r = sd_journal_get_usage(j, &bytes);
1062 return EXIT_FAILURE;
1064 printf("Journals take up %s on disk.\n",
1065 format_bytes(sbytes, sizeof(sbytes), bytes));
1066 return EXIT_SUCCESS;
1069 r = add_this_boot(j);
1071 return EXIT_FAILURE;
1075 return EXIT_FAILURE;
1077 r = add_matches(j, argv + optind);
1079 return EXIT_FAILURE;
1081 r = add_priorities(j);
1083 return EXIT_FAILURE;
1085 /* Opening the fd now means the first sd_journal_wait() will actually wait */
1086 r = sd_journal_get_fd(j);
1088 return EXIT_FAILURE;
1094 r = sd_journal_query_unique(j, arg_field);
1096 log_error("Failed to query unique data objects: %s", strerror(-r));
1097 return EXIT_FAILURE;
1100 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
1103 if (arg_lines >= 0 && n_shown >= arg_lines)
1106 eq = memchr(data, '=', size);
1108 printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
1110 printf("%.*s\n", (int) size, (const char*) data);
1115 return EXIT_SUCCESS;
1119 r = sd_journal_seek_cursor(j, arg_cursor);
1121 log_error("Failed to seek to cursor: %s", strerror(-r));
1122 return EXIT_FAILURE;
1125 r = sd_journal_next(j);
1127 r = sd_journal_previous(j);
1129 } else if (arg_since_set && !arg_reverse) {
1130 r = sd_journal_seek_realtime_usec(j, arg_since);
1132 log_error("Failed to seek to date: %s", strerror(-r));
1133 return EXIT_FAILURE;
1135 r = sd_journal_next(j);
1137 } else if (arg_until_set && arg_reverse) {
1138 r = sd_journal_seek_realtime_usec(j, arg_until);
1140 log_error("Failed to seek to date: %s", strerror(-r));
1141 return EXIT_FAILURE;
1143 r = sd_journal_previous(j);
1145 } else if (arg_lines >= 0) {
1146 r = sd_journal_seek_tail(j);
1148 log_error("Failed to seek to tail: %s", strerror(-r));
1149 return EXIT_FAILURE;
1152 r = sd_journal_previous_skip(j, arg_lines);
1154 } else if (arg_reverse) {
1155 r = sd_journal_seek_tail(j);
1157 log_error("Failed to seek to tail: %s", strerror(-r));
1158 return EXIT_FAILURE;
1161 r = sd_journal_previous(j);
1164 r = sd_journal_seek_head(j);
1166 log_error("Failed to seek to head: %s", strerror(-r));
1167 return EXIT_FAILURE;
1170 r = sd_journal_next(j);
1174 log_error("Failed to iterate through journal: %s", strerror(-r));
1175 return EXIT_FAILURE;
1178 if (!arg_no_pager && !arg_follow)
1179 pager_open(arg_pager_end);
1183 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
1185 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
1187 log_error("Failed to get cutoff: %s", strerror(-r));
1193 printf("-- Logs begin at %s. --\n",
1194 format_timestamp(start_buf, sizeof(start_buf), start));
1196 printf("-- Logs begin at %s, end at %s. --\n",
1197 format_timestamp(start_buf, sizeof(start_buf), start),
1198 format_timestamp(end_buf, sizeof(end_buf), end));
1203 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
1208 r = sd_journal_next(j);
1210 r = sd_journal_previous(j);
1212 log_error("Failed to iterate through journal: %s", strerror(-r));
1220 if (arg_until_set && !arg_reverse) {
1223 r = sd_journal_get_realtime_usec(j, &usec);
1225 log_error("Failed to determine timestamp: %s", strerror(-r));
1228 if (usec > arg_until)
1232 if (arg_since_set && arg_reverse) {
1235 r = sd_journal_get_realtime_usec(j, &usec);
1237 log_error("Failed to determine timestamp: %s", strerror(-r));
1240 if (usec < arg_since)
1247 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
1249 if (previous_boot_id_valid &&
1250 !sd_id128_equal(boot_id, previous_boot_id))
1251 printf(ANSI_HIGHLIGHT_ON "-- Reboot --" ANSI_HIGHLIGHT_OFF "\n");
1253 previous_boot_id = boot_id;
1254 previous_boot_id_valid = true;
1259 arg_all * OUTPUT_SHOW_ALL |
1260 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
1261 on_tty() * OUTPUT_COLOR |
1262 arg_catalog * OUTPUT_CATALOG;
1264 r = output_journal(stdout, j, arg_output, 0, flags);
1265 if (r < 0 || ferror(stdout))
1275 r = sd_journal_wait(j, (uint64_t) -1);
1277 log_error("Couldn't wait for journal event: %s", strerror(-r));
1287 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;