sd-journal: fix sd_journal_seek_monotonic_usec()
This fixes the following issues: - We have a journal file, which contains entries of boot A and B. Let T be the timestamp of the _last_ entry of boot A. If sd_journal_seek_monotonic_usec() is called for boot A with a timestamp _after_ T, following sd_journal_next() will provide the _first_ entry of boot A, rather than the first entry of boot B. - We have two journal files X and Y. The file X contains entries of boot A. Let T be the timestamp of the _last_ entry of boot A in file X. The file Y contains entries of boot A after timestamp T. If sd_journal_seek_monotonic_usec() is called for boot A with a timestamp _after_ T, following sd_journal_next() will provide the _first_ entry of boot A, whose timestamp is of course earlier than T.
This commit is contained in:
@@ -766,6 +766,55 @@ static int next_for_match(
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int move_by_boot_for_data(
|
||||
sd_journal *j,
|
||||
JournalFile *f,
|
||||
direction_t direction,
|
||||
sd_id128_t boot_id,
|
||||
uint64_t data_offset,
|
||||
Object **ret,
|
||||
uint64_t *ret_offset) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(j);
|
||||
assert(f);
|
||||
assert(IN_SET(direction, DIRECTION_DOWN, DIRECTION_UP));
|
||||
|
||||
for (;;) {
|
||||
/* First, move to the last (or first when DIRECTION_UP) entry for the boot. */
|
||||
uint64_t p = 0;
|
||||
r = journal_file_move_to_entry_by_monotonic(f, boot_id,
|
||||
direction == DIRECTION_DOWN ? USEC_INFINITY : 0,
|
||||
direction == DIRECTION_DOWN ? DIRECTION_UP : DIRECTION_DOWN,
|
||||
NULL, &p);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
/* Then, move to the first entry of the next boot (or the last entry of the previous boot with DIRECTION_UP). */
|
||||
Object *entry;
|
||||
r = journal_file_next_entry(f, p, direction, &entry, NULL);
|
||||
if (r <= 0) /* r == 0 means that no next (or previous) boot found. That is, we are at HEAD or TAIL now. */
|
||||
return r;
|
||||
|
||||
assert(entry->object.type == OBJECT_ENTRY);
|
||||
boot_id = entry->entry.boot_id;
|
||||
|
||||
/* Note, this object cannot be reused, as journal_file_move_to_entry_by_monotonic() may invalidate the object. */
|
||||
Object *data;
|
||||
r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &data);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Then, move to the matching entry. */
|
||||
r = journal_file_move_to_entry_by_monotonic_for_data(f, data, boot_id,
|
||||
direction == DIRECTION_DOWN ? 0 : USEC_INFINITY, direction,
|
||||
ret, ret_offset);
|
||||
if (r != 0) /* Here r == 0 is OK, as that means the boot contains no entry matching with the data. */
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
static int find_location_for_match(
|
||||
sd_journal *j,
|
||||
Match *m,
|
||||
@@ -793,8 +842,6 @@ static int find_location_for_match(
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
/* FIXME: missing: find by monotonic */
|
||||
|
||||
if (j->current_location.type == LOCATION_HEAD)
|
||||
return direction == DIRECTION_DOWN ? journal_file_move_to_entry_for_data(f, d, DIRECTION_DOWN, ret, offset) : 0;
|
||||
if (j->current_location.type == LOCATION_TAIL)
|
||||
@@ -810,10 +857,16 @@ static int find_location_for_match(
|
||||
r = journal_file_move_to_object(f, OBJECT_DATA, dp, &d);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* If not found, fall back to realtime if set, or go to the first entry of the next boot
|
||||
* (or the last entry of the previous boot when DIRECTION_UP). */
|
||||
}
|
||||
if (j->current_location.realtime_set)
|
||||
return journal_file_move_to_entry_by_realtime_for_data(f, d, j->current_location.realtime, direction, ret, offset);
|
||||
|
||||
if (j->current_location.monotonic_set)
|
||||
return move_by_boot_for_data(j, f, direction, j->current_location.boot_id, dp, ret, offset);
|
||||
|
||||
return journal_file_move_to_entry_for_data(f, d, direction, ret, offset);
|
||||
|
||||
} else if (m->type == MATCH_OR_TERM) {
|
||||
@@ -900,10 +953,28 @@ static int find_location_with_matches(
|
||||
r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
/* If not found, fall back to realtime if set, or go to the first entry of the next boot
|
||||
* (or the last entry of the previous boot when DIRECTION_UP). */
|
||||
}
|
||||
if (j->current_location.realtime_set)
|
||||
return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
|
||||
|
||||
if (j->current_location.monotonic_set) {
|
||||
uint64_t p = 0;
|
||||
|
||||
/* If not found in the above, first move to the last (or first when DIRECTION_UP) entry for the boot. */
|
||||
r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id,
|
||||
direction == DIRECTION_DOWN ? USEC_INFINITY : 0,
|
||||
direction == DIRECTION_DOWN ? DIRECTION_UP : DIRECTION_DOWN,
|
||||
NULL, &p);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
/* Then, move to the next or previous boot. */
|
||||
return journal_file_next_entry(f, p, direction, ret, offset);
|
||||
}
|
||||
|
||||
return journal_file_next_entry(f, 0, direction, ret, offset);
|
||||
} else
|
||||
return find_location_for_match(j, j->level0, f, direction, ret, offset);
|
||||
@@ -967,9 +1038,18 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc
|
||||
journal_file_save_location(f, c, cp);
|
||||
}
|
||||
} else {
|
||||
f->last_direction = direction;
|
||||
|
||||
r = find_location_with_matches(j, f, direction, &c, &cp);
|
||||
/* LOCATION_SEEK specified to j->current_location.type here means that this is called first
|
||||
* after sd_journal_seek_monotonic_usec() or friends was called. In that case, this file may
|
||||
* not contain any matching entries with the user-specified location, but another file may
|
||||
* contain them. If so, the second call of this function will use the seqnum, and we may find
|
||||
* an entry in _this_ file with the seqnum. To prevent the second call of this function exits
|
||||
* earlier by the first 'if' block of this function, do not save the direction if the current
|
||||
* location is LOCATION_SEEK. */
|
||||
if (r > 0 || j->current_location.type != LOCATION_SEEK)
|
||||
f->last_direction = direction;
|
||||
else
|
||||
assert(f->last_direction == _DIRECTION_INVALID);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user