1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-09 01:18:19 +03:00

Merge pull request #33955 from yuwata/sd-journal-fix-sd_journal_seek_monotonic_usec

sd-journal: fix sd_journal_seek_monotonic_usec()
This commit is contained in:
Yu Watanabe 2024-08-15 15:38:19 +09:00 committed by GitHub
commit 6ddf879ce8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 910 additions and 398 deletions

View File

@ -164,7 +164,7 @@ enum {
STATE_OFFLINE = 0,
STATE_ONLINE = 1,
STATE_ARCHIVED = 2,
_STATE_MAX
_STATE_MAX,
};
/* Header flags */

View File

@ -46,7 +46,7 @@ typedef enum LocationType {
/* We should seek to the precise location specified, and
* return it, as we haven't read it yet. */
LOCATION_SEEK
LOCATION_SEEK,
} LocationType;
typedef enum OfflineState {
@ -56,7 +56,7 @@ typedef enum OfflineState {
OFFLINE_CANCEL,
OFFLINE_AGAIN_FROM_SYNCING,
OFFLINE_AGAIN_FROM_OFFLINING,
OFFLINE_DONE
OFFLINE_DONE,
} OfflineState;
typedef struct JournalFile {

View File

@ -25,7 +25,7 @@ typedef struct Directory Directory;
typedef enum MatchType {
MATCH_DISCRETE,
MATCH_OR_TERM,
MATCH_AND_TERM
MATCH_AND_TERM,
} MatchType;
struct Match {

View File

@ -62,7 +62,6 @@ static void journal_file_unlink_newest_by_boot_id(sd_journal *j, JournalFile *f)
static int journal_put_error(sd_journal *j, int r, const char *path) {
_cleanup_free_ char *copy = NULL;
int k;
/* Memorize an error we encountered, and store which
* file/directory it was generated from. Note that we store
@ -84,13 +83,11 @@ static int journal_put_error(sd_journal *j, int r, const char *path) {
return -ENOMEM;
}
k = hashmap_ensure_put(&j->errors, NULL, INT_TO_PTR(r), copy);
if (k < 0) {
if (k == -EEXIST)
return 0;
return k;
}
r = hashmap_ensure_put(&j->errors, NULL, INT_TO_PTR(r), copy);
if (r == -EEXIST)
return 0;
if (r < 0)
return r;
TAKE_PTR(copy);
return 0;
@ -675,7 +672,7 @@ static int next_for_match(
uint64_t after_offset,
direction_t direction,
Object **ret,
uint64_t *offset) {
uint64_t *ret_offset) {
int r;
uint64_t np = 0;
@ -699,7 +696,7 @@ static int next_for_match(
if (r <= 0)
return r;
return journal_file_move_to_entry_by_offset_for_data(f, d, after_offset, direction, ret, offset);
return journal_file_move_to_entry_by_offset_for_data(f, d, after_offset, direction, ret, ret_offset);
} else if (m->type == MATCH_OR_TERM) {
@ -760,19 +757,68 @@ static int next_for_match(
return r;
}
if (offset)
*offset = np;
if (ret_offset)
*ret_offset = np;
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,
JournalFile *f,
direction_t direction,
Object **ret,
uint64_t *offset) {
uint64_t *ret_offset) {
int r;
@ -793,16 +839,14 @@ 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;
return direction == DIRECTION_DOWN ? journal_file_move_to_entry_for_data(f, d, DIRECTION_DOWN, ret, ret_offset) : 0;
if (j->current_location.type == LOCATION_TAIL)
return direction == DIRECTION_UP ? journal_file_move_to_entry_for_data(f, d, DIRECTION_UP, ret, offset) : 0;
return direction == DIRECTION_UP ? journal_file_move_to_entry_for_data(f, d, DIRECTION_UP, ret, ret_offset) : 0;
if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
return journal_file_move_to_entry_by_seqnum_for_data(f, d, j->current_location.seqnum, direction, ret, offset);
return journal_file_move_to_entry_by_seqnum_for_data(f, d, j->current_location.seqnum, direction, ret, ret_offset);
if (j->current_location.monotonic_set) {
r = journal_file_move_to_entry_by_monotonic_for_data(f, d, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
r = journal_file_move_to_entry_by_monotonic_for_data(f, d, j->current_location.boot_id, j->current_location.monotonic, direction, ret, ret_offset);
if (r != 0)
return r;
@ -810,11 +854,17 @@ 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);
return journal_file_move_to_entry_by_realtime_for_data(f, d, j->current_location.realtime, direction, ret, ret_offset);
return journal_file_move_to_entry_for_data(f, d, 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, ret_offset);
return journal_file_move_to_entry_for_data(f, d, direction, ret, ret_offset);
} else if (m->type == MATCH_OR_TERM) {
uint64_t np = 0;
@ -842,8 +892,8 @@ static int find_location_for_match(
return r;
}
if (offset)
*offset = np;
if (ret_offset)
*ret_offset = np;
return 1;
@ -869,7 +919,7 @@ static int find_location_for_match(
np = cp;
}
return next_for_match(j, m, f, np, direction, ret, offset);
return next_for_match(j, m, f, np, direction, ret, ret_offset);
}
}
@ -878,35 +928,51 @@ static int find_location_with_matches(
JournalFile *f,
direction_t direction,
Object **ret,
uint64_t *offset) {
uint64_t *ret_offset) {
int r;
assert(j);
assert(f);
assert(ret);
assert(offset);
if (!j->level0) {
/* No matches is simple */
if (j->level0)
return find_location_for_match(j, j->level0, f, direction, ret, ret_offset);
if (j->current_location.type == LOCATION_HEAD)
return direction == DIRECTION_DOWN ? journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset) : 0;
if (j->current_location.type == LOCATION_TAIL)
return direction == DIRECTION_UP ? journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset) : 0;
if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
if (j->current_location.monotonic_set) {
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 (j->current_location.realtime_set)
return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
/* No matches is simple */
return journal_file_next_entry(f, 0, direction, ret, offset);
} else
return find_location_for_match(j, j->level0, f, direction, ret, offset);
if (j->current_location.type == LOCATION_HEAD)
return direction == DIRECTION_DOWN ? journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, ret_offset) : 0;
if (j->current_location.type == LOCATION_TAIL)
return direction == DIRECTION_UP ? journal_file_next_entry(f, 0, DIRECTION_UP, ret, ret_offset) : 0;
if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, ret_offset);
if (j->current_location.monotonic_set) {
r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, 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, 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, ret_offset);
}
return journal_file_next_entry(f, 0, direction, ret, ret_offset);
}
static int next_with_matches(
@ -914,24 +980,22 @@ static int next_with_matches(
JournalFile *f,
direction_t direction,
Object **ret,
uint64_t *offset) {
uint64_t *ret_offset) {
assert(j);
assert(f);
assert(ret);
assert(offset);
/* No matches is easy. We simple advance the file
* pointer by one. */
if (!j->level0)
return journal_file_next_entry(f, f->current_offset, direction, ret, offset);
return journal_file_next_entry(f, f->current_offset, direction, ret, ret_offset);
/* If we have a match then we look for the next matching entry
* with an offset at least one step larger */
return next_for_match(j, j->level0, f,
direction == DIRECTION_DOWN ? f->current_offset + 1
: f->current_offset - 1,
direction, ret, offset);
direction, ret, ret_offset);
}
static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction) {
@ -967,9 +1031,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;
@ -986,11 +1059,8 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc
bool found;
if (j->current_location.type == LOCATION_DISCRETE) {
int k;
k = compare_with_location(j, f, &j->current_location, j->current_file);
found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
r = compare_with_location(j, f, &j->current_location, j->current_file);
found = direction == DIRECTION_DOWN ? r > 0 : r < 0;
} else
found = true;
@ -1084,11 +1154,8 @@ static int real_journal_next(sd_journal *j, direction_t direction) {
if (!new_file)
found = true;
else {
int k;
k = compare_locations(j, f, new_file);
found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
r = compare_locations(j, f, new_file);
found = direction == DIRECTION_DOWN ? r < 0 : r > 0;
}
if (found)
@ -1315,7 +1382,6 @@ _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
_cleanup_free_ char *item = NULL;
unsigned long long ll;
sd_id128_t id;
int k = 0;
r = extract_first_word(&cursor, &item, ";", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
@ -1330,9 +1396,9 @@ _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
switch (item[0]) {
case 's':
k = sd_id128_from_string(item+2, &id);
if (k < 0)
return k;
r = sd_id128_from_string(item+2, &id);
if (r < 0)
return r;
if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
return 0;
break;
@ -1345,9 +1411,9 @@ _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
break;
case 'b':
k = sd_id128_from_string(item+2, &id);
if (k < 0)
return k;
r = sd_id128_from_string(item+2, &id);
if (r < 0)
return r;
if (!sd_id128_equal(id, o->entry.boot_id))
return 0;
break;

File diff suppressed because it is too large Load Diff

View File

@ -555,7 +555,7 @@ TEST(dns_cache_lookup_success) {
key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com");
ASSERT_NOT_NULL(key);
query_flags = 0;
ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
ASSERT_OK_POSITIVE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
ASSERT_EQ(cache.n_hit, 1u);
ASSERT_EQ(cache.n_miss, 0u);
@ -592,7 +592,7 @@ TEST(dns_cache_lookup_clamp_ttl) {
key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com");
ASSERT_NOT_NULL(key);
query_flags = SD_RESOLVED_CLAMP_TTL;
ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
ASSERT_OK_POSITIVE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
ASSERT_EQ(cache.n_hit, 1u);
ASSERT_EQ(cache.n_miss, 0u);
@ -635,7 +635,7 @@ TEST(dns_cache_lookup_returns_most_recent_response) {
key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com");
ASSERT_NOT_NULL(key);
query_flags = 0;
ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
ASSERT_OK_POSITIVE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
ASSERT_EQ(cache.n_hit, 1u);
ASSERT_EQ(cache.n_miss, 0u);
@ -680,7 +680,7 @@ TEST(dns_cache_lookup_retains_multiple_answers_from_one_response) {
key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com");
ASSERT_NOT_NULL(key);
query_flags = 0;
ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
ASSERT_OK_POSITIVE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
ASSERT_EQ(cache.n_hit, 1u);
ASSERT_EQ(cache.n_miss, 0u);
@ -724,7 +724,7 @@ TEST(dns_cache_lookup_nxdomain) {
key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.example.com");
ASSERT_NOT_NULL(key);
query_flags = 0;
ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
ASSERT_OK_POSITIVE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
ASSERT_EQ(cache.n_hit, 1u);
ASSERT_EQ(cache.n_miss, 0u);
@ -809,7 +809,7 @@ TEST(dns_cache_lookup_mdns_multiple_shared_responses_are_cached) {
key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com");
ASSERT_NOT_NULL(key);
query_flags = 0;
ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
ASSERT_OK_POSITIVE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
dns_resource_key_unref(key);
ASSERT_EQ(cache.n_hit, 1u);
@ -865,7 +865,7 @@ TEST(dns_cache_lookup_mdns_multiple_unshared_responses_are_not_cached) {
key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "example.com");
ASSERT_NOT_NULL(key);
query_flags = 0;
ASSERT_TRUE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
ASSERT_OK_POSITIVE(dns_cache_lookup(&cache, key, query_flags, &ret_rcode, &ret_answer, &ret_full_packet, &ret_query_flags, NULL));
dns_resource_key_unref(key);
ASSERT_EQ(cache.n_hit, 1u);

View File

@ -217,6 +217,39 @@ static inline int run_test_table(void) {
} \
})
/* For funtions that return a boolean on success and a negative errno on failure. */
#define ASSERT_OK_POSITIVE(expr) \
({ \
typeof(expr) _result = (expr); \
if (_result < 0) { \
log_error_errno(_result, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \
PROJECT_FILE, __LINE__, #expr); \
abort(); \
} \
if (_result == 0) { \
log_error("%s:%i: Assertion failed: expected \"%s\" to be positive, but it is zero.", \
PROJECT_FILE, __LINE__, #expr); \
abort(); \
} \
})
#define ASSERT_OK_ZERO(expr) \
({ \
typeof(expr) _result = (expr); \
if (_result < 0) { \
log_error_errno(_result, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \
PROJECT_FILE, __LINE__, #expr); \
abort(); \
} \
if (_result != 0) { \
char _sexpr[DECIMAL_STR_MAX(typeof(expr))]; \
xsprintf(_sexpr, DECIMAL_STR_FMT(_result), _result); \
log_error("%s:%i: Assertion failed: expected \"%s\" to be zero, but it is %s.", \
PROJECT_FILE, __LINE__, #expr, _sexpr); \
abort(); \
} \
})
#define ASSERT_OK_ERRNO(expr) \
({ \
typeof(expr) _result = (expr); \

View File

@ -1117,6 +1117,18 @@ TEST(ASSERT) {
ASSERT_SIGNAL(ASSERT_OK(-1), SIGABRT);
ASSERT_SIGNAL(ASSERT_OK(-ENOANO), SIGABRT);
ASSERT_OK_POSITIVE(1);
ASSERT_OK_POSITIVE(255);
ASSERT_SIGNAL(ASSERT_OK_POSITIVE(0), SIGABRT);
ASSERT_SIGNAL(ASSERT_OK_POSITIVE(-1), SIGABRT);
ASSERT_SIGNAL(ASSERT_OK_POSITIVE(-ENOANO), SIGABRT);
ASSERT_OK_ZERO(0);
ASSERT_SIGNAL(ASSERT_OK_ZERO(1), SIGABRT);
ASSERT_SIGNAL(ASSERT_OK_ZERO(255), SIGABRT);
ASSERT_SIGNAL(ASSERT_OK_ZERO(-1), SIGABRT);
ASSERT_SIGNAL(ASSERT_OK_ZERO(-ENOANO), SIGABRT);
ASSERT_OK_ERRNO(0 >= 0);
ASSERT_OK_ERRNO(255 >= 0);
ASSERT_OK_ERRNO(printf("Hello world\n"));