Since262299dccbcompare_locations() falls through to a realtime-based comparison in compare_boot_ids() if the compared entries have different boot IDs (and seqnum IDs). This can, however, cause us to pick up the wrong entry/file on systems with unreliable/missing RTC, where early-boot entries of a boot might have earlier realtime timestamp compared to the previous boot. For example, here's a minimal reproducer consisting of two journal files containing three entries from two separate boot IDs: $ journalctl --file one.journal -o export __CURSOR=s=ba37c62fcacf406aad088166c4963cd1;i=1;b=c220efc91dd440deb89d82c98ddb11a5;m=186a0;t=1e8480;x=39d4323a8e5a248d __REALTIME_TIMESTAMP=2000000 __MONOTONIC_TIMESTAMP=100000 __SEQNUM=1 __SEQNUM_ID=ba37c62fcacf406aad088166c4963cd1 _BOOT_ID=c220efc91dd440deb89d82c98ddb11a5 NUMBER=1 $ journalctl --file two.journal -o export __CURSOR=s=9d15197efcc54d8ca5d6e7cce28373e2;i=1;b=686dc35e78f64b6ba126018ab6e8bae9;m=186a0;t=f4240;x=57db1c7bd38b37ed __REALTIME_TIMESTAMP=1000000 __MONOTONIC_TIMESTAMP=100000 __SEQNUM=1 __SEQNUM_ID=9d15197efcc54d8ca5d6e7cce28373e2 _BOOT_ID=686dc35e78f64b6ba126018ab6e8bae9 NUMBER=2 __CURSOR=s=9d15197efcc54d8ca5d6e7cce28373e2;i=2;b=686dc35e78f64b6ba126018ab6e8bae9;m=30d40;t=2dc6c0;x=3513732159948113 __REALTIME_TIMESTAMP=3000000 __MONOTONIC_TIMESTAMP=200000 __SEQNUM=2 __SEQNUM_ID=9d15197efcc54d8ca5d6e7cce28373e2 _BOOT_ID=686dc35e78f64b6ba126018ab6e8bae9 NUMBER=3 The first file (one.journal) contains a single entry from the first boot (c220efc91dd440deb89d82c98ddb11a5) and a realtime timestamp of 2s. The second file (two.journal) contains two entries from the second boot (686dc35e78f64b6ba126018ab6e8bae9) - the first entry has a realtime timestamp of 1s (i.e. _earlier_ than the tail timestamp of the first boot), and the second entry has a realtime timestamp of 3s (i.e. after a time correction). This works fine for forward iteration, but seeking directly to the second entry is when things go south: First entry: $ python3 <<EOF c = "s=ba37c62fcacf406aad088166c4963cd1;i=1;b=c220efc91dd440deb89d82c98ddb11a5;m=186a0;t=1e8480;x=39d4323a8e5a248d" from systemd import journal; j = journal.Reader(path="."); j.seek_cursor(c); j.get_next(); print(j.test_cursor(c)) EOF True Second entry: $ python3 <<EOF c = "s=9d15197efcc54d8ca5d6e7cce28373e2;i=1;b=686dc35e78f64b6ba126018ab6e8bae9;m=186a0;t=f4240;x=57db1c7bd38b37ed" from systemd import journal; j = journal.Reader(path="."); j.seek_cursor(c); j.get_next(); print(j.test_cursor(c)) EOF False That's because: - we call sd_journal_seek_cursor(...) and parse it (seqnum, seqnum_id, boot_id, realtime, ...) - we call sd_journal_next() -> real_journal_next() which iterates over all open journal files to find all candidate entries and pick the best one - for each journal file we call next_beyond_location() - for next_beyond_location(one.journal) we get: - last_direction is _DIRECTION_INVALID, so we call find_location_with_matches() - we fall all the way through to journal_file_move_to_entry_by_realtime() - here we try to find the first entry with realtime >= 1s; this file has only one entry with realtime = 2s which matches - we got a match and new_file is unset, so we set it to one.journal - for next_beyond_location(two.journal) we get: - we fall through to find_location_with_matches() - here we have a matching seqnum, so we call journal_file_move_to_entry_by_seqnum() - we got another match but new_file is set, so we call compare_locations(two.journal, one.journal): - we fall through to compare_boot_ids() - we get the tail timestamps of both journal files and return CMP(second boot, first boot) -> CMP(3s, 2s) = 1 - this bubbles up back to real_journal_next() where found = false, so new_file remains one.journal (the wrong journal file) - this is followed by set_location(one.journal), which makes the following sd_journal_test_cursor() call fail, as the boot_id/seqnum_id/... of the current position don't match with the cursor To fix this, let's track the journal file that holds the exact cursor match, and override the chosen candidate if needed once we process all open journals. Resolves: #31516 (cherry picked from commit868d07d1d4) (cherry picked from commit9df40e25f2)
System and Service Manager
Details
Most documentation is available on systemd's web site.
Assorted, older, general information about systemd can be found in the systemd Wiki.
Information about build requirements is provided in the README file.
Consult our NEWS file for information about what's new in the most recent systemd versions.
Please see the Code Map for information about this repository's layout and content.
Please see the Hacking guide for information on how to hack on systemd and test your modifications.
Please see our Contribution Guidelines for more information about filing GitHub Issues and posting GitHub Pull Requests.
When preparing patches for systemd, please follow our Coding Style Guidelines.
If you are looking for support, please contact our mailing list, join our IRC channel #systemd on libera.chat or Matrix channel
Stable branches with backported patches are available in the stable repo.
We have a security bug bounty program sponsored by the Sovereign Tech Fund hosted on YesWeHack
Repositories with distribution packages built from git main are available on OBS, and also repositories with packages built from the latest stable release
