From eb86030ec0e53ef3834d1b230440e88fdf020e9d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jan 2016 18:59:29 +0100 Subject: sd-journal: add an API to enumerate known field names of the journal This adds two new calls to get the list of all journal fields names currently in use. This is the low-level support to implement the feature requested in #2176 in a more optimized way. --- src/journal/journal-internal.h | 21 ++++-- src/journal/sd-journal.c | 156 +++++++++++++++++++++++++++++++++++++++++ src/libsystemd/libsystemd.sym | 6 ++ src/systemd/sd-journal.h | 13 +++- 4 files changed, 188 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h index fa5ca11636..a55d1bcc47 100644 --- a/src/journal/journal-internal.h +++ b/src/journal/journal-internal.h @@ -103,18 +103,27 @@ struct sd_journal { unsigned current_invalidate_counter, last_invalidate_counter; usec_t last_process_usec; + /* Iterating through unique fields and their data values */ char *unique_field; JournalFile *unique_file; uint64_t unique_offset; + /* Iterating through known fields */ + JournalFile *fields_file; + uint64_t fields_offset; + uint64_t fields_hash_table_index; + char *fields_buffer; + size_t fields_buffer_allocated; + int flags; - bool on_network; - bool no_new_files; - bool unique_file_lost; /* File we were iterating over got - removed, and there were no more - files, so sd_j_enumerate_unique - will return a value equal to 0. */ + bool on_network:1; + bool no_new_files:1; + bool unique_file_lost:1; /* File we were iterating over got + removed, and there were no more + files, so sd_j_enumerate_unique + will return a value equal to 0. */ + bool fields_file_lost:1; bool has_runtime_files:1; bool has_persistent_files:1; diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 74a5e262f8..85d9bbe04f 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -1338,6 +1338,13 @@ static void remove_file_real(sd_journal *j, JournalFile *f) { j->unique_file_lost = true; } + if (j->fields_file == f) { + j->fields_file = ordered_hashmap_next(j->files, j->fields_file->path); + j->fields_offset = 0; + if (!j->fields_file) + j->fields_file_lost = true; + } + journal_file_close(f); j->current_invalidate_counter ++; @@ -1806,6 +1813,7 @@ _public_ void sd_journal_close(sd_journal *j) { free(j->path); free(j->prefix); free(j->unique_field); + free(j->fields_buffer); free(j); } @@ -2552,6 +2560,154 @@ _public_ void sd_journal_restart_unique(sd_journal *j) { j->unique_file_lost = false; } +_public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) { + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(field, -EINVAL); + + if (!j->fields_file) { + if (j->fields_file_lost) + return 0; + + j->fields_file = ordered_hashmap_first(j->files); + if (!j->fields_file) + return 0; + + j->fields_hash_table_index = 0; + j->fields_offset = 0; + } + + for (;;) { + JournalFile *f, *of; + Iterator i; + uint64_t m; + Object *o; + size_t sz; + bool found; + + f = j->fields_file; + + if (j->fields_offset == 0) { + bool eof = false; + + /* We are not yet positioned at any field. Let's pick the first one */ + r = journal_file_map_field_hash_table(f); + if (r < 0) + return r; + + m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem); + for (;;) { + if (j->fields_hash_table_index >= m) { + /* Reached the end of the hash table, go to the next file. */ + eof = true; + break; + } + + j->fields_offset = le64toh(f->field_hash_table[j->fields_hash_table_index].head_hash_offset); + + if (j->fields_offset != 0) + break; + + /* Empty hash table bucket, go to next one */ + j->fields_hash_table_index++; + } + + if (eof) { + /* Proceed with next file */ + j->fields_file = ordered_hashmap_next(j->files, f->path); + if (!j->fields_file) { + *field = NULL; + return 0; + } + + j->fields_offset = 0; + j->fields_hash_table_index = 0; + continue; + } + + } else { + /* We are already positioned at a field. If so, let's figure out the next field from it */ + + r = journal_file_move_to_object(f, OBJECT_FIELD, j->fields_offset, &o); + if (r < 0) + return r; + + j->fields_offset = le64toh(o->field.next_hash_offset); + if (j->fields_offset == 0) { + /* Reached the end of the hash table chain */ + j->fields_hash_table_index++; + continue; + } + } + + /* We use OBJECT_UNUSED here, so that the iteator below doesn't remove our mmap window */ + r = journal_file_move_to_object(f, OBJECT_UNUSED, j->fields_offset, &o); + if (r < 0) + return r; + + /* Because we used OBJECT_UNUSED above, we need to do our type check manually */ + if (o->object.type != OBJECT_FIELD) { + log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD); + return -EBADMSG; + } + + sz = le64toh(o->object.size) - offsetof(Object, field.payload); + + /* Let's see if we already returned this field name before. */ + found = false; + ORDERED_HASHMAP_FOREACH(of, j->files, i) { + if (of == f) + break; + + /* Skip this file it didn't have any fields indexed */ + if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0) + continue; + + r = journal_file_find_field_object_with_hash(of, o->field.payload, sz, le64toh(o->field.hash), NULL, NULL); + if (r < 0) + return r; + if (r > 0) { + found = true; + break; + } + } + + if (found) + continue; + + /* Check if this is really a valid string containing no NUL byte */ + if (memchr(o->field.payload, 0, sz)) + return -EBADMSG; + + if (sz > j->data_threshold) + sz = j->data_threshold; + + if (!GREEDY_REALLOC(j->fields_buffer, j->fields_buffer_allocated, sz + 1)) + return -ENOMEM; + + memcpy(j->fields_buffer, o->field.payload, sz); + j->fields_buffer[sz] = 0; + + if (!field_is_valid(j->fields_buffer)) + return -EBADMSG; + + *field = j->fields_buffer; + return 1; + } +} + +_public_ void sd_journal_restart_fields(sd_journal *j) { + if (!j) + return; + + j->fields_file = NULL; + j->fields_hash_table_index = 0; + j->fields_offset = 0; + j->fields_file_lost = false; +} + _public_ int sd_journal_reliable_fd(sd_journal *j) { assert_return(j, -EINVAL); assert_return(!journal_pid_changed(j), -ECHILD); diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 043ff13e6f..d6928bfbb7 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -481,3 +481,9 @@ global: sd_bus_path_encode_many; sd_listen_fds_with_names; } LIBSYSTEMD_226; + +LIBSYSTEMD_229 { +global: + sd_journal_enumerate_fields; + sd_journal_restart_fields; +} LIBSYSTEMD_227; diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h index 7f16c69ce5..caf322f062 100644 --- a/src/systemd/sd-journal.h +++ b/src/systemd/sd-journal.h @@ -129,6 +129,9 @@ int sd_journal_query_unique(sd_journal *j, const char *field); int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l); void sd_journal_restart_unique(sd_journal *j); +int sd_journal_enumerate_fields(sd_journal *j, const char **field); +void sd_journal_restart_fields(sd_journal *j); + int sd_journal_get_fd(sd_journal *j); int sd_journal_get_events(sd_journal *j); int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec); @@ -142,22 +145,28 @@ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **text); int sd_journal_has_runtime_files(sd_journal *j); int sd_journal_has_persistent_files(sd_journal *j); -/* the inverse condition avoids ambiguity of danling 'else' after the macro */ +/* The inverse condition avoids ambiguity of dangling 'else' after the macro */ #define SD_JOURNAL_FOREACH(j) \ if (sd_journal_seek_head(j) < 0) { } \ else while (sd_journal_next(j) > 0) -/* the inverse condition avoids ambiguity of danling 'else' after the macro */ +/* The inverse condition avoids ambiguity of dangling 'else' after the macro */ #define SD_JOURNAL_FOREACH_BACKWARDS(j) \ if (sd_journal_seek_tail(j) < 0) { } \ else while (sd_journal_previous(j) > 0) +/* Iterate through the data fields of the current journal entry */ #define SD_JOURNAL_FOREACH_DATA(j, data, l) \ for (sd_journal_restart_data(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; ) +/* Iterate through the all known values of a specific field */ #define SD_JOURNAL_FOREACH_UNIQUE(j, data, l) \ for (sd_journal_restart_unique(j); sd_journal_enumerate_unique((j), &(data), &(l)) > 0; ) +/* Iterate through all known field names */ +#define SD_JOURNAL_FOREACH_FIELD(j, field) \ + for (sd_journal_restart_fields(j); sd_journal_enumerate_fields((j), &(field)) > 0; ) + _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_journal, sd_journal_close); _SD_END_DECLARATIONS; -- cgit v1.2.3-54-g00ecf