diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h index 5cb1e6d9c8..1a63ca1f8d 100644 --- a/src/journal/journal-def.h +++ b/src/journal/journal-def.h @@ -31,9 +31,10 @@ typedef struct Header Header; typedef struct ObjectHeader ObjectHeader; typedef union Object Object; typedef struct DataObject DataObject; +typedef struct FieldObject FieldObject; typedef struct EntryObject EntryObject; typedef struct HashTableObject HashTableObject; -typedef struct BisectTableObject BisectTableObject; +typedef struct EntryArrayObject EntryArrayObject; typedef struct EntryItem EntryItem; typedef struct HashItem HashItem; @@ -41,9 +42,12 @@ typedef struct HashItem HashItem; enum { OBJECT_UNUSED, OBJECT_DATA, + OBJECT_FIELD, OBJECT_ENTRY, - OBJECT_HASH_TABLE, - OBJECT_BISECT_TABLE + OBJECT_DATA_HASH_TABLE, + OBJECT_FIELD_HASH_TABLE, + OBJECT_ENTRY_ARRAY, + _OBJECT_TYPE_MAX }; _packed_ struct ObjectHeader { @@ -56,18 +60,26 @@ _packed_ struct ObjectHeader { _packed_ struct DataObject { ObjectHeader object; uint64_t hash; - uint64_t head_entry_offset; - uint64_t tail_entry_offset; - uint64_t prev_hash_offset; uint64_t next_hash_offset; + uint64_t next_field_offset; + uint64_t entry_offset; /* the first array entry we store inline */ + uint64_t entry_array_offset; + uint64_t n_entries; + uint8_t payload[]; +}; + +_packed_ struct FieldObject { + ObjectHeader object; + uint64_t hash; + uint64_t next_hash_offset; + uint64_t head_data_offset; + uint64_t tail_data_offset; uint8_t payload[]; }; _packed_ struct EntryItem { uint64_t object_offset; uint64_t hash; - uint64_t prev_entry_offset; - uint64_t next_entry_offset; }; _packed_ struct EntryObject { @@ -77,8 +89,6 @@ _packed_ struct EntryObject { uint64_t monotonic; sd_id128_t boot_id; uint64_t xor_hash; - uint64_t prev_entry_offset; - uint64_t next_entry_offset; EntryItem items[]; }; @@ -89,20 +99,22 @@ _packed_ struct HashItem { _packed_ struct HashTableObject { ObjectHeader object; - HashItem table[]; + HashItem items[]; }; -_packed_ struct BisectTableObject { +_packed_ struct EntryArrayObject { ObjectHeader object; - uint64_t table[]; + uint64_t next_entry_array_offset; + uint64_t items[]; }; union Object { ObjectHeader object; DataObject data; + FieldObject field; EntryObject entry; HashTableObject hash_table; - BisectTableObject bisect_table; + EntryArrayObject entry_array; }; enum { @@ -115,30 +127,30 @@ _packed_ struct Header { uint8_t signature[8]; /* "LPKSHHRH" */ uint32_t compatible_flags; uint32_t incompatible_flags; - uint32_t state; - uint8_t reserved[4]; + uint8_t state; + uint8_t reserved[7]; sd_id128_t file_id; sd_id128_t machine_id; sd_id128_t boot_id; sd_id128_t seqnum_id; uint64_t arena_offset; uint64_t arena_size; - uint64_t arena_max_size; - uint64_t arena_min_size; - uint64_t arena_keep_free; - uint64_t hash_table_offset; /* for looking up data objects */ - uint64_t hash_table_size; - uint64_t bisect_table_offset; /* for looking up entry objects */ - uint64_t bisect_table_size; - uint64_t head_object_offset; + uint64_t arena_max_size; /* obsolete */ + uint64_t arena_min_size; /* obsolete */ + uint64_t arena_keep_free; /* obsolete */ + uint64_t data_hash_table_offset; /* for looking up data objects */ + uint64_t data_hash_table_size; + uint64_t field_hash_table_offset; /* for looking up field objects */ + uint64_t field_hash_table_size; uint64_t tail_object_offset; - uint64_t head_entry_offset; - uint64_t tail_entry_offset; - uint64_t last_bisect_offset; uint64_t n_objects; + uint64_t n_entries; uint64_t seqnum; + uint64_t first_seqnum; + uint64_t entry_array_offset; uint64_t head_entry_realtime; uint64_t tail_entry_realtime; + uint64_t tail_entry_monotonic; }; #endif diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index ab45132f0f..427631d30a 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -37,8 +37,8 @@ #define DEFAULT_MAX_USE (16ULL*1024ULL*1024ULL*16ULL) -#define DEFAULT_HASH_TABLE_SIZE (2047ULL*16ULL) -#define DEFAULT_BISECT_TABLE_SIZE ((DEFAULT_ARENA_MAX_SIZE/(64ULL*1024ULL))*8ULL) +#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*16ULL) +#define DEFAULT_FIELD_HASH_TABLE_SIZE (2047ULL*16ULL) #define DEFAULT_WINDOW_SIZE (128ULL*1024ULL*1024ULL) @@ -47,23 +47,17 @@ static const char signature[] = { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' }; #define ALIGN64(x) (((x) + 7ULL) & ~7ULL) void journal_file_close(JournalFile *f) { + int t; + assert(f); - if (f->header) { - if (f->writable && f->header->state == htole32(STATE_ONLINE)) - f->header->state = htole32(STATE_OFFLINE); + if (f->header && f->writable) + f->header->state = STATE_OFFLINE; - munmap(f->header, PAGE_ALIGN(sizeof(Header))); - } - if (f->hash_table_window) - munmap(f->hash_table_window, f->hash_table_window_size); - - if (f->bisect_table_window) - munmap(f->bisect_table_window, f->bisect_table_window_size); - - if (f->window) - munmap(f->window, f->window_size); + for (t = 0; t < _WINDOW_MAX; t++) + if (f->windows[t].ptr) + munmap(f->windows[t].ptr, f->windows[t].size); if (f->fd >= 0) close_nointr_nofail(f->fd); @@ -108,6 +102,7 @@ static int journal_file_init_header(JournalFile *f, JournalFile *template) { static int journal_file_refresh_header(JournalFile *f) { int r; + sd_id128_t boot_id; assert(f); @@ -115,11 +110,16 @@ static int journal_file_refresh_header(JournalFile *f) { if (r < 0) return r; - r = sd_id128_get_boot(&f->header->boot_id); + r = sd_id128_get_boot(&boot_id); if (r < 0) return r; - f->header->state = htole32(STATE_ONLINE); + if (sd_id128_equal(boot_id, f->header->boot_id)) + f->tail_entry_monotonic_valid = true; + + f->header->boot_id = boot_id; + + f->header->state = STATE_ONLINE; return 0; } @@ -147,7 +147,7 @@ static int journal_file_verify_header(JournalFile *f) { if (!sd_id128_equal(machine_id, f->header->machine_id)) return -EHOSTDOWN; - state = le32toh(f->header->state); + state = f->header->state; if (state == STATE_ONLINE) log_debug("Journal file %s is already online. Assuming unclean closing. Ignoring.", f->path); @@ -254,28 +254,33 @@ static int journal_file_map( return 0; } -static int journal_file_move_to(JournalFile *f, uint64_t offset, uint64_t size, void **ret) { +static int journal_file_move_to(JournalFile *f, int wt, uint64_t offset, uint64_t size, void **ret) { void *p; uint64_t delta; int r; + Window *w; assert(f); assert(ret); + assert(wt >= 0); + assert(wt < _WINDOW_MAX); - if (_likely_(f->window && - f->window_offset <= offset && - f->window_offset+f->window_size >= offset + size)) { + w = f->windows + wt; - *ret = (uint8_t*) f->window + (offset - f->window_offset); + if (_likely_(w->ptr && + w->offset <= offset && + w->offset + w->size >= offset + size)) { + + *ret = (uint8_t*) w->ptr + (offset - w->offset); return 0; } - if (f->window) { - if (munmap(f->window, f->window_size) < 0) + if (w->ptr) { + if (munmap(w->ptr, w->size) < 0) return -errno; - f->window = NULL; - f->window_size = f->window_offset = 0; + w->ptr = NULL; + w->size = w->offset = 0; } if (size < DEFAULT_WINDOW_SIZE) { @@ -297,8 +302,8 @@ static int journal_file_move_to(JournalFile *f, uint64_t offset, uint64_t size, r = journal_file_map(f, offset, size, - &f->window, &f->window_offset, &f->window_size, - & p); + &w->ptr, &w->offset, &w->size, + &p); if (r < 0) return r; @@ -308,26 +313,23 @@ static int journal_file_move_to(JournalFile *f, uint64_t offset, uint64_t size, } static bool verify_hash(Object *o) { - uint64_t t; + uint64_t h1, h2; assert(o); - t = le64toh(o->object.type); - if (t == OBJECT_DATA) { - uint64_t s, h1, h2; - - s = le64toh(o->object.size); - + if (o->object.type == OBJECT_DATA) { h1 = le64toh(o->data.hash); - h2 = hash64(o->data.payload, s - offsetof(Object, data.payload)); + h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); + } else if (o->object.type == OBJECT_FIELD) { + h1 = le64toh(o->field.hash); + h2 = hash64(o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload)); + } else + return true; - return h1 == h2; - } - - return true; + return h1 == h2; } -int journal_file_move_to_object(JournalFile *f, uint64_t offset, int type, Object **ret) { +int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret) { int r; void *t; Object *o; @@ -335,8 +337,9 @@ int journal_file_move_to_object(JournalFile *f, uint64_t offset, int type, Objec assert(f); assert(ret); + assert(type < _OBJECT_TYPE_MAX); - r = journal_file_move_to(f, offset, sizeof(ObjectHeader), &t); + r = journal_file_move_to(f, type >= 0 ? type : WINDOW_UNKNOWN, offset, sizeof(ObjectHeader), &t); if (r < 0) return r; @@ -346,11 +349,11 @@ int journal_file_move_to_object(JournalFile *f, uint64_t offset, int type, Objec if (s < sizeof(ObjectHeader)) return -EBADMSG; - if (type >= 0 && le64toh(o->object.type) != type) + if (type >= 0 && o->object.type != type) return -EBADMSG; if (s > sizeof(ObjectHeader)) { - r = journal_file_move_to(f, offset, s, &t); + r = journal_file_move_to(f, o->object.type, offset, s, &t); if (r < 0) return r; @@ -372,7 +375,7 @@ static uint64_t journal_file_seqnum(JournalFile *f, uint64_t *seqnum) { r = le64toh(f->header->seqnum) + 1; if (seqnum) { - /* If an external seqno counter was passed, we update + /* If an external seqnum counter was passed, we update * both the local and the external one, and set it to * the maximum of both */ @@ -384,10 +387,13 @@ static uint64_t journal_file_seqnum(JournalFile *f, uint64_t *seqnum) { f->header->seqnum = htole64(r); + if (f->header->first_seqnum == 0) + f->header->first_seqnum = htole64(r); + return r; } -static int journal_file_append_object(JournalFile *f, uint64_t size, Object **ret, uint64_t *offset) { +static int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset) { int r; uint64_t p; Object *tail, *o; @@ -399,11 +405,10 @@ static int journal_file_append_object(JournalFile *f, uint64_t size, Object **re assert(ret); p = le64toh(f->header->tail_object_offset); - if (p == 0) p = le64toh(f->header->arena_offset); else { - r = journal_file_move_to_object(f, p, -1, &tail); + r = journal_file_move_to_object(f, -1, p, &tail); if (r < 0) return r; @@ -414,21 +419,17 @@ static int journal_file_append_object(JournalFile *f, uint64_t size, Object **re if (r < 0) return r; - r = journal_file_move_to(f, p, size, &t); + r = journal_file_move_to(f, type, p, size, &t); if (r < 0) return r; o = (Object*) t; zero(o->object); - o->object.type = htole64(OBJECT_UNUSED); - zero(o->object.reserved); + o->object.type = type; o->object.size = htole64(size); f->header->tail_object_offset = htole64(p); - if (f->header->head_object_offset == 0) - f->header->head_object_offset = htole64(p); - f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1); *ret = o; @@ -437,135 +438,137 @@ static int journal_file_append_object(JournalFile *f, uint64_t size, Object **re return 0; } -static int journal_file_setup_hash_table(JournalFile *f) { +static int journal_file_setup_data_hash_table(JournalFile *f) { uint64_t s, p; Object *o; int r; assert(f); - s = DEFAULT_HASH_TABLE_SIZE; - r = journal_file_append_object(f, offsetof(Object, hash_table.table) + s, &o, &p); + s = DEFAULT_DATA_HASH_TABLE_SIZE; + r = journal_file_append_object(f, + OBJECT_DATA_HASH_TABLE, + offsetof(Object, hash_table.items) + s, + &o, &p); if (r < 0) return r; - o->object.type = htole64(OBJECT_HASH_TABLE); - memset(o->hash_table.table, 0, s); + memset(o->hash_table.items, 0, s); - f->header->hash_table_offset = htole64(p + offsetof(Object, hash_table.table)); - f->header->hash_table_size = htole64(s); + f->header->data_hash_table_offset = htole64(p + offsetof(Object, hash_table.items)); + f->header->data_hash_table_size = htole64(s); return 0; } -static int journal_file_setup_bisect_table(JournalFile *f) { +static int journal_file_setup_field_hash_table(JournalFile *f) { uint64_t s, p; Object *o; int r; assert(f); - s = DEFAULT_BISECT_TABLE_SIZE; - r = journal_file_append_object(f, offsetof(Object, bisect_table.table) + s, &o, &p); + s = DEFAULT_FIELD_HASH_TABLE_SIZE; + r = journal_file_append_object(f, + OBJECT_FIELD_HASH_TABLE, + offsetof(Object, hash_table.items) + s, + &o, &p); if (r < 0) return r; - o->object.type = htole64(OBJECT_BISECT_TABLE); - memset(o->bisect_table.table, 0, s); + memset(o->hash_table.items, 0, s); - f->header->bisect_table_offset = htole64(p + offsetof(Object, bisect_table.table)); - f->header->bisect_table_size = htole64(s); + f->header->field_hash_table_offset = htole64(p + offsetof(Object, hash_table.items)); + f->header->field_hash_table_size = htole64(s); return 0; } -static int journal_file_map_hash_table(JournalFile *f) { +static int journal_file_map_data_hash_table(JournalFile *f) { uint64_t s, p; void *t; int r; assert(f); - p = le64toh(f->header->hash_table_offset); - s = le64toh(f->header->hash_table_size); + p = le64toh(f->header->data_hash_table_offset); + s = le64toh(f->header->data_hash_table_size); - r = journal_file_map(f, - p, s, - &f->hash_table_window, NULL, &f->hash_table_window_size, - &t); + r = journal_file_move_to(f, + WINDOW_DATA_HASH_TABLE, + p, s, + &t); if (r < 0) return r; - f->hash_table = t; + f->data_hash_table = t; return 0; } -static int journal_file_map_bisect_table(JournalFile *f) { +static int journal_file_map_field_hash_table(JournalFile *f) { uint64_t s, p; void *t; int r; assert(f); - p = le64toh(f->header->bisect_table_offset); - s = le64toh(f->header->bisect_table_size); - - r = journal_file_map(f, - p, s, - &f->bisect_table_window, NULL, &f->bisect_table_window_size, - &t); + p = le64toh(f->header->field_hash_table_offset); + s = le64toh(f->header->field_hash_table_size); + r = journal_file_move_to(f, + WINDOW_FIELD_HASH_TABLE, + p, s, + &t); if (r < 0) return r; - f->bisect_table = t; + f->field_hash_table = t; return 0; } -static int journal_file_link_data(JournalFile *f, Object *o, uint64_t offset, uint64_t hash_index) { - uint64_t p; +static int journal_file_link_data(JournalFile *f, Object *o, uint64_t offset, uint64_t hash) { + uint64_t p, h; int r; assert(f); assert(o); assert(offset > 0); - assert(o->object.type == htole64(OBJECT_DATA)); + assert(o->object.type == OBJECT_DATA); - o->data.head_entry_offset = o->data.tail_entry_offset = 0; - o->data.next_hash_offset = 0; + o->data.next_hash_offset = o->data.next_field_offset = 0; + o->data.entry_offset = o->data.entry_array_offset = 0; + o->data.n_entries = 0; - p = le64toh(f->hash_table[hash_index].tail_hash_offset); + h = hash % (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)); + p = le64toh(f->data_hash_table[h].head_hash_offset); if (p == 0) { /* Only entry in the hash table is easy */ - - o->data.prev_hash_offset = 0; - f->hash_table[hash_index].head_hash_offset = htole64(offset); + f->data_hash_table[h].head_hash_offset = htole64(offset); } else { - o->data.prev_hash_offset = htole64(p); - /* Temporarily move back to the previous data object, * to patch in pointer */ - r = journal_file_move_to_object(f, p, OBJECT_DATA, &o); + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); if (r < 0) return r; - o->data.next_hash_offset = offset; + o->data.next_hash_offset = htole64(offset); - r = journal_file_move_to_object(f, offset, OBJECT_DATA, &o); + r = journal_file_move_to_object(f, OBJECT_DATA, offset, &o); if (r < 0) return r; } - f->hash_table[hash_index].tail_hash_offset = htole64(offset); + f->data_hash_table[h].tail_hash_offset = htole64(offset); return 0; } -static int journal_file_append_data(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) { - uint64_t hash, h, p, np; - uint64_t osize; - Object *o; +int journal_file_find_data_object_with_hash( + JournalFile *f, + const void *data, uint64_t size, uint64_t hash, + Object **ret, uint64_t *offset) { + uint64_t p, osize, h; int r; assert(f); @@ -573,14 +576,13 @@ static int journal_file_append_data(JournalFile *f, const void *data, uint64_t s osize = offsetof(Object, data.payload) + size; - hash = hash64(data, size); - h = hash % (le64toh(f->header->hash_table_size) / sizeof(HashItem)); - p = le64toh(f->hash_table[h].head_hash_offset); + h = hash % (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)); + p = le64toh(f->data_hash_table[h].head_hash_offset); - while (p != 0) { - /* Look for this data object in the hash table */ + while (p > 0) { + Object *o; - r = journal_file_move_to_object(f, p, OBJECT_DATA, &o); + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); if (r < 0) return r; @@ -596,21 +598,66 @@ static int journal_file_append_data(JournalFile *f, const void *data, uint64_t s if (offset) *offset = p; - return 0; + return 1; } p = le64toh(o->data.next_hash_offset); } - r = journal_file_append_object(f, osize, &o, &np); + return 0; +} + +int journal_file_find_data_object( + JournalFile *f, + const void *data, uint64_t size, + Object **ret, uint64_t *offset) { + + uint64_t hash; + + assert(f); + assert(data || size == 0); + + hash = hash64(data, size); + + return journal_file_find_data_object_with_hash(f, + data, size, hash, + ret, offset); +} + +static int journal_file_append_data(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) { + uint64_t hash, p; + uint64_t osize; + Object *o; + int r; + + assert(f); + assert(data || size == 0); + + hash = hash64(data, size); + + r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p); + if (r < 0) + return r; + else if (r > 0) { + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 0; + } + + osize = offsetof(Object, data.payload) + size; + r = journal_file_append_object(f, OBJECT_DATA, osize, &o, &p); if (r < 0) return r; - o->object.type = htole64(OBJECT_DATA); o->data.hash = htole64(hash); memcpy(o->data.payload, data, size); - r = journal_file_link_data(f, o, np, h); + r = journal_file_link_data(f, o, p, hash); if (r < 0) return r; @@ -618,7 +665,7 @@ static int journal_file_append_data(JournalFile *f, const void *data, uint64_t s *ret = o; if (offset) - *offset = np; + *offset = p; return 0; } @@ -630,8 +677,108 @@ uint64_t journal_file_entry_n_items(Object *o) { return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem); } +static uint64_t journal_file_entry_array_n_items(Object *o) { + assert(o); + assert(o->object.type == htole64(OBJECT_ENTRY_ARRAY)); + + return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t); +} + +static int link_entry_into_array(JournalFile *f, + uint64_t *first, + uint64_t *idx, + uint64_t p) { + int r; + uint64_t n = 0, ap = 0, q, i, a, hidx; + Object *o; + + assert(f); + assert(first); + assert(idx); + assert(p > 0); + + a = le64toh(*first); + i = hidx = le64toh(*idx); + while (a > 0) { + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + n = journal_file_entry_array_n_items(o); + if (i < n) { + o->entry_array.items[i] = htole64(p); + *idx = htole64(hidx + 1); + return 0; + } + + i -= n; + ap = a; + a = le64toh(o->entry_array.next_entry_array_offset); + } + + if (hidx > n) + n = (hidx+1) * 2; + else + n = n * 2; + + if (n < 4) + n = 4; + + r = journal_file_append_object(f, OBJECT_ENTRY_ARRAY, + offsetof(Object, entry_array.items) + n * sizeof(uint64_t), + &o, &q); + if (r < 0) + return r; + + o->entry_array.items[i] = htole64(p); + + if (ap == 0) + *first = q; + else { + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, ap, &o); + if (r < 0) + return r; + + o->entry_array.next_entry_array_offset = htole64(q); + } + + *idx = htole64(hidx + 1); + + return 0; +} + +static int link_entry_into_array_plus_one(JournalFile *f, + uint64_t *extra, + uint64_t *first, + uint64_t *idx, + uint64_t p) { + + int r; + + assert(f); + assert(extra); + assert(first); + assert(idx); + assert(p > 0); + + if (*idx == 0) + *extra = htole64(p); + else { + uint64_t i; + + i = le64toh(*idx) - 1; + r = link_entry_into_array(f, first, &i, p); + if (r < 0) + return r; + } + + *idx = htole64(le64toh(*idx) + 1); + return 0; +} + static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) { - uint64_t p, q; + uint64_t p; int r; assert(f); assert(o); @@ -641,81 +788,43 @@ static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offs if (p == 0) return -EINVAL; - o->entry.items[i].next_entry_offset = 0; - - /* Move to the data object */ - r = journal_file_move_to_object(f, p, OBJECT_DATA, &o); + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); if (r < 0) return r; - q = le64toh(o->data.tail_entry_offset); - o->data.tail_entry_offset = htole64(offset); - - if (q == 0) - o->data.head_entry_offset = htole64(offset); - else { - uint64_t n, j; - - /* Move to previous entry */ - r = journal_file_move_to_object(f, q, OBJECT_ENTRY, &o); - if (r < 0) - return r; - - n = journal_file_entry_n_items(o); - for (j = 0; j < n; j++) - if (le64toh(o->entry.items[j].object_offset) == p) - break; - - if (j >= n) - return -EBADMSG; - - o->entry.items[j].next_entry_offset = offset; - } - - /* Move back to original entry */ - r = journal_file_move_to_object(f, offset, OBJECT_ENTRY, &o); - if (r < 0) - return r; - - o->entry.items[i].prev_entry_offset = q; - return 0; + return link_entry_into_array_plus_one(f, + &o->data.entry_offset, + &o->data.entry_array_offset, + &o->data.n_entries, + offset); } static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) { - uint64_t p, i, n, k, a, b; + uint64_t n, i; int r; assert(f); assert(o); assert(offset > 0); - assert(o->object.type == htole64(OBJECT_ENTRY)); + assert(o->object.type == OBJECT_ENTRY); /* Link up the entry itself */ - p = le64toh(f->header->tail_entry_offset); + r = link_entry_into_array(f, + &f->header->entry_array_offset, + &f->header->n_entries, + offset); + if (r < 0) + return r; - o->entry.prev_entry_offset = f->header->tail_entry_offset; - o->entry.next_entry_offset = 0; + log_error("%s %lu", f->path, (unsigned long) f->header->n_entries); - if (p == 0) { - f->header->head_entry_offset = htole64(offset); + if (f->header->head_entry_realtime == 0) f->header->head_entry_realtime = o->entry.realtime; - } else { - /* Temporarily move back to the previous entry, to - * patch in pointer */ - r = journal_file_move_to_object(f, p, OBJECT_ENTRY, &o); - if (r < 0) - return r; - - o->entry.next_entry_offset = htole64(offset); - - r = journal_file_move_to_object(f, offset, OBJECT_ENTRY, &o); - if (r < 0) - return r; - } - - f->header->tail_entry_offset = htole64(offset); f->header->tail_entry_realtime = o->entry.realtime; + f->header->tail_entry_monotonic = o->entry.monotonic; + + f->tail_entry_monotonic_valid = true; /* Link up the items */ n = journal_file_entry_n_items(o); @@ -725,18 +834,6 @@ static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) { return r; } - /* Link up the entry in the bisect table */ - n = le64toh(f->header->bisect_table_size) / sizeof(uint64_t); - k = le64toh(f->header->arena_max_size) / n; - - a = (le64toh(f->header->last_bisect_offset) + k - 1) / k; - b = offset / k; - - for (; a <= b; a++) - f->bisect_table[a] = htole64(offset); - - f->header->last_bisect_offset = htole64(offset + le64toh(o->object.size)); - return 0; } @@ -745,7 +842,7 @@ static int journal_file_append_entry_internal( const dual_timestamp *ts, uint64_t xor_hash, const EntryItem items[], unsigned n_items, - uint64_t *seqno, + uint64_t *seqnum, Object **ret, uint64_t *offset) { uint64_t np; uint64_t osize; @@ -754,18 +851,18 @@ static int journal_file_append_entry_internal( assert(f); assert(items || n_items == 0); + assert(ts); osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem)); - r = journal_file_append_object(f, osize, &o, &np); + r = journal_file_append_object(f, OBJECT_ENTRY, osize, &o, &np); if (r < 0) return r; - o->object.type = htole64(OBJECT_ENTRY); - o->entry.seqnum = htole64(journal_file_seqnum(f, seqno)); + o->entry.seqnum = htole64(journal_file_seqnum(f, seqnum)); memcpy(o->entry.items, items, n_items * sizeof(EntryItem)); - o->entry.realtime = htole64(ts ? ts->realtime : now(CLOCK_REALTIME)); - o->entry.monotonic = htole64(ts ? ts->monotonic : now(CLOCK_MONOTONIC)); + o->entry.realtime = htole64(ts->realtime); + o->entry.monotonic = htole64(ts->monotonic); o->entry.xor_hash = htole64(xor_hash); o->entry.boot_id = f->header->boot_id; @@ -782,15 +879,31 @@ static int journal_file_append_entry_internal( return 0; } -int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqno, Object **ret, uint64_t *offset) { +int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqnum, Object **ret, uint64_t *offset) { unsigned i; EntryItem *items; int r; uint64_t xor_hash = 0; + struct dual_timestamp _ts; assert(f); assert(iovec || n_iovec == 0); + if (!f->writable) + return -EPERM; + + if (!ts) { + dual_timestamp_get(&_ts); + ts = &_ts; + } + + if (f->tail_entry_monotonic_valid && + ts->monotonic < le64toh(f->header->tail_entry_monotonic)) + return -EINVAL; + + if (ts->realtime < le64toh(f->header->tail_entry_realtime)) + return -EINVAL; + items = new(EntryItem, n_iovec); if (!items) return -ENOMEM; @@ -808,7 +921,7 @@ int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const st items[i].hash = o->data.hash; } - r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, seqno, ret, offset); + r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, seqnum, ret, offset); finish: free(items); @@ -816,105 +929,186 @@ finish: return r; } -int journal_file_move_to_entry(JournalFile *f, uint64_t seqnum, Object **ret, uint64_t *offset) { +static int generic_array_get(JournalFile *f, + uint64_t first, + uint64_t i, + Object **ret, uint64_t *offset) { + Object *o; - uint64_t lower, upper, p, n, k; + uint64_t p, a; int r; assert(f); - n = le64toh(f->header->bisect_table_size) / sizeof(uint64_t); - k = le64toh(f->header->arena_max_size) / n; + a = first; + while (a > 0) { + uint64_t n; - lower = 0; - upper = le64toh(f->header->last_bisect_offset)/k+1; + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; - while (lower < upper) { - k = (upper + lower) / 2; - p = le64toh(f->bisect_table[k]); - - if (p == 0) { - upper = k; - continue; + n = journal_file_entry_array_n_items(o); + if (i < n) { + p = le64toh(o->entry_array.items[i]); + break; } - r = journal_file_move_to_object(f, p, OBJECT_ENTRY, &o); - if (r < 0) - return r; - - if (o->entry.seqnum == seqnum) { - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 1; - } else if (seqnum < o->entry.seqnum) - upper = k; - else if (seqnum > o->entry.seqnum) - lower = k+1; + i -= n; + a = le64toh(o->entry_array.next_entry_array_offset); } - assert(lower == upper); - - if (lower <= 0) + if (a <= 0 || p <= 0) return 0; - /* The object we are looking for is between - * bisect_table[lower-1] and bisect_table[lower] */ + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; - p = le64toh(f->bisect_table[lower-1]); + if (ret) + *ret = o; - for (;;) { - r = journal_file_move_to_object(f, p, OBJECT_ENTRY, &o); + if (offset) + *offset = p; + + return 1; +} + +static int generic_array_get_plus_one(JournalFile *f, + uint64_t extra, + uint64_t first, + uint64_t i, + Object **ret, uint64_t *offset) { + + Object *o; + + assert(f); + + if (i == 0) { + int r; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); if (r < 0) return r; - if (o->entry.seqnum == seqnum) { - if (ret) - *ret = o; + if (ret) + *ret = o; - if (offset) - *offset = p; + if (offset) + *offset = extra; - return 1; + return 1; + } - } if (seqnum < o->entry.seqnum) + return generic_array_get(f, first, i-1, ret, offset); +} + +enum { + TEST_FOUND, + TEST_LEFT, + TEST_RIGHT +}; + +static int generic_array_bisect(JournalFile *f, + uint64_t first, + uint64_t n, + uint64_t needle, + int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle), + direction_t direction, + Object **ret, + uint64_t *offset, + uint64_t *idx) { + + uint64_t a, p, t = 0, i = 0, last_p = 0; + bool subtract_one = false; + Object *o, *array = NULL; + int r; + + assert(f); + assert(test_object); + + a = first; + while (a > 0) { + uint64_t left, right, k, lp; + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array); + if (r < 0) + return r; + + k = journal_file_entry_array_n_items(array); + right = MIN(k, n); + if (right <= 0) return 0; - if (o->entry.next_entry_offset == 0) + i = right - 1; + lp = p = le64toh(array->entry_array.items[i]); + if (p <= 0) + return -EBADMSG; + + r = test_object(f, p, needle); + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + if (r == TEST_RIGHT) { + left = 0; + right -= 1; + for (;;) { + if (left == right) { + if (direction == DIRECTION_UP) + subtract_one = true; + + i = left; + goto found; + } + + assert(left < right); + + i = (left + right) / 2; + p = le64toh(array->entry_array.items[i]); + if (p <= 0) + return -EBADMSG; + + r = test_object(f, p, needle); + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + if (r == TEST_RIGHT) + right = i; + else + left = i + 1; + } + } + + if (k > n) return 0; - p = le64toh(o->entry.next_entry_offset); + last_p = lp; + + n -= k; + t += k; + a = le64toh(array->entry_array.next_entry_array_offset); } return 0; -} -int journal_file_next_entry(JournalFile *f, Object *o, direction_t direction, Object **ret, uint64_t *offset) { - uint64_t np; - int r; - - assert(f); - - if (!o) - np = le64toh(direction == DIRECTION_DOWN ? - f->header->head_entry_offset : - f->header->tail_entry_offset); - else { - if (le64toh(o->object.type) != OBJECT_ENTRY) - return -EINVAL; - - np = le64toh(direction == DIRECTION_DOWN ? - o->entry.next_entry_offset : - o->entry.prev_entry_offset); - } - - if (np == 0) +found: + if (subtract_one && t == 0 && i == 0) return 0; - r = journal_file_move_to_object(f, np, OBJECT_ENTRY, &o); + if (subtract_one && i == 0) + p = last_p; + else if (subtract_one) + p = le64toh(array->entry_array.items[i-1]); + else + p = le64toh(array->entry_array.items[i]); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); if (r < 0) return r; @@ -922,92 +1116,403 @@ int journal_file_next_entry(JournalFile *f, Object *o, direction_t direction, Ob *ret = o; if (offset) - *offset = np; + *offset = p; + + if (idx) + *idx = t + i - (subtract_one ? 1 : 0); return 1; } -int journal_file_prev_entry(JournalFile *f, Object *o, Object **ret, uint64_t *offset) { - uint64_t np; +static int generic_array_bisect_plus_one(JournalFile *f, + uint64_t extra, + uint64_t first, + uint64_t n, + uint64_t needle, + int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle), + direction_t direction, + Object **ret, + uint64_t *offset, + uint64_t *idx) { + int r; assert(f); + assert(test_object); - if (!o) - np = le64toh(f->header->tail_entry_offset); - else { - if (le64toh(o->object.type) != OBJECT_ENTRY) - return -EINVAL; - - np = le64toh(o->entry.prev_entry_offset); - } - - if (np == 0) + if (n <= 0) return 0; - r = journal_file_move_to_object(f, np, OBJECT_ENTRY, &o); + /* This bisects the array in object 'first', but first checks + * an extra */ + + r = test_object(f, extra, needle); if (r < 0) return r; - - if (ret) - *ret = o; - - if (offset) - *offset = np; - - return 1; -} - -int journal_file_find_first_entry(JournalFile *f, const void *data, uint64_t size, direction_t direction, Object **ret, uint64_t *offset) { - uint64_t p, osize, hash, h; - int r; - - assert(f); - assert(data || size == 0); - - osize = offsetof(Object, data.payload) + size; - - hash = hash64(data, size); - h = hash % (le64toh(f->header->hash_table_size) / sizeof(HashItem)); - p = le64toh(f->hash_table[h].head_hash_offset); - - while (p != 0) { + else if (r == TEST_FOUND) { Object *o; - r = journal_file_move_to_object(f, p, OBJECT_DATA, &o); + r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); if (r < 0) return r; - if (le64toh(o->object.size) == osize && - memcmp(o->data.payload, data, size) == 0) { + if (ret) + *ret = o; - if (le64toh(o->data.hash) != hash) - return -EBADMSG; + if (offset) + *offset = extra; + } else if (r == TEST_RIGHT) + return 0; - p = le64toh(direction == DIRECTION_DOWN ? - o->data.head_entry_offset : - o->data.tail_entry_offset); + r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx); - if (p == 0) + if (r > 0) + (*idx) ++; + + return r; +} + +static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) { + Object *o; + int r; + + assert(f); + assert(p > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (le64toh(o->entry.seqnum) == needle) + return TEST_FOUND; + else if (le64toh(o->entry.seqnum) < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +int journal_file_move_to_entry_by_seqnum( + JournalFile *f, + uint64_t seqnum, + direction_t direction, + Object **ret, + uint64_t *offset) { + + return generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + seqnum, + test_object_seqnum, + direction, + ret, offset, NULL); +} + +static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) { + Object *o; + int r; + + assert(f); + assert(p > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (le64toh(o->entry.realtime) == needle) + return TEST_FOUND; + else if (le64toh(o->entry.realtime) < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +int journal_file_move_to_entry_by_realtime( + JournalFile *f, + uint64_t realtime, + direction_t direction, + Object **ret, + uint64_t *offset) { + + return generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + realtime, + test_object_realtime, + direction, + ret, offset, NULL); +} + +static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) { + Object *o; + int r; + + assert(f); + assert(p > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (le64toh(o->entry.monotonic) == needle) + return TEST_FOUND; + else if (le64toh(o->entry.monotonic) < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +int journal_file_move_to_entry_by_monotonic( + JournalFile *f, + sd_id128_t boot_id, + uint64_t monotonic, + direction_t direction, + Object **ret, + uint64_t *offset) { + + char t[8+32+1] = "_BOOT_ID="; + Object *o; + int r; + + sd_id128_to_string(boot_id, t + 8); + + r = journal_file_find_data_object(f, t, strlen(t), &o, NULL); + if (r < 0) + return r; + else if (r == 0) + return -ENOENT; + + return generic_array_bisect_plus_one(f, + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + le64toh(o->data.n_entries), + monotonic, + test_object_monotonic, + direction, + ret, offset, NULL); +} + +static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) { + assert(f); + assert(p > 0); + + if (p == needle) + return TEST_FOUND; + else if (p < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +int journal_file_next_entry( + JournalFile *f, + Object *o, uint64_t p, + direction_t direction, + Object **ret, uint64_t *offset) { + + uint64_t i, n; + int r; + + assert(f); + assert(p > 0 || !o); + + n = le64toh(f->header->n_entries); + if (n <= 0) + return 0; + + if (!o) + i = direction == DIRECTION_DOWN ? 0 : n - 1; + else { + if (o->object.type != OBJECT_ENTRY) + return -EINVAL; + + r = generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + p, + test_object_offset, + DIRECTION_DOWN, + NULL, NULL, + &i); + if (r <= 0) + return r; + + if (direction == DIRECTION_DOWN) { + if (i >= n - 1) return 0; - r = journal_file_move_to_object(f, p, OBJECT_ENTRY, &o); - if (r < 0) - return r; + i++; + } else { + if (i <= 0) + return 0; - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 1; + i--; } - - p = le64toh(o->data.next_hash_offset); } - return 0; + /* And jump to it */ + return generic_array_get(f, + le64toh(f->header->entry_array_offset), + i, + ret, offset); +} + +int journal_file_skip_entry( + JournalFile *f, + Object *o, uint64_t p, + int64_t skip, + Object **ret, uint64_t *offset) { + + uint64_t i, n; + int r; + + assert(f); + assert(o); + assert(p > 0); + + if (o->object.type != OBJECT_ENTRY) + return -EINVAL; + + r = generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + p, + test_object_offset, + DIRECTION_DOWN, + NULL, NULL, + &i); + if (r <= 0) + return r; + + /* Calculate new index */ + if (skip < 0) { + if ((uint64_t) -skip >= i) + i = 0; + else + i = i - (uint64_t) -skip; + } else + i += (uint64_t) skip; + + n = le64toh(f->header->n_entries); + if (n <= 0) + return -EBADMSG; + + if (i >= n) + i = n-1; + + return generic_array_get(f, + le64toh(f->header->entry_array_offset), + i, + ret, offset); +} + +int journal_file_next_entry_for_data( + JournalFile *f, + Object *o, uint64_t p, + uint64_t data_offset, + direction_t direction, + Object **ret, uint64_t *offset) { + + uint64_t n, i; + int r; + Object *d; + + assert(f); + assert(p > 0 || !o); + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r <= 0) + return r; + + n = le64toh(d->data.n_entries); + if (n <= 0) + return n; + + if (!o) + i = direction == DIRECTION_DOWN ? 0 : n - 1; + else { + if (o->object.type != OBJECT_ENTRY) + return -EINVAL; + + r = generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + p, + test_object_offset, + DIRECTION_DOWN, + NULL, NULL, + &i); + + if (r <= 0) + return r; + + if (direction == DIRECTION_DOWN) { + if (i >= n - 1) + return 0; + + i++; + } else { + if (i <= 0) + return 0; + + i--; + } + + } + + return generic_array_get_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + i, + ret, offset); +} + +int journal_file_move_to_entry_by_seqnum_for_data( + JournalFile *f, + uint64_t data_offset, + uint64_t seqnum, + direction_t direction, + Object **ret, uint64_t *offset) { + + Object *d; + int r; + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r <= 0) + return r; + + return generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + seqnum, + test_object_seqnum, + direction, + ret, offset, NULL); +} + +int journal_file_move_to_entry_by_realtime_for_data( + JournalFile *f, + uint64_t data_offset, + uint64_t realtime, + direction_t direction, + Object **ret, uint64_t *offset) { + + Object *d; + int r; + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r <= 0) + return r; + + return generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + realtime, + test_object_realtime, + direction, + ret, offset, NULL); } void journal_file_dump(JournalFile *f) { @@ -1018,18 +1523,24 @@ void journal_file_dump(JournalFile *f) { assert(f); - printf("File ID: %s\n" + printf("File Path: %s\n" + "File ID: %s\n" "Machine ID: %s\n" "Boot ID: %s\n" - "Arena size: %llu\n", + "Arena size: %llu\n" + "Objects: %lu\n" + "Entries: %lu\n", + f->path, sd_id128_to_string(f->header->file_id, a), sd_id128_to_string(f->header->machine_id, b), sd_id128_to_string(f->header->boot_id, c), - (unsigned long long) le64toh(f->header->arena_size)); + (unsigned long long) le64toh(f->header->arena_size), + (unsigned long) le64toh(f->header->n_objects), + (unsigned long) le64toh(f->header->n_entries)); - p = le64toh(f->header->head_object_offset); + p = le64toh(f->header->arena_offset); while (p != 0) { - r = journal_file_move_to_object(f, p, -1, &o); + r = journal_file_move_to_object(f, -1, p, &o); if (r < 0) goto fail; @@ -1050,12 +1561,16 @@ void journal_file_dump(JournalFile *f) { (unsigned long long) le64toh(o->entry.realtime)); break; - case OBJECT_HASH_TABLE: - printf("Type: OBJECT_HASH_TABLE\n"); + case OBJECT_FIELD_HASH_TABLE: + printf("Type: OBJECT_FIELD_HASH_TABLE\n"); break; - case OBJECT_BISECT_TABLE: - printf("Type: OBJECT_BISECT_TABLE\n"); + case OBJECT_DATA_HASH_TABLE: + printf("Type: OBJECT_DATA_HASH_TABLE\n"); + break; + + case OBJECT_ENTRY_ARRAY: + printf("Type: OBJECT_ENTRY_ARRAY\n"); break; } @@ -1153,20 +1668,20 @@ int journal_file_open( if (newly_created) { - r = journal_file_setup_hash_table(f); + r = journal_file_setup_field_hash_table(f); if (r < 0) goto fail; - r = journal_file_setup_bisect_table(f); + r = journal_file_setup_data_hash_table(f); if (r < 0) goto fail; } - r = journal_file_map_hash_table(f); + r = journal_file_map_field_hash_table(f); if (r < 0) goto fail; - r = journal_file_map_bisect_table(f); + r = journal_file_map_data_hash_table(f); if (r < 0) goto fail; @@ -1381,5 +1896,8 @@ finish: free(list); + if (d) + closedir(d); + return r; } diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 795a446dc8..664f917bb0 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -28,6 +28,23 @@ #include "util.h" #include "sd-id128.h" +typedef struct Window { + void *ptr; + uint64_t offset; + uint64_t size; +} Window; + +enum { + WINDOW_UNKNOWN = OBJECT_UNUSED, + WINDOW_DATA = OBJECT_DATA, + WINDOW_ENTRY = OBJECT_ENTRY, + WINDOW_DATA_HASH_TABLE = OBJECT_DATA_HASH_TABLE, + WINDOW_FIELD_HASH_TABLE = OBJECT_FIELD_HASH_TABLE, + WINDOW_ENTRY_ARRAY = OBJECT_ENTRY_ARRAY, + WINDOW_HEADER, + _WINDOW_MAX +}; + typedef struct JournalFile { int fd; char *path; @@ -36,20 +53,13 @@ typedef struct JournalFile { int flags; int prot; bool writable; + bool tail_entry_monotonic_valid; Header *header; + HashItem *data_hash_table; + HashItem *field_hash_table; - HashItem *hash_table; - void *hash_table_window; - uint64_t hash_table_window_size; - - uint64_t *bisect_table; - void *bisect_table_window; - uint64_t bisect_table_window_size; - - void *window; - uint64_t window_offset; - uint64_t window_size; + Window windows[_WINDOW_MAX]; uint64_t current_offset; } JournalFile; @@ -60,20 +70,28 @@ typedef enum direction { } direction_t; int journal_file_open(const char *fname, int flags, mode_t mode, JournalFile *template, JournalFile **ret); - void journal_file_close(JournalFile *j); -int journal_file_move_to_object(JournalFile *f, uint64_t offset, int type, Object **ret); +int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret); uint64_t journal_file_entry_n_items(Object *o); int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqno, Object **ret, uint64_t *offset); -int journal_file_move_to_entry(JournalFile *f, uint64_t seqnum, Object **ret, uint64_t *offset); +int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); +int journal_file_find_data_object_with_hash(JournalFile *f, const void *data, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset); -int journal_file_find_first_entry(JournalFile *f, const void *data, uint64_t size, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_next_entry(JournalFile *f, Object *o, uint64_t p, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_skip_entry(JournalFile *f, Object *o, uint64_t p, int64_t skip, Object **ret, uint64_t *offset); -int journal_file_next_entry(JournalFile *f, Object *o, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_next_entry_for_data(JournalFile *f, Object *o, uint64_t p, uint64_t data_offset, direction_t direction, Object **ret, uint64_t *offset); + +int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset); + +int journal_file_move_to_entry_by_seqnum_for_data(JournalFile *f, uint64_t data_offset, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_realtime_for_data(JournalFile *f, uint64_t data_offset, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); void journal_file_dump(JournalFile *f); @@ -81,5 +99,4 @@ int journal_file_rotate(JournalFile **f); int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t min_free); - #endif diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 3e5ee1af96..9220efdfec 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) { } } - SD_JOURNAL_FOREACH_BEGIN(j) { + SD_JOURNAL_FOREACH(j) { const void *data; size_t length; @@ -71,7 +71,7 @@ int main(int argc, char *argv[]) { free(cursor); sd_journal_get_realtime_usec(j, &realtime); - sd_journal_get_monotonic_usec(j, &monotonic); + sd_journal_get_monotonic_usec(j, &monotonic, NULL); printf("realtime: %llu\n" "monotonic: %llu\n", (unsigned long long) realtime, @@ -80,7 +80,7 @@ int main(int argc, char *argv[]) { SD_JOURNAL_FOREACH_DATA(j, data, length) printf("\t%.*s\n", (int) length, (const char*) data); - } SD_JOURNAL_FOREACH_END(j); + } finish: if (j) diff --git a/src/journal/journald.c b/src/journal/journald.c index ede314a55f..89d8bee2a2 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -144,14 +144,14 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str *audit_session = NULL, *audit_loginuid = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *exe = NULL, *cgroup = NULL; - struct iovec iovec[16]; + struct iovec iovec[17]; unsigned n = 0; char idbuf[33]; sd_id128_t id; int r; char *t; int priority = LOG_USER | LOG_INFO; - uid_t loginuid = 0; + uid_t loginuid = 0, realuid = 0; JournalFile *f; parse_syslog_priority((char**) &buf, &priority); @@ -171,18 +171,20 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str uint32_t session; char *path; - if (asprintf(&pid, "PID=%lu", (unsigned long) ucred->pid) >= 0) + realuid = ucred->uid; + + if (asprintf(&pid, "_PID=%lu", (unsigned long) ucred->pid) >= 0) IOVEC_SET_STRING(iovec[n++], pid); - if (asprintf(&uid, "UID=%lu", (unsigned long) ucred->uid) >= 0) + if (asprintf(&uid, "_UID=%lu", (unsigned long) ucred->uid) >= 0) IOVEC_SET_STRING(iovec[n++], uid); - if (asprintf(&gid, "GID=%lu", (unsigned long) ucred->gid) >= 0) + if (asprintf(&gid, "_GID=%lu", (unsigned long) ucred->gid) >= 0) IOVEC_SET_STRING(iovec[n++], gid); r = get_process_comm(ucred->pid, &t); if (r >= 0) { - comm = strappend("COMM=", t); + comm = strappend("_COMM=", t); if (comm) IOVEC_SET_STRING(iovec[n++], comm); free(t); @@ -190,7 +192,7 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str r = get_process_exe(ucred->pid, &t); if (r >= 0) { - exe = strappend("EXE=", t); + exe = strappend("_EXE=", t); if (comm) IOVEC_SET_STRING(iovec[n++], exe); free(t); @@ -198,7 +200,7 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str r = get_process_cmdline(ucred->pid, LINE_MAX, false, &t); if (r >= 0) { - cmdline = strappend("CMDLINE=", t); + cmdline = strappend("_CMDLINE=", t); if (cmdline) IOVEC_SET_STRING(iovec[n++], cmdline); free(t); @@ -206,17 +208,17 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str r = audit_session_from_pid(ucred->pid, &session); if (r >= 0) - if (asprintf(&audit_session, "AUDIT_SESSION=%lu", (unsigned long) session) >= 0) + if (asprintf(&audit_session, "_AUDIT_SESSION=%lu", (unsigned long) session) >= 0) IOVEC_SET_STRING(iovec[n++], audit_session); r = audit_loginuid_from_pid(ucred->pid, &loginuid); if (r >= 0) - if (asprintf(&audit_loginuid, "AUDIT_LOGINUID=%lu", (unsigned long) loginuid) >= 0) + if (asprintf(&audit_loginuid, "_AUDIT_LOGINUID=%lu", (unsigned long) loginuid) >= 0) IOVEC_SET_STRING(iovec[n++], audit_loginuid); r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, ucred->pid, &path); if (r >= 0) { - cgroup = strappend("SYSTEMD_CGROUP=", path); + cgroup = strappend("_SYSTEMD_CGROUP=", path); if (cgroup) IOVEC_SET_STRING(iovec[n++], cgroup); free(path); @@ -224,7 +226,7 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str } if (tv) { - if (asprintf(&source_time, "SOURCE_REALTIME_TIMESTAMP=%llu", + if (asprintf(&source_time, "_SOURCE_REALTIME_TIMESTAMP=%llu", (unsigned long long) timeval_load(tv)) >= 0) IOVEC_SET_STRING(iovec[n++], source_time); } @@ -234,23 +236,23 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str * anyway. However, we need this indexed, too. */ r = sd_id128_get_boot(&id); if (r >= 0) - if (asprintf(&boot_id, "BOOT_ID=%s", sd_id128_to_string(id, idbuf)) >= 0) + if (asprintf(&boot_id, "_BOOT_ID=%s", sd_id128_to_string(id, idbuf)) >= 0) IOVEC_SET_STRING(iovec[n++], boot_id); r = sd_id128_get_machine(&id); if (r >= 0) - if (asprintf(&machine_id, "MACHINE_ID=%s", sd_id128_to_string(id, idbuf)) >= 0) + if (asprintf(&machine_id, "_MACHINE_ID=%s", sd_id128_to_string(id, idbuf)) >= 0) IOVEC_SET_STRING(iovec[n++], machine_id); t = gethostname_malloc(); if (t) { - hostname = strappend("HOSTNAME=", t); + hostname = strappend("_HOSTNAME=", t); if (hostname) IOVEC_SET_STRING(iovec[n++], hostname); free(t); } - f = find_journal(s, loginuid); + f = find_journal(s, realuid == 0 ? 0 : loginuid); if (!f) log_warning("Dropping message, as we can't find a place to store the data."); else { diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 5d518a3870..85c57170d5 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -40,9 +40,34 @@ struct Match { LIST_FIELDS(Match, matches); }; +typedef enum location_type { + LOCATION_HEAD, + LOCATION_TAIL, + LOCATION_DISCRETE +} location_type_t; + +typedef struct Location { + location_type_t type; + + uint64_t seqnum; + sd_id128_t seqnum_id; + bool seqnum_set; + + uint64_t realtime; + bool realtime_set; + + uint64_t monotonic; + sd_id128_t boot_id; + bool monotonic_set; + + uint64_t xor_hash; + bool xor_hash_set; +} Location; + struct sd_journal { Hashmap *files; + Location current_location; JournalFile *current_file; uint64_t current_field; @@ -50,7 +75,7 @@ struct sd_journal { unsigned n_matches; }; -static void reset_location(sd_journal *j) { +static void detach_location(sd_journal *j) { Iterator i; JournalFile *f; @@ -63,8 +88,66 @@ static void reset_location(sd_journal *j) { f->current_offset = 0; } +static void reset_location(sd_journal *j) { + assert(j); + + detach_location(j); + zero(j->current_location); +} + +static void init_location(Location *l, JournalFile *f, Object *o) { + assert(l); + assert(f); + assert(o->object.type == OBJECT_ENTRY); + + l->type = LOCATION_DISCRETE; + l->seqnum = le64toh(o->entry.seqnum); + l->seqnum_id = f->header->seqnum_id; + l->realtime = le64toh(o->entry.realtime); + l->monotonic = le64toh(o->entry.monotonic); + l->boot_id = le64toh(o->entry.boot_id); + l->xor_hash = le64toh(o->entry.xor_hash); + + l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true; +} + +static void set_location(sd_journal *j, JournalFile *f, Object *o, uint64_t offset) { + assert(j); + assert(f); + assert(o); + + init_location(&j->current_location, f, o); + + j->current_file = f; + j->current_field = 0; + + f->current_offset = offset; +} + +static int same_field(const void *_a, size_t s, const void *_b, size_t t) { + const uint8_t *a = _a, *b = _b; + size_t j; + bool a_good = false, b_good = false, different = false; + + for (j = 0; j < s && j < t; j++) { + + if (a[j] == '=') + a_good = true; + if (b[j] == '=') + b_good = true; + if (a[j] != b[j]) + different = true; + + if (a_good && b_good) + return different ? 0 : 1; + } + + return -EINVAL; +} + int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { - Match *m; + Match *m, *after = NULL; + uint64_t le_hash; assert(j); @@ -73,6 +156,23 @@ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { assert(data); + le_hash = htole64(hash64(data, size)); + + LIST_FOREACH(matches, m, j->matches) { + int r; + + if (m->le_hash == le_hash && + m->size == size && + memcmp(m->data, data, size) == 0) + return 0; + + r = same_field(data, size, m->data, m->size); + if (r < 0) + return r; + else if (r > 0) + after = m; + } + m = new0(Match, 1); if (!m) return -ENOMEM; @@ -86,12 +186,14 @@ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { } memcpy(m->data, data, size); - m->le_hash = hash64(m->data, size); + m->le_hash = le_hash; - LIST_PREPEND(Match, matches, j->matches, m); + /* Matches for the same fields we order adjacent to each + * other */ + LIST_INSERT_AFTER(Match, matches, j->matches, after, m); j->n_matches ++; - reset_location(j); + detach_location(j); return 0; } @@ -109,14 +211,19 @@ void sd_journal_flush_matches(sd_journal *j) { j->n_matches = 0; - reset_location(j); + detach_location(j); } -static int compare_order(JournalFile *af, Object *ao, uint64_t ap, - JournalFile *bf, Object *bo, uint64_t bp) { +static int compare_order(JournalFile *af, Object *ao, + JournalFile *bf, Object *bo) { uint64_t a, b; + assert(af); + assert(ao); + assert(bf); + assert(bo); + /* We operate on two different files here, hence we can access * two objects at the same time, which we normally can't. * @@ -179,99 +286,302 @@ static int compare_order(JournalFile *af, Object *ao, uint64_t ap, return 0; } -static int move_to_next_with_matches(sd_journal *j, JournalFile *f, direction_t direction, Object **o, uint64_t *p) { +static int compare_with_location(JournalFile *af, Object *ao, Location *l) { + uint64_t a; + + assert(af); + assert(ao); + assert(l); + assert(l->type == LOCATION_DISCRETE); + + if (l->monotonic_set && + sd_id128_equal(ao->entry.boot_id, l->boot_id) && + l->realtime_set && + le64toh(ao->entry.realtime) == l->realtime && + l->xor_hash_set && + le64toh(ao->entry.xor_hash) == l->xor_hash) + return 0; + + if (l->seqnum_set && + sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) { + + a = le64toh(ao->entry.seqnum); + + if (a < l->seqnum) + return -1; + if (a > l->seqnum) + return 1; + } + + if (l->monotonic_set && + sd_id128_equal(ao->entry.boot_id, l->boot_id)) { + + a = le64toh(ao->entry.monotonic); + + if (a < l->monotonic) + return -1; + if (a > l->monotonic) + return 1; + } + + if (l->realtime_set) { + + a = le64toh(ao->entry.realtime); + + if (a < l->realtime) + return -1; + if (a > l->realtime) + return 1; + } + + if (l->xor_hash_set) { + a = le64toh(ao->entry.xor_hash); + + if (a < l->xor_hash) + return -1; + if (a > l->xor_hash) + return 1; + } + + return 0; +} + +static int find_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) { + Object *o = NULL; + uint64_t p = 0; + int r; + + assert(j); + + if (!j->matches) { + /* No matches is simple */ + + if (j->current_location.type == LOCATION_HEAD) + r = journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p); + else if (j->current_location.type == LOCATION_TAIL) + r = journal_file_next_entry(f, NULL, 0, DIRECTION_UP, &o, &p); + else if (j->current_location.seqnum_set && + sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) + r = journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, &o, &p); + else 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, &o, &p); + else if (j->current_location.realtime_set) + r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p); + else + r = journal_file_next_entry(f, NULL, 0, direction, &o, &p); + + if (r <= 0) + return r; + + } else { + Match *m, *term_match = NULL; + Object *to = NULL; + uint64_t tp = 0; + + /* We have matches, first, let's jump to the monotonic + * position if we have any, since it implies a + * match. */ + + if (j->current_location.type == LOCATION_DISCRETE && + j->current_location.monotonic_set) { + + r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p); + if (r <= 0) + return r; + } + + LIST_FOREACH(matches, m, j->matches) { + Object *c, *d; + uint64_t cp, dp; + + r = journal_file_find_data_object_with_hash(f, m->data, m->size, m->le_hash, &d, &dp); + if (r <= 0) + return r; + + if (j->current_location.type == LOCATION_HEAD) + r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, &c, &cp); + else if (j->current_location.type == LOCATION_TAIL) + r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, &c, &cp); + else if (j->current_location.seqnum_set && + sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) + r = journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, &c, &cp); + else if (j->current_location.realtime_set) + r = journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, &c, &cp); + else + r = journal_file_next_entry_for_data(f, NULL, 0, dp, direction, &c, &cp); + + if (!term_match) { + term_match = m; + + if (r > 0) { + to = c; + tp = cp; + } + } else if (same_field(term_match->data, term_match->size, m->data, m->size)) { + + /* Same field as previous match... */ + if (r > 0) { + + /* Find the earliest of the OR matches */ + + if (!to || + (direction == DIRECTION_DOWN && cp < tp) || + (direction == DIRECTION_UP && cp > tp)) { + to = c; + tp = tp; + } + + } + + } else { + + /* Previous term is finished, did anything match? */ + if (!to) + return 0; + + /* Find the last of the AND matches */ + if (!o || + (direction == DIRECTION_DOWN && tp > p) || + (direction == DIRECTION_UP && tp < p)) { + o = to; + p = tp; + } + + term_match = m; + + if (r > 0) { + to = c; + tp = cp; + } else { + to = NULL; + tp = 0; + } + } + } + + /* Last term is finished, did anything match? */ + if (!to) + return 0; + + if (!o || + (direction == DIRECTION_DOWN && tp > p) || + (direction == DIRECTION_UP && tp < p)) { + o = to; + p = tp; + } + + if (!o) + return 0; + } + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; +} + +static int next_with_matches(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) { int r; uint64_t cp; Object *c; assert(j); assert(f); - assert(o); - assert(p); + assert(ret); + assert(offset); + + c = *ret; + cp = *offset; if (!j->matches) { - /* No matches is easy, just go on to the next entry */ + /* No matches is easy */ - if (f->current_offset > 0) { - r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &c); - if (r < 0) - return r; - } else - c = NULL; + r = journal_file_next_entry(f, c, cp, direction, &c, &cp); + if (r <= 0) + return r; - return journal_file_next_entry(f, c, direction, o, p); + if (ret) + *ret = c; + if (offset) + *offset = cp; + return 1; } /* So there are matches we have to adhere to, let's find the * first entry that matches all of them */ - if (f->current_offset > 0) - cp = f->current_offset; - else { - r = journal_file_find_first_entry(f, j->matches->data, j->matches->size, direction, &c, &cp); - if (r <= 0) - return r; - - /* We can shortcut this if there's only one match */ - if (j->n_matches == 1) { - *o = c; - *p = cp; - return r; - } - } - for (;;) { uint64_t np, n; - bool found; - Match *m; - - r = journal_file_move_to_object(f, cp, OBJECT_ENTRY, &c); - if (r < 0) - return r; + bool found, term_result = false; + Match *m, *term_match = NULL; n = journal_file_entry_n_items(c); /* Make sure we don't match the entry we are starting * from. */ - found = f->current_offset != cp; + found = cp > *offset; np = 0; LIST_FOREACH(matches, m, j->matches) { uint64_t q, k; + /* Let's check if this is the beginning of a + * new term, i.e. has a different field prefix + * as the preceeding match. */ + if (!term_match) { + term_match = m; + term_result = false; + } else if (!same_field(term_match->data, term_match->size, m->data, m->size)) { + if (!term_result) + found = false; + + term_match = m; + term_result = false; + } + for (k = 0; k < n; k++) if (c->entry.items[k].hash == m->le_hash) break; if (k >= n) { - /* Hmm, didn't find any field that matched, so ignore - * this match. Go on with next match */ - - found = false; + /* Hmm, didn't find any field that + * matched this rule, so ignore this + * match. Go on with next match */ continue; } + term_result = true; + /* Hmm, so, this field matched, let's remember * where we'd have to try next, in case the other * matches are not OK */ - if (direction == DIRECTION_DOWN) { - q = le64toh(c->entry.items[k].next_entry_offset); + r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, NULL, &q); + if (r > 0) { - if (q > np) - np = q; - } else { - q = le64toh(c->entry.items[k].prev_entry_offset); - - if (q != 0 && (np == 0 || q < np)) - np = q; + if (direction == DIRECTION_DOWN) { + if (q > np) + np = q; + } else { + if (np == 0 || q < np) + np = q; + } } } + /* Check the last term */ + if (term_match && term_result) + found = true; + /* Did this entry match against all matches? */ if (found) { - *o = c; - *p = cp; + if (ret) + *ret = c; + if (offset) + *offset = cp; return 1; } @@ -285,6 +595,62 @@ static int move_to_next_with_matches(sd_journal *j, JournalFile *f, direction_t } } +static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) { + Object *c; + uint64_t cp; + int compare_value, r; + + assert(j); + assert(f); + + if (f->current_offset > 0) { + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &c); + if (r < 0) + return r; + + cp = f->current_offset; + + r = next_with_matches(j, f, direction, &c, &cp); + if (r <= 0) + return r; + + compare_value = 1; + } else { + r = find_location(j, f, direction, &c, &cp); + if (r <= 0) + return r; + + compare_value = 0; + } + + for (;;) { + bool found; + + if (j->current_location.type == LOCATION_DISCRETE) { + int k; + + k = compare_with_location(f, c, &j->current_location); + if (direction == DIRECTION_DOWN) + found = k >= compare_value; + else + found = k <= -compare_value; + } else + found = true; + + if (found) { + if (ret) + *ret = c; + if (offset) + *offset = cp; + return 1; + } + + r = next_with_matches(j, f, direction, &c, &cp); + if (r <= 0) + return r; + } +} + static int real_journal_next(sd_journal *j, direction_t direction) { JournalFile *f, *new_current = NULL; Iterator i; @@ -297,49 +663,40 @@ static int real_journal_next(sd_journal *j, direction_t direction) { HASHMAP_FOREACH(f, j->files, i) { Object *o; uint64_t p; + bool found; - r = move_to_next_with_matches(j, f, direction, &o, &p); + r = next_beyond_location(j, f, direction, &o, &p); if (r < 0) return r; else if (r == 0) continue; - if (!new_current || - compare_order(new_current, new_entry, new_offset, f, o, p) > 0) { + if (!new_current) + found = true; + else { + int k; + + k = compare_order(f, o, new_current, new_entry); + + if (direction == DIRECTION_DOWN) + found = k < 0; + else + found = k > 0; + } + + if (found) { new_current = f; new_entry = o; new_offset = p; } } - if (new_current) { - j->current_file = new_current; - j->current_file->current_offset = new_offset; - j->current_field = 0; + if (!new_current) + return 0; - /* Skip over any identical entries in the other files too */ + set_location(j, new_current, new_entry, new_offset); - HASHMAP_FOREACH(f, j->files, i) { - Object *o; - uint64_t p; - - if (j->current_file == f) - continue; - - r = move_to_next_with_matches(j, f, direction, &o, &p); - if (r < 0) - return r; - else if (r == 0) - continue; - - if (compare_order(new_current, new_entry, new_offset, f, o, p) == 0) - f->current_offset = p; - } - - return 1; - } - - return 0; + return 1; } int sd_journal_next(sd_journal *j) { @@ -350,6 +707,46 @@ int sd_journal_previous(sd_journal *j) { return real_journal_next(j, DIRECTION_UP); } +int sd_journal_next_skip(sd_journal *j, uint64_t skip) { + int c = 0, r; + + assert(j); + + while (skip > 0) { + r = sd_journal_next(j); + if (r < 0) + return r; + + if (r == 0) + return c; + + skip--; + c++; + } + + return c; +} + +int sd_journal_previous_skip(sd_journal *j, uint64_t skip) { + int c = 0, r; + + assert(j); + + while (skip > 0) { + r = sd_journal_previous(j); + if (r < 0) + return r; + + if (r == 0) + return c; + + skip--; + c++; + } + + return 1; +} + int sd_journal_get_cursor(sd_journal *j, char **cursor) { Object *o; int r; @@ -361,7 +758,7 @@ int sd_journal_get_cursor(sd_journal *j, char **cursor) { if (!j->current_file || j->current_file->current_offset <= 0) return -EADDRNOTAVAIL; - r = journal_file_move_to_object(j->current_file, j->current_file->current_offset, OBJECT_ENTRY, &o); + r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o); if (r < 0) return r; @@ -380,8 +777,150 @@ int sd_journal_get_cursor(sd_journal *j, char **cursor) { return 1; } -int sd_journal_set_cursor(sd_journal *j, const char *cursor) { - return -EINVAL; +int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { + char *w; + size_t l; + char *state; + unsigned long long seqnum, monotonic, realtime, xor_hash; + bool + seqnum_id_set = false, + seqnum_set = false, + boot_id_set = false, + monotonic_set = false, + realtime_set = false, + xor_hash_set = false; + sd_id128_t seqnum_id, boot_id; + + assert(j); + assert(cursor); + + FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) { + char *item; + int k = 0; + + if (l < 2 || w[1] != '=') + return -EINVAL; + + item = strndup(w, l); + if (!item) + return -ENOMEM; + + switch (w[0]) { + + case 's': + seqnum_id_set = true; + k = sd_id128_from_string(w+2, &seqnum_id); + break; + + case 'i': + seqnum_set = true; + if (sscanf(w+2, "%llx", &seqnum) != 1) + k = -EINVAL; + break; + + case 'b': + boot_id_set = true; + k = sd_id128_from_string(w+2, &boot_id); + break; + + case 'm': + monotonic_set = true; + if (sscanf(w+2, "%llx", &monotonic) != 1) + k = -EINVAL; + break; + + case 't': + realtime_set = true; + if (sscanf(w+2, "%llx", &realtime) != 1) + k = -EINVAL; + break; + + case 'x': + xor_hash_set = true; + if (sscanf(w+2, "%llx", &xor_hash) != 1) + k = -EINVAL; + break; + } + + free(item); + + if (k < 0) + return k; + } + + if ((!seqnum_set || !seqnum_id_set) && + (!monotonic_set || !boot_id_set) && + !realtime_set) + return -EINVAL; + + reset_location(j); + + j->current_location.type = LOCATION_DISCRETE; + + if (realtime_set) { + j->current_location.realtime = (uint64_t) realtime; + j->current_location.realtime_set = true; + } + + if (seqnum_set && seqnum_id_set) { + j->current_location.seqnum = (uint64_t) seqnum; + j->current_location.seqnum_id = seqnum_id; + j->current_location.seqnum_set = true; + } + + if (monotonic_set && boot_id_set) { + j->current_location.monotonic = (uint64_t) monotonic; + j->current_location.boot_id = boot_id; + j->current_location.monotonic_set = true; + } + + if (xor_hash_set) { + j->current_location.xor_hash = (uint64_t) xor_hash; + j->current_location.xor_hash_set = true; + } + + return 0; +} + +int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) { + assert(j); + + reset_location(j); + j->current_location.type = LOCATION_DISCRETE; + j->current_location.boot_id = boot_id; + j->current_location.monotonic = usec; + j->current_location.monotonic_set = true; + + return 0; +} + +int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) { + assert(j); + + reset_location(j); + j->current_location.type = LOCATION_DISCRETE; + j->current_location.realtime = usec; + j->current_location.realtime_set = true; + + return 0; +} + +int sd_journal_seek_head(sd_journal *j) { + assert(j); + + reset_location(j); + j->current_location.type = LOCATION_HEAD; + + return 0; +} + +int sd_journal_seek_tail(sd_journal *j) { + assert(j); + + reset_location(j); + j->current_location.type = LOCATION_TAIL; + + return 0; } static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) { @@ -411,6 +950,9 @@ static int add_file(sd_journal *j, const char *prefix, const char *dir, const ch return r; } + journal_file_dump(f); + + r = hashmap_put(j->files, f->path, f); if (r < 0) { journal_file_close(f); @@ -557,20 +1099,20 @@ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) { f = j->current_file; if (!f) - return 0; + return -EADDRNOTAVAIL; if (f->current_offset <= 0) - return 0; + return -EADDRNOTAVAIL; - r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o); + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); if (r < 0) return r; *ret = le64toh(o->entry.realtime); - return 1; + return 0; } -int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret) { +int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) { Object *o; JournalFile *f; int r; @@ -581,25 +1123,28 @@ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret) { f = j->current_file; if (!f) - return 0; + return -EADDRNOTAVAIL; if (f->current_offset <= 0) - return 0; + return -EADDRNOTAVAIL; - r = sd_id128_get_boot(&id); + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); if (r < 0) return r; - r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o); - if (r < 0) - return r; + if (ret_boot_id) + *ret_boot_id = o->entry.boot_id; + else { + r = sd_id128_get_boot(&id); + if (r < 0) + return r; - if (!sd_id128_equal(id, o->entry.boot_id)) - return 0; + if (!sd_id128_equal(id, o->entry.boot_id)) + return -ENOENT; + } *ret = le64toh(o->entry.monotonic); - return 1; - + return 0; } int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) { @@ -619,12 +1164,12 @@ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, siz f = j->current_file; if (!f) - return 0; + return -EADDRNOTAVAIL; if (f->current_offset <= 0) - return 0; + return -EADDRNOTAVAIL; - r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o); + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); if (r < 0) return r; @@ -632,16 +1177,16 @@ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, siz n = journal_file_entry_n_items(o); for (i = 0; i < n; i++) { - uint64_t p, l, h; + uint64_t p, l, le_hash; size_t t; p = le64toh(o->entry.items[i].object_offset); - h = o->entry.items[j->current_field].hash; - r = journal_file_move_to_object(f, p, OBJECT_DATA, &o); + le_hash = o->entry.items[j->current_field].hash; + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); if (r < 0) return r; - if (h != o->data.hash) + if (le_hash != o->data.hash) return -EBADMSG; l = le64toh(o->object.size) - offsetof(Object, data.payload); @@ -658,23 +1203,23 @@ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, siz *data = o->data.payload; *size = t; - return 1; + return 0; } - r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o); + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); if (r < 0) return r; } - return 0; + return -ENOENT; } int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) { JournalFile *f; - uint64_t p, l, n, h; - size_t t; + uint64_t p, l, n, le_hash; int r; Object *o; + size_t t; assert(j); assert(data); @@ -682,12 +1227,12 @@ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) { f = j->current_file; if (!f) - return 0; + return -EADDRNOTAVAIL; if (f->current_offset <= 0) - return 0; + return -EADDRNOTAVAIL; - r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o); + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); if (r < 0) return r; @@ -696,12 +1241,12 @@ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) { return 0; p = le64toh(o->entry.items[j->current_field].object_offset); - h = o->entry.items[j->current_field].hash; - r = journal_file_move_to_object(f, p, OBJECT_DATA, &o); + le_hash = o->entry.items[j->current_field].hash; + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); if (r < 0) return r; - if (h != o->data.hash) + if (le_hash != o->data.hash) return -EBADMSG; l = le64toh(o->object.size) - offsetof(Object, data.payload); @@ -719,24 +1264,8 @@ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) { return 1; } -void sd_journal_start_data(sd_journal *j) { +void sd_journal_restart_data(sd_journal *j) { assert(j); j->current_field = 0; } - -int sd_journal_seek_head(sd_journal *j) { - assert(j); - - reset_location(j); - - return real_journal_next(j, DIRECTION_DOWN); -} - -int sd_journal_seek_tail(sd_journal *j) { - assert(j); - - reset_location(j); - - return real_journal_next(j, DIRECTION_UP); -} diff --git a/src/journal/sd-journal.h b/src/journal/sd-journal.h index 9978ca9ac0..8d7e314223 100644 --- a/src/journal/sd-journal.h +++ b/src/journal/sd-journal.h @@ -25,18 +25,22 @@ #include #include +#include "sd-id128.h" + /* TODO: * * - check LE/BE conversion for 8bit, 16bit, 32bit values - * - implement parallel traversal * - implement inotify usage on client * - implement audit gateway * - implement native gateway * - implement stdout gateway - * - extend hash table/bisect table as we go + * - extend hash tables table as we go * - accelerate looking for "all hostnames" and suchlike. * - throttling * - enforce limit on open journal files in journald and journalctl + * - cryptographic hash + * - fix space reservation logic + * - comm, argv can be manipulated, should it be _COMM=, _CMDLINE= or COMM=, CMDLINE=? */ typedef struct sd_journal sd_journal; @@ -47,59 +51,52 @@ void sd_journal_close(sd_journal *j); int sd_journal_previous(sd_journal *j); int sd_journal_next(sd_journal *j); +int sd_journal_previous_skip(sd_journal *j, uint64_t skip); +int sd_journal_next_skip(sd_journal *j, uint64_t skip); + int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret); -int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret); +int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id); int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *l); int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *l); -void sd_journal_start_data(sd_journal *j); +void sd_journal_restart_data(sd_journal *j); int sd_journal_add_match(sd_journal *j, const void *data, size_t size); void sd_journal_flush_matches(sd_journal *j); int sd_journal_seek_head(sd_journal *j); int sd_journal_seek_tail(sd_journal *j); - -int sd_journal_seek_monotonic_usec(sd_journal *j, uint64_t usec); /* missing */ -int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec); /* missing */ +int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec); +int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec); +int sd_journal_seek_cursor(sd_journal *j, const char *cursor); int sd_journal_get_cursor(sd_journal *j, char **cursor); -int sd_journal_set_cursor(sd_journal *j, const char *cursor); /* missing */ -int sd_journal_unique_seek(sd_journal *j, const char *field); /* missing */ -int sd_journal_unique_enumerate(sd_journal *j, const void **data, size_t *l); /* missing */ - -int sd_journal_get_fd(sd_journal *j); /* missing */ +int sd_journal_query_unique(sd_journal *j, const char *field); /* missing */ +int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l); /* missing */ +void sd_journal_restart_unique(sd_journal *j); /* missing */ enum { SD_JOURNAL_NOP, SD_JOURNAL_APPEND, - SD_JOURNAL_DROP + SD_JOURNAL_INVALIDATE_ADD, + SD_JOURNAL_INVALIDATE_REMOVE }; +int sd_journal_get_fd(sd_journal *j); /* missing */ int sd_journal_process(sd_journal *j); /* missing */ -#define SD_JOURNAL_FOREACH_BEGIN(j) \ - if (sd_journal_seek_head(j) > 0) do { +#define SD_JOURNAL_FOREACH(j) \ + if (sd_journal_seek_head(j) >= 0) \ + while (sd_journal_next(j) > 0) \ -#define SD_JOURNAL_FOREACH_END(j) \ - } while (sd_journal_next(j) > 0) - -#define SD_JOURNAL_FOREACH_CONTINUE(j) \ - do { - -#define SD_JOURNAL_FOREACH_BACKWARDS_BEGIN(j) \ - if (sd_journal_seek_tail(j) > 0) do { - -#define SD_JOURNAL_FOREACH_BACKWARDS_END(j) \ - } while (sd_journal_previous(j) > 0) - -#define SD_JOURNAL_FOREACH_BACKWARDS_CONTINUE(j) \ - do { +#define SD_JOURNAL_FOREACH_BACKWARDS(j) \ + if (sd_journal_seek_tail(j) >= 0) \ + while (sd_journal_previous(j) > 0) \ #define SD_JOURNAL_FOREACH_DATA(j, data, l) \ - for (sd_journal_start_data(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; ) + for (sd_journal_restart_data(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; ) -#define SD_JOURNAL_FOREACH_UNIQUE(j, data, l) \ - while (sd_journal_enumerate_unique_data((j), &(data), &(l)) > 0) +#define SD_JOURNAL_FOREACH_UNIQUE(j, data, l) \ + for (sd_journal_restart_unique(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; ) #endif diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index 93e2b4dab4..45ced12b46 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -31,6 +31,7 @@ int main(int argc, char *argv[]) { struct iovec iovec; static const char test[] = "test", test2[] = "test2"; Object *o; + uint64_t p; log_set_max_level(LOG_DEBUG); @@ -54,41 +55,55 @@ int main(int argc, char *argv[]) { journal_file_dump(f); - assert(journal_file_next_entry(f, NULL, DIRECTION_DOWN, &o, NULL) == 1); + assert(journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p) == 1); assert(le64toh(o->entry.seqnum) == 1); - assert(journal_file_next_entry(f, o, DIRECTION_DOWN, &o, NULL) == 1); + assert(journal_file_next_entry(f, o, p, DIRECTION_DOWN, &o, &p) == 1); assert(le64toh(o->entry.seqnum) == 2); - assert(journal_file_next_entry(f, o, DIRECTION_DOWN, &o, NULL) == 1); + assert(journal_file_next_entry(f, o, p, DIRECTION_DOWN, &o, &p) == 1); assert(le64toh(o->entry.seqnum) == 3); - assert(journal_file_next_entry(f, o, DIRECTION_DOWN, &o, NULL) == 0); + assert(journal_file_next_entry(f, o, p, DIRECTION_DOWN, &o, &p) == 0); - assert(journal_file_find_first_entry(f, test, strlen(test), DIRECTION_DOWN, &o, NULL) == 1); + assert(journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p) == 1); assert(le64toh(o->entry.seqnum) == 1); - assert(journal_file_find_first_entry(f, test, strlen(test), DIRECTION_UP, &o, NULL) == 1); + assert(journal_file_skip_entry(f, o, p, 2, &o, &p) == 1); assert(le64toh(o->entry.seqnum) == 3); - assert(journal_file_find_first_entry(f, test2, strlen(test2), DIRECTION_UP, &o, NULL) == 1); - assert(le64toh(o->entry.seqnum) == 2); - - assert(journal_file_find_first_entry(f, test2, strlen(test2), DIRECTION_DOWN, &o, NULL) == 1); - assert(le64toh(o->entry.seqnum) == 2); - - assert(journal_file_find_first_entry(f, "quux", 4, DIRECTION_DOWN, &o, NULL) == 0); - - assert(journal_file_move_to_entry(f, 1, &o, NULL) == 1); + assert(journal_file_skip_entry(f, o, p, -2, &o, &p) == 1); assert(le64toh(o->entry.seqnum) == 1); - assert(journal_file_move_to_entry(f, 3, &o, NULL) == 1); + assert(journal_file_skip_entry(f, o, p, -2, &o, &p) == 1); + assert(le64toh(o->entry.seqnum) == 1); + + assert(journal_file_find_data_object(f, test, strlen(test), NULL, &p) == 1); + assert(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 1); + + assert(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1); assert(le64toh(o->entry.seqnum) == 3); - assert(journal_file_move_to_entry(f, 2, &o, NULL) == 1); + assert(journal_file_find_data_object(f, test2, strlen(test2), NULL, &p) == 1); + assert(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1); assert(le64toh(o->entry.seqnum) == 2); - assert(journal_file_move_to_entry(f, 10, &o, NULL) == 0); + assert(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 2); + + assert(journal_file_find_data_object(f, "quux", 4, NULL, &p) == 0); + + assert(journal_file_move_to_entry_by_seqnum(f, 1, DIRECTION_DOWN, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 1); + + assert(journal_file_move_to_entry_by_seqnum(f, 3, DIRECTION_DOWN, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 3); + + assert(journal_file_move_to_entry_by_seqnum(f, 2, DIRECTION_DOWN, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 2); + + assert(journal_file_move_to_entry_by_seqnum(f, 10, DIRECTION_DOWN, &o, NULL) == 0); journal_file_rotate(&f); journal_file_rotate(&f); diff --git a/src/journal/wjournal.c b/src/journal/wjournal.c deleted file mode 100644 index 3122aa054e..0000000000 --- a/src/journal/wjournal.c +++ /dev/null @@ -1,57 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with systemd; If not, see . -***/ - -#include "wjournal.h" -#include "journal-def.h" - -struct WJournal { - int fd; - - Header *header; - HashItem *hash_table; - uint64_t *bisect_table; -}; - -int wjournal_open(const char *fn, WJournal **ret) { - assert(fn); - assert(ret); -} - -void wjournal_close(WJournal *j) { - assert(j); - - if (j->fd >= 0) - close_nointr_nofail(j->fd); - - if (j->header) { - munmap(j->header, PAGE_ALIGN(sizeof(Header))); - - } - - free(j); -} - -int wjournal_write_object_begin(WJournal *j, uint64_t type, uint64_t size, Object **ret); -int wjournal_write_object_finish(WJournal *j, Object *ret); - -int wjournal_write_field(WJournal *j, const char *buffer, uint64_t size, Object **ret); -int wjournal_write_entry(WJournal *j, const Field *fields, unsigned n_fields, Object **ret); -int wjournal_write_eof(WJournal *j); diff --git a/src/journal/wjournal.h b/src/journal/wjournal.h deleted file mode 100644 index b0250d0fe2..0000000000 --- a/src/journal/wjournal.h +++ /dev/null @@ -1,39 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foojournalhfoo -#define foojournalhfoo - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with systemd; If not, see . -***/ - -#include - -typedef struct WJournal WJournal; - -int wjournal_open(const char *fn, WJournal **ret); -void wjournal_close(WJournal *j); - -int wjournal_write_object_begin(WJournal *j, uint64_t type, uint64_t size, Object **ret); -int wjournal_write_object_finish(WJournal *j, Object *ret); - -int wjournal_write_field(WJournal *j, const char *buffer, uint64_t size, Object **ret); -int wjournal_write_entry(WJournal *j, const Field *fields, unsigned n_fields, Object **ret); -int wjournal_write_eof(WJournal *j); - -#endif