X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fjournal%2Fsd-journal.c;h=16ed4239cee2a1e716d15f7319e5d8c2999b1693;hb=0ce8860a15fb08ac358fb9c5347bd20c0bcdebcd;hp=57572d4f0113f988c8d88db5cf105501eb927c76;hpb=b7def684941808600c344f0be7a2b9fcdda97e0f;p=elogind.git diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 57572d4f0..16ed4239c 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -58,12 +58,13 @@ static void reset_location(sd_journal *j) { zero(j->current_location); } -static void init_location(Location *l, JournalFile *f, Object *o) { +static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) { assert(l); + assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK); assert(f); assert(o->object.type == OBJECT_ENTRY); - l->type = LOCATION_DISCRETE; + l->type = type; l->seqnum = le64toh(o->entry.seqnum); l->seqnum_id = f->header->seqnum_id; l->realtime = le64toh(o->entry.realtime); @@ -74,12 +75,13 @@ static void init_location(Location *l, JournalFile *f, Object *o) { l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true; } -static void set_location(sd_journal *j, JournalFile *f, Object *o, uint64_t offset) { +static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o, uint64_t offset) { assert(j); + assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK); assert(f); assert(o); - init_location(&j->current_location, f, o); + init_location(&j->current_location, type, f, o); j->current_file = f; j->current_field = 0; @@ -357,15 +359,27 @@ _public_ void sd_journal_flush_matches(sd_journal *j) { detach_location(j); } -static int compare_order(JournalFile *af, Object *ao, - JournalFile *bf, Object *bo) { +static int compare_entry_order(JournalFile *af, Object *_ao, + JournalFile *bf, uint64_t bp) { uint64_t a, b; + Object *ao, *bo; + int r; assert(af); - assert(ao); assert(bf); - assert(bo); + assert(_ao); + + /* The mmap cache might invalidate the object from the first + * file if we look at the one from the second file. Hence + * temporarily copy the header of the first one, and look at + * that only. */ + ao = alloca(offsetof(EntryObject, items)); + memcpy(ao, _ao, offsetof(EntryObject, items)); + + r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo); + if (r < 0) + return strcmp(af->path, bf->path); /* We operate on two different files here, hence we can access * two objects at the same time, which we normally can't. @@ -435,7 +449,7 @@ static int compare_with_location(JournalFile *af, Object *ao, Location *l) { assert(af); assert(ao); assert(l); - assert(l->type == LOCATION_DISCRETE); + assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK); if (l->monotonic_set && sd_id128_equal(ao->entry.boot_id, l->boot_id) && @@ -745,7 +759,7 @@ static int next_with_matches( return journal_file_next_entry(f, c, cp, direction, ret, offset); /* If we have a match then we look for the next matching entry - * wiht an offset at least one step larger */ + * with an offset at least one step larger */ return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset); } @@ -808,18 +822,17 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc } static int real_journal_next(sd_journal *j, direction_t direction) { - JournalFile *f, *new_current = NULL; + JournalFile *f, *new_file = NULL; + uint64_t new_offset = 0; + Object *o; + uint64_t p; Iterator i; int r; - uint64_t new_offset = 0; - Object *new_entry = NULL; if (!j) return -EINVAL; HASHMAP_FOREACH(f, j->files, i) { - Object *o; - uint64_t p; bool found; r = next_beyond_location(j, f, direction, &o, &p); @@ -829,12 +842,12 @@ static int real_journal_next(sd_journal *j, direction_t direction) { } else if (r == 0) continue; - if (!new_current) + if (!new_file) found = true; else { int k; - k = compare_order(f, o, new_current, new_entry); + k = compare_entry_order(f, o, new_file, new_offset); if (direction == DIRECTION_DOWN) found = k < 0; @@ -843,16 +856,19 @@ static int real_journal_next(sd_journal *j, direction_t direction) { } if (found) { - new_current = f; - new_entry = o; + new_file = f; new_offset = p; } } - if (!new_current) + if (!new_file) return 0; - set_location(j, new_current, new_entry, new_offset); + r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o); + if (r < 0) + return r; + + set_location(j, LOCATION_DISCRETE, new_file, o, new_offset); return 1; } @@ -924,21 +940,19 @@ _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;p=%s", + "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), - path_get_file_name(j->current_file->path)) < 0) + (unsigned long long) le64toh(o->entry.xor_hash)) < 0) return -ENOMEM; return 1; } _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { - char *w; + char *w, *state; size_t l; - char *state; unsigned long long seqnum, monotonic, realtime, xor_hash; bool seqnum_id_set = false, @@ -951,7 +965,7 @@ _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { if (!j) return -EINVAL; - if (!cursor) + if (isempty(cursor)) return -EINVAL; FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) { @@ -969,35 +983,35 @@ _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { case 's': seqnum_id_set = true; - k = sd_id128_from_string(w+2, &seqnum_id); + k = sd_id128_from_string(item+2, &seqnum_id); break; case 'i': seqnum_set = true; - if (sscanf(w+2, "%llx", &seqnum) != 1) + if (sscanf(item+2, "%llx", &seqnum) != 1) k = -EINVAL; break; case 'b': boot_id_set = true; - k = sd_id128_from_string(w+2, &boot_id); + k = sd_id128_from_string(item+2, &boot_id); break; case 'm': monotonic_set = true; - if (sscanf(w+2, "%llx", &monotonic) != 1) + if (sscanf(item+2, "%llx", &monotonic) != 1) k = -EINVAL; break; case 't': realtime_set = true; - if (sscanf(w+2, "%llx", &realtime) != 1) + if (sscanf(item+2, "%llx", &realtime) != 1) k = -EINVAL; break; case 'x': xor_hash_set = true; - if (sscanf(w+2, "%llx", &xor_hash) != 1) + if (sscanf(item+2, "%llx", &xor_hash) != 1) k = -EINVAL; break; } @@ -1015,7 +1029,7 @@ _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { reset_location(j); - j->current_location.type = LOCATION_DISCRETE; + j->current_location.type = LOCATION_SEEK; if (realtime_set) { j->current_location.realtime = (uint64_t) realtime; @@ -1042,12 +1056,95 @@ _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { return 0; } +_public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) { + int r; + char *w, *state; + size_t l; + Object *o; + + if (!j) + return -EINVAL; + if (isempty(cursor)) + return -EINVAL; + + if (!j->current_file || j->current_file->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o); + if (r < 0) + return r; + + FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) { + _cleanup_free_ char *item = NULL; + sd_id128_t id; + unsigned long long ll; + int k = 0; + + if (l < 2 || w[1] != '=') + return -EINVAL; + + item = strndup(w, l); + if (!item) + return -ENOMEM; + + switch (w[0]) { + + case 's': + k = sd_id128_from_string(item+2, &id); + if (k < 0) + return k; + if (!sd_id128_equal(id, j->current_file->header->seqnum_id)) + return 0; + break; + + case 'i': + if (sscanf(item+2, "%llx", &ll) != 1) + return -EINVAL; + if (ll != le64toh(o->entry.seqnum)) + return 0; + break; + + case 'b': + k = sd_id128_from_string(item+2, &id); + if (k < 0) + return k; + if (!sd_id128_equal(id, o->entry.boot_id)) + return 0; + break; + + case 'm': + if (sscanf(item+2, "%llx", &ll) != 1) + return -EINVAL; + if (ll != le64toh(o->entry.monotonic)) + return 0; + break; + + case 't': + if (sscanf(item+2, "%llx", &ll) != 1) + return -EINVAL; + if (ll != le64toh(o->entry.realtime)) + return 0; + break; + + case 'x': + if (sscanf(item+2, "%llx", &ll) != 1) + return -EINVAL; + if (ll != le64toh(o->entry.xor_hash)) + return 0; + break; + } + } + + return 1; +} + + _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; + j->current_location.type = LOCATION_SEEK; j->current_location.boot_id = boot_id; j->current_location.monotonic = usec; j->current_location.monotonic_set = true; @@ -1060,7 +1157,7 @@ _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) { return -EINVAL; reset_location(j); - j->current_location.type = LOCATION_DISCRETE; + j->current_location.type = LOCATION_SEEK; j->current_location.realtime = usec; j->current_location.realtime_set = true; @@ -1098,7 +1195,9 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) { if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) && !(streq(filename, "system.journal") || - (startswith(filename, "system@") && endswith(filename, ".journal")))) + streq(filename, "system.journal~") || + (startswith(filename, "system@") && + (endswith(filename, ".journal") || endswith(filename, ".journal~"))))) return 0; path = strjoin(prefix, "/", filename, NULL); @@ -1116,7 +1215,7 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) { return 0; } - r = journal_file_open(path, O_RDONLY, 0, NULL, &f); + r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f); free(path); if (r < 0) { @@ -1159,11 +1258,13 @@ static int remove_file(sd_journal *j, const char *prefix, const char *filename) return 0; hashmap_remove(j->files, f->path); + + log_debug("File %s got removed.", f->path); + journal_file_close(f); j->current_invalidate_counter ++; - log_debug("File %s got removed.", f->path); return 0; } @@ -1232,21 +1333,23 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) 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); + IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM| + 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; + struct dirent *de; + union dirent_storage buf; - r = readdir_r(d, &buf, &de); + r = readdir_r(d, &buf.de, &de); if (r != 0 || !de) break; - if (dirent_is_file_with_suffix(de, ".journal")) { + if (dirent_is_file_with_suffix(de, ".journal") || + dirent_is_file_with_suffix(de, ".journal~")) { r = add_file(j, m->path, de->d_name); if (r < 0) log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r)); @@ -1310,26 +1413,28 @@ static int add_root_directory(sd_journal *j, const char *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); + 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; + struct dirent *de; + union dirent_storage buf; sd_id128_t id; - r = readdir_r(d, &buf, &de); + r = readdir_r(d, &buf.de, &de); if (r != 0 || !de) break; - if (dirent_is_file_with_suffix(de, ".journal")) { + if (dirent_is_file_with_suffix(de, ".journal") || + dirent_is_file_with_suffix(de, ".journal~")) { r = add_file(j, m->path, de->d_name); if (r < 0) log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r)); - } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) && + } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) && sd_id128_from_string(de->d_name, &id) >= 0) { r = add_directory(j, m->path, de->d_name); @@ -1402,7 +1507,7 @@ static int allocate_inotify(sd_journal *j) { return 0; } -static sd_journal *journal_new(int flags) { +static sd_journal *journal_new(int flags, const char *path) { sd_journal *j; j = new0(sd_journal, 1); @@ -1412,8 +1517,17 @@ static sd_journal *journal_new(int flags) { j->inotify_fd = -1; j->flags = flags; + if (path) { + j->path = strdup(path); + if (!j->path) { + free(j); + return NULL; + } + } + j->files = hashmap_new(string_hash_func, string_compare_func); if (!j->files) { + free(j->path); free(j); return NULL; } @@ -1421,6 +1535,16 @@ static sd_journal *journal_new(int flags) { j->directories_by_path = hashmap_new(string_hash_func, string_compare_func); if (!j->directories_by_path) { hashmap_free(j->files); + free(j->path); + free(j); + return NULL; + } + + j->mmap = mmap_cache_new(); + if (!j->mmap) { + hashmap_free(j->files); + hashmap_free(j->directories_by_path); + free(j->path); free(j); return NULL; } @@ -1440,7 +1564,7 @@ _public_ int sd_journal_open(sd_journal **ret, int flags) { SD_JOURNAL_SYSTEM_ONLY)) return -EINVAL; - j = journal_new(flags); + j = journal_new(flags, NULL); if (!j) return -ENOMEM; @@ -1470,7 +1594,7 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f if (flags != 0) return -EINVAL; - j = journal_new(flags); + j = journal_new(flags, path); if (!j) return -ENOMEM; @@ -1513,6 +1637,10 @@ _public_ void sd_journal_close(sd_journal *j) { sd_journal_flush_matches(j); + if (j->mmap) + mmap_cache_unref(j->mmap); + + free(j->path); free(j); } @@ -1790,7 +1918,10 @@ _public_ int sd_journal_get_fd(sd_journal *j) { /* Iterate through all dirs again, to add them to the * inotify */ - r = add_search_paths(j); + if (j->path) + r = add_root_directory(j, j->path); + else + r = add_search_paths(j); if (r < 0) return r; @@ -1809,7 +1940,9 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { if (d) { sd_id128_t id; - if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) { + if (!(e->mask & IN_ISDIR) && e->len > 0 && + (endswith(e->name, ".journal") || + endswith(e->name, ".journal~"))) { /* Event for a journal file */ @@ -1818,7 +1951,7 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { if (r < 0) log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r)); - } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) { + } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) { r = remove_file(j, d->path, e->name); if (r < 0) @@ -1868,7 +2001,7 @@ static int determine_change(sd_journal *j) { } _public_ int sd_journal_process(sd_journal *j) { - uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX]; + uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event); bool got_something = false; if (!j) @@ -1950,6 +2083,8 @@ _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, usec_t fr, t; r = journal_file_get_cutoff_realtime_usec(f, &fr, &t); + if (r == -ENOENT) + continue; if (r < 0) return r; if (r == 0) @@ -1987,6 +2122,8 @@ _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot usec_t fr, t; r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t); + if (r == -ENOENT) + continue; if (r < 0) return r; if (r == 0) @@ -2009,6 +2146,45 @@ _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot return first ? 0 : 1; } +void journal_print_header(sd_journal *j) { + Iterator i; + JournalFile *f; + bool newline = false; + + assert(j); + + HASHMAP_FOREACH(f, j->files, i) { + if (newline) + putchar('\n'); + else + newline = true; + + journal_file_print_header(f); + } +} + +_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) { + Iterator i; + JournalFile *f; + uint64_t sum = 0; + + if (!j) + return -EINVAL; + if (!bytes) + return -EINVAL; + + HASHMAP_FOREACH(f, j->files, i) { + struct stat st; + + if (fstat(f->fd, &st) < 0) + return -errno; + + sum += (uint64_t) st.st_blocks * 512ULL; + } + + *bytes = sum; + return 0; +} /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */ /* if (!j) */