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 /* FIXME: iterating with multiple matches is currently
126 le_hash = htole64(hash64(data, size));
128 LIST_FOREACH(matches, m, j->matches) {
131 if (m->le_hash == le_hash &&
133 memcmp(m->data, data, size) == 0)
136 r = same_field(data, size, m->data, m->size);
149 m->data = malloc(m->size);
155 memcpy(m->data, data, size);
156 m->le_hash = le_hash;
158 /* Matches for the same fields we order adjacent to each
160 LIST_INSERT_AFTER(Match, matches, j->matches, after, m);
168 _public_ void sd_journal_flush_matches(sd_journal *j) {
173 Match *m = j->matches;
175 LIST_REMOVE(Match, matches, j->matches, m);
185 static int compare_order(JournalFile *af, Object *ao,
186 JournalFile *bf, Object *bo) {
195 /* We operate on two different files here, hence we can access
196 * two objects at the same time, which we normally can't.
198 * If contents and timestamps match, these entries are
199 * identical, even if the seqnum does not match */
201 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
202 ao->entry.monotonic == bo->entry.monotonic &&
203 ao->entry.realtime == bo->entry.realtime &&
204 ao->entry.xor_hash == bo->entry.xor_hash)
207 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
209 /* If this is from the same seqnum source, compare
211 a = le64toh(ao->entry.seqnum);
212 b = le64toh(bo->entry.seqnum);
219 /* Wow! This is weird, different data but the same
220 * seqnums? Something is borked, but let's make the
221 * best of it and compare by time. */
224 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
226 /* If the boot id matches compare monotonic time */
227 a = le64toh(ao->entry.monotonic);
228 b = le64toh(bo->entry.monotonic);
236 /* Otherwise compare UTC time */
237 a = le64toh(ao->entry.realtime);
238 b = le64toh(ao->entry.realtime);
245 /* Finally, compare by contents */
246 a = le64toh(ao->entry.xor_hash);
247 b = le64toh(ao->entry.xor_hash);
257 static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
263 assert(l->type == LOCATION_DISCRETE);
265 if (l->monotonic_set &&
266 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
268 le64toh(ao->entry.realtime) == l->realtime &&
270 le64toh(ao->entry.xor_hash) == l->xor_hash)
274 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
276 a = le64toh(ao->entry.seqnum);
284 if (l->monotonic_set &&
285 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
287 a = le64toh(ao->entry.monotonic);
289 if (a < l->monotonic)
291 if (a > l->monotonic)
295 if (l->realtime_set) {
297 a = le64toh(ao->entry.realtime);
305 if (l->xor_hash_set) {
306 a = le64toh(ao->entry.xor_hash);
317 static int find_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
325 /* No matches is simple */
327 if (j->current_location.type == LOCATION_HEAD)
328 r = journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p);
329 else if (j->current_location.type == LOCATION_TAIL)
330 r = journal_file_next_entry(f, NULL, 0, DIRECTION_UP, &o, &p);
331 else if (j->current_location.seqnum_set &&
332 sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
333 r = journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, &o, &p);
334 else if (j->current_location.monotonic_set) {
335 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
338 /* boot id unknown in this file */
339 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);
344 } else if (j->current_location.realtime_set)
345 r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p);
347 r = journal_file_next_entry(f, NULL, 0, direction, &o, &p);
353 Match *m, *term_match = NULL;
357 /* We have matches, first, let's jump to the monotonic
358 * position if we have any, since it implies a
361 if (j->current_location.type == LOCATION_DISCRETE &&
362 j->current_location.monotonic_set) {
364 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
366 return r == -ENOENT ? 0 : r;
369 LIST_FOREACH(matches, m, j->matches) {
373 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), &d, &dp);
377 if (j->current_location.type == LOCATION_HEAD)
378 r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, &c, &cp);
379 else if (j->current_location.type == LOCATION_TAIL)
380 r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, &c, &cp);
381 else if (j->current_location.seqnum_set &&
382 sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
383 r = journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, &c, &cp);
384 else if (j->current_location.realtime_set)
385 r = journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, &c, &cp);
387 r = journal_file_next_entry_for_data(f, NULL, 0, dp, direction, &c, &cp);
399 } else if (same_field(term_match->data, term_match->size, m->data, m->size)) {
401 /* Same field as previous match... */
404 /* Find the earliest of the OR matches */
407 (direction == DIRECTION_DOWN && cp < tp) ||
408 (direction == DIRECTION_UP && cp > tp)) {
417 /* Previous term is finished, did anything match? */
421 /* Find the last of the AND matches */
423 (direction == DIRECTION_DOWN && tp > p) ||
424 (direction == DIRECTION_UP && tp < p)) {
441 /* Last term is finished, did anything match? */
446 (direction == DIRECTION_DOWN && tp > p) ||
447 (direction == DIRECTION_UP && tp < p)) {
465 static int next_with_matches(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
479 /* No matches is easy */
481 r = journal_file_next_entry(f, c, cp, direction, &c, &cp);
492 /* So there are matches we have to adhere to, let's find the
493 * first entry that matches all of them */
497 bool found, term_result = false;
498 Match *m, *term_match = NULL;
501 n = journal_file_entry_n_items(c);
503 /* Make sure we don't match the entry we are starting
505 found = cp != *offset;
508 LIST_FOREACH(matches, m, j->matches) {
512 /* Let's check if this is the beginning of a
513 * new term, i.e. has a different field prefix
514 * as the preceeding match. */
518 } else if (!same_field(term_match->data, term_match->size, m->data, m->size)) {
526 for (k = 0; k < n; k++)
527 if (c->entry.items[k].hash == m->le_hash)
531 /* Hmm, didn't find any field that
532 * matched this rule, so ignore this
533 * match. Go on with next match */
539 /* Hmm, so, this field matched, let's remember
540 * where we'd have to try next, in case the other
541 * matches are not OK */
543 r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, &qo, &q);
544 /* This pointer is invalidated if the window was
545 * remapped. May need to re-fetch it later */
552 if (direction == DIRECTION_DOWN) {
558 if (np == 0 || q < np) {
566 /* Check the last term */
567 if (term_match && !term_result)
570 /* Did this entry match against all matches? */
574 /* Re-fetch the entry */
575 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
586 /* Did we find a subsequent entry? */
590 /* Hmm, ok, this entry only matched partially, so
591 * let's try another one */
597 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
600 int compare_value, r;
605 if (f->current_offset > 0) {
606 cp = f->current_offset;
608 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
612 r = next_with_matches(j, f, direction, &c, &cp);
618 r = find_location(j, f, direction, &c, &cp);
628 if (j->current_location.type == LOCATION_DISCRETE) {
631 k = compare_with_location(f, c, &j->current_location);
632 if (direction == DIRECTION_DOWN)
633 found = k >= compare_value;
635 found = k <= -compare_value;
647 r = next_with_matches(j, f, direction, &c, &cp);
653 static int real_journal_next(sd_journal *j, direction_t direction) {
654 JournalFile *f, *new_current = NULL;
657 uint64_t new_offset = 0;
658 Object *new_entry = NULL;
663 HASHMAP_FOREACH(f, j->files, i) {
668 r = next_beyond_location(j, f, direction, &o, &p);
679 k = compare_order(f, o, new_current, new_entry);
681 if (direction == DIRECTION_DOWN)
697 set_location(j, new_current, new_entry, new_offset);
702 _public_ int sd_journal_next(sd_journal *j) {
703 return real_journal_next(j, DIRECTION_DOWN);
706 _public_ int sd_journal_previous(sd_journal *j) {
707 return real_journal_next(j, DIRECTION_UP);
710 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
717 /* If this is not a discrete skip, then at least
718 * resolve the current location */
719 if (j->current_location.type != LOCATION_DISCRETE)
720 return real_journal_next(j, direction);
726 r = real_journal_next(j, direction);
740 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
741 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
744 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
745 return real_journal_next_skip(j, DIRECTION_UP, skip);
748 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
751 char bid[33], sid[33];
758 if (!j->current_file || j->current_file->current_offset <= 0)
759 return -EADDRNOTAVAIL;
761 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
765 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
766 sd_id128_to_string(o->entry.boot_id, bid);
769 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
770 sid, (unsigned long long) le64toh(o->entry.seqnum),
771 bid, (unsigned long long) le64toh(o->entry.monotonic),
772 (unsigned long long) le64toh(o->entry.realtime),
773 (unsigned long long) le64toh(o->entry.xor_hash),
774 path_get_file_name(j->current_file->path)) < 0)
780 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
784 unsigned long long seqnum, monotonic, realtime, xor_hash;
786 seqnum_id_set = false,
789 monotonic_set = false,
790 realtime_set = false,
791 xor_hash_set = false;
792 sd_id128_t seqnum_id, boot_id;
799 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
803 if (l < 2 || w[1] != '=')
806 item = strndup(w, l);
813 seqnum_id_set = true;
814 k = sd_id128_from_string(w+2, &seqnum_id);
819 if (sscanf(w+2, "%llx", &seqnum) != 1)
825 k = sd_id128_from_string(w+2, &boot_id);
829 monotonic_set = true;
830 if (sscanf(w+2, "%llx", &monotonic) != 1)
836 if (sscanf(w+2, "%llx", &realtime) != 1)
842 if (sscanf(w+2, "%llx", &xor_hash) != 1)
853 if ((!seqnum_set || !seqnum_id_set) &&
854 (!monotonic_set || !boot_id_set) &&
860 j->current_location.type = LOCATION_DISCRETE;
863 j->current_location.realtime = (uint64_t) realtime;
864 j->current_location.realtime_set = true;
867 if (seqnum_set && seqnum_id_set) {
868 j->current_location.seqnum = (uint64_t) seqnum;
869 j->current_location.seqnum_id = seqnum_id;
870 j->current_location.seqnum_set = true;
873 if (monotonic_set && boot_id_set) {
874 j->current_location.monotonic = (uint64_t) monotonic;
875 j->current_location.boot_id = boot_id;
876 j->current_location.monotonic_set = true;
880 j->current_location.xor_hash = (uint64_t) xor_hash;
881 j->current_location.xor_hash_set = true;
887 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
892 j->current_location.type = LOCATION_DISCRETE;
893 j->current_location.boot_id = boot_id;
894 j->current_location.monotonic = usec;
895 j->current_location.monotonic_set = true;
900 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
905 j->current_location.type = LOCATION_DISCRETE;
906 j->current_location.realtime = usec;
907 j->current_location.realtime_set = true;
912 _public_ int sd_journal_seek_head(sd_journal *j) {
917 j->current_location.type = LOCATION_HEAD;
922 _public_ int sd_journal_seek_tail(sd_journal *j) {
927 j->current_location.type = LOCATION_TAIL;
932 static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
941 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
942 !startswith(filename, "system.journal"))
946 fn = join(prefix, "/", dir, "/", filename, NULL);
948 fn = join(prefix, "/", filename, NULL);
953 if (hashmap_get(j->files, fn)) {
958 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
959 log_debug("Too many open journal files, not adding %s, ignoring.", fn);
964 r = journal_file_open(fn, O_RDONLY, 0, NULL, &f);
974 /* journal_file_dump(f); */
976 r = hashmap_put(j->files, f->path, f);
978 journal_file_close(f);
982 log_debug("File %s got added.", f->path);
987 static int remove_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
996 fn = join(prefix, "/", dir, "/", filename, NULL);
998 fn = join(prefix, "/", filename, NULL);
1003 f = hashmap_get(j->files, fn);
1009 hashmap_remove(j->files, f->path);
1010 journal_file_close(f);
1012 log_debug("File %s got removed.", f->path);
1016 static int add_directory(sd_journal *j, const char *prefix, const char *dir) {
1027 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1028 (sd_id128_from_string(dir, &id) < 0 ||
1029 sd_id128_get_machine(&mid) < 0 ||
1030 !sd_id128_equal(id, mid)))
1033 fn = join(prefix, "/", dir, NULL);
1041 if (errno == ENOENT)
1047 wd = inotify_add_watch(j->inotify_fd, fn,
1048 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1049 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
1050 IN_DONT_FOLLOW|IN_ONLYDIR);
1052 if (hashmap_put(j->inotify_wd_dirs, INT_TO_PTR(wd), fn) < 0)
1053 inotify_rm_watch(j->inotify_fd, wd);
1061 struct dirent buf, *de;
1063 r = readdir_r(d, &buf, &de);
1067 if (!dirent_is_file_with_suffix(de, ".journal"))
1070 r = add_file(j, prefix, dir, de->d_name);
1072 log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r));
1077 log_debug("Directory %s/%s got added.", prefix, dir);
1082 static void remove_directory_wd(sd_journal *j, int wd) {
1088 if (j->inotify_fd >= 0)
1089 inotify_rm_watch(j->inotify_fd, wd);
1091 p = hashmap_remove(j->inotify_wd_dirs, INT_TO_PTR(wd));
1094 log_debug("Directory %s got removed.", p);
1099 static void add_root_wd(sd_journal *j, const char *p) {
1106 wd = inotify_add_watch(j->inotify_fd, p,
1107 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1108 IN_DONT_FOLLOW|IN_ONLYDIR);
1113 if (!k || hashmap_put(j->inotify_wd_roots, INT_TO_PTR(wd), k) < 0) {
1114 inotify_rm_watch(j->inotify_fd, wd);
1119 static void remove_root_wd(sd_journal *j, int wd) {
1125 if (j->inotify_fd >= 0)
1126 inotify_rm_watch(j->inotify_fd, wd);
1128 p = hashmap_remove(j->inotify_wd_roots, INT_TO_PTR(wd));
1131 log_debug("Root %s got removed.", p);
1136 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1139 const char search_paths[] =
1140 "/run/log/journal\0"
1141 "/var/log/journal\0";
1147 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1148 SD_JOURNAL_RUNTIME_ONLY|
1149 SD_JOURNAL_SYSTEM_ONLY))
1152 j = new0(sd_journal, 1);
1158 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1159 if (j->inotify_fd < 0) {
1164 j->files = hashmap_new(string_hash_func, string_compare_func);
1170 j->inotify_wd_dirs = hashmap_new(trivial_hash_func, trivial_compare_func);
1171 j->inotify_wd_roots = hashmap_new(trivial_hash_func, trivial_compare_func);
1173 if (!j->inotify_wd_dirs || !j->inotify_wd_roots) {
1178 /* We ignore most errors here, since the idea is to only open
1179 * what's actually accessible, and ignore the rest. */
1181 NULSTR_FOREACH(p, search_paths) {
1184 if ((flags & SD_JOURNAL_RUNTIME_ONLY) &&
1185 !path_startswith(p, "/run"))
1190 if (errno != ENOENT)
1191 log_debug("Failed to open %s: %m", p);
1198 struct dirent buf, *de;
1201 r = readdir_r(d, &buf, &de);
1205 if (dirent_is_file_with_suffix(de, ".journal")) {
1206 r = add_file(j, p, NULL, de->d_name);
1208 log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r));
1210 } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
1211 sd_id128_from_string(de->d_name, &id) >= 0) {
1213 r = add_directory(j, p, de->d_name);
1215 log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r));
1226 sd_journal_close(j);
1231 _public_ void sd_journal_close(sd_journal *j) {
1235 if (j->inotify_wd_dirs) {
1238 while ((k = hashmap_first_key(j->inotify_wd_dirs)))
1239 remove_directory_wd(j, PTR_TO_INT(k));
1241 hashmap_free(j->inotify_wd_dirs);
1244 if (j->inotify_wd_roots) {
1247 while ((k = hashmap_first_key(j->inotify_wd_roots)))
1248 remove_root_wd(j, PTR_TO_INT(k));
1250 hashmap_free(j->inotify_wd_roots);
1256 while ((f = hashmap_steal_first(j->files)))
1257 journal_file_close(f);
1259 hashmap_free(j->files);
1262 sd_journal_flush_matches(j);
1264 if (j->inotify_fd >= 0)
1265 close_nointr_nofail(j->inotify_fd);
1270 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1280 f = j->current_file;
1282 return -EADDRNOTAVAIL;
1284 if (f->current_offset <= 0)
1285 return -EADDRNOTAVAIL;
1287 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1291 *ret = le64toh(o->entry.realtime);
1295 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1306 f = j->current_file;
1308 return -EADDRNOTAVAIL;
1310 if (f->current_offset <= 0)
1311 return -EADDRNOTAVAIL;
1313 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1318 *ret_boot_id = o->entry.boot_id;
1320 r = sd_id128_get_boot(&id);
1324 if (!sd_id128_equal(id, o->entry.boot_id))
1328 *ret = le64toh(o->entry.monotonic);
1332 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1335 size_t field_length;
1348 if (isempty(field) || strchr(field, '='))
1351 f = j->current_file;
1353 return -EADDRNOTAVAIL;
1355 if (f->current_offset <= 0)
1356 return -EADDRNOTAVAIL;
1358 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1362 field_length = strlen(field);
1364 n = journal_file_entry_n_items(o);
1365 for (i = 0; i < n; i++) {
1370 p = le64toh(o->entry.items[i].object_offset);
1371 le_hash = o->entry.items[i].hash;
1372 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1376 if (le_hash != o->data.hash)
1379 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1381 if (o->object.flags & OBJECT_COMPRESSED) {
1384 if (uncompress_startswith(o->data.payload, l,
1385 &f->compress_buffer, &f->compress_buffer_size,
1386 field, field_length, '=')) {
1390 if (!uncompress_blob(o->data.payload, l,
1391 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1394 *data = f->compress_buffer;
1395 *size = (size_t) rsize;
1400 return -EPROTONOSUPPORT;
1403 } else if (l >= field_length+1 &&
1404 memcmp(o->data.payload, field, field_length) == 0 &&
1405 o->data.payload[field_length] == '=') {
1409 if ((uint64_t) t != l)
1412 *data = o->data.payload;
1418 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1426 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1441 f = j->current_file;
1443 return -EADDRNOTAVAIL;
1445 if (f->current_offset <= 0)
1446 return -EADDRNOTAVAIL;
1448 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1452 n = journal_file_entry_n_items(o);
1453 if (j->current_field >= n)
1456 p = le64toh(o->entry.items[j->current_field].object_offset);
1457 le_hash = o->entry.items[j->current_field].hash;
1458 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1462 if (le_hash != o->data.hash)
1465 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1468 /* We can't read objects larger than 4G on a 32bit machine */
1469 if ((uint64_t) t != l)
1472 if (o->object.flags & OBJECT_COMPRESSED) {
1476 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1479 *data = f->compress_buffer;
1480 *size = (size_t) rsize;
1482 return -EPROTONOSUPPORT;
1485 *data = o->data.payload;
1489 j->current_field ++;
1494 _public_ void sd_journal_restart_data(sd_journal *j) {
1498 j->current_field = 0;
1501 _public_ int sd_journal_get_fd(sd_journal *j) {
1505 return j->inotify_fd;
1508 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1515 /* Is this a subdirectory we watch? */
1516 p = hashmap_get(j->inotify_wd_dirs, INT_TO_PTR(e->wd));
1519 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1521 /* Event for a journal file */
1523 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1524 r = add_file(j, p, NULL, e->name);
1526 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1527 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1529 r = remove_file(j, p, NULL, e->name);
1531 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1534 } else if (e->len == 0) {
1536 /* Event for the directory itself */
1538 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
1539 remove_directory_wd(j, e->wd);
1545 /* Must be the root directory then? */
1546 p = hashmap_get(j->inotify_wd_roots, INT_TO_PTR(e->wd));
1550 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1552 /* Event for a journal file */
1554 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1555 r = add_file(j, p, NULL, e->name);
1557 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1558 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1560 r = remove_file(j, p, NULL, e->name);
1562 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1565 } else if ((e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1567 /* Event for subdirectory */
1569 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1571 r = add_directory(j, p, e->name);
1573 log_debug("Failed to add directory %s/%s: %s", p, e->name, strerror(-r));
1580 if (e->mask & IN_IGNORED)
1583 log_warning("Unknown inotify event.");
1586 _public_ int sd_journal_process(sd_journal *j) {
1587 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX];
1593 struct inotify_event *e;
1596 l = read(j->inotify_fd, buffer, sizeof(buffer));
1598 if (errno == EINTR || errno == EAGAIN)
1604 e = (struct inotify_event*) buffer;
1608 process_inotify_event(j, e);
1610 step = sizeof(struct inotify_event) + e->len;
1611 assert(step <= (size_t) l);
1613 e = (struct inotify_event*) ((uint8_t*) e + step);
1619 /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
1621 /* return -EINVAL; */
1623 /* return -EINVAL; */
1625 /* return -ENOTSUP; */
1628 /* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
1630 /* return -EINVAL; */
1632 /* return -EINVAL; */
1634 /* return -EINVAL; */
1636 /* return -ENOTSUP; */
1639 /* _public_ void sd_journal_restart_unique(sd_journal *j) { */