X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fjournal%2Fsd-journal.c;h=b6e74a0d4cc43e9edf81032fdd802a362af70b81;hb=c4aff78b2dffc1405396bff2458e479d0a19d93b;hp=8fc8ec58855f9248cfe9811216f2b36697a576d0;hpb=24b51289e3f61c2483bb61231f8c16b65cf101be;p=elogind.git diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 8fc8ec588..b6e74a0d4 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -6,16 +6,16 @@ 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 + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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. + Lesser General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see . ***/ @@ -24,66 +24,20 @@ #include #include #include +#include #include "sd-journal.h" #include "journal-def.h" #include "journal-file.h" #include "hashmap.h" #include "list.h" +#include "path-util.h" #include "lookup3.h" #include "compress.h" +#include "journal-internal.h" #define JOURNAL_FILES_MAX 1024 -typedef struct Match Match; - -struct Match { - char *data; - size_t size; - uint64_t le_hash; - - 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; - - int inotify_fd; - Hashmap *inotify_wd_dirs; - Hashmap *inotify_wd_roots; - - LIST_HEAD(Match, matches); - unsigned n_matches; -}; - static void detach_location(sd_journal *j) { Iterator i; JournalFile *f; @@ -114,7 +68,7 @@ static void init_location(Location *l, JournalFile *f, Object *o) { 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->boot_id = 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; @@ -154,16 +108,25 @@ static int same_field(const void *_a, size_t s, const void *_b, size_t t) { return -EINVAL; } -int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { +_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { Match *m, *after = NULL; - uint64_t le_hash; - - assert(j); + le64_t le_hash; - if (size <= 0) + if (!j) + return -EINVAL; + if (!data) + return -EINVAL; + if (size <= 1) + return -EINVAL; + if (!memchr(data, '=', size)) + return -EINVAL; + if (*(char*) data == '=') return -EINVAL; - assert(data); + /* FIXME: iterating with multiple matches is currently + * broken */ + if (j->matches) + return -ENOTSUP; le_hash = htole64(hash64(data, size)); @@ -207,8 +170,9 @@ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { return 0; } -void sd_journal_flush_matches(sd_journal *j) { - assert(j); +_public_ void sd_journal_flush_matches(sd_journal *j) { + if (!j) + return; while (j->matches) { Match *m = j->matches; @@ -276,7 +240,7 @@ static int compare_order(JournalFile *af, Object *ao, /* Otherwise compare UTC time */ a = le64toh(ao->entry.realtime); - b = le64toh(ao->entry.realtime); + b = le64toh(bo->entry.realtime); if (a < b) return -1; @@ -285,7 +249,7 @@ static int compare_order(JournalFile *af, Object *ao, /* Finally, compare by contents */ a = le64toh(ao->entry.xor_hash); - b = le64toh(ao->entry.xor_hash); + b = le64toh(bo->entry.xor_hash); if (a < b) return -1; @@ -372,9 +336,17 @@ static int find_location(sd_journal *j, JournalFile *f, direction_t direction, O 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) + 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) + + if (r == -ENOENT) { + /* boot id unknown in this file */ + 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); + } + } 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); @@ -396,14 +368,14 @@ static int find_location(sd_journal *j, JournalFile *f, direction_t direction, O 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; + return r == -ENOENT ? 0 : 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); + r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), &d, &dp); if (r <= 0) return r; @@ -419,6 +391,9 @@ static int find_location(sd_journal *j, JournalFile *f, direction_t direction, O else r = journal_file_next_entry_for_data(f, NULL, 0, dp, direction, &c, &cp); + if (r < 0) + return r; + if (!term_match) { term_match = m; @@ -532,7 +507,7 @@ static int next_with_matches(sd_journal *j, JournalFile *f, direction_t directio /* Make sure we don't match the entry we are starting * from. */ - found = cp > *offset; + found = cp != *offset; np = 0; LIST_FOREACH(matches, m, j->matches) { @@ -571,6 +546,12 @@ static int next_with_matches(sd_journal *j, JournalFile *f, direction_t directio * matches are not OK */ r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, &qo, &q); + /* This pointer is invalidated if the window was + * remapped. May need to re-fetch it later */ + c = NULL; + if (r < 0) + return r; + if (r > 0) { if (direction == DIRECTION_DOWN) { @@ -593,8 +574,15 @@ static int next_with_matches(sd_journal *j, JournalFile *f, direction_t directio /* Did this entry match against all matches? */ if (found) { - if (ret) + if (ret) { + if (c == NULL) { + /* Re-fetch the entry */ + r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c); + if (r < 0) + return r; + } *ret = c; + } if (offset) *offset = cp; return 1; @@ -674,7 +662,8 @@ static int real_journal_next(sd_journal *j, direction_t direction) { uint64_t new_offset = 0; Object *new_entry = NULL; - assert(j); + if (!j) + return -EINVAL; HASHMAP_FOREACH(f, j->files, i) { Object *o; @@ -682,9 +671,10 @@ static int real_journal_next(sd_journal *j, direction_t direction) { bool found; r = next_beyond_location(j, f, direction, &o, &p); - if (r < 0) - return r; - else if (r == 0) + if (r < 0) { + log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r)); + continue; + } else if (r == 0) continue; if (!new_current) @@ -715,21 +705,31 @@ static int real_journal_next(sd_journal *j, direction_t direction) { return 1; } -int sd_journal_next(sd_journal *j) { +_public_ int sd_journal_next(sd_journal *j) { return real_journal_next(j, DIRECTION_DOWN); } -int sd_journal_previous(sd_journal *j) { +_public_ 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) { +static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) { int c = 0, r; - assert(j); + if (!j) + return -EINVAL; - while (skip > 0) { - r = sd_journal_next(j); + if (skip == 0) { + /* If this is not a discrete skip, then at least + * resolve the current location */ + if (j->current_location.type != LOCATION_DISCRETE) + return real_journal_next(j, direction); + + return 0; + } + + do { + r = real_journal_next(j, direction); if (r < 0) return r; @@ -738,38 +738,28 @@ int sd_journal_next_skip(sd_journal *j, uint64_t skip) { skip--; c++; - } + } while (skip > 0); 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++; - } +_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) { + return real_journal_next_skip(j, DIRECTION_DOWN, skip); +} - return 1; +_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) { + return real_journal_next_skip(j, DIRECTION_UP, skip); } -int sd_journal_get_cursor(sd_journal *j, char **cursor) { +_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) { Object *o; int r; char bid[33], sid[33]; - assert(j); - assert(cursor); + if (!j) + return -EINVAL; + if (!cursor) + return -EINVAL; if (!j->current_file || j->current_file->current_offset <= 0) return -EADDRNOTAVAIL; @@ -787,13 +777,13 @@ int sd_journal_get_cursor(sd_journal *j, char **cursor) { bid, (unsigned long long) le64toh(o->entry.monotonic), (unsigned long long) le64toh(o->entry.realtime), (unsigned long long) le64toh(o->entry.xor_hash), - file_name_from_path(j->current_file->path)) < 0) + path_get_file_name(j->current_file->path)) < 0) return -ENOMEM; return 1; } -int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { +_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { char *w; size_t l; char *state; @@ -807,8 +797,10 @@ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { xor_hash_set = false; sd_id128_t seqnum_id, boot_id; - assert(j); - assert(cursor); + if (!j) + return -EINVAL; + if (!cursor) + return -EINVAL; FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) { char *item; @@ -898,8 +890,9 @@ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { return 0; } -int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) { - assert(j); +_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) { + if (!j) + return -EINVAL; reset_location(j); j->current_location.type = LOCATION_DISCRETE; @@ -910,8 +903,9 @@ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t u return 0; } -int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) { - assert(j); +_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) { + if (!j) + return -EINVAL; reset_location(j); j->current_location.type = LOCATION_DISCRETE; @@ -921,8 +915,9 @@ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) { return 0; } -int sd_journal_seek_head(sd_journal *j) { - assert(j); +_public_ int sd_journal_seek_head(sd_journal *j) { + if (!j) + return -EINVAL; reset_location(j); j->current_location.type = LOCATION_HEAD; @@ -930,8 +925,9 @@ int sd_journal_seek_head(sd_journal *j) { return 0; } -int sd_journal_seek_tail(sd_journal *j) { - assert(j); +_public_ int sd_journal_seek_tail(sd_journal *j) { + if (!j) + return -EINVAL; reset_location(j); j->current_location.type = LOCATION_TAIL; @@ -939,8 +935,8 @@ int sd_journal_seek_tail(sd_journal *j) { return 0; } -static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) { - char *fn; +static int add_file(sd_journal *j, const char *prefix, const char *filename) { + char *path; int r; JournalFile *f; @@ -948,27 +944,28 @@ static int add_file(sd_journal *j, const char *prefix, const char *dir, const ch assert(prefix); assert(filename); - if (dir) - fn = join(prefix, "/", dir, "/", filename, NULL); - else - fn = join(prefix, "/", filename, NULL); + if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) && + !(streq(filename, "system.journal") || + (startswith(filename, "system@") && endswith(filename, ".journal")))) + return 0; - if (!fn) + path = join(prefix, "/", filename, NULL); + if (!path) return -ENOMEM; - if (hashmap_get(j->files, fn)) { - free(fn); + if (hashmap_get(j->files, path)) { + free(path); return 0; } if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) { - log_debug("Too many open journal files, not adding %s, ignoring.", fn); - free(fn); + log_debug("Too many open journal files, not adding %s, ignoring.", path); + free(path); return 0; } - r = journal_file_open(fn, O_RDONLY, 0, NULL, &f); - free(fn); + r = journal_file_open(path, O_RDONLY, 0, NULL, &f); + free(path); if (r < 0) { if (errno == ENOENT) @@ -985,76 +982,110 @@ static int add_file(sd_journal *j, const char *prefix, const char *dir, const ch return r; } + j->current_invalidate_counter ++; + log_debug("File %s got added.", f->path); return 0; } -static int remove_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) { - char *fn; +static int remove_file(sd_journal *j, const char *prefix, const char *filename) { + char *path; JournalFile *f; assert(j); assert(prefix); assert(filename); - if (dir) - fn = join(prefix, "/", dir, "/", filename, NULL); - else - fn = join(prefix, "/", filename, NULL); - - if (!fn) + path = join(prefix, "/", filename, NULL); + if (!path) return -ENOMEM; - f = hashmap_get(j->files, fn); - free(fn); - + f = hashmap_get(j->files, path); + free(path); if (!f) return 0; hashmap_remove(j->files, f->path); journal_file_close(f); + j->current_invalidate_counter ++; + log_debug("File %s got removed.", f->path); return 0; } -static int add_directory(sd_journal *j, const char *prefix, const char *dir) { - char *fn; +static int add_directory(sd_journal *j, const char *prefix, const char *dirname) { + char *path; int r; DIR *d; - int wd; + sd_id128_t id, mid; + Directory *m; assert(j); assert(prefix); - assert(dir); + assert(dirname); - fn = join(prefix, "/", dir, NULL); - if (!fn) - return -ENOMEM; + if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && + (sd_id128_from_string(dirname, &id) < 0 || + sd_id128_get_machine(&mid) < 0 || + !sd_id128_equal(id, mid))) + return 0; - d = opendir(fn); + path = join(prefix, "/", dirname, NULL); + if (!path) + return -ENOMEM; + d = opendir(path); if (!d) { - free(fn); + log_debug("Failed to open %s: %m", path); + free(path); + if (errno == ENOENT) return 0; - return -errno; } - wd = inotify_add_watch(j->inotify_fd, fn, - IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| - IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT| - IN_DONT_FOLLOW|IN_ONLYDIR); - if (wd > 0) { - if (hashmap_put(j->inotify_wd_dirs, INT_TO_PTR(wd), fn) < 0) - inotify_rm_watch(j->inotify_fd, wd); - else - fn = NULL; - } + m = hashmap_get(j->directories_by_path, path); + if (!m) { + m = new0(Directory, 1); + if (!m) { + closedir(d); + free(path); + return -ENOMEM; + } + + m->is_root = false; + m->path = path; + + if (hashmap_put(j->directories_by_path, m->path, m) < 0) { + closedir(d); + free(m->path); + free(m); + return -ENOMEM; + } + + j->current_invalidate_counter ++; + + log_debug("Directory %s got added.", m->path); + + } else if (m->is_root) { + free (path); + closedir(d); + return 0; + } else + free(path); - free(fn); + if (m->wd <= 0 && j->inotify_fd >= 0) { + + m->wd = inotify_add_watch(j->inotify_fd, m->path, + IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| + IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT| + IN_DONT_FOLLOW|IN_ONLYDIR); + + if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0) + inotify_rm_watch(j->inotify_fd, m->wd); + } for (;;) { struct dirent buf, *de; @@ -1063,148 +1094,207 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dir) { if (r != 0 || !de) break; - if (!dirent_is_file_with_suffix(de, ".journal")) - continue; - - r = add_file(j, prefix, dir, de->d_name); - if (r < 0) - log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r)); + if (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)); + } } closedir(d); - log_debug("Directory %s/%s got added.", prefix, dir); - return 0; } -static void remove_directory_wd(sd_journal *j, int wd) { - char *p; +static int add_root_directory(sd_journal *j, const char *p) { + DIR *d; + Directory *m; + int r; assert(j); - assert(wd > 0); + assert(p); - if (j->inotify_fd >= 0) - inotify_rm_watch(j->inotify_fd, wd); + if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) && + !path_startswith(p, "/run")) + return -EINVAL; + + d = opendir(p); + if (!d) + return -errno; + + m = hashmap_get(j->directories_by_path, p); + if (!m) { + m = new0(Directory, 1); + if (!m) { + closedir(d); + return -ENOMEM; + } + + m->is_root = true; + m->path = strdup(p); + if (!m->path) { + closedir(d); + free(m); + return -ENOMEM; + } + + if (hashmap_put(j->directories_by_path, m->path, m) < 0) { + closedir(d); + free(m->path); + free(m); + return -ENOMEM; + } - p = hashmap_remove(j->inotify_wd_dirs, INT_TO_PTR(wd)); + j->current_invalidate_counter ++; - if (p) { - log_debug("Directory %s got removed.", p); - free(p); + log_debug("Root directory %s got added.", m->path); + + } else if (!m->is_root) { + closedir(d); + return 0; } -} -static void add_root_wd(sd_journal *j, const char *p) { - int wd; - char *k; + if (m->wd <= 0 && j->inotify_fd >= 0) { - assert(j); - assert(p); + m->wd = inotify_add_watch(j->inotify_fd, m->path, + IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| + IN_DONT_FOLLOW|IN_ONLYDIR); - wd = inotify_add_watch(j->inotify_fd, p, - IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| - IN_DONT_FOLLOW|IN_ONLYDIR); - if (wd <= 0) - return; + if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0) + inotify_rm_watch(j->inotify_fd, m->wd); + } + + for (;;) { + struct dirent buf, *de; + sd_id128_t id; + + r = readdir_r(d, &buf, &de); + if (r != 0 || !de) + break; + + if (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)); - k = strdup(p); - if (!k || hashmap_put(j->inotify_wd_roots, INT_TO_PTR(wd), k) < 0) { - inotify_rm_watch(j->inotify_fd, wd); - free(k); + } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) && + sd_id128_from_string(de->d_name, &id) >= 0) { + + 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)); + } } -} -static void remove_root_wd(sd_journal *j, int wd) { - char *p; + closedir(d); - assert(j); - assert(wd > 0); + return 0; +} - if (j->inotify_fd >= 0) - inotify_rm_watch(j->inotify_fd, wd); +static int remove_directory(sd_journal *j, Directory *d) { + assert(j); - p = hashmap_remove(j->inotify_wd_roots, INT_TO_PTR(wd)); + if (d->wd > 0) { + hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd)); - if (p) { - log_debug("Root %s got removed.", p); - free(p); + if (j->inotify_fd >= 0) + 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 got removed.", d->path); + else + log_debug("Directory %s got removed.", d->path); + + free(d->path); + free(d); + + return 0; } -int sd_journal_open(sd_journal **ret) { - sd_journal *j; - const char *p; +static int add_search_paths(sd_journal *j) { + const char search_paths[] = "/run/log/journal\0" "/var/log/journal\0"; - int r; + const char *p; - assert(ret); + assert(j); - j = new0(sd_journal, 1); - if (!j) - return -ENOMEM; + /* We ignore most errors here, since the idea is to only open + * what's actually accessible, and ignore the rest. */ + + NULSTR_FOREACH(p, search_paths) + add_root_directory(j, p); + + return 0; +} + +static int allocate_inotify(sd_journal *j) { + assert(j); - j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); if (j->inotify_fd < 0) { - r = -errno; - goto fail; + j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (j->inotify_fd < 0) + return -errno; } - j->files = hashmap_new(string_hash_func, string_compare_func); - if (!j->files) { - r = -ENOMEM; - goto fail; + if (!j->directories_by_wd) { + j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func); + if (!j->directories_by_wd) + return -ENOMEM; } - j->inotify_wd_dirs = hashmap_new(trivial_hash_func, trivial_compare_func); - j->inotify_wd_roots = hashmap_new(trivial_hash_func, trivial_compare_func); + return 0; +} - if (!j->inotify_wd_dirs || !j->inotify_wd_roots) { - r = -ENOMEM; - goto fail; - } +static sd_journal *journal_new(int flags) { + sd_journal *j; - /* We ignore most errors here, since the idea is to only open - * what's actually accessible, and ignore the rest. */ + j = new0(sd_journal, 1); + if (!j) + return NULL; - NULSTR_FOREACH(p, search_paths) { - DIR *d; + j->inotify_fd = -1; + j->flags = flags; - d = opendir(p); - if (!d) { - if (errno != ENOENT) - log_debug("Failed to open %s: %m", p); - continue; - } + j->files = hashmap_new(string_hash_func, string_compare_func); + if (!j->files) { + free(j); + return NULL; + } - add_root_wd(j, p); + j->directories_by_path = hashmap_new(string_hash_func, string_compare_func); + if (!j->directories_by_path) { + hashmap_free(j->files); + free(j); + return NULL; + } - for (;;) { - struct dirent buf, *de; - sd_id128_t id; + return j; +} - r = readdir_r(d, &buf, &de); - if (r != 0 || !de) - break; +_public_ int sd_journal_open(sd_journal **ret, int flags) { + sd_journal *j; + int r; - if (dirent_is_file_with_suffix(de, ".journal")) { - r = add_file(j, p, NULL, de->d_name); - if (r < 0) - log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r)); + if (!ret) + return -EINVAL; - } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) && - sd_id128_from_string(de->d_name, &id) >= 0) { + if (flags & ~(SD_JOURNAL_LOCAL_ONLY| + SD_JOURNAL_RUNTIME_ONLY| + SD_JOURNAL_SYSTEM_ONLY)) + return -EINVAL; - r = add_directory(j, p, de->d_name); - if (r < 0) - log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r)); - } - } + j = journal_new(flags); + if (!j) + return -ENOMEM; - closedir(d); - } + r = add_search_paths(j); + if (r < 0) + goto fail; *ret = j; return 0; @@ -1213,53 +1303,76 @@ fail: sd_journal_close(j); return r; -}; +} -void sd_journal_close(sd_journal *j) { - assert(j); +_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) { + sd_journal *j; + int r; - if (j->inotify_wd_dirs) { - void *k; + if (!ret) + return -EINVAL; - while ((k = hashmap_first_key(j->inotify_wd_dirs))) - remove_directory_wd(j, PTR_TO_INT(k)); + if (!path || !path_is_absolute(path)) + return -EINVAL; - hashmap_free(j->inotify_wd_dirs); - } + if (flags != 0) + return -EINVAL; - if (j->inotify_wd_roots) { - void *k; + j = journal_new(flags); + if (!j) + return -ENOMEM; - while ((k = hashmap_first_key(j->inotify_wd_roots))) - remove_root_wd(j, PTR_TO_INT(k)); + r = add_root_directory(j, path); + if (r < 0) + goto fail; - hashmap_free(j->inotify_wd_roots); - } + *ret = j; + return 0; + +fail: + sd_journal_close(j); - if (j->files) { - JournalFile *f; + return r; +} - while ((f = hashmap_steal_first(j->files))) - journal_file_close(f); +_public_ void sd_journal_close(sd_journal *j) { + Directory *d; + JournalFile *f; - hashmap_free(j->files); - } + if (!j) + return; - sd_journal_flush_matches(j); + while ((f = hashmap_steal_first(j->files))) + journal_file_close(f); + + hashmap_free(j->files); + + 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); if (j->inotify_fd >= 0) close_nointr_nofail(j->inotify_fd); + sd_journal_flush_matches(j); + free(j); } -int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) { +_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) { Object *o; JournalFile *f; int r; - assert(j); - assert(ret); + if (!j) + return -EINVAL; + if (!ret) + return -EINVAL; f = j->current_file; if (!f) @@ -1276,14 +1389,14 @@ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) { return 0; } -int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) { +_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) { Object *o; JournalFile *f; int r; sd_id128_t id; - assert(j); - assert(ret); + if (!j) + return -EINVAL; f = j->current_file; if (!f) @@ -1304,24 +1417,30 @@ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_ return r; if (!sd_id128_equal(id, o->entry.boot_id)) - return -ENOENT; + return -ESTALE; } - *ret = le64toh(o->entry.monotonic); + if (ret) + *ret = le64toh(o->entry.monotonic); + return 0; } -int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) { +_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) { JournalFile *f; uint64_t i, n; size_t field_length; int r; Object *o; - assert(j); - assert(field); - assert(data); - assert(size); + if (!j) + return -EINVAL; + if (!field) + return -EINVAL; + if (!data) + return -EINVAL; + if (!size) + return -EINVAL; if (isempty(field) || strchr(field, '=')) return -EINVAL; @@ -1341,7 +1460,8 @@ 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, le_hash; + uint64_t p, l; + le64_t le_hash; size_t t; p = le64toh(o->entry.items[i].object_offset); @@ -1400,16 +1520,20 @@ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, siz return -ENOENT; } -int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) { +_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) { JournalFile *f; - uint64_t p, l, n, le_hash; + uint64_t p, l, n; + le64_t le_hash; int r; Object *o; size_t t; - assert(j); - assert(data); - assert(size); + if (!j) + return -EINVAL; + if (!data) + return -EINVAL; + if (!size) + return -EINVAL; f = j->current_file; if (!f) @@ -1464,84 +1588,82 @@ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) { return 1; } -void sd_journal_restart_data(sd_journal *j) { - assert(j); +_public_ void sd_journal_restart_data(sd_journal *j) { + if (!j) + return; j->current_field = 0; } -int sd_journal_get_fd(sd_journal *j) { - assert(j); +_public_ int sd_journal_get_fd(sd_journal *j) { + int r; + + if (!j) + return -EINVAL; + + if (j->inotify_fd >= 0) + return j->inotify_fd; + + r = allocate_inotify(j); + if (r < 0) + return r; + + /* Iterate through all dirs again, to add them to the + * inotify */ + r = add_search_paths(j); + if (r < 0) + return r; return j->inotify_fd; } static void process_inotify_event(sd_journal *j, struct inotify_event *e) { - char *p; + Directory *d; int r; assert(j); assert(e); /* Is this a subdirectory we watch? */ - p = hashmap_get(j->inotify_wd_dirs, INT_TO_PTR(e->wd)); - if (p) { + d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd)); + if (d) { + sd_id128_t id; if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) { /* Event for a journal file */ if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { - r = add_file(j, p, NULL, e->name); + r = add_file(j, d->path, e->name); if (r < 0) - log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r)); + log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r)); + } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) { - r = remove_file(j, p, NULL, e->name); + r = remove_file(j, d->path, e->name); if (r < 0) - log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r)); + log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r)); } - } else if (e->len == 0) { - - /* Event for the directory itself */ - - if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) - remove_directory_wd(j, e->wd); - } - - return; - } - - /* Must be the root directory then? */ - p = hashmap_get(j->inotify_wd_roots, INT_TO_PTR(e->wd)); - if (p) { - sd_id128_t id; - - if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) { + } else if (!d->is_root && e->len == 0) { - /* Event for a journal file */ - - if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { - r = add_file(j, p, NULL, e->name); - if (r < 0) - log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r)); - } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) { + /* Event for a subdirectory */ - r = remove_file(j, p, NULL, e->name); + if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) { + r = remove_directory(j, d); if (r < 0) - log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r)); + log_debug("Failed to remove directory %s: %s", d->path, strerror(-r)); } - } else if ((e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) { - /* Event for subdirectory */ + } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) { - if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { + /* Event for root directory */ - r = add_directory(j, p, e->name); + 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", p, e->name, strerror(-r)); + log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r)); } } @@ -1554,23 +1676,38 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { log_warning("Unknown inotify event."); } -int sd_journal_process(sd_journal *j) { - uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX]; +static int determine_change(sd_journal *j) { + bool b; assert(j); + b = j->current_invalidate_counter != j->last_invalidate_counter; + j->last_invalidate_counter = j->current_invalidate_counter; + + return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND; +} + +_public_ int sd_journal_process(sd_journal *j) { + uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX]; + bool got_something = false; + + if (!j) + return -EINVAL; + for (;;) { struct inotify_event *e; ssize_t l; l = read(j->inotify_fd, buffer, sizeof(buffer)); if (l < 0) { - if (errno == EINTR || errno == EAGAIN) - return 0; + if (errno == EAGAIN || errno == EINTR) + return got_something ? determine_change(j) : SD_JOURNAL_NOP; return -errno; } + got_something = true; + e = (struct inotify_event*) buffer; while (l > 0) { size_t step; @@ -1584,21 +1721,136 @@ int sd_journal_process(sd_journal *j) { l -= step; } } + + return determine_change(j); } -int sd_journal_query_unique(sd_journal *j, const char *field) { +_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) { + int r; + assert(j); - assert(field); - return -ENOTSUP; + if (j->inotify_fd < 0) { + + /* This is the first invocation, hence create the + * inotify watch */ + r = sd_journal_get_fd(j); + if (r < 0) + return r; + + /* The journal might have changed since the context + * object was created and we weren't watching before, + * hence don't wait for anything, and return + * immediately. */ + return determine_change(j); + } + + do { + r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec); + } while (r == -EINTR); + + if (r < 0) + return r; + + return sd_journal_process(j); } -int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { - assert(j); +_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) { + Iterator i; + JournalFile *f; + bool first = true; + int r; + + if (!j) + return -EINVAL; + if (!from && !to) + return -EINVAL; - return -ENOTSUP; + HASHMAP_FOREACH(f, j->files, i) { + usec_t fr, t; + + r = journal_file_get_cutoff_realtime_usec(f, &fr, &t); + if (r < 0) + return r; + if (r == 0) + continue; + + if (first) { + if (from) + *from = fr; + if (to) + *to = t; + first = false; + } else { + if (from) + *from = MIN(fr, *from); + if (to) + *to = MIN(t, *to); + } + } + + return first ? 0 : 1; } -void sd_journal_restart_unique(sd_journal *j) { - assert(j); +_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; + int r; + + if (!j) + return -EINVAL; + if (!from && !to) + return -EINVAL; + + HASHMAP_FOREACH(f, j->files, i) { + usec_t fr, t; + + r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t); + if (r < 0) + return r; + if (r == 0) + continue; + + if (first) { + if (from) + *from = fr; + if (to) + *to = t; + first = false; + } else { + if (from) + *from = MIN(fr, *from); + if (to) + *to = MIN(t, *to); + } + } + + return first ? 0 : 1; } + + +/* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */ +/* if (!j) */ +/* return -EINVAL; */ +/* if (!field) */ +/* return -EINVAL; */ + +/* return -ENOTSUP; */ +/* } */ + +/* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */ +/* if (!j) */ +/* return -EINVAL; */ +/* if (!data) */ +/* return -EINVAL; */ +/* if (!l) */ +/* return -EINVAL; */ + +/* return -ENOTSUP; */ +/* } */ + +/* _public_ void sd_journal_restart_unique(sd_journal *j) { */ +/* if (!j) */ +/* return; */ +/* } */