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_order(JournalFile *af, Object *ao,
361 JournalFile *bf, Object *bo) {
370 /* We operate on two different files here, hence we can access
371 * two objects at the same time, which we normally can't.
373 * If contents and timestamps match, these entries are
374 * identical, even if the seqnum does not match */
376 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
377 ao->entry.monotonic == bo->entry.monotonic &&
378 ao->entry.realtime == bo->entry.realtime &&
379 ao->entry.xor_hash == bo->entry.xor_hash)
382 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
384 /* If this is from the same seqnum source, compare
386 a = le64toh(ao->entry.seqnum);
387 b = le64toh(bo->entry.seqnum);
394 /* Wow! This is weird, different data but the same
395 * seqnums? Something is borked, but let's make the
396 * best of it and compare by time. */
399 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
401 /* If the boot id matches compare monotonic time */
402 a = le64toh(ao->entry.monotonic);
403 b = le64toh(bo->entry.monotonic);
411 /* Otherwise compare UTC time */
412 a = le64toh(ao->entry.realtime);
413 b = le64toh(bo->entry.realtime);
420 /* Finally, compare by contents */
421 a = le64toh(ao->entry.xor_hash);
422 b = le64toh(bo->entry.xor_hash);
432 static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
438 assert(l->type == LOCATION_DISCRETE);
440 if (l->monotonic_set &&
441 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
443 le64toh(ao->entry.realtime) == l->realtime &&
445 le64toh(ao->entry.xor_hash) == l->xor_hash)
449 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
451 a = le64toh(ao->entry.seqnum);
459 if (l->monotonic_set &&
460 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
462 a = le64toh(ao->entry.monotonic);
464 if (a < l->monotonic)
466 if (a > l->monotonic)
470 if (l->realtime_set) {
472 a = le64toh(ao->entry.realtime);
480 if (l->xor_hash_set) {
481 a = le64toh(ao->entry.xor_hash);
492 static int next_for_match(
496 uint64_t after_offset,
497 direction_t direction,
509 if (m->type == MATCH_DISCRETE) {
512 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
516 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
518 } else if (m->type == MATCH_OR_TERM) {
521 /* Find the earliest match beyond after_offset */
523 LIST_FOREACH(matches, i, m->matches) {
526 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
530 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
535 } else if (m->type == MATCH_AND_TERM) {
537 bool continue_looking;
539 /* Always jump to the next matching entry and repeat
540 * this until we fine and offset that matches for all
548 continue_looking = false;
550 LIST_FOREACH(matches, i, m->matches) {
554 limit = after_offset;
555 else if (direction == DIRECTION_DOWN)
556 limit = MAX(np, after_offset);
558 limit = MIN(np, after_offset);
560 r = next_for_match(j, i, f, limit, direction, NULL, &cp);
564 if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) &&
565 (np == 0 || (direction == DIRECTION_DOWN ? cp > np : np < cp))) {
567 continue_looking = true;
571 } while (continue_looking);
577 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
589 static int find_location_for_match(
593 direction_t direction,
603 if (m->type == MATCH_DISCRETE) {
606 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
610 /* FIXME: missing: find by monotonic */
612 if (j->current_location.type == LOCATION_HEAD)
613 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
614 if (j->current_location.type == LOCATION_TAIL)
615 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
616 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
617 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
618 if (j->current_location.monotonic_set) {
619 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
623 if (j->current_location.realtime_set)
624 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
626 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
628 } else if (m->type == MATCH_OR_TERM) {
633 /* Find the earliest match */
635 LIST_FOREACH(matches, i, m->matches) {
638 r = find_location_for_match(j, i, f, direction, NULL, &cp);
642 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
650 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
665 assert(m->type == MATCH_AND_TERM);
667 /* First jump to the last match, and then find the
668 * next one where all matches match */
673 LIST_FOREACH(matches, i, m->matches) {
676 r = find_location_for_match(j, i, f, direction, NULL, &cp);
680 if (np == 0 || (direction == DIRECTION_DOWN ? np < cp : np > cp))
684 return next_for_match(j, m, f, np, direction, ret, offset);
688 static int find_location_with_matches(
691 direction_t direction,
703 /* No matches is simple */
705 if (j->current_location.type == LOCATION_HEAD)
706 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
707 if (j->current_location.type == LOCATION_TAIL)
708 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
709 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
710 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
711 if (j->current_location.monotonic_set) {
712 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
716 if (j->current_location.realtime_set)
717 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
719 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
721 return find_location_for_match(j, j->level0, f, direction, ret, offset);
724 static int next_with_matches(
727 direction_t direction,
742 /* No matches is easy. We simple advance the file
745 return journal_file_next_entry(f, c, cp, direction, ret, offset);
747 /* If we have a match then we look for the next matching entry
748 * with an offset at least one step larger */
749 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
752 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
760 if (f->current_offset > 0) {
761 cp = f->current_offset;
763 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
767 r = next_with_matches(j, f, direction, &c, &cp);
771 r = find_location_with_matches(j, f, direction, &c, &cp);
776 /* OK, we found the spot, now let's advance until to an entry
777 * that is actually different from what we were previously
778 * looking at. This is necessary to handle entries which exist
779 * in two (or more) journal files, and which shall all be
780 * suppressed but one. */
785 if (j->current_location.type == LOCATION_DISCRETE) {
788 k = compare_with_location(f, c, &j->current_location);
789 if (direction == DIRECTION_DOWN)
804 r = next_with_matches(j, f, direction, &c, &cp);
810 static int real_journal_next(sd_journal *j, direction_t direction) {
811 JournalFile *f, *new_current = NULL;
814 uint64_t new_offset = 0;
815 Object *new_entry = NULL;
820 HASHMAP_FOREACH(f, j->files, i) {
825 r = next_beyond_location(j, f, direction, &o, &p);
827 log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
837 k = compare_order(f, o, new_current, new_entry);
839 if (direction == DIRECTION_DOWN)
855 set_location(j, new_current, new_entry, new_offset);
860 _public_ int sd_journal_next(sd_journal *j) {
861 return real_journal_next(j, DIRECTION_DOWN);
864 _public_ int sd_journal_previous(sd_journal *j) {
865 return real_journal_next(j, DIRECTION_UP);
868 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
875 /* If this is not a discrete skip, then at least
876 * resolve the current location */
877 if (j->current_location.type != LOCATION_DISCRETE)
878 return real_journal_next(j, direction);
884 r = real_journal_next(j, direction);
898 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
899 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
902 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
903 return real_journal_next_skip(j, DIRECTION_UP, skip);
906 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
909 char bid[33], sid[33];
916 if (!j->current_file || j->current_file->current_offset <= 0)
917 return -EADDRNOTAVAIL;
919 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
923 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
924 sd_id128_to_string(o->entry.boot_id, bid);
927 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
928 sid, (unsigned long long) le64toh(o->entry.seqnum),
929 bid, (unsigned long long) le64toh(o->entry.monotonic),
930 (unsigned long long) le64toh(o->entry.realtime),
931 (unsigned long long) le64toh(o->entry.xor_hash),
932 path_get_file_name(j->current_file->path)) < 0)
938 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
942 unsigned long long seqnum, monotonic, realtime, xor_hash;
944 seqnum_id_set = false,
947 monotonic_set = false,
948 realtime_set = false,
949 xor_hash_set = false;
950 sd_id128_t seqnum_id, boot_id;
957 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
961 if (l < 2 || w[1] != '=')
964 item = strndup(w, l);
971 seqnum_id_set = true;
972 k = sd_id128_from_string(w+2, &seqnum_id);
977 if (sscanf(w+2, "%llx", &seqnum) != 1)
983 k = sd_id128_from_string(w+2, &boot_id);
987 monotonic_set = true;
988 if (sscanf(w+2, "%llx", &monotonic) != 1)
994 if (sscanf(w+2, "%llx", &realtime) != 1)
1000 if (sscanf(w+2, "%llx", &xor_hash) != 1)
1011 if ((!seqnum_set || !seqnum_id_set) &&
1012 (!monotonic_set || !boot_id_set) &&
1018 j->current_location.type = LOCATION_DISCRETE;
1021 j->current_location.realtime = (uint64_t) realtime;
1022 j->current_location.realtime_set = true;
1025 if (seqnum_set && seqnum_id_set) {
1026 j->current_location.seqnum = (uint64_t) seqnum;
1027 j->current_location.seqnum_id = seqnum_id;
1028 j->current_location.seqnum_set = true;
1031 if (monotonic_set && boot_id_set) {
1032 j->current_location.monotonic = (uint64_t) monotonic;
1033 j->current_location.boot_id = boot_id;
1034 j->current_location.monotonic_set = true;
1038 j->current_location.xor_hash = (uint64_t) xor_hash;
1039 j->current_location.xor_hash_set = true;
1045 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1050 j->current_location.type = LOCATION_DISCRETE;
1051 j->current_location.boot_id = boot_id;
1052 j->current_location.monotonic = usec;
1053 j->current_location.monotonic_set = true;
1058 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1063 j->current_location.type = LOCATION_DISCRETE;
1064 j->current_location.realtime = usec;
1065 j->current_location.realtime_set = true;
1070 _public_ int sd_journal_seek_head(sd_journal *j) {
1075 j->current_location.type = LOCATION_HEAD;
1080 _public_ int sd_journal_seek_tail(sd_journal *j) {
1085 j->current_location.type = LOCATION_TAIL;
1090 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1099 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
1100 !(streq(filename, "system.journal") ||
1101 streq(filename, "system.journal~") ||
1102 (startswith(filename, "system@") &&
1103 (endswith(filename, ".journal") || endswith(filename, ".journal~")))))
1106 path = strjoin(prefix, "/", filename, NULL);
1110 if (hashmap_get(j->files, path)) {
1115 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1116 log_debug("Too many open journal files, not adding %s, ignoring.", path);
1121 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1125 if (errno == ENOENT)
1131 /* journal_file_dump(f); */
1133 r = hashmap_put(j->files, f->path, f);
1135 journal_file_close(f);
1139 j->current_invalidate_counter ++;
1141 log_debug("File %s got added.", f->path);
1146 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1154 path = strjoin(prefix, "/", filename, NULL);
1158 f = hashmap_get(j->files, path);
1163 hashmap_remove(j->files, f->path);
1164 journal_file_close(f);
1166 j->current_invalidate_counter ++;
1168 log_debug("File %s got removed.", f->path);
1172 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1183 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1184 (sd_id128_from_string(dirname, &id) < 0 ||
1185 sd_id128_get_machine(&mid) < 0 ||
1186 !sd_id128_equal(id, mid)))
1189 path = strjoin(prefix, "/", dirname, NULL);
1195 log_debug("Failed to open %s: %m", path);
1198 if (errno == ENOENT)
1203 m = hashmap_get(j->directories_by_path, path);
1205 m = new0(Directory, 1);
1215 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1222 j->current_invalidate_counter ++;
1224 log_debug("Directory %s got added.", m->path);
1226 } else if (m->is_root) {
1233 if (m->wd <= 0 && j->inotify_fd >= 0) {
1235 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1236 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1237 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
1240 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1241 inotify_rm_watch(j->inotify_fd, m->wd);
1245 struct dirent buf, *de;
1247 r = readdir_r(d, &buf, &de);
1251 if (dirent_is_file_with_suffix(de, ".journal") ||
1252 dirent_is_file_with_suffix(de, ".journal~")) {
1253 r = add_file(j, m->path, de->d_name);
1255 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1264 static int add_root_directory(sd_journal *j, const char *p) {
1272 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1273 !path_startswith(p, "/run"))
1280 m = hashmap_get(j->directories_by_path, p);
1282 m = new0(Directory, 1);
1289 m->path = strdup(p);
1296 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1303 j->current_invalidate_counter ++;
1305 log_debug("Root directory %s got added.", m->path);
1307 } else if (!m->is_root) {
1312 if (m->wd <= 0 && j->inotify_fd >= 0) {
1314 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1315 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1318 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1319 inotify_rm_watch(j->inotify_fd, m->wd);
1323 struct dirent buf, *de;
1326 r = readdir_r(d, &buf, &de);
1330 if (dirent_is_file_with_suffix(de, ".journal") ||
1331 dirent_is_file_with_suffix(de, ".journal~")) {
1332 r = add_file(j, m->path, de->d_name);
1334 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1336 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1337 sd_id128_from_string(de->d_name, &id) >= 0) {
1339 r = add_directory(j, m->path, de->d_name);
1341 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1350 static int remove_directory(sd_journal *j, Directory *d) {
1354 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1356 if (j->inotify_fd >= 0)
1357 inotify_rm_watch(j->inotify_fd, d->wd);
1360 hashmap_remove(j->directories_by_path, d->path);
1363 log_debug("Root directory %s got removed.", d->path);
1365 log_debug("Directory %s got removed.", d->path);
1373 static int add_search_paths(sd_journal *j) {
1375 const char search_paths[] =
1376 "/run/log/journal\0"
1377 "/var/log/journal\0";
1382 /* We ignore most errors here, since the idea is to only open
1383 * what's actually accessible, and ignore the rest. */
1385 NULSTR_FOREACH(p, search_paths)
1386 add_root_directory(j, p);
1391 static int allocate_inotify(sd_journal *j) {
1394 if (j->inotify_fd < 0) {
1395 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1396 if (j->inotify_fd < 0)
1400 if (!j->directories_by_wd) {
1401 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1402 if (!j->directories_by_wd)
1409 static sd_journal *journal_new(int flags, const char *path) {
1412 j = new0(sd_journal, 1);
1420 j->path = strdup(path);
1427 j->files = hashmap_new(string_hash_func, string_compare_func);
1434 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1435 if (!j->directories_by_path) {
1436 hashmap_free(j->files);
1442 /* One context for each type, plus the zeroth catchall
1443 * context. One fd for each file plus one for each type, which
1444 * is need when verifying things */
1445 j->mmap = mmap_cache_new(_OBJECT_TYPE_MAX, JOURNAL_FILES_MAX + _OBJECT_TYPE_MAX);
1447 hashmap_free(j->files);
1448 hashmap_free(j->directories_by_path);
1456 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1463 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1464 SD_JOURNAL_RUNTIME_ONLY|
1465 SD_JOURNAL_SYSTEM_ONLY))
1468 j = journal_new(flags, NULL);
1472 r = add_search_paths(j);
1480 sd_journal_close(j);
1485 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1492 if (!path || !path_is_absolute(path))
1498 j = journal_new(flags, path);
1502 r = add_root_directory(j, path);
1510 sd_journal_close(j);
1515 _public_ void sd_journal_close(sd_journal *j) {
1522 while ((f = hashmap_steal_first(j->files)))
1523 journal_file_close(f);
1525 hashmap_free(j->files);
1527 while ((d = hashmap_first(j->directories_by_path)))
1528 remove_directory(j, d);
1530 while ((d = hashmap_first(j->directories_by_wd)))
1531 remove_directory(j, d);
1533 hashmap_free(j->directories_by_path);
1534 hashmap_free(j->directories_by_wd);
1536 if (j->inotify_fd >= 0)
1537 close_nointr_nofail(j->inotify_fd);
1539 sd_journal_flush_matches(j);
1542 mmap_cache_unref(j->mmap);
1548 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1558 f = j->current_file;
1560 return -EADDRNOTAVAIL;
1562 if (f->current_offset <= 0)
1563 return -EADDRNOTAVAIL;
1565 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1569 *ret = le64toh(o->entry.realtime);
1573 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1582 f = j->current_file;
1584 return -EADDRNOTAVAIL;
1586 if (f->current_offset <= 0)
1587 return -EADDRNOTAVAIL;
1589 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1594 *ret_boot_id = o->entry.boot_id;
1596 r = sd_id128_get_boot(&id);
1600 if (!sd_id128_equal(id, o->entry.boot_id))
1605 *ret = le64toh(o->entry.monotonic);
1610 static bool field_is_valid(const char *field) {
1618 if (startswith(field, "__"))
1621 for (p = field; *p; p++) {
1626 if (*p >= 'A' && *p <= 'Z')
1629 if (*p >= '0' && *p <= '9')
1638 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1641 size_t field_length;
1654 if (!field_is_valid(field))
1657 f = j->current_file;
1659 return -EADDRNOTAVAIL;
1661 if (f->current_offset <= 0)
1662 return -EADDRNOTAVAIL;
1664 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1668 field_length = strlen(field);
1670 n = journal_file_entry_n_items(o);
1671 for (i = 0; i < n; i++) {
1676 p = le64toh(o->entry.items[i].object_offset);
1677 le_hash = o->entry.items[i].hash;
1678 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1682 if (le_hash != o->data.hash)
1685 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1687 if (o->object.flags & OBJECT_COMPRESSED) {
1690 if (uncompress_startswith(o->data.payload, l,
1691 &f->compress_buffer, &f->compress_buffer_size,
1692 field, field_length, '=')) {
1696 if (!uncompress_blob(o->data.payload, l,
1697 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1700 *data = f->compress_buffer;
1701 *size = (size_t) rsize;
1706 return -EPROTONOSUPPORT;
1709 } else if (l >= field_length+1 &&
1710 memcmp(o->data.payload, field, field_length) == 0 &&
1711 o->data.payload[field_length] == '=') {
1715 if ((uint64_t) t != l)
1718 *data = o->data.payload;
1724 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1732 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1747 f = j->current_file;
1749 return -EADDRNOTAVAIL;
1751 if (f->current_offset <= 0)
1752 return -EADDRNOTAVAIL;
1754 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1758 n = journal_file_entry_n_items(o);
1759 if (j->current_field >= n)
1762 p = le64toh(o->entry.items[j->current_field].object_offset);
1763 le_hash = o->entry.items[j->current_field].hash;
1764 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1768 if (le_hash != o->data.hash)
1771 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1774 /* We can't read objects larger than 4G on a 32bit machine */
1775 if ((uint64_t) t != l)
1778 if (o->object.flags & OBJECT_COMPRESSED) {
1782 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1785 *data = f->compress_buffer;
1786 *size = (size_t) rsize;
1788 return -EPROTONOSUPPORT;
1791 *data = o->data.payload;
1795 j->current_field ++;
1800 _public_ void sd_journal_restart_data(sd_journal *j) {
1804 j->current_field = 0;
1807 _public_ int sd_journal_get_fd(sd_journal *j) {
1813 if (j->inotify_fd >= 0)
1814 return j->inotify_fd;
1816 r = allocate_inotify(j);
1820 /* Iterate through all dirs again, to add them to the
1823 r = add_root_directory(j, j->path);
1825 r = add_search_paths(j);
1829 return j->inotify_fd;
1832 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1839 /* Is this a subdirectory we watch? */
1840 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
1844 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
1845 (endswith(e->name, ".journal") ||
1846 endswith(e->name, ".journal~"))) {
1848 /* Event for a journal file */
1850 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1851 r = add_file(j, d->path, e->name);
1853 log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r));
1855 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1857 r = remove_file(j, d->path, e->name);
1859 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
1862 } else if (!d->is_root && e->len == 0) {
1864 /* Event for a subdirectory */
1866 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
1867 r = remove_directory(j, d);
1869 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
1873 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1875 /* Event for root directory */
1877 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1878 r = add_directory(j, d->path, e->name);
1880 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
1887 if (e->mask & IN_IGNORED)
1890 log_warning("Unknown inotify event.");
1893 static int determine_change(sd_journal *j) {
1898 b = j->current_invalidate_counter != j->last_invalidate_counter;
1899 j->last_invalidate_counter = j->current_invalidate_counter;
1901 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
1904 _public_ int sd_journal_process(sd_journal *j) {
1905 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
1906 bool got_something = false;
1912 struct inotify_event *e;
1915 l = read(j->inotify_fd, buffer, sizeof(buffer));
1917 if (errno == EAGAIN || errno == EINTR)
1918 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
1923 got_something = true;
1925 e = (struct inotify_event*) buffer;
1929 process_inotify_event(j, e);
1931 step = sizeof(struct inotify_event) + e->len;
1932 assert(step <= (size_t) l);
1934 e = (struct inotify_event*) ((uint8_t*) e + step);
1939 return determine_change(j);
1942 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
1947 if (j->inotify_fd < 0) {
1949 /* This is the first invocation, hence create the
1951 r = sd_journal_get_fd(j);
1955 /* The journal might have changed since the context
1956 * object was created and we weren't watching before,
1957 * hence don't wait for anything, and return
1959 return determine_change(j);
1963 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
1964 } while (r == -EINTR);
1969 return sd_journal_process(j);
1972 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
1983 HASHMAP_FOREACH(f, j->files, i) {
1986 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2002 *from = MIN(fr, *from);
2008 return first ? 0 : 1;
2011 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2022 HASHMAP_FOREACH(f, j->files, i) {
2025 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2041 *from = MIN(fr, *from);
2047 return first ? 0 : 1;
2050 void journal_print_header(sd_journal *j) {
2053 bool newline = false;
2057 HASHMAP_FOREACH(f, j->files, i) {
2063 journal_file_print_header(f);
2067 /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
2069 /* return -EINVAL; */
2071 /* return -EINVAL; */
2073 /* return -ENOTSUP; */
2076 /* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
2078 /* return -EINVAL; */
2080 /* return -EINVAL; */
2082 /* return -EINVAL; */
2084 /* return -ENOTSUP; */
2087 /* _public_ void sd_journal_restart_unique(sd_journal *j) { */