X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fjournal%2Fsd-journal.c;h=38e58f57328261d91d520f505da07b0aa9dec3c3;hb=8b18eb674ce4d14e4819e102a0d6679a0fd2e6ce;hp=85c57170d56be46d2e3c55b49ab32f303a8593e2;hpb=de190aef08bb267b645205a747762df573b36834;p=elogind.git diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 85c57170d..38e58f573 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "sd-journal.h" #include "journal-def.h" @@ -29,51 +31,10 @@ #include "hashmap.h" #include "list.h" #include "lookup3.h" +#include "compress.h" +#include "journal-internal.h" -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; - - LIST_HEAD(Match, matches); - unsigned n_matches; -}; +#define JOURNAL_FILES_MAX 1024 static void detach_location(sd_journal *j) { Iterator i; @@ -428,7 +389,7 @@ static int find_location(sd_journal *j, JournalFile *f, direction_t direction, O (direction == DIRECTION_DOWN && cp < tp) || (direction == DIRECTION_UP && cp > tp)) { to = c; - tp = tp; + tp = cp; } } @@ -517,6 +478,7 @@ static int next_with_matches(sd_journal *j, JournalFile *f, direction_t directio uint64_t np, n; bool found, term_result = false; Match *m, *term_match = NULL; + Object *npo = NULL; n = journal_file_entry_n_items(c); @@ -527,6 +489,7 @@ static int next_with_matches(sd_journal *j, JournalFile *f, direction_t directio np = 0; LIST_FOREACH(matches, m, j->matches) { uint64_t q, k; + Object *qo = NULL; /* Let's check if this is the beginning of a * new term, i.e. has a different field prefix @@ -559,22 +522,26 @@ static int next_with_matches(sd_journal *j, JournalFile *f, direction_t directio * where we'd have to try next, in case the other * matches are not OK */ - r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, NULL, &q); + r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, &qo, &q); if (r > 0) { if (direction == DIRECTION_DOWN) { - if (q > np) + if (q > np) { np = q; + npo = qo; + } } else { - if (np == 0 || q < np) + if (np == 0 || q < np) { np = q; + npo = qo; + } } } } /* Check the last term */ - if (term_match && term_result) - found = true; + if (term_match && !term_result) + found = false; /* Did this entry match against all matches? */ if (found) { @@ -592,6 +559,7 @@ static int next_with_matches(sd_journal *j, JournalFile *f, direction_t directio /* Hmm, ok, this entry only matched partially, so * let's try another one */ cp = np; + c = npo; } } @@ -604,12 +572,12 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc assert(f); if (f->current_offset > 0) { - r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &c); + cp = f->current_offset; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c); if (r < 0) return r; - cp = f->current_offset; - r = next_with_matches(j, f, direction, &c, &cp); if (r <= 0) return r; @@ -932,6 +900,10 @@ static int add_file(sd_journal *j, const char *prefix, const char *dir, const ch assert(prefix); assert(filename); + if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) && + !startswith(filename, "system.journal")) + return 0; + if (dir) fn = join(prefix, "/", dir, "/", filename, NULL); else @@ -940,6 +912,17 @@ static int add_file(sd_journal *j, const char *prefix, const char *dir, const ch if (!fn) return -ENOMEM; + if (hashmap_get(j->files, fn)) { + free(fn); + return 0; + } + + if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) { + log_debug("Too many open journal files, not adding %s, ignoring.", fn); + free(fn); + return 0; + } + r = journal_file_open(fn, O_RDONLY, 0, NULL, &f); free(fn); @@ -950,8 +933,7 @@ static int add_file(sd_journal *j, const char *prefix, const char *dir, const ch return r; } - journal_file_dump(f); - + /* journal_file_dump(f); */ r = hashmap_put(j->files, f->path, f); if (r < 0) { @@ -959,6 +941,37 @@ static int add_file(sd_journal *j, const char *prefix, const char *dir, const ch return r; } + 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; + JournalFile *f; + + assert(j); + assert(prefix); + assert(filename); + + if (dir) + fn = join(prefix, "/", dir, "/", filename, NULL); + else + fn = join(prefix, "/", filename, NULL); + + if (!fn) + return -ENOMEM; + + f = hashmap_get(j->files, fn); + free(fn); + + if (!f) + return 0; + + hashmap_remove(j->files, f->path); + journal_file_close(f); + + log_debug("File %s got removed.", f->path); return 0; } @@ -966,25 +979,46 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dir) { char *fn; int r; DIR *d; + int wd; + sd_id128_t id, mid; assert(j); assert(prefix); assert(dir); + if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && + (sd_id128_from_string(dir, &id) < 0 || + sd_id128_get_machine(&mid) < 0 || + !sd_id128_equal(id, mid))) + return 0; + fn = join(prefix, "/", dir, NULL); if (!fn) return -ENOMEM; d = opendir(fn); - free(fn); if (!d) { + free(fn); 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; + } + + free(fn); + for (;;) { struct dirent buf, *de; @@ -1002,10 +1036,66 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dir) { closedir(d); + log_debug("Directory %s/%s got added.", prefix, dir); + return 0; } -int sd_journal_open(sd_journal **ret) { +static void remove_directory_wd(sd_journal *j, int wd) { + char *p; + + assert(j); + assert(wd > 0); + + if (j->inotify_fd >= 0) + inotify_rm_watch(j->inotify_fd, wd); + + p = hashmap_remove(j->inotify_wd_dirs, INT_TO_PTR(wd)); + + if (p) { + log_debug("Directory %s got removed.", p); + free(p); + } +} + +static void add_root_wd(sd_journal *j, const char *p) { + int wd; + char *k; + + assert(j); + assert(p); + + 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; + + 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); + } +} + +static void remove_root_wd(sd_journal *j, int wd) { + char *p; + + assert(j); + assert(wd > 0); + + if (j->inotify_fd >= 0) + inotify_rm_watch(j->inotify_fd, wd); + + p = hashmap_remove(j->inotify_wd_roots, INT_TO_PTR(wd)); + + if (p) { + log_debug("Root %s got removed.", p); + free(p); + } +} + +int sd_journal_open(sd_journal **ret, int flags) { sd_journal *j; const char *p; const char search_paths[] = @@ -1019,18 +1109,38 @@ int sd_journal_open(sd_journal **ret) { if (!j) return -ENOMEM; + j->flags = flags; + + j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (j->inotify_fd < 0) { + r = -errno; + goto fail; + } + j->files = hashmap_new(string_hash_func, string_compare_func); if (!j->files) { r = -ENOMEM; goto fail; } + j->inotify_wd_dirs = hashmap_new(trivial_hash_func, trivial_compare_func); + j->inotify_wd_roots = hashmap_new(trivial_hash_func, trivial_compare_func); + + if (!j->inotify_wd_dirs || !j->inotify_wd_roots) { + r = -ENOMEM; + goto fail; + } + /* 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) { DIR *d; + if ((flags & SD_JOURNAL_RUNTIME_ONLY) && + !path_startswith(p, "/run")) + continue; + d = opendir(p); if (!d) { if (errno != ENOENT) @@ -1038,6 +1148,8 @@ int sd_journal_open(sd_journal **ret) { continue; } + add_root_wd(j, p); + for (;;) { struct dirent buf, *de; sd_id128_t id; @@ -1075,6 +1187,24 @@ fail: void sd_journal_close(sd_journal *j) { assert(j); + if (j->inotify_wd_dirs) { + void *k; + + while ((k = hashmap_first_key(j->inotify_wd_dirs))) + remove_directory_wd(j, PTR_TO_INT(k)); + + hashmap_free(j->inotify_wd_dirs); + } + + if (j->inotify_wd_roots) { + void *k; + + while ((k = hashmap_first_key(j->inotify_wd_roots))) + remove_root_wd(j, PTR_TO_INT(k)); + + hashmap_free(j->inotify_wd_roots); + } + if (j->files) { JournalFile *f; @@ -1086,6 +1216,9 @@ void sd_journal_close(sd_journal *j) { sd_journal_flush_matches(j); + if (j->inotify_fd >= 0) + close_nointr_nofail(j->inotify_fd); + free(j); } @@ -1181,7 +1314,7 @@ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, siz size_t t; p = le64toh(o->entry.items[i].object_offset); - le_hash = o->entry.items[j->current_field].hash; + le_hash = o->entry.items[i].hash; r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); if (r < 0) return r; @@ -1191,9 +1324,31 @@ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, siz l = le64toh(o->object.size) - offsetof(Object, data.payload); - if (l >= field_length+1 && - memcmp(o->data.payload, field, field_length) == 0 && - o->data.payload[field_length] == '=') { + if (o->object.flags & OBJECT_COMPRESSED) { + +#ifdef HAVE_XZ + if (uncompress_startswith(o->data.payload, l, + &f->compress_buffer, &f->compress_buffer_size, + field, field_length, '=')) { + + uint64_t rsize; + + if (!uncompress_blob(o->data.payload, l, + &f->compress_buffer, &f->compress_buffer_size, &rsize)) + return -EBADMSG; + + *data = f->compress_buffer; + *size = (size_t) rsize; + + return 0; + } +#else + return -EPROTONOSUPPORT; +#endif + + } else if (l >= field_length+1 && + memcmp(o->data.payload, field, field_length) == 0 && + o->data.payload[field_length] == '=') { t = (size_t) l; @@ -1256,8 +1411,22 @@ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) { if ((uint64_t) t != l) return -E2BIG; - *data = o->data.payload; - *size = t; + if (o->object.flags & OBJECT_COMPRESSED) { +#ifdef HAVE_XZ + uint64_t rsize; + + if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize)) + return -EBADMSG; + + *data = f->compress_buffer; + *size = (size_t) rsize; +#else + return -EPROTONOSUPPORT; +#endif + } else { + *data = o->data.payload; + *size = t; + } j->current_field ++; @@ -1269,3 +1438,136 @@ void sd_journal_restart_data(sd_journal *j) { j->current_field = 0; } + +int sd_journal_get_fd(sd_journal *j) { + assert(j); + + return j->inotify_fd; +} + +static void process_inotify_event(sd_journal *j, struct inotify_event *e) { + char *p; + 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) { + + 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); + 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)) { + + r = remove_file(j, p, NULL, e->name); + if (r < 0) + log_debug("Failed to remove file %s/%s: %s", p, 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")) { + + /* 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)) { + + r = remove_file(j, p, NULL, e->name); + if (r < 0) + log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r)); + } + + } else if ((e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) { + + /* Event for subdirectory */ + + if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { + + r = add_directory(j, p, e->name); + if (r < 0) + log_debug("Failed to add directory %s/%s: %s", p, e->name, strerror(-r)); + } + } + + return; + } + + if (e->mask & IN_IGNORED) + return; + + log_warning("Unknown inotify event."); +} + +int sd_journal_process(sd_journal *j) { + uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX]; + + assert(j); + + 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; + + return -errno; + } + + e = (struct inotify_event*) buffer; + while (l > 0) { + size_t step; + + process_inotify_event(j, e); + + step = sizeof(struct inotify_event) + e->len; + assert(step <= (size_t) l); + + e = (struct inotify_event*) ((uint8_t*) e + step); + l -= step; + } + } +} + +int sd_journal_query_unique(sd_journal *j, const char *field) { + assert(j); + assert(field); + + return -ENOTSUP; +} + +int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { + assert(j); + + return -ENOTSUP; +} + +void sd_journal_restart_unique(sd_journal *j) { + assert(j); +}