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, LocationType type, JournalFile *f, Object *o) {
63 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
65 assert(o->object.type == OBJECT_ENTRY);
68 l->seqnum = le64toh(o->entry.seqnum);
69 l->seqnum_id = f->header->seqnum_id;
70 l->realtime = le64toh(o->entry.realtime);
71 l->monotonic = le64toh(o->entry.monotonic);
72 l->boot_id = o->entry.boot_id;
73 l->xor_hash = le64toh(o->entry.xor_hash);
75 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
78 static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o, uint64_t offset) {
80 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
84 init_location(&j->current_location, type, f, o);
89 f->current_offset = offset;
92 static int match_is_valid(const void *data, size_t size) {
100 if (startswith(data, "__"))
104 for (p = b; p < b + size; p++) {
112 if (*p >= 'A' && *p <= 'Z')
115 if (*p >= '0' && *p <= '9')
124 static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
125 const uint8_t *a = _a, *b = _b;
128 for (j = 0; j < s && j < t; j++) {
140 static Match *match_new(Match *p, MatchType t) {
151 LIST_PREPEND(Match, matches, p->matches, m);
157 static void match_free(Match *m) {
161 match_free(m->matches);
164 LIST_REMOVE(Match, matches, m->parent->matches, m);
170 static void match_free_if_empty(Match *m) {
179 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
180 Match *l2, *l3, *add_here = NULL, *m;
192 if (!match_is_valid(data, size))
198 * level 3: concrete matches */
201 j->level0 = match_new(NULL, MATCH_OR_TERM);
207 j->level1 = match_new(j->level0, MATCH_AND_TERM);
212 assert(j->level0->type == MATCH_OR_TERM);
213 assert(j->level1->type == MATCH_AND_TERM);
215 le_hash = htole64(hash64(data, size));
217 LIST_FOREACH(matches, l2, j->level1->matches) {
218 assert(l2->type == MATCH_OR_TERM);
220 LIST_FOREACH(matches, l3, l2->matches) {
221 assert(l3->type == MATCH_DISCRETE);
223 /* Exactly the same match already? Then ignore
225 if (l3->le_hash == le_hash &&
227 memcmp(l3->data, data, size) == 0)
230 /* Same field? Then let's add this to this OR term */
231 if (same_field(data, size, l3->data, l3->size)) {
242 add_here = match_new(j->level1, MATCH_OR_TERM);
247 m = match_new(add_here, MATCH_DISCRETE);
251 m->le_hash = le_hash;
253 m->data = memdup(data, size);
263 match_free_if_empty(add_here);
266 match_free_if_empty(j->level1);
269 match_free_if_empty(j->level0);
274 _public_ int sd_journal_add_disjunction(sd_journal *j) {
285 if (!j->level1->matches)
288 m = match_new(j->level0, MATCH_AND_TERM);
296 static char *match_make_string(Match *m) {
299 bool enclose = false;
304 if (m->type == MATCH_DISCRETE)
305 return strndup(m->data, m->size);
308 LIST_FOREACH(matches, i, m->matches) {
311 t = match_make_string(i);
318 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
335 r = strjoin("(", p, ")", NULL);
343 char *journal_make_match_string(sd_journal *j) {
346 return match_make_string(j->level0);
349 _public_ void sd_journal_flush_matches(sd_journal *j) {
355 match_free(j->level0);
357 j->level0 = j->level1 = NULL;
362 static int compare_entry_order(JournalFile *af, Object *_ao,
363 JournalFile *bf, uint64_t bp) {
373 /* The mmap cache might invalidate the object from the first
374 * file if we look at the one from the second file. Hence
375 * temporarily copy the header of the first one, and look at
377 ao = alloca(offsetof(EntryObject, items));
378 memcpy(ao, _ao, offsetof(EntryObject, items));
380 r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
382 return strcmp(af->path, bf->path);
384 /* We operate on two different files here, hence we can access
385 * two objects at the same time, which we normally can't.
387 * If contents and timestamps match, these entries are
388 * identical, even if the seqnum does not match */
390 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
391 ao->entry.monotonic == bo->entry.monotonic &&
392 ao->entry.realtime == bo->entry.realtime &&
393 ao->entry.xor_hash == bo->entry.xor_hash)
396 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
398 /* If this is from the same seqnum source, compare
400 a = le64toh(ao->entry.seqnum);
401 b = le64toh(bo->entry.seqnum);
408 /* Wow! This is weird, different data but the same
409 * seqnums? Something is borked, but let's make the
410 * best of it and compare by time. */
413 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
415 /* If the boot id matches compare monotonic time */
416 a = le64toh(ao->entry.monotonic);
417 b = le64toh(bo->entry.monotonic);
425 /* Otherwise compare UTC time */
426 a = le64toh(ao->entry.realtime);
427 b = le64toh(bo->entry.realtime);
434 /* Finally, compare by contents */
435 a = le64toh(ao->entry.xor_hash);
436 b = le64toh(bo->entry.xor_hash);
446 static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
452 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
454 if (l->monotonic_set &&
455 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
457 le64toh(ao->entry.realtime) == l->realtime &&
459 le64toh(ao->entry.xor_hash) == l->xor_hash)
463 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
465 a = le64toh(ao->entry.seqnum);
473 if (l->monotonic_set &&
474 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
476 a = le64toh(ao->entry.monotonic);
478 if (a < l->monotonic)
480 if (a > l->monotonic)
484 if (l->realtime_set) {
486 a = le64toh(ao->entry.realtime);
494 if (l->xor_hash_set) {
495 a = le64toh(ao->entry.xor_hash);
506 static int next_for_match(
510 uint64_t after_offset,
511 direction_t direction,
523 if (m->type == MATCH_DISCRETE) {
526 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
530 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
532 } else if (m->type == MATCH_OR_TERM) {
535 /* Find the earliest match beyond after_offset */
537 LIST_FOREACH(matches, i, m->matches) {
540 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
544 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
549 } else if (m->type == MATCH_AND_TERM) {
551 bool continue_looking;
553 /* Always jump to the next matching entry and repeat
554 * this until we fine and offset that matches for all
562 continue_looking = false;
564 LIST_FOREACH(matches, i, m->matches) {
568 limit = after_offset;
569 else if (direction == DIRECTION_DOWN)
570 limit = MAX(np, after_offset);
572 limit = MIN(np, after_offset);
574 r = next_for_match(j, i, f, limit, direction, NULL, &cp);
578 if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) &&
579 (np == 0 || (direction == DIRECTION_DOWN ? cp > np : np < cp))) {
581 continue_looking = true;
585 } while (continue_looking);
591 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
603 static int find_location_for_match(
607 direction_t direction,
617 if (m->type == MATCH_DISCRETE) {
620 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
624 /* FIXME: missing: find by monotonic */
626 if (j->current_location.type == LOCATION_HEAD)
627 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
628 if (j->current_location.type == LOCATION_TAIL)
629 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
630 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
631 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
632 if (j->current_location.monotonic_set) {
633 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
637 if (j->current_location.realtime_set)
638 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
640 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
642 } else if (m->type == MATCH_OR_TERM) {
647 /* Find the earliest match */
649 LIST_FOREACH(matches, i, m->matches) {
652 r = find_location_for_match(j, i, f, direction, NULL, &cp);
656 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
664 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
679 assert(m->type == MATCH_AND_TERM);
681 /* First jump to the last match, and then find the
682 * next one where all matches match */
687 LIST_FOREACH(matches, i, m->matches) {
690 r = find_location_for_match(j, i, f, direction, NULL, &cp);
694 if (np == 0 || (direction == DIRECTION_DOWN ? np < cp : np > cp))
698 return next_for_match(j, m, f, np, direction, ret, offset);
702 static int find_location_with_matches(
705 direction_t direction,
717 /* No matches is simple */
719 if (j->current_location.type == LOCATION_HEAD)
720 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
721 if (j->current_location.type == LOCATION_TAIL)
722 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
723 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
724 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
725 if (j->current_location.monotonic_set) {
726 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
730 if (j->current_location.realtime_set)
731 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
733 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
735 return find_location_for_match(j, j->level0, f, direction, ret, offset);
738 static int next_with_matches(
741 direction_t direction,
756 /* No matches is easy. We simple advance the file
759 return journal_file_next_entry(f, c, cp, direction, ret, offset);
761 /* If we have a match then we look for the next matching entry
762 * with an offset at least one step larger */
763 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
766 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
774 if (f->current_offset > 0) {
775 cp = f->current_offset;
777 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
781 r = next_with_matches(j, f, direction, &c, &cp);
785 r = find_location_with_matches(j, f, direction, &c, &cp);
790 /* OK, we found the spot, now let's advance until to an entry
791 * that is actually different from what we were previously
792 * looking at. This is necessary to handle entries which exist
793 * in two (or more) journal files, and which shall all be
794 * suppressed but one. */
799 if (j->current_location.type == LOCATION_DISCRETE) {
802 k = compare_with_location(f, c, &j->current_location);
803 if (direction == DIRECTION_DOWN)
818 r = next_with_matches(j, f, direction, &c, &cp);
824 static int real_journal_next(sd_journal *j, direction_t direction) {
825 JournalFile *f, *new_file = NULL;
826 uint64_t new_offset = 0;
835 HASHMAP_FOREACH(f, j->files, i) {
838 r = next_beyond_location(j, f, direction, &o, &p);
840 log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
850 k = compare_entry_order(f, o, new_file, new_offset);
852 if (direction == DIRECTION_DOWN)
867 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
871 set_location(j, LOCATION_DISCRETE, new_file, o, new_offset);
876 _public_ int sd_journal_next(sd_journal *j) {
877 return real_journal_next(j, DIRECTION_DOWN);
880 _public_ int sd_journal_previous(sd_journal *j) {
881 return real_journal_next(j, DIRECTION_UP);
884 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
891 /* If this is not a discrete skip, then at least
892 * resolve the current location */
893 if (j->current_location.type != LOCATION_DISCRETE)
894 return real_journal_next(j, direction);
900 r = real_journal_next(j, direction);
914 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
915 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
918 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
919 return real_journal_next_skip(j, DIRECTION_UP, skip);
922 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
925 char bid[33], sid[33];
932 if (!j->current_file || j->current_file->current_offset <= 0)
933 return -EADDRNOTAVAIL;
935 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
939 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
940 sd_id128_to_string(o->entry.boot_id, bid);
943 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx",
944 sid, (unsigned long long) le64toh(o->entry.seqnum),
945 bid, (unsigned long long) le64toh(o->entry.monotonic),
946 (unsigned long long) le64toh(o->entry.realtime),
947 (unsigned long long) le64toh(o->entry.xor_hash)) < 0)
953 _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_SEEK;
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_test_cursor(sd_journal *j, const char *cursor) {
1067 if (isempty(cursor))
1070 if (!j->current_file || j->current_file->current_offset <= 0)
1071 return -EADDRNOTAVAIL;
1073 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1077 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
1078 _cleanup_free_ char *item = NULL;
1080 unsigned long long ll;
1083 if (l < 2 || w[1] != '=')
1086 item = strndup(w, l);
1093 k = sd_id128_from_string(item+2, &id);
1096 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1101 if (sscanf(item+2, "%llx", &ll) != 1)
1103 if (ll != le64toh(o->entry.seqnum))
1108 k = sd_id128_from_string(item+2, &id);
1111 if (!sd_id128_equal(id, o->entry.boot_id))
1116 if (sscanf(item+2, "%llx", &ll) != 1)
1118 if (ll != le64toh(o->entry.monotonic))
1123 if (sscanf(item+2, "%llx", &ll) != 1)
1125 if (ll != le64toh(o->entry.realtime))
1130 if (sscanf(item+2, "%llx", &ll) != 1)
1132 if (ll != le64toh(o->entry.xor_hash))
1142 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1147 j->current_location.type = LOCATION_SEEK;
1148 j->current_location.boot_id = boot_id;
1149 j->current_location.monotonic = usec;
1150 j->current_location.monotonic_set = true;
1155 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1160 j->current_location.type = LOCATION_SEEK;
1161 j->current_location.realtime = usec;
1162 j->current_location.realtime_set = true;
1167 _public_ int sd_journal_seek_head(sd_journal *j) {
1172 j->current_location.type = LOCATION_HEAD;
1177 _public_ int sd_journal_seek_tail(sd_journal *j) {
1182 j->current_location.type = LOCATION_TAIL;
1187 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1196 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
1197 !(streq(filename, "system.journal") ||
1198 streq(filename, "system.journal~") ||
1199 (startswith(filename, "system@") &&
1200 (endswith(filename, ".journal") || endswith(filename, ".journal~")))))
1203 path = strjoin(prefix, "/", filename, NULL);
1207 if (hashmap_get(j->files, path)) {
1212 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1213 log_debug("Too many open journal files, not adding %s, ignoring.", path);
1218 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1222 if (errno == ENOENT)
1228 /* journal_file_dump(f); */
1230 r = hashmap_put(j->files, f->path, f);
1232 journal_file_close(f);
1236 j->current_invalidate_counter ++;
1238 log_debug("File %s got added.", f->path);
1243 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1251 path = strjoin(prefix, "/", filename, NULL);
1255 f = hashmap_get(j->files, path);
1260 hashmap_remove(j->files, f->path);
1262 log_debug("File %s got removed.", f->path);
1264 if (j->current_file == f) {
1265 j->current_file = NULL;
1266 j->current_field = 0;
1269 if (j->unique_file == f) {
1270 j->unique_file = NULL;
1271 j->unique_offset = 0;
1274 journal_file_close(f);
1276 j->current_invalidate_counter ++;
1281 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1292 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1293 (sd_id128_from_string(dirname, &id) < 0 ||
1294 sd_id128_get_machine(&mid) < 0 ||
1295 !sd_id128_equal(id, mid)))
1298 path = strjoin(prefix, "/", dirname, NULL);
1304 log_debug("Failed to open %s: %m", path);
1307 if (errno == ENOENT)
1312 m = hashmap_get(j->directories_by_path, path);
1314 m = new0(Directory, 1);
1324 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1331 j->current_invalidate_counter ++;
1333 log_debug("Directory %s got added.", m->path);
1335 } else if (m->is_root) {
1342 if (m->wd <= 0 && j->inotify_fd >= 0) {
1344 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1345 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1346 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1349 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1350 inotify_rm_watch(j->inotify_fd, m->wd);
1355 union dirent_storage buf;
1357 r = readdir_r(d, &buf.de, &de);
1361 if (dirent_is_file_with_suffix(de, ".journal") ||
1362 dirent_is_file_with_suffix(de, ".journal~")) {
1363 r = add_file(j, m->path, de->d_name);
1365 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1374 static int add_root_directory(sd_journal *j, const char *p) {
1382 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1383 !path_startswith(p, "/run"))
1390 m = hashmap_get(j->directories_by_path, p);
1392 m = new0(Directory, 1);
1399 m->path = strdup(p);
1406 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1413 j->current_invalidate_counter ++;
1415 log_debug("Root directory %s got added.", m->path);
1417 } else if (!m->is_root) {
1422 if (m->wd <= 0 && j->inotify_fd >= 0) {
1424 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1425 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1428 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1429 inotify_rm_watch(j->inotify_fd, m->wd);
1434 union dirent_storage buf;
1437 r = readdir_r(d, &buf.de, &de);
1441 if (dirent_is_file_with_suffix(de, ".journal") ||
1442 dirent_is_file_with_suffix(de, ".journal~")) {
1443 r = add_file(j, m->path, de->d_name);
1445 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1447 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1448 sd_id128_from_string(de->d_name, &id) >= 0) {
1450 r = add_directory(j, m->path, de->d_name);
1452 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1461 static int remove_directory(sd_journal *j, Directory *d) {
1465 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1467 if (j->inotify_fd >= 0)
1468 inotify_rm_watch(j->inotify_fd, d->wd);
1471 hashmap_remove(j->directories_by_path, d->path);
1474 log_debug("Root directory %s got removed.", d->path);
1476 log_debug("Directory %s got removed.", d->path);
1484 static int add_search_paths(sd_journal *j) {
1486 const char search_paths[] =
1487 "/run/log/journal\0"
1488 "/var/log/journal\0";
1493 /* We ignore most errors here, since the idea is to only open
1494 * what's actually accessible, and ignore the rest. */
1496 NULSTR_FOREACH(p, search_paths)
1497 add_root_directory(j, p);
1502 static int allocate_inotify(sd_journal *j) {
1505 if (j->inotify_fd < 0) {
1506 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1507 if (j->inotify_fd < 0)
1511 if (!j->directories_by_wd) {
1512 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1513 if (!j->directories_by_wd)
1520 static sd_journal *journal_new(int flags, const char *path) {
1523 j = new0(sd_journal, 1);
1531 j->path = strdup(path);
1538 j->files = hashmap_new(string_hash_func, string_compare_func);
1545 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1546 if (!j->directories_by_path) {
1547 hashmap_free(j->files);
1553 j->mmap = mmap_cache_new();
1555 hashmap_free(j->files);
1556 hashmap_free(j->directories_by_path);
1565 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1572 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1573 SD_JOURNAL_RUNTIME_ONLY|
1574 SD_JOURNAL_SYSTEM_ONLY))
1577 j = journal_new(flags, NULL);
1581 r = add_search_paths(j);
1589 sd_journal_close(j);
1594 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1601 if (!path || !path_is_absolute(path))
1607 j = journal_new(flags, path);
1611 r = add_root_directory(j, path);
1619 sd_journal_close(j);
1624 _public_ void sd_journal_close(sd_journal *j) {
1631 while ((f = hashmap_steal_first(j->files)))
1632 journal_file_close(f);
1634 hashmap_free(j->files);
1636 while ((d = hashmap_first(j->directories_by_path)))
1637 remove_directory(j, d);
1639 while ((d = hashmap_first(j->directories_by_wd)))
1640 remove_directory(j, d);
1642 hashmap_free(j->directories_by_path);
1643 hashmap_free(j->directories_by_wd);
1645 if (j->inotify_fd >= 0)
1646 close_nointr_nofail(j->inotify_fd);
1648 sd_journal_flush_matches(j);
1651 mmap_cache_unref(j->mmap);
1654 free(j->unique_field);
1658 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1668 f = j->current_file;
1670 return -EADDRNOTAVAIL;
1672 if (f->current_offset <= 0)
1673 return -EADDRNOTAVAIL;
1675 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1679 *ret = le64toh(o->entry.realtime);
1683 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1692 f = j->current_file;
1694 return -EADDRNOTAVAIL;
1696 if (f->current_offset <= 0)
1697 return -EADDRNOTAVAIL;
1699 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1704 *ret_boot_id = o->entry.boot_id;
1706 r = sd_id128_get_boot(&id);
1710 if (!sd_id128_equal(id, o->entry.boot_id))
1715 *ret = le64toh(o->entry.monotonic);
1720 static bool field_is_valid(const char *field) {
1728 if (startswith(field, "__"))
1731 for (p = field; *p; p++) {
1736 if (*p >= 'A' && *p <= 'Z')
1739 if (*p >= '0' && *p <= '9')
1748 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1751 size_t field_length;
1764 if (!field_is_valid(field))
1767 f = j->current_file;
1769 return -EADDRNOTAVAIL;
1771 if (f->current_offset <= 0)
1772 return -EADDRNOTAVAIL;
1774 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1778 field_length = strlen(field);
1780 n = journal_file_entry_n_items(o);
1781 for (i = 0; i < n; i++) {
1786 p = le64toh(o->entry.items[i].object_offset);
1787 le_hash = o->entry.items[i].hash;
1788 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1792 if (le_hash != o->data.hash)
1795 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1797 if (o->object.flags & OBJECT_COMPRESSED) {
1800 if (uncompress_startswith(o->data.payload, l,
1801 &f->compress_buffer, &f->compress_buffer_size,
1802 field, field_length, '=')) {
1806 if (!uncompress_blob(o->data.payload, l,
1807 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1810 *data = f->compress_buffer;
1811 *size = (size_t) rsize;
1816 return -EPROTONOSUPPORT;
1819 } else if (l >= field_length+1 &&
1820 memcmp(o->data.payload, field, field_length) == 0 &&
1821 o->data.payload[field_length] == '=') {
1825 if ((uint64_t) t != l)
1828 *data = o->data.payload;
1834 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1842 static int return_data(JournalFile *f, Object *o, const void **data, size_t *size) {
1846 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1849 /* We can't read objects larger than 4G on a 32bit machine */
1850 if ((uint64_t) t != l)
1853 if (o->object.flags & OBJECT_COMPRESSED) {
1857 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1860 *data = f->compress_buffer;
1861 *size = (size_t) rsize;
1863 return -EPROTONOSUPPORT;
1866 *data = o->data.payload;
1873 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1887 f = j->current_file;
1889 return -EADDRNOTAVAIL;
1891 if (f->current_offset <= 0)
1892 return -EADDRNOTAVAIL;
1894 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1898 n = journal_file_entry_n_items(o);
1899 if (j->current_field >= n)
1902 p = le64toh(o->entry.items[j->current_field].object_offset);
1903 le_hash = o->entry.items[j->current_field].hash;
1904 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1908 if (le_hash != o->data.hash)
1911 r = return_data(f, o, data, size);
1915 j->current_field ++;
1920 _public_ void sd_journal_restart_data(sd_journal *j) {
1924 j->current_field = 0;
1927 _public_ int sd_journal_get_fd(sd_journal *j) {
1933 if (j->inotify_fd >= 0)
1934 return j->inotify_fd;
1936 r = allocate_inotify(j);
1940 /* Iterate through all dirs again, to add them to the
1943 r = add_root_directory(j, j->path);
1945 r = add_search_paths(j);
1949 return j->inotify_fd;
1952 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1959 /* Is this a subdirectory we watch? */
1960 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
1964 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
1965 (endswith(e->name, ".journal") ||
1966 endswith(e->name, ".journal~"))) {
1968 /* Event for a journal file */
1970 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1971 r = add_file(j, d->path, e->name);
1973 log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r));
1975 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
1977 r = remove_file(j, d->path, e->name);
1979 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
1982 } else if (!d->is_root && e->len == 0) {
1984 /* Event for a subdirectory */
1986 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
1987 r = remove_directory(j, d);
1989 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
1993 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1995 /* Event for root directory */
1997 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1998 r = add_directory(j, d->path, e->name);
2000 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
2007 if (e->mask & IN_IGNORED)
2010 log_warning("Unknown inotify event.");
2013 static int determine_change(sd_journal *j) {
2018 b = j->current_invalidate_counter != j->last_invalidate_counter;
2019 j->last_invalidate_counter = j->current_invalidate_counter;
2021 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2024 _public_ int sd_journal_process(sd_journal *j) {
2025 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
2026 bool got_something = false;
2032 struct inotify_event *e;
2035 l = read(j->inotify_fd, buffer, sizeof(buffer));
2037 if (errno == EAGAIN || errno == EINTR)
2038 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2043 got_something = true;
2045 e = (struct inotify_event*) buffer;
2049 process_inotify_event(j, e);
2051 step = sizeof(struct inotify_event) + e->len;
2052 assert(step <= (size_t) l);
2054 e = (struct inotify_event*) ((uint8_t*) e + step);
2059 return determine_change(j);
2062 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2067 if (j->inotify_fd < 0) {
2069 /* This is the first invocation, hence create the
2071 r = sd_journal_get_fd(j);
2075 /* The journal might have changed since the context
2076 * object was created and we weren't watching before,
2077 * hence don't wait for anything, and return
2079 return determine_change(j);
2083 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2084 } while (r == -EINTR);
2089 return sd_journal_process(j);
2092 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2103 HASHMAP_FOREACH(f, j->files, i) {
2106 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2122 *from = MIN(fr, *from);
2128 return first ? 0 : 1;
2131 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2142 HASHMAP_FOREACH(f, j->files, i) {
2145 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2161 *from = MIN(fr, *from);
2167 return first ? 0 : 1;
2170 void journal_print_header(sd_journal *j) {
2173 bool newline = false;
2177 HASHMAP_FOREACH(f, j->files, i) {
2183 journal_file_print_header(f);
2187 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2197 HASHMAP_FOREACH(f, j->files, i) {
2200 if (fstat(f->fd, &st) < 0)
2203 sum += (uint64_t) st.st_blocks * 512ULL;
2210 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2217 if (!field_is_valid(field))
2224 free(j->unique_field);
2225 j->unique_field = f;
2226 j->unique_file = NULL;
2227 j->unique_offset = 0;
2232 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2243 if (!j->unique_field)
2246 k = strlen(j->unique_field);
2248 if (!j->unique_file) {
2249 j->unique_file = hashmap_first(j->files);
2250 if (!j->unique_file)
2252 j->unique_offset = 0;
2262 /* Proceed to next data object in the field's linked list */
2263 if (j->unique_offset == 0) {
2264 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2268 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2270 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2274 j->unique_offset = le64toh(o->data.next_field_offset);
2277 /* We reached the end of the list? Then start again, with the next file */
2278 if (j->unique_offset == 0) {
2281 n = hashmap_next(j->files, j->unique_file->path);
2289 /* We do not use the type context here, but 0 instead,
2290 * so that we can look at this data object at the same
2291 * time as one on another file */
2292 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2296 /* Let's do the type check by hand, since we used 0 context above. */
2297 if (o->object.type != OBJECT_DATA)
2300 r = return_data(j->unique_file, o, &odata, &ol);
2304 /* OK, now let's see if we already returned this data
2305 * object by checking if it exists in the earlier
2306 * traversed files. */
2308 HASHMAP_FOREACH(of, j->files, i) {
2312 if (of == j->unique_file)
2315 /* Skip this file it didn't have any fields
2317 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2318 le64toh(of->header->n_fields) <= 0)
2321 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2332 r = return_data(j->unique_file, o, data, l);
2340 _public_ void sd_journal_restart_unique(sd_journal *j) {
2344 j->unique_file = NULL;
2345 j->unique_offset = 0;