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=831ab6b4e9b28446812f05e02faf74a40519f096;hp=c1f69827e98cd1754049cfd71e3906e4d617ee39;hb=581483bf59ea62147739e71dd2216477be03411c;hpb=39c155ea0d8b24895017fd5cf48508924ce2016d diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index c1f69827e..831ab6b4e 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -33,6 +33,7 @@ #include "journal-file.h" #include "hashmap.h" #include "list.h" +#include "strv.h" #include "path-util.h" #include "lookup3.h" #include "compress.h" @@ -40,6 +41,7 @@ #include "missing.h" #include "catalog.h" #include "replace-var.h" +#include "fileio.h" #define JOURNAL_FILES_MAX 1024 @@ -49,6 +51,15 @@ #define DEFAULT_DATA_THRESHOLD (64*1024) +static bool journal_pid_changed(sd_journal *j) { + assert(j); + + /* We don't support people creating a journal object and + * keeping it around over a fork(). Let's complain. */ + + return j->original_pid != getpid(); +} + /* We return an error here only if we didn't manage to memorize the real error. */ static int set_put_error(sd_journal *j, int r) { @@ -101,7 +112,8 @@ static void init_location(Location *l, LocationType type, JournalFile *f, Object l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true; } -static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o, uint64_t offset) { +static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o, + direction_t direction, uint64_t offset) { assert(j); assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK); assert(f); @@ -112,6 +124,7 @@ static void set_location(sd_journal *j, LocationType type, JournalFile *f, Objec j->current_file = f; j->current_field = 0; + f->last_direction = direction; f->current_offset = offset; } @@ -160,7 +173,7 @@ static bool same_field(const void *_a, size_t s, const void *_b, size_t t) { return true; } - return true; + assert_not_reached("\"=\" not found"); } static Match *match_new(Match *p, MatchType t) { @@ -174,7 +187,7 @@ static Match *match_new(Match *p, MatchType t) { if (p) { m->parent = p; - LIST_PREPEND(Match, matches, p->matches, m); + LIST_PREPEND(matches, p->matches, m); } return m; @@ -187,75 +200,78 @@ static void match_free(Match *m) { match_free(m->matches); if (m->parent) - LIST_REMOVE(Match, matches, m->parent->matches, m); + LIST_REMOVE(matches, m->parent->matches, m); free(m->data); free(m); } static void match_free_if_empty(Match *m) { - assert(m); - - if (m->matches) + if (!m || m->matches) return; match_free(m); } _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { - Match *l2, *l3, *add_here = NULL, *m; + Match *l3, *l4, *add_here = NULL, *m; le64_t le_hash; - if (!j) - return -EINVAL; - - 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: OR term - * level 1: AND terms - * level 2: OR terms - * level 3: concrete matches */ + /* level 0: AND term + * level 1: OR terms + * level 2: AND terms + * level 3: OR terms + * level 4: concrete matches */ if (!j->level0) { - j->level0 = match_new(NULL, MATCH_OR_TERM); + j->level0 = match_new(NULL, MATCH_AND_TERM); if (!j->level0) return -ENOMEM; } if (!j->level1) { - j->level1 = match_new(j->level0, MATCH_AND_TERM); + j->level1 = match_new(j->level0, MATCH_OR_TERM); if (!j->level1) return -ENOMEM; } - assert(j->level0->type == MATCH_OR_TERM); - assert(j->level1->type == MATCH_AND_TERM); + if (!j->level2) { + j->level2 = match_new(j->level1, MATCH_AND_TERM); + if (!j->level2) + return -ENOMEM; + } + + assert(j->level0->type == MATCH_AND_TERM); + assert(j->level1->type == MATCH_OR_TERM); + assert(j->level2->type == MATCH_AND_TERM); le_hash = htole64(hash64(data, size)); - LIST_FOREACH(matches, l2, j->level1->matches) { - assert(l2->type == MATCH_OR_TERM); + LIST_FOREACH(matches, l3, j->level2->matches) { + assert(l3->type == MATCH_OR_TERM); - LIST_FOREACH(matches, l3, l2->matches) { - assert(l3->type == MATCH_DISCRETE); + LIST_FOREACH(matches, l4, l3->matches) { + assert(l4->type == MATCH_DISCRETE); /* Exactly the same match already? Then ignore * this addition */ - if (l3->le_hash == le_hash && - l3->size == size && - memcmp(l3->data, data, size) == 0) + if (l4->le_hash == le_hash && + l4->size == size && + memcmp(l4->data, data, size) == 0) return 0; /* Same field? Then let's add this to this OR term */ - if (same_field(data, size, l3->data, l3->size)) { - add_here = l2; + if (same_field(data, size, l4->data, l4->size)) { + add_here = l3; break; } } @@ -265,7 +281,7 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) } if (!add_here) { - add_here = match_new(j->level1, MATCH_OR_TERM); + add_here = match_new(j->level2, MATCH_OR_TERM); if (!add_here) goto fail; } @@ -285,22 +301,36 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) return 0; fail: - if (add_here) - match_free_if_empty(add_here); + match_free_if_empty(add_here); + match_free_if_empty(j->level2); + match_free_if_empty(j->level1); + match_free_if_empty(j->level0); - if (j->level1) - match_free_if_empty(j->level1); + return -ENOMEM; +} - if (j->level0) - match_free_if_empty(j->level0); +_public_ int sd_journal_add_conjunction(sd_journal *j) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); - return -ENOMEM; + if (!j->level0) + return 0; + + if (!j->level1) + return 0; + + if (!j->level1->matches) + return 0; + + j->level1 = NULL; + j->level2 = NULL; + + return 0; } _public_ int sd_journal_add_disjunction(sd_journal *j) { - Match *m; - - assert(j); + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); if (!j->level0) return 0; @@ -308,14 +338,13 @@ _public_ int sd_journal_add_disjunction(sd_journal *j) { if (!j->level1) return 0; - if (!j->level1->matches) + if (!j->level2) return 0; - m = match_new(j->level0, MATCH_AND_TERM); - if (!m) - return -ENOMEM; + if (!j->level2->matches) + return 0; - j->level1 = m; + j->level2 = NULL; return 0; } @@ -325,7 +354,7 @@ static char *match_make_string(Match *m) { bool enclose = false; if (!m) - return strdup(""); + return strdup("none"); if (m->type == MATCH_DISCRETE) return strndup(m->data, m->size); @@ -351,10 +380,8 @@ static char *match_make_string(Match *m) { p = k; enclose = true; - } else { - free(p); + } else p = t; - } } if (enclose) { @@ -373,20 +400,19 @@ char *journal_make_match_string(sd_journal *j) { } _public_ void sd_journal_flush_matches(sd_journal *j) { - if (!j) return; if (j->level0) match_free(j->level0); - j->level0 = j->level1 = NULL; + j->level0 = j->level1 = j->level2 = NULL; detach_location(j); } static int compare_entry_order(JournalFile *af, Object *_ao, - JournalFile *bf, uint64_t bp) { + JournalFile *bf, uint64_t bp) { uint64_t a, b; Object *ao, *bo; @@ -438,7 +464,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); @@ -448,7 +474,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); @@ -469,7 +495,7 @@ static int compare_entry_order(JournalFile *af, Object *_ao, return 0; } -static int compare_with_location(JournalFile *af, Object *ao, Location *l) { +_pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) { uint64_t a; assert(af); @@ -567,52 +593,47 @@ static int next_for_match( if (r < 0) return r; else if (r > 0) { - if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp)) + if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np)) np = cp; } } + if (np == 0) + return 0; + } else if (m->type == MATCH_AND_TERM) { - Match *i; - bool continue_looking; + Match *i, *last_moved; /* Always jump to the next matching entry and repeat - * this until we fine and offset that matches for all + * this until we find an offset that matches for all * matches. */ if (!m->matches) return 0; - np = 0; - do { - continue_looking = false; + r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np); + if (r <= 0) + return r; - LIST_FOREACH(matches, i, m->matches) { - uint64_t cp, limit; + assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset); + last_moved = m->matches; - if (np == 0) - limit = after_offset; - else if (direction == DIRECTION_DOWN) - limit = MAX(np, after_offset); - else - limit = MIN(np, after_offset); + LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) { + uint64_t cp; - r = next_for_match(j, i, f, limit, direction, NULL, &cp); - if (r <= 0) - return r; + r = next_for_match(j, i, f, np, direction, NULL, &cp); + if (r <= 0) + return r; - if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) && - (np == 0 || (direction == DIRECTION_DOWN ? cp > np : np < cp))) { - np = cp; - continue_looking = true; - } + assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np); + if (direction == DIRECTION_DOWN ? cp > np : cp < np) { + np = cp; + last_moved = i; } - - } while (continue_looking); + } } - if (np == 0) - return 0; + assert(np > 0); r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n); if (r < 0) @@ -717,7 +738,7 @@ static int find_location_for_match( if (r <= 0) return r; - if (np == 0 || (direction == DIRECTION_DOWN ? np < cp : np > cp)) + if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np)) np = cp; } @@ -797,7 +818,7 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc assert(j); assert(f); - if (f->current_offset > 0) { + if (f->last_direction == direction && f->current_offset > 0) { cp = f->current_offset; r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c); @@ -813,7 +834,7 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc return r; } - /* OK, we found the spot, now let's advance until to an entry + /* OK, we found the spot, now let's advance until an entry * that is actually different from what we were previously * looking at. This is necessary to handle entries which exist * in two (or more) journal files, and which shall all be @@ -855,8 +876,8 @@ static int real_journal_next(sd_journal *j, direction_t direction) { Iterator i; int r; - if (!j) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); HASHMAP_FOREACH(f, j->files, i) { bool found; @@ -875,10 +896,7 @@ static int real_journal_next(sd_journal *j, direction_t direction) { k = compare_entry_order(f, o, new_file, new_offset); - if (direction == DIRECTION_DOWN) - found = k < 0; - else - found = k > 0; + found = direction == DIRECTION_DOWN ? k < 0 : k > 0; } if (found) { @@ -894,7 +912,7 @@ static int real_journal_next(sd_journal *j, direction_t direction) { if (r < 0) return r; - set_location(j, LOCATION_DISCRETE, new_file, o, new_offset); + set_location(j, LOCATION_DISCRETE, new_file, o, direction, new_offset); return 1; } @@ -910,8 +928,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; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); if (skip == 0) { /* If this is not a discrete skip, then at least @@ -950,10 +968,9 @@ _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) { int r; char bid[33], sid[33]; - if (!j) - return -EINVAL; - 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; @@ -966,11 +983,11 @@ _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) { sd_id128_to_string(o->entry.boot_id, bid); if (asprintf(cursor, - "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx", - sid, (unsigned long long) le64toh(o->entry.seqnum), - bid, (unsigned long long) le64toh(o->entry.monotonic), - (unsigned long long) le64toh(o->entry.realtime), - (unsigned long long) le64toh(o->entry.xor_hash)) < 0) + "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64, + sid, le64toh(o->entry.seqnum), + bid, le64toh(o->entry.monotonic), + le64toh(o->entry.realtime), + le64toh(o->entry.xor_hash)) < 0) return -ENOMEM; return 0; @@ -989,10 +1006,9 @@ _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 (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) { char *item; @@ -1088,10 +1104,9 @@ _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) { size_t l; Object *o; - if (!j) - return -EINVAL; - 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; @@ -1166,8 +1181,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; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); reset_location(j); j->current_location.type = LOCATION_SEEK; @@ -1179,8 +1194,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; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); reset_location(j); j->current_location.type = LOCATION_SEEK; @@ -1191,8 +1206,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; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); reset_location(j); j->current_location.type = LOCATION_HEAD; @@ -1201,8 +1216,8 @@ _public_ int sd_journal_seek_head(sd_journal *j) { } _public_ int sd_journal_seek_tail(sd_journal *j) { - if (!j) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); reset_location(j); j->current_location.type = LOCATION_TAIL; @@ -1222,48 +1237,67 @@ static void check_network(sd_journal *j, int fd) { return; j->on_network = - (long)sfs.f_type == (long)CIFS_MAGIC_NUMBER || - sfs.f_type == CODA_SUPER_MAGIC || - sfs.f_type == NCP_SUPER_MAGIC || - sfs.f_type == NFS_SUPER_MAGIC || - sfs.f_type == SMB_SUPER_MAGIC; + F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) || + F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC); } -static int add_file(sd_journal *j, const char *prefix, const char *filename) { - char _cleanup_free_ *path = NULL; - int r; - JournalFile *f; +static bool file_has_type_prefix(const char *prefix, const char *filename) { + const char *full, *tilded, *atted; - assert(j); - assert(prefix); - assert(filename); + full = strappenda(prefix, ".journal"); + tilded = strappenda(full, "~"); + atted = strappenda(prefix, "@"); - if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) && - !(streq(filename, "system.journal") || - streq(filename, "system.journal~") || - (startswith(filename, "system@") && - (endswith(filename, ".journal") || endswith(filename, ".journal~"))))) - return 0; + return streq(filename, full) || + streq(filename, tilded) || + startswith(filename, atted); +} - path = strjoin(prefix, "/", filename, NULL); - if (!path) - return -ENOMEM; +static bool file_type_wanted(int flags, const char *filename) { + if (!endswith(filename, ".journal") && !endswith(filename, ".journal~")) + return false; + + /* no flags set → every type is OK */ + if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER))) + return true; + + if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename)) + return true; + + 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()) + < (int) sizeof(prefix)); + + if (file_has_type_prefix(prefix, filename)) + return true; + } + + return false; +} + +static int add_any_file(sd_journal *j, const char *path) { + JournalFile *f = NULL; + int r; + + assert(j); + assert(path); if (hashmap_get(j->files, path)) return 0; if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) { - log_debug("Too many open journal files, not adding %s, ignoring.", path); + log_warning("Too many open journal files, not adding %s.", path); return set_put_error(j, -ETOOMANYREFS); } r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f); - if (r < 0) { - if (errno == ENOENT) - return 0; - + if (r < 0) return r; - } /* journal_file_dump(f); */ @@ -1273,7 +1307,7 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) { return r; } - log_debug("File %s got added.", f->path); + log_debug("File %s added.", f->path); check_network(j, f->fd); @@ -1282,6 +1316,28 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) { return 0; } +static int add_file(sd_journal *j, const char *prefix, const char *filename) { + _cleanup_free_ char *path = NULL; + int r; + + assert(j); + assert(prefix); + assert(filename); + + if (j->no_new_files || + !file_type_wanted(j->flags, filename)) + return 0; + + path = strjoin(prefix, "/", filename, NULL); + if (!path) + return -ENOMEM; + + r = add_any_file(j, path); + if (r == -ENOENT) + return 0; + return 0; +} + static int remove_file(sd_journal *j, const char *prefix, const char *filename) { char *path; JournalFile *f; @@ -1301,7 +1357,7 @@ static int remove_file(sd_journal *j, const char *prefix, const char *filename) hashmap_remove(j->files, f->path); - log_debug("File %s got removed.", f->path); + log_debug("File %s removed.", f->path); if (j->current_file == f) { j->current_file = NULL; @@ -1321,9 +1377,9 @@ static int remove_file(sd_journal *j, const char *prefix, const char *filename) } static int add_directory(sd_journal *j, const char *prefix, const char *dirname) { - char _cleanup_free_ *path = NULL; + _cleanup_free_ char *path = NULL; int r; - DIR _cleanup_closedir_ *d = NULL; + _cleanup_closedir_ DIR *d = NULL; sd_id128_t id, mid; Directory *m; @@ -1368,7 +1424,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) path = NULL; /* avoid freeing in cleanup */ j->current_invalidate_counter ++; - log_debug("Directory %s got added.", m->path); + log_debug("Directory %s added.", m->path); } else if (m->is_root) return 0; @@ -1386,10 +1442,16 @@ 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("Failed to read directory %s: %s", + m->path, strerror(errno)); + return r; + } + if (!de) break; if (dirent_is_file_with_suffix(de, ".journal") || @@ -1411,7 +1473,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) } static int add_root_directory(sd_journal *j, const char *p) { - DIR _cleanup_closedir_ *d = NULL; + _cleanup_closedir_ DIR *d = NULL; Directory *m; int r; @@ -1422,6 +1484,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; @@ -1447,7 +1512,7 @@ static int add_root_directory(sd_journal *j, const char *p) { j->current_invalidate_counter ++; - log_debug("Root directory %s got added.", m->path); + log_debug("Root directory %s added.", m->path); } else if (!m->is_root) return 0; @@ -1462,13 +1527,22 @@ static int add_root_directory(sd_journal *j, const char *p) { inotify_rm_watch(j->inotify_fd, m->wd); } + if (j->no_new_files) + return 0; + 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("Failed to read directory %s: %s", + m->path, strerror(errno)); + return r; + } + if (!de) break; if (dirent_is_file_with_suffix(de, ".journal") || @@ -1508,9 +1582,9 @@ static int remove_directory(sd_journal *j, Directory *d) { hashmap_remove(j->directories_by_path, d->path); if (d->is_root) - log_debug("Root directory %s got removed.", d->path); + log_debug("Root directory %s removed.", d->path); else - log_debug("Directory %s got removed.", d->path); + log_debug("Directory %s removed.", d->path); free(d->path); free(d); @@ -1542,6 +1616,36 @@ static int add_search_paths(sd_journal *j) { return 0; } +static int add_current_paths(sd_journal *j) { + Iterator i; + JournalFile *f; + + assert(j); + assert(j->no_new_files); + + /* Simply adds all directories for files we have open as + * "root" directories. We don't expect errors here, so we + * treat them as fatal. */ + + HASHMAP_FOREACH(f, j->files, i) { + _cleanup_free_ char *dir; + int r; + + dir = dirname_malloc(f->path); + if (!dir) + return -ENOMEM; + + r = add_root_directory(j, dir); + if (r < 0) { + set_put_error(j, r); + return r; + } + } + + return 0; +} + + static int allocate_inotify(sd_journal *j) { assert(j); @@ -1567,6 +1671,7 @@ static sd_journal *journal_new(int flags, const char *path) { if (!j) return NULL; + j->original_pid = getpid(); j->inotify_fd = -1; j->flags = flags; j->data_threshold = DEFAULT_DATA_THRESHOLD; @@ -1594,13 +1699,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_ONLY)) - 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) @@ -1619,18 +1719,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(filename_is_safe(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) @@ -1651,6 +1788,37 @@ fail: return r; } +_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) { + sd_journal *j; + const char **path; + int r; + + assert_return(ret, -EINVAL); + assert_return(flags == 0, -EINVAL); + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + STRV_FOREACH(path, paths) { + r = add_any_file(j, *path); + if (r < 0) { + log_error("Failed to open %s: %s", *path, strerror(-r)); + goto fail; + } + } + + j->no_new_files = true; + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + + return r; +} + _public_ void sd_journal_close(sd_journal *j) { Directory *d; JournalFile *f; @@ -1677,10 +1845,13 @@ _public_ void sd_journal_close(sd_journal *j) { if (j->inotify_fd >= 0) close_nointr_nofail(j->inotify_fd); - if (j->mmap) + if (j->mmap) { + log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap)); mmap_cache_unref(j->mmap); + } free(j->path); + free(j->prefix); free(j->unique_field); set_free(j->errors); free(j); @@ -1691,10 +1862,9 @@ _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) { JournalFile *f; int r; - if (!j) - return -EINVAL; - 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) @@ -1717,8 +1887,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; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); f = j->current_file; if (!f) @@ -1783,17 +1953,12 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void ** int r; Object *o; - if (!j) - return -EINVAL; - 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) @@ -1909,12 +2074,10 @@ _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t int r; Object *o; - if (!j) - return -EINVAL; - 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) @@ -1959,8 +2122,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; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); if (j->inotify_fd >= 0) return j->inotify_fd; @@ -1971,7 +2134,9 @@ _public_ int sd_journal_get_fd(sd_journal *j) { /* Iterate through all dirs again, to add them to the * inotify */ - if (j->path) + if (j->no_new_files) + r = add_current_paths(j); + else if (j->path) r = add_root_directory(j, j->path); else r = add_search_paths(j); @@ -1984,8 +2149,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; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); fd = sd_journal_get_fd(j); if (fd < 0) @@ -1997,10 +2162,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 (!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) @@ -2097,8 +2261,8 @@ _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; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); j->last_process_usec = now(CLOCK_MONOTONIC); @@ -2137,7 +2301,8 @@ _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) { int r; uint64_t t; - assert(j); + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); if (j->inotify_fd < 0) { @@ -2182,12 +2347,13 @@ _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 (!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) { usec_t fr, t; @@ -2201,19 +2367,20 @@ _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; } @@ -2223,10 +2390,10 @@ _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot bool first = true; int r; - if (!j) - 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) { usec_t fr, t; @@ -2278,10 +2445,9 @@ _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) { JournalFile *f; uint64_t sum = 0; - if (!j) - return -EINVAL; - 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) { struct stat st; @@ -2299,12 +2465,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 (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) @@ -2319,18 +2483,13 @@ _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { } _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 (!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); @@ -2344,9 +2503,11 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_ 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) { @@ -2383,8 +2544,16 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_ 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_error("%s:offset " OFSfmt ": object has type %d, expected %d", + j->unique_file->path, j->unique_offset, + o->object.type, OBJECT_DATA); return -EBADMSG; + } + + r = journal_file_object_keep(j->unique_file, o, j->unique_offset); + if (r < 0) + return r; r = return_data(j, j->unique_file, o, &odata, &ol); if (r < 0) @@ -2418,6 +2587,10 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_ if (found) continue; + r = journal_file_object_release(j->unique_file, o, j->unique_offset); + if (r < 0) + return r; + r = return_data(j, j->unique_file, o, data, l); if (r < 0) return r; @@ -2435,8 +2608,8 @@ _public_ void sd_journal_restart_unique(sd_journal *j) { } _public_ int sd_journal_reliable_fd(sd_journal *j) { - if (!j) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); return !j->on_network; } @@ -2468,10 +2641,9 @@ _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) { char *t; int r; - if (!j) - return -EINVAL; - 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) @@ -2498,25 +2670,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; + 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 (!sz) - return -EINVAL; + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(sz, -EINVAL); *sz = j->data_threshold; return 0;