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 <linux/magic.h>
31 #include "sd-journal.h"
32 #include "journal-def.h"
33 #include "journal-file.h"
36 #include "path-util.h"
39 #include "journal-internal.h"
42 #define JOURNAL_FILES_MAX 1024
44 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
46 static void detach_location(sd_journal *j) {
52 j->current_file = NULL;
55 HASHMAP_FOREACH(f, j->files, i)
56 f->current_offset = 0;
59 static void reset_location(sd_journal *j) {
63 zero(j->current_location);
66 static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
68 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
70 assert(o->object.type == OBJECT_ENTRY);
73 l->seqnum = le64toh(o->entry.seqnum);
74 l->seqnum_id = f->header->seqnum_id;
75 l->realtime = le64toh(o->entry.realtime);
76 l->monotonic = le64toh(o->entry.monotonic);
77 l->boot_id = o->entry.boot_id;
78 l->xor_hash = le64toh(o->entry.xor_hash);
80 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
83 static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o, uint64_t offset) {
85 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
89 init_location(&j->current_location, type, f, o);
94 f->current_offset = offset;
97 static int match_is_valid(const void *data, size_t size) {
105 if (startswith(data, "__"))
109 for (p = b; p < b + size; p++) {
117 if (*p >= 'A' && *p <= 'Z')
120 if (*p >= '0' && *p <= '9')
129 static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
130 const uint8_t *a = _a, *b = _b;
133 for (j = 0; j < s && j < t; j++) {
145 static Match *match_new(Match *p, MatchType t) {
156 LIST_PREPEND(Match, matches, p->matches, m);
162 static void match_free(Match *m) {
166 match_free(m->matches);
169 LIST_REMOVE(Match, matches, m->parent->matches, m);
175 static void match_free_if_empty(Match *m) {
184 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
185 Match *l2, *l3, *add_here = NULL, *m;
197 if (!match_is_valid(data, size))
203 * level 3: concrete matches */
206 j->level0 = match_new(NULL, MATCH_OR_TERM);
212 j->level1 = match_new(j->level0, MATCH_AND_TERM);
217 assert(j->level0->type == MATCH_OR_TERM);
218 assert(j->level1->type == MATCH_AND_TERM);
220 le_hash = htole64(hash64(data, size));
222 LIST_FOREACH(matches, l2, j->level1->matches) {
223 assert(l2->type == MATCH_OR_TERM);
225 LIST_FOREACH(matches, l3, l2->matches) {
226 assert(l3->type == MATCH_DISCRETE);
228 /* Exactly the same match already? Then ignore
230 if (l3->le_hash == le_hash &&
232 memcmp(l3->data, data, size) == 0)
235 /* Same field? Then let's add this to this OR term */
236 if (same_field(data, size, l3->data, l3->size)) {
247 add_here = match_new(j->level1, MATCH_OR_TERM);
252 m = match_new(add_here, MATCH_DISCRETE);
256 m->le_hash = le_hash;
258 m->data = memdup(data, size);
268 match_free_if_empty(add_here);
271 match_free_if_empty(j->level1);
274 match_free_if_empty(j->level0);
279 _public_ int sd_journal_add_disjunction(sd_journal *j) {
290 if (!j->level1->matches)
293 m = match_new(j->level0, MATCH_AND_TERM);
301 static char *match_make_string(Match *m) {
304 bool enclose = false;
309 if (m->type == MATCH_DISCRETE)
310 return strndup(m->data, m->size);
313 LIST_FOREACH(matches, i, m->matches) {
316 t = match_make_string(i);
323 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
340 r = strjoin("(", p, ")", NULL);
348 char *journal_make_match_string(sd_journal *j) {
351 return match_make_string(j->level0);
354 _public_ void sd_journal_flush_matches(sd_journal *j) {
360 match_free(j->level0);
362 j->level0 = j->level1 = NULL;
367 static int compare_entry_order(JournalFile *af, Object *_ao,
368 JournalFile *bf, uint64_t bp) {
378 /* The mmap cache might invalidate the object from the first
379 * file if we look at the one from the second file. Hence
380 * temporarily copy the header of the first one, and look at
382 ao = alloca(offsetof(EntryObject, items));
383 memcpy(ao, _ao, offsetof(EntryObject, items));
385 r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
387 return strcmp(af->path, bf->path);
389 /* We operate on two different files here, hence we can access
390 * two objects at the same time, which we normally can't.
392 * If contents and timestamps match, these entries are
393 * identical, even if the seqnum does not match */
395 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
396 ao->entry.monotonic == bo->entry.monotonic &&
397 ao->entry.realtime == bo->entry.realtime &&
398 ao->entry.xor_hash == bo->entry.xor_hash)
401 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
403 /* If this is from the same seqnum source, compare
405 a = le64toh(ao->entry.seqnum);
406 b = le64toh(bo->entry.seqnum);
413 /* Wow! This is weird, different data but the same
414 * seqnums? Something is borked, but let's make the
415 * best of it and compare by time. */
418 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
420 /* If the boot id matches compare monotonic time */
421 a = le64toh(ao->entry.monotonic);
422 b = le64toh(bo->entry.monotonic);
430 /* Otherwise compare UTC time */
431 a = le64toh(ao->entry.realtime);
432 b = le64toh(bo->entry.realtime);
439 /* Finally, compare by contents */
440 a = le64toh(ao->entry.xor_hash);
441 b = le64toh(bo->entry.xor_hash);
451 static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
457 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
459 if (l->monotonic_set &&
460 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
462 le64toh(ao->entry.realtime) == l->realtime &&
464 le64toh(ao->entry.xor_hash) == l->xor_hash)
468 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
470 a = le64toh(ao->entry.seqnum);
478 if (l->monotonic_set &&
479 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
481 a = le64toh(ao->entry.monotonic);
483 if (a < l->monotonic)
485 if (a > l->monotonic)
489 if (l->realtime_set) {
491 a = le64toh(ao->entry.realtime);
499 if (l->xor_hash_set) {
500 a = le64toh(ao->entry.xor_hash);
511 static int next_for_match(
515 uint64_t after_offset,
516 direction_t direction,
528 if (m->type == MATCH_DISCRETE) {
531 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
535 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
537 } else if (m->type == MATCH_OR_TERM) {
540 /* Find the earliest match beyond after_offset */
542 LIST_FOREACH(matches, i, m->matches) {
545 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
549 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
554 } else if (m->type == MATCH_AND_TERM) {
556 bool continue_looking;
558 /* Always jump to the next matching entry and repeat
559 * this until we fine and offset that matches for all
567 continue_looking = false;
569 LIST_FOREACH(matches, i, m->matches) {
573 limit = after_offset;
574 else if (direction == DIRECTION_DOWN)
575 limit = MAX(np, after_offset);
577 limit = MIN(np, after_offset);
579 r = next_for_match(j, i, f, limit, direction, NULL, &cp);
583 if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) &&
584 (np == 0 || (direction == DIRECTION_DOWN ? cp > np : np < cp))) {
586 continue_looking = true;
590 } while (continue_looking);
596 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
608 static int find_location_for_match(
612 direction_t direction,
622 if (m->type == MATCH_DISCRETE) {
625 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
629 /* FIXME: missing: find by monotonic */
631 if (j->current_location.type == LOCATION_HEAD)
632 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
633 if (j->current_location.type == LOCATION_TAIL)
634 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
635 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
636 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
637 if (j->current_location.monotonic_set) {
638 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
642 if (j->current_location.realtime_set)
643 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
645 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
647 } else if (m->type == MATCH_OR_TERM) {
652 /* Find the earliest match */
654 LIST_FOREACH(matches, i, m->matches) {
657 r = find_location_for_match(j, i, f, direction, NULL, &cp);
661 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
669 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
684 assert(m->type == MATCH_AND_TERM);
686 /* First jump to the last match, and then find the
687 * next one where all matches match */
692 LIST_FOREACH(matches, i, m->matches) {
695 r = find_location_for_match(j, i, f, direction, NULL, &cp);
699 if (np == 0 || (direction == DIRECTION_DOWN ? np < cp : np > cp))
703 return next_for_match(j, m, f, np, direction, ret, offset);
707 static int find_location_with_matches(
710 direction_t direction,
722 /* No matches is simple */
724 if (j->current_location.type == LOCATION_HEAD)
725 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
726 if (j->current_location.type == LOCATION_TAIL)
727 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
728 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
729 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
730 if (j->current_location.monotonic_set) {
731 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
735 if (j->current_location.realtime_set)
736 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
738 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
740 return find_location_for_match(j, j->level0, f, direction, ret, offset);
743 static int next_with_matches(
746 direction_t direction,
761 /* No matches is easy. We simple advance the file
764 return journal_file_next_entry(f, c, cp, direction, ret, offset);
766 /* If we have a match then we look for the next matching entry
767 * with an offset at least one step larger */
768 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
771 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
779 if (f->current_offset > 0) {
780 cp = f->current_offset;
782 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
786 r = next_with_matches(j, f, direction, &c, &cp);
790 r = find_location_with_matches(j, f, direction, &c, &cp);
795 /* OK, we found the spot, now let's advance until to an entry
796 * that is actually different from what we were previously
797 * looking at. This is necessary to handle entries which exist
798 * in two (or more) journal files, and which shall all be
799 * suppressed but one. */
804 if (j->current_location.type == LOCATION_DISCRETE) {
807 k = compare_with_location(f, c, &j->current_location);
808 if (direction == DIRECTION_DOWN)
823 r = next_with_matches(j, f, direction, &c, &cp);
829 static int real_journal_next(sd_journal *j, direction_t direction) {
830 JournalFile *f, *new_file = NULL;
831 uint64_t new_offset = 0;
840 HASHMAP_FOREACH(f, j->files, i) {
843 r = next_beyond_location(j, f, direction, &o, &p);
845 log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
855 k = compare_entry_order(f, o, new_file, new_offset);
857 if (direction == DIRECTION_DOWN)
872 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
876 set_location(j, LOCATION_DISCRETE, new_file, o, new_offset);
881 _public_ int sd_journal_next(sd_journal *j) {
882 return real_journal_next(j, DIRECTION_DOWN);
885 _public_ int sd_journal_previous(sd_journal *j) {
886 return real_journal_next(j, DIRECTION_UP);
889 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
896 /* If this is not a discrete skip, then at least
897 * resolve the current location */
898 if (j->current_location.type != LOCATION_DISCRETE)
899 return real_journal_next(j, direction);
905 r = real_journal_next(j, direction);
919 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
920 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
923 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
924 return real_journal_next_skip(j, DIRECTION_UP, skip);
927 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
930 char bid[33], sid[33];
937 if (!j->current_file || j->current_file->current_offset <= 0)
938 return -EADDRNOTAVAIL;
940 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
944 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
945 sd_id128_to_string(o->entry.boot_id, bid);
948 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx",
949 sid, (unsigned long long) le64toh(o->entry.seqnum),
950 bid, (unsigned long long) le64toh(o->entry.monotonic),
951 (unsigned long long) le64toh(o->entry.realtime),
952 (unsigned long long) le64toh(o->entry.xor_hash)) < 0)
958 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
961 unsigned long long seqnum, monotonic, realtime, xor_hash;
963 seqnum_id_set = false,
966 monotonic_set = false,
967 realtime_set = false,
968 xor_hash_set = false;
969 sd_id128_t seqnum_id, boot_id;
976 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
980 if (l < 2 || w[1] != '=')
983 item = strndup(w, l);
990 seqnum_id_set = true;
991 k = sd_id128_from_string(item+2, &seqnum_id);
996 if (sscanf(item+2, "%llx", &seqnum) != 1)
1002 k = sd_id128_from_string(item+2, &boot_id);
1006 monotonic_set = true;
1007 if (sscanf(item+2, "%llx", &monotonic) != 1)
1012 realtime_set = true;
1013 if (sscanf(item+2, "%llx", &realtime) != 1)
1018 xor_hash_set = true;
1019 if (sscanf(item+2, "%llx", &xor_hash) != 1)
1030 if ((!seqnum_set || !seqnum_id_set) &&
1031 (!monotonic_set || !boot_id_set) &&
1037 j->current_location.type = LOCATION_SEEK;
1040 j->current_location.realtime = (uint64_t) realtime;
1041 j->current_location.realtime_set = true;
1044 if (seqnum_set && seqnum_id_set) {
1045 j->current_location.seqnum = (uint64_t) seqnum;
1046 j->current_location.seqnum_id = seqnum_id;
1047 j->current_location.seqnum_set = true;
1050 if (monotonic_set && boot_id_set) {
1051 j->current_location.monotonic = (uint64_t) monotonic;
1052 j->current_location.boot_id = boot_id;
1053 j->current_location.monotonic_set = true;
1057 j->current_location.xor_hash = (uint64_t) xor_hash;
1058 j->current_location.xor_hash_set = true;
1064 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1072 if (isempty(cursor))
1075 if (!j->current_file || j->current_file->current_offset <= 0)
1076 return -EADDRNOTAVAIL;
1078 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1082 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
1083 _cleanup_free_ char *item = NULL;
1085 unsigned long long ll;
1088 if (l < 2 || w[1] != '=')
1091 item = strndup(w, l);
1098 k = sd_id128_from_string(item+2, &id);
1101 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1106 if (sscanf(item+2, "%llx", &ll) != 1)
1108 if (ll != le64toh(o->entry.seqnum))
1113 k = sd_id128_from_string(item+2, &id);
1116 if (!sd_id128_equal(id, o->entry.boot_id))
1121 if (sscanf(item+2, "%llx", &ll) != 1)
1123 if (ll != le64toh(o->entry.monotonic))
1128 if (sscanf(item+2, "%llx", &ll) != 1)
1130 if (ll != le64toh(o->entry.realtime))
1135 if (sscanf(item+2, "%llx", &ll) != 1)
1137 if (ll != le64toh(o->entry.xor_hash))
1147 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1152 j->current_location.type = LOCATION_SEEK;
1153 j->current_location.boot_id = boot_id;
1154 j->current_location.monotonic = usec;
1155 j->current_location.monotonic_set = true;
1160 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1165 j->current_location.type = LOCATION_SEEK;
1166 j->current_location.realtime = usec;
1167 j->current_location.realtime_set = true;
1172 _public_ int sd_journal_seek_head(sd_journal *j) {
1177 j->current_location.type = LOCATION_HEAD;
1182 _public_ int sd_journal_seek_tail(sd_journal *j) {
1187 j->current_location.type = LOCATION_TAIL;
1192 static void check_network(sd_journal *j, int fd) {
1200 if (fstatfs(fd, &sfs) < 0)
1204 sfs.f_type == CIFS_MAGIC_NUMBER ||
1205 sfs.f_type == CODA_SUPER_MAGIC ||
1206 sfs.f_type == NCP_SUPER_MAGIC ||
1207 sfs.f_type == NFS_SUPER_MAGIC ||
1208 sfs.f_type == SMB_SUPER_MAGIC;
1211 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1220 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
1221 !(streq(filename, "system.journal") ||
1222 streq(filename, "system.journal~") ||
1223 (startswith(filename, "system@") &&
1224 (endswith(filename, ".journal") || endswith(filename, ".journal~")))))
1227 path = strjoin(prefix, "/", filename, NULL);
1231 if (hashmap_get(j->files, path)) {
1236 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1237 log_debug("Too many open journal files, not adding %s, ignoring.", path);
1242 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1246 if (errno == ENOENT)
1252 /* journal_file_dump(f); */
1254 r = hashmap_put(j->files, f->path, f);
1256 journal_file_close(f);
1260 check_network(j, f->fd);
1262 j->current_invalidate_counter ++;
1264 log_debug("File %s got added.", f->path);
1269 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1277 path = strjoin(prefix, "/", filename, NULL);
1281 f = hashmap_get(j->files, path);
1286 hashmap_remove(j->files, f->path);
1288 log_debug("File %s got removed.", f->path);
1290 if (j->current_file == f) {
1291 j->current_file = NULL;
1292 j->current_field = 0;
1295 if (j->unique_file == f) {
1296 j->unique_file = NULL;
1297 j->unique_offset = 0;
1300 journal_file_close(f);
1302 j->current_invalidate_counter ++;
1307 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1318 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1319 (sd_id128_from_string(dirname, &id) < 0 ||
1320 sd_id128_get_machine(&mid) < 0 ||
1321 !sd_id128_equal(id, mid)))
1324 path = strjoin(prefix, "/", dirname, NULL);
1330 log_debug("Failed to open %s: %m", path);
1333 if (errno == ENOENT)
1338 m = hashmap_get(j->directories_by_path, path);
1340 m = new0(Directory, 1);
1350 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1357 j->current_invalidate_counter ++;
1359 log_debug("Directory %s got added.", m->path);
1361 } else if (m->is_root) {
1368 if (m->wd <= 0 && j->inotify_fd >= 0) {
1370 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1371 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1372 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1375 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1376 inotify_rm_watch(j->inotify_fd, m->wd);
1381 union dirent_storage buf;
1383 r = readdir_r(d, &buf.de, &de);
1387 if (dirent_is_file_with_suffix(de, ".journal") ||
1388 dirent_is_file_with_suffix(de, ".journal~")) {
1389 r = add_file(j, m->path, de->d_name);
1391 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1395 check_network(j, dirfd(d));
1402 static int add_root_directory(sd_journal *j, const char *p) {
1410 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1411 !path_startswith(p, "/run"))
1418 m = hashmap_get(j->directories_by_path, p);
1420 m = new0(Directory, 1);
1427 m->path = strdup(p);
1434 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1441 j->current_invalidate_counter ++;
1443 log_debug("Root directory %s got added.", m->path);
1445 } else if (!m->is_root) {
1450 if (m->wd <= 0 && j->inotify_fd >= 0) {
1452 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1453 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1456 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1457 inotify_rm_watch(j->inotify_fd, m->wd);
1462 union dirent_storage buf;
1465 r = readdir_r(d, &buf.de, &de);
1469 if (dirent_is_file_with_suffix(de, ".journal") ||
1470 dirent_is_file_with_suffix(de, ".journal~")) {
1471 r = add_file(j, m->path, de->d_name);
1473 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1475 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1476 sd_id128_from_string(de->d_name, &id) >= 0) {
1478 r = add_directory(j, m->path, de->d_name);
1480 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1484 check_network(j, dirfd(d));
1491 static int remove_directory(sd_journal *j, Directory *d) {
1495 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1497 if (j->inotify_fd >= 0)
1498 inotify_rm_watch(j->inotify_fd, d->wd);
1501 hashmap_remove(j->directories_by_path, d->path);
1504 log_debug("Root directory %s got removed.", d->path);
1506 log_debug("Directory %s got removed.", d->path);
1514 static int add_search_paths(sd_journal *j) {
1516 const char search_paths[] =
1517 "/run/log/journal\0"
1518 "/var/log/journal\0";
1523 /* We ignore most errors here, since the idea is to only open
1524 * what's actually accessible, and ignore the rest. */
1526 NULSTR_FOREACH(p, search_paths)
1527 add_root_directory(j, p);
1532 static int allocate_inotify(sd_journal *j) {
1535 if (j->inotify_fd < 0) {
1536 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1537 if (j->inotify_fd < 0)
1541 if (!j->directories_by_wd) {
1542 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1543 if (!j->directories_by_wd)
1550 static sd_journal *journal_new(int flags, const char *path) {
1553 j = new0(sd_journal, 1);
1561 j->path = strdup(path);
1568 j->files = hashmap_new(string_hash_func, string_compare_func);
1575 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1576 if (!j->directories_by_path) {
1577 hashmap_free(j->files);
1583 j->mmap = mmap_cache_new();
1585 hashmap_free(j->files);
1586 hashmap_free(j->directories_by_path);
1595 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1602 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1603 SD_JOURNAL_RUNTIME_ONLY|
1604 SD_JOURNAL_SYSTEM_ONLY))
1607 j = journal_new(flags, NULL);
1611 r = add_search_paths(j);
1619 sd_journal_close(j);
1624 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1631 if (!path || !path_is_absolute(path))
1637 j = journal_new(flags, path);
1641 r = add_root_directory(j, path);
1649 sd_journal_close(j);
1654 _public_ void sd_journal_close(sd_journal *j) {
1661 while ((f = hashmap_steal_first(j->files)))
1662 journal_file_close(f);
1664 hashmap_free(j->files);
1666 while ((d = hashmap_first(j->directories_by_path)))
1667 remove_directory(j, d);
1669 while ((d = hashmap_first(j->directories_by_wd)))
1670 remove_directory(j, d);
1672 hashmap_free(j->directories_by_path);
1673 hashmap_free(j->directories_by_wd);
1675 if (j->inotify_fd >= 0)
1676 close_nointr_nofail(j->inotify_fd);
1678 sd_journal_flush_matches(j);
1681 mmap_cache_unref(j->mmap);
1684 free(j->unique_field);
1688 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1698 f = j->current_file;
1700 return -EADDRNOTAVAIL;
1702 if (f->current_offset <= 0)
1703 return -EADDRNOTAVAIL;
1705 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1709 *ret = le64toh(o->entry.realtime);
1713 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1722 f = j->current_file;
1724 return -EADDRNOTAVAIL;
1726 if (f->current_offset <= 0)
1727 return -EADDRNOTAVAIL;
1729 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1734 *ret_boot_id = o->entry.boot_id;
1736 r = sd_id128_get_boot(&id);
1740 if (!sd_id128_equal(id, o->entry.boot_id))
1745 *ret = le64toh(o->entry.monotonic);
1750 static bool field_is_valid(const char *field) {
1758 if (startswith(field, "__"))
1761 for (p = field; *p; p++) {
1766 if (*p >= 'A' && *p <= 'Z')
1769 if (*p >= '0' && *p <= '9')
1778 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1781 size_t field_length;
1794 if (!field_is_valid(field))
1797 f = j->current_file;
1799 return -EADDRNOTAVAIL;
1801 if (f->current_offset <= 0)
1802 return -EADDRNOTAVAIL;
1804 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1808 field_length = strlen(field);
1810 n = journal_file_entry_n_items(o);
1811 for (i = 0; i < n; i++) {
1816 p = le64toh(o->entry.items[i].object_offset);
1817 le_hash = o->entry.items[i].hash;
1818 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1822 if (le_hash != o->data.hash)
1825 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1827 if (o->object.flags & OBJECT_COMPRESSED) {
1830 if (uncompress_startswith(o->data.payload, l,
1831 &f->compress_buffer, &f->compress_buffer_size,
1832 field, field_length, '=')) {
1836 if (!uncompress_blob(o->data.payload, l,
1837 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1840 *data = f->compress_buffer;
1841 *size = (size_t) rsize;
1846 return -EPROTONOSUPPORT;
1849 } else if (l >= field_length+1 &&
1850 memcmp(o->data.payload, field, field_length) == 0 &&
1851 o->data.payload[field_length] == '=') {
1855 if ((uint64_t) t != l)
1858 *data = o->data.payload;
1864 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1872 static int return_data(JournalFile *f, Object *o, const void **data, size_t *size) {
1876 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1879 /* We can't read objects larger than 4G on a 32bit machine */
1880 if ((uint64_t) t != l)
1883 if (o->object.flags & OBJECT_COMPRESSED) {
1887 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1890 *data = f->compress_buffer;
1891 *size = (size_t) rsize;
1893 return -EPROTONOSUPPORT;
1896 *data = o->data.payload;
1903 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1917 f = j->current_file;
1919 return -EADDRNOTAVAIL;
1921 if (f->current_offset <= 0)
1922 return -EADDRNOTAVAIL;
1924 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1928 n = journal_file_entry_n_items(o);
1929 if (j->current_field >= n)
1932 p = le64toh(o->entry.items[j->current_field].object_offset);
1933 le_hash = o->entry.items[j->current_field].hash;
1934 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1938 if (le_hash != o->data.hash)
1941 r = return_data(f, o, data, size);
1945 j->current_field ++;
1950 _public_ void sd_journal_restart_data(sd_journal *j) {
1954 j->current_field = 0;
1957 _public_ int sd_journal_get_fd(sd_journal *j) {
1963 if (j->inotify_fd >= 0)
1964 return j->inotify_fd;
1966 r = allocate_inotify(j);
1970 /* Iterate through all dirs again, to add them to the
1973 r = add_root_directory(j, j->path);
1975 r = add_search_paths(j);
1979 return j->inotify_fd;
1982 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1989 /* Is this a subdirectory we watch? */
1990 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
1994 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
1995 (endswith(e->name, ".journal") ||
1996 endswith(e->name, ".journal~"))) {
1998 /* Event for a journal file */
2000 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2001 r = add_file(j, d->path, e->name);
2003 log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r));
2005 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2007 r = remove_file(j, d->path, e->name);
2009 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
2012 } else if (!d->is_root && e->len == 0) {
2014 /* Event for a subdirectory */
2016 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2017 r = remove_directory(j, d);
2019 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
2023 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2025 /* Event for root directory */
2027 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2028 r = add_directory(j, d->path, e->name);
2030 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
2037 if (e->mask & IN_IGNORED)
2040 log_warning("Unknown inotify event.");
2043 static int determine_change(sd_journal *j) {
2048 b = j->current_invalidate_counter != j->last_invalidate_counter;
2049 j->last_invalidate_counter = j->current_invalidate_counter;
2051 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2054 _public_ int sd_journal_process(sd_journal *j) {
2055 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
2056 bool got_something = false;
2062 struct inotify_event *e;
2065 l = read(j->inotify_fd, buffer, sizeof(buffer));
2067 if (errno == EAGAIN || errno == EINTR)
2068 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2073 got_something = true;
2075 e = (struct inotify_event*) buffer;
2079 process_inotify_event(j, e);
2081 step = sizeof(struct inotify_event) + e->len;
2082 assert(step <= (size_t) l);
2084 e = (struct inotify_event*) ((uint8_t*) e + step);
2089 return determine_change(j);
2092 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2097 if (j->inotify_fd < 0) {
2099 /* This is the first invocation, hence create the
2101 r = sd_journal_get_fd(j);
2105 /* The journal might have changed since the context
2106 * object was created and we weren't watching before,
2107 * hence don't wait for anything, and return
2109 return determine_change(j);
2112 if (j->on_network) {
2113 /* If we are on the network we need to regularly check
2114 * for changes manually */
2116 if (timeout_usec == (uint64_t) -1 || timeout_usec > JOURNAL_FILES_RECHECK_USEC)
2117 timeout_usec = JOURNAL_FILES_RECHECK_USEC;
2121 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2122 } while (r == -EINTR);
2127 return sd_journal_process(j);
2130 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2141 HASHMAP_FOREACH(f, j->files, i) {
2144 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2160 *from = MIN(fr, *from);
2166 return first ? 0 : 1;
2169 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2180 HASHMAP_FOREACH(f, j->files, i) {
2183 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2199 *from = MIN(fr, *from);
2205 return first ? 0 : 1;
2208 void journal_print_header(sd_journal *j) {
2211 bool newline = false;
2215 HASHMAP_FOREACH(f, j->files, i) {
2221 journal_file_print_header(f);
2225 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2235 HASHMAP_FOREACH(f, j->files, i) {
2238 if (fstat(f->fd, &st) < 0)
2241 sum += (uint64_t) st.st_blocks * 512ULL;
2248 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2255 if (!field_is_valid(field))
2262 free(j->unique_field);
2263 j->unique_field = f;
2264 j->unique_file = NULL;
2265 j->unique_offset = 0;
2270 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2281 if (!j->unique_field)
2284 k = strlen(j->unique_field);
2286 if (!j->unique_file) {
2287 j->unique_file = hashmap_first(j->files);
2288 if (!j->unique_file)
2290 j->unique_offset = 0;
2300 /* Proceed to next data object in the field's linked list */
2301 if (j->unique_offset == 0) {
2302 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2306 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2308 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2312 j->unique_offset = le64toh(o->data.next_field_offset);
2315 /* We reached the end of the list? Then start again, with the next file */
2316 if (j->unique_offset == 0) {
2319 n = hashmap_next(j->files, j->unique_file->path);
2327 /* We do not use the type context here, but 0 instead,
2328 * so that we can look at this data object at the same
2329 * time as one on another file */
2330 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2334 /* Let's do the type check by hand, since we used 0 context above. */
2335 if (o->object.type != OBJECT_DATA)
2338 r = return_data(j->unique_file, o, &odata, &ol);
2342 /* OK, now let's see if we already returned this data
2343 * object by checking if it exists in the earlier
2344 * traversed files. */
2346 HASHMAP_FOREACH(of, j->files, i) {
2350 if (of == j->unique_file)
2353 /* Skip this file it didn't have any fields
2355 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2356 le64toh(of->header->n_fields) <= 0)
2359 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2370 r = return_data(j->unique_file, o, data, l);
2378 _public_ void sd_journal_restart_unique(sd_journal *j) {
2382 j->unique_file = NULL;
2383 j->unique_offset = 0;
2386 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2390 return !j->on_network;