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 !(streq(filename, "system.journal") ||
947 (startswith(filename, "system@") && endswith(filename, ".journal"))))
951 fn = join(prefix, "/", dir, "/", filename, NULL);
953 fn = join(prefix, "/", filename, NULL);
958 if (hashmap_get(j->files, fn)) {
963 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
964 log_debug("Too many open journal files, not adding %s, ignoring.", fn);
969 r = journal_file_open(fn, O_RDONLY, 0, NULL, &f);
979 /* journal_file_dump(f); */
981 r = hashmap_put(j->files, f->path, f);
983 journal_file_close(f);
987 log_debug("File %s got added.", f->path);
992 static int remove_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
1001 fn = join(prefix, "/", dir, "/", filename, NULL);
1003 fn = join(prefix, "/", filename, NULL);
1008 f = hashmap_get(j->files, fn);
1014 hashmap_remove(j->files, f->path);
1015 journal_file_close(f);
1017 log_debug("File %s got removed.", f->path);
1021 static int add_directory(sd_journal *j, const char *prefix, const char *dir) {
1032 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1033 (sd_id128_from_string(dir, &id) < 0 ||
1034 sd_id128_get_machine(&mid) < 0 ||
1035 !sd_id128_equal(id, mid)))
1038 fn = join(prefix, "/", dir, NULL);
1046 if (errno == ENOENT)
1052 wd = inotify_add_watch(j->inotify_fd, fn,
1053 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1054 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
1055 IN_DONT_FOLLOW|IN_ONLYDIR);
1057 if (hashmap_put(j->inotify_wd_dirs, INT_TO_PTR(wd), fn) < 0)
1058 inotify_rm_watch(j->inotify_fd, wd);
1066 struct dirent buf, *de;
1068 r = readdir_r(d, &buf, &de);
1072 if (!dirent_is_file_with_suffix(de, ".journal"))
1075 r = add_file(j, prefix, dir, de->d_name);
1077 log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r));
1082 log_debug("Directory %s/%s got added.", prefix, dir);
1087 static void remove_directory_wd(sd_journal *j, int wd) {
1093 if (j->inotify_fd >= 0)
1094 inotify_rm_watch(j->inotify_fd, wd);
1096 p = hashmap_remove(j->inotify_wd_dirs, INT_TO_PTR(wd));
1099 log_debug("Directory %s got removed.", p);
1104 static void add_root_wd(sd_journal *j, const char *p) {
1111 wd = inotify_add_watch(j->inotify_fd, p,
1112 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1113 IN_DONT_FOLLOW|IN_ONLYDIR);
1118 if (!k || hashmap_put(j->inotify_wd_roots, INT_TO_PTR(wd), k) < 0) {
1119 inotify_rm_watch(j->inotify_fd, wd);
1124 static void remove_root_wd(sd_journal *j, int wd) {
1130 if (j->inotify_fd >= 0)
1131 inotify_rm_watch(j->inotify_fd, wd);
1133 p = hashmap_remove(j->inotify_wd_roots, INT_TO_PTR(wd));
1136 log_debug("Root %s got removed.", p);
1141 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1144 const char search_paths[] =
1145 "/run/log/journal\0"
1146 "/var/log/journal\0";
1152 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1153 SD_JOURNAL_RUNTIME_ONLY|
1154 SD_JOURNAL_SYSTEM_ONLY))
1157 j = new0(sd_journal, 1);
1163 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1164 if (j->inotify_fd < 0) {
1169 j->files = hashmap_new(string_hash_func, string_compare_func);
1175 j->inotify_wd_dirs = hashmap_new(trivial_hash_func, trivial_compare_func);
1176 j->inotify_wd_roots = hashmap_new(trivial_hash_func, trivial_compare_func);
1178 if (!j->inotify_wd_dirs || !j->inotify_wd_roots) {
1183 /* We ignore most errors here, since the idea is to only open
1184 * what's actually accessible, and ignore the rest. */
1186 NULSTR_FOREACH(p, search_paths) {
1189 if ((flags & SD_JOURNAL_RUNTIME_ONLY) &&
1190 !path_startswith(p, "/run"))
1195 if (errno != ENOENT)
1196 log_debug("Failed to open %s: %m", p);
1203 struct dirent buf, *de;
1206 r = readdir_r(d, &buf, &de);
1210 if (dirent_is_file_with_suffix(de, ".journal")) {
1211 r = add_file(j, p, NULL, de->d_name);
1213 log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r));
1215 } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
1216 sd_id128_from_string(de->d_name, &id) >= 0) {
1218 r = add_directory(j, p, de->d_name);
1220 log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r));
1231 sd_journal_close(j);
1236 _public_ void sd_journal_close(sd_journal *j) {
1240 if (j->inotify_wd_dirs) {
1243 while ((k = hashmap_first_key(j->inotify_wd_dirs)))
1244 remove_directory_wd(j, PTR_TO_INT(k));
1246 hashmap_free(j->inotify_wd_dirs);
1249 if (j->inotify_wd_roots) {
1252 while ((k = hashmap_first_key(j->inotify_wd_roots)))
1253 remove_root_wd(j, PTR_TO_INT(k));
1255 hashmap_free(j->inotify_wd_roots);
1261 while ((f = hashmap_steal_first(j->files)))
1262 journal_file_close(f);
1264 hashmap_free(j->files);
1267 sd_journal_flush_matches(j);
1269 if (j->inotify_fd >= 0)
1270 close_nointr_nofail(j->inotify_fd);
1275 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1285 f = j->current_file;
1287 return -EADDRNOTAVAIL;
1289 if (f->current_offset <= 0)
1290 return -EADDRNOTAVAIL;
1292 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1296 *ret = le64toh(o->entry.realtime);
1300 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1311 f = j->current_file;
1313 return -EADDRNOTAVAIL;
1315 if (f->current_offset <= 0)
1316 return -EADDRNOTAVAIL;
1318 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1323 *ret_boot_id = o->entry.boot_id;
1325 r = sd_id128_get_boot(&id);
1329 if (!sd_id128_equal(id, o->entry.boot_id))
1333 *ret = le64toh(o->entry.monotonic);
1337 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1340 size_t field_length;
1353 if (isempty(field) || strchr(field, '='))
1356 f = j->current_file;
1358 return -EADDRNOTAVAIL;
1360 if (f->current_offset <= 0)
1361 return -EADDRNOTAVAIL;
1363 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1367 field_length = strlen(field);
1369 n = journal_file_entry_n_items(o);
1370 for (i = 0; i < n; i++) {
1375 p = le64toh(o->entry.items[i].object_offset);
1376 le_hash = o->entry.items[i].hash;
1377 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1381 if (le_hash != o->data.hash)
1384 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1386 if (o->object.flags & OBJECT_COMPRESSED) {
1389 if (uncompress_startswith(o->data.payload, l,
1390 &f->compress_buffer, &f->compress_buffer_size,
1391 field, field_length, '=')) {
1395 if (!uncompress_blob(o->data.payload, l,
1396 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1399 *data = f->compress_buffer;
1400 *size = (size_t) rsize;
1405 return -EPROTONOSUPPORT;
1408 } else if (l >= field_length+1 &&
1409 memcmp(o->data.payload, field, field_length) == 0 &&
1410 o->data.payload[field_length] == '=') {
1414 if ((uint64_t) t != l)
1417 *data = o->data.payload;
1423 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1431 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1446 f = j->current_file;
1448 return -EADDRNOTAVAIL;
1450 if (f->current_offset <= 0)
1451 return -EADDRNOTAVAIL;
1453 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1457 n = journal_file_entry_n_items(o);
1458 if (j->current_field >= n)
1461 p = le64toh(o->entry.items[j->current_field].object_offset);
1462 le_hash = o->entry.items[j->current_field].hash;
1463 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1467 if (le_hash != o->data.hash)
1470 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1473 /* We can't read objects larger than 4G on a 32bit machine */
1474 if ((uint64_t) t != l)
1477 if (o->object.flags & OBJECT_COMPRESSED) {
1481 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1484 *data = f->compress_buffer;
1485 *size = (size_t) rsize;
1487 return -EPROTONOSUPPORT;
1490 *data = o->data.payload;
1494 j->current_field ++;
1499 _public_ void sd_journal_restart_data(sd_journal *j) {
1503 j->current_field = 0;
1506 _public_ int sd_journal_get_fd(sd_journal *j) {
1510 return j->inotify_fd;
1513 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1520 /* Is this a subdirectory we watch? */
1521 p = hashmap_get(j->inotify_wd_dirs, INT_TO_PTR(e->wd));
1524 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1526 /* Event for a journal file */
1528 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1529 r = add_file(j, p, NULL, e->name);
1531 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1532 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1534 r = remove_file(j, p, NULL, e->name);
1536 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1539 } else if (e->len == 0) {
1541 /* Event for the directory itself */
1543 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
1544 remove_directory_wd(j, e->wd);
1550 /* Must be the root directory then? */
1551 p = hashmap_get(j->inotify_wd_roots, INT_TO_PTR(e->wd));
1555 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1557 /* Event for a journal file */
1559 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1560 r = add_file(j, p, NULL, e->name);
1562 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1563 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1565 r = remove_file(j, p, NULL, e->name);
1567 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1570 } else if ((e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1572 /* Event for subdirectory */
1574 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1576 r = add_directory(j, p, e->name);
1578 log_debug("Failed to add directory %s/%s: %s", p, e->name, strerror(-r));
1585 if (e->mask & IN_IGNORED)
1588 log_warning("Unknown inotify event.");
1591 _public_ int sd_journal_process(sd_journal *j) {
1592 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX];
1598 struct inotify_event *e;
1601 l = read(j->inotify_fd, buffer, sizeof(buffer));
1603 if (errno == EINTR || errno == EAGAIN)
1609 e = (struct inotify_event*) buffer;
1613 process_inotify_event(j, e);
1615 step = sizeof(struct inotify_event) + e->len;
1616 assert(step <= (size_t) l);
1618 e = (struct inotify_event*) ((uint8_t*) e + step);
1624 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
1635 HASHMAP_FOREACH(f, j->files, i) {
1638 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
1652 *from = MIN(fr, *from);
1658 return first ? 0 : 1;
1661 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
1672 HASHMAP_FOREACH(f, j->files, i) {
1675 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
1689 *from = MIN(fr, *from);
1695 return first ? 0 : 1;
1699 /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
1701 /* return -EINVAL; */
1703 /* return -EINVAL; */
1705 /* return -ENOTSUP; */
1708 /* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
1710 /* return -EINVAL; */
1712 /* return -EINVAL; */
1714 /* return -EINVAL; */
1716 /* return -ENOTSUP; */
1719 /* _public_ void sd_journal_restart_unique(sd_journal *j) { */