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"
37 #include "path-util.h"
40 #include "journal-internal.h"
43 #include "replace-var.h"
45 #define JOURNAL_FILES_MAX 1024
47 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
49 #define REPLACE_VAR_MAX 256
51 #define DEFAULT_DATA_THRESHOLD (64*1024)
53 /* We return an error here only if we didn't manage to
54 memorize the real error. */
55 static int set_put_error(sd_journal *j, int r) {
61 k = set_ensure_allocated(&j->errors, trivial_hash_func, trivial_compare_func);
65 return set_put(j->errors, INT_TO_PTR(r));
68 static void detach_location(sd_journal *j) {
74 j->current_file = NULL;
77 HASHMAP_FOREACH(f, j->files, i)
78 f->current_offset = 0;
81 static void reset_location(sd_journal *j) {
85 zero(j->current_location);
88 static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
90 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
92 assert(o->object.type == OBJECT_ENTRY);
95 l->seqnum = le64toh(o->entry.seqnum);
96 l->seqnum_id = f->header->seqnum_id;
97 l->realtime = le64toh(o->entry.realtime);
98 l->monotonic = le64toh(o->entry.monotonic);
99 l->boot_id = o->entry.boot_id;
100 l->xor_hash = le64toh(o->entry.xor_hash);
102 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
105 static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o,
106 direction_t direction, uint64_t offset) {
108 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
112 init_location(&j->current_location, type, f, o);
115 j->current_field = 0;
117 f->last_direction = direction;
118 f->current_offset = offset;
121 static int match_is_valid(const void *data, size_t size) {
129 if (startswith(data, "__"))
133 for (p = b; p < b + size; p++) {
141 if (*p >= 'A' && *p <= 'Z')
144 if (*p >= '0' && *p <= '9')
153 static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
154 const uint8_t *a = _a, *b = _b;
157 for (j = 0; j < s && j < t; j++) {
166 assert_not_reached("\"=\" not found");
169 static Match *match_new(Match *p, MatchType t) {
180 LIST_PREPEND(Match, matches, p->matches, m);
186 static void match_free(Match *m) {
190 match_free(m->matches);
193 LIST_REMOVE(Match, matches, m->parent->matches, m);
199 static void match_free_if_empty(Match *m) {
200 if (!m || m->matches)
206 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
207 Match *l3, *l4, *add_here = NULL, *m;
219 if (!match_is_valid(data, size))
226 * level 4: concrete matches */
229 j->level0 = match_new(NULL, MATCH_AND_TERM);
235 j->level1 = match_new(j->level0, MATCH_OR_TERM);
241 j->level2 = match_new(j->level1, MATCH_AND_TERM);
246 assert(j->level0->type == MATCH_AND_TERM);
247 assert(j->level1->type == MATCH_OR_TERM);
248 assert(j->level2->type == MATCH_AND_TERM);
250 le_hash = htole64(hash64(data, size));
252 LIST_FOREACH(matches, l3, j->level2->matches) {
253 assert(l3->type == MATCH_OR_TERM);
255 LIST_FOREACH(matches, l4, l3->matches) {
256 assert(l4->type == MATCH_DISCRETE);
258 /* Exactly the same match already? Then ignore
260 if (l4->le_hash == le_hash &&
262 memcmp(l4->data, data, size) == 0)
265 /* Same field? Then let's add this to this OR term */
266 if (same_field(data, size, l4->data, l4->size)) {
277 add_here = match_new(j->level2, MATCH_OR_TERM);
282 m = match_new(add_here, MATCH_DISCRETE);
286 m->le_hash = le_hash;
288 m->data = memdup(data, size);
297 match_free_if_empty(add_here);
298 match_free_if_empty(j->level2);
299 match_free_if_empty(j->level1);
300 match_free_if_empty(j->level0);
305 _public_ int sd_journal_add_conjunction(sd_journal *j) {
314 if (!j->level1->matches)
323 _public_ int sd_journal_add_disjunction(sd_journal *j) {
335 if (!j->level2->matches)
342 static char *match_make_string(Match *m) {
345 bool enclose = false;
350 if (m->type == MATCH_DISCRETE)
351 return strndup(m->data, m->size);
354 LIST_FOREACH(matches, i, m->matches) {
357 t = match_make_string(i);
364 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
379 r = strjoin("(", p, ")", NULL);
387 char *journal_make_match_string(sd_journal *j) {
390 return match_make_string(j->level0);
393 _public_ void sd_journal_flush_matches(sd_journal *j) {
399 match_free(j->level0);
401 j->level0 = j->level1 = j->level2 = NULL;
406 static int compare_entry_order(JournalFile *af, Object *_ao,
407 JournalFile *bf, uint64_t bp) {
417 /* The mmap cache might invalidate the object from the first
418 * file if we look at the one from the second file. Hence
419 * temporarily copy the header of the first one, and look at
421 ao = alloca(offsetof(EntryObject, items));
422 memcpy(ao, _ao, offsetof(EntryObject, items));
424 r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
426 return strcmp(af->path, bf->path);
428 /* We operate on two different files here, hence we can access
429 * two objects at the same time, which we normally can't.
431 * If contents and timestamps match, these entries are
432 * identical, even if the seqnum does not match */
434 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
435 ao->entry.monotonic == bo->entry.monotonic &&
436 ao->entry.realtime == bo->entry.realtime &&
437 ao->entry.xor_hash == bo->entry.xor_hash)
440 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
442 /* If this is from the same seqnum source, compare
444 a = le64toh(ao->entry.seqnum);
445 b = le64toh(bo->entry.seqnum);
452 /* Wow! This is weird, different data but the same
453 * seqnums? Something is borked, but let's make the
454 * best of it and compare by time. */
457 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
459 /* If the boot id matches compare monotonic time */
460 a = le64toh(ao->entry.monotonic);
461 b = le64toh(bo->entry.monotonic);
469 /* Otherwise compare UTC time */
470 a = le64toh(ao->entry.realtime);
471 b = le64toh(bo->entry.realtime);
478 /* Finally, compare by contents */
479 a = le64toh(ao->entry.xor_hash);
480 b = le64toh(bo->entry.xor_hash);
490 _pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
496 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
498 if (l->monotonic_set &&
499 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
501 le64toh(ao->entry.realtime) == l->realtime &&
503 le64toh(ao->entry.xor_hash) == l->xor_hash)
507 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
509 a = le64toh(ao->entry.seqnum);
517 if (l->monotonic_set &&
518 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
520 a = le64toh(ao->entry.monotonic);
522 if (a < l->monotonic)
524 if (a > l->monotonic)
528 if (l->realtime_set) {
530 a = le64toh(ao->entry.realtime);
538 if (l->xor_hash_set) {
539 a = le64toh(ao->entry.xor_hash);
550 static int next_for_match(
554 uint64_t after_offset,
555 direction_t direction,
567 if (m->type == MATCH_DISCRETE) {
570 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
574 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
576 } else if (m->type == MATCH_OR_TERM) {
579 /* Find the earliest match beyond after_offset */
581 LIST_FOREACH(matches, i, m->matches) {
584 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
588 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
596 } else if (m->type == MATCH_AND_TERM) {
597 Match *i, *last_moved;
599 /* Always jump to the next matching entry and repeat
600 * this until we find an offset that matches for all
606 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
610 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
611 last_moved = m->matches;
613 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
616 r = next_for_match(j, i, f, np, direction, NULL, &cp);
620 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
621 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
630 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
642 static int find_location_for_match(
646 direction_t direction,
656 if (m->type == MATCH_DISCRETE) {
659 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
663 /* FIXME: missing: find by monotonic */
665 if (j->current_location.type == LOCATION_HEAD)
666 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
667 if (j->current_location.type == LOCATION_TAIL)
668 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
669 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
670 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
671 if (j->current_location.monotonic_set) {
672 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
676 if (j->current_location.realtime_set)
677 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
679 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
681 } else if (m->type == MATCH_OR_TERM) {
686 /* Find the earliest match */
688 LIST_FOREACH(matches, i, m->matches) {
691 r = find_location_for_match(j, i, f, direction, NULL, &cp);
695 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
703 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
718 assert(m->type == MATCH_AND_TERM);
720 /* First jump to the last match, and then find the
721 * next one where all matches match */
726 LIST_FOREACH(matches, i, m->matches) {
729 r = find_location_for_match(j, i, f, direction, NULL, &cp);
733 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
737 return next_for_match(j, m, f, np, direction, ret, offset);
741 static int find_location_with_matches(
744 direction_t direction,
756 /* No matches is simple */
758 if (j->current_location.type == LOCATION_HEAD)
759 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
760 if (j->current_location.type == LOCATION_TAIL)
761 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
762 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
763 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
764 if (j->current_location.monotonic_set) {
765 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
769 if (j->current_location.realtime_set)
770 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
772 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
774 return find_location_for_match(j, j->level0, f, direction, ret, offset);
777 static int next_with_matches(
780 direction_t direction,
795 /* No matches is easy. We simple advance the file
798 return journal_file_next_entry(f, c, cp, direction, ret, offset);
800 /* If we have a match then we look for the next matching entry
801 * with an offset at least one step larger */
802 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
805 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
813 if (f->last_direction == direction && f->current_offset > 0) {
814 cp = f->current_offset;
816 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
820 r = next_with_matches(j, f, direction, &c, &cp);
824 r = find_location_with_matches(j, f, direction, &c, &cp);
829 /* OK, we found the spot, now let's advance until an entry
830 * that is actually different from what we were previously
831 * looking at. This is necessary to handle entries which exist
832 * in two (or more) journal files, and which shall all be
833 * suppressed but one. */
838 if (j->current_location.type == LOCATION_DISCRETE) {
841 k = compare_with_location(f, c, &j->current_location);
842 if (direction == DIRECTION_DOWN)
857 r = next_with_matches(j, f, direction, &c, &cp);
863 static int real_journal_next(sd_journal *j, direction_t direction) {
864 JournalFile *f, *new_file = NULL;
865 uint64_t new_offset = 0;
874 HASHMAP_FOREACH(f, j->files, i) {
877 r = next_beyond_location(j, f, direction, &o, &p);
879 log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
889 k = compare_entry_order(f, o, new_file, new_offset);
891 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
903 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
907 set_location(j, LOCATION_DISCRETE, new_file, o, direction, new_offset);
912 _public_ int sd_journal_next(sd_journal *j) {
913 return real_journal_next(j, DIRECTION_DOWN);
916 _public_ int sd_journal_previous(sd_journal *j) {
917 return real_journal_next(j, DIRECTION_UP);
920 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
927 /* If this is not a discrete skip, then at least
928 * resolve the current location */
929 if (j->current_location.type != LOCATION_DISCRETE)
930 return real_journal_next(j, direction);
936 r = real_journal_next(j, direction);
950 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
951 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
954 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
955 return real_journal_next_skip(j, DIRECTION_UP, skip);
958 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
961 char bid[33], sid[33];
968 if (!j->current_file || j->current_file->current_offset <= 0)
969 return -EADDRNOTAVAIL;
971 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
975 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
976 sd_id128_to_string(o->entry.boot_id, bid);
979 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
980 sid, le64toh(o->entry.seqnum),
981 bid, le64toh(o->entry.monotonic),
982 le64toh(o->entry.realtime),
983 le64toh(o->entry.xor_hash)) < 0)
989 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
992 unsigned long long seqnum, monotonic, realtime, xor_hash;
994 seqnum_id_set = false,
997 monotonic_set = false,
998 realtime_set = false,
999 xor_hash_set = false;
1000 sd_id128_t seqnum_id, boot_id;
1004 if (isempty(cursor))
1007 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
1011 if (l < 2 || w[1] != '=')
1014 item = strndup(w, l);
1021 seqnum_id_set = true;
1022 k = sd_id128_from_string(item+2, &seqnum_id);
1027 if (sscanf(item+2, "%llx", &seqnum) != 1)
1033 k = sd_id128_from_string(item+2, &boot_id);
1037 monotonic_set = true;
1038 if (sscanf(item+2, "%llx", &monotonic) != 1)
1043 realtime_set = true;
1044 if (sscanf(item+2, "%llx", &realtime) != 1)
1049 xor_hash_set = true;
1050 if (sscanf(item+2, "%llx", &xor_hash) != 1)
1061 if ((!seqnum_set || !seqnum_id_set) &&
1062 (!monotonic_set || !boot_id_set) &&
1068 j->current_location.type = LOCATION_SEEK;
1071 j->current_location.realtime = (uint64_t) realtime;
1072 j->current_location.realtime_set = true;
1075 if (seqnum_set && seqnum_id_set) {
1076 j->current_location.seqnum = (uint64_t) seqnum;
1077 j->current_location.seqnum_id = seqnum_id;
1078 j->current_location.seqnum_set = true;
1081 if (monotonic_set && boot_id_set) {
1082 j->current_location.monotonic = (uint64_t) monotonic;
1083 j->current_location.boot_id = boot_id;
1084 j->current_location.monotonic_set = true;
1088 j->current_location.xor_hash = (uint64_t) xor_hash;
1089 j->current_location.xor_hash_set = true;
1095 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1103 if (isempty(cursor))
1106 if (!j->current_file || j->current_file->current_offset <= 0)
1107 return -EADDRNOTAVAIL;
1109 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1113 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
1114 _cleanup_free_ char *item = NULL;
1116 unsigned long long ll;
1119 if (l < 2 || w[1] != '=')
1122 item = strndup(w, l);
1129 k = sd_id128_from_string(item+2, &id);
1132 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1137 if (sscanf(item+2, "%llx", &ll) != 1)
1139 if (ll != le64toh(o->entry.seqnum))
1144 k = sd_id128_from_string(item+2, &id);
1147 if (!sd_id128_equal(id, o->entry.boot_id))
1152 if (sscanf(item+2, "%llx", &ll) != 1)
1154 if (ll != le64toh(o->entry.monotonic))
1159 if (sscanf(item+2, "%llx", &ll) != 1)
1161 if (ll != le64toh(o->entry.realtime))
1166 if (sscanf(item+2, "%llx", &ll) != 1)
1168 if (ll != le64toh(o->entry.xor_hash))
1178 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1183 j->current_location.type = LOCATION_SEEK;
1184 j->current_location.boot_id = boot_id;
1185 j->current_location.monotonic = usec;
1186 j->current_location.monotonic_set = true;
1191 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1196 j->current_location.type = LOCATION_SEEK;
1197 j->current_location.realtime = usec;
1198 j->current_location.realtime_set = true;
1203 _public_ int sd_journal_seek_head(sd_journal *j) {
1208 j->current_location.type = LOCATION_HEAD;
1213 _public_ int sd_journal_seek_tail(sd_journal *j) {
1218 j->current_location.type = LOCATION_TAIL;
1223 static void check_network(sd_journal *j, int fd) {
1231 if (fstatfs(fd, &sfs) < 0)
1235 F_TYPE_CMP(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1236 F_TYPE_CMP(sfs.f_type, CODA_SUPER_MAGIC) ||
1237 F_TYPE_CMP(sfs.f_type, NCP_SUPER_MAGIC) ||
1238 F_TYPE_CMP(sfs.f_type, NFS_SUPER_MAGIC) ||
1239 F_TYPE_CMP(sfs.f_type, SMB_SUPER_MAGIC);
1242 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1243 const char *full, *tilded, *atted;
1245 full = strappend(prefix, ".journal");
1246 tilded = strappenda(full, "~");
1247 atted = strappenda(prefix, "@");
1249 return streq(filename, full) ||
1250 streq(filename, tilded) ||
1251 startswith(filename, atted);
1254 static bool file_type_wanted(int flags, const char *filename) {
1255 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1258 /* no flags set → every type is OK */
1259 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1262 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1265 if (flags & SD_JOURNAL_CURRENT_USER) {
1266 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1268 assert_se(snprintf(prefix, sizeof(prefix), "user-%lu", (unsigned long) getuid())
1269 < (int) sizeof(prefix));
1271 if (file_has_type_prefix(prefix, filename))
1278 static int add_any_file(sd_journal *j, const char *path) {
1285 if (hashmap_get(j->files, path))
1288 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1289 log_warning("Too many open journal files, not adding %s.", path);
1290 return set_put_error(j, -ETOOMANYREFS);
1293 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1297 /* journal_file_dump(f); */
1299 r = hashmap_put(j->files, f->path, f);
1301 journal_file_close(f);
1305 log_debug("File %s added.", f->path);
1307 check_network(j, f->fd);
1309 j->current_invalidate_counter ++;
1314 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1315 _cleanup_free_ char *path = NULL;
1322 if (j->no_new_files ||
1323 !file_type_wanted(j->flags, filename))
1326 path = strjoin(prefix, "/", filename, NULL);
1330 r = add_any_file(j, path);
1336 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1344 path = strjoin(prefix, "/", filename, NULL);
1348 f = hashmap_get(j->files, path);
1353 hashmap_remove(j->files, f->path);
1355 log_debug("File %s removed.", f->path);
1357 if (j->current_file == f) {
1358 j->current_file = NULL;
1359 j->current_field = 0;
1362 if (j->unique_file == f) {
1363 j->unique_file = NULL;
1364 j->unique_offset = 0;
1367 journal_file_close(f);
1369 j->current_invalidate_counter ++;
1374 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1375 _cleanup_free_ char *path = NULL;
1377 _cleanup_closedir_ DIR *d = NULL;
1385 log_debug("Considering %s/%s.", prefix, dirname);
1387 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1388 (sd_id128_from_string(dirname, &id) < 0 ||
1389 sd_id128_get_machine(&mid) < 0 ||
1390 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1393 path = strjoin(prefix, "/", dirname, NULL);
1399 log_debug("Failed to open %s: %m", path);
1400 if (errno == ENOENT)
1405 m = hashmap_get(j->directories_by_path, path);
1407 m = new0(Directory, 1);
1414 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1419 path = NULL; /* avoid freeing in cleanup */
1420 j->current_invalidate_counter ++;
1422 log_debug("Directory %s added.", m->path);
1424 } else if (m->is_root)
1427 if (m->wd <= 0 && j->inotify_fd >= 0) {
1429 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1430 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1431 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1434 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1435 inotify_rm_watch(j->inotify_fd, m->wd);
1440 union dirent_storage buf;
1442 r = readdir_r(d, &buf.de, &de);
1446 if (dirent_is_file_with_suffix(de, ".journal") ||
1447 dirent_is_file_with_suffix(de, ".journal~")) {
1448 r = add_file(j, m->path, de->d_name);
1450 log_debug("Failed to add file %s/%s: %s",
1451 m->path, de->d_name, strerror(-r));
1452 r = set_put_error(j, r);
1459 check_network(j, dirfd(d));
1464 static int add_root_directory(sd_journal *j, const char *p) {
1465 _cleanup_closedir_ DIR *d = NULL;
1472 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1473 !path_startswith(p, "/run"))
1480 m = hashmap_get(j->directories_by_path, p);
1482 m = new0(Directory, 1);
1487 m->path = strdup(p);
1493 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1499 j->current_invalidate_counter ++;
1501 log_debug("Root directory %s added.", m->path);
1503 } else if (!m->is_root)
1506 if (m->wd <= 0 && j->inotify_fd >= 0) {
1508 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1509 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1512 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1513 inotify_rm_watch(j->inotify_fd, m->wd);
1516 if (j->no_new_files)
1521 union dirent_storage buf;
1524 r = readdir_r(d, &buf.de, &de);
1528 if (dirent_is_file_with_suffix(de, ".journal") ||
1529 dirent_is_file_with_suffix(de, ".journal~")) {
1530 r = add_file(j, m->path, de->d_name);
1532 log_debug("Failed to add file %s/%s: %s",
1533 m->path, de->d_name, strerror(-r));
1534 r = set_put_error(j, r);
1538 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1539 sd_id128_from_string(de->d_name, &id) >= 0) {
1541 r = add_directory(j, m->path, de->d_name);
1543 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1547 check_network(j, dirfd(d));
1552 static int remove_directory(sd_journal *j, Directory *d) {
1556 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1558 if (j->inotify_fd >= 0)
1559 inotify_rm_watch(j->inotify_fd, d->wd);
1562 hashmap_remove(j->directories_by_path, d->path);
1565 log_debug("Root directory %s removed.", d->path);
1567 log_debug("Directory %s removed.", d->path);
1575 static int add_search_paths(sd_journal *j) {
1577 const char search_paths[] =
1578 "/run/log/journal\0"
1579 "/var/log/journal\0";
1584 /* We ignore most errors here, since the idea is to only open
1585 * what's actually accessible, and ignore the rest. */
1587 NULSTR_FOREACH(p, search_paths) {
1588 r = add_root_directory(j, p);
1589 if (r < 0 && r != -ENOENT) {
1590 r = set_put_error(j, r);
1599 static int add_current_paths(sd_journal *j) {
1604 assert(j->no_new_files);
1606 /* Simply adds all directories for files we have open as
1607 * "root" directories. We don't expect errors here, so we
1608 * treat them as fatal. */
1610 HASHMAP_FOREACH(f, j->files, i) {
1612 _cleanup_free_ char *dir;
1614 dir = dirname_malloc(f->path);
1618 r = add_root_directory(j, dir);
1620 set_put_error(j, r);
1629 static int allocate_inotify(sd_journal *j) {
1632 if (j->inotify_fd < 0) {
1633 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1634 if (j->inotify_fd < 0)
1638 if (!j->directories_by_wd) {
1639 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1640 if (!j->directories_by_wd)
1647 static sd_journal *journal_new(int flags, const char *path) {
1650 j = new0(sd_journal, 1);
1656 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1659 j->path = strdup(path);
1664 j->files = hashmap_new(string_hash_func, string_compare_func);
1665 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1666 j->mmap = mmap_cache_new();
1667 if (!j->files || !j->directories_by_path || !j->mmap)
1673 sd_journal_close(j);
1677 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1684 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1685 SD_JOURNAL_RUNTIME_ONLY|
1687 SD_JOURNAL_CURRENT_USER))
1690 j = journal_new(flags, NULL);
1694 r = add_search_paths(j);
1702 sd_journal_close(j);
1707 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1720 j = journal_new(flags, path);
1724 r = add_root_directory(j, path);
1726 set_put_error(j, r);
1734 sd_journal_close(j);
1739 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1750 j = journal_new(flags, NULL);
1754 STRV_FOREACH(path, paths) {
1755 r = add_any_file(j, *path);
1757 log_error("Failed to open %s: %s", *path, strerror(-r));
1762 j->no_new_files = true;
1768 sd_journal_close(j);
1773 _public_ void sd_journal_close(sd_journal *j) {
1780 sd_journal_flush_matches(j);
1782 while ((f = hashmap_steal_first(j->files)))
1783 journal_file_close(f);
1785 hashmap_free(j->files);
1787 while ((d = hashmap_first(j->directories_by_path)))
1788 remove_directory(j, d);
1790 while ((d = hashmap_first(j->directories_by_wd)))
1791 remove_directory(j, d);
1793 hashmap_free(j->directories_by_path);
1794 hashmap_free(j->directories_by_wd);
1796 if (j->inotify_fd >= 0)
1797 close_nointr_nofail(j->inotify_fd);
1800 mmap_cache_unref(j->mmap);
1803 free(j->unique_field);
1804 set_free(j->errors);
1808 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1818 f = j->current_file;
1820 return -EADDRNOTAVAIL;
1822 if (f->current_offset <= 0)
1823 return -EADDRNOTAVAIL;
1825 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1829 *ret = le64toh(o->entry.realtime);
1833 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1842 f = j->current_file;
1844 return -EADDRNOTAVAIL;
1846 if (f->current_offset <= 0)
1847 return -EADDRNOTAVAIL;
1849 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1854 *ret_boot_id = o->entry.boot_id;
1856 r = sd_id128_get_boot(&id);
1860 if (!sd_id128_equal(id, o->entry.boot_id))
1865 *ret = le64toh(o->entry.monotonic);
1870 static bool field_is_valid(const char *field) {
1878 if (startswith(field, "__"))
1881 for (p = field; *p; p++) {
1886 if (*p >= 'A' && *p <= 'Z')
1889 if (*p >= '0' && *p <= '9')
1898 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1901 size_t field_length;
1914 if (!field_is_valid(field))
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 field_length = strlen(field);
1930 n = journal_file_entry_n_items(o);
1931 for (i = 0; i < n; i++) {
1936 p = le64toh(o->entry.items[i].object_offset);
1937 le_hash = o->entry.items[i].hash;
1938 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1942 if (le_hash != o->data.hash)
1945 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1947 if (o->object.flags & OBJECT_COMPRESSED) {
1950 if (uncompress_startswith(o->data.payload, l,
1951 &f->compress_buffer, &f->compress_buffer_size,
1952 field, field_length, '=')) {
1956 if (!uncompress_blob(o->data.payload, l,
1957 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1961 *data = f->compress_buffer;
1962 *size = (size_t) rsize;
1967 return -EPROTONOSUPPORT;
1970 } else if (l >= field_length+1 &&
1971 memcmp(o->data.payload, field, field_length) == 0 &&
1972 o->data.payload[field_length] == '=') {
1976 if ((uint64_t) t != l)
1979 *data = o->data.payload;
1985 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1993 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
1997 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2000 /* We can't read objects larger than 4G on a 32bit machine */
2001 if ((uint64_t) t != l)
2004 if (o->object.flags & OBJECT_COMPRESSED) {
2008 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, j->data_threshold))
2011 *data = f->compress_buffer;
2012 *size = (size_t) rsize;
2014 return -EPROTONOSUPPORT;
2017 *data = o->data.payload;
2024 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2038 f = j->current_file;
2040 return -EADDRNOTAVAIL;
2042 if (f->current_offset <= 0)
2043 return -EADDRNOTAVAIL;
2045 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2049 n = journal_file_entry_n_items(o);
2050 if (j->current_field >= n)
2053 p = le64toh(o->entry.items[j->current_field].object_offset);
2054 le_hash = o->entry.items[j->current_field].hash;
2055 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2059 if (le_hash != o->data.hash)
2062 r = return_data(j, f, o, data, size);
2066 j->current_field ++;
2071 _public_ void sd_journal_restart_data(sd_journal *j) {
2075 j->current_field = 0;
2078 _public_ int sd_journal_get_fd(sd_journal *j) {
2084 if (j->inotify_fd >= 0)
2085 return j->inotify_fd;
2087 r = allocate_inotify(j);
2091 /* Iterate through all dirs again, to add them to the
2093 if (j->no_new_files)
2094 r = add_current_paths(j);
2096 r = add_root_directory(j, j->path);
2098 r = add_search_paths(j);
2102 return j->inotify_fd;
2105 _public_ int sd_journal_get_events(sd_journal *j) {
2111 fd = sd_journal_get_fd(j);
2118 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2126 fd = sd_journal_get_fd(j);
2130 if (!j->on_network) {
2131 *timeout_usec = (uint64_t) -1;
2135 /* If we are on the network we need to regularly check for
2136 * changes manually */
2138 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2142 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2149 /* Is this a subdirectory we watch? */
2150 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2154 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2155 (endswith(e->name, ".journal") ||
2156 endswith(e->name, ".journal~"))) {
2158 /* Event for a journal file */
2160 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2161 r = add_file(j, d->path, e->name);
2163 log_debug("Failed to add file %s/%s: %s",
2164 d->path, e->name, strerror(-r));
2165 set_put_error(j, r);
2168 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2170 r = remove_file(j, d->path, e->name);
2172 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
2175 } else if (!d->is_root && e->len == 0) {
2177 /* Event for a subdirectory */
2179 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2180 r = remove_directory(j, d);
2182 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
2186 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2188 /* Event for root directory */
2190 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2191 r = add_directory(j, d->path, e->name);
2193 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
2200 if (e->mask & IN_IGNORED)
2203 log_warning("Unknown inotify event.");
2206 static int determine_change(sd_journal *j) {
2211 b = j->current_invalidate_counter != j->last_invalidate_counter;
2212 j->last_invalidate_counter = j->current_invalidate_counter;
2214 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2217 _public_ int sd_journal_process(sd_journal *j) {
2218 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
2219 bool got_something = false;
2224 j->last_process_usec = now(CLOCK_MONOTONIC);
2227 struct inotify_event *e;
2230 l = read(j->inotify_fd, buffer, sizeof(buffer));
2232 if (errno == EAGAIN || errno == EINTR)
2233 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2238 got_something = true;
2240 e = (struct inotify_event*) buffer;
2244 process_inotify_event(j, e);
2246 step = sizeof(struct inotify_event) + e->len;
2247 assert(step <= (size_t) l);
2249 e = (struct inotify_event*) ((uint8_t*) e + step);
2254 return determine_change(j);
2257 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2263 if (j->inotify_fd < 0) {
2265 /* This is the first invocation, hence create the
2267 r = sd_journal_get_fd(j);
2271 /* The journal might have changed since the context
2272 * object was created and we weren't watching before,
2273 * hence don't wait for anything, and return
2275 return determine_change(j);
2278 r = sd_journal_get_timeout(j, &t);
2282 if (t != (uint64_t) -1) {
2285 n = now(CLOCK_MONOTONIC);
2286 t = t > n ? t - n : 0;
2288 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2293 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2294 } while (r == -EINTR);
2299 return sd_journal_process(j);
2302 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2315 HASHMAP_FOREACH(f, j->files, i) {
2318 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2334 *from = MIN(fr, *from);
2340 return first ? 0 : 1;
2343 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2356 HASHMAP_FOREACH(f, j->files, i) {
2359 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2375 *from = MIN(fr, *from);
2381 return first ? 0 : 1;
2384 void journal_print_header(sd_journal *j) {
2387 bool newline = false;
2391 HASHMAP_FOREACH(f, j->files, i) {
2397 journal_file_print_header(f);
2401 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2411 HASHMAP_FOREACH(f, j->files, i) {
2414 if (fstat(f->fd, &st) < 0)
2417 sum += (uint64_t) st.st_blocks * 512ULL;
2424 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2431 if (!field_is_valid(field))
2438 free(j->unique_field);
2439 j->unique_field = f;
2440 j->unique_file = NULL;
2441 j->unique_offset = 0;
2446 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2457 if (!j->unique_field)
2460 k = strlen(j->unique_field);
2462 if (!j->unique_file) {
2463 j->unique_file = hashmap_first(j->files);
2464 if (!j->unique_file)
2466 j->unique_offset = 0;
2476 /* Proceed to next data object in the field's linked list */
2477 if (j->unique_offset == 0) {
2478 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2482 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2484 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2488 j->unique_offset = le64toh(o->data.next_field_offset);
2491 /* We reached the end of the list? Then start again, with the next file */
2492 if (j->unique_offset == 0) {
2495 n = hashmap_next(j->files, j->unique_file->path);
2503 /* We do not use the type context here, but 0 instead,
2504 * so that we can look at this data object at the same
2505 * time as one on another file */
2506 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2510 /* Let's do the type check by hand, since we used 0 context above. */
2511 if (o->object.type != OBJECT_DATA)
2514 r = return_data(j, j->unique_file, o, &odata, &ol);
2518 /* OK, now let's see if we already returned this data
2519 * object by checking if it exists in the earlier
2520 * traversed files. */
2522 HASHMAP_FOREACH(of, j->files, i) {
2526 if (of == j->unique_file)
2529 /* Skip this file it didn't have any fields
2531 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2532 le64toh(of->header->n_fields) <= 0)
2535 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2546 r = return_data(j, j->unique_file, o, data, l);
2554 _public_ void sd_journal_restart_unique(sd_journal *j) {
2558 j->unique_file = NULL;
2559 j->unique_offset = 0;
2562 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2566 return !j->on_network;
2569 static char *lookup_field(const char *field, void *userdata) {
2570 sd_journal *j = userdata;
2578 r = sd_journal_get_data(j, field, &data, &size);
2580 size > REPLACE_VAR_MAX)
2581 return strdup(field);
2583 d = strlen(field) + 1;
2585 return strndup((const char*) data + d, size - d);
2588 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2592 _cleanup_free_ char *text = NULL, *cid = NULL;
2601 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2605 cid = strndup((const char*) data + 11, size - 11);
2609 r = sd_id128_from_string(cid, &id);
2613 r = catalog_get(CATALOG_DATABASE, id, &text);
2617 t = replace_var(text, lookup_field, j);
2625 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2629 return catalog_get(CATALOG_DATABASE, id, ret);
2632 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2636 j->data_threshold = sz;
2640 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2646 *sz = j->data_threshold;