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 static bool journal_pid_changed(sd_journal *j) {
56 /* We don't support people creating a journal object and
57 * keeping it around over a fork(). Let's complain. */
59 return j->original_pid != getpid();
62 /* We return an error here only if we didn't manage to
63 memorize the real error. */
64 static int set_put_error(sd_journal *j, int r) {
70 k = set_ensure_allocated(&j->errors, trivial_hash_func, trivial_compare_func);
74 return set_put(j->errors, INT_TO_PTR(r));
77 static void detach_location(sd_journal *j) {
83 j->current_file = NULL;
86 HASHMAP_FOREACH(f, j->files, i)
87 f->current_offset = 0;
90 static void reset_location(sd_journal *j) {
94 zero(j->current_location);
97 static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
99 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
101 assert(o->object.type == OBJECT_ENTRY);
104 l->seqnum = le64toh(o->entry.seqnum);
105 l->seqnum_id = f->header->seqnum_id;
106 l->realtime = le64toh(o->entry.realtime);
107 l->monotonic = le64toh(o->entry.monotonic);
108 l->boot_id = o->entry.boot_id;
109 l->xor_hash = le64toh(o->entry.xor_hash);
111 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
114 static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o,
115 direction_t direction, uint64_t offset) {
117 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
121 init_location(&j->current_location, type, f, o);
124 j->current_field = 0;
126 f->last_direction = direction;
127 f->current_offset = offset;
130 static int match_is_valid(const void *data, size_t size) {
138 if (startswith(data, "__"))
142 for (p = b; p < b + size; p++) {
150 if (*p >= 'A' && *p <= 'Z')
153 if (*p >= '0' && *p <= '9')
162 static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
163 const uint8_t *a = _a, *b = _b;
166 for (j = 0; j < s && j < t; j++) {
175 assert_not_reached("\"=\" not found");
178 static Match *match_new(Match *p, MatchType t) {
189 LIST_PREPEND(matches, p->matches, m);
195 static void match_free(Match *m) {
199 match_free(m->matches);
202 LIST_REMOVE(matches, m->parent->matches, m);
208 static void match_free_if_empty(Match *m) {
209 if (!m || m->matches)
215 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
216 Match *l3, *l4, *add_here = NULL, *m;
219 assert_return(j, -EINVAL);
220 assert_return(!journal_pid_changed(j), -ECHILD);
221 assert_return(data, -EINVAL);
226 assert_return(match_is_valid(data, size), -EINVAL);
232 * level 4: concrete matches */
235 j->level0 = match_new(NULL, MATCH_AND_TERM);
241 j->level1 = match_new(j->level0, MATCH_OR_TERM);
247 j->level2 = match_new(j->level1, MATCH_AND_TERM);
252 assert(j->level0->type == MATCH_AND_TERM);
253 assert(j->level1->type == MATCH_OR_TERM);
254 assert(j->level2->type == MATCH_AND_TERM);
256 le_hash = htole64(hash64(data, size));
258 LIST_FOREACH(matches, l3, j->level2->matches) {
259 assert(l3->type == MATCH_OR_TERM);
261 LIST_FOREACH(matches, l4, l3->matches) {
262 assert(l4->type == MATCH_DISCRETE);
264 /* Exactly the same match already? Then ignore
266 if (l4->le_hash == le_hash &&
268 memcmp(l4->data, data, size) == 0)
271 /* Same field? Then let's add this to this OR term */
272 if (same_field(data, size, l4->data, l4->size)) {
283 add_here = match_new(j->level2, MATCH_OR_TERM);
288 m = match_new(add_here, MATCH_DISCRETE);
292 m->le_hash = le_hash;
294 m->data = memdup(data, size);
303 match_free_if_empty(add_here);
304 match_free_if_empty(j->level2);
305 match_free_if_empty(j->level1);
306 match_free_if_empty(j->level0);
311 _public_ int sd_journal_add_conjunction(sd_journal *j) {
312 assert_return(j, -EINVAL);
313 assert_return(!journal_pid_changed(j), -ECHILD);
321 if (!j->level1->matches)
330 _public_ int sd_journal_add_disjunction(sd_journal *j) {
331 assert_return(j, -EINVAL);
332 assert_return(!journal_pid_changed(j), -ECHILD);
343 if (!j->level2->matches)
350 static char *match_make_string(Match *m) {
353 bool enclose = false;
356 return strdup("none");
358 if (m->type == MATCH_DISCRETE)
359 return strndup(m->data, m->size);
362 LIST_FOREACH(matches, i, m->matches) {
365 t = match_make_string(i);
372 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
387 r = strjoin("(", p, ")", NULL);
395 char *journal_make_match_string(sd_journal *j) {
398 return match_make_string(j->level0);
401 _public_ void sd_journal_flush_matches(sd_journal *j) {
406 match_free(j->level0);
408 j->level0 = j->level1 = j->level2 = NULL;
413 static int compare_entry_order(JournalFile *af, Object *_ao,
414 JournalFile *bf, uint64_t bp) {
424 /* The mmap cache might invalidate the object from the first
425 * file if we look at the one from the second file. Hence
426 * temporarily copy the header of the first one, and look at
428 ao = alloca(offsetof(EntryObject, items));
429 memcpy(ao, _ao, offsetof(EntryObject, items));
431 r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
433 return strcmp(af->path, bf->path);
435 /* We operate on two different files here, hence we can access
436 * two objects at the same time, which we normally can't.
438 * If contents and timestamps match, these entries are
439 * identical, even if the seqnum does not match */
441 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
442 ao->entry.monotonic == bo->entry.monotonic &&
443 ao->entry.realtime == bo->entry.realtime &&
444 ao->entry.xor_hash == bo->entry.xor_hash)
447 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
449 /* If this is from the same seqnum source, compare
451 a = le64toh(ao->entry.seqnum);
452 b = le64toh(bo->entry.seqnum);
459 /* Wow! This is weird, different data but the same
460 * seqnums? Something is borked, but let's make the
461 * best of it and compare by time. */
464 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
466 /* If the boot id matches compare monotonic time */
467 a = le64toh(ao->entry.monotonic);
468 b = le64toh(bo->entry.monotonic);
476 /* Otherwise compare UTC time */
477 a = le64toh(ao->entry.realtime);
478 b = le64toh(bo->entry.realtime);
485 /* Finally, compare by contents */
486 a = le64toh(ao->entry.xor_hash);
487 b = le64toh(bo->entry.xor_hash);
497 _pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
503 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
505 if (l->monotonic_set &&
506 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
508 le64toh(ao->entry.realtime) == l->realtime &&
510 le64toh(ao->entry.xor_hash) == l->xor_hash)
514 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
516 a = le64toh(ao->entry.seqnum);
524 if (l->monotonic_set &&
525 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
527 a = le64toh(ao->entry.monotonic);
529 if (a < l->monotonic)
531 if (a > l->monotonic)
535 if (l->realtime_set) {
537 a = le64toh(ao->entry.realtime);
545 if (l->xor_hash_set) {
546 a = le64toh(ao->entry.xor_hash);
557 static int next_for_match(
561 uint64_t after_offset,
562 direction_t direction,
574 if (m->type == MATCH_DISCRETE) {
577 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
581 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
583 } else if (m->type == MATCH_OR_TERM) {
586 /* Find the earliest match beyond after_offset */
588 LIST_FOREACH(matches, i, m->matches) {
591 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
595 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
603 } else if (m->type == MATCH_AND_TERM) {
604 Match *i, *last_moved;
606 /* Always jump to the next matching entry and repeat
607 * this until we find an offset that matches for all
613 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
617 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
618 last_moved = m->matches;
620 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
623 r = next_for_match(j, i, f, np, direction, NULL, &cp);
627 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
628 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
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 ? cp > np : cp < np))
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->last_direction == direction && 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 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;
878 assert_return(j, -EINVAL);
879 assert_return(!journal_pid_changed(j), -ECHILD);
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 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
910 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
914 set_location(j, LOCATION_DISCRETE, new_file, o, direction, new_offset);
919 _public_ int sd_journal_next(sd_journal *j) {
920 return real_journal_next(j, DIRECTION_DOWN);
923 _public_ int sd_journal_previous(sd_journal *j) {
924 return real_journal_next(j, DIRECTION_UP);
927 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
930 assert_return(j, -EINVAL);
931 assert_return(!journal_pid_changed(j), -ECHILD);
934 /* If this is not a discrete skip, then at least
935 * resolve the current location */
936 if (j->current_location.type != LOCATION_DISCRETE)
937 return real_journal_next(j, direction);
943 r = real_journal_next(j, direction);
957 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
958 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
961 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
962 return real_journal_next_skip(j, DIRECTION_UP, skip);
965 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
968 char bid[33], sid[33];
970 assert_return(j, -EINVAL);
971 assert_return(!journal_pid_changed(j), -ECHILD);
972 assert_return(cursor, -EINVAL);
974 if (!j->current_file || j->current_file->current_offset <= 0)
975 return -EADDRNOTAVAIL;
977 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
981 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
982 sd_id128_to_string(o->entry.boot_id, bid);
985 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
986 sid, le64toh(o->entry.seqnum),
987 bid, le64toh(o->entry.monotonic),
988 le64toh(o->entry.realtime),
989 le64toh(o->entry.xor_hash)) < 0)
995 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
998 unsigned long long seqnum, monotonic, realtime, xor_hash;
1000 seqnum_id_set = false,
1002 boot_id_set = false,
1003 monotonic_set = false,
1004 realtime_set = false,
1005 xor_hash_set = false;
1006 sd_id128_t seqnum_id, boot_id;
1008 assert_return(j, -EINVAL);
1009 assert_return(!journal_pid_changed(j), -ECHILD);
1010 assert_return(!isempty(cursor), -EINVAL);
1012 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
1016 if (l < 2 || w[1] != '=')
1019 item = strndup(w, l);
1026 seqnum_id_set = true;
1027 k = sd_id128_from_string(item+2, &seqnum_id);
1032 if (sscanf(item+2, "%llx", &seqnum) != 1)
1038 k = sd_id128_from_string(item+2, &boot_id);
1042 monotonic_set = true;
1043 if (sscanf(item+2, "%llx", &monotonic) != 1)
1048 realtime_set = true;
1049 if (sscanf(item+2, "%llx", &realtime) != 1)
1054 xor_hash_set = true;
1055 if (sscanf(item+2, "%llx", &xor_hash) != 1)
1066 if ((!seqnum_set || !seqnum_id_set) &&
1067 (!monotonic_set || !boot_id_set) &&
1073 j->current_location.type = LOCATION_SEEK;
1076 j->current_location.realtime = (uint64_t) realtime;
1077 j->current_location.realtime_set = true;
1080 if (seqnum_set && seqnum_id_set) {
1081 j->current_location.seqnum = (uint64_t) seqnum;
1082 j->current_location.seqnum_id = seqnum_id;
1083 j->current_location.seqnum_set = true;
1086 if (monotonic_set && boot_id_set) {
1087 j->current_location.monotonic = (uint64_t) monotonic;
1088 j->current_location.boot_id = boot_id;
1089 j->current_location.monotonic_set = true;
1093 j->current_location.xor_hash = (uint64_t) xor_hash;
1094 j->current_location.xor_hash_set = true;
1100 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1106 assert_return(j, -EINVAL);
1107 assert_return(!journal_pid_changed(j), -ECHILD);
1108 assert_return(!isempty(cursor), -EINVAL);
1110 if (!j->current_file || j->current_file->current_offset <= 0)
1111 return -EADDRNOTAVAIL;
1113 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1117 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
1118 _cleanup_free_ char *item = NULL;
1120 unsigned long long ll;
1123 if (l < 2 || w[1] != '=')
1126 item = strndup(w, l);
1133 k = sd_id128_from_string(item+2, &id);
1136 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1141 if (sscanf(item+2, "%llx", &ll) != 1)
1143 if (ll != le64toh(o->entry.seqnum))
1148 k = sd_id128_from_string(item+2, &id);
1151 if (!sd_id128_equal(id, o->entry.boot_id))
1156 if (sscanf(item+2, "%llx", &ll) != 1)
1158 if (ll != le64toh(o->entry.monotonic))
1163 if (sscanf(item+2, "%llx", &ll) != 1)
1165 if (ll != le64toh(o->entry.realtime))
1170 if (sscanf(item+2, "%llx", &ll) != 1)
1172 if (ll != le64toh(o->entry.xor_hash))
1182 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1183 assert_return(j, -EINVAL);
1184 assert_return(!journal_pid_changed(j), -ECHILD);
1187 j->current_location.type = LOCATION_SEEK;
1188 j->current_location.boot_id = boot_id;
1189 j->current_location.monotonic = usec;
1190 j->current_location.monotonic_set = true;
1195 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1196 assert_return(j, -EINVAL);
1197 assert_return(!journal_pid_changed(j), -ECHILD);
1200 j->current_location.type = LOCATION_SEEK;
1201 j->current_location.realtime = usec;
1202 j->current_location.realtime_set = true;
1207 _public_ int sd_journal_seek_head(sd_journal *j) {
1208 assert_return(j, -EINVAL);
1209 assert_return(!journal_pid_changed(j), -ECHILD);
1212 j->current_location.type = LOCATION_HEAD;
1217 _public_ int sd_journal_seek_tail(sd_journal *j) {
1218 assert_return(j, -EINVAL);
1219 assert_return(!journal_pid_changed(j), -ECHILD);
1222 j->current_location.type = LOCATION_TAIL;
1227 static void check_network(sd_journal *j, int fd) {
1235 if (fstatfs(fd, &sfs) < 0)
1239 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1240 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1241 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1242 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1243 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1246 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1247 const char *full, *tilded, *atted;
1249 full = strappenda(prefix, ".journal");
1250 tilded = strappenda(full, "~");
1251 atted = strappenda(prefix, "@");
1253 return streq(filename, full) ||
1254 streq(filename, tilded) ||
1255 startswith(filename, atted);
1258 static bool file_type_wanted(int flags, const char *filename) {
1259 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1262 /* no flags set → every type is OK */
1263 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1266 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1269 if (flags & SD_JOURNAL_CURRENT_USER) {
1270 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1272 assert_se(snprintf(prefix, sizeof(prefix), "user-%lu", (unsigned long) getuid())
1273 < (int) sizeof(prefix));
1275 if (file_has_type_prefix(prefix, filename))
1282 static int add_any_file(sd_journal *j, const char *path) {
1289 if (hashmap_get(j->files, path))
1292 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1293 log_warning("Too many open journal files, not adding %s.", path);
1294 return set_put_error(j, -ETOOMANYREFS);
1297 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1301 /* journal_file_dump(f); */
1303 r = hashmap_put(j->files, f->path, f);
1305 journal_file_close(f);
1309 log_debug("File %s added.", f->path);
1311 check_network(j, f->fd);
1313 j->current_invalidate_counter ++;
1318 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1319 _cleanup_free_ char *path = NULL;
1326 if (j->no_new_files ||
1327 !file_type_wanted(j->flags, filename))
1330 path = strjoin(prefix, "/", filename, NULL);
1334 r = add_any_file(j, path);
1340 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1348 path = strjoin(prefix, "/", filename, NULL);
1352 f = hashmap_get(j->files, path);
1357 hashmap_remove(j->files, f->path);
1359 log_debug("File %s removed.", f->path);
1361 if (j->current_file == f) {
1362 j->current_file = NULL;
1363 j->current_field = 0;
1366 if (j->unique_file == f) {
1367 j->unique_file = NULL;
1368 j->unique_offset = 0;
1371 journal_file_close(f);
1373 j->current_invalidate_counter ++;
1378 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1379 _cleanup_free_ char *path = NULL;
1381 _cleanup_closedir_ DIR *d = NULL;
1389 log_debug("Considering %s/%s.", prefix, dirname);
1391 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1392 (sd_id128_from_string(dirname, &id) < 0 ||
1393 sd_id128_get_machine(&mid) < 0 ||
1394 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1397 path = strjoin(prefix, "/", dirname, NULL);
1403 log_debug("Failed to open %s: %m", path);
1404 if (errno == ENOENT)
1409 m = hashmap_get(j->directories_by_path, path);
1411 m = new0(Directory, 1);
1418 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1423 path = NULL; /* avoid freeing in cleanup */
1424 j->current_invalidate_counter ++;
1426 log_debug("Directory %s added.", m->path);
1428 } else if (m->is_root)
1431 if (m->wd <= 0 && j->inotify_fd >= 0) {
1433 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1434 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1435 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1438 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1439 inotify_rm_watch(j->inotify_fd, m->wd);
1444 union dirent_storage buf;
1446 r = readdir_r(d, &buf.de, &de);
1450 if (dirent_is_file_with_suffix(de, ".journal") ||
1451 dirent_is_file_with_suffix(de, ".journal~")) {
1452 r = add_file(j, m->path, de->d_name);
1454 log_debug("Failed to add file %s/%s: %s",
1455 m->path, de->d_name, strerror(-r));
1456 r = set_put_error(j, r);
1463 check_network(j, dirfd(d));
1468 static int add_root_directory(sd_journal *j, const char *p) {
1469 _cleanup_closedir_ DIR *d = NULL;
1476 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1477 !path_startswith(p, "/run"))
1484 m = hashmap_get(j->directories_by_path, p);
1486 m = new0(Directory, 1);
1491 m->path = strdup(p);
1497 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1503 j->current_invalidate_counter ++;
1505 log_debug("Root directory %s added.", m->path);
1507 } else if (!m->is_root)
1510 if (m->wd <= 0 && j->inotify_fd >= 0) {
1512 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1513 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1516 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1517 inotify_rm_watch(j->inotify_fd, m->wd);
1520 if (j->no_new_files)
1525 union dirent_storage buf;
1528 r = readdir_r(d, &buf.de, &de);
1532 if (dirent_is_file_with_suffix(de, ".journal") ||
1533 dirent_is_file_with_suffix(de, ".journal~")) {
1534 r = add_file(j, m->path, de->d_name);
1536 log_debug("Failed to add file %s/%s: %s",
1537 m->path, de->d_name, strerror(-r));
1538 r = set_put_error(j, r);
1542 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1543 sd_id128_from_string(de->d_name, &id) >= 0) {
1545 r = add_directory(j, m->path, de->d_name);
1547 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1551 check_network(j, dirfd(d));
1556 static int remove_directory(sd_journal *j, Directory *d) {
1560 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1562 if (j->inotify_fd >= 0)
1563 inotify_rm_watch(j->inotify_fd, d->wd);
1566 hashmap_remove(j->directories_by_path, d->path);
1569 log_debug("Root directory %s removed.", d->path);
1571 log_debug("Directory %s removed.", d->path);
1579 static int add_search_paths(sd_journal *j) {
1581 const char search_paths[] =
1582 "/run/log/journal\0"
1583 "/var/log/journal\0";
1588 /* We ignore most errors here, since the idea is to only open
1589 * what's actually accessible, and ignore the rest. */
1591 NULSTR_FOREACH(p, search_paths) {
1592 r = add_root_directory(j, p);
1593 if (r < 0 && r != -ENOENT) {
1594 r = set_put_error(j, r);
1603 static int add_current_paths(sd_journal *j) {
1608 assert(j->no_new_files);
1610 /* Simply adds all directories for files we have open as
1611 * "root" directories. We don't expect errors here, so we
1612 * treat them as fatal. */
1614 HASHMAP_FOREACH(f, j->files, i) {
1616 _cleanup_free_ char *dir;
1618 dir = dirname_malloc(f->path);
1622 r = add_root_directory(j, dir);
1624 set_put_error(j, r);
1633 static int allocate_inotify(sd_journal *j) {
1636 if (j->inotify_fd < 0) {
1637 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1638 if (j->inotify_fd < 0)
1642 if (!j->directories_by_wd) {
1643 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1644 if (!j->directories_by_wd)
1651 static sd_journal *journal_new(int flags, const char *path) {
1654 j = new0(sd_journal, 1);
1658 j->original_pid = getpid();
1661 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1664 j->path = strdup(path);
1669 j->files = hashmap_new(string_hash_func, string_compare_func);
1670 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1671 j->mmap = mmap_cache_new();
1672 if (!j->files || !j->directories_by_path || !j->mmap)
1678 sd_journal_close(j);
1682 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1686 assert_return(ret, -EINVAL);
1688 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1689 SD_JOURNAL_RUNTIME_ONLY|
1691 SD_JOURNAL_CURRENT_USER))
1694 j = journal_new(flags, NULL);
1698 r = add_search_paths(j);
1706 sd_journal_close(j);
1711 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1715 assert_return(ret, -EINVAL);
1716 assert_return(path, -EINVAL);
1717 assert_return(flags == 0, -EINVAL);
1719 j = journal_new(flags, path);
1723 r = add_root_directory(j, path);
1725 set_put_error(j, r);
1733 sd_journal_close(j);
1738 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1743 assert_return(ret, -EINVAL);
1744 assert_return(flags == 0, -EINVAL);
1746 j = journal_new(flags, NULL);
1750 STRV_FOREACH(path, paths) {
1751 r = add_any_file(j, *path);
1753 log_error("Failed to open %s: %s", *path, strerror(-r));
1758 j->no_new_files = true;
1764 sd_journal_close(j);
1769 _public_ void sd_journal_close(sd_journal *j) {
1776 sd_journal_flush_matches(j);
1778 while ((f = hashmap_steal_first(j->files)))
1779 journal_file_close(f);
1781 hashmap_free(j->files);
1783 while ((d = hashmap_first(j->directories_by_path)))
1784 remove_directory(j, d);
1786 while ((d = hashmap_first(j->directories_by_wd)))
1787 remove_directory(j, d);
1789 hashmap_free(j->directories_by_path);
1790 hashmap_free(j->directories_by_wd);
1792 if (j->inotify_fd >= 0)
1793 close_nointr_nofail(j->inotify_fd);
1796 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1797 mmap_cache_unref(j->mmap);
1801 free(j->unique_field);
1802 set_free(j->errors);
1806 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1811 assert_return(j, -EINVAL);
1812 assert_return(!journal_pid_changed(j), -ECHILD);
1813 assert_return(ret, -EINVAL);
1815 f = j->current_file;
1817 return -EADDRNOTAVAIL;
1819 if (f->current_offset <= 0)
1820 return -EADDRNOTAVAIL;
1822 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1826 *ret = le64toh(o->entry.realtime);
1830 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1836 assert_return(j, -EINVAL);
1837 assert_return(!journal_pid_changed(j), -ECHILD);
1839 f = j->current_file;
1841 return -EADDRNOTAVAIL;
1843 if (f->current_offset <= 0)
1844 return -EADDRNOTAVAIL;
1846 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1851 *ret_boot_id = o->entry.boot_id;
1853 r = sd_id128_get_boot(&id);
1857 if (!sd_id128_equal(id, o->entry.boot_id))
1862 *ret = le64toh(o->entry.monotonic);
1867 static bool field_is_valid(const char *field) {
1875 if (startswith(field, "__"))
1878 for (p = field; *p; p++) {
1883 if (*p >= 'A' && *p <= 'Z')
1886 if (*p >= '0' && *p <= '9')
1895 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1898 size_t field_length;
1902 assert_return(j, -EINVAL);
1903 assert_return(!journal_pid_changed(j), -ECHILD);
1904 assert_return(field, -EINVAL);
1905 assert_return(data, -EINVAL);
1906 assert_return(size, -EINVAL);
1907 assert_return(field_is_valid(field), -EINVAL);
1909 f = j->current_file;
1911 return -EADDRNOTAVAIL;
1913 if (f->current_offset <= 0)
1914 return -EADDRNOTAVAIL;
1916 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1920 field_length = strlen(field);
1922 n = journal_file_entry_n_items(o);
1923 for (i = 0; i < n; i++) {
1928 p = le64toh(o->entry.items[i].object_offset);
1929 le_hash = o->entry.items[i].hash;
1930 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1934 if (le_hash != o->data.hash)
1937 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1939 if (o->object.flags & OBJECT_COMPRESSED) {
1942 if (uncompress_startswith(o->data.payload, l,
1943 &f->compress_buffer, &f->compress_buffer_size,
1944 field, field_length, '=')) {
1948 if (!uncompress_blob(o->data.payload, l,
1949 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1953 *data = f->compress_buffer;
1954 *size = (size_t) rsize;
1959 return -EPROTONOSUPPORT;
1962 } else if (l >= field_length+1 &&
1963 memcmp(o->data.payload, field, field_length) == 0 &&
1964 o->data.payload[field_length] == '=') {
1968 if ((uint64_t) t != l)
1971 *data = o->data.payload;
1977 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1985 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
1989 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1992 /* We can't read objects larger than 4G on a 32bit machine */
1993 if ((uint64_t) t != l)
1996 if (o->object.flags & OBJECT_COMPRESSED) {
2000 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, j->data_threshold))
2003 *data = f->compress_buffer;
2004 *size = (size_t) rsize;
2006 return -EPROTONOSUPPORT;
2009 *data = o->data.payload;
2016 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2023 assert_return(j, -EINVAL);
2024 assert_return(!journal_pid_changed(j), -ECHILD);
2025 assert_return(data, -EINVAL);
2026 assert_return(size, -EINVAL);
2028 f = j->current_file;
2030 return -EADDRNOTAVAIL;
2032 if (f->current_offset <= 0)
2033 return -EADDRNOTAVAIL;
2035 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2039 n = journal_file_entry_n_items(o);
2040 if (j->current_field >= n)
2043 p = le64toh(o->entry.items[j->current_field].object_offset);
2044 le_hash = o->entry.items[j->current_field].hash;
2045 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2049 if (le_hash != o->data.hash)
2052 r = return_data(j, f, o, data, size);
2056 j->current_field ++;
2061 _public_ void sd_journal_restart_data(sd_journal *j) {
2065 j->current_field = 0;
2068 _public_ int sd_journal_get_fd(sd_journal *j) {
2071 assert_return(j, -EINVAL);
2072 assert_return(!journal_pid_changed(j), -ECHILD);
2074 if (j->inotify_fd >= 0)
2075 return j->inotify_fd;
2077 r = allocate_inotify(j);
2081 /* Iterate through all dirs again, to add them to the
2083 if (j->no_new_files)
2084 r = add_current_paths(j);
2086 r = add_root_directory(j, j->path);
2088 r = add_search_paths(j);
2092 return j->inotify_fd;
2095 _public_ int sd_journal_get_events(sd_journal *j) {
2098 assert_return(j, -EINVAL);
2099 assert_return(!journal_pid_changed(j), -ECHILD);
2101 fd = sd_journal_get_fd(j);
2108 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2111 assert_return(j, -EINVAL);
2112 assert_return(!journal_pid_changed(j), -ECHILD);
2113 assert_return(timeout_usec, -EINVAL);
2115 fd = sd_journal_get_fd(j);
2119 if (!j->on_network) {
2120 *timeout_usec = (uint64_t) -1;
2124 /* If we are on the network we need to regularly check for
2125 * changes manually */
2127 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2131 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2138 /* Is this a subdirectory we watch? */
2139 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2143 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2144 (endswith(e->name, ".journal") ||
2145 endswith(e->name, ".journal~"))) {
2147 /* Event for a journal file */
2149 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2150 r = add_file(j, d->path, e->name);
2152 log_debug("Failed to add file %s/%s: %s",
2153 d->path, e->name, strerror(-r));
2154 set_put_error(j, r);
2157 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2159 r = remove_file(j, d->path, e->name);
2161 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
2164 } else if (!d->is_root && e->len == 0) {
2166 /* Event for a subdirectory */
2168 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2169 r = remove_directory(j, d);
2171 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
2175 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2177 /* Event for root directory */
2179 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2180 r = add_directory(j, d->path, e->name);
2182 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
2189 if (e->mask & IN_IGNORED)
2192 log_warning("Unknown inotify event.");
2195 static int determine_change(sd_journal *j) {
2200 b = j->current_invalidate_counter != j->last_invalidate_counter;
2201 j->last_invalidate_counter = j->current_invalidate_counter;
2203 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2206 _public_ int sd_journal_process(sd_journal *j) {
2207 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
2208 bool got_something = false;
2210 assert_return(j, -EINVAL);
2211 assert_return(!journal_pid_changed(j), -ECHILD);
2213 j->last_process_usec = now(CLOCK_MONOTONIC);
2216 struct inotify_event *e;
2219 l = read(j->inotify_fd, buffer, sizeof(buffer));
2221 if (errno == EAGAIN || errno == EINTR)
2222 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2227 got_something = true;
2229 e = (struct inotify_event*) buffer;
2233 process_inotify_event(j, e);
2235 step = sizeof(struct inotify_event) + e->len;
2236 assert(step <= (size_t) l);
2238 e = (struct inotify_event*) ((uint8_t*) e + step);
2243 return determine_change(j);
2246 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2250 assert_return(j, -EINVAL);
2251 assert_return(!journal_pid_changed(j), -ECHILD);
2253 if (j->inotify_fd < 0) {
2255 /* This is the first invocation, hence create the
2257 r = sd_journal_get_fd(j);
2261 /* The journal might have changed since the context
2262 * object was created and we weren't watching before,
2263 * hence don't wait for anything, and return
2265 return determine_change(j);
2268 r = sd_journal_get_timeout(j, &t);
2272 if (t != (uint64_t) -1) {
2275 n = now(CLOCK_MONOTONIC);
2276 t = t > n ? t - n : 0;
2278 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2283 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2284 } while (r == -EINTR);
2289 return sd_journal_process(j);
2292 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2298 assert_return(j, -EINVAL);
2299 assert_return(!journal_pid_changed(j), -ECHILD);
2300 assert_return(from || to, -EINVAL);
2301 assert_return(from != to, -EINVAL);
2303 HASHMAP_FOREACH(f, j->files, i) {
2306 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2322 *from = MIN(fr, *from);
2328 return first ? 0 : 1;
2331 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2337 assert_return(j, -EINVAL);
2338 assert_return(!journal_pid_changed(j), -ECHILD);
2339 assert_return(from || to, -EINVAL);
2340 assert_return(from != to, -EINVAL);
2342 HASHMAP_FOREACH(f, j->files, i) {
2345 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2361 *from = MIN(fr, *from);
2367 return first ? 0 : 1;
2370 void journal_print_header(sd_journal *j) {
2373 bool newline = false;
2377 HASHMAP_FOREACH(f, j->files, i) {
2383 journal_file_print_header(f);
2387 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2392 assert_return(j, -EINVAL);
2393 assert_return(!journal_pid_changed(j), -ECHILD);
2394 assert_return(bytes, -EINVAL);
2396 HASHMAP_FOREACH(f, j->files, i) {
2399 if (fstat(f->fd, &st) < 0)
2402 sum += (uint64_t) st.st_blocks * 512ULL;
2409 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2412 assert_return(j, -EINVAL);
2413 assert_return(!journal_pid_changed(j), -ECHILD);
2414 assert_return(!isempty(field), -EINVAL);
2415 assert_return(field_is_valid(field), -EINVAL);
2421 free(j->unique_field);
2422 j->unique_field = f;
2423 j->unique_file = NULL;
2424 j->unique_offset = 0;
2429 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2434 assert_return(j, -EINVAL);
2435 assert_return(!journal_pid_changed(j), -ECHILD);
2436 assert_return(data, -EINVAL);
2437 assert_return(l, -EINVAL);
2438 assert_return(j->unique_field, -EINVAL);
2440 k = strlen(j->unique_field);
2442 if (!j->unique_file) {
2443 j->unique_file = hashmap_first(j->files);
2444 if (!j->unique_file)
2446 j->unique_offset = 0;
2456 /* Proceed to next data object in the field's linked list */
2457 if (j->unique_offset == 0) {
2458 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2462 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2464 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2468 j->unique_offset = le64toh(o->data.next_field_offset);
2471 /* We reached the end of the list? Then start again, with the next file */
2472 if (j->unique_offset == 0) {
2475 n = hashmap_next(j->files, j->unique_file->path);
2483 /* We do not use the type context here, but 0 instead,
2484 * so that we can look at this data object at the same
2485 * time as one on another file */
2486 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2490 /* Let's do the type check by hand, since we used 0 context above. */
2491 if (o->object.type != OBJECT_DATA)
2494 r = return_data(j, j->unique_file, o, &odata, &ol);
2498 /* OK, now let's see if we already returned this data
2499 * object by checking if it exists in the earlier
2500 * traversed files. */
2502 HASHMAP_FOREACH(of, j->files, i) {
2506 if (of == j->unique_file)
2509 /* Skip this file it didn't have any fields
2511 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2512 le64toh(of->header->n_fields) <= 0)
2515 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2526 r = return_data(j, j->unique_file, o, data, l);
2534 _public_ void sd_journal_restart_unique(sd_journal *j) {
2538 j->unique_file = NULL;
2539 j->unique_offset = 0;
2542 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2543 assert_return(j, -EINVAL);
2544 assert_return(!journal_pid_changed(j), -ECHILD);
2546 return !j->on_network;
2549 static char *lookup_field(const char *field, void *userdata) {
2550 sd_journal *j = userdata;
2558 r = sd_journal_get_data(j, field, &data, &size);
2560 size > REPLACE_VAR_MAX)
2561 return strdup(field);
2563 d = strlen(field) + 1;
2565 return strndup((const char*) data + d, size - d);
2568 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2572 _cleanup_free_ char *text = NULL, *cid = NULL;
2576 assert_return(j, -EINVAL);
2577 assert_return(!journal_pid_changed(j), -ECHILD);
2578 assert_return(ret, -EINVAL);
2580 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2584 cid = strndup((const char*) data + 11, size - 11);
2588 r = sd_id128_from_string(cid, &id);
2592 r = catalog_get(CATALOG_DATABASE, id, &text);
2596 t = replace_var(text, lookup_field, j);
2604 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2605 assert_return(ret, -EINVAL);
2607 return catalog_get(CATALOG_DATABASE, id, ret);
2610 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2611 assert_return(j, -EINVAL);
2612 assert_return(!journal_pid_changed(j), -ECHILD);
2614 j->data_threshold = sz;
2618 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2619 assert_return(j, -EINVAL);
2620 assert_return(!journal_pid_changed(j), -ECHILD);
2621 assert_return(sz, -EINVAL);
2623 *sz = j->data_threshold;