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"
35 #include "journal-internal.h"
37 #define JOURNAL_FILES_MAX 1024
39 static void detach_location(sd_journal *j) {
45 j->current_file = NULL;
48 HASHMAP_FOREACH(f, j->files, i)
49 f->current_offset = 0;
52 static void reset_location(sd_journal *j) {
56 zero(j->current_location);
59 static void init_location(Location *l, JournalFile *f, Object *o) {
62 assert(o->object.type == OBJECT_ENTRY);
64 l->type = LOCATION_DISCRETE;
65 l->seqnum = le64toh(o->entry.seqnum);
66 l->seqnum_id = f->header->seqnum_id;
67 l->realtime = le64toh(o->entry.realtime);
68 l->monotonic = le64toh(o->entry.monotonic);
69 l->boot_id = o->entry.boot_id;
70 l->xor_hash = le64toh(o->entry.xor_hash);
72 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
75 static void set_location(sd_journal *j, JournalFile *f, Object *o, uint64_t offset) {
80 init_location(&j->current_location, f, o);
85 f->current_offset = offset;
88 static int same_field(const void *_a, size_t s, const void *_b, size_t t) {
89 const uint8_t *a = _a, *b = _b;
91 bool a_good = false, b_good = false, different = false;
93 for (j = 0; j < s && j < t; j++) {
102 if (a_good && b_good)
103 return different ? 0 : 1;
109 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
110 Match *m, *after = NULL;
120 le_hash = htole64(hash64(data, size));
122 LIST_FOREACH(matches, m, j->matches) {
125 if (m->le_hash == le_hash &&
127 memcmp(m->data, data, size) == 0)
130 r = same_field(data, size, m->data, m->size);
143 m->data = malloc(m->size);
149 memcpy(m->data, data, size);
150 m->le_hash = le_hash;
152 /* Matches for the same fields we order adjacent to each
154 LIST_INSERT_AFTER(Match, matches, j->matches, after, m);
162 _public_ void sd_journal_flush_matches(sd_journal *j) {
167 Match *m = j->matches;
169 LIST_REMOVE(Match, matches, j->matches, m);
179 static int compare_order(JournalFile *af, Object *ao,
180 JournalFile *bf, Object *bo) {
189 /* We operate on two different files here, hence we can access
190 * two objects at the same time, which we normally can't.
192 * If contents and timestamps match, these entries are
193 * identical, even if the seqnum does not match */
195 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
196 ao->entry.monotonic == bo->entry.monotonic &&
197 ao->entry.realtime == bo->entry.realtime &&
198 ao->entry.xor_hash == bo->entry.xor_hash)
201 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
203 /* If this is from the same seqnum source, compare
205 a = le64toh(ao->entry.seqnum);
206 b = le64toh(bo->entry.seqnum);
213 /* Wow! This is weird, different data but the same
214 * seqnums? Something is borked, but let's make the
215 * best of it and compare by time. */
218 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
220 /* If the boot id matches compare monotonic time */
221 a = le64toh(ao->entry.monotonic);
222 b = le64toh(bo->entry.monotonic);
230 /* Otherwise compare UTC time */
231 a = le64toh(ao->entry.realtime);
232 b = le64toh(ao->entry.realtime);
239 /* Finally, compare by contents */
240 a = le64toh(ao->entry.xor_hash);
241 b = le64toh(ao->entry.xor_hash);
251 static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
257 assert(l->type == LOCATION_DISCRETE);
259 if (l->monotonic_set &&
260 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
262 le64toh(ao->entry.realtime) == l->realtime &&
264 le64toh(ao->entry.xor_hash) == l->xor_hash)
268 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
270 a = le64toh(ao->entry.seqnum);
278 if (l->monotonic_set &&
279 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
281 a = le64toh(ao->entry.monotonic);
283 if (a < l->monotonic)
285 if (a > l->monotonic)
289 if (l->realtime_set) {
291 a = le64toh(ao->entry.realtime);
299 if (l->xor_hash_set) {
300 a = le64toh(ao->entry.xor_hash);
311 static int find_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
319 /* No matches is simple */
321 if (j->current_location.type == LOCATION_HEAD)
322 r = journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p);
323 else if (j->current_location.type == LOCATION_TAIL)
324 r = journal_file_next_entry(f, NULL, 0, DIRECTION_UP, &o, &p);
325 else if (j->current_location.seqnum_set &&
326 sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
327 r = journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, &o, &p);
328 else if (j->current_location.monotonic_set) {
329 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
332 /* boot id unknown in this file */
333 if (j->current_location.realtime_set)
334 r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p);
336 r = journal_file_next_entry(f, NULL, 0, direction, &o, &p);
338 } else if (j->current_location.realtime_set)
339 r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p);
341 r = journal_file_next_entry(f, NULL, 0, direction, &o, &p);
347 Match *m, *term_match = NULL;
351 /* We have matches, first, let's jump to the monotonic
352 * position if we have any, since it implies a
355 if (j->current_location.type == LOCATION_DISCRETE &&
356 j->current_location.monotonic_set) {
358 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
360 return r == -ENOENT ? 0 : r;
363 LIST_FOREACH(matches, m, j->matches) {
367 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), &d, &dp);
371 if (j->current_location.type == LOCATION_HEAD)
372 r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, &c, &cp);
373 else if (j->current_location.type == LOCATION_TAIL)
374 r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, &c, &cp);
375 else if (j->current_location.seqnum_set &&
376 sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
377 r = journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, &c, &cp);
378 else if (j->current_location.realtime_set)
379 r = journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, &c, &cp);
381 r = journal_file_next_entry_for_data(f, NULL, 0, dp, direction, &c, &cp);
393 } else if (same_field(term_match->data, term_match->size, m->data, m->size)) {
395 /* Same field as previous match... */
398 /* Find the earliest of the OR matches */
401 (direction == DIRECTION_DOWN && cp < tp) ||
402 (direction == DIRECTION_UP && cp > tp)) {
411 /* Previous term is finished, did anything match? */
415 /* Find the last of the AND matches */
417 (direction == DIRECTION_DOWN && tp > p) ||
418 (direction == DIRECTION_UP && tp < p)) {
435 /* Last term is finished, did anything match? */
440 (direction == DIRECTION_DOWN && tp > p) ||
441 (direction == DIRECTION_UP && tp < p)) {
459 static int next_with_matches(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
473 /* No matches is easy */
475 r = journal_file_next_entry(f, c, cp, direction, &c, &cp);
486 /* So there are matches we have to adhere to, let's find the
487 * first entry that matches all of them */
491 bool found, term_result = false;
492 Match *m, *term_match = NULL;
495 n = journal_file_entry_n_items(c);
497 /* Make sure we don't match the entry we are starting
499 found = cp != *offset;
502 LIST_FOREACH(matches, m, j->matches) {
506 /* Let's check if this is the beginning of a
507 * new term, i.e. has a different field prefix
508 * as the preceeding match. */
512 } else if (!same_field(term_match->data, term_match->size, m->data, m->size)) {
520 for (k = 0; k < n; k++)
521 if (c->entry.items[k].hash == m->le_hash)
525 /* Hmm, didn't find any field that
526 * matched this rule, so ignore this
527 * match. Go on with next match */
533 /* Hmm, so, this field matched, let's remember
534 * where we'd have to try next, in case the other
535 * matches are not OK */
537 r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, &qo, &q);
538 /* This pointer is invalidated if the window was
539 * remapped. May need to re-fetch it later */
546 if (direction == DIRECTION_DOWN) {
552 if (np == 0 || q < np) {
560 /* Check the last term */
561 if (term_match && !term_result)
564 /* Did this entry match against all matches? */
568 /* Re-fetch the entry */
569 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
580 /* Did we find a subsequent entry? */
584 /* Hmm, ok, this entry only matched partially, so
585 * let's try another one */
591 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
594 int compare_value, r;
599 if (f->current_offset > 0) {
600 cp = f->current_offset;
602 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
606 r = next_with_matches(j, f, direction, &c, &cp);
612 r = find_location(j, f, direction, &c, &cp);
622 if (j->current_location.type == LOCATION_DISCRETE) {
625 k = compare_with_location(f, c, &j->current_location);
626 if (direction == DIRECTION_DOWN)
627 found = k >= compare_value;
629 found = k <= -compare_value;
641 r = next_with_matches(j, f, direction, &c, &cp);
647 static int real_journal_next(sd_journal *j, direction_t direction) {
648 JournalFile *f, *new_current = NULL;
651 uint64_t new_offset = 0;
652 Object *new_entry = NULL;
657 HASHMAP_FOREACH(f, j->files, i) {
662 r = next_beyond_location(j, f, direction, &o, &p);
673 k = compare_order(f, o, new_current, new_entry);
675 if (direction == DIRECTION_DOWN)
691 set_location(j, new_current, new_entry, new_offset);
696 _public_ int sd_journal_next(sd_journal *j) {
697 return real_journal_next(j, DIRECTION_DOWN);
700 _public_ int sd_journal_previous(sd_journal *j) {
701 return real_journal_next(j, DIRECTION_UP);
704 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
711 /* If this is not a discrete skip, then at least
712 * resolve the current location */
713 if (j->current_location.type != LOCATION_DISCRETE)
714 return real_journal_next(j, direction);
720 r = real_journal_next(j, direction);
734 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
735 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
738 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
739 return real_journal_next_skip(j, DIRECTION_UP, skip);
742 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
745 char bid[33], sid[33];
752 if (!j->current_file || j->current_file->current_offset <= 0)
753 return -EADDRNOTAVAIL;
755 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
759 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
760 sd_id128_to_string(o->entry.boot_id, bid);
763 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
764 sid, (unsigned long long) le64toh(o->entry.seqnum),
765 bid, (unsigned long long) le64toh(o->entry.monotonic),
766 (unsigned long long) le64toh(o->entry.realtime),
767 (unsigned long long) le64toh(o->entry.xor_hash),
768 file_name_from_path(j->current_file->path)) < 0)
774 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
778 unsigned long long seqnum, monotonic, realtime, xor_hash;
780 seqnum_id_set = false,
783 monotonic_set = false,
784 realtime_set = false,
785 xor_hash_set = false;
786 sd_id128_t seqnum_id, boot_id;
793 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
797 if (l < 2 || w[1] != '=')
800 item = strndup(w, l);
807 seqnum_id_set = true;
808 k = sd_id128_from_string(w+2, &seqnum_id);
813 if (sscanf(w+2, "%llx", &seqnum) != 1)
819 k = sd_id128_from_string(w+2, &boot_id);
823 monotonic_set = true;
824 if (sscanf(w+2, "%llx", &monotonic) != 1)
830 if (sscanf(w+2, "%llx", &realtime) != 1)
836 if (sscanf(w+2, "%llx", &xor_hash) != 1)
847 if ((!seqnum_set || !seqnum_id_set) &&
848 (!monotonic_set || !boot_id_set) &&
854 j->current_location.type = LOCATION_DISCRETE;
857 j->current_location.realtime = (uint64_t) realtime;
858 j->current_location.realtime_set = true;
861 if (seqnum_set && seqnum_id_set) {
862 j->current_location.seqnum = (uint64_t) seqnum;
863 j->current_location.seqnum_id = seqnum_id;
864 j->current_location.seqnum_set = true;
867 if (monotonic_set && boot_id_set) {
868 j->current_location.monotonic = (uint64_t) monotonic;
869 j->current_location.boot_id = boot_id;
870 j->current_location.monotonic_set = true;
874 j->current_location.xor_hash = (uint64_t) xor_hash;
875 j->current_location.xor_hash_set = true;
881 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
886 j->current_location.type = LOCATION_DISCRETE;
887 j->current_location.boot_id = boot_id;
888 j->current_location.monotonic = usec;
889 j->current_location.monotonic_set = true;
894 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
899 j->current_location.type = LOCATION_DISCRETE;
900 j->current_location.realtime = usec;
901 j->current_location.realtime_set = true;
906 _public_ int sd_journal_seek_head(sd_journal *j) {
911 j->current_location.type = LOCATION_HEAD;
916 _public_ int sd_journal_seek_tail(sd_journal *j) {
921 j->current_location.type = LOCATION_TAIL;
926 static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
935 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
936 !startswith(filename, "system.journal"))
940 fn = join(prefix, "/", dir, "/", filename, NULL);
942 fn = join(prefix, "/", filename, NULL);
947 if (hashmap_get(j->files, fn)) {
952 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
953 log_debug("Too many open journal files, not adding %s, ignoring.", fn);
958 r = journal_file_open(fn, O_RDONLY, 0, NULL, &f);
968 /* journal_file_dump(f); */
970 r = hashmap_put(j->files, f->path, f);
972 journal_file_close(f);
976 log_debug("File %s got added.", f->path);
981 static int remove_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
990 fn = join(prefix, "/", dir, "/", filename, NULL);
992 fn = join(prefix, "/", filename, NULL);
997 f = hashmap_get(j->files, fn);
1003 hashmap_remove(j->files, f->path);
1004 journal_file_close(f);
1006 log_debug("File %s got removed.", f->path);
1010 static int add_directory(sd_journal *j, const char *prefix, const char *dir) {
1021 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1022 (sd_id128_from_string(dir, &id) < 0 ||
1023 sd_id128_get_machine(&mid) < 0 ||
1024 !sd_id128_equal(id, mid)))
1027 fn = join(prefix, "/", dir, NULL);
1035 if (errno == ENOENT)
1041 wd = inotify_add_watch(j->inotify_fd, fn,
1042 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1043 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
1044 IN_DONT_FOLLOW|IN_ONLYDIR);
1046 if (hashmap_put(j->inotify_wd_dirs, INT_TO_PTR(wd), fn) < 0)
1047 inotify_rm_watch(j->inotify_fd, wd);
1055 struct dirent buf, *de;
1057 r = readdir_r(d, &buf, &de);
1061 if (!dirent_is_file_with_suffix(de, ".journal"))
1064 r = add_file(j, prefix, dir, de->d_name);
1066 log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r));
1071 log_debug("Directory %s/%s got added.", prefix, dir);
1076 static void remove_directory_wd(sd_journal *j, int wd) {
1082 if (j->inotify_fd >= 0)
1083 inotify_rm_watch(j->inotify_fd, wd);
1085 p = hashmap_remove(j->inotify_wd_dirs, INT_TO_PTR(wd));
1088 log_debug("Directory %s got removed.", p);
1093 static void add_root_wd(sd_journal *j, const char *p) {
1100 wd = inotify_add_watch(j->inotify_fd, p,
1101 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1102 IN_DONT_FOLLOW|IN_ONLYDIR);
1107 if (!k || hashmap_put(j->inotify_wd_roots, INT_TO_PTR(wd), k) < 0) {
1108 inotify_rm_watch(j->inotify_fd, wd);
1113 static void remove_root_wd(sd_journal *j, int wd) {
1119 if (j->inotify_fd >= 0)
1120 inotify_rm_watch(j->inotify_fd, wd);
1122 p = hashmap_remove(j->inotify_wd_roots, INT_TO_PTR(wd));
1125 log_debug("Root %s got removed.", p);
1130 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1133 const char search_paths[] =
1134 "/run/log/journal\0"
1135 "/var/log/journal\0";
1141 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1142 SD_JOURNAL_RUNTIME_ONLY|
1143 SD_JOURNAL_SYSTEM_ONLY))
1146 j = new0(sd_journal, 1);
1152 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1153 if (j->inotify_fd < 0) {
1158 j->files = hashmap_new(string_hash_func, string_compare_func);
1164 j->inotify_wd_dirs = hashmap_new(trivial_hash_func, trivial_compare_func);
1165 j->inotify_wd_roots = hashmap_new(trivial_hash_func, trivial_compare_func);
1167 if (!j->inotify_wd_dirs || !j->inotify_wd_roots) {
1172 /* We ignore most errors here, since the idea is to only open
1173 * what's actually accessible, and ignore the rest. */
1175 NULSTR_FOREACH(p, search_paths) {
1178 if ((flags & SD_JOURNAL_RUNTIME_ONLY) &&
1179 !path_startswith(p, "/run"))
1184 if (errno != ENOENT)
1185 log_debug("Failed to open %s: %m", p);
1192 struct dirent buf, *de;
1195 r = readdir_r(d, &buf, &de);
1199 if (dirent_is_file_with_suffix(de, ".journal")) {
1200 r = add_file(j, p, NULL, de->d_name);
1202 log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r));
1204 } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
1205 sd_id128_from_string(de->d_name, &id) >= 0) {
1207 r = add_directory(j, p, de->d_name);
1209 log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r));
1220 sd_journal_close(j);
1225 _public_ void sd_journal_close(sd_journal *j) {
1229 if (j->inotify_wd_dirs) {
1232 while ((k = hashmap_first_key(j->inotify_wd_dirs)))
1233 remove_directory_wd(j, PTR_TO_INT(k));
1235 hashmap_free(j->inotify_wd_dirs);
1238 if (j->inotify_wd_roots) {
1241 while ((k = hashmap_first_key(j->inotify_wd_roots)))
1242 remove_root_wd(j, PTR_TO_INT(k));
1244 hashmap_free(j->inotify_wd_roots);
1250 while ((f = hashmap_steal_first(j->files)))
1251 journal_file_close(f);
1253 hashmap_free(j->files);
1256 sd_journal_flush_matches(j);
1258 if (j->inotify_fd >= 0)
1259 close_nointr_nofail(j->inotify_fd);
1264 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1274 f = j->current_file;
1276 return -EADDRNOTAVAIL;
1278 if (f->current_offset <= 0)
1279 return -EADDRNOTAVAIL;
1281 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1285 *ret = le64toh(o->entry.realtime);
1289 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1300 f = j->current_file;
1302 return -EADDRNOTAVAIL;
1304 if (f->current_offset <= 0)
1305 return -EADDRNOTAVAIL;
1307 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1312 *ret_boot_id = o->entry.boot_id;
1314 r = sd_id128_get_boot(&id);
1318 if (!sd_id128_equal(id, o->entry.boot_id))
1322 *ret = le64toh(o->entry.monotonic);
1326 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1329 size_t field_length;
1342 if (isempty(field) || strchr(field, '='))
1345 f = j->current_file;
1347 return -EADDRNOTAVAIL;
1349 if (f->current_offset <= 0)
1350 return -EADDRNOTAVAIL;
1352 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1356 field_length = strlen(field);
1358 n = journal_file_entry_n_items(o);
1359 for (i = 0; i < n; i++) {
1364 p = le64toh(o->entry.items[i].object_offset);
1365 le_hash = o->entry.items[i].hash;
1366 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1370 if (le_hash != o->data.hash)
1373 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1375 if (o->object.flags & OBJECT_COMPRESSED) {
1378 if (uncompress_startswith(o->data.payload, l,
1379 &f->compress_buffer, &f->compress_buffer_size,
1380 field, field_length, '=')) {
1384 if (!uncompress_blob(o->data.payload, l,
1385 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1388 *data = f->compress_buffer;
1389 *size = (size_t) rsize;
1394 return -EPROTONOSUPPORT;
1397 } else if (l >= field_length+1 &&
1398 memcmp(o->data.payload, field, field_length) == 0 &&
1399 o->data.payload[field_length] == '=') {
1403 if ((uint64_t) t != l)
1406 *data = o->data.payload;
1412 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1420 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1435 f = j->current_file;
1437 return -EADDRNOTAVAIL;
1439 if (f->current_offset <= 0)
1440 return -EADDRNOTAVAIL;
1442 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1446 n = journal_file_entry_n_items(o);
1447 if (j->current_field >= n)
1450 p = le64toh(o->entry.items[j->current_field].object_offset);
1451 le_hash = o->entry.items[j->current_field].hash;
1452 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1456 if (le_hash != o->data.hash)
1459 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1462 /* We can't read objects larger than 4G on a 32bit machine */
1463 if ((uint64_t) t != l)
1466 if (o->object.flags & OBJECT_COMPRESSED) {
1470 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1473 *data = f->compress_buffer;
1474 *size = (size_t) rsize;
1476 return -EPROTONOSUPPORT;
1479 *data = o->data.payload;
1483 j->current_field ++;
1488 _public_ void sd_journal_restart_data(sd_journal *j) {
1492 j->current_field = 0;
1495 _public_ int sd_journal_get_fd(sd_journal *j) {
1499 return j->inotify_fd;
1502 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1509 /* Is this a subdirectory we watch? */
1510 p = hashmap_get(j->inotify_wd_dirs, INT_TO_PTR(e->wd));
1513 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1515 /* Event for a journal file */
1517 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1518 r = add_file(j, p, NULL, e->name);
1520 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1521 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1523 r = remove_file(j, p, NULL, e->name);
1525 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1528 } else if (e->len == 0) {
1530 /* Event for the directory itself */
1532 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
1533 remove_directory_wd(j, e->wd);
1539 /* Must be the root directory then? */
1540 p = hashmap_get(j->inotify_wd_roots, INT_TO_PTR(e->wd));
1544 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1546 /* Event for a journal file */
1548 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1549 r = add_file(j, p, NULL, e->name);
1551 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1552 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1554 r = remove_file(j, p, NULL, e->name);
1556 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1559 } else if ((e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1561 /* Event for subdirectory */
1563 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1565 r = add_directory(j, p, e->name);
1567 log_debug("Failed to add directory %s/%s: %s", p, e->name, strerror(-r));
1574 if (e->mask & IN_IGNORED)
1577 log_warning("Unknown inotify event.");
1580 _public_ int sd_journal_process(sd_journal *j) {
1581 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX];
1587 struct inotify_event *e;
1590 l = read(j->inotify_fd, buffer, sizeof(buffer));
1592 if (errno == EINTR || errno == EAGAIN)
1598 e = (struct inotify_event*) buffer;
1602 process_inotify_event(j, e);
1604 step = sizeof(struct inotify_event) + e->len;
1605 assert(step <= (size_t) l);
1607 e = (struct inotify_event*) ((uint8_t*) e + step);
1613 /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
1615 /* return -EINVAL; */
1617 /* return -EINVAL; */
1619 /* return -ENOTSUP; */
1622 /* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
1624 /* return -EINVAL; */
1626 /* return -EINVAL; */
1628 /* return -EINVAL; */
1630 /* return -ENOTSUP; */
1633 /* _public_ void sd_journal_restart_unique(sd_journal *j) { */