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) {
1470 _cleanup_closedir_ DIR *d = NULL;
1477 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1478 !path_startswith(p, "/run"))
1482 p = strappenda(j->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) {
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);
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) {
1619 _cleanup_free_ char *dir;
1622 dir = dirname_malloc(f->path);
1626 r = add_root_directory(j, dir);
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);
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);
1740 r = add_search_paths(j);
1748 sd_journal_close(j);
1752 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1756 assert_return(ret, -EINVAL);
1757 assert_return(path, -EINVAL);
1758 assert_return(flags == 0, -EINVAL);
1760 j = journal_new(flags, path);
1764 r = add_root_directory(j, path);
1766 set_put_error(j, r);
1774 sd_journal_close(j);
1779 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1784 assert_return(ret, -EINVAL);
1785 assert_return(flags == 0, -EINVAL);
1787 j = journal_new(flags, NULL);
1791 STRV_FOREACH(path, paths) {
1792 r = add_any_file(j, *path);
1794 log_error("Failed to open %s: %s", *path, strerror(-r));
1799 j->no_new_files = true;
1805 sd_journal_close(j);
1810 _public_ void sd_journal_close(sd_journal *j) {
1817 sd_journal_flush_matches(j);
1819 while ((f = hashmap_steal_first(j->files)))
1820 journal_file_close(f);
1822 hashmap_free(j->files);
1824 while ((d = hashmap_first(j->directories_by_path)))
1825 remove_directory(j, d);
1827 while ((d = hashmap_first(j->directories_by_wd)))
1828 remove_directory(j, d);
1830 hashmap_free(j->directories_by_path);
1831 hashmap_free(j->directories_by_wd);
1833 if (j->inotify_fd >= 0)
1834 close_nointr_nofail(j->inotify_fd);
1837 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1838 mmap_cache_unref(j->mmap);
1843 free(j->unique_field);
1844 set_free(j->errors);
1848 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1853 assert_return(j, -EINVAL);
1854 assert_return(!journal_pid_changed(j), -ECHILD);
1855 assert_return(ret, -EINVAL);
1857 f = j->current_file;
1859 return -EADDRNOTAVAIL;
1861 if (f->current_offset <= 0)
1862 return -EADDRNOTAVAIL;
1864 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1868 *ret = le64toh(o->entry.realtime);
1872 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1878 assert_return(j, -EINVAL);
1879 assert_return(!journal_pid_changed(j), -ECHILD);
1881 f = j->current_file;
1883 return -EADDRNOTAVAIL;
1885 if (f->current_offset <= 0)
1886 return -EADDRNOTAVAIL;
1888 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1893 *ret_boot_id = o->entry.boot_id;
1895 r = sd_id128_get_boot(&id);
1899 if (!sd_id128_equal(id, o->entry.boot_id))
1904 *ret = le64toh(o->entry.monotonic);
1909 static bool field_is_valid(const char *field) {
1917 if (startswith(field, "__"))
1920 for (p = field; *p; p++) {
1925 if (*p >= 'A' && *p <= 'Z')
1928 if (*p >= '0' && *p <= '9')
1937 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1940 size_t field_length;
1944 assert_return(j, -EINVAL);
1945 assert_return(!journal_pid_changed(j), -ECHILD);
1946 assert_return(field, -EINVAL);
1947 assert_return(data, -EINVAL);
1948 assert_return(size, -EINVAL);
1949 assert_return(field_is_valid(field), -EINVAL);
1951 f = j->current_file;
1953 return -EADDRNOTAVAIL;
1955 if (f->current_offset <= 0)
1956 return -EADDRNOTAVAIL;
1958 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1962 field_length = strlen(field);
1964 n = journal_file_entry_n_items(o);
1965 for (i = 0; i < n; i++) {
1970 p = le64toh(o->entry.items[i].object_offset);
1971 le_hash = o->entry.items[i].hash;
1972 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1976 if (le_hash != o->data.hash)
1979 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1981 if (o->object.flags & OBJECT_COMPRESSED) {
1984 if (uncompress_startswith(o->data.payload, l,
1985 &f->compress_buffer, &f->compress_buffer_size,
1986 field, field_length, '=')) {
1990 if (!uncompress_blob(o->data.payload, l,
1991 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1995 *data = f->compress_buffer;
1996 *size = (size_t) rsize;
2001 return -EPROTONOSUPPORT;
2004 } else if (l >= field_length+1 &&
2005 memcmp(o->data.payload, field, field_length) == 0 &&
2006 o->data.payload[field_length] == '=') {
2010 if ((uint64_t) t != l)
2013 *data = o->data.payload;
2019 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2027 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
2031 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2034 /* We can't read objects larger than 4G on a 32bit machine */
2035 if ((uint64_t) t != l)
2038 if (o->object.flags & OBJECT_COMPRESSED) {
2042 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, j->data_threshold))
2045 *data = f->compress_buffer;
2046 *size = (size_t) rsize;
2048 return -EPROTONOSUPPORT;
2051 *data = o->data.payload;
2058 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2065 assert_return(j, -EINVAL);
2066 assert_return(!journal_pid_changed(j), -ECHILD);
2067 assert_return(data, -EINVAL);
2068 assert_return(size, -EINVAL);
2070 f = j->current_file;
2072 return -EADDRNOTAVAIL;
2074 if (f->current_offset <= 0)
2075 return -EADDRNOTAVAIL;
2077 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2081 n = journal_file_entry_n_items(o);
2082 if (j->current_field >= n)
2085 p = le64toh(o->entry.items[j->current_field].object_offset);
2086 le_hash = o->entry.items[j->current_field].hash;
2087 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2091 if (le_hash != o->data.hash)
2094 r = return_data(j, f, o, data, size);
2098 j->current_field ++;
2103 _public_ void sd_journal_restart_data(sd_journal *j) {
2107 j->current_field = 0;
2110 _public_ int sd_journal_get_fd(sd_journal *j) {
2113 assert_return(j, -EINVAL);
2114 assert_return(!journal_pid_changed(j), -ECHILD);
2116 if (j->inotify_fd >= 0)
2117 return j->inotify_fd;
2119 r = allocate_inotify(j);
2123 /* Iterate through all dirs again, to add them to the
2125 if (j->no_new_files)
2126 r = add_current_paths(j);
2128 r = add_root_directory(j, j->path);
2130 r = add_search_paths(j);
2134 return j->inotify_fd;
2137 _public_ int sd_journal_get_events(sd_journal *j) {
2140 assert_return(j, -EINVAL);
2141 assert_return(!journal_pid_changed(j), -ECHILD);
2143 fd = sd_journal_get_fd(j);
2150 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2153 assert_return(j, -EINVAL);
2154 assert_return(!journal_pid_changed(j), -ECHILD);
2155 assert_return(timeout_usec, -EINVAL);
2157 fd = sd_journal_get_fd(j);
2161 if (!j->on_network) {
2162 *timeout_usec = (uint64_t) -1;
2166 /* If we are on the network we need to regularly check for
2167 * changes manually */
2169 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2173 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2180 /* Is this a subdirectory we watch? */
2181 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2185 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2186 (endswith(e->name, ".journal") ||
2187 endswith(e->name, ".journal~"))) {
2189 /* Event for a journal file */
2191 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2192 r = add_file(j, d->path, e->name);
2194 log_debug("Failed to add file %s/%s: %s",
2195 d->path, e->name, strerror(-r));
2196 set_put_error(j, r);
2199 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2201 r = remove_file(j, d->path, e->name);
2203 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
2206 } else if (!d->is_root && e->len == 0) {
2208 /* Event for a subdirectory */
2210 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2211 r = remove_directory(j, d);
2213 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
2217 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2219 /* Event for root directory */
2221 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2222 r = add_directory(j, d->path, e->name);
2224 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
2231 if (e->mask & IN_IGNORED)
2234 log_warning("Unknown inotify event.");
2237 static int determine_change(sd_journal *j) {
2242 b = j->current_invalidate_counter != j->last_invalidate_counter;
2243 j->last_invalidate_counter = j->current_invalidate_counter;
2245 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2248 _public_ int sd_journal_process(sd_journal *j) {
2249 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
2250 bool got_something = false;
2252 assert_return(j, -EINVAL);
2253 assert_return(!journal_pid_changed(j), -ECHILD);
2255 j->last_process_usec = now(CLOCK_MONOTONIC);
2258 struct inotify_event *e;
2261 l = read(j->inotify_fd, buffer, sizeof(buffer));
2263 if (errno == EAGAIN || errno == EINTR)
2264 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2269 got_something = true;
2271 e = (struct inotify_event*) buffer;
2275 process_inotify_event(j, e);
2277 step = sizeof(struct inotify_event) + e->len;
2278 assert(step <= (size_t) l);
2280 e = (struct inotify_event*) ((uint8_t*) e + step);
2285 return determine_change(j);
2288 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2292 assert_return(j, -EINVAL);
2293 assert_return(!journal_pid_changed(j), -ECHILD);
2295 if (j->inotify_fd < 0) {
2297 /* This is the first invocation, hence create the
2299 r = sd_journal_get_fd(j);
2303 /* The journal might have changed since the context
2304 * object was created and we weren't watching before,
2305 * hence don't wait for anything, and return
2307 return determine_change(j);
2310 r = sd_journal_get_timeout(j, &t);
2314 if (t != (uint64_t) -1) {
2317 n = now(CLOCK_MONOTONIC);
2318 t = t > n ? t - n : 0;
2320 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2325 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2326 } while (r == -EINTR);
2331 return sd_journal_process(j);
2334 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2340 assert_return(j, -EINVAL);
2341 assert_return(!journal_pid_changed(j), -ECHILD);
2342 assert_return(from || to, -EINVAL);
2343 assert_return(from != to, -EINVAL);
2345 HASHMAP_FOREACH(f, j->files, i) {
2348 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2364 *from = MIN(fr, *from);
2370 return first ? 0 : 1;
2373 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2379 assert_return(j, -EINVAL);
2380 assert_return(!journal_pid_changed(j), -ECHILD);
2381 assert_return(from || to, -EINVAL);
2382 assert_return(from != to, -EINVAL);
2384 HASHMAP_FOREACH(f, j->files, i) {
2387 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2403 *from = MIN(fr, *from);
2409 return first ? 0 : 1;
2412 void journal_print_header(sd_journal *j) {
2415 bool newline = false;
2419 HASHMAP_FOREACH(f, j->files, i) {
2425 journal_file_print_header(f);
2429 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2434 assert_return(j, -EINVAL);
2435 assert_return(!journal_pid_changed(j), -ECHILD);
2436 assert_return(bytes, -EINVAL);
2438 HASHMAP_FOREACH(f, j->files, i) {
2441 if (fstat(f->fd, &st) < 0)
2444 sum += (uint64_t) st.st_blocks * 512ULL;
2451 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2454 assert_return(j, -EINVAL);
2455 assert_return(!journal_pid_changed(j), -ECHILD);
2456 assert_return(!isempty(field), -EINVAL);
2457 assert_return(field_is_valid(field), -EINVAL);
2463 free(j->unique_field);
2464 j->unique_field = f;
2465 j->unique_file = NULL;
2466 j->unique_offset = 0;
2471 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2476 assert_return(j, -EINVAL);
2477 assert_return(!journal_pid_changed(j), -ECHILD);
2478 assert_return(data, -EINVAL);
2479 assert_return(l, -EINVAL);
2480 assert_return(j->unique_field, -EINVAL);
2482 k = strlen(j->unique_field);
2484 if (!j->unique_file) {
2485 j->unique_file = hashmap_first(j->files);
2486 if (!j->unique_file)
2488 j->unique_offset = 0;
2498 /* Proceed to next data object in the field's linked list */
2499 if (j->unique_offset == 0) {
2500 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2504 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2506 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2510 j->unique_offset = le64toh(o->data.next_field_offset);
2513 /* We reached the end of the list? Then start again, with the next file */
2514 if (j->unique_offset == 0) {
2517 n = hashmap_next(j->files, j->unique_file->path);
2525 /* We do not use the type context here, but 0 instead,
2526 * so that we can look at this data object at the same
2527 * time as one on another file */
2528 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2532 /* Let's do the type check by hand, since we used 0 context above. */
2533 if (o->object.type != OBJECT_DATA)
2536 r = return_data(j, j->unique_file, o, &odata, &ol);
2540 /* OK, now let's see if we already returned this data
2541 * object by checking if it exists in the earlier
2542 * traversed files. */
2544 HASHMAP_FOREACH(of, j->files, i) {
2548 if (of == j->unique_file)
2551 /* Skip this file it didn't have any fields
2553 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2554 le64toh(of->header->n_fields) <= 0)
2557 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2568 r = return_data(j, j->unique_file, o, data, l);
2576 _public_ void sd_journal_restart_unique(sd_journal *j) {
2580 j->unique_file = NULL;
2581 j->unique_offset = 0;
2584 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2585 assert_return(j, -EINVAL);
2586 assert_return(!journal_pid_changed(j), -ECHILD);
2588 return !j->on_network;
2591 static char *lookup_field(const char *field, void *userdata) {
2592 sd_journal *j = userdata;
2600 r = sd_journal_get_data(j, field, &data, &size);
2602 size > REPLACE_VAR_MAX)
2603 return strdup(field);
2605 d = strlen(field) + 1;
2607 return strndup((const char*) data + d, size - d);
2610 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2614 _cleanup_free_ char *text = NULL, *cid = NULL;
2618 assert_return(j, -EINVAL);
2619 assert_return(!journal_pid_changed(j), -ECHILD);
2620 assert_return(ret, -EINVAL);
2622 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2626 cid = strndup((const char*) data + 11, size - 11);
2630 r = sd_id128_from_string(cid, &id);
2634 r = catalog_get(CATALOG_DATABASE, id, &text);
2638 t = replace_var(text, lookup_field, j);
2646 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2647 assert_return(ret, -EINVAL);
2649 return catalog_get(CATALOG_DATABASE, id, ret);
2652 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2653 assert_return(j, -EINVAL);
2654 assert_return(!journal_pid_changed(j), -ECHILD);
2656 j->data_threshold = sz;
2660 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2661 assert_return(j, -EINVAL);
2662 assert_return(!journal_pid_changed(j), -ECHILD);
2663 assert_return(sz, -EINVAL);
2665 *sz = j->data_threshold;