1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <sys/inotify.h>
28 #include "sd-journal.h"
29 #include "journal-def.h"
30 #include "journal-file.h"
33 #include "path-util.h"
36 #include "journal-internal.h"
38 #define JOURNAL_FILES_MAX 1024
40 static void detach_location(sd_journal *j) {
46 j->current_file = NULL;
49 HASHMAP_FOREACH(f, j->files, i)
50 f->current_offset = 0;
53 static void reset_location(sd_journal *j) {
57 zero(j->current_location);
60 static void init_location(Location *l, JournalFile *f, Object *o) {
63 assert(o->object.type == OBJECT_ENTRY);
65 l->type = LOCATION_DISCRETE;
66 l->seqnum = le64toh(o->entry.seqnum);
67 l->seqnum_id = f->header->seqnum_id;
68 l->realtime = le64toh(o->entry.realtime);
69 l->monotonic = le64toh(o->entry.monotonic);
70 l->boot_id = o->entry.boot_id;
71 l->xor_hash = le64toh(o->entry.xor_hash);
73 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
76 static void set_location(sd_journal *j, JournalFile *f, Object *o, uint64_t offset) {
81 init_location(&j->current_location, f, o);
86 f->current_offset = offset;
89 static int same_field(const void *_a, size_t s, const void *_b, size_t t) {
90 const uint8_t *a = _a, *b = _b;
92 bool a_good = false, b_good = false, different = false;
94 for (j = 0; j < s && j < t; j++) {
103 if (a_good && b_good)
104 return different ? 0 : 1;
110 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
111 Match *m, *after = NULL;
121 le_hash = htole64(hash64(data, size));
123 LIST_FOREACH(matches, m, j->matches) {
126 if (m->le_hash == le_hash &&
128 memcmp(m->data, data, size) == 0)
131 r = same_field(data, size, m->data, m->size);
144 m->data = malloc(m->size);
150 memcpy(m->data, data, size);
151 m->le_hash = le_hash;
153 /* Matches for the same fields we order adjacent to each
155 LIST_INSERT_AFTER(Match, matches, j->matches, after, m);
163 _public_ void sd_journal_flush_matches(sd_journal *j) {
168 Match *m = j->matches;
170 LIST_REMOVE(Match, matches, j->matches, m);
180 static int compare_order(JournalFile *af, Object *ao,
181 JournalFile *bf, Object *bo) {
190 /* We operate on two different files here, hence we can access
191 * two objects at the same time, which we normally can't.
193 * If contents and timestamps match, these entries are
194 * identical, even if the seqnum does not match */
196 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
197 ao->entry.monotonic == bo->entry.monotonic &&
198 ao->entry.realtime == bo->entry.realtime &&
199 ao->entry.xor_hash == bo->entry.xor_hash)
202 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
204 /* If this is from the same seqnum source, compare
206 a = le64toh(ao->entry.seqnum);
207 b = le64toh(bo->entry.seqnum);
214 /* Wow! This is weird, different data but the same
215 * seqnums? Something is borked, but let's make the
216 * best of it and compare by time. */
219 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
221 /* If the boot id matches compare monotonic time */
222 a = le64toh(ao->entry.monotonic);
223 b = le64toh(bo->entry.monotonic);
231 /* Otherwise compare UTC time */
232 a = le64toh(ao->entry.realtime);
233 b = le64toh(ao->entry.realtime);
240 /* Finally, compare by contents */
241 a = le64toh(ao->entry.xor_hash);
242 b = le64toh(ao->entry.xor_hash);
252 static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
258 assert(l->type == LOCATION_DISCRETE);
260 if (l->monotonic_set &&
261 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
263 le64toh(ao->entry.realtime) == l->realtime &&
265 le64toh(ao->entry.xor_hash) == l->xor_hash)
269 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
271 a = le64toh(ao->entry.seqnum);
279 if (l->monotonic_set &&
280 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
282 a = le64toh(ao->entry.monotonic);
284 if (a < l->monotonic)
286 if (a > l->monotonic)
290 if (l->realtime_set) {
292 a = le64toh(ao->entry.realtime);
300 if (l->xor_hash_set) {
301 a = le64toh(ao->entry.xor_hash);
312 static int find_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
320 /* No matches is simple */
322 if (j->current_location.type == LOCATION_HEAD)
323 r = journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p);
324 else if (j->current_location.type == LOCATION_TAIL)
325 r = journal_file_next_entry(f, NULL, 0, DIRECTION_UP, &o, &p);
326 else if (j->current_location.seqnum_set &&
327 sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
328 r = journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, &o, &p);
329 else if (j->current_location.monotonic_set) {
330 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
333 /* boot id unknown in this file */
334 if (j->current_location.realtime_set)
335 r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p);
337 r = journal_file_next_entry(f, NULL, 0, direction, &o, &p);
339 } else if (j->current_location.realtime_set)
340 r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p);
342 r = journal_file_next_entry(f, NULL, 0, direction, &o, &p);
348 Match *m, *term_match = NULL;
352 /* We have matches, first, let's jump to the monotonic
353 * position if we have any, since it implies a
356 if (j->current_location.type == LOCATION_DISCRETE &&
357 j->current_location.monotonic_set) {
359 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
361 return r == -ENOENT ? 0 : r;
364 LIST_FOREACH(matches, m, j->matches) {
368 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), &d, &dp);
372 if (j->current_location.type == LOCATION_HEAD)
373 r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, &c, &cp);
374 else if (j->current_location.type == LOCATION_TAIL)
375 r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, &c, &cp);
376 else if (j->current_location.seqnum_set &&
377 sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
378 r = journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, &c, &cp);
379 else if (j->current_location.realtime_set)
380 r = journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, &c, &cp);
382 r = journal_file_next_entry_for_data(f, NULL, 0, dp, direction, &c, &cp);
394 } else if (same_field(term_match->data, term_match->size, m->data, m->size)) {
396 /* Same field as previous match... */
399 /* Find the earliest of the OR matches */
402 (direction == DIRECTION_DOWN && cp < tp) ||
403 (direction == DIRECTION_UP && cp > tp)) {
412 /* Previous term is finished, did anything match? */
416 /* Find the last of the AND matches */
418 (direction == DIRECTION_DOWN && tp > p) ||
419 (direction == DIRECTION_UP && tp < p)) {
436 /* Last term is finished, did anything match? */
441 (direction == DIRECTION_DOWN && tp > p) ||
442 (direction == DIRECTION_UP && tp < p)) {
460 static int next_with_matches(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
474 /* No matches is easy */
476 r = journal_file_next_entry(f, c, cp, direction, &c, &cp);
487 /* So there are matches we have to adhere to, let's find the
488 * first entry that matches all of them */
492 bool found, term_result = false;
493 Match *m, *term_match = NULL;
496 n = journal_file_entry_n_items(c);
498 /* Make sure we don't match the entry we are starting
500 found = cp != *offset;
503 LIST_FOREACH(matches, m, j->matches) {
507 /* Let's check if this is the beginning of a
508 * new term, i.e. has a different field prefix
509 * as the preceeding match. */
513 } else if (!same_field(term_match->data, term_match->size, m->data, m->size)) {
521 for (k = 0; k < n; k++)
522 if (c->entry.items[k].hash == m->le_hash)
526 /* Hmm, didn't find any field that
527 * matched this rule, so ignore this
528 * match. Go on with next match */
534 /* Hmm, so, this field matched, let's remember
535 * where we'd have to try next, in case the other
536 * matches are not OK */
538 r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, &qo, &q);
539 /* This pointer is invalidated if the window was
540 * remapped. May need to re-fetch it later */
547 if (direction == DIRECTION_DOWN) {
553 if (np == 0 || q < np) {
561 /* Check the last term */
562 if (term_match && !term_result)
565 /* Did this entry match against all matches? */
569 /* Re-fetch the entry */
570 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
581 /* Did we find a subsequent entry? */
585 /* Hmm, ok, this entry only matched partially, so
586 * let's try another one */
592 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
595 int compare_value, r;
600 if (f->current_offset > 0) {
601 cp = f->current_offset;
603 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
607 r = next_with_matches(j, f, direction, &c, &cp);
613 r = find_location(j, f, direction, &c, &cp);
623 if (j->current_location.type == LOCATION_DISCRETE) {
626 k = compare_with_location(f, c, &j->current_location);
627 if (direction == DIRECTION_DOWN)
628 found = k >= compare_value;
630 found = k <= -compare_value;
642 r = next_with_matches(j, f, direction, &c, &cp);
648 static int real_journal_next(sd_journal *j, direction_t direction) {
649 JournalFile *f, *new_current = NULL;
652 uint64_t new_offset = 0;
653 Object *new_entry = NULL;
658 HASHMAP_FOREACH(f, j->files, i) {
663 r = next_beyond_location(j, f, direction, &o, &p);
674 k = compare_order(f, o, new_current, new_entry);
676 if (direction == DIRECTION_DOWN)
692 set_location(j, new_current, new_entry, new_offset);
697 _public_ int sd_journal_next(sd_journal *j) {
698 return real_journal_next(j, DIRECTION_DOWN);
701 _public_ int sd_journal_previous(sd_journal *j) {
702 return real_journal_next(j, DIRECTION_UP);
705 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
712 /* If this is not a discrete skip, then at least
713 * resolve the current location */
714 if (j->current_location.type != LOCATION_DISCRETE)
715 return real_journal_next(j, direction);
721 r = real_journal_next(j, direction);
735 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
736 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
739 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
740 return real_journal_next_skip(j, DIRECTION_UP, skip);
743 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
746 char bid[33], sid[33];
753 if (!j->current_file || j->current_file->current_offset <= 0)
754 return -EADDRNOTAVAIL;
756 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
760 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
761 sd_id128_to_string(o->entry.boot_id, bid);
764 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
765 sid, (unsigned long long) le64toh(o->entry.seqnum),
766 bid, (unsigned long long) le64toh(o->entry.monotonic),
767 (unsigned long long) le64toh(o->entry.realtime),
768 (unsigned long long) le64toh(o->entry.xor_hash),
769 path_get_file_name(j->current_file->path)) < 0)
775 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
779 unsigned long long seqnum, monotonic, realtime, xor_hash;
781 seqnum_id_set = false,
784 monotonic_set = false,
785 realtime_set = false,
786 xor_hash_set = false;
787 sd_id128_t seqnum_id, boot_id;
794 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
798 if (l < 2 || w[1] != '=')
801 item = strndup(w, l);
808 seqnum_id_set = true;
809 k = sd_id128_from_string(w+2, &seqnum_id);
814 if (sscanf(w+2, "%llx", &seqnum) != 1)
820 k = sd_id128_from_string(w+2, &boot_id);
824 monotonic_set = true;
825 if (sscanf(w+2, "%llx", &monotonic) != 1)
831 if (sscanf(w+2, "%llx", &realtime) != 1)
837 if (sscanf(w+2, "%llx", &xor_hash) != 1)
848 if ((!seqnum_set || !seqnum_id_set) &&
849 (!monotonic_set || !boot_id_set) &&
855 j->current_location.type = LOCATION_DISCRETE;
858 j->current_location.realtime = (uint64_t) realtime;
859 j->current_location.realtime_set = true;
862 if (seqnum_set && seqnum_id_set) {
863 j->current_location.seqnum = (uint64_t) seqnum;
864 j->current_location.seqnum_id = seqnum_id;
865 j->current_location.seqnum_set = true;
868 if (monotonic_set && boot_id_set) {
869 j->current_location.monotonic = (uint64_t) monotonic;
870 j->current_location.boot_id = boot_id;
871 j->current_location.monotonic_set = true;
875 j->current_location.xor_hash = (uint64_t) xor_hash;
876 j->current_location.xor_hash_set = true;
882 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
887 j->current_location.type = LOCATION_DISCRETE;
888 j->current_location.boot_id = boot_id;
889 j->current_location.monotonic = usec;
890 j->current_location.monotonic_set = true;
895 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
900 j->current_location.type = LOCATION_DISCRETE;
901 j->current_location.realtime = usec;
902 j->current_location.realtime_set = true;
907 _public_ int sd_journal_seek_head(sd_journal *j) {
912 j->current_location.type = LOCATION_HEAD;
917 _public_ int sd_journal_seek_tail(sd_journal *j) {
922 j->current_location.type = LOCATION_TAIL;
927 static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
936 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
937 !startswith(filename, "system.journal"))
941 fn = join(prefix, "/", dir, "/", filename, NULL);
943 fn = join(prefix, "/", filename, NULL);
948 if (hashmap_get(j->files, fn)) {
953 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
954 log_debug("Too many open journal files, not adding %s, ignoring.", fn);
959 r = journal_file_open(fn, O_RDONLY, 0, NULL, &f);
969 /* journal_file_dump(f); */
971 r = hashmap_put(j->files, f->path, f);
973 journal_file_close(f);
977 log_debug("File %s got added.", f->path);
982 static int remove_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
991 fn = join(prefix, "/", dir, "/", filename, NULL);
993 fn = join(prefix, "/", filename, NULL);
998 f = hashmap_get(j->files, fn);
1004 hashmap_remove(j->files, f->path);
1005 journal_file_close(f);
1007 log_debug("File %s got removed.", f->path);
1011 static int add_directory(sd_journal *j, const char *prefix, const char *dir) {
1022 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1023 (sd_id128_from_string(dir, &id) < 0 ||
1024 sd_id128_get_machine(&mid) < 0 ||
1025 !sd_id128_equal(id, mid)))
1028 fn = join(prefix, "/", dir, NULL);
1036 if (errno == ENOENT)
1042 wd = inotify_add_watch(j->inotify_fd, fn,
1043 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1044 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
1045 IN_DONT_FOLLOW|IN_ONLYDIR);
1047 if (hashmap_put(j->inotify_wd_dirs, INT_TO_PTR(wd), fn) < 0)
1048 inotify_rm_watch(j->inotify_fd, wd);
1056 struct dirent buf, *de;
1058 r = readdir_r(d, &buf, &de);
1062 if (!dirent_is_file_with_suffix(de, ".journal"))
1065 r = add_file(j, prefix, dir, de->d_name);
1067 log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r));
1072 log_debug("Directory %s/%s got added.", prefix, dir);
1077 static void remove_directory_wd(sd_journal *j, int wd) {
1083 if (j->inotify_fd >= 0)
1084 inotify_rm_watch(j->inotify_fd, wd);
1086 p = hashmap_remove(j->inotify_wd_dirs, INT_TO_PTR(wd));
1089 log_debug("Directory %s got removed.", p);
1094 static void add_root_wd(sd_journal *j, const char *p) {
1101 wd = inotify_add_watch(j->inotify_fd, p,
1102 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1103 IN_DONT_FOLLOW|IN_ONLYDIR);
1108 if (!k || hashmap_put(j->inotify_wd_roots, INT_TO_PTR(wd), k) < 0) {
1109 inotify_rm_watch(j->inotify_fd, wd);
1114 static void remove_root_wd(sd_journal *j, int wd) {
1120 if (j->inotify_fd >= 0)
1121 inotify_rm_watch(j->inotify_fd, wd);
1123 p = hashmap_remove(j->inotify_wd_roots, INT_TO_PTR(wd));
1126 log_debug("Root %s got removed.", p);
1131 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1134 const char search_paths[] =
1135 "/run/log/journal\0"
1136 "/var/log/journal\0";
1142 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1143 SD_JOURNAL_RUNTIME_ONLY|
1144 SD_JOURNAL_SYSTEM_ONLY))
1147 j = new0(sd_journal, 1);
1153 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1154 if (j->inotify_fd < 0) {
1159 j->files = hashmap_new(string_hash_func, string_compare_func);
1165 j->inotify_wd_dirs = hashmap_new(trivial_hash_func, trivial_compare_func);
1166 j->inotify_wd_roots = hashmap_new(trivial_hash_func, trivial_compare_func);
1168 if (!j->inotify_wd_dirs || !j->inotify_wd_roots) {
1173 /* We ignore most errors here, since the idea is to only open
1174 * what's actually accessible, and ignore the rest. */
1176 NULSTR_FOREACH(p, search_paths) {
1179 if ((flags & SD_JOURNAL_RUNTIME_ONLY) &&
1180 !path_startswith(p, "/run"))
1185 if (errno != ENOENT)
1186 log_debug("Failed to open %s: %m", p);
1193 struct dirent buf, *de;
1196 r = readdir_r(d, &buf, &de);
1200 if (dirent_is_file_with_suffix(de, ".journal")) {
1201 r = add_file(j, p, NULL, de->d_name);
1203 log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r));
1205 } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
1206 sd_id128_from_string(de->d_name, &id) >= 0) {
1208 r = add_directory(j, p, de->d_name);
1210 log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r));
1221 sd_journal_close(j);
1226 _public_ void sd_journal_close(sd_journal *j) {
1230 if (j->inotify_wd_dirs) {
1233 while ((k = hashmap_first_key(j->inotify_wd_dirs)))
1234 remove_directory_wd(j, PTR_TO_INT(k));
1236 hashmap_free(j->inotify_wd_dirs);
1239 if (j->inotify_wd_roots) {
1242 while ((k = hashmap_first_key(j->inotify_wd_roots)))
1243 remove_root_wd(j, PTR_TO_INT(k));
1245 hashmap_free(j->inotify_wd_roots);
1251 while ((f = hashmap_steal_first(j->files)))
1252 journal_file_close(f);
1254 hashmap_free(j->files);
1257 sd_journal_flush_matches(j);
1259 if (j->inotify_fd >= 0)
1260 close_nointr_nofail(j->inotify_fd);
1265 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1275 f = j->current_file;
1277 return -EADDRNOTAVAIL;
1279 if (f->current_offset <= 0)
1280 return -EADDRNOTAVAIL;
1282 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1286 *ret = le64toh(o->entry.realtime);
1290 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1301 f = j->current_file;
1303 return -EADDRNOTAVAIL;
1305 if (f->current_offset <= 0)
1306 return -EADDRNOTAVAIL;
1308 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1313 *ret_boot_id = o->entry.boot_id;
1315 r = sd_id128_get_boot(&id);
1319 if (!sd_id128_equal(id, o->entry.boot_id))
1323 *ret = le64toh(o->entry.monotonic);
1327 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1330 size_t field_length;
1343 if (isempty(field) || strchr(field, '='))
1346 f = j->current_file;
1348 return -EADDRNOTAVAIL;
1350 if (f->current_offset <= 0)
1351 return -EADDRNOTAVAIL;
1353 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1357 field_length = strlen(field);
1359 n = journal_file_entry_n_items(o);
1360 for (i = 0; i < n; i++) {
1365 p = le64toh(o->entry.items[i].object_offset);
1366 le_hash = o->entry.items[i].hash;
1367 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1371 if (le_hash != o->data.hash)
1374 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1376 if (o->object.flags & OBJECT_COMPRESSED) {
1379 if (uncompress_startswith(o->data.payload, l,
1380 &f->compress_buffer, &f->compress_buffer_size,
1381 field, field_length, '=')) {
1385 if (!uncompress_blob(o->data.payload, l,
1386 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1389 *data = f->compress_buffer;
1390 *size = (size_t) rsize;
1395 return -EPROTONOSUPPORT;
1398 } else if (l >= field_length+1 &&
1399 memcmp(o->data.payload, field, field_length) == 0 &&
1400 o->data.payload[field_length] == '=') {
1404 if ((uint64_t) t != l)
1407 *data = o->data.payload;
1413 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1421 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1436 f = j->current_file;
1438 return -EADDRNOTAVAIL;
1440 if (f->current_offset <= 0)
1441 return -EADDRNOTAVAIL;
1443 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1447 n = journal_file_entry_n_items(o);
1448 if (j->current_field >= n)
1451 p = le64toh(o->entry.items[j->current_field].object_offset);
1452 le_hash = o->entry.items[j->current_field].hash;
1453 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1457 if (le_hash != o->data.hash)
1460 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1463 /* We can't read objects larger than 4G on a 32bit machine */
1464 if ((uint64_t) t != l)
1467 if (o->object.flags & OBJECT_COMPRESSED) {
1471 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1474 *data = f->compress_buffer;
1475 *size = (size_t) rsize;
1477 return -EPROTONOSUPPORT;
1480 *data = o->data.payload;
1484 j->current_field ++;
1489 _public_ void sd_journal_restart_data(sd_journal *j) {
1493 j->current_field = 0;
1496 _public_ int sd_journal_get_fd(sd_journal *j) {
1500 return j->inotify_fd;
1503 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1510 /* Is this a subdirectory we watch? */
1511 p = hashmap_get(j->inotify_wd_dirs, INT_TO_PTR(e->wd));
1514 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1516 /* Event for a journal file */
1518 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1519 r = add_file(j, p, NULL, e->name);
1521 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1522 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1524 r = remove_file(j, p, NULL, e->name);
1526 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1529 } else if (e->len == 0) {
1531 /* Event for the directory itself */
1533 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
1534 remove_directory_wd(j, e->wd);
1540 /* Must be the root directory then? */
1541 p = hashmap_get(j->inotify_wd_roots, INT_TO_PTR(e->wd));
1545 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1547 /* Event for a journal file */
1549 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1550 r = add_file(j, p, NULL, e->name);
1552 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1553 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1555 r = remove_file(j, p, NULL, e->name);
1557 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1560 } else if ((e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1562 /* Event for subdirectory */
1564 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1566 r = add_directory(j, p, e->name);
1568 log_debug("Failed to add directory %s/%s: %s", p, e->name, strerror(-r));
1575 if (e->mask & IN_IGNORED)
1578 log_warning("Unknown inotify event.");
1581 _public_ int sd_journal_process(sd_journal *j) {
1582 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX];
1588 struct inotify_event *e;
1591 l = read(j->inotify_fd, buffer, sizeof(buffer));
1593 if (errno == EINTR || errno == EAGAIN)
1599 e = (struct inotify_event*) buffer;
1603 process_inotify_event(j, e);
1605 step = sizeof(struct inotify_event) + e->len;
1606 assert(step <= (size_t) l);
1608 e = (struct inotify_event*) ((uint8_t*) e + step);
1614 /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
1616 /* return -EINVAL; */
1618 /* return -EINVAL; */
1620 /* return -ENOTSUP; */
1623 /* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
1625 /* return -EINVAL; */
1627 /* return -EINVAL; */
1629 /* return -EINVAL; */
1631 /* return -ENOTSUP; */
1634 /* _public_ void sd_journal_restart_unique(sd_journal *j) { */