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>
29 #include "sd-journal.h"
30 #include "journal-def.h"
31 #include "journal-file.h"
34 #include "path-util.h"
37 #include "journal-internal.h"
39 #define JOURNAL_FILES_MAX 1024
41 static void detach_location(sd_journal *j) {
47 j->current_file = NULL;
50 HASHMAP_FOREACH(f, j->files, i)
51 f->current_offset = 0;
54 static void reset_location(sd_journal *j) {
58 zero(j->current_location);
61 static void init_location(Location *l, JournalFile *f, Object *o) {
64 assert(o->object.type == OBJECT_ENTRY);
66 l->type = LOCATION_DISCRETE;
67 l->seqnum = le64toh(o->entry.seqnum);
68 l->seqnum_id = f->header->seqnum_id;
69 l->realtime = le64toh(o->entry.realtime);
70 l->monotonic = le64toh(o->entry.monotonic);
71 l->boot_id = o->entry.boot_id;
72 l->xor_hash = le64toh(o->entry.xor_hash);
74 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
77 static void set_location(sd_journal *j, JournalFile *f, Object *o, uint64_t offset) {
82 init_location(&j->current_location, f, o);
87 f->current_offset = offset;
90 static int match_is_valid(const void *data, size_t size) {
98 if (startswith(data, "__"))
102 for (p = b; p < b + size; p++) {
110 if (*p >= 'A' && *p <= 'Z')
113 if (*p >= '0' && *p <= '9')
122 static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
123 const uint8_t *a = _a, *b = _b;
126 for (j = 0; j < s && j < t; j++) {
138 static Match *match_new(Match *p, MatchType t) {
149 LIST_PREPEND(Match, matches, p->matches, m);
155 static void match_free(Match *m) {
159 match_free(m->matches);
162 LIST_REMOVE(Match, matches, m->parent->matches, m);
168 static void match_free_if_empty(Match *m) {
177 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
178 Match *l2, *l3, *add_here = NULL, *m;
190 if (!match_is_valid(data, size))
196 * level 3: concrete matches */
199 j->level0 = match_new(NULL, MATCH_OR_TERM);
205 j->level1 = match_new(j->level0, MATCH_AND_TERM);
210 assert(j->level0->type == MATCH_OR_TERM);
211 assert(j->level1->type == MATCH_AND_TERM);
213 le_hash = htole64(hash64(data, size));
215 LIST_FOREACH(matches, l2, j->level1->matches) {
216 assert(l2->type == MATCH_OR_TERM);
218 LIST_FOREACH(matches, l3, l2->matches) {
219 assert(l3->type == MATCH_DISCRETE);
221 /* Exactly the same match already? Then ignore
223 if (l3->le_hash == le_hash &&
225 memcmp(l3->data, data, size) == 0)
228 /* Same field? Then let's add this to this OR term */
229 if (same_field(data, size, l3->data, l3->size)) {
240 add_here = match_new(j->level1, MATCH_OR_TERM);
245 m = match_new(add_here, MATCH_DISCRETE);
249 m->le_hash = le_hash;
251 m->data = memdup(data, size);
261 match_free_if_empty(add_here);
264 match_free_if_empty(j->level1);
267 match_free_if_empty(j->level0);
272 _public_ int sd_journal_add_disjunction(sd_journal *j) {
283 if (!j->level1->matches)
286 m = match_new(j->level0, MATCH_AND_TERM);
294 static char *match_make_string(Match *m) {
297 bool enclose = false;
302 if (m->type == MATCH_DISCRETE)
303 return strndup(m->data, m->size);
306 LIST_FOREACH(matches, i, m->matches) {
309 t = match_make_string(i);
316 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
333 r = strjoin("(", p, ")", NULL);
341 char *journal_make_match_string(sd_journal *j) {
344 return match_make_string(j->level0);
347 _public_ void sd_journal_flush_matches(sd_journal *j) {
353 match_free(j->level0);
355 j->level0 = j->level1 = NULL;
360 static int compare_entry_order(JournalFile *af, Object *_ao,
361 JournalFile *bf, uint64_t bp) {
371 /* The mmap cache might invalidate the object from the first
372 * file if we look at the one from the second file. Hence
373 * temporarily copy the header of the first one, and look at
375 ao = alloca(offsetof(EntryObject, items));
376 memcpy(ao, _ao, offsetof(EntryObject, items));
378 r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
380 return strcmp(af->path, bf->path);
382 /* We operate on two different files here, hence we can access
383 * two objects at the same time, which we normally can't.
385 * If contents and timestamps match, these entries are
386 * identical, even if the seqnum does not match */
388 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
389 ao->entry.monotonic == bo->entry.monotonic &&
390 ao->entry.realtime == bo->entry.realtime &&
391 ao->entry.xor_hash == bo->entry.xor_hash)
394 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
396 /* If this is from the same seqnum source, compare
398 a = le64toh(ao->entry.seqnum);
399 b = le64toh(bo->entry.seqnum);
406 /* Wow! This is weird, different data but the same
407 * seqnums? Something is borked, but let's make the
408 * best of it and compare by time. */
411 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
413 /* If the boot id matches compare monotonic time */
414 a = le64toh(ao->entry.monotonic);
415 b = le64toh(bo->entry.monotonic);
423 /* Otherwise compare UTC time */
424 a = le64toh(ao->entry.realtime);
425 b = le64toh(bo->entry.realtime);
432 /* Finally, compare by contents */
433 a = le64toh(ao->entry.xor_hash);
434 b = le64toh(bo->entry.xor_hash);
444 static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
450 assert(l->type == LOCATION_DISCRETE);
452 if (l->monotonic_set &&
453 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
455 le64toh(ao->entry.realtime) == l->realtime &&
457 le64toh(ao->entry.xor_hash) == l->xor_hash)
461 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
463 a = le64toh(ao->entry.seqnum);
471 if (l->monotonic_set &&
472 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
474 a = le64toh(ao->entry.monotonic);
476 if (a < l->monotonic)
478 if (a > l->monotonic)
482 if (l->realtime_set) {
484 a = le64toh(ao->entry.realtime);
492 if (l->xor_hash_set) {
493 a = le64toh(ao->entry.xor_hash);
504 static int next_for_match(
508 uint64_t after_offset,
509 direction_t direction,
521 if (m->type == MATCH_DISCRETE) {
524 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
528 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
530 } else if (m->type == MATCH_OR_TERM) {
533 /* Find the earliest match beyond after_offset */
535 LIST_FOREACH(matches, i, m->matches) {
538 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
542 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
547 } else if (m->type == MATCH_AND_TERM) {
549 bool continue_looking;
551 /* Always jump to the next matching entry and repeat
552 * this until we fine and offset that matches for all
560 continue_looking = false;
562 LIST_FOREACH(matches, i, m->matches) {
566 limit = after_offset;
567 else if (direction == DIRECTION_DOWN)
568 limit = MAX(np, after_offset);
570 limit = MIN(np, after_offset);
572 r = next_for_match(j, i, f, limit, direction, NULL, &cp);
576 if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) &&
577 (np == 0 || (direction == DIRECTION_DOWN ? cp > np : np < cp))) {
579 continue_looking = true;
583 } while (continue_looking);
589 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
601 static int find_location_for_match(
605 direction_t direction,
615 if (m->type == MATCH_DISCRETE) {
618 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
622 /* FIXME: missing: find by monotonic */
624 if (j->current_location.type == LOCATION_HEAD)
625 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
626 if (j->current_location.type == LOCATION_TAIL)
627 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
628 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
629 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
630 if (j->current_location.monotonic_set) {
631 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
635 if (j->current_location.realtime_set)
636 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
638 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
640 } else if (m->type == MATCH_OR_TERM) {
645 /* Find the earliest match */
647 LIST_FOREACH(matches, i, m->matches) {
650 r = find_location_for_match(j, i, f, direction, NULL, &cp);
654 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
662 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
677 assert(m->type == MATCH_AND_TERM);
679 /* First jump to the last match, and then find the
680 * next one where all matches match */
685 LIST_FOREACH(matches, i, m->matches) {
688 r = find_location_for_match(j, i, f, direction, NULL, &cp);
692 if (np == 0 || (direction == DIRECTION_DOWN ? np < cp : np > cp))
696 return next_for_match(j, m, f, np, direction, ret, offset);
700 static int find_location_with_matches(
703 direction_t direction,
715 /* No matches is simple */
717 if (j->current_location.type == LOCATION_HEAD)
718 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
719 if (j->current_location.type == LOCATION_TAIL)
720 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
721 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
722 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
723 if (j->current_location.monotonic_set) {
724 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
728 if (j->current_location.realtime_set)
729 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
731 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
733 return find_location_for_match(j, j->level0, f, direction, ret, offset);
736 static int next_with_matches(
739 direction_t direction,
754 /* No matches is easy. We simple advance the file
757 return journal_file_next_entry(f, c, cp, direction, ret, offset);
759 /* If we have a match then we look for the next matching entry
760 * with an offset at least one step larger */
761 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
764 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
772 if (f->current_offset > 0) {
773 cp = f->current_offset;
775 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
779 r = next_with_matches(j, f, direction, &c, &cp);
783 r = find_location_with_matches(j, f, direction, &c, &cp);
788 /* OK, we found the spot, now let's advance until to an entry
789 * that is actually different from what we were previously
790 * looking at. This is necessary to handle entries which exist
791 * in two (or more) journal files, and which shall all be
792 * suppressed but one. */
797 if (j->current_location.type == LOCATION_DISCRETE) {
800 k = compare_with_location(f, c, &j->current_location);
801 if (direction == DIRECTION_DOWN)
816 r = next_with_matches(j, f, direction, &c, &cp);
822 static int real_journal_next(sd_journal *j, direction_t direction) {
823 JournalFile *f, *new_file = NULL;
824 uint64_t new_offset = 0;
833 HASHMAP_FOREACH(f, j->files, i) {
836 r = next_beyond_location(j, f, direction, &o, &p);
838 log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
848 k = compare_entry_order(f, o, new_file, new_offset);
850 if (direction == DIRECTION_DOWN)
865 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
869 set_location(j, new_file, o, new_offset);
874 _public_ int sd_journal_next(sd_journal *j) {
875 return real_journal_next(j, DIRECTION_DOWN);
878 _public_ int sd_journal_previous(sd_journal *j) {
879 return real_journal_next(j, DIRECTION_UP);
882 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
889 /* If this is not a discrete skip, then at least
890 * resolve the current location */
891 if (j->current_location.type != LOCATION_DISCRETE)
892 return real_journal_next(j, direction);
898 r = real_journal_next(j, direction);
912 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
913 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
916 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
917 return real_journal_next_skip(j, DIRECTION_UP, skip);
920 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
923 char bid[33], sid[33];
930 if (!j->current_file || j->current_file->current_offset <= 0)
931 return -EADDRNOTAVAIL;
933 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
937 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
938 sd_id128_to_string(o->entry.boot_id, bid);
941 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
942 sid, (unsigned long long) le64toh(o->entry.seqnum),
943 bid, (unsigned long long) le64toh(o->entry.monotonic),
944 (unsigned long long) le64toh(o->entry.realtime),
945 (unsigned long long) le64toh(o->entry.xor_hash),
946 path_get_file_name(j->current_file->path)) < 0)
952 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
956 unsigned long long seqnum, monotonic, realtime, xor_hash;
958 seqnum_id_set = false,
961 monotonic_set = false,
962 realtime_set = false,
963 xor_hash_set = false;
964 sd_id128_t seqnum_id, boot_id;
971 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
975 if (l < 2 || w[1] != '=')
978 item = strndup(w, l);
985 seqnum_id_set = true;
986 k = sd_id128_from_string(item+2, &seqnum_id);
991 if (sscanf(item+2, "%llx", &seqnum) != 1)
997 k = sd_id128_from_string(item+2, &boot_id);
1001 monotonic_set = true;
1002 if (sscanf(item+2, "%llx", &monotonic) != 1)
1007 realtime_set = true;
1008 if (sscanf(item+2, "%llx", &realtime) != 1)
1013 xor_hash_set = true;
1014 if (sscanf(item+2, "%llx", &xor_hash) != 1)
1025 if ((!seqnum_set || !seqnum_id_set) &&
1026 (!monotonic_set || !boot_id_set) &&
1032 j->current_location.type = LOCATION_DISCRETE;
1035 j->current_location.realtime = (uint64_t) realtime;
1036 j->current_location.realtime_set = true;
1039 if (seqnum_set && seqnum_id_set) {
1040 j->current_location.seqnum = (uint64_t) seqnum;
1041 j->current_location.seqnum_id = seqnum_id;
1042 j->current_location.seqnum_set = true;
1045 if (monotonic_set && boot_id_set) {
1046 j->current_location.monotonic = (uint64_t) monotonic;
1047 j->current_location.boot_id = boot_id;
1048 j->current_location.monotonic_set = true;
1052 j->current_location.xor_hash = (uint64_t) xor_hash;
1053 j->current_location.xor_hash_set = true;
1059 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1064 j->current_location.type = LOCATION_DISCRETE;
1065 j->current_location.boot_id = boot_id;
1066 j->current_location.monotonic = usec;
1067 j->current_location.monotonic_set = true;
1072 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1077 j->current_location.type = LOCATION_DISCRETE;
1078 j->current_location.realtime = usec;
1079 j->current_location.realtime_set = true;
1084 _public_ int sd_journal_seek_head(sd_journal *j) {
1089 j->current_location.type = LOCATION_HEAD;
1094 _public_ int sd_journal_seek_tail(sd_journal *j) {
1099 j->current_location.type = LOCATION_TAIL;
1104 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1113 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
1114 !(streq(filename, "system.journal") ||
1115 streq(filename, "system.journal~") ||
1116 (startswith(filename, "system@") &&
1117 (endswith(filename, ".journal") || endswith(filename, ".journal~")))))
1120 path = strjoin(prefix, "/", filename, NULL);
1124 if (hashmap_get(j->files, path)) {
1129 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1130 log_debug("Too many open journal files, not adding %s, ignoring.", path);
1135 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1139 if (errno == ENOENT)
1145 /* journal_file_dump(f); */
1147 r = hashmap_put(j->files, f->path, f);
1149 journal_file_close(f);
1153 j->current_invalidate_counter ++;
1155 log_debug("File %s got added.", f->path);
1160 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1168 path = strjoin(prefix, "/", filename, NULL);
1172 f = hashmap_get(j->files, path);
1177 hashmap_remove(j->files, f->path);
1178 journal_file_close(f);
1180 j->current_invalidate_counter ++;
1182 log_debug("File %s got removed.", f->path);
1186 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1197 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1198 (sd_id128_from_string(dirname, &id) < 0 ||
1199 sd_id128_get_machine(&mid) < 0 ||
1200 !sd_id128_equal(id, mid)))
1203 path = strjoin(prefix, "/", dirname, NULL);
1209 log_debug("Failed to open %s: %m", path);
1212 if (errno == ENOENT)
1217 m = hashmap_get(j->directories_by_path, path);
1219 m = new0(Directory, 1);
1229 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1236 j->current_invalidate_counter ++;
1238 log_debug("Directory %s got added.", m->path);
1240 } else if (m->is_root) {
1247 if (m->wd <= 0 && j->inotify_fd >= 0) {
1249 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1250 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1251 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
1254 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1255 inotify_rm_watch(j->inotify_fd, m->wd);
1260 union dirent_storage buf;
1262 r = readdir_r(d, &buf.de, &de);
1266 if (dirent_is_file_with_suffix(de, ".journal") ||
1267 dirent_is_file_with_suffix(de, ".journal~")) {
1268 r = add_file(j, m->path, de->d_name);
1270 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1279 static int add_root_directory(sd_journal *j, const char *p) {
1287 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1288 !path_startswith(p, "/run"))
1295 m = hashmap_get(j->directories_by_path, p);
1297 m = new0(Directory, 1);
1304 m->path = strdup(p);
1311 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1318 j->current_invalidate_counter ++;
1320 log_debug("Root directory %s got added.", m->path);
1322 } else if (!m->is_root) {
1327 if (m->wd <= 0 && j->inotify_fd >= 0) {
1329 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1330 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1333 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1334 inotify_rm_watch(j->inotify_fd, m->wd);
1339 union dirent_storage buf;
1342 r = readdir_r(d, &buf.de, &de);
1346 if (dirent_is_file_with_suffix(de, ".journal") ||
1347 dirent_is_file_with_suffix(de, ".journal~")) {
1348 r = add_file(j, m->path, de->d_name);
1350 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1352 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1353 sd_id128_from_string(de->d_name, &id) >= 0) {
1355 r = add_directory(j, m->path, de->d_name);
1357 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1366 static int remove_directory(sd_journal *j, Directory *d) {
1370 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1372 if (j->inotify_fd >= 0)
1373 inotify_rm_watch(j->inotify_fd, d->wd);
1376 hashmap_remove(j->directories_by_path, d->path);
1379 log_debug("Root directory %s got removed.", d->path);
1381 log_debug("Directory %s got removed.", d->path);
1389 static int add_search_paths(sd_journal *j) {
1391 const char search_paths[] =
1392 "/run/log/journal\0"
1393 "/var/log/journal\0";
1398 /* We ignore most errors here, since the idea is to only open
1399 * what's actually accessible, and ignore the rest. */
1401 NULSTR_FOREACH(p, search_paths)
1402 add_root_directory(j, p);
1407 static int allocate_inotify(sd_journal *j) {
1410 if (j->inotify_fd < 0) {
1411 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1412 if (j->inotify_fd < 0)
1416 if (!j->directories_by_wd) {
1417 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1418 if (!j->directories_by_wd)
1425 static sd_journal *journal_new(int flags, const char *path) {
1428 j = new0(sd_journal, 1);
1436 j->path = strdup(path);
1443 j->files = hashmap_new(string_hash_func, string_compare_func);
1450 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1451 if (!j->directories_by_path) {
1452 hashmap_free(j->files);
1458 j->mmap = mmap_cache_new();
1460 hashmap_free(j->files);
1461 hashmap_free(j->directories_by_path);
1470 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1477 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1478 SD_JOURNAL_RUNTIME_ONLY|
1479 SD_JOURNAL_SYSTEM_ONLY))
1482 j = journal_new(flags, NULL);
1486 r = add_search_paths(j);
1494 sd_journal_close(j);
1499 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1506 if (!path || !path_is_absolute(path))
1512 j = journal_new(flags, path);
1516 r = add_root_directory(j, path);
1524 sd_journal_close(j);
1529 _public_ void sd_journal_close(sd_journal *j) {
1536 while ((f = hashmap_steal_first(j->files)))
1537 journal_file_close(f);
1539 hashmap_free(j->files);
1541 while ((d = hashmap_first(j->directories_by_path)))
1542 remove_directory(j, d);
1544 while ((d = hashmap_first(j->directories_by_wd)))
1545 remove_directory(j, d);
1547 hashmap_free(j->directories_by_path);
1548 hashmap_free(j->directories_by_wd);
1550 if (j->inotify_fd >= 0)
1551 close_nointr_nofail(j->inotify_fd);
1553 sd_journal_flush_matches(j);
1556 mmap_cache_unref(j->mmap);
1562 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1572 f = j->current_file;
1574 return -EADDRNOTAVAIL;
1576 if (f->current_offset <= 0)
1577 return -EADDRNOTAVAIL;
1579 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1583 *ret = le64toh(o->entry.realtime);
1587 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1596 f = j->current_file;
1598 return -EADDRNOTAVAIL;
1600 if (f->current_offset <= 0)
1601 return -EADDRNOTAVAIL;
1603 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1608 *ret_boot_id = o->entry.boot_id;
1610 r = sd_id128_get_boot(&id);
1614 if (!sd_id128_equal(id, o->entry.boot_id))
1619 *ret = le64toh(o->entry.monotonic);
1624 static bool field_is_valid(const char *field) {
1632 if (startswith(field, "__"))
1635 for (p = field; *p; p++) {
1640 if (*p >= 'A' && *p <= 'Z')
1643 if (*p >= '0' && *p <= '9')
1652 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1655 size_t field_length;
1668 if (!field_is_valid(field))
1671 f = j->current_file;
1673 return -EADDRNOTAVAIL;
1675 if (f->current_offset <= 0)
1676 return -EADDRNOTAVAIL;
1678 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1682 field_length = strlen(field);
1684 n = journal_file_entry_n_items(o);
1685 for (i = 0; i < n; i++) {
1690 p = le64toh(o->entry.items[i].object_offset);
1691 le_hash = o->entry.items[i].hash;
1692 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1696 if (le_hash != o->data.hash)
1699 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1701 if (o->object.flags & OBJECT_COMPRESSED) {
1704 if (uncompress_startswith(o->data.payload, l,
1705 &f->compress_buffer, &f->compress_buffer_size,
1706 field, field_length, '=')) {
1710 if (!uncompress_blob(o->data.payload, l,
1711 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1714 *data = f->compress_buffer;
1715 *size = (size_t) rsize;
1720 return -EPROTONOSUPPORT;
1723 } else if (l >= field_length+1 &&
1724 memcmp(o->data.payload, field, field_length) == 0 &&
1725 o->data.payload[field_length] == '=') {
1729 if ((uint64_t) t != l)
1732 *data = o->data.payload;
1738 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1746 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1761 f = j->current_file;
1763 return -EADDRNOTAVAIL;
1765 if (f->current_offset <= 0)
1766 return -EADDRNOTAVAIL;
1768 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1772 n = journal_file_entry_n_items(o);
1773 if (j->current_field >= n)
1776 p = le64toh(o->entry.items[j->current_field].object_offset);
1777 le_hash = o->entry.items[j->current_field].hash;
1778 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1782 if (le_hash != o->data.hash)
1785 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1788 /* We can't read objects larger than 4G on a 32bit machine */
1789 if ((uint64_t) t != l)
1792 if (o->object.flags & OBJECT_COMPRESSED) {
1796 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1799 *data = f->compress_buffer;
1800 *size = (size_t) rsize;
1802 return -EPROTONOSUPPORT;
1805 *data = o->data.payload;
1809 j->current_field ++;
1814 _public_ void sd_journal_restart_data(sd_journal *j) {
1818 j->current_field = 0;
1821 _public_ int sd_journal_get_fd(sd_journal *j) {
1827 if (j->inotify_fd >= 0)
1828 return j->inotify_fd;
1830 r = allocate_inotify(j);
1834 /* Iterate through all dirs again, to add them to the
1837 r = add_root_directory(j, j->path);
1839 r = add_search_paths(j);
1843 return j->inotify_fd;
1846 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1853 /* Is this a subdirectory we watch? */
1854 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
1858 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
1859 (endswith(e->name, ".journal") ||
1860 endswith(e->name, ".journal~"))) {
1862 /* Event for a journal file */
1864 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1865 r = add_file(j, d->path, e->name);
1867 log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r));
1869 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1871 r = remove_file(j, d->path, e->name);
1873 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
1876 } else if (!d->is_root && e->len == 0) {
1878 /* Event for a subdirectory */
1880 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
1881 r = remove_directory(j, d);
1883 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
1887 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1889 /* Event for root directory */
1891 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1892 r = add_directory(j, d->path, e->name);
1894 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
1901 if (e->mask & IN_IGNORED)
1904 log_warning("Unknown inotify event.");
1907 static int determine_change(sd_journal *j) {
1912 b = j->current_invalidate_counter != j->last_invalidate_counter;
1913 j->last_invalidate_counter = j->current_invalidate_counter;
1915 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
1918 _public_ int sd_journal_process(sd_journal *j) {
1919 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
1920 bool got_something = false;
1926 struct inotify_event *e;
1929 l = read(j->inotify_fd, buffer, sizeof(buffer));
1931 if (errno == EAGAIN || errno == EINTR)
1932 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
1937 got_something = true;
1939 e = (struct inotify_event*) buffer;
1943 process_inotify_event(j, e);
1945 step = sizeof(struct inotify_event) + e->len;
1946 assert(step <= (size_t) l);
1948 e = (struct inotify_event*) ((uint8_t*) e + step);
1953 return determine_change(j);
1956 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
1961 if (j->inotify_fd < 0) {
1963 /* This is the first invocation, hence create the
1965 r = sd_journal_get_fd(j);
1969 /* The journal might have changed since the context
1970 * object was created and we weren't watching before,
1971 * hence don't wait for anything, and return
1973 return determine_change(j);
1977 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
1978 } while (r == -EINTR);
1983 return sd_journal_process(j);
1986 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
1997 HASHMAP_FOREACH(f, j->files, i) {
2000 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2016 *from = MIN(fr, *from);
2022 return first ? 0 : 1;
2025 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2036 HASHMAP_FOREACH(f, j->files, i) {
2039 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2055 *from = MIN(fr, *from);
2061 return first ? 0 : 1;
2064 void journal_print_header(sd_journal *j) {
2067 bool newline = false;
2071 HASHMAP_FOREACH(f, j->files, i) {
2077 journal_file_print_header(f);
2081 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2091 HASHMAP_FOREACH(f, j->files, i) {
2094 if (fstat(f->fd, &st) < 0)
2097 sum += (uint64_t) st.st_blocks * 512ULL;
2104 /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
2106 /* return -EINVAL; */
2108 /* return -EINVAL; */
2110 /* return -ENOTSUP; */
2113 /* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
2115 /* return -EINVAL; */
2117 /* return -EINVAL; */
2119 /* return -EINVAL; */
2121 /* return -ENOTSUP; */
2124 /* _public_ void sd_journal_restart_unique(sd_journal *j) { */