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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
18 You should have received a copy of the GNU 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) { */