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_RECHECK_USEC (2 * USEC_PER_SEC)
48 #define REPLACE_VAR_MAX 256
50 #define DEFAULT_DATA_THRESHOLD (64*1024)
52 static void remove_file_real(sd_journal *j, JournalFile *f);
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, NULL);
75 return set_put(j->errors, INT_TO_PTR(r));
78 static void detach_location(sd_journal *j) {
84 j->current_file = NULL;
87 ORDERED_HASHMAP_FOREACH(f, j->files, i)
88 journal_file_reset_location(f);
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, JournalFile *f, Object *o) {
120 init_location(&j->current_location, LOCATION_DISCRETE, f, o);
123 j->current_field = 0;
125 /* Let f know its candidate entry was picked. */
126 assert(f->location_type == LOCATION_SEEK);
127 f->location_type = LOCATION_DISCRETE;
130 static int match_is_valid(const void *data, size_t size) {
138 if (startswith(data, "__"))
142 for (p = b; p < b + size; p++) {
150 if (*p >= 'A' && *p <= 'Z')
153 if (*p >= '0' && *p <= '9')
162 static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
163 const uint8_t *a = _a, *b = _b;
166 for (j = 0; j < s && j < t; j++) {
175 assert_not_reached("\"=\" not found");
178 static Match *match_new(Match *p, MatchType t) {
189 LIST_PREPEND(matches, p->matches, m);
195 static void match_free(Match *m) {
199 match_free(m->matches);
202 LIST_REMOVE(matches, m->parent->matches, m);
208 static void match_free_if_empty(Match *m) {
209 if (!m || m->matches)
215 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
216 Match *l3, *l4, *add_here = NULL, *m;
219 assert_return(j, -EINVAL);
220 assert_return(!journal_pid_changed(j), -ECHILD);
221 assert_return(data, -EINVAL);
226 assert_return(match_is_valid(data, size), -EINVAL);
232 * level 4: concrete matches */
235 j->level0 = match_new(NULL, MATCH_AND_TERM);
241 j->level1 = match_new(j->level0, MATCH_OR_TERM);
247 j->level2 = match_new(j->level1, MATCH_AND_TERM);
252 assert(j->level0->type == MATCH_AND_TERM);
253 assert(j->level1->type == MATCH_OR_TERM);
254 assert(j->level2->type == MATCH_AND_TERM);
256 le_hash = htole64(hash64(data, size));
258 LIST_FOREACH(matches, l3, j->level2->matches) {
259 assert(l3->type == MATCH_OR_TERM);
261 LIST_FOREACH(matches, l4, l3->matches) {
262 assert(l4->type == MATCH_DISCRETE);
264 /* Exactly the same match already? Then ignore
266 if (l4->le_hash == le_hash &&
268 memcmp(l4->data, data, size) == 0)
271 /* Same field? Then let's add this to this OR term */
272 if (same_field(data, size, l4->data, l4->size)) {
283 add_here = match_new(j->level2, MATCH_OR_TERM);
288 m = match_new(add_here, MATCH_DISCRETE);
292 m->le_hash = le_hash;
294 m->data = memdup(data, size);
303 match_free_if_empty(add_here);
304 match_free_if_empty(j->level2);
305 match_free_if_empty(j->level1);
306 match_free_if_empty(j->level0);
311 _public_ int sd_journal_add_conjunction(sd_journal *j) {
312 assert_return(j, -EINVAL);
313 assert_return(!journal_pid_changed(j), -ECHILD);
321 if (!j->level1->matches)
330 _public_ int sd_journal_add_disjunction(sd_journal *j) {
331 assert_return(j, -EINVAL);
332 assert_return(!journal_pid_changed(j), -ECHILD);
343 if (!j->level2->matches)
350 static char *match_make_string(Match *m) {
353 bool enclose = false;
356 return strdup("none");
358 if (m->type == MATCH_DISCRETE)
359 return strndup(m->data, m->size);
362 LIST_FOREACH(matches, i, m->matches) {
365 t = match_make_string(i);
372 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
387 r = strjoin("(", p, ")", NULL);
395 char *journal_make_match_string(sd_journal *j) {
398 return match_make_string(j->level0);
401 _public_ void sd_journal_flush_matches(sd_journal *j) {
406 match_free(j->level0);
408 j->level0 = j->level1 = j->level2 = NULL;
413 _pure_ static int compare_with_location(JournalFile *f, Location *l) {
416 assert(f->location_type == LOCATION_SEEK);
417 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
419 if (l->monotonic_set &&
420 sd_id128_equal(f->current_boot_id, l->boot_id) &&
422 f->current_realtime == l->realtime &&
424 f->current_xor_hash == l->xor_hash)
428 sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) {
430 if (f->current_seqnum < l->seqnum)
432 if (f->current_seqnum > l->seqnum)
436 if (l->monotonic_set &&
437 sd_id128_equal(f->current_boot_id, l->boot_id)) {
439 if (f->current_monotonic < l->monotonic)
441 if (f->current_monotonic > l->monotonic)
445 if (l->realtime_set) {
447 if (f->current_realtime < l->realtime)
449 if (f->current_realtime > l->realtime)
453 if (l->xor_hash_set) {
455 if (f->current_xor_hash < l->xor_hash)
457 if (f->current_xor_hash > l->xor_hash)
464 static int next_for_match(
468 uint64_t after_offset,
469 direction_t direction,
481 if (m->type == MATCH_DISCRETE) {
484 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
488 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
490 } else if (m->type == MATCH_OR_TERM) {
493 /* Find the earliest match beyond after_offset */
495 LIST_FOREACH(matches, i, m->matches) {
498 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
502 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
510 } else if (m->type == MATCH_AND_TERM) {
511 Match *i, *last_moved;
513 /* Always jump to the next matching entry and repeat
514 * this until we find an offset that matches for all
520 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
524 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
525 last_moved = m->matches;
527 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
530 r = next_for_match(j, i, f, np, direction, NULL, &cp);
534 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
535 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
544 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
556 static int find_location_for_match(
560 direction_t direction,
570 if (m->type == MATCH_DISCRETE) {
573 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
577 /* FIXME: missing: find by monotonic */
579 if (j->current_location.type == LOCATION_HEAD)
580 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
581 if (j->current_location.type == LOCATION_TAIL)
582 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
583 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
584 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
585 if (j->current_location.monotonic_set) {
586 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
590 if (j->current_location.realtime_set)
591 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
593 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
595 } else if (m->type == MATCH_OR_TERM) {
600 /* Find the earliest match */
602 LIST_FOREACH(matches, i, m->matches) {
605 r = find_location_for_match(j, i, f, direction, NULL, &cp);
609 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
617 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
632 assert(m->type == MATCH_AND_TERM);
634 /* First jump to the last match, and then find the
635 * next one where all matches match */
640 LIST_FOREACH(matches, i, m->matches) {
643 r = find_location_for_match(j, i, f, direction, NULL, &cp);
647 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
651 return next_for_match(j, m, f, np, direction, ret, offset);
655 static int find_location_with_matches(
658 direction_t direction,
670 /* No matches is simple */
672 if (j->current_location.type == LOCATION_HEAD)
673 return journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset);
674 if (j->current_location.type == LOCATION_TAIL)
675 return journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset);
676 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
677 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
678 if (j->current_location.monotonic_set) {
679 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
683 if (j->current_location.realtime_set)
684 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
686 return journal_file_next_entry(f, 0, direction, ret, offset);
688 return find_location_for_match(j, j->level0, f, direction, ret, offset);
691 static int next_with_matches(
694 direction_t direction,
703 /* No matches is easy. We simple advance the file
706 return journal_file_next_entry(f, f->current_offset, direction, ret, offset);
708 /* If we have a match then we look for the next matching entry
709 * with an offset at least one step larger */
710 return next_for_match(j, j->level0, f,
711 direction == DIRECTION_DOWN ? f->current_offset + 1
712 : f->current_offset - 1,
713 direction, ret, offset);
716 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction) {
718 uint64_t cp, n_entries;
724 if (f->last_direction == direction && f->current_offset > 0) {
725 /* If we hit EOF before, recheck if any new entries arrived. */
726 n_entries = le64toh(f->header->n_entries);
727 if (f->location_type == LOCATION_TAIL && n_entries == f->last_n_entries)
729 f->last_n_entries = n_entries;
731 /* LOCATION_SEEK here means we did the work in a previous
732 * iteration and the current location already points to a
733 * candidate entry. */
734 if (f->location_type != LOCATION_SEEK) {
735 r = next_with_matches(j, f, direction, &c, &cp);
739 journal_file_save_location(f, direction, c, cp);
742 r = find_location_with_matches(j, f, direction, &c, &cp);
746 journal_file_save_location(f, direction, c, cp);
749 /* OK, we found the spot, now let's advance until an entry
750 * that is actually different from what we were previously
751 * looking at. This is necessary to handle entries which exist
752 * in two (or more) journal files, and which shall all be
753 * suppressed but one. */
758 if (j->current_location.type == LOCATION_DISCRETE) {
761 k = compare_with_location(f, &j->current_location);
763 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
770 r = next_with_matches(j, f, direction, &c, &cp);
774 journal_file_save_location(f, direction, c, cp);
778 static int real_journal_next(sd_journal *j, direction_t direction) {
779 JournalFile *f, *new_file = NULL;
784 assert_return(j, -EINVAL);
785 assert_return(!journal_pid_changed(j), -ECHILD);
787 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
790 r = next_beyond_location(j, f, direction);
792 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
793 remove_file_real(j, f);
796 f->location_type = LOCATION_TAIL;
805 k = journal_file_compare_locations(f, new_file);
807 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
817 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o);
821 set_location(j, new_file, o);
826 _public_ int sd_journal_next(sd_journal *j) {
827 return real_journal_next(j, DIRECTION_DOWN);
830 _public_ int sd_journal_previous(sd_journal *j) {
831 return real_journal_next(j, DIRECTION_UP);
834 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
837 assert_return(j, -EINVAL);
838 assert_return(!journal_pid_changed(j), -ECHILD);
841 /* If this is not a discrete skip, then at least
842 * resolve the current location */
843 if (j->current_location.type != LOCATION_DISCRETE)
844 return real_journal_next(j, direction);
850 r = real_journal_next(j, direction);
864 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
865 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
868 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
869 return real_journal_next_skip(j, DIRECTION_UP, skip);
872 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
875 char bid[33], sid[33];
877 assert_return(j, -EINVAL);
878 assert_return(!journal_pid_changed(j), -ECHILD);
879 assert_return(cursor, -EINVAL);
881 if (!j->current_file || j->current_file->current_offset <= 0)
882 return -EADDRNOTAVAIL;
884 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
888 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
889 sd_id128_to_string(o->entry.boot_id, bid);
892 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
893 sid, le64toh(o->entry.seqnum),
894 bid, le64toh(o->entry.monotonic),
895 le64toh(o->entry.realtime),
896 le64toh(o->entry.xor_hash)) < 0)
902 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
903 const char *word, *state;
905 unsigned long long seqnum, monotonic, realtime, xor_hash;
907 seqnum_id_set = false,
910 monotonic_set = false,
911 realtime_set = false,
912 xor_hash_set = false;
913 sd_id128_t seqnum_id, boot_id;
915 assert_return(j, -EINVAL);
916 assert_return(!journal_pid_changed(j), -ECHILD);
917 assert_return(!isempty(cursor), -EINVAL);
919 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
923 if (l < 2 || word[1] != '=')
926 item = strndup(word, l);
933 seqnum_id_set = true;
934 k = sd_id128_from_string(item+2, &seqnum_id);
939 if (sscanf(item+2, "%llx", &seqnum) != 1)
945 k = sd_id128_from_string(item+2, &boot_id);
949 monotonic_set = true;
950 if (sscanf(item+2, "%llx", &monotonic) != 1)
956 if (sscanf(item+2, "%llx", &realtime) != 1)
962 if (sscanf(item+2, "%llx", &xor_hash) != 1)
973 if ((!seqnum_set || !seqnum_id_set) &&
974 (!monotonic_set || !boot_id_set) &&
980 j->current_location.type = LOCATION_SEEK;
983 j->current_location.realtime = (uint64_t) realtime;
984 j->current_location.realtime_set = true;
987 if (seqnum_set && seqnum_id_set) {
988 j->current_location.seqnum = (uint64_t) seqnum;
989 j->current_location.seqnum_id = seqnum_id;
990 j->current_location.seqnum_set = true;
993 if (monotonic_set && boot_id_set) {
994 j->current_location.monotonic = (uint64_t) monotonic;
995 j->current_location.boot_id = boot_id;
996 j->current_location.monotonic_set = true;
1000 j->current_location.xor_hash = (uint64_t) xor_hash;
1001 j->current_location.xor_hash_set = true;
1007 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1009 const char *word, *state;
1013 assert_return(j, -EINVAL);
1014 assert_return(!journal_pid_changed(j), -ECHILD);
1015 assert_return(!isempty(cursor), -EINVAL);
1017 if (!j->current_file || j->current_file->current_offset <= 0)
1018 return -EADDRNOTAVAIL;
1020 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1024 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1025 _cleanup_free_ char *item = NULL;
1027 unsigned long long ll;
1030 if (l < 2 || word[1] != '=')
1033 item = strndup(word, l);
1040 k = sd_id128_from_string(item+2, &id);
1043 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1048 if (sscanf(item+2, "%llx", &ll) != 1)
1050 if (ll != le64toh(o->entry.seqnum))
1055 k = sd_id128_from_string(item+2, &id);
1058 if (!sd_id128_equal(id, o->entry.boot_id))
1063 if (sscanf(item+2, "%llx", &ll) != 1)
1065 if (ll != le64toh(o->entry.monotonic))
1070 if (sscanf(item+2, "%llx", &ll) != 1)
1072 if (ll != le64toh(o->entry.realtime))
1077 if (sscanf(item+2, "%llx", &ll) != 1)
1079 if (ll != le64toh(o->entry.xor_hash))
1089 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1090 assert_return(j, -EINVAL);
1091 assert_return(!journal_pid_changed(j), -ECHILD);
1094 j->current_location.type = LOCATION_SEEK;
1095 j->current_location.boot_id = boot_id;
1096 j->current_location.monotonic = usec;
1097 j->current_location.monotonic_set = true;
1102 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1103 assert_return(j, -EINVAL);
1104 assert_return(!journal_pid_changed(j), -ECHILD);
1107 j->current_location.type = LOCATION_SEEK;
1108 j->current_location.realtime = usec;
1109 j->current_location.realtime_set = true;
1114 _public_ int sd_journal_seek_head(sd_journal *j) {
1115 assert_return(j, -EINVAL);
1116 assert_return(!journal_pid_changed(j), -ECHILD);
1119 j->current_location.type = LOCATION_HEAD;
1124 _public_ int sd_journal_seek_tail(sd_journal *j) {
1125 assert_return(j, -EINVAL);
1126 assert_return(!journal_pid_changed(j), -ECHILD);
1129 j->current_location.type = LOCATION_TAIL;
1134 static void check_network(sd_journal *j, int fd) {
1142 if (fstatfs(fd, &sfs) < 0)
1146 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1147 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1148 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1149 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1150 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1153 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1154 const char *full, *tilded, *atted;
1156 full = strappenda(prefix, ".journal");
1157 tilded = strappenda(full, "~");
1158 atted = strappenda(prefix, "@");
1160 return streq(filename, full) ||
1161 streq(filename, tilded) ||
1162 startswith(filename, atted);
1165 static bool file_type_wanted(int flags, const char *filename) {
1166 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1169 /* no flags set → every type is OK */
1170 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1173 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1176 if (flags & SD_JOURNAL_CURRENT_USER) {
1177 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1179 assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid())
1180 < (int) sizeof(prefix));
1182 if (file_has_type_prefix(prefix, filename))
1189 static int add_any_file(sd_journal *j, const char *path) {
1190 JournalFile *f = NULL;
1196 if (ordered_hashmap_get(j->files, path))
1199 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1203 /* journal_file_dump(f); */
1205 r = ordered_hashmap_put(j->files, f->path, f);
1207 journal_file_close(f);
1211 log_debug("File %s added.", f->path);
1213 check_network(j, f->fd);
1215 j->current_invalidate_counter ++;
1220 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1221 _cleanup_free_ char *path = NULL;
1228 if (j->no_new_files ||
1229 !file_type_wanted(j->flags, filename))
1232 path = strjoin(prefix, "/", filename, NULL);
1236 r = add_any_file(j, path);
1242 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1243 _cleanup_free_ char *path;
1250 path = strjoin(prefix, "/", filename, NULL);
1254 f = ordered_hashmap_get(j->files, path);
1258 remove_file_real(j, f);
1262 static void remove_file_real(sd_journal *j, JournalFile *f) {
1266 ordered_hashmap_remove(j->files, f->path);
1268 log_debug("File %s removed.", f->path);
1270 if (j->current_file == f) {
1271 j->current_file = NULL;
1272 j->current_field = 0;
1275 if (j->unique_file == f) {
1276 /* Jump to the next unique_file or NULL if that one was last */
1277 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
1278 j->unique_offset = 0;
1279 if (!j->unique_file)
1280 j->unique_file_lost = true;
1283 journal_file_close(f);
1285 j->current_invalidate_counter ++;
1288 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1289 _cleanup_free_ char *path = NULL;
1291 _cleanup_closedir_ DIR *d = NULL;
1299 log_debug("Considering %s/%s.", prefix, dirname);
1301 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1302 (sd_id128_from_string(dirname, &id) < 0 ||
1303 sd_id128_get_machine(&mid) < 0 ||
1304 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1307 path = strjoin(prefix, "/", dirname, NULL);
1313 log_debug_errno(errno, "Failed to open %s: %m", path);
1314 if (errno == ENOENT)
1319 m = hashmap_get(j->directories_by_path, path);
1321 m = new0(Directory, 1);
1328 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1333 path = NULL; /* avoid freeing in cleanup */
1334 j->current_invalidate_counter ++;
1336 log_debug("Directory %s added.", m->path);
1338 } else if (m->is_root)
1341 if (m->wd <= 0 && j->inotify_fd >= 0) {
1343 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1344 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1345 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1348 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1349 inotify_rm_watch(j->inotify_fd, m->wd);
1357 if (!de && errno != 0) {
1359 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1365 if (dirent_is_file_with_suffix(de, ".journal") ||
1366 dirent_is_file_with_suffix(de, ".journal~")) {
1367 r = add_file(j, m->path, de->d_name);
1369 log_debug_errno(r, "Failed to add file %s/%s: %m",
1370 m->path, de->d_name);
1371 r = set_put_error(j, r);
1378 check_network(j, dirfd(d));
1383 static int add_root_directory(sd_journal *j, const char *p) {
1384 _cleanup_closedir_ DIR *d = NULL;
1391 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1392 !path_startswith(p, "/run"))
1396 p = strappenda(j->prefix, p);
1402 m = hashmap_get(j->directories_by_path, p);
1404 m = new0(Directory, 1);
1409 m->path = strdup(p);
1415 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1421 j->current_invalidate_counter ++;
1423 log_debug("Root directory %s added.", m->path);
1425 } else if (!m->is_root)
1428 if (m->wd <= 0 && j->inotify_fd >= 0) {
1430 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1431 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1434 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1435 inotify_rm_watch(j->inotify_fd, m->wd);
1438 if (j->no_new_files)
1447 if (!de && errno != 0) {
1449 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1455 if (dirent_is_file_with_suffix(de, ".journal") ||
1456 dirent_is_file_with_suffix(de, ".journal~")) {
1457 r = add_file(j, m->path, de->d_name);
1459 log_debug_errno(r, "Failed to add file %s/%s: %m",
1460 m->path, de->d_name);
1461 r = set_put_error(j, r);
1465 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1466 sd_id128_from_string(de->d_name, &id) >= 0) {
1468 r = add_directory(j, m->path, de->d_name);
1470 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
1474 check_network(j, dirfd(d));
1479 static int remove_directory(sd_journal *j, Directory *d) {
1483 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1485 if (j->inotify_fd >= 0)
1486 inotify_rm_watch(j->inotify_fd, d->wd);
1489 hashmap_remove(j->directories_by_path, d->path);
1492 log_debug("Root directory %s removed.", d->path);
1494 log_debug("Directory %s removed.", d->path);
1502 static int add_search_paths(sd_journal *j) {
1504 const char search_paths[] =
1505 "/run/log/journal\0"
1506 "/var/log/journal\0";
1511 /* We ignore most errors here, since the idea is to only open
1512 * what's actually accessible, and ignore the rest. */
1514 NULSTR_FOREACH(p, search_paths) {
1515 r = add_root_directory(j, p);
1516 if (r < 0 && r != -ENOENT) {
1517 r = set_put_error(j, r);
1526 static int add_current_paths(sd_journal *j) {
1531 assert(j->no_new_files);
1533 /* Simply adds all directories for files we have open as
1534 * "root" directories. We don't expect errors here, so we
1535 * treat them as fatal. */
1537 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
1538 _cleanup_free_ char *dir;
1541 dir = dirname_malloc(f->path);
1545 r = add_root_directory(j, dir);
1547 set_put_error(j, r);
1556 static int allocate_inotify(sd_journal *j) {
1559 if (j->inotify_fd < 0) {
1560 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1561 if (j->inotify_fd < 0)
1565 if (!j->directories_by_wd) {
1566 j->directories_by_wd = hashmap_new(NULL);
1567 if (!j->directories_by_wd)
1574 static sd_journal *journal_new(int flags, const char *path) {
1577 j = new0(sd_journal, 1);
1581 j->original_pid = getpid();
1584 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1587 j->path = strdup(path);
1592 j->files = ordered_hashmap_new(&string_hash_ops);
1593 j->directories_by_path = hashmap_new(&string_hash_ops);
1594 j->mmap = mmap_cache_new();
1595 if (!j->files || !j->directories_by_path || !j->mmap)
1601 sd_journal_close(j);
1605 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1609 assert_return(ret, -EINVAL);
1610 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1612 j = journal_new(flags, NULL);
1616 r = add_search_paths(j);
1624 sd_journal_close(j);
1629 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1630 _cleanup_free_ char *root = NULL, *class = NULL;
1635 assert_return(machine, -EINVAL);
1636 assert_return(ret, -EINVAL);
1637 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1638 assert_return(machine_name_is_valid(machine), -EINVAL);
1640 p = strappenda("/run/systemd/machines/", machine);
1641 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1649 if (!streq_ptr(class, "container"))
1652 j = journal_new(flags, NULL);
1659 r = add_search_paths(j);
1667 sd_journal_close(j);
1671 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1675 assert_return(ret, -EINVAL);
1676 assert_return(path, -EINVAL);
1677 assert_return(flags == 0, -EINVAL);
1679 j = journal_new(flags, path);
1683 r = add_root_directory(j, path);
1685 set_put_error(j, r);
1693 sd_journal_close(j);
1698 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1703 assert_return(ret, -EINVAL);
1704 assert_return(flags == 0, -EINVAL);
1706 j = journal_new(flags, NULL);
1710 STRV_FOREACH(path, paths) {
1711 r = add_any_file(j, *path);
1713 log_error_errno(r, "Failed to open %s: %m", *path);
1718 j->no_new_files = true;
1724 sd_journal_close(j);
1729 _public_ void sd_journal_close(sd_journal *j) {
1736 sd_journal_flush_matches(j);
1738 while ((f = ordered_hashmap_steal_first(j->files)))
1739 journal_file_close(f);
1741 ordered_hashmap_free(j->files);
1743 while ((d = hashmap_first(j->directories_by_path)))
1744 remove_directory(j, d);
1746 while ((d = hashmap_first(j->directories_by_wd)))
1747 remove_directory(j, d);
1749 hashmap_free(j->directories_by_path);
1750 hashmap_free(j->directories_by_wd);
1752 safe_close(j->inotify_fd);
1755 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1756 mmap_cache_unref(j->mmap);
1761 free(j->unique_field);
1762 set_free(j->errors);
1766 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1771 assert_return(j, -EINVAL);
1772 assert_return(!journal_pid_changed(j), -ECHILD);
1773 assert_return(ret, -EINVAL);
1775 f = j->current_file;
1777 return -EADDRNOTAVAIL;
1779 if (f->current_offset <= 0)
1780 return -EADDRNOTAVAIL;
1782 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1786 *ret = le64toh(o->entry.realtime);
1790 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1796 assert_return(j, -EINVAL);
1797 assert_return(!journal_pid_changed(j), -ECHILD);
1799 f = j->current_file;
1801 return -EADDRNOTAVAIL;
1803 if (f->current_offset <= 0)
1804 return -EADDRNOTAVAIL;
1806 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1811 *ret_boot_id = o->entry.boot_id;
1813 r = sd_id128_get_boot(&id);
1817 if (!sd_id128_equal(id, o->entry.boot_id))
1822 *ret = le64toh(o->entry.monotonic);
1827 static bool field_is_valid(const char *field) {
1835 if (startswith(field, "__"))
1838 for (p = field; *p; p++) {
1843 if (*p >= 'A' && *p <= 'Z')
1846 if (*p >= '0' && *p <= '9')
1855 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1858 size_t field_length;
1862 assert_return(j, -EINVAL);
1863 assert_return(!journal_pid_changed(j), -ECHILD);
1864 assert_return(field, -EINVAL);
1865 assert_return(data, -EINVAL);
1866 assert_return(size, -EINVAL);
1867 assert_return(field_is_valid(field), -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 field_length = strlen(field);
1882 n = journal_file_entry_n_items(o);
1883 for (i = 0; i < n; i++) {
1889 p = le64toh(o->entry.items[i].object_offset);
1890 le_hash = o->entry.items[i].hash;
1891 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1895 if (le_hash != o->data.hash)
1898 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1900 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1902 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1903 if (decompress_startswith(compression,
1905 &f->compress_buffer, &f->compress_buffer_size,
1906 field, field_length, '=')) {
1910 r = decompress_blob(compression,
1912 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1917 *data = f->compress_buffer;
1918 *size = (size_t) rsize;
1923 return -EPROTONOSUPPORT;
1925 } else if (l >= field_length+1 &&
1926 memcmp(o->data.payload, field, field_length) == 0 &&
1927 o->data.payload[field_length] == '=') {
1931 if ((uint64_t) t != l)
1934 *data = o->data.payload;
1940 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1948 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
1953 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1956 /* We can't read objects larger than 4G on a 32bit machine */
1957 if ((uint64_t) t != l)
1960 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1962 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1966 r = decompress_blob(compression,
1967 o->data.payload, l, &f->compress_buffer,
1968 &f->compress_buffer_size, &rsize, j->data_threshold);
1972 *data = f->compress_buffer;
1973 *size = (size_t) rsize;
1975 return -EPROTONOSUPPORT;
1978 *data = o->data.payload;
1985 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1992 assert_return(j, -EINVAL);
1993 assert_return(!journal_pid_changed(j), -ECHILD);
1994 assert_return(data, -EINVAL);
1995 assert_return(size, -EINVAL);
1997 f = j->current_file;
1999 return -EADDRNOTAVAIL;
2001 if (f->current_offset <= 0)
2002 return -EADDRNOTAVAIL;
2004 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2008 n = journal_file_entry_n_items(o);
2009 if (j->current_field >= n)
2012 p = le64toh(o->entry.items[j->current_field].object_offset);
2013 le_hash = o->entry.items[j->current_field].hash;
2014 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2018 if (le_hash != o->data.hash)
2021 r = return_data(j, f, o, data, size);
2025 j->current_field ++;
2030 _public_ void sd_journal_restart_data(sd_journal *j) {
2034 j->current_field = 0;
2037 _public_ int sd_journal_get_fd(sd_journal *j) {
2040 assert_return(j, -EINVAL);
2041 assert_return(!journal_pid_changed(j), -ECHILD);
2043 if (j->inotify_fd >= 0)
2044 return j->inotify_fd;
2046 r = allocate_inotify(j);
2050 /* Iterate through all dirs again, to add them to the
2052 if (j->no_new_files)
2053 r = add_current_paths(j);
2055 r = add_root_directory(j, j->path);
2057 r = add_search_paths(j);
2061 return j->inotify_fd;
2064 _public_ int sd_journal_get_events(sd_journal *j) {
2067 assert_return(j, -EINVAL);
2068 assert_return(!journal_pid_changed(j), -ECHILD);
2070 fd = sd_journal_get_fd(j);
2077 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2080 assert_return(j, -EINVAL);
2081 assert_return(!journal_pid_changed(j), -ECHILD);
2082 assert_return(timeout_usec, -EINVAL);
2084 fd = sd_journal_get_fd(j);
2088 if (!j->on_network) {
2089 *timeout_usec = (uint64_t) -1;
2093 /* If we are on the network we need to regularly check for
2094 * changes manually */
2096 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2100 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2107 /* Is this a subdirectory we watch? */
2108 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2112 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2113 (endswith(e->name, ".journal") ||
2114 endswith(e->name, ".journal~"))) {
2116 /* Event for a journal file */
2118 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2119 r = add_file(j, d->path, e->name);
2121 log_debug_errno(r, "Failed to add file %s/%s: %m",
2123 set_put_error(j, r);
2126 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2128 r = remove_file(j, d->path, e->name);
2130 log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
2133 } else if (!d->is_root && e->len == 0) {
2135 /* Event for a subdirectory */
2137 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2138 r = remove_directory(j, d);
2140 log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
2144 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2146 /* Event for root directory */
2148 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2149 r = add_directory(j, d->path, e->name);
2151 log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
2158 if (e->mask & IN_IGNORED)
2161 log_warning("Unknown inotify event.");
2164 static int determine_change(sd_journal *j) {
2169 b = j->current_invalidate_counter != j->last_invalidate_counter;
2170 j->last_invalidate_counter = j->current_invalidate_counter;
2172 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2175 _public_ int sd_journal_process(sd_journal *j) {
2176 bool got_something = false;
2178 assert_return(j, -EINVAL);
2179 assert_return(!journal_pid_changed(j), -ECHILD);
2181 j->last_process_usec = now(CLOCK_MONOTONIC);
2184 union inotify_event_buffer buffer;
2185 struct inotify_event *e;
2188 l = read(j->inotify_fd, &buffer, sizeof(buffer));
2190 if (errno == EAGAIN || errno == EINTR)
2191 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2196 got_something = true;
2198 FOREACH_INOTIFY_EVENT(e, buffer, l)
2199 process_inotify_event(j, e);
2203 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2207 assert_return(j, -EINVAL);
2208 assert_return(!journal_pid_changed(j), -ECHILD);
2210 if (j->inotify_fd < 0) {
2212 /* This is the first invocation, hence create the
2214 r = sd_journal_get_fd(j);
2218 /* The journal might have changed since the context
2219 * object was created and we weren't watching before,
2220 * hence don't wait for anything, and return
2222 return determine_change(j);
2225 r = sd_journal_get_timeout(j, &t);
2229 if (t != (uint64_t) -1) {
2232 n = now(CLOCK_MONOTONIC);
2233 t = t > n ? t - n : 0;
2235 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2240 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2241 } while (r == -EINTR);
2246 return sd_journal_process(j);
2249 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2253 uint64_t fmin = 0, tmax = 0;
2256 assert_return(j, -EINVAL);
2257 assert_return(!journal_pid_changed(j), -ECHILD);
2258 assert_return(from || to, -EINVAL);
2259 assert_return(from != to, -EINVAL);
2261 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2264 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2277 fmin = MIN(fr, fmin);
2278 tmax = MAX(t, tmax);
2287 return first ? 0 : 1;
2290 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2296 assert_return(j, -EINVAL);
2297 assert_return(!journal_pid_changed(j), -ECHILD);
2298 assert_return(from || to, -EINVAL);
2299 assert_return(from != to, -EINVAL);
2301 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2304 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2314 *from = MIN(fr, *from);
2329 void journal_print_header(sd_journal *j) {
2332 bool newline = false;
2336 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2342 journal_file_print_header(f);
2346 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2351 assert_return(j, -EINVAL);
2352 assert_return(!journal_pid_changed(j), -ECHILD);
2353 assert_return(bytes, -EINVAL);
2355 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2358 if (fstat(f->fd, &st) < 0)
2361 sum += (uint64_t) st.st_blocks * 512ULL;
2368 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2371 assert_return(j, -EINVAL);
2372 assert_return(!journal_pid_changed(j), -ECHILD);
2373 assert_return(!isempty(field), -EINVAL);
2374 assert_return(field_is_valid(field), -EINVAL);
2380 free(j->unique_field);
2381 j->unique_field = f;
2382 j->unique_file = NULL;
2383 j->unique_offset = 0;
2384 j->unique_file_lost = false;
2389 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2392 assert_return(j, -EINVAL);
2393 assert_return(!journal_pid_changed(j), -ECHILD);
2394 assert_return(data, -EINVAL);
2395 assert_return(l, -EINVAL);
2396 assert_return(j->unique_field, -EINVAL);
2398 k = strlen(j->unique_field);
2400 if (!j->unique_file) {
2401 if (j->unique_file_lost)
2404 j->unique_file = ordered_hashmap_first(j->files);
2405 if (!j->unique_file)
2408 j->unique_offset = 0;
2420 /* Proceed to next data object in the field's linked list */
2421 if (j->unique_offset == 0) {
2422 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2426 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2428 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2432 j->unique_offset = le64toh(o->data.next_field_offset);
2435 /* We reached the end of the list? Then start again, with the next file */
2436 if (j->unique_offset == 0) {
2437 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
2438 if (!j->unique_file)
2444 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2445 * instead, so that we can look at this data object at the same
2446 * time as one on another file */
2447 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
2451 /* Let's do the type check by hand, since we used 0 context above. */
2452 if (o->object.type != OBJECT_DATA) {
2453 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
2454 j->unique_file->path, j->unique_offset,
2455 o->object.type, OBJECT_DATA);
2459 r = return_data(j, j->unique_file, o, &odata, &ol);
2463 /* Check if we have at least the field name and "=". */
2465 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2466 j->unique_file->path, j->unique_offset,
2471 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2472 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2473 j->unique_file->path, j->unique_offset,
2478 /* OK, now let's see if we already returned this data
2479 * object by checking if it exists in the earlier
2480 * traversed files. */
2482 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2486 if (of == j->unique_file)
2489 /* Skip this file it didn't have any fields
2491 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2492 le64toh(of->header->n_fields) <= 0)
2495 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2506 r = return_data(j, j->unique_file, o, data, l);
2514 _public_ void sd_journal_restart_unique(sd_journal *j) {
2518 j->unique_file = NULL;
2519 j->unique_offset = 0;
2520 j->unique_file_lost = false;
2523 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2524 assert_return(j, -EINVAL);
2525 assert_return(!journal_pid_changed(j), -ECHILD);
2527 return !j->on_network;
2530 static char *lookup_field(const char *field, void *userdata) {
2531 sd_journal *j = userdata;
2539 r = sd_journal_get_data(j, field, &data, &size);
2541 size > REPLACE_VAR_MAX)
2542 return strdup(field);
2544 d = strlen(field) + 1;
2546 return strndup((const char*) data + d, size - d);
2549 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2553 _cleanup_free_ char *text = NULL, *cid = NULL;
2557 assert_return(j, -EINVAL);
2558 assert_return(!journal_pid_changed(j), -ECHILD);
2559 assert_return(ret, -EINVAL);
2561 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2565 cid = strndup((const char*) data + 11, size - 11);
2569 r = sd_id128_from_string(cid, &id);
2573 r = catalog_get(CATALOG_DATABASE, id, &text);
2577 t = replace_var(text, lookup_field, j);
2585 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2586 assert_return(ret, -EINVAL);
2588 return catalog_get(CATALOG_DATABASE, id, ret);
2591 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2592 assert_return(j, -EINVAL);
2593 assert_return(!journal_pid_changed(j), -ECHILD);
2595 j->data_threshold = sz;
2599 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2600 assert_return(j, -EINVAL);
2601 assert_return(!journal_pid_changed(j), -ECHILD);
2602 assert_return(sz, -EINVAL);
2604 *sz = j->data_threshold;