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;
120 if (!memchr(data, '=', size))
122 if (*(char*) data == '=')
125 /* FIXME: iterating with multiple matches is currently
130 le_hash = htole64(hash64(data, size));
132 LIST_FOREACH(matches, m, j->matches) {
135 if (m->le_hash == le_hash &&
137 memcmp(m->data, data, size) == 0)
140 r = same_field(data, size, m->data, m->size);
153 m->data = malloc(m->size);
159 memcpy(m->data, data, size);
160 m->le_hash = le_hash;
162 /* Matches for the same fields we order adjacent to each
164 LIST_INSERT_AFTER(Match, matches, j->matches, after, m);
172 _public_ void sd_journal_flush_matches(sd_journal *j) {
177 Match *m = j->matches;
179 LIST_REMOVE(Match, matches, j->matches, m);
189 static int compare_order(JournalFile *af, Object *ao,
190 JournalFile *bf, Object *bo) {
199 /* We operate on two different files here, hence we can access
200 * two objects at the same time, which we normally can't.
202 * If contents and timestamps match, these entries are
203 * identical, even if the seqnum does not match */
205 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
206 ao->entry.monotonic == bo->entry.monotonic &&
207 ao->entry.realtime == bo->entry.realtime &&
208 ao->entry.xor_hash == bo->entry.xor_hash)
211 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
213 /* If this is from the same seqnum source, compare
215 a = le64toh(ao->entry.seqnum);
216 b = le64toh(bo->entry.seqnum);
223 /* Wow! This is weird, different data but the same
224 * seqnums? Something is borked, but let's make the
225 * best of it and compare by time. */
228 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
230 /* If the boot id matches compare monotonic time */
231 a = le64toh(ao->entry.monotonic);
232 b = le64toh(bo->entry.monotonic);
240 /* Otherwise compare UTC time */
241 a = le64toh(ao->entry.realtime);
242 b = le64toh(ao->entry.realtime);
249 /* Finally, compare by contents */
250 a = le64toh(ao->entry.xor_hash);
251 b = le64toh(ao->entry.xor_hash);
261 static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
267 assert(l->type == LOCATION_DISCRETE);
269 if (l->monotonic_set &&
270 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
272 le64toh(ao->entry.realtime) == l->realtime &&
274 le64toh(ao->entry.xor_hash) == l->xor_hash)
278 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
280 a = le64toh(ao->entry.seqnum);
288 if (l->monotonic_set &&
289 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
291 a = le64toh(ao->entry.monotonic);
293 if (a < l->monotonic)
295 if (a > l->monotonic)
299 if (l->realtime_set) {
301 a = le64toh(ao->entry.realtime);
309 if (l->xor_hash_set) {
310 a = le64toh(ao->entry.xor_hash);
321 static int find_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
329 /* No matches is simple */
331 if (j->current_location.type == LOCATION_HEAD)
332 r = journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p);
333 else if (j->current_location.type == LOCATION_TAIL)
334 r = journal_file_next_entry(f, NULL, 0, DIRECTION_UP, &o, &p);
335 else if (j->current_location.seqnum_set &&
336 sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
337 r = journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, &o, &p);
338 else if (j->current_location.monotonic_set) {
339 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
342 /* boot id unknown in this file */
343 if (j->current_location.realtime_set)
344 r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p);
346 r = journal_file_next_entry(f, NULL, 0, direction, &o, &p);
348 } else if (j->current_location.realtime_set)
349 r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p);
351 r = journal_file_next_entry(f, NULL, 0, direction, &o, &p);
357 Match *m, *term_match = NULL;
361 /* We have matches, first, let's jump to the monotonic
362 * position if we have any, since it implies a
365 if (j->current_location.type == LOCATION_DISCRETE &&
366 j->current_location.monotonic_set) {
368 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
370 return r == -ENOENT ? 0 : r;
373 LIST_FOREACH(matches, m, j->matches) {
377 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), &d, &dp);
381 if (j->current_location.type == LOCATION_HEAD)
382 r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, &c, &cp);
383 else if (j->current_location.type == LOCATION_TAIL)
384 r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, &c, &cp);
385 else if (j->current_location.seqnum_set &&
386 sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
387 r = journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, &c, &cp);
388 else if (j->current_location.realtime_set)
389 r = journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, &c, &cp);
391 r = journal_file_next_entry_for_data(f, NULL, 0, dp, direction, &c, &cp);
403 } else if (same_field(term_match->data, term_match->size, m->data, m->size)) {
405 /* Same field as previous match... */
408 /* Find the earliest of the OR matches */
411 (direction == DIRECTION_DOWN && cp < tp) ||
412 (direction == DIRECTION_UP && cp > tp)) {
421 /* Previous term is finished, did anything match? */
425 /* Find the last of the AND matches */
427 (direction == DIRECTION_DOWN && tp > p) ||
428 (direction == DIRECTION_UP && tp < p)) {
445 /* Last term is finished, did anything match? */
450 (direction == DIRECTION_DOWN && tp > p) ||
451 (direction == DIRECTION_UP && tp < p)) {
469 static int next_with_matches(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
483 /* No matches is easy */
485 r = journal_file_next_entry(f, c, cp, direction, &c, &cp);
496 /* So there are matches we have to adhere to, let's find the
497 * first entry that matches all of them */
501 bool found, term_result = false;
502 Match *m, *term_match = NULL;
505 n = journal_file_entry_n_items(c);
507 /* Make sure we don't match the entry we are starting
509 found = cp != *offset;
512 LIST_FOREACH(matches, m, j->matches) {
516 /* Let's check if this is the beginning of a
517 * new term, i.e. has a different field prefix
518 * as the preceeding match. */
522 } else if (!same_field(term_match->data, term_match->size, m->data, m->size)) {
530 for (k = 0; k < n; k++)
531 if (c->entry.items[k].hash == m->le_hash)
535 /* Hmm, didn't find any field that
536 * matched this rule, so ignore this
537 * match. Go on with next match */
543 /* Hmm, so, this field matched, let's remember
544 * where we'd have to try next, in case the other
545 * matches are not OK */
547 r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, &qo, &q);
548 /* This pointer is invalidated if the window was
549 * remapped. May need to re-fetch it later */
556 if (direction == DIRECTION_DOWN) {
562 if (np == 0 || q < np) {
570 /* Check the last term */
571 if (term_match && !term_result)
574 /* Did this entry match against all matches? */
578 /* Re-fetch the entry */
579 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
590 /* Did we find a subsequent entry? */
594 /* Hmm, ok, this entry only matched partially, so
595 * let's try another one */
601 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
604 int compare_value, r;
609 if (f->current_offset > 0) {
610 cp = f->current_offset;
612 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
616 r = next_with_matches(j, f, direction, &c, &cp);
622 r = find_location(j, f, direction, &c, &cp);
632 if (j->current_location.type == LOCATION_DISCRETE) {
635 k = compare_with_location(f, c, &j->current_location);
636 if (direction == DIRECTION_DOWN)
637 found = k >= compare_value;
639 found = k <= -compare_value;
651 r = next_with_matches(j, f, direction, &c, &cp);
657 static int real_journal_next(sd_journal *j, direction_t direction) {
658 JournalFile *f, *new_current = NULL;
661 uint64_t new_offset = 0;
662 Object *new_entry = NULL;
667 HASHMAP_FOREACH(f, j->files, i) {
672 r = next_beyond_location(j, f, direction, &o, &p);
674 log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
684 k = compare_order(f, o, new_current, new_entry);
686 if (direction == DIRECTION_DOWN)
702 set_location(j, new_current, new_entry, new_offset);
707 _public_ int sd_journal_next(sd_journal *j) {
708 return real_journal_next(j, DIRECTION_DOWN);
711 _public_ int sd_journal_previous(sd_journal *j) {
712 return real_journal_next(j, DIRECTION_UP);
715 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
722 /* If this is not a discrete skip, then at least
723 * resolve the current location */
724 if (j->current_location.type != LOCATION_DISCRETE)
725 return real_journal_next(j, direction);
731 r = real_journal_next(j, direction);
745 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
746 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
749 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
750 return real_journal_next_skip(j, DIRECTION_UP, skip);
753 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
756 char bid[33], sid[33];
763 if (!j->current_file || j->current_file->current_offset <= 0)
764 return -EADDRNOTAVAIL;
766 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
770 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
771 sd_id128_to_string(o->entry.boot_id, bid);
774 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
775 sid, (unsigned long long) le64toh(o->entry.seqnum),
776 bid, (unsigned long long) le64toh(o->entry.monotonic),
777 (unsigned long long) le64toh(o->entry.realtime),
778 (unsigned long long) le64toh(o->entry.xor_hash),
779 path_get_file_name(j->current_file->path)) < 0)
785 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
789 unsigned long long seqnum, monotonic, realtime, xor_hash;
791 seqnum_id_set = false,
794 monotonic_set = false,
795 realtime_set = false,
796 xor_hash_set = false;
797 sd_id128_t seqnum_id, boot_id;
804 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
808 if (l < 2 || w[1] != '=')
811 item = strndup(w, l);
818 seqnum_id_set = true;
819 k = sd_id128_from_string(w+2, &seqnum_id);
824 if (sscanf(w+2, "%llx", &seqnum) != 1)
830 k = sd_id128_from_string(w+2, &boot_id);
834 monotonic_set = true;
835 if (sscanf(w+2, "%llx", &monotonic) != 1)
841 if (sscanf(w+2, "%llx", &realtime) != 1)
847 if (sscanf(w+2, "%llx", &xor_hash) != 1)
858 if ((!seqnum_set || !seqnum_id_set) &&
859 (!monotonic_set || !boot_id_set) &&
865 j->current_location.type = LOCATION_DISCRETE;
868 j->current_location.realtime = (uint64_t) realtime;
869 j->current_location.realtime_set = true;
872 if (seqnum_set && seqnum_id_set) {
873 j->current_location.seqnum = (uint64_t) seqnum;
874 j->current_location.seqnum_id = seqnum_id;
875 j->current_location.seqnum_set = true;
878 if (monotonic_set && boot_id_set) {
879 j->current_location.monotonic = (uint64_t) monotonic;
880 j->current_location.boot_id = boot_id;
881 j->current_location.monotonic_set = true;
885 j->current_location.xor_hash = (uint64_t) xor_hash;
886 j->current_location.xor_hash_set = true;
892 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
897 j->current_location.type = LOCATION_DISCRETE;
898 j->current_location.boot_id = boot_id;
899 j->current_location.monotonic = usec;
900 j->current_location.monotonic_set = true;
905 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
910 j->current_location.type = LOCATION_DISCRETE;
911 j->current_location.realtime = usec;
912 j->current_location.realtime_set = true;
917 _public_ int sd_journal_seek_head(sd_journal *j) {
922 j->current_location.type = LOCATION_HEAD;
927 _public_ int sd_journal_seek_tail(sd_journal *j) {
932 j->current_location.type = LOCATION_TAIL;
937 static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
946 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
947 !(streq(filename, "system.journal") ||
948 (startswith(filename, "system@") && endswith(filename, ".journal"))))
952 fn = join(prefix, "/", dir, "/", filename, NULL);
954 fn = join(prefix, "/", filename, NULL);
959 if (hashmap_get(j->files, fn)) {
964 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
965 log_debug("Too many open journal files, not adding %s, ignoring.", fn);
970 r = journal_file_open(fn, O_RDONLY, 0, NULL, &f);
980 /* journal_file_dump(f); */
982 r = hashmap_put(j->files, f->path, f);
984 journal_file_close(f);
988 log_debug("File %s got added.", f->path);
993 static int remove_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
1002 fn = join(prefix, "/", dir, "/", filename, NULL);
1004 fn = join(prefix, "/", filename, NULL);
1009 f = hashmap_get(j->files, fn);
1015 hashmap_remove(j->files, f->path);
1016 journal_file_close(f);
1018 log_debug("File %s got removed.", f->path);
1022 static int add_directory(sd_journal *j, const char *prefix, const char *dir) {
1033 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1034 (sd_id128_from_string(dir, &id) < 0 ||
1035 sd_id128_get_machine(&mid) < 0 ||
1036 !sd_id128_equal(id, mid)))
1039 fn = join(prefix, "/", dir, NULL);
1047 if (errno == ENOENT)
1053 wd = inotify_add_watch(j->inotify_fd, fn,
1054 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1055 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
1056 IN_DONT_FOLLOW|IN_ONLYDIR);
1058 if (hashmap_put(j->inotify_wd_dirs, INT_TO_PTR(wd), fn) < 0)
1059 inotify_rm_watch(j->inotify_fd, wd);
1067 struct dirent buf, *de;
1069 r = readdir_r(d, &buf, &de);
1073 if (!dirent_is_file_with_suffix(de, ".journal"))
1076 r = add_file(j, prefix, dir, de->d_name);
1078 log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r));
1083 log_debug("Directory %s/%s got added.", prefix, dir);
1088 static void remove_directory_wd(sd_journal *j, int wd) {
1094 if (j->inotify_fd >= 0)
1095 inotify_rm_watch(j->inotify_fd, wd);
1097 p = hashmap_remove(j->inotify_wd_dirs, INT_TO_PTR(wd));
1100 log_debug("Directory %s got removed.", p);
1105 static void add_root_wd(sd_journal *j, const char *p) {
1112 wd = inotify_add_watch(j->inotify_fd, p,
1113 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1114 IN_DONT_FOLLOW|IN_ONLYDIR);
1119 if (!k || hashmap_put(j->inotify_wd_roots, INT_TO_PTR(wd), k) < 0) {
1120 inotify_rm_watch(j->inotify_fd, wd);
1125 static void remove_root_wd(sd_journal *j, int wd) {
1131 if (j->inotify_fd >= 0)
1132 inotify_rm_watch(j->inotify_fd, wd);
1134 p = hashmap_remove(j->inotify_wd_roots, INT_TO_PTR(wd));
1137 log_debug("Root %s got removed.", p);
1142 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1145 const char search_paths[] =
1146 "/run/log/journal\0"
1147 "/var/log/journal\0";
1153 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1154 SD_JOURNAL_RUNTIME_ONLY|
1155 SD_JOURNAL_SYSTEM_ONLY))
1158 j = new0(sd_journal, 1);
1164 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1165 if (j->inotify_fd < 0) {
1170 j->files = hashmap_new(string_hash_func, string_compare_func);
1176 j->inotify_wd_dirs = hashmap_new(trivial_hash_func, trivial_compare_func);
1177 j->inotify_wd_roots = hashmap_new(trivial_hash_func, trivial_compare_func);
1179 if (!j->inotify_wd_dirs || !j->inotify_wd_roots) {
1184 /* We ignore most errors here, since the idea is to only open
1185 * what's actually accessible, and ignore the rest. */
1187 NULSTR_FOREACH(p, search_paths) {
1190 if ((flags & SD_JOURNAL_RUNTIME_ONLY) &&
1191 !path_startswith(p, "/run"))
1196 if (errno != ENOENT)
1197 log_debug("Failed to open %s: %m", p);
1204 struct dirent buf, *de;
1207 r = readdir_r(d, &buf, &de);
1211 if (dirent_is_file_with_suffix(de, ".journal")) {
1212 r = add_file(j, p, NULL, de->d_name);
1214 log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r));
1216 } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
1217 sd_id128_from_string(de->d_name, &id) >= 0) {
1219 r = add_directory(j, p, de->d_name);
1221 log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r));
1232 sd_journal_close(j);
1237 _public_ void sd_journal_close(sd_journal *j) {
1241 if (j->inotify_wd_dirs) {
1244 while ((k = hashmap_first_key(j->inotify_wd_dirs)))
1245 remove_directory_wd(j, PTR_TO_INT(k));
1247 hashmap_free(j->inotify_wd_dirs);
1250 if (j->inotify_wd_roots) {
1253 while ((k = hashmap_first_key(j->inotify_wd_roots)))
1254 remove_root_wd(j, PTR_TO_INT(k));
1256 hashmap_free(j->inotify_wd_roots);
1262 while ((f = hashmap_steal_first(j->files)))
1263 journal_file_close(f);
1265 hashmap_free(j->files);
1268 sd_journal_flush_matches(j);
1270 if (j->inotify_fd >= 0)
1271 close_nointr_nofail(j->inotify_fd);
1276 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1286 f = j->current_file;
1288 return -EADDRNOTAVAIL;
1290 if (f->current_offset <= 0)
1291 return -EADDRNOTAVAIL;
1293 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1297 *ret = le64toh(o->entry.realtime);
1301 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1312 f = j->current_file;
1314 return -EADDRNOTAVAIL;
1316 if (f->current_offset <= 0)
1317 return -EADDRNOTAVAIL;
1319 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1324 *ret_boot_id = o->entry.boot_id;
1326 r = sd_id128_get_boot(&id);
1330 if (!sd_id128_equal(id, o->entry.boot_id))
1334 *ret = le64toh(o->entry.monotonic);
1338 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1341 size_t field_length;
1354 if (isempty(field) || strchr(field, '='))
1357 f = j->current_file;
1359 return -EADDRNOTAVAIL;
1361 if (f->current_offset <= 0)
1362 return -EADDRNOTAVAIL;
1364 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1368 field_length = strlen(field);
1370 n = journal_file_entry_n_items(o);
1371 for (i = 0; i < n; i++) {
1376 p = le64toh(o->entry.items[i].object_offset);
1377 le_hash = o->entry.items[i].hash;
1378 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1382 if (le_hash != o->data.hash)
1385 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1387 if (o->object.flags & OBJECT_COMPRESSED) {
1390 if (uncompress_startswith(o->data.payload, l,
1391 &f->compress_buffer, &f->compress_buffer_size,
1392 field, field_length, '=')) {
1396 if (!uncompress_blob(o->data.payload, l,
1397 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1400 *data = f->compress_buffer;
1401 *size = (size_t) rsize;
1406 return -EPROTONOSUPPORT;
1409 } else if (l >= field_length+1 &&
1410 memcmp(o->data.payload, field, field_length) == 0 &&
1411 o->data.payload[field_length] == '=') {
1415 if ((uint64_t) t != l)
1418 *data = o->data.payload;
1424 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1432 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1447 f = j->current_file;
1449 return -EADDRNOTAVAIL;
1451 if (f->current_offset <= 0)
1452 return -EADDRNOTAVAIL;
1454 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1458 n = journal_file_entry_n_items(o);
1459 if (j->current_field >= n)
1462 p = le64toh(o->entry.items[j->current_field].object_offset);
1463 le_hash = o->entry.items[j->current_field].hash;
1464 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1468 if (le_hash != o->data.hash)
1471 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1474 /* We can't read objects larger than 4G on a 32bit machine */
1475 if ((uint64_t) t != l)
1478 if (o->object.flags & OBJECT_COMPRESSED) {
1482 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1485 *data = f->compress_buffer;
1486 *size = (size_t) rsize;
1488 return -EPROTONOSUPPORT;
1491 *data = o->data.payload;
1495 j->current_field ++;
1500 _public_ void sd_journal_restart_data(sd_journal *j) {
1504 j->current_field = 0;
1507 _public_ int sd_journal_get_fd(sd_journal *j) {
1511 return j->inotify_fd;
1514 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1521 /* Is this a subdirectory we watch? */
1522 p = hashmap_get(j->inotify_wd_dirs, INT_TO_PTR(e->wd));
1525 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1527 /* Event for a journal file */
1529 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1530 r = add_file(j, p, NULL, e->name);
1532 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1533 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1535 r = remove_file(j, p, NULL, e->name);
1537 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1540 } else if (e->len == 0) {
1542 /* Event for the directory itself */
1544 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
1545 remove_directory_wd(j, e->wd);
1551 /* Must be the root directory then? */
1552 p = hashmap_get(j->inotify_wd_roots, INT_TO_PTR(e->wd));
1556 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1558 /* Event for a journal file */
1560 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1561 r = add_file(j, p, NULL, e->name);
1563 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1564 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1566 r = remove_file(j, p, NULL, e->name);
1568 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1571 } else if ((e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1573 /* Event for subdirectory */
1575 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1577 r = add_directory(j, p, e->name);
1579 log_debug("Failed to add directory %s/%s: %s", p, e->name, strerror(-r));
1586 if (e->mask & IN_IGNORED)
1589 log_warning("Unknown inotify event.");
1592 _public_ int sd_journal_process(sd_journal *j) {
1593 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX];
1599 struct inotify_event *e;
1602 l = read(j->inotify_fd, buffer, sizeof(buffer));
1604 if (errno == EINTR || errno == EAGAIN)
1610 e = (struct inotify_event*) buffer;
1614 process_inotify_event(j, e);
1616 step = sizeof(struct inotify_event) + e->len;
1617 assert(step <= (size_t) l);
1619 e = (struct inotify_event*) ((uint8_t*) e + step);
1625 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
1636 HASHMAP_FOREACH(f, j->files, i) {
1639 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
1653 *from = MIN(fr, *from);
1659 return first ? 0 : 1;
1662 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
1673 HASHMAP_FOREACH(f, j->files, i) {
1676 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
1690 *from = MIN(fr, *from);
1696 return first ? 0 : 1;
1700 /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
1702 /* return -EINVAL; */
1704 /* return -EINVAL; */
1706 /* return -ENOTSUP; */
1709 /* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
1711 /* return -EINVAL; */
1713 /* return -EINVAL; */
1715 /* return -EINVAL; */
1717 /* return -ENOTSUP; */
1720 /* _public_ void sd_journal_restart_unique(sd_journal *j) { */