]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/journal/journalctl.c
Merge pull request #30284 from YHNdnzj/fstab-wantedby-defaultdeps
[thirdparty/systemd.git] / src / journal / journalctl.c
index 5d1934156819036429000474281d1d905d6c82bc..fe68ec4310b3dc40358e30d3228bca5d4e69c588 100644 (file)
@@ -175,12 +175,6 @@ static enum {
         ACTION_LIST_FIELD_NAMES,
 } arg_action = ACTION_SHOW;
 
-typedef struct BootId {
-        sd_id128_t id;
-        usec_t first_usec;
-        usec_t last_usec;
-} BootId;
-
 static int add_matches_for_device(sd_journal *j, const char *devpath) {
         _cleanup_(sd_device_unrefp) sd_device *device = NULL;
         sd_device *d = NULL;
@@ -324,7 +318,7 @@ static int parse_lines(const char *arg, bool graceful) {
         }
 
         arg_lines = n;
-        arg_lines_oldest = !!l;
+        arg_lines_oldest = l;
 
         return 1;
 
@@ -371,7 +365,7 @@ static int help(void) {
                "  -M --machine=CONTAINER     Operate on local container\n"
                "  -m --merge                 Show entries from all available journals\n"
                "  -D --directory=PATH        Show journal files from directory\n"
-               "     --file=PATH             Show journal file\n"
+               "  -i --file=PATH             Show journal file\n"
                "     --root=PATH             Operate on an alternate filesystem root\n"
                "     --image=PATH            Operate on disk image as filesystem root\n"
                "     --image-policy=POLICY   Specify disk image dissection policy\n"
@@ -467,7 +461,6 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_HEADER,
                 ARG_FACILITY,
                 ARG_SETUP_KEYS,
-                ARG_FILE,
                 ARG_INTERVAL,
                 ARG_VERIFY,
                 ARG_VERIFY_KEY,
@@ -520,7 +513,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "system",               no_argument,       NULL, ARG_SYSTEM               },
                 { "user",                 no_argument,       NULL, ARG_USER                 },
                 { "directory",            required_argument, NULL, 'D'                      },
-                { "file",                 required_argument, NULL, ARG_FILE                 },
+                { "file",                 required_argument, NULL, 'i'                      },
                 { "root",                 required_argument, NULL, ARG_ROOT                 },
                 { "image",                required_argument, NULL, ARG_IMAGE                },
                 { "image-policy",         required_argument, NULL, ARG_IMAGE_POLICY         },
@@ -571,7 +564,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:g:c:S:U:t:u:NF:xrM:", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:g:c:S:U:t:u:NF:xrM:i:", options, NULL)) >= 0)
 
                 switch (c) {
 
@@ -733,7 +726,7 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_directory = optarg;
                         break;
 
-                case ARG_FILE:
+                case 'i':
                         if (streq(optarg, "-"))
                                 /* An undocumented feature: we can read journal files from STDIN. We don't document
                                  * this though, since after all we only support this for mmap-able, seekable files, and
@@ -1095,9 +1088,9 @@ static int parse_argv(int argc, char *argv[]) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "--since= must be before --until=.");
 
-        if (!!arg_cursor + !!arg_after_cursor + !!arg_since_set > 1)
+        if (!!arg_cursor + !!arg_after_cursor + !!arg_cursor_file + !!arg_since_set > 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Please specify only one of --since=, --cursor=, and --after-cursor=.");
+                                       "Please specify only one of --since=, --cursor=, --cursor-file=, and --after-cursor=.");
 
         if (arg_follow && arg_reverse)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -1225,207 +1218,6 @@ static int add_matches(sd_journal *j, char **args) {
         return 0;
 }
 
-static int discover_next_boot(
-                sd_journal *j,
-                sd_id128_t previous_boot_id,
-                bool advance_older,
-                BootId *ret) {
-
-        BootId boot;
-        int r;
-
-        assert(j);
-        assert(ret);
-
-        /* We expect the journal to be on the last position of a boot
-         * (in relation to the direction we are going), so that the next
-         * invocation of sd_journal_next/previous will be from a different
-         * boot. We then collect any information we desire and then jump
-         * to the last location of the new boot by using a _BOOT_ID match
-         * coming from the other journal direction. */
-
-        /* Make sure we aren't restricted by any _BOOT_ID matches, so that
-         * we can actually advance to a *different* boot. */
-        sd_journal_flush_matches(j);
-
-        do {
-                if (advance_older)
-                        r = sd_journal_previous(j);
-                else
-                        r = sd_journal_next(j);
-                if (r < 0)
-                        return r;
-                else if (r == 0) {
-                        *ret = (BootId) {};
-                        return 0; /* End of journal, yay. */
-                }
-
-                r = sd_journal_get_monotonic_usec(j, NULL, &boot.id);
-                if (r < 0)
-                        return r;
-
-                /* We iterate through this in a loop, until the boot ID differs from the previous one. Note that
-                 * normally, this will only require a single iteration, as we moved to the last entry of the previous
-                 * boot entry already. However, it might happen that the per-journal-field entry arrays are less
-                 * complete than the main entry array, and hence might reference an entry that's not actually the last
-                 * one of the boot ID as last one. Let's hence use the per-field array is initial seek position to
-                 * speed things up, but let's not trust that it is complete, and hence, manually advance as
-                 * necessary. */
-
-        } while (sd_id128_equal(boot.id, previous_boot_id));
-
-        r = sd_journal_get_realtime_usec(j, &boot.first_usec);
-        if (r < 0)
-                return r;
-
-        /* Now seek to the last occurrence of this boot ID. */
-        r = add_match_boot_id(j, boot.id);
-        if (r < 0)
-                return r;
-
-        if (advance_older)
-                r = sd_journal_seek_head(j);
-        else
-                r = sd_journal_seek_tail(j);
-        if (r < 0)
-                return r;
-
-        if (advance_older)
-                r = sd_journal_next(j);
-        else
-                r = sd_journal_previous(j);
-        if (r < 0)
-                return r;
-        else if (r == 0)
-                return log_debug_errno(SYNTHETIC_ERRNO(ENODATA),
-                                       "Whoopsie! We found a boot ID but can't read its last entry."); /* This shouldn't happen. We just came from this very boot ID. */
-
-        r = sd_journal_get_realtime_usec(j, &boot.last_usec);
-        if (r < 0)
-                return r;
-
-        sd_journal_flush_matches(j);
-        *ret = boot;
-        return 1;
-}
-
-static int find_boot_by_id(sd_journal *j) {
-        int r;
-
-        assert(j);
-
-        sd_journal_flush_matches(j);
-
-        r = add_match_boot_id(j, arg_boot_id);
-        if (r < 0)
-                return r;
-
-        r = sd_journal_seek_head(j); /* seek to oldest */
-        if (r < 0)
-                return r;
-
-        r = sd_journal_next(j);      /* read the oldest entry */
-        if (r < 0)
-                return r;
-
-        /* At this point the read pointer is positioned at the oldest occurrence of the reference boot ID.
-         * After flushing the matches, one more invocation of _previous() will hence place us at the
-         * following entry, which must then have an older boot ID */
-
-        sd_journal_flush_matches(j);
-        return r > 0;
-}
-
-static int find_boot_by_offset(sd_journal *j) {
-        bool advance_older, skip_once;
-        int r;
-
-        /* Adjust for the asymmetry that offset 0 is the last (and current) boot, while 1 is considered the
-         * (chronological) first boot in the journal. */
-        advance_older = skip_once = arg_boot_offset <= 0;
-
-        if (advance_older)
-                r = sd_journal_seek_tail(j); /* seek to newest */
-        else
-                r = sd_journal_seek_head(j); /* seek to oldest */
-        if (r < 0)
-                return r;
-
-        /* No sd_journal_next()/_previous() here.
-         *
-         * At this point the read pointer is positioned after the newest/before the oldest entry in the whole
-         * journal. The next invocation of _previous()/_next() will hence position us at the newest/oldest
-         * entry we have. */
-
-        int offset = arg_boot_offset;
-        sd_id128_t previous_boot_id = SD_ID128_NULL;
-        for (;;) {
-                BootId boot;
-
-                r = discover_next_boot(j, previous_boot_id, advance_older, &boot);
-                if (r <= 0)
-                        return r;
-
-                previous_boot_id = boot.id;
-
-                if (!skip_once)
-                        offset += advance_older ? 1 : -1;
-                skip_once = false;
-
-                if (offset == 0) {
-                        arg_boot_id = boot.id;
-                        return true;
-                }
-        }
-}
-
-static int get_boots(sd_journal *j, BootId **ret_boots, size_t *ret_n_boots) {
-        _cleanup_free_ BootId *boots = NULL;
-        size_t n_boots = 0;
-        int r;
-
-        assert(j);
-        assert(ret_boots);
-        assert(ret_n_boots);
-
-        r = sd_journal_seek_head(j); /* seek to oldest */
-        if (r < 0)
-                return r;
-
-        /* No sd_journal_next()/_previous() here.
-         *
-         * At this point the read pointer is positioned before the oldest entry in the whole journal. The
-         * next invocation of _next() will hence position us at the oldest entry we have. */
-
-        sd_id128_t previous_boot_id = SD_ID128_NULL;
-        for (;;) {
-                BootId boot;
-
-                r = discover_next_boot(j, previous_boot_id, /* advance_older = */ false, &boot);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        break;
-
-                previous_boot_id = boot.id;
-
-                FOREACH_ARRAY(i, boots, n_boots)
-                        if (sd_id128_equal(i->id, boot.id))
-                                /* The boot id is already stored, something wrong with the journal files.
-                                 * Exiting as otherwise this problem would cause an infinite loop. */
-                                break;
-
-                if (!GREEDY_REALLOC(boots, n_boots + 1))
-                        return -ENOMEM;
-
-                boots[n_boots++] = boot;
-        }
-
-        *ret_boots = TAKE_PTR(boots);
-        *ret_n_boots = n_boots;
-        return n_boots > 0;
-}
-
 static int list_boots(sd_journal *j) {
         _cleanup_(table_unrefp) Table *table = NULL;
         _cleanup_free_ BootId *boots = NULL;
@@ -1434,7 +1226,7 @@ static int list_boots(sd_journal *j) {
 
         assert(j);
 
-        r = get_boots(j, &boots, &n_boots);
+        r = journal_get_boots(j, &boots, &n_boots);
         if (r < 0)
                 return log_error_errno(r, "Failed to determine boots: %m");
         if (r == 0)
@@ -1488,7 +1280,7 @@ static int add_boot(sd_journal *j) {
                 return add_match_this_boot(j, arg_machine);
 
         if (sd_id128_is_null(arg_boot_id)) {
-                r = find_boot_by_offset(j);
+                r = journal_find_boot_by_offset(j, arg_boot_offset, &arg_boot_id);
                 if (r < 0)
                         return log_error_errno(r, "Failed to find journal entry from the specified boot offset (%+i): %m",
                                                arg_boot_offset);
@@ -1497,7 +1289,7 @@ static int add_boot(sd_journal *j) {
                                                "No journal boot entry found from the specified boot offset (%+i).",
                                                arg_boot_offset);
         } else {
-                r = find_boot_by_id(j);
+                r = journal_find_boot_by_id(j, arg_boot_id);
                 if (r < 0)
                         return log_error_errno(r, "Failed to find journal entry from the specified boot ID (%s): %m",
                                                SD_ID128_TO_STRING(arg_boot_id));
@@ -2372,10 +2164,12 @@ static int setup_event(Context *c, int fd, sd_event **ret) {
 }
 
 static int run(int argc, char *argv[]) {
-        bool need_seek = false, since_seeked = false, use_cursor = false, after_cursor = false;
+        bool need_seek = false, since_seeked = false, after_cursor = false;
         _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
         _cleanup_(umount_and_freep) char *mounted_dir = NULL;
         _cleanup_(sd_journal_closep) sd_journal *j = NULL;
+        _cleanup_free_ char *cursor_from_file = NULL;
+        const char *cursor = NULL;
         int n_shown, r, poll_fd = -EBADF;
 
         setlocale(LC_ALL, "");
@@ -2652,8 +2446,7 @@ static int run(int argc, char *argv[]) {
         }
 
         if (arg_cursor || arg_after_cursor || arg_cursor_file) {
-                _cleanup_free_ char *cursor_from_file = NULL;
-                const char *cursor = arg_cursor ?: arg_after_cursor;
+                cursor = arg_cursor ?: arg_after_cursor;
 
                 if (arg_cursor_file) {
                         r = read_one_line_file(arg_cursor_file, &cursor_from_file);
@@ -2666,30 +2459,40 @@ static int run(int argc, char *argv[]) {
                         }
                 } else
                         after_cursor = arg_after_cursor;
+        }
 
-                if (cursor) {
-                        r = sd_journal_seek_cursor(j, cursor);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to seek to cursor: %m");
+        if (cursor) {
+                r = sd_journal_seek_cursor(j, cursor);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to seek to cursor: %m");
 
-                        use_cursor = true;
+                r = sd_journal_step_one(j, !arg_reverse);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to iterate through journal: %m");
+
+                if (after_cursor && r > 0) {
+                        /* With --after-cursor=/--cursor-file= we want to skip the first entry only if it's
+                         * the entry the cursor is pointing at, otherwise, if some journal filters are used,
+                         * we might skip the first entry of the filter match, which leads to unexpectedly
+                         * missing journal entries. */
+                        int k;
+
+                        k = sd_journal_test_cursor(j, cursor);
+                        if (k < 0)
+                                return log_error_errno(k, "Failed to test cursor against current entry: %m");
+                        if (k > 0)
+                                /* Current entry matches the one our cursor is pointing at, so let's try
+                                 * to advance the next entry. */
+                                r = sd_journal_step_one(j, !arg_reverse);
                 }
-        }
 
-        if (use_cursor) {
-                if (!arg_reverse)
-                        r = sd_journal_next_skip(j, 1 + after_cursor);
-                else
-                        r = sd_journal_previous_skip(j, 1 + after_cursor);
-
-                if (after_cursor && r < 2) {
+                if (r == 0) {
                         /* We couldn't find the next entry after the cursor. */
                         if (arg_follow)
                                 need_seek = true;
                         else
                                 arg_lines = 0;
                 }
-
         } else if (arg_until_set && (arg_reverse || arg_lines_needs_seek_end())) {
                 /* If both --until and any of --reverse and --lines=N is specified, things get
                  * a little tricky. We seek to the place of --until first. If only --reverse or