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);
1448 if (!de && errno != 0) {
1450 log_debug("Failed to read directory %s: %s",
1451 m->path, strerror(errno));
1457 if (dirent_is_file_with_suffix(de, ".journal") ||
1458 dirent_is_file_with_suffix(de, ".journal~")) {
1459 r = add_file(j, m->path, de->d_name);
1461 log_debug("Failed to add file %s/%s: %s",
1462 m->path, de->d_name, strerror(-r));
1463 r = set_put_error(j, r);
1470 check_network(j, dirfd(d));
1475 static int add_root_directory(sd_journal *j, const char *p) {
1476 _cleanup_closedir_ DIR *d = NULL;
1483 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1484 !path_startswith(p, "/run"))
1488 p = strappenda(j->prefix, p);
1494 m = hashmap_get(j->directories_by_path, p);
1496 m = new0(Directory, 1);
1501 m->path = strdup(p);
1507 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1513 j->current_invalidate_counter ++;
1515 log_debug("Root directory %s added.", m->path);
1517 } else if (!m->is_root)
1520 if (m->wd <= 0 && j->inotify_fd >= 0) {
1522 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1523 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1526 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1527 inotify_rm_watch(j->inotify_fd, m->wd);
1530 if (j->no_new_files)
1539 if (!de && errno != 0) {
1541 log_debug("Failed to read directory %s: %s",
1542 m->path, strerror(errno));
1548 if (dirent_is_file_with_suffix(de, ".journal") ||
1549 dirent_is_file_with_suffix(de, ".journal~")) {
1550 r = add_file(j, m->path, de->d_name);
1552 log_debug("Failed to add file %s/%s: %s",
1553 m->path, de->d_name, strerror(-r));
1554 r = set_put_error(j, r);
1558 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1559 sd_id128_from_string(de->d_name, &id) >= 0) {
1561 r = add_directory(j, m->path, de->d_name);
1563 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1567 check_network(j, dirfd(d));
1572 static int remove_directory(sd_journal *j, Directory *d) {
1576 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1578 if (j->inotify_fd >= 0)
1579 inotify_rm_watch(j->inotify_fd, d->wd);
1582 hashmap_remove(j->directories_by_path, d->path);
1585 log_debug("Root directory %s removed.", d->path);
1587 log_debug("Directory %s removed.", d->path);
1595 static int add_search_paths(sd_journal *j) {
1597 const char search_paths[] =
1598 "/run/log/journal\0"
1599 "/var/log/journal\0";
1604 /* We ignore most errors here, since the idea is to only open
1605 * what's actually accessible, and ignore the rest. */
1607 NULSTR_FOREACH(p, search_paths) {
1608 r = add_root_directory(j, p);
1609 if (r < 0 && r != -ENOENT) {
1610 r = set_put_error(j, r);
1619 static int add_current_paths(sd_journal *j) {
1624 assert(j->no_new_files);
1626 /* Simply adds all directories for files we have open as
1627 * "root" directories. We don't expect errors here, so we
1628 * treat them as fatal. */
1630 HASHMAP_FOREACH(f, j->files, i) {
1631 _cleanup_free_ char *dir;
1634 dir = dirname_malloc(f->path);
1638 r = add_root_directory(j, dir);
1640 set_put_error(j, r);
1649 static int allocate_inotify(sd_journal *j) {
1652 if (j->inotify_fd < 0) {
1653 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1654 if (j->inotify_fd < 0)
1658 if (!j->directories_by_wd) {
1659 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1660 if (!j->directories_by_wd)
1667 static sd_journal *journal_new(int flags, const char *path) {
1670 j = new0(sd_journal, 1);
1674 j->original_pid = getpid();
1677 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1680 j->path = strdup(path);
1685 j->files = hashmap_new(string_hash_func, string_compare_func);
1686 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1687 j->mmap = mmap_cache_new();
1688 if (!j->files || !j->directories_by_path || !j->mmap)
1694 sd_journal_close(j);
1698 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1702 assert_return(ret, -EINVAL);
1703 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1705 j = journal_new(flags, NULL);
1709 r = add_search_paths(j);
1717 sd_journal_close(j);
1722 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1723 _cleanup_free_ char *root = NULL, *class = NULL;
1728 assert_return(machine, -EINVAL);
1729 assert_return(ret, -EINVAL);
1730 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1731 assert_return(filename_is_safe(machine), -EINVAL);
1733 p = strappenda("/run/systemd/machines/", machine);
1734 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1742 if (!streq_ptr(class, "container"))
1745 j = journal_new(flags, NULL);
1752 r = add_search_paths(j);
1760 sd_journal_close(j);
1764 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1768 assert_return(ret, -EINVAL);
1769 assert_return(path, -EINVAL);
1770 assert_return(flags == 0, -EINVAL);
1772 j = journal_new(flags, path);
1776 r = add_root_directory(j, path);
1778 set_put_error(j, r);
1786 sd_journal_close(j);
1791 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1796 assert_return(ret, -EINVAL);
1797 assert_return(flags == 0, -EINVAL);
1799 j = journal_new(flags, NULL);
1803 STRV_FOREACH(path, paths) {
1804 r = add_any_file(j, *path);
1806 log_error("Failed to open %s: %s", *path, strerror(-r));
1811 j->no_new_files = true;
1817 sd_journal_close(j);
1822 _public_ void sd_journal_close(sd_journal *j) {
1829 sd_journal_flush_matches(j);
1831 while ((f = hashmap_steal_first(j->files)))
1832 journal_file_close(f);
1834 hashmap_free(j->files);
1836 while ((d = hashmap_first(j->directories_by_path)))
1837 remove_directory(j, d);
1839 while ((d = hashmap_first(j->directories_by_wd)))
1840 remove_directory(j, d);
1842 hashmap_free(j->directories_by_path);
1843 hashmap_free(j->directories_by_wd);
1845 if (j->inotify_fd >= 0)
1846 close_nointr_nofail(j->inotify_fd);
1849 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1850 mmap_cache_unref(j->mmap);
1855 free(j->unique_field);
1856 set_free(j->errors);
1860 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1865 assert_return(j, -EINVAL);
1866 assert_return(!journal_pid_changed(j), -ECHILD);
1867 assert_return(ret, -EINVAL);
1869 f = j->current_file;
1871 return -EADDRNOTAVAIL;
1873 if (f->current_offset <= 0)
1874 return -EADDRNOTAVAIL;
1876 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1880 *ret = le64toh(o->entry.realtime);
1884 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1890 assert_return(j, -EINVAL);
1891 assert_return(!journal_pid_changed(j), -ECHILD);
1893 f = j->current_file;
1895 return -EADDRNOTAVAIL;
1897 if (f->current_offset <= 0)
1898 return -EADDRNOTAVAIL;
1900 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1905 *ret_boot_id = o->entry.boot_id;
1907 r = sd_id128_get_boot(&id);
1911 if (!sd_id128_equal(id, o->entry.boot_id))
1916 *ret = le64toh(o->entry.monotonic);
1921 static bool field_is_valid(const char *field) {
1929 if (startswith(field, "__"))
1932 for (p = field; *p; p++) {
1937 if (*p >= 'A' && *p <= 'Z')
1940 if (*p >= '0' && *p <= '9')
1949 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1952 size_t field_length;
1956 assert_return(j, -EINVAL);
1957 assert_return(!journal_pid_changed(j), -ECHILD);
1958 assert_return(field, -EINVAL);
1959 assert_return(data, -EINVAL);
1960 assert_return(size, -EINVAL);
1961 assert_return(field_is_valid(field), -EINVAL);
1963 f = j->current_file;
1965 return -EADDRNOTAVAIL;
1967 if (f->current_offset <= 0)
1968 return -EADDRNOTAVAIL;
1970 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1974 field_length = strlen(field);
1976 n = journal_file_entry_n_items(o);
1977 for (i = 0; i < n; i++) {
1982 p = le64toh(o->entry.items[i].object_offset);
1983 le_hash = o->entry.items[i].hash;
1984 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1988 if (le_hash != o->data.hash)
1991 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1993 if (o->object.flags & OBJECT_COMPRESSED) {
1996 if (uncompress_startswith(o->data.payload, l,
1997 &f->compress_buffer, &f->compress_buffer_size,
1998 field, field_length, '=')) {
2002 if (!uncompress_blob(o->data.payload, l,
2003 &f->compress_buffer, &f->compress_buffer_size, &rsize,
2007 *data = f->compress_buffer;
2008 *size = (size_t) rsize;
2013 return -EPROTONOSUPPORT;
2016 } else if (l >= field_length+1 &&
2017 memcmp(o->data.payload, field, field_length) == 0 &&
2018 o->data.payload[field_length] == '=') {
2022 if ((uint64_t) t != l)
2025 *data = o->data.payload;
2031 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2039 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
2043 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2046 /* We can't read objects larger than 4G on a 32bit machine */
2047 if ((uint64_t) t != l)
2050 if (o->object.flags & OBJECT_COMPRESSED) {
2054 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, j->data_threshold))
2057 *data = f->compress_buffer;
2058 *size = (size_t) rsize;
2060 return -EPROTONOSUPPORT;
2063 *data = o->data.payload;
2070 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2077 assert_return(j, -EINVAL);
2078 assert_return(!journal_pid_changed(j), -ECHILD);
2079 assert_return(data, -EINVAL);
2080 assert_return(size, -EINVAL);
2082 f = j->current_file;
2084 return -EADDRNOTAVAIL;
2086 if (f->current_offset <= 0)
2087 return -EADDRNOTAVAIL;
2089 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2093 n = journal_file_entry_n_items(o);
2094 if (j->current_field >= n)
2097 p = le64toh(o->entry.items[j->current_field].object_offset);
2098 le_hash = o->entry.items[j->current_field].hash;
2099 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2103 if (le_hash != o->data.hash)
2106 r = return_data(j, f, o, data, size);
2110 j->current_field ++;
2115 _public_ void sd_journal_restart_data(sd_journal *j) {
2119 j->current_field = 0;
2122 _public_ int sd_journal_get_fd(sd_journal *j) {
2125 assert_return(j, -EINVAL);
2126 assert_return(!journal_pid_changed(j), -ECHILD);
2128 if (j->inotify_fd >= 0)
2129 return j->inotify_fd;
2131 r = allocate_inotify(j);
2135 /* Iterate through all dirs again, to add them to the
2137 if (j->no_new_files)
2138 r = add_current_paths(j);
2140 r = add_root_directory(j, j->path);
2142 r = add_search_paths(j);
2146 return j->inotify_fd;
2149 _public_ int sd_journal_get_events(sd_journal *j) {
2152 assert_return(j, -EINVAL);
2153 assert_return(!journal_pid_changed(j), -ECHILD);
2155 fd = sd_journal_get_fd(j);
2162 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2165 assert_return(j, -EINVAL);
2166 assert_return(!journal_pid_changed(j), -ECHILD);
2167 assert_return(timeout_usec, -EINVAL);
2169 fd = sd_journal_get_fd(j);
2173 if (!j->on_network) {
2174 *timeout_usec = (uint64_t) -1;
2178 /* If we are on the network we need to regularly check for
2179 * changes manually */
2181 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2185 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2192 /* Is this a subdirectory we watch? */
2193 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2197 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2198 (endswith(e->name, ".journal") ||
2199 endswith(e->name, ".journal~"))) {
2201 /* Event for a journal file */
2203 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2204 r = add_file(j, d->path, e->name);
2206 log_debug("Failed to add file %s/%s: %s",
2207 d->path, e->name, strerror(-r));
2208 set_put_error(j, r);
2211 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2213 r = remove_file(j, d->path, e->name);
2215 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
2218 } else if (!d->is_root && e->len == 0) {
2220 /* Event for a subdirectory */
2222 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2223 r = remove_directory(j, d);
2225 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
2229 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2231 /* Event for root directory */
2233 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2234 r = add_directory(j, d->path, e->name);
2236 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
2243 if (e->mask & IN_IGNORED)
2246 log_warning("Unknown inotify event.");
2249 static int determine_change(sd_journal *j) {
2254 b = j->current_invalidate_counter != j->last_invalidate_counter;
2255 j->last_invalidate_counter = j->current_invalidate_counter;
2257 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2260 _public_ int sd_journal_process(sd_journal *j) {
2261 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
2262 bool got_something = false;
2264 assert_return(j, -EINVAL);
2265 assert_return(!journal_pid_changed(j), -ECHILD);
2267 j->last_process_usec = now(CLOCK_MONOTONIC);
2270 struct inotify_event *e;
2273 l = read(j->inotify_fd, buffer, sizeof(buffer));
2275 if (errno == EAGAIN || errno == EINTR)
2276 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2281 got_something = true;
2283 e = (struct inotify_event*) buffer;
2287 process_inotify_event(j, e);
2289 step = sizeof(struct inotify_event) + e->len;
2290 assert(step <= (size_t) l);
2292 e = (struct inotify_event*) ((uint8_t*) e + step);
2297 return determine_change(j);
2300 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2304 assert_return(j, -EINVAL);
2305 assert_return(!journal_pid_changed(j), -ECHILD);
2307 if (j->inotify_fd < 0) {
2309 /* This is the first invocation, hence create the
2311 r = sd_journal_get_fd(j);
2315 /* The journal might have changed since the context
2316 * object was created and we weren't watching before,
2317 * hence don't wait for anything, and return
2319 return determine_change(j);
2322 r = sd_journal_get_timeout(j, &t);
2326 if (t != (uint64_t) -1) {
2329 n = now(CLOCK_MONOTONIC);
2330 t = t > n ? t - n : 0;
2332 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2337 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2338 } while (r == -EINTR);
2343 return sd_journal_process(j);
2346 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2352 assert_return(j, -EINVAL);
2353 assert_return(!journal_pid_changed(j), -ECHILD);
2354 assert_return(from || to, -EINVAL);
2355 assert_return(from != to, -EINVAL);
2357 HASHMAP_FOREACH(f, j->files, i) {
2360 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2376 *from = MIN(fr, *from);
2382 return first ? 0 : 1;
2385 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2391 assert_return(j, -EINVAL);
2392 assert_return(!journal_pid_changed(j), -ECHILD);
2393 assert_return(from || to, -EINVAL);
2394 assert_return(from != to, -EINVAL);
2396 HASHMAP_FOREACH(f, j->files, i) {
2399 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2415 *from = MIN(fr, *from);
2421 return first ? 0 : 1;
2424 void journal_print_header(sd_journal *j) {
2427 bool newline = false;
2431 HASHMAP_FOREACH(f, j->files, i) {
2437 journal_file_print_header(f);
2441 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2446 assert_return(j, -EINVAL);
2447 assert_return(!journal_pid_changed(j), -ECHILD);
2448 assert_return(bytes, -EINVAL);
2450 HASHMAP_FOREACH(f, j->files, i) {
2453 if (fstat(f->fd, &st) < 0)
2456 sum += (uint64_t) st.st_blocks * 512ULL;
2463 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2466 assert_return(j, -EINVAL);
2467 assert_return(!journal_pid_changed(j), -ECHILD);
2468 assert_return(!isempty(field), -EINVAL);
2469 assert_return(field_is_valid(field), -EINVAL);
2475 free(j->unique_field);
2476 j->unique_field = f;
2477 j->unique_file = NULL;
2478 j->unique_offset = 0;
2483 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2486 assert_return(j, -EINVAL);
2487 assert_return(!journal_pid_changed(j), -ECHILD);
2488 assert_return(data, -EINVAL);
2489 assert_return(l, -EINVAL);
2490 assert_return(j->unique_field, -EINVAL);
2492 k = strlen(j->unique_field);
2494 if (!j->unique_file) {
2495 j->unique_file = hashmap_first(j->files);
2496 if (!j->unique_file)
2498 j->unique_offset = 0;
2510 /* Proceed to next data object in the field's linked list */
2511 if (j->unique_offset == 0) {
2512 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2516 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2518 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2522 j->unique_offset = le64toh(o->data.next_field_offset);
2525 /* We reached the end of the list? Then start again, with the next file */
2526 if (j->unique_offset == 0) {
2529 n = hashmap_next(j->files, j->unique_file->path);
2537 /* We do not use the type context here, but 0 instead,
2538 * so that we can look at this data object at the same
2539 * time as one on another file */
2540 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2544 /* Let's do the type check by hand, since we used 0 context above. */
2545 if (o->object.type != OBJECT_DATA) {
2546 log_error("%s:offset " OFSfmt ": object has type %d, expected %d",
2547 j->unique_file->path, j->unique_offset,
2548 o->object.type, OBJECT_DATA);
2552 r = journal_file_object_keep(j->unique_file, o, j->unique_offset);
2556 r = return_data(j, j->unique_file, o, &odata, &ol);
2560 /* OK, now let's see if we already returned this data
2561 * object by checking if it exists in the earlier
2562 * traversed files. */
2564 HASHMAP_FOREACH(of, j->files, i) {
2568 if (of == j->unique_file)
2571 /* Skip this file it didn't have any fields
2573 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2574 le64toh(of->header->n_fields) <= 0)
2577 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2588 r = journal_file_object_release(j->unique_file, o, j->unique_offset);
2592 r = return_data(j, j->unique_file, o, data, l);
2600 _public_ void sd_journal_restart_unique(sd_journal *j) {
2604 j->unique_file = NULL;
2605 j->unique_offset = 0;
2608 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2609 assert_return(j, -EINVAL);
2610 assert_return(!journal_pid_changed(j), -ECHILD);
2612 return !j->on_network;
2615 static char *lookup_field(const char *field, void *userdata) {
2616 sd_journal *j = userdata;
2624 r = sd_journal_get_data(j, field, &data, &size);
2626 size > REPLACE_VAR_MAX)
2627 return strdup(field);
2629 d = strlen(field) + 1;
2631 return strndup((const char*) data + d, size - d);
2634 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2638 _cleanup_free_ char *text = NULL, *cid = NULL;
2642 assert_return(j, -EINVAL);
2643 assert_return(!journal_pid_changed(j), -ECHILD);
2644 assert_return(ret, -EINVAL);
2646 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2650 cid = strndup((const char*) data + 11, size - 11);
2654 r = sd_id128_from_string(cid, &id);
2658 r = catalog_get(CATALOG_DATABASE, id, &text);
2662 t = replace_var(text, lookup_field, j);
2670 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2671 assert_return(ret, -EINVAL);
2673 return catalog_get(CATALOG_DATABASE, id, ret);
2676 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2677 assert_return(j, -EINVAL);
2678 assert_return(!journal_pid_changed(j), -ECHILD);
2680 j->data_threshold = sz;
2684 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2685 assert_return(j, -EINVAL);
2686 assert_return(!journal_pid_changed(j), -ECHILD);
2687 assert_return(sz, -EINVAL);
2689 *sz = j->data_threshold;