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 bool journal_pid_changed(sd_journal *j) {
57 /* We don't support people creating a journal object and
58 * keeping it around over a fork(). Let's complain. */
60 return j->original_pid != getpid();
63 /* We return an error here only if we didn't manage to
64 memorize the real error. */
65 static int set_put_error(sd_journal *j, int r) {
71 k = set_ensure_allocated(&j->errors, trivial_hash_func, trivial_compare_func);
75 return set_put(j->errors, INT_TO_PTR(r));
78 static void detach_location(sd_journal *j) {
84 j->current_file = NULL;
87 HASHMAP_FOREACH(f, j->files, i)
88 f->current_offset = 0;
91 static void reset_location(sd_journal *j) {
95 zero(j->current_location);
98 static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
100 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
102 assert(o->object.type == OBJECT_ENTRY);
105 l->seqnum = le64toh(o->entry.seqnum);
106 l->seqnum_id = f->header->seqnum_id;
107 l->realtime = le64toh(o->entry.realtime);
108 l->monotonic = le64toh(o->entry.monotonic);
109 l->boot_id = o->entry.boot_id;
110 l->xor_hash = le64toh(o->entry.xor_hash);
112 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
115 static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o,
116 direction_t direction, uint64_t offset) {
118 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
122 init_location(&j->current_location, type, f, o);
125 j->current_field = 0;
127 f->last_direction = direction;
128 f->current_offset = offset;
131 static int match_is_valid(const void *data, size_t size) {
139 if (startswith(data, "__"))
143 for (p = b; p < b + size; p++) {
151 if (*p >= 'A' && *p <= 'Z')
154 if (*p >= '0' && *p <= '9')
163 static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
164 const uint8_t *a = _a, *b = _b;
167 for (j = 0; j < s && j < t; j++) {
176 assert_not_reached("\"=\" not found");
179 static Match *match_new(Match *p, MatchType t) {
190 LIST_PREPEND(matches, p->matches, m);
196 static void match_free(Match *m) {
200 match_free(m->matches);
203 LIST_REMOVE(matches, m->parent->matches, m);
209 static void match_free_if_empty(Match *m) {
210 if (!m || m->matches)
216 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
217 Match *l3, *l4, *add_here = NULL, *m;
220 assert_return(j, -EINVAL);
221 assert_return(!journal_pid_changed(j), -ECHILD);
222 assert_return(data, -EINVAL);
227 assert_return(match_is_valid(data, size), -EINVAL);
233 * level 4: concrete matches */
236 j->level0 = match_new(NULL, MATCH_AND_TERM);
242 j->level1 = match_new(j->level0, MATCH_OR_TERM);
248 j->level2 = match_new(j->level1, MATCH_AND_TERM);
253 assert(j->level0->type == MATCH_AND_TERM);
254 assert(j->level1->type == MATCH_OR_TERM);
255 assert(j->level2->type == MATCH_AND_TERM);
257 le_hash = htole64(hash64(data, size));
259 LIST_FOREACH(matches, l3, j->level2->matches) {
260 assert(l3->type == MATCH_OR_TERM);
262 LIST_FOREACH(matches, l4, l3->matches) {
263 assert(l4->type == MATCH_DISCRETE);
265 /* Exactly the same match already? Then ignore
267 if (l4->le_hash == le_hash &&
269 memcmp(l4->data, data, size) == 0)
272 /* Same field? Then let's add this to this OR term */
273 if (same_field(data, size, l4->data, l4->size)) {
284 add_here = match_new(j->level2, MATCH_OR_TERM);
289 m = match_new(add_here, MATCH_DISCRETE);
293 m->le_hash = le_hash;
295 m->data = memdup(data, size);
304 match_free_if_empty(add_here);
305 match_free_if_empty(j->level2);
306 match_free_if_empty(j->level1);
307 match_free_if_empty(j->level0);
312 _public_ int sd_journal_add_conjunction(sd_journal *j) {
313 assert_return(j, -EINVAL);
314 assert_return(!journal_pid_changed(j), -ECHILD);
322 if (!j->level1->matches)
331 _public_ int sd_journal_add_disjunction(sd_journal *j) {
332 assert_return(j, -EINVAL);
333 assert_return(!journal_pid_changed(j), -ECHILD);
344 if (!j->level2->matches)
351 static char *match_make_string(Match *m) {
354 bool enclose = false;
357 return strdup("none");
359 if (m->type == MATCH_DISCRETE)
360 return strndup(m->data, m->size);
363 LIST_FOREACH(matches, i, m->matches) {
366 t = match_make_string(i);
373 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
388 r = strjoin("(", p, ")", NULL);
396 char *journal_make_match_string(sd_journal *j) {
399 return match_make_string(j->level0);
402 _public_ void sd_journal_flush_matches(sd_journal *j) {
407 match_free(j->level0);
409 j->level0 = j->level1 = j->level2 = NULL;
414 static int compare_entry_order(JournalFile *af, Object *_ao,
415 JournalFile *bf, uint64_t bp) {
425 /* The mmap cache might invalidate the object from the first
426 * file if we look at the one from the second file. Hence
427 * temporarily copy the header of the first one, and look at
429 ao = alloca(offsetof(EntryObject, items));
430 memcpy(ao, _ao, offsetof(EntryObject, items));
432 r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
434 return strcmp(af->path, bf->path);
436 /* We operate on two different files here, hence we can access
437 * two objects at the same time, which we normally can't.
439 * If contents and timestamps match, these entries are
440 * identical, even if the seqnum does not match */
442 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
443 ao->entry.monotonic == bo->entry.monotonic &&
444 ao->entry.realtime == bo->entry.realtime &&
445 ao->entry.xor_hash == bo->entry.xor_hash)
448 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
450 /* If this is from the same seqnum source, compare
452 a = le64toh(ao->entry.seqnum);
453 b = le64toh(bo->entry.seqnum);
460 /* Wow! This is weird, different data but the same
461 * seqnums? Something is borked, but let's make the
462 * best of it and compare by time. */
465 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
467 /* If the boot id matches compare monotonic time */
468 a = le64toh(ao->entry.monotonic);
469 b = le64toh(bo->entry.monotonic);
477 /* Otherwise compare UTC time */
478 a = le64toh(ao->entry.realtime);
479 b = le64toh(bo->entry.realtime);
486 /* Finally, compare by contents */
487 a = le64toh(ao->entry.xor_hash);
488 b = le64toh(bo->entry.xor_hash);
498 _pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
504 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
506 if (l->monotonic_set &&
507 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
509 le64toh(ao->entry.realtime) == l->realtime &&
511 le64toh(ao->entry.xor_hash) == l->xor_hash)
515 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
517 a = le64toh(ao->entry.seqnum);
525 if (l->monotonic_set &&
526 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
528 a = le64toh(ao->entry.monotonic);
530 if (a < l->monotonic)
532 if (a > l->monotonic)
536 if (l->realtime_set) {
538 a = le64toh(ao->entry.realtime);
546 if (l->xor_hash_set) {
547 a = le64toh(ao->entry.xor_hash);
558 static int next_for_match(
562 uint64_t after_offset,
563 direction_t direction,
575 if (m->type == MATCH_DISCRETE) {
578 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
582 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
584 } else if (m->type == MATCH_OR_TERM) {
587 /* Find the earliest match beyond after_offset */
589 LIST_FOREACH(matches, i, m->matches) {
592 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
596 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
604 } else if (m->type == MATCH_AND_TERM) {
605 Match *i, *last_moved;
607 /* Always jump to the next matching entry and repeat
608 * this until we find an offset that matches for all
614 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
618 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
619 last_moved = m->matches;
621 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
624 r = next_for_match(j, i, f, np, direction, NULL, &cp);
628 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
629 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
638 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
650 static int find_location_for_match(
654 direction_t direction,
664 if (m->type == MATCH_DISCRETE) {
667 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
671 /* FIXME: missing: find by monotonic */
673 if (j->current_location.type == LOCATION_HEAD)
674 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
675 if (j->current_location.type == LOCATION_TAIL)
676 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
677 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
678 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
679 if (j->current_location.monotonic_set) {
680 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
684 if (j->current_location.realtime_set)
685 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
687 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
689 } else if (m->type == MATCH_OR_TERM) {
694 /* Find the earliest match */
696 LIST_FOREACH(matches, i, m->matches) {
699 r = find_location_for_match(j, i, f, direction, NULL, &cp);
703 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
711 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
726 assert(m->type == MATCH_AND_TERM);
728 /* First jump to the last match, and then find the
729 * next one where all matches match */
734 LIST_FOREACH(matches, i, m->matches) {
737 r = find_location_for_match(j, i, f, direction, NULL, &cp);
741 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
745 return next_for_match(j, m, f, np, direction, ret, offset);
749 static int find_location_with_matches(
752 direction_t direction,
764 /* No matches is simple */
766 if (j->current_location.type == LOCATION_HEAD)
767 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
768 if (j->current_location.type == LOCATION_TAIL)
769 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
770 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
771 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
772 if (j->current_location.monotonic_set) {
773 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
777 if (j->current_location.realtime_set)
778 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
780 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
782 return find_location_for_match(j, j->level0, f, direction, ret, offset);
785 static int next_with_matches(
788 direction_t direction,
803 /* No matches is easy. We simple advance the file
806 return journal_file_next_entry(f, c, cp, direction, ret, offset);
808 /* If we have a match then we look for the next matching entry
809 * with an offset at least one step larger */
810 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
813 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
821 if (f->last_direction == direction && f->current_offset > 0) {
822 cp = f->current_offset;
824 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
828 r = next_with_matches(j, f, direction, &c, &cp);
832 r = find_location_with_matches(j, f, direction, &c, &cp);
837 /* OK, we found the spot, now let's advance until an entry
838 * that is actually different from what we were previously
839 * looking at. This is necessary to handle entries which exist
840 * in two (or more) journal files, and which shall all be
841 * suppressed but one. */
846 if (j->current_location.type == LOCATION_DISCRETE) {
849 k = compare_with_location(f, c, &j->current_location);
850 if (direction == DIRECTION_DOWN)
865 r = next_with_matches(j, f, direction, &c, &cp);
871 static int real_journal_next(sd_journal *j, direction_t direction) {
872 JournalFile *f, *new_file = NULL;
873 uint64_t new_offset = 0;
879 assert_return(j, -EINVAL);
880 assert_return(!journal_pid_changed(j), -ECHILD);
882 HASHMAP_FOREACH(f, j->files, i) {
885 r = next_beyond_location(j, f, direction, &o, &p);
887 log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
897 k = compare_entry_order(f, o, new_file, new_offset);
899 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
911 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
915 set_location(j, LOCATION_DISCRETE, new_file, o, direction, new_offset);
920 _public_ int sd_journal_next(sd_journal *j) {
921 return real_journal_next(j, DIRECTION_DOWN);
924 _public_ int sd_journal_previous(sd_journal *j) {
925 return real_journal_next(j, DIRECTION_UP);
928 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
931 assert_return(j, -EINVAL);
932 assert_return(!journal_pid_changed(j), -ECHILD);
935 /* If this is not a discrete skip, then at least
936 * resolve the current location */
937 if (j->current_location.type != LOCATION_DISCRETE)
938 return real_journal_next(j, direction);
944 r = real_journal_next(j, direction);
958 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
959 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
962 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
963 return real_journal_next_skip(j, DIRECTION_UP, skip);
966 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
969 char bid[33], sid[33];
971 assert_return(j, -EINVAL);
972 assert_return(!journal_pid_changed(j), -ECHILD);
973 assert_return(cursor, -EINVAL);
975 if (!j->current_file || j->current_file->current_offset <= 0)
976 return -EADDRNOTAVAIL;
978 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
982 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
983 sd_id128_to_string(o->entry.boot_id, bid);
986 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
987 sid, le64toh(o->entry.seqnum),
988 bid, le64toh(o->entry.monotonic),
989 le64toh(o->entry.realtime),
990 le64toh(o->entry.xor_hash)) < 0)
996 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
999 unsigned long long seqnum, monotonic, realtime, xor_hash;
1001 seqnum_id_set = false,
1003 boot_id_set = false,
1004 monotonic_set = false,
1005 realtime_set = false,
1006 xor_hash_set = false;
1007 sd_id128_t seqnum_id, boot_id;
1009 assert_return(j, -EINVAL);
1010 assert_return(!journal_pid_changed(j), -ECHILD);
1011 assert_return(!isempty(cursor), -EINVAL);
1013 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
1017 if (l < 2 || w[1] != '=')
1020 item = strndup(w, l);
1027 seqnum_id_set = true;
1028 k = sd_id128_from_string(item+2, &seqnum_id);
1033 if (sscanf(item+2, "%llx", &seqnum) != 1)
1039 k = sd_id128_from_string(item+2, &boot_id);
1043 monotonic_set = true;
1044 if (sscanf(item+2, "%llx", &monotonic) != 1)
1049 realtime_set = true;
1050 if (sscanf(item+2, "%llx", &realtime) != 1)
1055 xor_hash_set = true;
1056 if (sscanf(item+2, "%llx", &xor_hash) != 1)
1067 if ((!seqnum_set || !seqnum_id_set) &&
1068 (!monotonic_set || !boot_id_set) &&
1074 j->current_location.type = LOCATION_SEEK;
1077 j->current_location.realtime = (uint64_t) realtime;
1078 j->current_location.realtime_set = true;
1081 if (seqnum_set && seqnum_id_set) {
1082 j->current_location.seqnum = (uint64_t) seqnum;
1083 j->current_location.seqnum_id = seqnum_id;
1084 j->current_location.seqnum_set = true;
1087 if (monotonic_set && boot_id_set) {
1088 j->current_location.monotonic = (uint64_t) monotonic;
1089 j->current_location.boot_id = boot_id;
1090 j->current_location.monotonic_set = true;
1094 j->current_location.xor_hash = (uint64_t) xor_hash;
1095 j->current_location.xor_hash_set = true;
1101 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1107 assert_return(j, -EINVAL);
1108 assert_return(!journal_pid_changed(j), -ECHILD);
1109 assert_return(!isempty(cursor), -EINVAL);
1111 if (!j->current_file || j->current_file->current_offset <= 0)
1112 return -EADDRNOTAVAIL;
1114 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1118 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
1119 _cleanup_free_ char *item = NULL;
1121 unsigned long long ll;
1124 if (l < 2 || w[1] != '=')
1127 item = strndup(w, l);
1134 k = sd_id128_from_string(item+2, &id);
1137 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1142 if (sscanf(item+2, "%llx", &ll) != 1)
1144 if (ll != le64toh(o->entry.seqnum))
1149 k = sd_id128_from_string(item+2, &id);
1152 if (!sd_id128_equal(id, o->entry.boot_id))
1157 if (sscanf(item+2, "%llx", &ll) != 1)
1159 if (ll != le64toh(o->entry.monotonic))
1164 if (sscanf(item+2, "%llx", &ll) != 1)
1166 if (ll != le64toh(o->entry.realtime))
1171 if (sscanf(item+2, "%llx", &ll) != 1)
1173 if (ll != le64toh(o->entry.xor_hash))
1183 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1184 assert_return(j, -EINVAL);
1185 assert_return(!journal_pid_changed(j), -ECHILD);
1188 j->current_location.type = LOCATION_SEEK;
1189 j->current_location.boot_id = boot_id;
1190 j->current_location.monotonic = usec;
1191 j->current_location.monotonic_set = true;
1196 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1197 assert_return(j, -EINVAL);
1198 assert_return(!journal_pid_changed(j), -ECHILD);
1201 j->current_location.type = LOCATION_SEEK;
1202 j->current_location.realtime = usec;
1203 j->current_location.realtime_set = true;
1208 _public_ int sd_journal_seek_head(sd_journal *j) {
1209 assert_return(j, -EINVAL);
1210 assert_return(!journal_pid_changed(j), -ECHILD);
1213 j->current_location.type = LOCATION_HEAD;
1218 _public_ int sd_journal_seek_tail(sd_journal *j) {
1219 assert_return(j, -EINVAL);
1220 assert_return(!journal_pid_changed(j), -ECHILD);
1223 j->current_location.type = LOCATION_TAIL;
1228 static void check_network(sd_journal *j, int fd) {
1236 if (fstatfs(fd, &sfs) < 0)
1240 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1241 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1242 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1243 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1244 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1247 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1248 const char *full, *tilded, *atted;
1250 full = strappenda(prefix, ".journal");
1251 tilded = strappenda(full, "~");
1252 atted = strappenda(prefix, "@");
1254 return streq(filename, full) ||
1255 streq(filename, tilded) ||
1256 startswith(filename, atted);
1259 static bool file_type_wanted(int flags, const char *filename) {
1260 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1263 /* no flags set → every type is OK */
1264 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1267 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1270 if (flags & SD_JOURNAL_CURRENT_USER) {
1271 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1273 assert_se(snprintf(prefix, sizeof(prefix), "user-%lu", (unsigned long) getuid())
1274 < (int) sizeof(prefix));
1276 if (file_has_type_prefix(prefix, filename))
1283 static int add_any_file(sd_journal *j, const char *path) {
1290 if (hashmap_get(j->files, path))
1293 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1294 log_warning("Too many open journal files, not adding %s.", path);
1295 return set_put_error(j, -ETOOMANYREFS);
1298 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1302 /* journal_file_dump(f); */
1304 r = hashmap_put(j->files, f->path, f);
1306 journal_file_close(f);
1310 log_debug("File %s added.", f->path);
1312 check_network(j, f->fd);
1314 j->current_invalidate_counter ++;
1319 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1320 _cleanup_free_ char *path = NULL;
1327 if (j->no_new_files ||
1328 !file_type_wanted(j->flags, filename))
1331 path = strjoin(prefix, "/", filename, NULL);
1335 r = add_any_file(j, path);
1341 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1349 path = strjoin(prefix, "/", filename, NULL);
1353 f = hashmap_get(j->files, path);
1358 hashmap_remove(j->files, f->path);
1360 log_debug("File %s removed.", f->path);
1362 if (j->current_file == f) {
1363 j->current_file = NULL;
1364 j->current_field = 0;
1367 if (j->unique_file == f) {
1368 j->unique_file = NULL;
1369 j->unique_offset = 0;
1372 journal_file_close(f);
1374 j->current_invalidate_counter ++;
1379 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1380 _cleanup_free_ char *path = NULL;
1382 _cleanup_closedir_ DIR *d = NULL;
1390 log_debug("Considering %s/%s.", prefix, dirname);
1392 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1393 (sd_id128_from_string(dirname, &id) < 0 ||
1394 sd_id128_get_machine(&mid) < 0 ||
1395 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1398 path = strjoin(prefix, "/", dirname, NULL);
1404 log_debug("Failed to open %s: %m", path);
1405 if (errno == ENOENT)
1410 m = hashmap_get(j->directories_by_path, path);
1412 m = new0(Directory, 1);
1419 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1424 path = NULL; /* avoid freeing in cleanup */
1425 j->current_invalidate_counter ++;
1427 log_debug("Directory %s added.", m->path);
1429 } else if (m->is_root)
1432 if (m->wd <= 0 && j->inotify_fd >= 0) {
1434 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1435 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1436 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1439 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1440 inotify_rm_watch(j->inotify_fd, m->wd);
1445 union dirent_storage buf;
1447 r = readdir_r(d, &buf.de, &de);
1451 if (dirent_is_file_with_suffix(de, ".journal") ||
1452 dirent_is_file_with_suffix(de, ".journal~")) {
1453 r = add_file(j, m->path, de->d_name);
1455 log_debug("Failed to add file %s/%s: %s",
1456 m->path, de->d_name, strerror(-r));
1457 r = set_put_error(j, r);
1464 check_network(j, dirfd(d));
1469 static int add_root_directory(sd_journal *j, const char *p, const char *prefix) {
1470 _cleanup_closedir_ DIR *d = NULL;
1477 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1478 !path_startswith(p, "/run"))
1482 p = strappenda(prefix, p);
1488 m = hashmap_get(j->directories_by_path, p);
1490 m = new0(Directory, 1);
1495 m->path = strdup(p);
1501 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1507 j->current_invalidate_counter ++;
1509 log_debug("Root directory %s added.", m->path);
1511 } else if (!m->is_root)
1514 if (m->wd <= 0 && j->inotify_fd >= 0) {
1516 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1517 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1520 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1521 inotify_rm_watch(j->inotify_fd, m->wd);
1524 if (j->no_new_files)
1529 union dirent_storage buf;
1532 r = readdir_r(d, &buf.de, &de);
1536 if (dirent_is_file_with_suffix(de, ".journal") ||
1537 dirent_is_file_with_suffix(de, ".journal~")) {
1538 r = add_file(j, m->path, de->d_name);
1540 log_debug("Failed to add file %s/%s: %s",
1541 m->path, de->d_name, strerror(-r));
1542 r = set_put_error(j, r);
1546 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1547 sd_id128_from_string(de->d_name, &id) >= 0) {
1549 r = add_directory(j, m->path, de->d_name);
1551 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1555 check_network(j, dirfd(d));
1560 static int remove_directory(sd_journal *j, Directory *d) {
1564 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1566 if (j->inotify_fd >= 0)
1567 inotify_rm_watch(j->inotify_fd, d->wd);
1570 hashmap_remove(j->directories_by_path, d->path);
1573 log_debug("Root directory %s removed.", d->path);
1575 log_debug("Directory %s removed.", d->path);
1583 static int add_search_paths(sd_journal *j, const char *prefix) {
1585 const char search_paths[] =
1586 "/run/log/journal\0"
1587 "/var/log/journal\0";
1592 /* We ignore most errors here, since the idea is to only open
1593 * what's actually accessible, and ignore the rest. */
1595 NULSTR_FOREACH(p, search_paths) {
1596 r = add_root_directory(j, p, prefix);
1597 if (r < 0 && r != -ENOENT) {
1598 r = set_put_error(j, r);
1607 static int add_current_paths(sd_journal *j) {
1612 assert(j->no_new_files);
1614 /* Simply adds all directories for files we have open as
1615 * "root" directories. We don't expect errors here, so we
1616 * treat them as fatal. */
1618 HASHMAP_FOREACH(f, j->files, i) {
1620 _cleanup_free_ char *dir;
1622 dir = dirname_malloc(f->path);
1626 r = add_root_directory(j, dir, NULL);
1628 set_put_error(j, r);
1637 static int allocate_inotify(sd_journal *j) {
1640 if (j->inotify_fd < 0) {
1641 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1642 if (j->inotify_fd < 0)
1646 if (!j->directories_by_wd) {
1647 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1648 if (!j->directories_by_wd)
1655 static sd_journal *journal_new(int flags, const char *path) {
1658 j = new0(sd_journal, 1);
1662 j->original_pid = getpid();
1665 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1668 j->path = strdup(path);
1673 j->files = hashmap_new(string_hash_func, string_compare_func);
1674 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1675 j->mmap = mmap_cache_new();
1676 if (!j->files || !j->directories_by_path || !j->mmap)
1682 sd_journal_close(j);
1686 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1690 assert_return(ret, -EINVAL);
1691 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1693 j = journal_new(flags, NULL);
1697 r = add_search_paths(j, NULL);
1705 sd_journal_close(j);
1710 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1711 _cleanup_free_ char *root = NULL, *class = NULL;
1716 assert_return(machine, -EINVAL);
1717 assert_return(ret, -EINVAL);
1718 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1719 assert_return(filename_is_safe(machine), -EINVAL);
1721 p = strappenda("/run/systemd/machines/", machine);
1722 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1730 if (!streq_ptr(class, "container"))
1733 j = journal_new(flags, NULL);
1737 r = add_search_paths(j, root);
1745 sd_journal_close(j);
1749 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1753 assert_return(ret, -EINVAL);
1754 assert_return(path, -EINVAL);
1755 assert_return(flags == 0, -EINVAL);
1757 j = journal_new(flags, path);
1761 r = add_root_directory(j, path, NULL);
1763 set_put_error(j, r);
1771 sd_journal_close(j);
1776 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1781 assert_return(ret, -EINVAL);
1782 assert_return(flags == 0, -EINVAL);
1784 j = journal_new(flags, NULL);
1788 STRV_FOREACH(path, paths) {
1789 r = add_any_file(j, *path);
1791 log_error("Failed to open %s: %s", *path, strerror(-r));
1796 j->no_new_files = true;
1802 sd_journal_close(j);
1807 _public_ void sd_journal_close(sd_journal *j) {
1814 sd_journal_flush_matches(j);
1816 while ((f = hashmap_steal_first(j->files)))
1817 journal_file_close(f);
1819 hashmap_free(j->files);
1821 while ((d = hashmap_first(j->directories_by_path)))
1822 remove_directory(j, d);
1824 while ((d = hashmap_first(j->directories_by_wd)))
1825 remove_directory(j, d);
1827 hashmap_free(j->directories_by_path);
1828 hashmap_free(j->directories_by_wd);
1830 if (j->inotify_fd >= 0)
1831 close_nointr_nofail(j->inotify_fd);
1834 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1835 mmap_cache_unref(j->mmap);
1839 free(j->unique_field);
1840 set_free(j->errors);
1844 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1849 assert_return(j, -EINVAL);
1850 assert_return(!journal_pid_changed(j), -ECHILD);
1851 assert_return(ret, -EINVAL);
1853 f = j->current_file;
1855 return -EADDRNOTAVAIL;
1857 if (f->current_offset <= 0)
1858 return -EADDRNOTAVAIL;
1860 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1864 *ret = le64toh(o->entry.realtime);
1868 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1874 assert_return(j, -EINVAL);
1875 assert_return(!journal_pid_changed(j), -ECHILD);
1877 f = j->current_file;
1879 return -EADDRNOTAVAIL;
1881 if (f->current_offset <= 0)
1882 return -EADDRNOTAVAIL;
1884 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1889 *ret_boot_id = o->entry.boot_id;
1891 r = sd_id128_get_boot(&id);
1895 if (!sd_id128_equal(id, o->entry.boot_id))
1900 *ret = le64toh(o->entry.monotonic);
1905 static bool field_is_valid(const char *field) {
1913 if (startswith(field, "__"))
1916 for (p = field; *p; p++) {
1921 if (*p >= 'A' && *p <= 'Z')
1924 if (*p >= '0' && *p <= '9')
1933 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1936 size_t field_length;
1940 assert_return(j, -EINVAL);
1941 assert_return(!journal_pid_changed(j), -ECHILD);
1942 assert_return(field, -EINVAL);
1943 assert_return(data, -EINVAL);
1944 assert_return(size, -EINVAL);
1945 assert_return(field_is_valid(field), -EINVAL);
1947 f = j->current_file;
1949 return -EADDRNOTAVAIL;
1951 if (f->current_offset <= 0)
1952 return -EADDRNOTAVAIL;
1954 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1958 field_length = strlen(field);
1960 n = journal_file_entry_n_items(o);
1961 for (i = 0; i < n; i++) {
1966 p = le64toh(o->entry.items[i].object_offset);
1967 le_hash = o->entry.items[i].hash;
1968 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1972 if (le_hash != o->data.hash)
1975 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1977 if (o->object.flags & OBJECT_COMPRESSED) {
1980 if (uncompress_startswith(o->data.payload, l,
1981 &f->compress_buffer, &f->compress_buffer_size,
1982 field, field_length, '=')) {
1986 if (!uncompress_blob(o->data.payload, l,
1987 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1991 *data = f->compress_buffer;
1992 *size = (size_t) rsize;
1997 return -EPROTONOSUPPORT;
2000 } else if (l >= field_length+1 &&
2001 memcmp(o->data.payload, field, field_length) == 0 &&
2002 o->data.payload[field_length] == '=') {
2006 if ((uint64_t) t != l)
2009 *data = o->data.payload;
2015 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2023 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
2027 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2030 /* We can't read objects larger than 4G on a 32bit machine */
2031 if ((uint64_t) t != l)
2034 if (o->object.flags & OBJECT_COMPRESSED) {
2038 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, j->data_threshold))
2041 *data = f->compress_buffer;
2042 *size = (size_t) rsize;
2044 return -EPROTONOSUPPORT;
2047 *data = o->data.payload;
2054 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2061 assert_return(j, -EINVAL);
2062 assert_return(!journal_pid_changed(j), -ECHILD);
2063 assert_return(data, -EINVAL);
2064 assert_return(size, -EINVAL);
2066 f = j->current_file;
2068 return -EADDRNOTAVAIL;
2070 if (f->current_offset <= 0)
2071 return -EADDRNOTAVAIL;
2073 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2077 n = journal_file_entry_n_items(o);
2078 if (j->current_field >= n)
2081 p = le64toh(o->entry.items[j->current_field].object_offset);
2082 le_hash = o->entry.items[j->current_field].hash;
2083 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2087 if (le_hash != o->data.hash)
2090 r = return_data(j, f, o, data, size);
2094 j->current_field ++;
2099 _public_ void sd_journal_restart_data(sd_journal *j) {
2103 j->current_field = 0;
2106 _public_ int sd_journal_get_fd(sd_journal *j) {
2109 assert_return(j, -EINVAL);
2110 assert_return(!journal_pid_changed(j), -ECHILD);
2112 if (j->inotify_fd >= 0)
2113 return j->inotify_fd;
2115 r = allocate_inotify(j);
2119 /* Iterate through all dirs again, to add them to the
2121 if (j->no_new_files)
2122 r = add_current_paths(j);
2124 r = add_root_directory(j, j->path, NULL);
2126 r = add_search_paths(j, NULL);
2130 return j->inotify_fd;
2133 _public_ int sd_journal_get_events(sd_journal *j) {
2136 assert_return(j, -EINVAL);
2137 assert_return(!journal_pid_changed(j), -ECHILD);
2139 fd = sd_journal_get_fd(j);
2146 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2149 assert_return(j, -EINVAL);
2150 assert_return(!journal_pid_changed(j), -ECHILD);
2151 assert_return(timeout_usec, -EINVAL);
2153 fd = sd_journal_get_fd(j);
2157 if (!j->on_network) {
2158 *timeout_usec = (uint64_t) -1;
2162 /* If we are on the network we need to regularly check for
2163 * changes manually */
2165 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2169 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2176 /* Is this a subdirectory we watch? */
2177 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2181 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2182 (endswith(e->name, ".journal") ||
2183 endswith(e->name, ".journal~"))) {
2185 /* Event for a journal file */
2187 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2188 r = add_file(j, d->path, e->name);
2190 log_debug("Failed to add file %s/%s: %s",
2191 d->path, e->name, strerror(-r));
2192 set_put_error(j, r);
2195 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2197 r = remove_file(j, d->path, e->name);
2199 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
2202 } else if (!d->is_root && e->len == 0) {
2204 /* Event for a subdirectory */
2206 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2207 r = remove_directory(j, d);
2209 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
2213 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2215 /* Event for root directory */
2217 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2218 r = add_directory(j, d->path, e->name);
2220 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
2227 if (e->mask & IN_IGNORED)
2230 log_warning("Unknown inotify event.");
2233 static int determine_change(sd_journal *j) {
2238 b = j->current_invalidate_counter != j->last_invalidate_counter;
2239 j->last_invalidate_counter = j->current_invalidate_counter;
2241 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2244 _public_ int sd_journal_process(sd_journal *j) {
2245 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
2246 bool got_something = false;
2248 assert_return(j, -EINVAL);
2249 assert_return(!journal_pid_changed(j), -ECHILD);
2251 j->last_process_usec = now(CLOCK_MONOTONIC);
2254 struct inotify_event *e;
2257 l = read(j->inotify_fd, buffer, sizeof(buffer));
2259 if (errno == EAGAIN || errno == EINTR)
2260 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2265 got_something = true;
2267 e = (struct inotify_event*) buffer;
2271 process_inotify_event(j, e);
2273 step = sizeof(struct inotify_event) + e->len;
2274 assert(step <= (size_t) l);
2276 e = (struct inotify_event*) ((uint8_t*) e + step);
2281 return determine_change(j);
2284 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2288 assert_return(j, -EINVAL);
2289 assert_return(!journal_pid_changed(j), -ECHILD);
2291 if (j->inotify_fd < 0) {
2293 /* This is the first invocation, hence create the
2295 r = sd_journal_get_fd(j);
2299 /* The journal might have changed since the context
2300 * object was created and we weren't watching before,
2301 * hence don't wait for anything, and return
2303 return determine_change(j);
2306 r = sd_journal_get_timeout(j, &t);
2310 if (t != (uint64_t) -1) {
2313 n = now(CLOCK_MONOTONIC);
2314 t = t > n ? t - n : 0;
2316 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2321 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2322 } while (r == -EINTR);
2327 return sd_journal_process(j);
2330 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2336 assert_return(j, -EINVAL);
2337 assert_return(!journal_pid_changed(j), -ECHILD);
2338 assert_return(from || to, -EINVAL);
2339 assert_return(from != to, -EINVAL);
2341 HASHMAP_FOREACH(f, j->files, i) {
2344 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2360 *from = MIN(fr, *from);
2366 return first ? 0 : 1;
2369 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2375 assert_return(j, -EINVAL);
2376 assert_return(!journal_pid_changed(j), -ECHILD);
2377 assert_return(from || to, -EINVAL);
2378 assert_return(from != to, -EINVAL);
2380 HASHMAP_FOREACH(f, j->files, i) {
2383 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2399 *from = MIN(fr, *from);
2405 return first ? 0 : 1;
2408 void journal_print_header(sd_journal *j) {
2411 bool newline = false;
2415 HASHMAP_FOREACH(f, j->files, i) {
2421 journal_file_print_header(f);
2425 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2430 assert_return(j, -EINVAL);
2431 assert_return(!journal_pid_changed(j), -ECHILD);
2432 assert_return(bytes, -EINVAL);
2434 HASHMAP_FOREACH(f, j->files, i) {
2437 if (fstat(f->fd, &st) < 0)
2440 sum += (uint64_t) st.st_blocks * 512ULL;
2447 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2450 assert_return(j, -EINVAL);
2451 assert_return(!journal_pid_changed(j), -ECHILD);
2452 assert_return(!isempty(field), -EINVAL);
2453 assert_return(field_is_valid(field), -EINVAL);
2459 free(j->unique_field);
2460 j->unique_field = f;
2461 j->unique_file = NULL;
2462 j->unique_offset = 0;
2467 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2472 assert_return(j, -EINVAL);
2473 assert_return(!journal_pid_changed(j), -ECHILD);
2474 assert_return(data, -EINVAL);
2475 assert_return(l, -EINVAL);
2476 assert_return(j->unique_field, -EINVAL);
2478 k = strlen(j->unique_field);
2480 if (!j->unique_file) {
2481 j->unique_file = hashmap_first(j->files);
2482 if (!j->unique_file)
2484 j->unique_offset = 0;
2494 /* Proceed to next data object in the field's linked list */
2495 if (j->unique_offset == 0) {
2496 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2500 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2502 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2506 j->unique_offset = le64toh(o->data.next_field_offset);
2509 /* We reached the end of the list? Then start again, with the next file */
2510 if (j->unique_offset == 0) {
2513 n = hashmap_next(j->files, j->unique_file->path);
2521 /* We do not use the type context here, but 0 instead,
2522 * so that we can look at this data object at the same
2523 * time as one on another file */
2524 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2528 /* Let's do the type check by hand, since we used 0 context above. */
2529 if (o->object.type != OBJECT_DATA)
2532 r = return_data(j, j->unique_file, o, &odata, &ol);
2536 /* OK, now let's see if we already returned this data
2537 * object by checking if it exists in the earlier
2538 * traversed files. */
2540 HASHMAP_FOREACH(of, j->files, i) {
2544 if (of == j->unique_file)
2547 /* Skip this file it didn't have any fields
2549 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2550 le64toh(of->header->n_fields) <= 0)
2553 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2564 r = return_data(j, j->unique_file, o, data, l);
2572 _public_ void sd_journal_restart_unique(sd_journal *j) {
2576 j->unique_file = NULL;
2577 j->unique_offset = 0;
2580 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2581 assert_return(j, -EINVAL);
2582 assert_return(!journal_pid_changed(j), -ECHILD);
2584 return !j->on_network;
2587 static char *lookup_field(const char *field, void *userdata) {
2588 sd_journal *j = userdata;
2596 r = sd_journal_get_data(j, field, &data, &size);
2598 size > REPLACE_VAR_MAX)
2599 return strdup(field);
2601 d = strlen(field) + 1;
2603 return strndup((const char*) data + d, size - d);
2606 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2610 _cleanup_free_ char *text = NULL, *cid = NULL;
2614 assert_return(j, -EINVAL);
2615 assert_return(!journal_pid_changed(j), -ECHILD);
2616 assert_return(ret, -EINVAL);
2618 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2622 cid = strndup((const char*) data + 11, size - 11);
2626 r = sd_id128_from_string(cid, &id);
2630 r = catalog_get(CATALOG_DATABASE, id, &text);
2634 t = replace_var(text, lookup_field, j);
2642 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2643 assert_return(ret, -EINVAL);
2645 return catalog_get(CATALOG_DATABASE, id, ret);
2648 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2649 assert_return(j, -EINVAL);
2650 assert_return(!journal_pid_changed(j), -ECHILD);
2652 j->data_threshold = sz;
2656 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2657 assert_return(j, -EINVAL);
2658 assert_return(!journal_pid_changed(j), -ECHILD);
2659 assert_return(sz, -EINVAL);
2661 *sz = j->data_threshold;