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);
683 k = compare_order(f, o, new_current, new_entry);
685 if (direction == DIRECTION_DOWN)
701 set_location(j, new_current, new_entry, new_offset);
706 _public_ int sd_journal_next(sd_journal *j) {
707 return real_journal_next(j, DIRECTION_DOWN);
710 _public_ int sd_journal_previous(sd_journal *j) {
711 return real_journal_next(j, DIRECTION_UP);
714 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
721 /* If this is not a discrete skip, then at least
722 * resolve the current location */
723 if (j->current_location.type != LOCATION_DISCRETE)
724 return real_journal_next(j, direction);
730 r = real_journal_next(j, direction);
744 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
745 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
748 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
749 return real_journal_next_skip(j, DIRECTION_UP, skip);
752 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
755 char bid[33], sid[33];
762 if (!j->current_file || j->current_file->current_offset <= 0)
763 return -EADDRNOTAVAIL;
765 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
769 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
770 sd_id128_to_string(o->entry.boot_id, bid);
773 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
774 sid, (unsigned long long) le64toh(o->entry.seqnum),
775 bid, (unsigned long long) le64toh(o->entry.monotonic),
776 (unsigned long long) le64toh(o->entry.realtime),
777 (unsigned long long) le64toh(o->entry.xor_hash),
778 path_get_file_name(j->current_file->path)) < 0)
784 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
788 unsigned long long seqnum, monotonic, realtime, xor_hash;
790 seqnum_id_set = false,
793 monotonic_set = false,
794 realtime_set = false,
795 xor_hash_set = false;
796 sd_id128_t seqnum_id, boot_id;
803 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
807 if (l < 2 || w[1] != '=')
810 item = strndup(w, l);
817 seqnum_id_set = true;
818 k = sd_id128_from_string(w+2, &seqnum_id);
823 if (sscanf(w+2, "%llx", &seqnum) != 1)
829 k = sd_id128_from_string(w+2, &boot_id);
833 monotonic_set = true;
834 if (sscanf(w+2, "%llx", &monotonic) != 1)
840 if (sscanf(w+2, "%llx", &realtime) != 1)
846 if (sscanf(w+2, "%llx", &xor_hash) != 1)
857 if ((!seqnum_set || !seqnum_id_set) &&
858 (!monotonic_set || !boot_id_set) &&
864 j->current_location.type = LOCATION_DISCRETE;
867 j->current_location.realtime = (uint64_t) realtime;
868 j->current_location.realtime_set = true;
871 if (seqnum_set && seqnum_id_set) {
872 j->current_location.seqnum = (uint64_t) seqnum;
873 j->current_location.seqnum_id = seqnum_id;
874 j->current_location.seqnum_set = true;
877 if (monotonic_set && boot_id_set) {
878 j->current_location.monotonic = (uint64_t) monotonic;
879 j->current_location.boot_id = boot_id;
880 j->current_location.monotonic_set = true;
884 j->current_location.xor_hash = (uint64_t) xor_hash;
885 j->current_location.xor_hash_set = true;
891 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
896 j->current_location.type = LOCATION_DISCRETE;
897 j->current_location.boot_id = boot_id;
898 j->current_location.monotonic = usec;
899 j->current_location.monotonic_set = true;
904 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
909 j->current_location.type = LOCATION_DISCRETE;
910 j->current_location.realtime = usec;
911 j->current_location.realtime_set = true;
916 _public_ int sd_journal_seek_head(sd_journal *j) {
921 j->current_location.type = LOCATION_HEAD;
926 _public_ int sd_journal_seek_tail(sd_journal *j) {
931 j->current_location.type = LOCATION_TAIL;
936 static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
945 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
946 !startswith(filename, "system.journal"))
950 fn = join(prefix, "/", dir, "/", filename, NULL);
952 fn = join(prefix, "/", filename, NULL);
957 if (hashmap_get(j->files, fn)) {
962 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
963 log_debug("Too many open journal files, not adding %s, ignoring.", fn);
968 r = journal_file_open(fn, O_RDONLY, 0, NULL, &f);
978 /* journal_file_dump(f); */
980 r = hashmap_put(j->files, f->path, f);
982 journal_file_close(f);
986 log_debug("File %s got added.", f->path);
991 static int remove_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
1000 fn = join(prefix, "/", dir, "/", filename, NULL);
1002 fn = join(prefix, "/", filename, NULL);
1007 f = hashmap_get(j->files, fn);
1013 hashmap_remove(j->files, f->path);
1014 journal_file_close(f);
1016 log_debug("File %s got removed.", f->path);
1020 static int add_directory(sd_journal *j, const char *prefix, const char *dir) {
1031 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1032 (sd_id128_from_string(dir, &id) < 0 ||
1033 sd_id128_get_machine(&mid) < 0 ||
1034 !sd_id128_equal(id, mid)))
1037 fn = join(prefix, "/", dir, NULL);
1045 if (errno == ENOENT)
1051 wd = inotify_add_watch(j->inotify_fd, fn,
1052 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1053 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
1054 IN_DONT_FOLLOW|IN_ONLYDIR);
1056 if (hashmap_put(j->inotify_wd_dirs, INT_TO_PTR(wd), fn) < 0)
1057 inotify_rm_watch(j->inotify_fd, wd);
1065 struct dirent buf, *de;
1067 r = readdir_r(d, &buf, &de);
1071 if (!dirent_is_file_with_suffix(de, ".journal"))
1074 r = add_file(j, prefix, dir, de->d_name);
1076 log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r));
1081 log_debug("Directory %s/%s got added.", prefix, dir);
1086 static void remove_directory_wd(sd_journal *j, int wd) {
1092 if (j->inotify_fd >= 0)
1093 inotify_rm_watch(j->inotify_fd, wd);
1095 p = hashmap_remove(j->inotify_wd_dirs, INT_TO_PTR(wd));
1098 log_debug("Directory %s got removed.", p);
1103 static void add_root_wd(sd_journal *j, const char *p) {
1110 wd = inotify_add_watch(j->inotify_fd, p,
1111 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1112 IN_DONT_FOLLOW|IN_ONLYDIR);
1117 if (!k || hashmap_put(j->inotify_wd_roots, INT_TO_PTR(wd), k) < 0) {
1118 inotify_rm_watch(j->inotify_fd, wd);
1123 static void remove_root_wd(sd_journal *j, int wd) {
1129 if (j->inotify_fd >= 0)
1130 inotify_rm_watch(j->inotify_fd, wd);
1132 p = hashmap_remove(j->inotify_wd_roots, INT_TO_PTR(wd));
1135 log_debug("Root %s got removed.", p);
1140 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1143 const char search_paths[] =
1144 "/run/log/journal\0"
1145 "/var/log/journal\0";
1151 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1152 SD_JOURNAL_RUNTIME_ONLY|
1153 SD_JOURNAL_SYSTEM_ONLY))
1156 j = new0(sd_journal, 1);
1162 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1163 if (j->inotify_fd < 0) {
1168 j->files = hashmap_new(string_hash_func, string_compare_func);
1174 j->inotify_wd_dirs = hashmap_new(trivial_hash_func, trivial_compare_func);
1175 j->inotify_wd_roots = hashmap_new(trivial_hash_func, trivial_compare_func);
1177 if (!j->inotify_wd_dirs || !j->inotify_wd_roots) {
1182 /* We ignore most errors here, since the idea is to only open
1183 * what's actually accessible, and ignore the rest. */
1185 NULSTR_FOREACH(p, search_paths) {
1188 if ((flags & SD_JOURNAL_RUNTIME_ONLY) &&
1189 !path_startswith(p, "/run"))
1194 if (errno != ENOENT)
1195 log_debug("Failed to open %s: %m", p);
1202 struct dirent buf, *de;
1205 r = readdir_r(d, &buf, &de);
1209 if (dirent_is_file_with_suffix(de, ".journal")) {
1210 r = add_file(j, p, NULL, de->d_name);
1212 log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r));
1214 } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
1215 sd_id128_from_string(de->d_name, &id) >= 0) {
1217 r = add_directory(j, p, de->d_name);
1219 log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r));
1230 sd_journal_close(j);
1235 _public_ void sd_journal_close(sd_journal *j) {
1239 if (j->inotify_wd_dirs) {
1242 while ((k = hashmap_first_key(j->inotify_wd_dirs)))
1243 remove_directory_wd(j, PTR_TO_INT(k));
1245 hashmap_free(j->inotify_wd_dirs);
1248 if (j->inotify_wd_roots) {
1251 while ((k = hashmap_first_key(j->inotify_wd_roots)))
1252 remove_root_wd(j, PTR_TO_INT(k));
1254 hashmap_free(j->inotify_wd_roots);
1260 while ((f = hashmap_steal_first(j->files)))
1261 journal_file_close(f);
1263 hashmap_free(j->files);
1266 sd_journal_flush_matches(j);
1268 if (j->inotify_fd >= 0)
1269 close_nointr_nofail(j->inotify_fd);
1274 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1284 f = j->current_file;
1286 return -EADDRNOTAVAIL;
1288 if (f->current_offset <= 0)
1289 return -EADDRNOTAVAIL;
1291 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1295 *ret = le64toh(o->entry.realtime);
1299 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1310 f = j->current_file;
1312 return -EADDRNOTAVAIL;
1314 if (f->current_offset <= 0)
1315 return -EADDRNOTAVAIL;
1317 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1322 *ret_boot_id = o->entry.boot_id;
1324 r = sd_id128_get_boot(&id);
1328 if (!sd_id128_equal(id, o->entry.boot_id))
1332 *ret = le64toh(o->entry.monotonic);
1336 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1339 size_t field_length;
1352 if (isempty(field) || strchr(field, '='))
1355 f = j->current_file;
1357 return -EADDRNOTAVAIL;
1359 if (f->current_offset <= 0)
1360 return -EADDRNOTAVAIL;
1362 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1366 field_length = strlen(field);
1368 n = journal_file_entry_n_items(o);
1369 for (i = 0; i < n; i++) {
1374 p = le64toh(o->entry.items[i].object_offset);
1375 le_hash = o->entry.items[i].hash;
1376 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1380 if (le_hash != o->data.hash)
1383 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1385 if (o->object.flags & OBJECT_COMPRESSED) {
1388 if (uncompress_startswith(o->data.payload, l,
1389 &f->compress_buffer, &f->compress_buffer_size,
1390 field, field_length, '=')) {
1394 if (!uncompress_blob(o->data.payload, l,
1395 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1398 *data = f->compress_buffer;
1399 *size = (size_t) rsize;
1404 return -EPROTONOSUPPORT;
1407 } else if (l >= field_length+1 &&
1408 memcmp(o->data.payload, field, field_length) == 0 &&
1409 o->data.payload[field_length] == '=') {
1413 if ((uint64_t) t != l)
1416 *data = o->data.payload;
1422 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1430 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1445 f = j->current_file;
1447 return -EADDRNOTAVAIL;
1449 if (f->current_offset <= 0)
1450 return -EADDRNOTAVAIL;
1452 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1456 n = journal_file_entry_n_items(o);
1457 if (j->current_field >= n)
1460 p = le64toh(o->entry.items[j->current_field].object_offset);
1461 le_hash = o->entry.items[j->current_field].hash;
1462 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1466 if (le_hash != o->data.hash)
1469 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1472 /* We can't read objects larger than 4G on a 32bit machine */
1473 if ((uint64_t) t != l)
1476 if (o->object.flags & OBJECT_COMPRESSED) {
1480 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1483 *data = f->compress_buffer;
1484 *size = (size_t) rsize;
1486 return -EPROTONOSUPPORT;
1489 *data = o->data.payload;
1493 j->current_field ++;
1498 _public_ void sd_journal_restart_data(sd_journal *j) {
1502 j->current_field = 0;
1505 _public_ int sd_journal_get_fd(sd_journal *j) {
1509 return j->inotify_fd;
1512 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1519 /* Is this a subdirectory we watch? */
1520 p = hashmap_get(j->inotify_wd_dirs, INT_TO_PTR(e->wd));
1523 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1525 /* Event for a journal file */
1527 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1528 r = add_file(j, p, NULL, e->name);
1530 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1531 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1533 r = remove_file(j, p, NULL, e->name);
1535 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1538 } else if (e->len == 0) {
1540 /* Event for the directory itself */
1542 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
1543 remove_directory_wd(j, e->wd);
1549 /* Must be the root directory then? */
1550 p = hashmap_get(j->inotify_wd_roots, INT_TO_PTR(e->wd));
1554 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1556 /* Event for a journal file */
1558 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1559 r = add_file(j, p, NULL, e->name);
1561 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1562 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1564 r = remove_file(j, p, NULL, e->name);
1566 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1569 } else if ((e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1571 /* Event for subdirectory */
1573 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1575 r = add_directory(j, p, e->name);
1577 log_debug("Failed to add directory %s/%s: %s", p, e->name, strerror(-r));
1584 if (e->mask & IN_IGNORED)
1587 log_warning("Unknown inotify event.");
1590 _public_ int sd_journal_process(sd_journal *j) {
1591 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX];
1597 struct inotify_event *e;
1600 l = read(j->inotify_fd, buffer, sizeof(buffer));
1602 if (errno == EINTR || errno == EAGAIN)
1608 e = (struct inotify_event*) buffer;
1612 process_inotify_event(j, e);
1614 step = sizeof(struct inotify_event) + e->len;
1615 assert(step <= (size_t) l);
1617 e = (struct inotify_event*) ((uint8_t*) e + step);
1623 /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
1625 /* return -EINVAL; */
1627 /* return -EINVAL; */
1629 /* return -ENOTSUP; */
1632 /* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
1634 /* return -EINVAL; */
1636 /* return -EINVAL; */
1638 /* return -EINVAL; */
1640 /* return -ENOTSUP; */
1643 /* _public_ void sd_journal_restart_unique(sd_journal *j) { */