X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fjournal%2Fsd-journal.c;h=71b056c234fa827ec3e8d41f60dd22d433523e42;hp=1c91f007b79bff96f56ed47d891190e5fd751197;hb=6573ef05a3cbe15949acfbbf1ad03726068907bd;hpb=bf807d4dbf27c783db8dfd7f4eca321ae4be5b00 diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 1c91f007b..71b056c23 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -41,6 +41,7 @@ #include "missing.h" #include "catalog.h" #include "replace-var.h" +#include "fileio.h" #define JOURNAL_FILES_MAX 1024 @@ -50,6 +51,8 @@ #define DEFAULT_DATA_THRESHOLD (64*1024) +static void remove_file_real(sd_journal *j, JournalFile *f); + static bool journal_pid_changed(sd_journal *j) { assert(j); @@ -67,7 +70,7 @@ static int set_put_error(sd_journal *j, int r) { if (r >= 0) return r; - k = set_ensure_allocated(&j->errors, trivial_hash_func, trivial_compare_func); + k = set_ensure_allocated(&j->errors, NULL); if (k < 0) return k; @@ -83,8 +86,8 @@ static void detach_location(sd_journal *j) { j->current_file = NULL; j->current_field = 0; - HASHMAP_FOREACH(f, j->files, i) - f->current_offset = 0; + ORDERED_HASHMAP_FOREACH(f, j->files, i) + journal_file_reset_location(f); } static void reset_location(sd_journal *j) { @@ -125,6 +128,10 @@ static void set_location(sd_journal *j, LocationType type, JournalFile *f, Objec f->last_direction = direction; f->current_offset = offset; + + /* Let f know its candidate entry was picked. */ + assert(f->location_type == LOCATION_SEEK); + f->location_type = LOCATION_DISCRETE; } static int match_is_valid(const void *data, size_t size) { @@ -216,19 +223,14 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) Match *l3, *l4, *add_here = NULL, *m; le64_t le_hash; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - - if (!data) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(data, -EINVAL); if (size == 0) size = strlen(data); - if (!match_is_valid(data, size)) - return -EINVAL; + assert_return(match_is_valid(data, size), -EINVAL); /* level 0: AND term * level 1: OR terms @@ -314,10 +316,8 @@ fail: } _public_ int sd_journal_add_conjunction(sd_journal *j) { - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); if (!j->level0) return 0; @@ -335,10 +335,8 @@ _public_ int sd_journal_add_conjunction(sd_journal *j) { } _public_ int sd_journal_add_disjunction(sd_journal *j) { - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); if (!j->level0) return 0; @@ -472,7 +470,7 @@ static int compare_entry_order(JournalFile *af, Object *_ao, if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) { - /* If the boot id matches compare monotonic time */ + /* If the boot id matches, compare monotonic time */ a = le64toh(ao->entry.monotonic); b = le64toh(bo->entry.monotonic); @@ -482,7 +480,7 @@ static int compare_entry_order(JournalFile *af, Object *_ao, return 1; } - /* Otherwise compare UTC time */ + /* Otherwise, compare UTC time */ a = le64toh(ao->entry.realtime); b = le64toh(bo->entry.realtime); @@ -855,14 +853,14 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc int k; k = compare_with_location(f, c, &j->current_location); - if (direction == DIRECTION_DOWN) - found = k > 0; - else - found = k < 0; + + found = direction == DIRECTION_DOWN ? k > 0 : k < 0; } else found = true; if (found) { + journal_file_save_location(f, direction, c, cp); + if (ret) *ret = c; if (offset) @@ -879,25 +877,26 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc static int real_journal_next(sd_journal *j, direction_t direction) { JournalFile *f, *new_file = NULL; uint64_t new_offset = 0; - Object *o; - uint64_t p; + uint64_t p = 0; Iterator i; + Object *o; int r; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); - HASHMAP_FOREACH(f, j->files, i) { + ORDERED_HASHMAP_FOREACH(f, j->files, i) { bool found; r = next_beyond_location(j, f, direction, &o, &p); if (r < 0) { - log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r)); + log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path); + remove_file_real(j, f); continue; - } else if (r == 0) + } else if (r == 0) { + f->location_type = LOCATION_TAIL; continue; + } if (!new_file) found = true; @@ -938,10 +937,8 @@ _public_ int sd_journal_previous(sd_journal *j) { static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) { int c = 0, r; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); if (skip == 0) { /* If this is not a discrete skip, then at least @@ -980,12 +977,9 @@ _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) { int r; char bid[33], sid[33]; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (!cursor) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(cursor, -EINVAL); if (!j->current_file || j->current_file->current_offset <= 0) return -EADDRNOTAVAIL; @@ -1009,7 +1003,7 @@ _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) { } _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { - char *w, *state; + const char *word, *state; size_t l; unsigned long long seqnum, monotonic, realtime, xor_hash; bool @@ -1021,25 +1015,22 @@ _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { xor_hash_set = false; sd_id128_t seqnum_id, boot_id; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (isempty(cursor)) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(!isempty(cursor), -EINVAL); - FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) { + FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) { char *item; int k = 0; - if (l < 2 || w[1] != '=') + if (l < 2 || word[1] != '=') return -EINVAL; - item = strndup(w, l); + item = strndup(word, l); if (!item) return -ENOMEM; - switch (w[0]) { + switch (word[0]) { case 's': seqnum_id_set = true; @@ -1118,16 +1109,13 @@ _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) { int r; - char *w, *state; + const char *word, *state; size_t l; Object *o; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (isempty(cursor)) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(!isempty(cursor), -EINVAL); if (!j->current_file || j->current_file->current_offset <= 0) return -EADDRNOTAVAIL; @@ -1136,20 +1124,20 @@ _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) { if (r < 0) return r; - FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) { + FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) { _cleanup_free_ char *item = NULL; sd_id128_t id; unsigned long long ll; int k = 0; - if (l < 2 || w[1] != '=') + if (l < 2 || word[1] != '=') return -EINVAL; - item = strndup(w, l); + item = strndup(word, l); if (!item) return -ENOMEM; - switch (w[0]) { + switch (word[0]) { case 's': k = sd_id128_from_string(item+2, &id); @@ -1202,10 +1190,8 @@ _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) { _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) { - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); reset_location(j); j->current_location.type = LOCATION_SEEK; @@ -1217,10 +1203,8 @@ _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, u } _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) { - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); reset_location(j); j->current_location.type = LOCATION_SEEK; @@ -1231,10 +1215,8 @@ _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) { } _public_ int sd_journal_seek_head(sd_journal *j) { - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); reset_location(j); j->current_location.type = LOCATION_HEAD; @@ -1243,10 +1225,8 @@ _public_ int sd_journal_seek_head(sd_journal *j) { } _public_ int sd_journal_seek_tail(sd_journal *j) { - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); reset_location(j); j->current_location.type = LOCATION_TAIL; @@ -1299,7 +1279,7 @@ static bool file_type_wanted(int flags, const char *filename) { if (flags & SD_JOURNAL_CURRENT_USER) { char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1]; - assert_se(snprintf(prefix, sizeof(prefix), "user-%lu", (unsigned long) getuid()) + assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid()) < (int) sizeof(prefix)); if (file_has_type_prefix(prefix, filename)) @@ -1310,16 +1290,16 @@ static bool file_type_wanted(int flags, const char *filename) { } static int add_any_file(sd_journal *j, const char *path) { - JournalFile *f; + JournalFile *f = NULL; int r; assert(j); assert(path); - if (hashmap_get(j->files, path)) + if (ordered_hashmap_get(j->files, path)) return 0; - if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) { + if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) { log_warning("Too many open journal files, not adding %s.", path); return set_put_error(j, -ETOOMANYREFS); } @@ -1330,7 +1310,7 @@ static int add_any_file(sd_journal *j, const char *path) { /* journal_file_dump(f); */ - r = hashmap_put(j->files, f->path, f); + r = ordered_hashmap_put(j->files, f->path, f); if (r < 0) { journal_file_close(f); return r; @@ -1368,7 +1348,7 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) { } static int remove_file(sd_journal *j, const char *prefix, const char *filename) { - char *path; + _cleanup_free_ char *path; JournalFile *f; assert(j); @@ -1379,12 +1359,19 @@ static int remove_file(sd_journal *j, const char *prefix, const char *filename) if (!path) return -ENOMEM; - f = hashmap_get(j->files, path); - free(path); + f = ordered_hashmap_get(j->files, path); if (!f) return 0; - hashmap_remove(j->files, f->path); + remove_file_real(j, f); + return 0; +} + +static void remove_file_real(sd_journal *j, JournalFile *f) { + assert(j); + assert(f); + + ordered_hashmap_remove(j->files, f->path); log_debug("File %s removed.", f->path); @@ -1394,15 +1381,16 @@ static int remove_file(sd_journal *j, const char *prefix, const char *filename) } if (j->unique_file == f) { - j->unique_file = NULL; + /* Jump to the next unique_file or NULL if that one was last */ + j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path); j->unique_offset = 0; + if (!j->unique_file) + j->unique_file_lost = true; } journal_file_close(f); j->current_invalidate_counter ++; - - return 0; } static int add_directory(sd_journal *j, const char *prefix, const char *dirname) { @@ -1430,7 +1418,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) d = opendir(path); if (!d) { - log_debug("Failed to open %s: %m", path); + log_debug_errno(errno, "Failed to open %s: %m", path); if (errno == ENOENT) return 0; return -errno; @@ -1471,18 +1459,23 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) for (;;) { struct dirent *de; - union dirent_storage buf; - r = readdir_r(d, &buf.de, &de); - if (r != 0 || !de) + errno = 0; + de = readdir(d); + if (!de && errno != 0) { + r = -errno; + log_debug_errno(errno, "Failed to read directory %s: %m", m->path); + return r; + } + if (!de) break; if (dirent_is_file_with_suffix(de, ".journal") || dirent_is_file_with_suffix(de, ".journal~")) { r = add_file(j, m->path, de->d_name); if (r < 0) { - log_debug("Failed to add file %s/%s: %s", - m->path, de->d_name, strerror(-r)); + log_debug_errno(r, "Failed to add file %s/%s: %m", + m->path, de->d_name); r = set_put_error(j, r); if (r < 0) return r; @@ -1507,6 +1500,9 @@ static int add_root_directory(sd_journal *j, const char *p) { !path_startswith(p, "/run")) return -EINVAL; + if (j->prefix) + p = strappenda(j->prefix, p); + d = opendir(p); if (!d) return -errno; @@ -1552,19 +1548,24 @@ static int add_root_directory(sd_journal *j, const char *p) { for (;;) { struct dirent *de; - union dirent_storage buf; sd_id128_t id; - r = readdir_r(d, &buf.de, &de); - if (r != 0 || !de) + errno = 0; + de = readdir(d); + if (!de && errno != 0) { + r = -errno; + log_debug_errno(errno, "Failed to read directory %s: %m", m->path); + return r; + } + if (!de) break; if (dirent_is_file_with_suffix(de, ".journal") || dirent_is_file_with_suffix(de, ".journal~")) { r = add_file(j, m->path, de->d_name); if (r < 0) { - log_debug("Failed to add file %s/%s: %s", - m->path, de->d_name, strerror(-r)); + log_debug_errno(r, "Failed to add file %s/%s: %m", + m->path, de->d_name); r = set_put_error(j, r); if (r < 0) return r; @@ -1574,7 +1575,7 @@ static int add_root_directory(sd_journal *j, const char *p) { r = add_directory(j, m->path, de->d_name); if (r < 0) - log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r)); + log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name); } } @@ -1641,9 +1642,9 @@ static int add_current_paths(sd_journal *j) { * "root" directories. We don't expect errors here, so we * treat them as fatal. */ - HASHMAP_FOREACH(f, j->files, i) { - int r; + ORDERED_HASHMAP_FOREACH(f, j->files, i) { _cleanup_free_ char *dir; + int r; dir = dirname_malloc(f->path); if (!dir) @@ -1670,7 +1671,7 @@ static int allocate_inotify(sd_journal *j) { } if (!j->directories_by_wd) { - j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func); + j->directories_by_wd = hashmap_new(NULL); if (!j->directories_by_wd) return -ENOMEM; } @@ -1696,8 +1697,8 @@ static sd_journal *journal_new(int flags, const char *path) { goto fail; } - j->files = hashmap_new(string_hash_func, string_compare_func); - j->directories_by_path = hashmap_new(string_hash_func, string_compare_func); + j->files = ordered_hashmap_new(&string_hash_ops); + j->directories_by_path = hashmap_new(&string_hash_ops); j->mmap = mmap_cache_new(); if (!j->files || !j->directories_by_path || !j->mmap) goto fail; @@ -1713,14 +1714,8 @@ _public_ int sd_journal_open(sd_journal **ret, int flags) { sd_journal *j; int r; - if (!ret) - return -EINVAL; - - if (flags & ~(SD_JOURNAL_LOCAL_ONLY| - SD_JOURNAL_RUNTIME_ONLY| - SD_JOURNAL_SYSTEM| - SD_JOURNAL_CURRENT_USER)) - return -EINVAL; + assert_return(ret, -EINVAL); + assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL); j = journal_new(flags, NULL); if (!j) @@ -1739,18 +1734,55 @@ fail: return r; } -_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) { +_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) { + _cleanup_free_ char *root = NULL, *class = NULL; sd_journal *j; + char *p; int r; - if (!ret) - return -EINVAL; + assert_return(machine, -EINVAL); + assert_return(ret, -EINVAL); + assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL); + assert_return(machine_name_is_valid(machine), -EINVAL); - if (!path) - return -EINVAL; + p = strappenda("/run/systemd/machines/", machine); + r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL); + if (r == -ENOENT) + return -EHOSTDOWN; + if (r < 0) + return r; + if (!root) + return -ENODATA; - if (flags != 0) - return -EINVAL; + if (!streq_ptr(class, "container")) + return -EIO; + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + j->prefix = root; + root = NULL; + + r = add_search_paths(j); + if (r < 0) + goto fail; + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) { + sd_journal *j; + int r; + + assert_return(ret, -EINVAL); + assert_return(path, -EINVAL); + assert_return(flags == 0, -EINVAL); j = journal_new(flags, path); if (!j) @@ -1776,11 +1808,8 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla const char **path; int r; - if (!ret) - return -EINVAL; - - if (flags != 0) - return -EINVAL; + assert_return(ret, -EINVAL); + assert_return(flags == 0, -EINVAL); j = journal_new(flags, NULL); if (!j) @@ -1789,7 +1818,7 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla STRV_FOREACH(path, paths) { r = add_any_file(j, *path); if (r < 0) { - log_error("Failed to open %s: %s", *path, strerror(-r)); + log_error_errno(r, "Failed to open %s: %m", *path); goto fail; } } @@ -1814,10 +1843,10 @@ _public_ void sd_journal_close(sd_journal *j) { sd_journal_flush_matches(j); - while ((f = hashmap_steal_first(j->files))) + while ((f = ordered_hashmap_steal_first(j->files))) journal_file_close(f); - hashmap_free(j->files); + ordered_hashmap_free(j->files); while ((d = hashmap_first(j->directories_by_path))) remove_directory(j, d); @@ -1828,8 +1857,7 @@ _public_ void sd_journal_close(sd_journal *j) { hashmap_free(j->directories_by_path); hashmap_free(j->directories_by_wd); - if (j->inotify_fd >= 0) - close_nointr_nofail(j->inotify_fd); + safe_close(j->inotify_fd); if (j->mmap) { log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap)); @@ -1837,6 +1865,7 @@ _public_ void sd_journal_close(sd_journal *j) { } free(j->path); + free(j->prefix); free(j->unique_field); set_free(j->errors); free(j); @@ -1847,12 +1876,9 @@ _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) { JournalFile *f; int r; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (!ret) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(ret, -EINVAL); f = j->current_file; if (!f) @@ -1875,10 +1901,8 @@ _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id12 int r; sd_id128_t id; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); f = j->current_file; if (!f) @@ -1943,19 +1967,12 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void ** int r; Object *o; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (!field) - return -EINVAL; - if (!data) - return -EINVAL; - if (!size) - return -EINVAL; - - if (!field_is_valid(field)) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(field, -EINVAL); + assert_return(data, -EINVAL); + assert_return(size, -EINVAL); + assert_return(field_is_valid(field), -EINVAL); f = j->current_file; if (!f) @@ -1975,6 +1992,7 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void ** uint64_t p, l; le64_t le_hash; size_t t; + int compression; p = le64toh(o->entry.items[i].object_offset); le_hash = o->entry.items[i].hash; @@ -1987,19 +2005,22 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void ** l = le64toh(o->object.size) - offsetof(Object, data.payload); - if (o->object.flags & OBJECT_COMPRESSED) { - -#ifdef HAVE_XZ - if (uncompress_startswith(o->data.payload, l, + compression = o->object.flags & OBJECT_COMPRESSION_MASK; + if (compression) { +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + if (decompress_startswith(compression, + o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, field, field_length, '=')) { - uint64_t rsize; + size_t rsize; - if (!uncompress_blob(o->data.payload, l, - &f->compress_buffer, &f->compress_buffer_size, &rsize, - j->data_threshold)) - return -EBADMSG; + r = decompress_blob(compression, + o->data.payload, l, + &f->compress_buffer, &f->compress_buffer_size, &rsize, + j->data_threshold); + if (r < 0) + return r; *data = f->compress_buffer; *size = (size_t) rsize; @@ -2009,7 +2030,6 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void ** #else return -EPROTONOSUPPORT; #endif - } else if (l >= field_length+1 && memcmp(o->data.payload, field, field_length) == 0 && o->data.payload[field_length] == '=') { @@ -2036,6 +2056,7 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void ** static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) { size_t t; uint64_t l; + int compression; l = le64toh(o->object.size) - offsetof(Object, data.payload); t = (size_t) l; @@ -2044,12 +2065,17 @@ static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **da if ((uint64_t) t != l) return -E2BIG; - if (o->object.flags & OBJECT_COMPRESSED) { -#ifdef HAVE_XZ - uint64_t rsize; + compression = o->object.flags & OBJECT_COMPRESSION_MASK; + if (compression) { +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + size_t rsize; + int r; - if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, j->data_threshold)) - return -EBADMSG; + r = decompress_blob(compression, + o->data.payload, l, &f->compress_buffer, + &f->compress_buffer_size, &rsize, j->data_threshold); + if (r < 0) + return r; *data = f->compress_buffer; *size = (size_t) rsize; @@ -2071,14 +2097,10 @@ _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t int r; Object *o; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (!data) - return -EINVAL; - if (!size) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(data, -EINVAL); + assert_return(size, -EINVAL); f = j->current_file; if (!f) @@ -2123,10 +2145,8 @@ _public_ void sd_journal_restart_data(sd_journal *j) { _public_ int sd_journal_get_fd(sd_journal *j) { int r; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); if (j->inotify_fd >= 0) return j->inotify_fd; @@ -2152,10 +2172,8 @@ _public_ int sd_journal_get_fd(sd_journal *j) { _public_ int sd_journal_get_events(sd_journal *j) { int fd; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); fd = sd_journal_get_fd(j); if (fd < 0) @@ -2167,12 +2185,9 @@ _public_ int sd_journal_get_events(sd_journal *j) { _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) { int fd; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (!timeout_usec) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(timeout_usec, -EINVAL); fd = sd_journal_get_fd(j); if (fd < 0) @@ -2211,8 +2226,8 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { r = add_file(j, d->path, e->name); if (r < 0) { - log_debug("Failed to add file %s/%s: %s", - d->path, e->name, strerror(-r)); + log_debug_errno(r, "Failed to add file %s/%s: %m", + d->path, e->name); set_put_error(j, r); } @@ -2220,7 +2235,7 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { r = remove_file(j, d->path, e->name); if (r < 0) - log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r)); + log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name); } } else if (!d->is_root && e->len == 0) { @@ -2230,7 +2245,7 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) { r = remove_directory(j, d); if (r < 0) - log_debug("Failed to remove directory %s: %s", d->path, strerror(-r)); + log_debug_errno(r, "Failed to remove directory %s: %m", d->path); } @@ -2241,7 +2256,7 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { r = add_directory(j, d->path, e->name); if (r < 0) - log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r)); + log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name); } } @@ -2266,17 +2281,15 @@ static int determine_change(sd_journal *j) { } _public_ int sd_journal_process(sd_journal *j) { - uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event); bool got_something = false; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); j->last_process_usec = now(CLOCK_MONOTONIC); for (;;) { + uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event); struct inotify_event *e; ssize_t l; @@ -2290,31 +2303,17 @@ _public_ int sd_journal_process(sd_journal *j) { got_something = true; - e = (struct inotify_event*) buffer; - while (l > 0) { - size_t step; - + FOREACH_INOTIFY_EVENT(e, buffer, l) process_inotify_event(j, e); - - step = sizeof(struct inotify_event) + e->len; - assert(step <= (size_t) l); - - e = (struct inotify_event*) ((uint8_t*) e + step); - l -= step; - } } - - return determine_change(j); } _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) { int r; uint64_t t; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); if (j->inotify_fd < 0) { @@ -2359,18 +2358,15 @@ _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, Iterator i; JournalFile *f; bool first = true; + uint64_t fmin = 0, tmax = 0; int r; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (!from && !to) - return -EINVAL; - if (from == to) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(from || to, -EINVAL); + assert_return(from != to, -EINVAL); - HASHMAP_FOREACH(f, j->files, i) { + ORDERED_HASHMAP_FOREACH(f, j->files, i) { usec_t fr, t; r = journal_file_get_cutoff_realtime_usec(f, &fr, &t); @@ -2382,38 +2378,35 @@ _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, continue; if (first) { - if (from) - *from = fr; - if (to) - *to = t; + fmin = fr; + tmax = t; first = false; } else { - if (from) - *from = MIN(fr, *from); - if (to) - *to = MAX(t, *to); + fmin = MIN(fr, fmin); + tmax = MAX(t, tmax); } } + if (from) + *from = fmin; + if (to) + *to = tmax; + return first ? 0 : 1; } _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) { Iterator i; JournalFile *f; - bool first = true; + bool found = false; int r; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (!from && !to) - return -EINVAL; - if (from == to) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(from || to, -EINVAL); + assert_return(from != to, -EINVAL); - HASHMAP_FOREACH(f, j->files, i) { + ORDERED_HASHMAP_FOREACH(f, j->files, i) { usec_t fr, t; r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t); @@ -2424,21 +2417,21 @@ _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot if (r == 0) continue; - if (first) { + if (found) { if (from) - *from = fr; + *from = MIN(fr, *from); if (to) - *to = t; - first = false; + *to = MAX(t, *to); } else { if (from) - *from = MIN(fr, *from); + *from = fr; if (to) - *to = MAX(t, *to); + *to = t; + found = true; } } - return first ? 0 : 1; + return found; } void journal_print_header(sd_journal *j) { @@ -2448,7 +2441,7 @@ void journal_print_header(sd_journal *j) { assert(j); - HASHMAP_FOREACH(f, j->files, i) { + ORDERED_HASHMAP_FOREACH(f, j->files, i) { if (newline) putchar('\n'); else @@ -2463,14 +2456,11 @@ _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) { JournalFile *f; uint64_t sum = 0; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (!bytes) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(bytes, -EINVAL); - HASHMAP_FOREACH(f, j->files, i) { + ORDERED_HASHMAP_FOREACH(f, j->files, i) { struct stat st; if (fstat(f->fd, &st) < 0) @@ -2486,14 +2476,10 @@ _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) { _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { char *f; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (isempty(field)) - return -EINVAL; - if (!field_is_valid(field)) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(!isempty(field), -EINVAL); + assert_return(field_is_valid(field), -EINVAL); f = strdup(field); if (!f) @@ -2503,41 +2489,41 @@ _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { j->unique_field = f; j->unique_file = NULL; j->unique_offset = 0; + j->unique_file_lost = false; return 0; } _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { - Object *o; size_t k; - int r; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (!data) - return -EINVAL; - if (!l) - return -EINVAL; - if (!j->unique_field) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(data, -EINVAL); + assert_return(l, -EINVAL); + assert_return(j->unique_field, -EINVAL); k = strlen(j->unique_field); if (!j->unique_file) { - j->unique_file = hashmap_first(j->files); + if (j->unique_file_lost) + return 0; + + j->unique_file = ordered_hashmap_first(j->files); if (!j->unique_file) return 0; + j->unique_offset = 0; } for (;;) { JournalFile *of; Iterator i; + Object *o; const void *odata; size_t ol; bool found; + int r; /* Proceed to next data object in the field's linked list */ if (j->unique_offset == 0) { @@ -2556,36 +2542,52 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_ /* We reached the end of the list? Then start again, with the next file */ if (j->unique_offset == 0) { - JournalFile *n; - - n = hashmap_next(j->files, j->unique_file->path); - if (!n) + j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path); + if (!j->unique_file) return 0; - j->unique_file = n; continue; } - /* We do not use the type context here, but 0 instead, - * so that we can look at this data object at the same + /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED + * instead, so that we can look at this data object at the same * time as one on another file */ - r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o); + r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o); if (r < 0) return r; /* Let's do the type check by hand, since we used 0 context above. */ - if (o->object.type != OBJECT_DATA) + if (o->object.type != OBJECT_DATA) { + log_debug("%s:offset " OFSfmt ": object has type %d, expected %d", + j->unique_file->path, j->unique_offset, + o->object.type, OBJECT_DATA); return -EBADMSG; + } r = return_data(j, j->unique_file, o, &odata, &ol); if (r < 0) return r; + /* Check if we have at least the field name and "=". */ + if (ol <= k) { + log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu", + j->unique_file->path, j->unique_offset, + ol, k + 1); + return -EBADMSG; + } + + if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') { + log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"", + j->unique_file->path, j->unique_offset, + j->unique_field); + return -EBADMSG; + } + /* OK, now let's see if we already returned this data * object by checking if it exists in the earlier * traversed files. */ found = false; - HASHMAP_FOREACH(of, j->files, i) { + ORDERED_HASHMAP_FOREACH(of, j->files, i) { Object *oo; uint64_t op; @@ -2623,13 +2625,12 @@ _public_ void sd_journal_restart_unique(sd_journal *j) { j->unique_file = NULL; j->unique_offset = 0; + j->unique_file_lost = false; } _public_ int sd_journal_reliable_fd(sd_journal *j) { - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); return !j->on_network; } @@ -2661,12 +2662,9 @@ _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) { char *t; int r; - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (!ret) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(ret, -EINVAL); r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size); if (r < 0) @@ -2693,29 +2691,23 @@ _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) { } _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) { - if (!ret) - return -EINVAL; + assert_return(ret, -EINVAL); return catalog_get(CATALOG_DATABASE, id, ret); } _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) { - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); j->data_threshold = sz; return 0; } _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) { - if (!j) - return -EINVAL; - if (journal_pid_changed(j)) - return -ECHILD; - if (!sz) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(sz, -EINVAL); *sz = j->data_threshold; return 0;