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"
46 #define JOURNAL_FILES_MAX 1024
48 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
50 #define REPLACE_VAR_MAX 256
52 #define DEFAULT_DATA_THRESHOLD (64*1024)
54 static void remove_file_real(sd_journal *j, JournalFile *f);
56 static bool journal_pid_changed(sd_journal *j) {
59 /* We don't support people creating a journal object and
60 * keeping it around over a fork(). Let's complain. */
62 return j->original_pid != getpid();
65 /* We return an error here only if we didn't manage to
66 memorize the real error. */
67 static int set_put_error(sd_journal *j, int r) {
73 k = set_ensure_allocated(&j->errors, NULL);
77 return set_put(j->errors, INT_TO_PTR(r));
80 static void detach_location(sd_journal *j) {
86 j->current_file = NULL;
89 ORDERED_HASHMAP_FOREACH(f, j->files, i)
90 journal_file_reset_location(f);
93 static void reset_location(sd_journal *j) {
97 zero(j->current_location);
100 static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
102 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
104 assert(o->object.type == OBJECT_ENTRY);
107 l->seqnum = le64toh(o->entry.seqnum);
108 l->seqnum_id = f->header->seqnum_id;
109 l->realtime = le64toh(o->entry.realtime);
110 l->monotonic = le64toh(o->entry.monotonic);
111 l->boot_id = o->entry.boot_id;
112 l->xor_hash = le64toh(o->entry.xor_hash);
114 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
117 static void set_location(sd_journal *j, JournalFile *f, Object *o) {
122 init_location(&j->current_location, LOCATION_DISCRETE, f, o);
125 j->current_field = 0;
127 /* Let f know its candidate entry was picked. */
128 assert(f->location_type == LOCATION_SEEK);
129 f->location_type = LOCATION_DISCRETE;
132 static int match_is_valid(const void *data, size_t size) {
140 if (startswith(data, "__"))
144 for (p = b; p < b + size; p++) {
152 if (*p >= 'A' && *p <= 'Z')
155 if (*p >= '0' && *p <= '9')
164 static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
165 const uint8_t *a = _a, *b = _b;
168 for (j = 0; j < s && j < t; j++) {
177 assert_not_reached("\"=\" not found");
180 static Match *match_new(Match *p, MatchType t) {
191 LIST_PREPEND(matches, p->matches, m);
197 static void match_free(Match *m) {
201 match_free(m->matches);
204 LIST_REMOVE(matches, m->parent->matches, m);
210 static void match_free_if_empty(Match *m) {
211 if (!m || m->matches)
217 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
218 Match *l3, *l4, *add_here = NULL, *m;
221 assert_return(j, -EINVAL);
222 assert_return(!journal_pid_changed(j), -ECHILD);
223 assert_return(data, -EINVAL);
228 assert_return(match_is_valid(data, size), -EINVAL);
234 * level 4: concrete matches */
237 j->level0 = match_new(NULL, MATCH_AND_TERM);
243 j->level1 = match_new(j->level0, MATCH_OR_TERM);
249 j->level2 = match_new(j->level1, MATCH_AND_TERM);
254 assert(j->level0->type == MATCH_AND_TERM);
255 assert(j->level1->type == MATCH_OR_TERM);
256 assert(j->level2->type == MATCH_AND_TERM);
258 le_hash = htole64(hash64(data, size));
260 LIST_FOREACH(matches, l3, j->level2->matches) {
261 assert(l3->type == MATCH_OR_TERM);
263 LIST_FOREACH(matches, l4, l3->matches) {
264 assert(l4->type == MATCH_DISCRETE);
266 /* Exactly the same match already? Then ignore
268 if (l4->le_hash == le_hash &&
270 memcmp(l4->data, data, size) == 0)
273 /* Same field? Then let's add this to this OR term */
274 if (same_field(data, size, l4->data, l4->size)) {
285 add_here = match_new(j->level2, MATCH_OR_TERM);
290 m = match_new(add_here, MATCH_DISCRETE);
294 m->le_hash = le_hash;
296 m->data = memdup(data, size);
305 match_free_if_empty(add_here);
306 match_free_if_empty(j->level2);
307 match_free_if_empty(j->level1);
308 match_free_if_empty(j->level0);
313 _public_ int sd_journal_add_conjunction(sd_journal *j) {
314 assert_return(j, -EINVAL);
315 assert_return(!journal_pid_changed(j), -ECHILD);
323 if (!j->level1->matches)
332 _public_ int sd_journal_add_disjunction(sd_journal *j) {
333 assert_return(j, -EINVAL);
334 assert_return(!journal_pid_changed(j), -ECHILD);
345 if (!j->level2->matches)
352 static char *match_make_string(Match *m) {
355 bool enclose = false;
358 return strdup("none");
360 if (m->type == MATCH_DISCRETE)
361 return strndup(m->data, m->size);
364 LIST_FOREACH(matches, i, m->matches) {
367 t = match_make_string(i);
374 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
389 r = strjoin("(", p, ")", NULL);
397 char *journal_make_match_string(sd_journal *j) {
400 return match_make_string(j->level0);
403 _public_ void sd_journal_flush_matches(sd_journal *j) {
408 match_free(j->level0);
410 j->level0 = j->level1 = j->level2 = NULL;
415 _pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
421 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
423 if (l->monotonic_set &&
424 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
426 le64toh(ao->entry.realtime) == l->realtime &&
428 le64toh(ao->entry.xor_hash) == l->xor_hash)
432 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
434 a = le64toh(ao->entry.seqnum);
442 if (l->monotonic_set &&
443 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
445 a = le64toh(ao->entry.monotonic);
447 if (a < l->monotonic)
449 if (a > l->monotonic)
453 if (l->realtime_set) {
455 a = le64toh(ao->entry.realtime);
463 if (l->xor_hash_set) {
464 a = le64toh(ao->entry.xor_hash);
475 static int next_for_match(
479 uint64_t after_offset,
480 direction_t direction,
492 if (m->type == MATCH_DISCRETE) {
495 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
499 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
501 } else if (m->type == MATCH_OR_TERM) {
504 /* Find the earliest match beyond after_offset */
506 LIST_FOREACH(matches, i, m->matches) {
509 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
513 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
521 } else if (m->type == MATCH_AND_TERM) {
522 Match *i, *last_moved;
524 /* Always jump to the next matching entry and repeat
525 * this until we find an offset that matches for all
531 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
535 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
536 last_moved = m->matches;
538 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
541 r = next_for_match(j, i, f, np, direction, NULL, &cp);
545 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
546 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
555 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
567 static int find_location_for_match(
571 direction_t direction,
581 if (m->type == MATCH_DISCRETE) {
584 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
588 /* FIXME: missing: find by monotonic */
590 if (j->current_location.type == LOCATION_HEAD)
591 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
592 if (j->current_location.type == LOCATION_TAIL)
593 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
594 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
595 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
596 if (j->current_location.monotonic_set) {
597 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
601 if (j->current_location.realtime_set)
602 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
604 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
606 } else if (m->type == MATCH_OR_TERM) {
611 /* Find the earliest match */
613 LIST_FOREACH(matches, i, m->matches) {
616 r = find_location_for_match(j, i, f, direction, NULL, &cp);
620 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
628 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
643 assert(m->type == MATCH_AND_TERM);
645 /* First jump to the last match, and then find the
646 * next one where all matches match */
651 LIST_FOREACH(matches, i, m->matches) {
654 r = find_location_for_match(j, i, f, direction, NULL, &cp);
658 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
662 return next_for_match(j, m, f, np, direction, ret, offset);
666 static int find_location_with_matches(
669 direction_t direction,
681 /* No matches is simple */
683 if (j->current_location.type == LOCATION_HEAD)
684 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
685 if (j->current_location.type == LOCATION_TAIL)
686 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
687 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
688 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
689 if (j->current_location.monotonic_set) {
690 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
694 if (j->current_location.realtime_set)
695 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
697 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
699 return find_location_for_match(j, j->level0, f, direction, ret, offset);
702 static int next_with_matches(
705 direction_t direction,
720 /* No matches is easy. We simple advance the file
723 return journal_file_next_entry(f, c, cp, direction, ret, offset);
725 /* If we have a match then we look for the next matching entry
726 * with an offset at least one step larger */
727 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
730 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction) {
738 if (f->last_direction == direction && f->current_offset > 0) {
739 cp = f->current_offset;
741 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
745 r = next_with_matches(j, f, direction, &c, &cp);
749 r = find_location_with_matches(j, f, direction, &c, &cp);
754 /* OK, we found the spot, now let's advance until an entry
755 * that is actually different from what we were previously
756 * looking at. This is necessary to handle entries which exist
757 * in two (or more) journal files, and which shall all be
758 * suppressed but one. */
763 if (j->current_location.type == LOCATION_DISCRETE) {
766 k = compare_with_location(f, c, &j->current_location);
768 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
773 journal_file_save_location(f, direction, c, cp);
777 r = next_with_matches(j, f, direction, &c, &cp);
783 static int real_journal_next(sd_journal *j, direction_t direction) {
784 JournalFile *f, *new_file = NULL;
789 assert_return(j, -EINVAL);
790 assert_return(!journal_pid_changed(j), -ECHILD);
792 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
795 r = next_beyond_location(j, f, direction);
797 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
798 remove_file_real(j, f);
801 f->location_type = LOCATION_TAIL;
810 k = journal_file_compare_locations(f, new_file);
812 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
822 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o);
826 set_location(j, new_file, o);
831 _public_ int sd_journal_next(sd_journal *j) {
832 return real_journal_next(j, DIRECTION_DOWN);
835 _public_ int sd_journal_previous(sd_journal *j) {
836 return real_journal_next(j, DIRECTION_UP);
839 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
842 assert_return(j, -EINVAL);
843 assert_return(!journal_pid_changed(j), -ECHILD);
846 /* If this is not a discrete skip, then at least
847 * resolve the current location */
848 if (j->current_location.type != LOCATION_DISCRETE)
849 return real_journal_next(j, direction);
855 r = real_journal_next(j, direction);
869 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
870 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
873 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
874 return real_journal_next_skip(j, DIRECTION_UP, skip);
877 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
880 char bid[33], sid[33];
882 assert_return(j, -EINVAL);
883 assert_return(!journal_pid_changed(j), -ECHILD);
884 assert_return(cursor, -EINVAL);
886 if (!j->current_file || j->current_file->current_offset <= 0)
887 return -EADDRNOTAVAIL;
889 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
893 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
894 sd_id128_to_string(o->entry.boot_id, bid);
897 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
898 sid, le64toh(o->entry.seqnum),
899 bid, le64toh(o->entry.monotonic),
900 le64toh(o->entry.realtime),
901 le64toh(o->entry.xor_hash)) < 0)
907 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
908 const char *word, *state;
910 unsigned long long seqnum, monotonic, realtime, xor_hash;
912 seqnum_id_set = false,
915 monotonic_set = false,
916 realtime_set = false,
917 xor_hash_set = false;
918 sd_id128_t seqnum_id, boot_id;
920 assert_return(j, -EINVAL);
921 assert_return(!journal_pid_changed(j), -ECHILD);
922 assert_return(!isempty(cursor), -EINVAL);
924 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
928 if (l < 2 || word[1] != '=')
931 item = strndup(word, l);
938 seqnum_id_set = true;
939 k = sd_id128_from_string(item+2, &seqnum_id);
944 if (sscanf(item+2, "%llx", &seqnum) != 1)
950 k = sd_id128_from_string(item+2, &boot_id);
954 monotonic_set = true;
955 if (sscanf(item+2, "%llx", &monotonic) != 1)
961 if (sscanf(item+2, "%llx", &realtime) != 1)
967 if (sscanf(item+2, "%llx", &xor_hash) != 1)
978 if ((!seqnum_set || !seqnum_id_set) &&
979 (!monotonic_set || !boot_id_set) &&
985 j->current_location.type = LOCATION_SEEK;
988 j->current_location.realtime = (uint64_t) realtime;
989 j->current_location.realtime_set = true;
992 if (seqnum_set && seqnum_id_set) {
993 j->current_location.seqnum = (uint64_t) seqnum;
994 j->current_location.seqnum_id = seqnum_id;
995 j->current_location.seqnum_set = true;
998 if (monotonic_set && boot_id_set) {
999 j->current_location.monotonic = (uint64_t) monotonic;
1000 j->current_location.boot_id = boot_id;
1001 j->current_location.monotonic_set = true;
1005 j->current_location.xor_hash = (uint64_t) xor_hash;
1006 j->current_location.xor_hash_set = true;
1012 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1014 const char *word, *state;
1018 assert_return(j, -EINVAL);
1019 assert_return(!journal_pid_changed(j), -ECHILD);
1020 assert_return(!isempty(cursor), -EINVAL);
1022 if (!j->current_file || j->current_file->current_offset <= 0)
1023 return -EADDRNOTAVAIL;
1025 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1029 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1030 _cleanup_free_ char *item = NULL;
1032 unsigned long long ll;
1035 if (l < 2 || word[1] != '=')
1038 item = strndup(word, l);
1045 k = sd_id128_from_string(item+2, &id);
1048 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1053 if (sscanf(item+2, "%llx", &ll) != 1)
1055 if (ll != le64toh(o->entry.seqnum))
1060 k = sd_id128_from_string(item+2, &id);
1063 if (!sd_id128_equal(id, o->entry.boot_id))
1068 if (sscanf(item+2, "%llx", &ll) != 1)
1070 if (ll != le64toh(o->entry.monotonic))
1075 if (sscanf(item+2, "%llx", &ll) != 1)
1077 if (ll != le64toh(o->entry.realtime))
1082 if (sscanf(item+2, "%llx", &ll) != 1)
1084 if (ll != le64toh(o->entry.xor_hash))
1094 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1095 assert_return(j, -EINVAL);
1096 assert_return(!journal_pid_changed(j), -ECHILD);
1099 j->current_location.type = LOCATION_SEEK;
1100 j->current_location.boot_id = boot_id;
1101 j->current_location.monotonic = usec;
1102 j->current_location.monotonic_set = true;
1107 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1108 assert_return(j, -EINVAL);
1109 assert_return(!journal_pid_changed(j), -ECHILD);
1112 j->current_location.type = LOCATION_SEEK;
1113 j->current_location.realtime = usec;
1114 j->current_location.realtime_set = true;
1119 _public_ int sd_journal_seek_head(sd_journal *j) {
1120 assert_return(j, -EINVAL);
1121 assert_return(!journal_pid_changed(j), -ECHILD);
1124 j->current_location.type = LOCATION_HEAD;
1129 _public_ int sd_journal_seek_tail(sd_journal *j) {
1130 assert_return(j, -EINVAL);
1131 assert_return(!journal_pid_changed(j), -ECHILD);
1134 j->current_location.type = LOCATION_TAIL;
1139 static void check_network(sd_journal *j, int fd) {
1147 if (fstatfs(fd, &sfs) < 0)
1151 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1152 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1153 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1154 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1155 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1158 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1159 const char *full, *tilded, *atted;
1161 full = strappenda(prefix, ".journal");
1162 tilded = strappenda(full, "~");
1163 atted = strappenda(prefix, "@");
1165 return streq(filename, full) ||
1166 streq(filename, tilded) ||
1167 startswith(filename, atted);
1170 static bool file_type_wanted(int flags, const char *filename) {
1171 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1174 /* no flags set → every type is OK */
1175 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1178 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1181 if (flags & SD_JOURNAL_CURRENT_USER) {
1182 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1184 assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid())
1185 < (int) sizeof(prefix));
1187 if (file_has_type_prefix(prefix, filename))
1194 static int add_any_file(sd_journal *j, const char *path) {
1195 JournalFile *f = NULL;
1201 if (ordered_hashmap_get(j->files, path))
1204 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1205 log_warning("Too many open journal files, not adding %s.", path);
1206 return set_put_error(j, -ETOOMANYREFS);
1209 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1213 /* journal_file_dump(f); */
1215 r = ordered_hashmap_put(j->files, f->path, f);
1217 journal_file_close(f);
1221 log_debug("File %s added.", f->path);
1223 check_network(j, f->fd);
1225 j->current_invalidate_counter ++;
1230 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1231 _cleanup_free_ char *path = NULL;
1238 if (j->no_new_files ||
1239 !file_type_wanted(j->flags, filename))
1242 path = strjoin(prefix, "/", filename, NULL);
1246 r = add_any_file(j, path);
1252 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1253 _cleanup_free_ char *path;
1260 path = strjoin(prefix, "/", filename, NULL);
1264 f = ordered_hashmap_get(j->files, path);
1268 remove_file_real(j, f);
1272 static void remove_file_real(sd_journal *j, JournalFile *f) {
1276 ordered_hashmap_remove(j->files, f->path);
1278 log_debug("File %s removed.", f->path);
1280 if (j->current_file == f) {
1281 j->current_file = NULL;
1282 j->current_field = 0;
1285 if (j->unique_file == f) {
1286 /* Jump to the next unique_file or NULL if that one was last */
1287 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
1288 j->unique_offset = 0;
1289 if (!j->unique_file)
1290 j->unique_file_lost = true;
1293 journal_file_close(f);
1295 j->current_invalidate_counter ++;
1298 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1299 _cleanup_free_ char *path = NULL;
1301 _cleanup_closedir_ DIR *d = NULL;
1309 log_debug("Considering %s/%s.", prefix, dirname);
1311 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1312 (sd_id128_from_string(dirname, &id) < 0 ||
1313 sd_id128_get_machine(&mid) < 0 ||
1314 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1317 path = strjoin(prefix, "/", dirname, NULL);
1323 log_debug_errno(errno, "Failed to open %s: %m", path);
1324 if (errno == ENOENT)
1329 m = hashmap_get(j->directories_by_path, path);
1331 m = new0(Directory, 1);
1338 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1343 path = NULL; /* avoid freeing in cleanup */
1344 j->current_invalidate_counter ++;
1346 log_debug("Directory %s added.", m->path);
1348 } else if (m->is_root)
1351 if (m->wd <= 0 && j->inotify_fd >= 0) {
1353 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1354 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1355 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1358 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1359 inotify_rm_watch(j->inotify_fd, m->wd);
1367 if (!de && errno != 0) {
1369 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1375 if (dirent_is_file_with_suffix(de, ".journal") ||
1376 dirent_is_file_with_suffix(de, ".journal~")) {
1377 r = add_file(j, m->path, de->d_name);
1379 log_debug_errno(r, "Failed to add file %s/%s: %m",
1380 m->path, de->d_name);
1381 r = set_put_error(j, r);
1388 check_network(j, dirfd(d));
1393 static int add_root_directory(sd_journal *j, const char *p) {
1394 _cleanup_closedir_ DIR *d = NULL;
1401 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1402 !path_startswith(p, "/run"))
1406 p = strappenda(j->prefix, p);
1412 m = hashmap_get(j->directories_by_path, p);
1414 m = new0(Directory, 1);
1419 m->path = strdup(p);
1425 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1431 j->current_invalidate_counter ++;
1433 log_debug("Root directory %s added.", m->path);
1435 } else if (!m->is_root)
1438 if (m->wd <= 0 && j->inotify_fd >= 0) {
1440 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1441 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1444 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1445 inotify_rm_watch(j->inotify_fd, m->wd);
1448 if (j->no_new_files)
1457 if (!de && errno != 0) {
1459 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1465 if (dirent_is_file_with_suffix(de, ".journal") ||
1466 dirent_is_file_with_suffix(de, ".journal~")) {
1467 r = add_file(j, m->path, de->d_name);
1469 log_debug_errno(r, "Failed to add file %s/%s: %m",
1470 m->path, de->d_name);
1471 r = set_put_error(j, r);
1475 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1476 sd_id128_from_string(de->d_name, &id) >= 0) {
1478 r = add_directory(j, m->path, de->d_name);
1480 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
1484 check_network(j, dirfd(d));
1489 static int remove_directory(sd_journal *j, Directory *d) {
1493 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1495 if (j->inotify_fd >= 0)
1496 inotify_rm_watch(j->inotify_fd, d->wd);
1499 hashmap_remove(j->directories_by_path, d->path);
1502 log_debug("Root directory %s removed.", d->path);
1504 log_debug("Directory %s removed.", d->path);
1512 static int add_search_paths(sd_journal *j) {
1514 const char search_paths[] =
1515 "/run/log/journal\0"
1516 "/var/log/journal\0";
1521 /* We ignore most errors here, since the idea is to only open
1522 * what's actually accessible, and ignore the rest. */
1524 NULSTR_FOREACH(p, search_paths) {
1525 r = add_root_directory(j, p);
1526 if (r < 0 && r != -ENOENT) {
1527 r = set_put_error(j, r);
1536 static int add_current_paths(sd_journal *j) {
1541 assert(j->no_new_files);
1543 /* Simply adds all directories for files we have open as
1544 * "root" directories. We don't expect errors here, so we
1545 * treat them as fatal. */
1547 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
1548 _cleanup_free_ char *dir;
1551 dir = dirname_malloc(f->path);
1555 r = add_root_directory(j, dir);
1557 set_put_error(j, r);
1566 static int allocate_inotify(sd_journal *j) {
1569 if (j->inotify_fd < 0) {
1570 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1571 if (j->inotify_fd < 0)
1575 if (!j->directories_by_wd) {
1576 j->directories_by_wd = hashmap_new(NULL);
1577 if (!j->directories_by_wd)
1584 static sd_journal *journal_new(int flags, const char *path) {
1587 j = new0(sd_journal, 1);
1591 j->original_pid = getpid();
1594 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1597 j->path = strdup(path);
1602 j->files = ordered_hashmap_new(&string_hash_ops);
1603 j->directories_by_path = hashmap_new(&string_hash_ops);
1604 j->mmap = mmap_cache_new();
1605 if (!j->files || !j->directories_by_path || !j->mmap)
1611 sd_journal_close(j);
1615 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1619 assert_return(ret, -EINVAL);
1620 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1622 j = journal_new(flags, NULL);
1626 r = add_search_paths(j);
1634 sd_journal_close(j);
1639 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1640 _cleanup_free_ char *root = NULL, *class = NULL;
1645 assert_return(machine, -EINVAL);
1646 assert_return(ret, -EINVAL);
1647 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1648 assert_return(machine_name_is_valid(machine), -EINVAL);
1650 p = strappenda("/run/systemd/machines/", machine);
1651 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1659 if (!streq_ptr(class, "container"))
1662 j = journal_new(flags, NULL);
1669 r = add_search_paths(j);
1677 sd_journal_close(j);
1681 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1685 assert_return(ret, -EINVAL);
1686 assert_return(path, -EINVAL);
1687 assert_return(flags == 0, -EINVAL);
1689 j = journal_new(flags, path);
1693 r = add_root_directory(j, path);
1695 set_put_error(j, r);
1703 sd_journal_close(j);
1708 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1713 assert_return(ret, -EINVAL);
1714 assert_return(flags == 0, -EINVAL);
1716 j = journal_new(flags, NULL);
1720 STRV_FOREACH(path, paths) {
1721 r = add_any_file(j, *path);
1723 log_error_errno(r, "Failed to open %s: %m", *path);
1728 j->no_new_files = true;
1734 sd_journal_close(j);
1739 _public_ void sd_journal_close(sd_journal *j) {
1746 sd_journal_flush_matches(j);
1748 while ((f = ordered_hashmap_steal_first(j->files)))
1749 journal_file_close(f);
1751 ordered_hashmap_free(j->files);
1753 while ((d = hashmap_first(j->directories_by_path)))
1754 remove_directory(j, d);
1756 while ((d = hashmap_first(j->directories_by_wd)))
1757 remove_directory(j, d);
1759 hashmap_free(j->directories_by_path);
1760 hashmap_free(j->directories_by_wd);
1762 safe_close(j->inotify_fd);
1765 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1766 mmap_cache_unref(j->mmap);
1771 free(j->unique_field);
1772 set_free(j->errors);
1776 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1781 assert_return(j, -EINVAL);
1782 assert_return(!journal_pid_changed(j), -ECHILD);
1783 assert_return(ret, -EINVAL);
1785 f = j->current_file;
1787 return -EADDRNOTAVAIL;
1789 if (f->current_offset <= 0)
1790 return -EADDRNOTAVAIL;
1792 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1796 *ret = le64toh(o->entry.realtime);
1800 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1806 assert_return(j, -EINVAL);
1807 assert_return(!journal_pid_changed(j), -ECHILD);
1809 f = j->current_file;
1811 return -EADDRNOTAVAIL;
1813 if (f->current_offset <= 0)
1814 return -EADDRNOTAVAIL;
1816 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1821 *ret_boot_id = o->entry.boot_id;
1823 r = sd_id128_get_boot(&id);
1827 if (!sd_id128_equal(id, o->entry.boot_id))
1832 *ret = le64toh(o->entry.monotonic);
1837 static bool field_is_valid(const char *field) {
1845 if (startswith(field, "__"))
1848 for (p = field; *p; p++) {
1853 if (*p >= 'A' && *p <= 'Z')
1856 if (*p >= '0' && *p <= '9')
1865 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1868 size_t field_length;
1872 assert_return(j, -EINVAL);
1873 assert_return(!journal_pid_changed(j), -ECHILD);
1874 assert_return(field, -EINVAL);
1875 assert_return(data, -EINVAL);
1876 assert_return(size, -EINVAL);
1877 assert_return(field_is_valid(field), -EINVAL);
1879 f = j->current_file;
1881 return -EADDRNOTAVAIL;
1883 if (f->current_offset <= 0)
1884 return -EADDRNOTAVAIL;
1886 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1890 field_length = strlen(field);
1892 n = journal_file_entry_n_items(o);
1893 for (i = 0; i < n; i++) {
1899 p = le64toh(o->entry.items[i].object_offset);
1900 le_hash = o->entry.items[i].hash;
1901 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1905 if (le_hash != o->data.hash)
1908 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1910 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1912 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1913 if (decompress_startswith(compression,
1915 &f->compress_buffer, &f->compress_buffer_size,
1916 field, field_length, '=')) {
1920 r = decompress_blob(compression,
1922 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1927 *data = f->compress_buffer;
1928 *size = (size_t) rsize;
1933 return -EPROTONOSUPPORT;
1935 } else if (l >= field_length+1 &&
1936 memcmp(o->data.payload, field, field_length) == 0 &&
1937 o->data.payload[field_length] == '=') {
1941 if ((uint64_t) t != l)
1944 *data = o->data.payload;
1950 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1958 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
1963 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1966 /* We can't read objects larger than 4G on a 32bit machine */
1967 if ((uint64_t) t != l)
1970 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1972 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1976 r = decompress_blob(compression,
1977 o->data.payload, l, &f->compress_buffer,
1978 &f->compress_buffer_size, &rsize, j->data_threshold);
1982 *data = f->compress_buffer;
1983 *size = (size_t) rsize;
1985 return -EPROTONOSUPPORT;
1988 *data = o->data.payload;
1995 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2002 assert_return(j, -EINVAL);
2003 assert_return(!journal_pid_changed(j), -ECHILD);
2004 assert_return(data, -EINVAL);
2005 assert_return(size, -EINVAL);
2007 f = j->current_file;
2009 return -EADDRNOTAVAIL;
2011 if (f->current_offset <= 0)
2012 return -EADDRNOTAVAIL;
2014 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2018 n = journal_file_entry_n_items(o);
2019 if (j->current_field >= n)
2022 p = le64toh(o->entry.items[j->current_field].object_offset);
2023 le_hash = o->entry.items[j->current_field].hash;
2024 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2028 if (le_hash != o->data.hash)
2031 r = return_data(j, f, o, data, size);
2035 j->current_field ++;
2040 _public_ void sd_journal_restart_data(sd_journal *j) {
2044 j->current_field = 0;
2047 _public_ int sd_journal_get_fd(sd_journal *j) {
2050 assert_return(j, -EINVAL);
2051 assert_return(!journal_pid_changed(j), -ECHILD);
2053 if (j->inotify_fd >= 0)
2054 return j->inotify_fd;
2056 r = allocate_inotify(j);
2060 /* Iterate through all dirs again, to add them to the
2062 if (j->no_new_files)
2063 r = add_current_paths(j);
2065 r = add_root_directory(j, j->path);
2067 r = add_search_paths(j);
2071 return j->inotify_fd;
2074 _public_ int sd_journal_get_events(sd_journal *j) {
2077 assert_return(j, -EINVAL);
2078 assert_return(!journal_pid_changed(j), -ECHILD);
2080 fd = sd_journal_get_fd(j);
2087 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2090 assert_return(j, -EINVAL);
2091 assert_return(!journal_pid_changed(j), -ECHILD);
2092 assert_return(timeout_usec, -EINVAL);
2094 fd = sd_journal_get_fd(j);
2098 if (!j->on_network) {
2099 *timeout_usec = (uint64_t) -1;
2103 /* If we are on the network we need to regularly check for
2104 * changes manually */
2106 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2110 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2117 /* Is this a subdirectory we watch? */
2118 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2122 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2123 (endswith(e->name, ".journal") ||
2124 endswith(e->name, ".journal~"))) {
2126 /* Event for a journal file */
2128 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2129 r = add_file(j, d->path, e->name);
2131 log_debug_errno(r, "Failed to add file %s/%s: %m",
2133 set_put_error(j, r);
2136 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2138 r = remove_file(j, d->path, e->name);
2140 log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
2143 } else if (!d->is_root && e->len == 0) {
2145 /* Event for a subdirectory */
2147 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2148 r = remove_directory(j, d);
2150 log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
2154 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2156 /* Event for root directory */
2158 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2159 r = add_directory(j, d->path, e->name);
2161 log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
2168 if (e->mask & IN_IGNORED)
2171 log_warning("Unknown inotify event.");
2174 static int determine_change(sd_journal *j) {
2179 b = j->current_invalidate_counter != j->last_invalidate_counter;
2180 j->last_invalidate_counter = j->current_invalidate_counter;
2182 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2185 _public_ int sd_journal_process(sd_journal *j) {
2186 bool got_something = false;
2188 assert_return(j, -EINVAL);
2189 assert_return(!journal_pid_changed(j), -ECHILD);
2191 j->last_process_usec = now(CLOCK_MONOTONIC);
2194 uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event);
2195 struct inotify_event *e;
2198 l = read(j->inotify_fd, buffer, sizeof(buffer));
2200 if (errno == EAGAIN || errno == EINTR)
2201 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2206 got_something = true;
2208 FOREACH_INOTIFY_EVENT(e, buffer, l)
2209 process_inotify_event(j, e);
2213 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2217 assert_return(j, -EINVAL);
2218 assert_return(!journal_pid_changed(j), -ECHILD);
2220 if (j->inotify_fd < 0) {
2222 /* This is the first invocation, hence create the
2224 r = sd_journal_get_fd(j);
2228 /* The journal might have changed since the context
2229 * object was created and we weren't watching before,
2230 * hence don't wait for anything, and return
2232 return determine_change(j);
2235 r = sd_journal_get_timeout(j, &t);
2239 if (t != (uint64_t) -1) {
2242 n = now(CLOCK_MONOTONIC);
2243 t = t > n ? t - n : 0;
2245 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2250 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2251 } while (r == -EINTR);
2256 return sd_journal_process(j);
2259 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2263 uint64_t fmin = 0, tmax = 0;
2266 assert_return(j, -EINVAL);
2267 assert_return(!journal_pid_changed(j), -ECHILD);
2268 assert_return(from || to, -EINVAL);
2269 assert_return(from != to, -EINVAL);
2271 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2274 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2287 fmin = MIN(fr, fmin);
2288 tmax = MAX(t, tmax);
2297 return first ? 0 : 1;
2300 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2306 assert_return(j, -EINVAL);
2307 assert_return(!journal_pid_changed(j), -ECHILD);
2308 assert_return(from || to, -EINVAL);
2309 assert_return(from != to, -EINVAL);
2311 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2314 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2324 *from = MIN(fr, *from);
2339 void journal_print_header(sd_journal *j) {
2342 bool newline = false;
2346 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2352 journal_file_print_header(f);
2356 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2361 assert_return(j, -EINVAL);
2362 assert_return(!journal_pid_changed(j), -ECHILD);
2363 assert_return(bytes, -EINVAL);
2365 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2368 if (fstat(f->fd, &st) < 0)
2371 sum += (uint64_t) st.st_blocks * 512ULL;
2378 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2381 assert_return(j, -EINVAL);
2382 assert_return(!journal_pid_changed(j), -ECHILD);
2383 assert_return(!isempty(field), -EINVAL);
2384 assert_return(field_is_valid(field), -EINVAL);
2390 free(j->unique_field);
2391 j->unique_field = f;
2392 j->unique_file = NULL;
2393 j->unique_offset = 0;
2394 j->unique_file_lost = false;
2399 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2402 assert_return(j, -EINVAL);
2403 assert_return(!journal_pid_changed(j), -ECHILD);
2404 assert_return(data, -EINVAL);
2405 assert_return(l, -EINVAL);
2406 assert_return(j->unique_field, -EINVAL);
2408 k = strlen(j->unique_field);
2410 if (!j->unique_file) {
2411 if (j->unique_file_lost)
2414 j->unique_file = ordered_hashmap_first(j->files);
2415 if (!j->unique_file)
2418 j->unique_offset = 0;
2430 /* Proceed to next data object in the field's linked list */
2431 if (j->unique_offset == 0) {
2432 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2436 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2438 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2442 j->unique_offset = le64toh(o->data.next_field_offset);
2445 /* We reached the end of the list? Then start again, with the next file */
2446 if (j->unique_offset == 0) {
2447 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
2448 if (!j->unique_file)
2454 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2455 * instead, so that we can look at this data object at the same
2456 * time as one on another file */
2457 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
2461 /* Let's do the type check by hand, since we used 0 context above. */
2462 if (o->object.type != OBJECT_DATA) {
2463 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
2464 j->unique_file->path, j->unique_offset,
2465 o->object.type, OBJECT_DATA);
2469 r = return_data(j, j->unique_file, o, &odata, &ol);
2473 /* Check if we have at least the field name and "=". */
2475 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2476 j->unique_file->path, j->unique_offset,
2481 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2482 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2483 j->unique_file->path, j->unique_offset,
2488 /* OK, now let's see if we already returned this data
2489 * object by checking if it exists in the earlier
2490 * traversed files. */
2492 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2496 if (of == j->unique_file)
2499 /* Skip this file it didn't have any fields
2501 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2502 le64toh(of->header->n_fields) <= 0)
2505 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2516 r = return_data(j, j->unique_file, o, data, l);
2524 _public_ void sd_journal_restart_unique(sd_journal *j) {
2528 j->unique_file = NULL;
2529 j->unique_offset = 0;
2530 j->unique_file_lost = false;
2533 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2534 assert_return(j, -EINVAL);
2535 assert_return(!journal_pid_changed(j), -ECHILD);
2537 return !j->on_network;
2540 static char *lookup_field(const char *field, void *userdata) {
2541 sd_journal *j = userdata;
2549 r = sd_journal_get_data(j, field, &data, &size);
2551 size > REPLACE_VAR_MAX)
2552 return strdup(field);
2554 d = strlen(field) + 1;
2556 return strndup((const char*) data + d, size - d);
2559 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2563 _cleanup_free_ char *text = NULL, *cid = NULL;
2567 assert_return(j, -EINVAL);
2568 assert_return(!journal_pid_changed(j), -ECHILD);
2569 assert_return(ret, -EINVAL);
2571 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2575 cid = strndup((const char*) data + 11, size - 11);
2579 r = sd_id128_from_string(cid, &id);
2583 r = catalog_get(CATALOG_DATABASE, id, &text);
2587 t = replace_var(text, lookup_field, j);
2595 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2596 assert_return(ret, -EINVAL);
2598 return catalog_get(CATALOG_DATABASE, id, ret);
2601 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2602 assert_return(j, -EINVAL);
2603 assert_return(!journal_pid_changed(j), -ECHILD);
2605 j->data_threshold = sz;
2609 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2610 assert_return(j, -EINVAL);
2611 assert_return(!journal_pid_changed(j), -ECHILD);
2612 assert_return(sz, -EINVAL);
2614 *sz = j->data_threshold;