diff options
Diffstat (limited to 'src/libsystemd/sd-journal/sd-journal.c')
-rw-r--r-- | src/libsystemd/sd-journal/sd-journal.c | 566 |
1 files changed, 351 insertions, 215 deletions
diff --git a/src/libsystemd/sd-journal/sd-journal.c b/src/libsystemd/sd-journal/sd-journal.c index ca1ef0c..0aa3726 100644 --- a/src/libsystemd/sd-journal/sd-journal.c +++ b/src/libsystemd/sd-journal/sd-journal.c @@ -38,12 +38,13 @@ #include "prioq.h" #include "process-util.h" #include "replace-var.h" +#include "sort-util.h" #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "syslog-util.h" -#include "uid-alloc-range.h" +#include "uid-classification.h" #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC) @@ -232,7 +233,11 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) assert_return(!journal_origin_changed(j), -ECHILD); assert_return(data, -EINVAL); - if (size == 0) + /* If the size is unspecified, assume it's a string. Note: 0 is the public value we document for + * this, for historical reasons. Internally, we pretty widely started using SIZE_MAX for this in + * similar cases however, hence accept that too. And internally we actually prefer it, to make things + * less surprising. */ + if (IN_SET(size, 0, SIZE_MAX)) size = strlen(data); if (!match_is_valid(data, size)) @@ -324,6 +329,37 @@ fail: return -ENOMEM; } +int journal_add_match_pair(sd_journal *j, const char *field, const char *value) { + _cleanup_free_ char *s = NULL; + + assert(j); + assert(field); + assert(value); + + s = strjoin(field, "=", value); + if (!s) + return -ENOMEM; + + return sd_journal_add_match(j, s, SIZE_MAX); +} + +int journal_add_matchf(sd_journal *j, const char *format, ...) { + _cleanup_free_ char *s = NULL; + va_list ap; + int r; + + assert(j); + assert(format); + + va_start(ap, format); + r = vasprintf(&s, format, ap); + va_end(ap); + if (r < 0) + return -ENOMEM; + + return sd_journal_add_match(j, s, SIZE_MAX); +} + _public_ int sd_journal_add_conjunction(sd_journal *j) { assert_return(j, -EINVAL); assert_return(!journal_origin_changed(j), -ECHILD); @@ -413,6 +449,99 @@ _public_ void sd_journal_flush_matches(sd_journal *j) { detach_location(j); } +static int newest_by_boot_id_compare(const NewestByBootId *a, const NewestByBootId *b) { + return id128_compare_func(&a->boot_id, &b->boot_id); +} + +static void journal_file_unlink_newest_by_boot_id(sd_journal *j, JournalFile *f) { + NewestByBootId *found; + + assert(j); + assert(f); + + if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) /* not linked currently, hence this is a NOP */ + return; + + found = typesafe_bsearch(&(NewestByBootId) { .boot_id = f->newest_boot_id }, + j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare); + assert(found); + + assert_se(prioq_remove(found->prioq, f, &f->newest_boot_id_prioq_idx) > 0); + f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL; + + /* The prioq may be empty, but that should not cause any issue. Let's keep it. */ +} + +static void journal_clear_newest_by_boot_id(sd_journal *j) { + FOREACH_ARRAY(i, j->newest_by_boot_id, j->n_newest_by_boot_id) { + JournalFile *f; + + while ((f = prioq_peek(i->prioq))) + journal_file_unlink_newest_by_boot_id(j, f); + + prioq_free(i->prioq); + } + + j->newest_by_boot_id = mfree(j->newest_by_boot_id); + j->n_newest_by_boot_id = 0; +} + +static int journal_file_newest_monotonic_compare(const void *a, const void *b) { + const JournalFile *x = a, *y = b; + + return -CMP(x->newest_monotonic_usec, y->newest_monotonic_usec); /* Invert order, we want newest first! */ +} + +static int journal_file_reshuffle_newest_by_boot_id(sd_journal *j, JournalFile *f) { + NewestByBootId *found; + int r; + + assert(j); + assert(f); + + found = typesafe_bsearch(&(NewestByBootId) { .boot_id = f->newest_boot_id }, + j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare); + if (found) { + /* There's already a priority queue for this boot ID */ + + if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) { + r = prioq_put(found->prioq, f, &f->newest_boot_id_prioq_idx); /* Insert if we aren't in there yet */ + if (r < 0) + return r; + } else + prioq_reshuffle(found->prioq, f, &f->newest_boot_id_prioq_idx); /* Reshuffle otherwise */ + + } else { + _cleanup_(prioq_freep) Prioq *q = NULL; + + /* No priority queue yet, then allocate one */ + + assert(f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL); /* we can't be a member either */ + + q = prioq_new(journal_file_newest_monotonic_compare); + if (!q) + return -ENOMEM; + + r = prioq_put(q, f, &f->newest_boot_id_prioq_idx); + if (r < 0) + return r; + + if (!GREEDY_REALLOC(j->newest_by_boot_id, j->n_newest_by_boot_id + 1)) { + f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL; + return -ENOMEM; + } + + j->newest_by_boot_id[j->n_newest_by_boot_id++] = (NewestByBootId) { + .boot_id = f->newest_boot_id, + .prioq = TAKE_PTR(q), + }; + + typesafe_qsort(j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare); + } + + return 0; +} + static int journal_file_find_newest_for_boot_id( sd_journal *j, sd_id128_t id, @@ -427,16 +556,17 @@ static int journal_file_find_newest_for_boot_id( /* Before we use it, let's refresh the timestamp from the header, and reshuffle our prioq * accordingly. We do this only a bunch of times, to not be caught in some update loop. */ for (unsigned n_tries = 0;; n_tries++) { + NewestByBootId *found; JournalFile *f; - Prioq *q; - q = hashmap_get(j->newest_by_boot_id, &id); - if (!q) + found = typesafe_bsearch(&(NewestByBootId) { .boot_id = id }, + j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare); + + f = found ? prioq_peek(found->prioq) : NULL; + if (!f) return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "Requested delta for boot ID %s, but we have no information about that boot ID.", SD_ID128_TO_STRING(id)); - assert_se(f = prioq_peek(q)); /* we delete hashmap entries once the prioq is empty, so this must hold */ - if (f == prev || n_tries >= 5) { /* This was already the best answer in the previous run, or we tried too often, use it */ *ret = f; @@ -449,6 +579,11 @@ static int journal_file_find_newest_for_boot_id( r = journal_file_read_tail_timestamp(j, f); if (r < 0) return log_debug_errno(r, "Failed to read tail timestamp while trying to find newest journal file for boot ID %s.", SD_ID128_TO_STRING(id)); + if (r == 0) { + /* No new entry found. */ + *ret = f; + return 0; + } /* Refreshing the timestamp we read might have reshuffled the prioq, hence let's check the * prioq again and only use the information once we reached an equilibrium or hit a limit */ @@ -932,8 +1067,8 @@ static int real_journal_next(sd_journal *j, direction_t direction) { if (r < 0) return r; - for (unsigned i = 0; i < n_files; i++) { - JournalFile *f = (JournalFile *)files[i]; + FOREACH_ARRAY(_f, files, n_files) { + JournalFile *f = (JournalFile*) *_f; bool found; r = next_beyond_location(j, f, direction); @@ -1373,17 +1508,6 @@ static void track_file_disposition(sd_journal *j, JournalFile *f) { j->has_persistent_files = true; } -static const char *skip_slash(const char *p) { - - if (!p) - return NULL; - - while (*p == '/') - p++; - - return p; -} - static int add_any_file( sd_journal *j, int fd, @@ -1403,7 +1527,7 @@ static int add_any_file( /* If there's a top-level fd defined make the path relative, explicitly, since otherwise * openat() ignores the first argument. */ - fd = our_fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC|O_NONBLOCK); + fd = our_fd = openat(j->toplevel_fd, skip_leading_slash(path), O_RDONLY|O_CLOEXEC|O_NONBLOCK); else fd = our_fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); if (fd < 0) { @@ -1494,6 +1618,41 @@ error: return r; } +int journal_get_directories(sd_journal *j, char ***ret) { + _cleanup_strv_free_ char **paths = NULL; + JournalFile *f; + const char *p; + size_t n = SIZE_MAX; + int r; + + assert(j); + assert(ret); + + /* This returns parent directories of opened journal files. */ + + ORDERED_HASHMAP_FOREACH_KEY(f, p, j->files) { + _cleanup_free_ char *d = NULL; + + /* Ignore paths generated from fd. */ + if (path_startswith(p, "/proc/")) + continue; + + r = path_extract_directory(p, &d); + if (r < 0) + return r; + + if (path_strv_contains(paths, d)) + continue; + + r = strv_extend_with_size(&paths, &n, d); + if (r < 0) + return r; + } + + *ret = TAKE_PTR(paths); + return 0; +} + static int add_file_by_name( sd_journal *j, const char *prefix, @@ -1676,7 +1835,7 @@ static int directory_open(sd_journal *j, const char *path, DIR **ret) { else /* Open the specified directory relative to the toplevel fd. Enforce that the path specified is * relative, by dropping the initial slash */ - d = xopendirat(j->toplevel_fd, skip_slash(path), 0); + d = xopendirat(j->toplevel_fd, skip_leading_slash(path), 0); if (!d) return -errno; @@ -1684,6 +1843,100 @@ static int directory_open(sd_journal *j, const char *path, DIR **ret) { return 0; } +static Directory* directory_free(Directory *d) { + if (!d) + return NULL; + + if (d->journal) { + if (d->wd > 0 && + hashmap_remove_value(d->journal->directories_by_wd, INT_TO_PTR(d->wd), d) && + d->journal->inotify_fd >= 0) + (void) inotify_rm_watch(d->journal->inotify_fd, d->wd); + + if (d->path) + hashmap_remove_value(d->journal->directories_by_path, d->path, d); + } + + if (d->path) { + if (d->is_root) + log_debug("Root directory %s removed.", d->path); + else + log_debug("Directory %s removed.", d->path); + + free(d->path); + } + + return mfree(d); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Directory*, directory_free); + +DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( + directories_by_path_hash_ops, + char, + path_hash_func, + path_compare, + Directory, + directory_free); + +DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( + directories_by_wd_hash_ops, + void, + trivial_hash_func, + trivial_compare_func, + Directory, + directory_free); + +static int add_directory_impl(sd_journal *j, const char *path, bool is_root, Directory **ret) { + _cleanup_(directory_freep) Directory *m = NULL; + Directory *existing; + int r; + + assert(j); + assert(path); + assert(ret); + + existing = hashmap_get(j->directories_by_path, path); + if (existing) { + if (existing->is_root != is_root) { + /* Don't 'downgrade' from root directory */ + *ret = NULL; + return 0; + } + + *ret = existing; + return 1; + } + + m = new(Directory, 1); + if (!m) + return -ENOMEM; + + *m = (Directory) { + .journal = j, + .is_root = is_root, + .path = strdup(path), + .wd = -1, + }; + + if (!m->path) + return -ENOMEM; + + r = hashmap_ensure_put(&j->directories_by_path, &directories_by_path_hash_ops, m->path, m); + if (r < 0) + return r; + + j->current_invalidate_counter++; + + if (is_root) + log_debug("Root directory %s added.", m->path); + else + log_debug("Directory %s added.", m->path); + + *ret = TAKE_PTR(m); + return 1; +} + static int add_directory(sd_journal *j, const char *prefix, const char *dirname); static void directory_enumerate(sd_journal *j, Directory *m, DIR *d) { @@ -1724,12 +1977,14 @@ static void directory_watch(sd_journal *j, Directory *m, int fd, uint32_t mask) return; } - r = hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m); - if (r == -EEXIST) - log_debug_errno(r, "Directory '%s' already being watched under a different path, ignoring: %m", m->path); + r = hashmap_ensure_put(&j->directories_by_wd, &directories_by_wd_hash_ops, INT_TO_PTR(m->wd), m); if (r < 0) { - log_debug_errno(r, "Failed to add watch for journal directory '%s' to hashmap, ignoring: %m", m->path); - (void) inotify_rm_watch(j->inotify_fd, m->wd); + if (r == -EEXIST) + log_debug_errno(r, "Directory '%s' already being watched under a different path, ignoring: %m", m->path); + else { + log_debug_errno(r, "Failed to add watch for journal directory '%s' to hashmap, ignoring: %m", m->path); + (void) inotify_rm_watch(j->inotify_fd, m->wd); + } m->wd = -1; } } @@ -1775,32 +2030,11 @@ static int add_directory( goto fail; } - m = hashmap_get(j->directories_by_path, path); - if (!m) { - m = new(Directory, 1); - if (!m) { - r = -ENOMEM; - goto fail; - } - - *m = (Directory) { - .is_root = false, - .path = path, - }; - - if (hashmap_put(j->directories_by_path, m->path, m) < 0) { - free(m); - r = -ENOMEM; - goto fail; - } - - path = NULL; /* avoid freeing in cleanup */ - j->current_invalidate_counter++; - - log_debug("Directory %s added.", m->path); - - } else if (m->is_root) - return 0; /* Don't 'downgrade' from root directory */ + r = add_directory_impl(j, path, /* is_root = */ false, &m); + if (r < 0) + goto fail; + if (r == 0) + return 0; m->last_seen_generation = j->generation; @@ -1878,35 +2112,10 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { rewinddir(d); } - m = hashmap_get(j->directories_by_path, p); - if (!m) { - m = new0(Directory, 1); - if (!m) { - r = -ENOMEM; - goto fail; - } - - m->is_root = true; - - m->path = strdup(p); - if (!m->path) { - free(m); - r = -ENOMEM; - goto fail; - } - - if (hashmap_put(j->directories_by_path, m->path, m) < 0) { - free(m->path); - free(m); - r = -ENOMEM; - goto fail; - } - - j->current_invalidate_counter++; - - log_debug("Root directory %s added.", m->path); - - } else if (!m->is_root) + r = add_directory_impl(j, p, /* is_root = */ true, &m); + if (r < 0) + goto fail; + if (r == 0) return 0; directory_watch(j, m, dirfd(d), @@ -1928,27 +2137,6 @@ fail: return r; } -static void remove_directory(sd_journal *j, Directory *d) { - assert(j); - - if (d->wd > 0) { - hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd)); - - if (j->inotify_fd >= 0) - (void) inotify_rm_watch(j->inotify_fd, d->wd); - } - - hashmap_remove(j->directories_by_path, d->path); - - if (d->is_root) - log_debug("Root directory %s removed.", d->path); - else - log_debug("Directory %s removed.", d->path); - - free(d->path); - free(d); -} - static int add_search_paths(sd_journal *j) { static const char search_paths[] = @@ -2003,7 +2191,7 @@ static int allocate_inotify(sd_journal *j) { return -errno; } - return hashmap_ensure_allocated(&j->directories_by_wd, NULL); + return 0; } static sd_journal *journal_new(int flags, const char *path, const char *namespace) { @@ -2045,9 +2233,8 @@ static sd_journal *journal_new(int flags, const char *path, const char *namespac return NULL; j->files_cache = ordered_hashmap_iterated_cache_new(j->files); - j->directories_by_path = hashmap_new(&path_hash_ops); j->mmap = mmap_cache_new(); - if (!j->files_cache || !j->directories_by_path || !j->mmap) + if (!j->files_cache || !j->mmap) return NULL; return TAKE_PTR(j); @@ -2059,7 +2246,8 @@ static sd_journal *journal_new(int flags, const char *path, const char *namespac SD_JOURNAL_SYSTEM | \ SD_JOURNAL_CURRENT_USER | \ SD_JOURNAL_ALL_NAMESPACES | \ - SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE) + SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE | \ + SD_JOURNAL_ASSUME_IMMUTABLE) _public_ int sd_journal_open_namespace(sd_journal **ret, const char *namespace, int flags) { _cleanup_(sd_journal_closep) sd_journal *j = NULL; @@ -2085,7 +2273,9 @@ _public_ int sd_journal_open(sd_journal **ret, int flags) { } #define OPEN_CONTAINER_ALLOWED_FLAGS \ - (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM) + (SD_JOURNAL_LOCAL_ONLY | \ + SD_JOURNAL_SYSTEM | \ + SD_JOURNAL_ASSUME_IMMUTABLE) _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) { _cleanup_free_ char *root = NULL, *class = NULL; @@ -2129,7 +2319,9 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in #define OPEN_DIRECTORY_ALLOWED_FLAGS \ (SD_JOURNAL_OS_ROOT | \ - SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER ) + SD_JOURNAL_SYSTEM | \ + SD_JOURNAL_CURRENT_USER | \ + SD_JOURNAL_ASSUME_IMMUTABLE) _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) { _cleanup_(sd_journal_closep) sd_journal *j = NULL; @@ -2154,12 +2346,15 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f return 0; } +#define OPEN_FILES_ALLOWED_FLAGS \ + (SD_JOURNAL_ASSUME_IMMUTABLE) + _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) { _cleanup_(sd_journal_closep) sd_journal *j = NULL; int r; assert_return(ret, -EINVAL); - assert_return(flags == 0, -EINVAL); + assert_return((flags & ~OPEN_FILES_ALLOWED_FLAGS) == 0, -EINVAL); j = journal_new(flags, NULL, NULL); if (!j) @@ -2181,7 +2376,8 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla (SD_JOURNAL_OS_ROOT | \ SD_JOURNAL_SYSTEM | \ SD_JOURNAL_CURRENT_USER | \ - SD_JOURNAL_TAKE_DIRECTORY_FD) + SD_JOURNAL_TAKE_DIRECTORY_FD | \ + SD_JOURNAL_ASSUME_IMMUTABLE) _public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) { _cleanup_(sd_journal_closep) sd_journal *j = NULL; @@ -2219,6 +2415,9 @@ _public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) { return 0; } +#define OPEN_FILES_FD_ALLOWED_FLAGS \ + (SD_JOURNAL_ASSUME_IMMUTABLE) + _public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) { JournalFile *f; _cleanup_(sd_journal_closep) sd_journal *j = NULL; @@ -2226,7 +2425,7 @@ _public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fd assert_return(ret, -EINVAL); assert_return(n_fds > 0, -EBADF); - assert_return(flags == 0, -EINVAL); + assert_return((flags & ~OPEN_FILES_FD_ALLOWED_FLAGS) == 0, -EINVAL); j = journal_new(flags, NULL, NULL); if (!j) @@ -2270,27 +2469,16 @@ fail: } _public_ void sd_journal_close(sd_journal *j) { - Directory *d; - Prioq *p; - if (!j || journal_origin_changed(j)) return; - while ((p = hashmap_first(j->newest_by_boot_id))) - journal_file_unlink_newest_by_boot_id(j, prioq_peek(p)); - hashmap_free(j->newest_by_boot_id); + journal_clear_newest_by_boot_id(j); sd_journal_flush_matches(j); ordered_hashmap_free_with_destructor(j->files, journal_file_close); iterated_cache_free(j->files_cache); - while ((d = hashmap_first(j->directories_by_path))) - remove_directory(j, d); - - while ((d = hashmap_first(j->directories_by_wd))) - remove_directory(j, d); - hashmap_free(j->directories_by_path); hashmap_free(j->directories_by_wd); @@ -2306,6 +2494,8 @@ _public_ void sd_journal_close(sd_journal *j) { hashmap_free_free(j->errors); + set_free(j->exclude_syslog_identifiers); + free(j->path); free(j->prefix); free(j->namespace); @@ -2314,84 +2504,6 @@ _public_ void sd_journal_close(sd_journal *j) { free(j); } -static void journal_file_unlink_newest_by_boot_id(sd_journal *j, JournalFile *f) { - JournalFile *nf; - Prioq *p; - - assert(j); - assert(f); - - if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) /* not linked currently, hence this is a NOP */ - return; - - assert_se(p = hashmap_get(j->newest_by_boot_id, &f->newest_boot_id)); - assert_se(prioq_remove(p, f, &f->newest_boot_id_prioq_idx) > 0); - - nf = prioq_peek(p); - if (nf) - /* There's still a member in the prioq? Then make sure the hashmap key now points to its - * .newest_boot_id field (and not ours!). Not we only replace the memory of the key here, the - * value of the key (and the data associated with it) remain the same. */ - assert_se(hashmap_replace(j->newest_by_boot_id, &nf->newest_boot_id, p) >= 0); - else { - assert_se(hashmap_remove(j->newest_by_boot_id, &f->newest_boot_id) == p); - prioq_free(p); - } - - f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL; -} - -static int journal_file_newest_monotonic_compare(const void *a, const void *b) { - const JournalFile *x = a, *y = b; - - return -CMP(x->newest_monotonic_usec, y->newest_monotonic_usec); /* Invert order, we want newest first! */ -} - -static int journal_file_reshuffle_newest_by_boot_id(sd_journal *j, JournalFile *f) { - Prioq *p; - int r; - - assert(j); - assert(f); - - p = hashmap_get(j->newest_by_boot_id, &f->newest_boot_id); - if (p) { - /* There's already a priority queue for this boot ID */ - - if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) { - r = prioq_put(p, f, &f->newest_boot_id_prioq_idx); /* Insert if we aren't in there yet */ - if (r < 0) - return r; - } else - prioq_reshuffle(p, f, &f->newest_boot_id_prioq_idx); /* Reshuffle otherwise */ - - } else { - _cleanup_(prioq_freep) Prioq *q = NULL; - - /* No priority queue yet, then allocate one */ - - assert(f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL); /* we can't be a member either */ - - q = prioq_new(journal_file_newest_monotonic_compare); - if (!q) - return -ENOMEM; - - r = prioq_put(q, f, &f->newest_boot_id_prioq_idx); - if (r < 0) - return r; - - r = hashmap_ensure_put(&j->newest_by_boot_id, &id128_hash_ops, &f->newest_boot_id, q); - if (r < 0) { - f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL; - return r; - } - - TAKE_PTR(q); - } - - return 0; -} - static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) { uint64_t offset, mo, rt; sd_id128_t id; @@ -2405,11 +2517,13 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) { /* Tries to read the timestamp of the most recently written entry. */ - r = journal_file_fstat(f); - if (r < 0) - return r; - if (f->newest_mtime == timespec_load(&f->last_stat.st_mtim)) - return 0; /* mtime didn't change since last time, don't bother */ + if (FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE) && f->newest_entry_offset != 0) + return 0; /* We have already read the file, and we assume that the file is immutable. */ + + if (f->header->state == f->newest_state && + f->header->state == STATE_ARCHIVED && + f->newest_entry_offset != 0) + return 0; /* We have already read archived file. */ if (JOURNAL_HEADER_CONTAINS(f->header, tail_entry_offset)) { offset = le64toh(READ_NOW(f->header->tail_entry_offset)); @@ -2420,6 +2534,8 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) { } if (offset == 0) return -ENODATA; /* not a single object/entry, hence no tail timestamp */ + if (offset == f->newest_entry_offset) + return 0; /* No new entry is added after we read last time. */ /* Move to the last object in the journal file, in the hope it is an entry (which it usually will * be). If we lack the "tail_entry_offset" field in the header, we specify the type as OBJECT_UNUSED @@ -2429,6 +2545,7 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) { if (r < 0) { log_debug_errno(r, "Failed to move to last object in journal file, ignoring: %m"); o = NULL; + offset = 0; } if (o && o->object.type == OBJECT_ENTRY) { /* Yay, last object is an entry, let's use the data. */ @@ -2446,10 +2563,11 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) { mo = le64toh(f->header->tail_entry_monotonic); rt = le64toh(f->header->tail_entry_realtime); id = f->header->tail_entry_boot_id; + offset = UINT64_MAX; } else { /* Otherwise let's find the last entry manually (this possibly means traversing the * chain of entry arrays, till the end */ - r = journal_file_next_entry(f, 0, DIRECTION_UP, &o, NULL); + r = journal_file_next_entry(f, 0, DIRECTION_UP, &o, offset == 0 ? &offset : NULL); if (r < 0) return r; if (r == 0) @@ -2464,6 +2582,17 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) { if (mo > rt) /* monotonic clock is further ahead than realtime? that's weird, refuse to use the data */ return -ENODATA; + if (offset == f->newest_entry_offset) { + /* Cached data and the current one should be equivalent. */ + if (!sd_id128_equal(f->newest_machine_id, f->header->machine_id) || + !sd_id128_equal(f->newest_boot_id, id) || + f->newest_monotonic_usec != mo || + f->newest_realtime_usec != rt) + return -EBADMSG; + + return 0; /* No new entry is added after we read last time. */ + } + if (!sd_id128_equal(f->newest_boot_id, id)) journal_file_unlink_newest_by_boot_id(j, f); @@ -2471,13 +2600,14 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) { f->newest_monotonic_usec = mo; f->newest_realtime_usec = rt; f->newest_machine_id = f->header->machine_id; - f->newest_mtime = timespec_load(&f->last_stat.st_mtim); + f->newest_entry_offset = offset; + f->newest_state = f->header->state; r = journal_file_reshuffle_newest_by_boot_id(j, f); if (r < 0) return r; - return 0; + return 1; /* Updated. */ } _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) { @@ -2526,9 +2656,7 @@ _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id12 if (r < 0) return r; - if (ret_boot_id) - *ret_boot_id = o->entry.boot_id; - else { + if (!ret_boot_id) { sd_id128_t id; r = sd_id128_get_boot(&id); @@ -2545,6 +2673,8 @@ _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id12 if (ret) *ret = t; + if (ret_boot_id) + *ret_boot_id = o->entry.boot_id; return 0; } @@ -2748,6 +2878,7 @@ _public_ int sd_journal_get_fd(sd_journal *j) { assert_return(j, -EINVAL); assert_return(!journal_origin_changed(j), -ECHILD); + assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH); if (j->no_inotify) return -EMEDIUMTYPE; @@ -2774,6 +2905,7 @@ _public_ int sd_journal_get_events(sd_journal *j) { assert_return(j, -EINVAL); assert_return(!journal_origin_changed(j), -ECHILD); + assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH); fd = sd_journal_get_fd(j); if (fd < 0) @@ -2787,6 +2919,7 @@ _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) { assert_return(j, -EINVAL); assert_return(!journal_origin_changed(j), -ECHILD); + assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH); assert_return(timeout_usec, -EINVAL); fd = sd_journal_get_fd(j); @@ -2839,7 +2972,7 @@ static void process_q_overflow(sd_journal *j) { continue; log_debug("Directory '%s' hasn't been seen in this enumeration, removing.", f->path); - remove_directory(j, m); + directory_free(m); } log_debug("Reiteration complete."); @@ -2875,7 +3008,7 @@ static void process_inotify_event(sd_journal *j, const struct inotify_event *e) /* Event for a subdirectory */ if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) - remove_directory(j, d); + directory_free(d); } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && id128_is_valid(e->name)) { @@ -2914,6 +3047,8 @@ _public_ int sd_journal_process(sd_journal *j) { if (j->inotify_fd < 0) /* We have no inotify fd yet? Then there's noting to process. */ return 0; + assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH); + j->last_process_usec = now(CLOCK_MONOTONIC); j->last_invalidate_counter = j->current_invalidate_counter; @@ -2942,6 +3077,7 @@ _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) { assert_return(j, -EINVAL); assert_return(!journal_origin_changed(j), -ECHILD); + assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH); if (j->inotify_fd < 0) { JournalFile *f; |