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 #include "replace-var.h"
44 #define JOURNAL_FILES_MAX 1024
46 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
48 #define REPLACE_VAR_MAX 256
50 #define DEFAULT_DATA_THRESHOLD (64*1024)
52 /* We return an error here only if we didn't manage to
53 memorize the real error. */
54 static int set_put_error(sd_journal *j, int r) {
60 k = set_ensure_allocated(&j->errors, trivial_hash_func, trivial_compare_func);
64 return set_put(j->errors, INT_TO_PTR(r));
67 static void detach_location(sd_journal *j) {
73 j->current_file = NULL;
76 HASHMAP_FOREACH(f, j->files, i)
77 f->current_offset = 0;
80 static void reset_location(sd_journal *j) {
84 zero(j->current_location);
87 static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
89 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
91 assert(o->object.type == OBJECT_ENTRY);
94 l->seqnum = le64toh(o->entry.seqnum);
95 l->seqnum_id = f->header->seqnum_id;
96 l->realtime = le64toh(o->entry.realtime);
97 l->monotonic = le64toh(o->entry.monotonic);
98 l->boot_id = o->entry.boot_id;
99 l->xor_hash = le64toh(o->entry.xor_hash);
101 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
104 static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o, uint64_t offset) {
106 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
110 init_location(&j->current_location, type, f, o);
113 j->current_file->current_offset = 0;
116 j->current_field = 0;
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++) {
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);
381 r = strjoin("(", p, ")", NULL);
389 char *journal_make_match_string(sd_journal *j) {
392 return match_make_string(j->level0);
395 _public_ void sd_journal_flush_matches(sd_journal *j) {
401 match_free(j->level0);
403 j->level0 = j->level1 = j->level2 = NULL;
408 static int compare_entry_order(JournalFile *af, Object *_ao,
409 JournalFile *bf, uint64_t bp) {
419 /* The mmap cache might invalidate the object from the first
420 * file if we look at the one from the second file. Hence
421 * temporarily copy the header of the first one, and look at
423 ao = alloca(offsetof(EntryObject, items));
424 memcpy(ao, _ao, offsetof(EntryObject, items));
426 r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
428 return strcmp(af->path, bf->path);
430 /* We operate on two different files here, hence we can access
431 * two objects at the same time, which we normally can't.
433 * If contents and timestamps match, these entries are
434 * identical, even if the seqnum does not match */
436 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
437 ao->entry.monotonic == bo->entry.monotonic &&
438 ao->entry.realtime == bo->entry.realtime &&
439 ao->entry.xor_hash == bo->entry.xor_hash)
442 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
444 /* If this is from the same seqnum source, compare
446 a = le64toh(ao->entry.seqnum);
447 b = le64toh(bo->entry.seqnum);
454 /* Wow! This is weird, different data but the same
455 * seqnums? Something is borked, but let's make the
456 * best of it and compare by time. */
459 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
461 /* If the boot id matches compare monotonic time */
462 a = le64toh(ao->entry.monotonic);
463 b = le64toh(bo->entry.monotonic);
471 /* Otherwise compare UTC time */
472 a = le64toh(ao->entry.realtime);
473 b = le64toh(bo->entry.realtime);
480 /* Finally, compare by contents */
481 a = le64toh(ao->entry.xor_hash);
482 b = le64toh(bo->entry.xor_hash);
492 _pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
498 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
500 if (l->monotonic_set &&
501 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
503 le64toh(ao->entry.realtime) == l->realtime &&
505 le64toh(ao->entry.xor_hash) == l->xor_hash)
509 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
511 a = le64toh(ao->entry.seqnum);
519 if (l->monotonic_set &&
520 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
522 a = le64toh(ao->entry.monotonic);
524 if (a < l->monotonic)
526 if (a > l->monotonic)
530 if (l->realtime_set) {
532 a = le64toh(ao->entry.realtime);
540 if (l->xor_hash_set) {
541 a = le64toh(ao->entry.xor_hash);
552 static int next_for_match(
556 uint64_t after_offset,
557 direction_t direction,
569 if (m->type == MATCH_DISCRETE) {
572 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
576 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
578 } else if (m->type == MATCH_OR_TERM) {
581 /* Find the earliest match beyond after_offset */
583 LIST_FOREACH(matches, i, m->matches) {
586 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
590 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
595 } else if (m->type == MATCH_AND_TERM) {
597 bool continue_looking;
599 /* Always jump to the next matching entry and repeat
600 * this until we fine and offset that matches for all
608 continue_looking = false;
610 LIST_FOREACH(matches, i, m->matches) {
614 limit = after_offset;
615 else if (direction == DIRECTION_DOWN)
616 limit = MAX(np, after_offset);
618 limit = MIN(np, after_offset);
620 r = next_for_match(j, i, f, limit, direction, NULL, &cp);
624 if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) &&
625 (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))) {
627 continue_looking = true;
631 } while (continue_looking);
637 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
649 static int find_location_for_match(
653 direction_t direction,
663 if (m->type == MATCH_DISCRETE) {
666 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
670 /* FIXME: missing: find by monotonic */
672 if (j->current_location.type == LOCATION_HEAD)
673 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
674 if (j->current_location.type == LOCATION_TAIL)
675 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
676 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
677 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
678 if (j->current_location.monotonic_set) {
679 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
683 if (j->current_location.realtime_set)
684 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
686 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
688 } else if (m->type == MATCH_OR_TERM) {
693 /* Find the earliest match */
695 LIST_FOREACH(matches, i, m->matches) {
698 r = find_location_for_match(j, i, f, direction, NULL, &cp);
702 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
710 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
725 assert(m->type == MATCH_AND_TERM);
727 /* First jump to the last match, and then find the
728 * next one where all matches match */
733 LIST_FOREACH(matches, i, m->matches) {
736 r = find_location_for_match(j, i, f, direction, NULL, &cp);
740 if (np == 0 || (direction == DIRECTION_DOWN ? np < cp : np > cp))
744 return next_for_match(j, m, f, np, direction, ret, offset);
748 static int find_location_with_matches(
751 direction_t direction,
763 /* No matches is simple */
765 if (j->current_location.type == LOCATION_HEAD)
766 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
767 if (j->current_location.type == LOCATION_TAIL)
768 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
769 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
770 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
771 if (j->current_location.monotonic_set) {
772 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
776 if (j->current_location.realtime_set)
777 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
779 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
781 return find_location_for_match(j, j->level0, f, direction, ret, offset);
784 static int next_with_matches(
787 direction_t direction,
802 /* No matches is easy. We simple advance the file
805 return journal_file_next_entry(f, c, cp, direction, ret, offset);
807 /* If we have a match then we look for the next matching entry
808 * with an offset at least one step larger */
809 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
812 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
820 if (f->current_offset > 0) {
821 cp = f->current_offset;
823 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
827 r = next_with_matches(j, f, direction, &c, &cp);
831 r = find_location_with_matches(j, f, direction, &c, &cp);
836 /* OK, we found the spot, now let's advance until to an entry
837 * that is actually different from what we were previously
838 * looking at. This is necessary to handle entries which exist
839 * in two (or more) journal files, and which shall all be
840 * suppressed but one. */
845 if (j->current_location.type == LOCATION_DISCRETE) {
848 k = compare_with_location(f, c, &j->current_location);
849 if (direction == DIRECTION_DOWN)
864 r = next_with_matches(j, f, direction, &c, &cp);
870 static int real_journal_next(sd_journal *j, direction_t direction) {
871 JournalFile *f, *new_file = NULL;
872 uint64_t new_offset = 0;
881 HASHMAP_FOREACH(f, j->files, i) {
884 r = next_beyond_location(j, f, direction, &o, &p);
886 log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
896 k = compare_entry_order(f, o, new_file, new_offset);
898 if (direction == DIRECTION_DOWN)
913 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
917 set_location(j, LOCATION_DISCRETE, new_file, o, new_offset);
922 _public_ int sd_journal_next(sd_journal *j) {
923 return real_journal_next(j, DIRECTION_DOWN);
926 _public_ int sd_journal_previous(sd_journal *j) {
927 return real_journal_next(j, DIRECTION_UP);
930 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
937 /* If this is not a discrete skip, then at least
938 * resolve the current location */
939 if (j->current_location.type != LOCATION_DISCRETE)
940 return real_journal_next(j, direction);
946 r = real_journal_next(j, direction);
960 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
961 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
964 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
965 return real_journal_next_skip(j, DIRECTION_UP, skip);
968 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
971 char bid[33], sid[33];
978 if (!j->current_file || j->current_file->current_offset <= 0)
979 return -EADDRNOTAVAIL;
981 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
985 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
986 sd_id128_to_string(o->entry.boot_id, bid);
989 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx",
990 sid, (unsigned long long) le64toh(o->entry.seqnum),
991 bid, (unsigned long long) le64toh(o->entry.monotonic),
992 (unsigned long long) le64toh(o->entry.realtime),
993 (unsigned long long) le64toh(o->entry.xor_hash)) < 0)
999 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
1002 unsigned long long seqnum, monotonic, realtime, xor_hash;
1004 seqnum_id_set = false,
1006 boot_id_set = false,
1007 monotonic_set = false,
1008 realtime_set = false,
1009 xor_hash_set = false;
1010 sd_id128_t seqnum_id, boot_id;
1014 if (isempty(cursor))
1017 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
1021 if (l < 2 || w[1] != '=')
1024 item = strndup(w, l);
1031 seqnum_id_set = true;
1032 k = sd_id128_from_string(item+2, &seqnum_id);
1037 if (sscanf(item+2, "%llx", &seqnum) != 1)
1043 k = sd_id128_from_string(item+2, &boot_id);
1047 monotonic_set = true;
1048 if (sscanf(item+2, "%llx", &monotonic) != 1)
1053 realtime_set = true;
1054 if (sscanf(item+2, "%llx", &realtime) != 1)
1059 xor_hash_set = true;
1060 if (sscanf(item+2, "%llx", &xor_hash) != 1)
1071 if ((!seqnum_set || !seqnum_id_set) &&
1072 (!monotonic_set || !boot_id_set) &&
1078 j->current_location.type = LOCATION_SEEK;
1081 j->current_location.realtime = (uint64_t) realtime;
1082 j->current_location.realtime_set = true;
1085 if (seqnum_set && seqnum_id_set) {
1086 j->current_location.seqnum = (uint64_t) seqnum;
1087 j->current_location.seqnum_id = seqnum_id;
1088 j->current_location.seqnum_set = true;
1091 if (monotonic_set && boot_id_set) {
1092 j->current_location.monotonic = (uint64_t) monotonic;
1093 j->current_location.boot_id = boot_id;
1094 j->current_location.monotonic_set = true;
1098 j->current_location.xor_hash = (uint64_t) xor_hash;
1099 j->current_location.xor_hash_set = true;
1105 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1113 if (isempty(cursor))
1116 if (!j->current_file || j->current_file->current_offset <= 0)
1117 return -EADDRNOTAVAIL;
1119 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1123 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
1124 _cleanup_free_ char *item = NULL;
1126 unsigned long long ll;
1129 if (l < 2 || w[1] != '=')
1132 item = strndup(w, l);
1139 k = sd_id128_from_string(item+2, &id);
1142 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1147 if (sscanf(item+2, "%llx", &ll) != 1)
1149 if (ll != le64toh(o->entry.seqnum))
1154 k = sd_id128_from_string(item+2, &id);
1157 if (!sd_id128_equal(id, o->entry.boot_id))
1162 if (sscanf(item+2, "%llx", &ll) != 1)
1164 if (ll != le64toh(o->entry.monotonic))
1169 if (sscanf(item+2, "%llx", &ll) != 1)
1171 if (ll != le64toh(o->entry.realtime))
1176 if (sscanf(item+2, "%llx", &ll) != 1)
1178 if (ll != le64toh(o->entry.xor_hash))
1188 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1193 j->current_location.type = LOCATION_SEEK;
1194 j->current_location.boot_id = boot_id;
1195 j->current_location.monotonic = usec;
1196 j->current_location.monotonic_set = true;
1201 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1206 j->current_location.type = LOCATION_SEEK;
1207 j->current_location.realtime = usec;
1208 j->current_location.realtime_set = true;
1213 _public_ int sd_journal_seek_head(sd_journal *j) {
1218 j->current_location.type = LOCATION_HEAD;
1223 _public_ int sd_journal_seek_tail(sd_journal *j) {
1228 j->current_location.type = LOCATION_TAIL;
1233 static void check_network(sd_journal *j, int fd) {
1241 if (fstatfs(fd, &sfs) < 0)
1245 F_TYPE_CMP(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1246 F_TYPE_CMP(sfs.f_type, CODA_SUPER_MAGIC) ||
1247 F_TYPE_CMP(sfs.f_type, NCP_SUPER_MAGIC) ||
1248 F_TYPE_CMP(sfs.f_type, NFS_SUPER_MAGIC) ||
1249 F_TYPE_CMP(sfs.f_type, SMB_SUPER_MAGIC);
1252 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1253 _cleanup_free_ char *path = NULL;
1261 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
1262 !(streq(filename, "system.journal") ||
1263 streq(filename, "system.journal~") ||
1264 (startswith(filename, "system@") &&
1265 (endswith(filename, ".journal") || endswith(filename, ".journal~")))))
1268 path = strjoin(prefix, "/", filename, NULL);
1272 if (hashmap_get(j->files, path))
1275 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1276 log_debug("Too many open journal files, not adding %s, ignoring.", path);
1277 return set_put_error(j, -ETOOMANYREFS);
1280 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1282 if (errno == ENOENT)
1288 /* journal_file_dump(f); */
1290 r = hashmap_put(j->files, f->path, f);
1292 journal_file_close(f);
1296 log_debug("File %s added.", f->path);
1298 check_network(j, f->fd);
1300 j->current_invalidate_counter ++;
1305 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1313 path = strjoin(prefix, "/", filename, NULL);
1317 f = hashmap_get(j->files, path);
1322 hashmap_remove(j->files, f->path);
1324 log_debug("File %s removed.", f->path);
1326 if (j->current_file == f) {
1327 j->current_file = NULL;
1328 j->current_field = 0;
1331 if (j->unique_file == f) {
1332 j->unique_file = NULL;
1333 j->unique_offset = 0;
1336 journal_file_close(f);
1338 j->current_invalidate_counter ++;
1343 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1344 _cleanup_free_ char *path = NULL;
1346 _cleanup_closedir_ DIR *d = NULL;
1354 log_debug("Considering %s/%s.", prefix, dirname);
1356 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1357 (sd_id128_from_string(dirname, &id) < 0 ||
1358 sd_id128_get_machine(&mid) < 0 ||
1359 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1362 path = strjoin(prefix, "/", dirname, NULL);
1368 log_debug("Failed to open %s: %m", path);
1369 if (errno == ENOENT)
1374 m = hashmap_get(j->directories_by_path, path);
1376 m = new0(Directory, 1);
1383 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1388 path = NULL; /* avoid freeing in cleanup */
1389 j->current_invalidate_counter ++;
1391 log_debug("Directory %s added.", m->path);
1393 } else if (m->is_root)
1396 if (m->wd <= 0 && j->inotify_fd >= 0) {
1398 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1399 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1400 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1403 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1404 inotify_rm_watch(j->inotify_fd, m->wd);
1409 union dirent_storage buf;
1411 r = readdir_r(d, &buf.de, &de);
1415 if (dirent_is_file_with_suffix(de, ".journal") ||
1416 dirent_is_file_with_suffix(de, ".journal~")) {
1417 r = add_file(j, m->path, de->d_name);
1419 log_debug("Failed to add file %s/%s: %s",
1420 m->path, de->d_name, strerror(-r));
1421 r = set_put_error(j, r);
1428 check_network(j, dirfd(d));
1433 static int add_root_directory(sd_journal *j, const char *p) {
1434 _cleanup_closedir_ DIR *d = NULL;
1441 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1442 !path_startswith(p, "/run"))
1449 m = hashmap_get(j->directories_by_path, p);
1451 m = new0(Directory, 1);
1456 m->path = strdup(p);
1462 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1468 j->current_invalidate_counter ++;
1470 log_debug("Root directory %s added.", m->path);
1472 } else if (!m->is_root)
1475 if (m->wd <= 0 && j->inotify_fd >= 0) {
1477 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1478 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1481 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1482 inotify_rm_watch(j->inotify_fd, m->wd);
1487 union dirent_storage buf;
1490 r = readdir_r(d, &buf.de, &de);
1494 if (dirent_is_file_with_suffix(de, ".journal") ||
1495 dirent_is_file_with_suffix(de, ".journal~")) {
1496 r = add_file(j, m->path, de->d_name);
1498 log_debug("Failed to add file %s/%s: %s",
1499 m->path, de->d_name, strerror(-r));
1500 r = set_put_error(j, r);
1504 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1505 sd_id128_from_string(de->d_name, &id) >= 0) {
1507 r = add_directory(j, m->path, de->d_name);
1509 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1513 check_network(j, dirfd(d));
1518 static int remove_directory(sd_journal *j, Directory *d) {
1522 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1524 if (j->inotify_fd >= 0)
1525 inotify_rm_watch(j->inotify_fd, d->wd);
1528 hashmap_remove(j->directories_by_path, d->path);
1531 log_debug("Root directory %s removed.", d->path);
1533 log_debug("Directory %s removed.", d->path);
1541 static int add_search_paths(sd_journal *j) {
1543 const char search_paths[] =
1544 "/run/log/journal\0"
1545 "/var/log/journal\0";
1550 /* We ignore most errors here, since the idea is to only open
1551 * what's actually accessible, and ignore the rest. */
1553 NULSTR_FOREACH(p, search_paths) {
1554 r = add_root_directory(j, p);
1555 if (r < 0 && r != -ENOENT) {
1556 r = set_put_error(j, r);
1565 static int allocate_inotify(sd_journal *j) {
1568 if (j->inotify_fd < 0) {
1569 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1570 if (j->inotify_fd < 0)
1574 if (!j->directories_by_wd) {
1575 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1576 if (!j->directories_by_wd)
1583 static sd_journal *journal_new(int flags, const char *path) {
1586 j = new0(sd_journal, 1);
1592 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1595 j->path = strdup(path);
1600 j->files = hashmap_new(string_hash_func, string_compare_func);
1601 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1602 j->mmap = mmap_cache_new();
1603 if (!j->files || !j->directories_by_path || !j->mmap)
1609 sd_journal_close(j);
1613 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1620 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1621 SD_JOURNAL_RUNTIME_ONLY|
1622 SD_JOURNAL_SYSTEM_ONLY))
1625 j = journal_new(flags, NULL);
1629 r = add_search_paths(j);
1637 sd_journal_close(j);
1642 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1655 j = journal_new(flags, path);
1659 r = add_root_directory(j, path);
1661 set_put_error(j, r);
1669 sd_journal_close(j);
1674 _public_ void sd_journal_close(sd_journal *j) {
1681 sd_journal_flush_matches(j);
1683 while ((f = hashmap_steal_first(j->files)))
1684 journal_file_close(f);
1686 hashmap_free(j->files);
1688 while ((d = hashmap_first(j->directories_by_path)))
1689 remove_directory(j, d);
1691 while ((d = hashmap_first(j->directories_by_wd)))
1692 remove_directory(j, d);
1694 hashmap_free(j->directories_by_path);
1695 hashmap_free(j->directories_by_wd);
1697 if (j->inotify_fd >= 0)
1698 close_nointr_nofail(j->inotify_fd);
1701 mmap_cache_unref(j->mmap);
1704 free(j->unique_field);
1705 set_free(j->errors);
1709 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1719 f = j->current_file;
1721 return -EADDRNOTAVAIL;
1723 if (f->current_offset <= 0)
1724 return -EADDRNOTAVAIL;
1726 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1730 *ret = le64toh(o->entry.realtime);
1734 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1743 f = j->current_file;
1745 return -EADDRNOTAVAIL;
1747 if (f->current_offset <= 0)
1748 return -EADDRNOTAVAIL;
1750 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1755 *ret_boot_id = o->entry.boot_id;
1757 r = sd_id128_get_boot(&id);
1761 if (!sd_id128_equal(id, o->entry.boot_id))
1766 *ret = le64toh(o->entry.monotonic);
1771 static bool field_is_valid(const char *field) {
1779 if (startswith(field, "__"))
1782 for (p = field; *p; p++) {
1787 if (*p >= 'A' && *p <= 'Z')
1790 if (*p >= '0' && *p <= '9')
1799 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1802 size_t field_length;
1815 if (!field_is_valid(field))
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 field_length = strlen(field);
1831 n = journal_file_entry_n_items(o);
1832 for (i = 0; i < n; i++) {
1837 p = le64toh(o->entry.items[i].object_offset);
1838 le_hash = o->entry.items[i].hash;
1839 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1843 if (le_hash != o->data.hash)
1846 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1848 if (o->object.flags & OBJECT_COMPRESSED) {
1851 if (uncompress_startswith(o->data.payload, l,
1852 &f->compress_buffer, &f->compress_buffer_size,
1853 field, field_length, '=')) {
1857 if (!uncompress_blob(o->data.payload, l,
1858 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1862 *data = f->compress_buffer;
1863 *size = (size_t) rsize;
1868 return -EPROTONOSUPPORT;
1871 } else if (l >= field_length+1 &&
1872 memcmp(o->data.payload, field, field_length) == 0 &&
1873 o->data.payload[field_length] == '=') {
1877 if ((uint64_t) t != l)
1880 *data = o->data.payload;
1886 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1894 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
1898 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1901 /* We can't read objects larger than 4G on a 32bit machine */
1902 if ((uint64_t) t != l)
1905 if (o->object.flags & OBJECT_COMPRESSED) {
1909 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, j->data_threshold))
1912 *data = f->compress_buffer;
1913 *size = (size_t) rsize;
1915 return -EPROTONOSUPPORT;
1918 *data = o->data.payload;
1925 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1939 f = j->current_file;
1941 return -EADDRNOTAVAIL;
1943 if (f->current_offset <= 0)
1944 return -EADDRNOTAVAIL;
1946 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1950 n = journal_file_entry_n_items(o);
1951 if (j->current_field >= n)
1954 p = le64toh(o->entry.items[j->current_field].object_offset);
1955 le_hash = o->entry.items[j->current_field].hash;
1956 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1960 if (le_hash != o->data.hash)
1963 r = return_data(j, f, o, data, size);
1967 j->current_field ++;
1972 _public_ void sd_journal_restart_data(sd_journal *j) {
1976 j->current_field = 0;
1979 _public_ int sd_journal_get_fd(sd_journal *j) {
1985 if (j->inotify_fd >= 0)
1986 return j->inotify_fd;
1988 r = allocate_inotify(j);
1992 /* Iterate through all dirs again, to add them to the
1995 r = add_root_directory(j, j->path);
1997 r = add_search_paths(j);
2001 return j->inotify_fd;
2004 _public_ int sd_journal_get_events(sd_journal *j) {
2010 fd = sd_journal_get_fd(j);
2017 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2025 fd = sd_journal_get_fd(j);
2029 if (!j->on_network) {
2030 *timeout_usec = (uint64_t) -1;
2034 /* If we are on the network we need to regularly check for
2035 * changes manually */
2037 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2041 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2048 /* Is this a subdirectory we watch? */
2049 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2053 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2054 (endswith(e->name, ".journal") ||
2055 endswith(e->name, ".journal~"))) {
2057 /* Event for a journal file */
2059 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2060 r = add_file(j, d->path, e->name);
2062 log_debug("Failed to add file %s/%s: %s",
2063 d->path, e->name, strerror(-r));
2064 set_put_error(j, r);
2067 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2069 r = remove_file(j, d->path, e->name);
2071 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
2074 } else if (!d->is_root && e->len == 0) {
2076 /* Event for a subdirectory */
2078 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2079 r = remove_directory(j, d);
2081 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
2085 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2087 /* Event for root directory */
2089 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2090 r = add_directory(j, d->path, e->name);
2092 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
2099 if (e->mask & IN_IGNORED)
2102 log_warning("Unknown inotify event.");
2105 static int determine_change(sd_journal *j) {
2110 b = j->current_invalidate_counter != j->last_invalidate_counter;
2111 j->last_invalidate_counter = j->current_invalidate_counter;
2113 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2116 _public_ int sd_journal_process(sd_journal *j) {
2117 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
2118 bool got_something = false;
2123 j->last_process_usec = now(CLOCK_MONOTONIC);
2126 struct inotify_event *e;
2129 l = read(j->inotify_fd, buffer, sizeof(buffer));
2131 if (errno == EAGAIN || errno == EINTR)
2132 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2137 got_something = true;
2139 e = (struct inotify_event*) buffer;
2143 process_inotify_event(j, e);
2145 step = sizeof(struct inotify_event) + e->len;
2146 assert(step <= (size_t) l);
2148 e = (struct inotify_event*) ((uint8_t*) e + step);
2153 return determine_change(j);
2156 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2162 if (j->inotify_fd < 0) {
2164 /* This is the first invocation, hence create the
2166 r = sd_journal_get_fd(j);
2170 /* The journal might have changed since the context
2171 * object was created and we weren't watching before,
2172 * hence don't wait for anything, and return
2174 return determine_change(j);
2177 r = sd_journal_get_timeout(j, &t);
2181 if (t != (uint64_t) -1) {
2184 n = now(CLOCK_MONOTONIC);
2185 t = t > n ? t - n : 0;
2187 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2192 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2193 } while (r == -EINTR);
2198 return sd_journal_process(j);
2201 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2214 HASHMAP_FOREACH(f, j->files, i) {
2217 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2233 *from = MIN(fr, *from);
2239 return first ? 0 : 1;
2242 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2255 HASHMAP_FOREACH(f, j->files, i) {
2258 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2274 *from = MIN(fr, *from);
2280 return first ? 0 : 1;
2283 void journal_print_header(sd_journal *j) {
2286 bool newline = false;
2290 HASHMAP_FOREACH(f, j->files, i) {
2296 journal_file_print_header(f);
2300 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2310 HASHMAP_FOREACH(f, j->files, i) {
2313 if (fstat(f->fd, &st) < 0)
2316 sum += (uint64_t) st.st_blocks * 512ULL;
2323 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2330 if (!field_is_valid(field))
2337 free(j->unique_field);
2338 j->unique_field = f;
2339 j->unique_file = NULL;
2340 j->unique_offset = 0;
2345 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2356 if (!j->unique_field)
2359 k = strlen(j->unique_field);
2361 if (!j->unique_file) {
2362 j->unique_file = hashmap_first(j->files);
2363 if (!j->unique_file)
2365 j->unique_offset = 0;
2375 /* Proceed to next data object in the field's linked list */
2376 if (j->unique_offset == 0) {
2377 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2381 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2383 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2387 j->unique_offset = le64toh(o->data.next_field_offset);
2390 /* We reached the end of the list? Then start again, with the next file */
2391 if (j->unique_offset == 0) {
2394 n = hashmap_next(j->files, j->unique_file->path);
2402 /* We do not use the type context here, but 0 instead,
2403 * so that we can look at this data object at the same
2404 * time as one on another file */
2405 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2409 /* Let's do the type check by hand, since we used 0 context above. */
2410 if (o->object.type != OBJECT_DATA)
2413 r = return_data(j, j->unique_file, o, &odata, &ol);
2417 /* OK, now let's see if we already returned this data
2418 * object by checking if it exists in the earlier
2419 * traversed files. */
2421 HASHMAP_FOREACH(of, j->files, i) {
2425 if (of == j->unique_file)
2428 /* Skip this file it didn't have any fields
2430 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2431 le64toh(of->header->n_fields) <= 0)
2434 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2445 r = return_data(j, j->unique_file, o, data, l);
2453 _public_ void sd_journal_restart_unique(sd_journal *j) {
2457 j->unique_file = NULL;
2458 j->unique_offset = 0;
2461 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2465 return !j->on_network;
2468 static char *lookup_field(const char *field, void *userdata) {
2469 sd_journal *j = userdata;
2477 r = sd_journal_get_data(j, field, &data, &size);
2479 size > REPLACE_VAR_MAX)
2480 return strdup(field);
2482 d = strlen(field) + 1;
2484 return strndup((const char*) data + d, size - d);
2487 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2491 _cleanup_free_ char *text = NULL, *cid = NULL;
2500 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2504 cid = strndup((const char*) data + 11, size - 11);
2508 r = sd_id128_from_string(cid, &id);
2512 r = catalog_get(CATALOG_DATABASE, id, &text);
2516 t = replace_var(text, lookup_field, j);
2524 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2528 return catalog_get(CATALOG_DATABASE, id, ret);
2531 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2535 j->data_threshold = sz;
2539 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2545 *sz = j->data_threshold;