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 void remove_file_real(sd_journal *j, JournalFile *f);
56 static bool journal_pid_changed(sd_journal *j) {
59 /* We don't support people creating a journal object and
60 * keeping it around over a fork(). Let's complain. */
62 return j->original_pid != getpid();
65 /* We return an error here only if we didn't manage to
66 memorize the real error. */
67 static int set_put_error(sd_journal *j, int r) {
73 k = set_ensure_allocated(&j->errors, NULL);
77 return set_put(j->errors, INT_TO_PTR(r));
80 static void detach_location(sd_journal *j) {
86 j->current_file = NULL;
89 ORDERED_HASHMAP_FOREACH(f, j->files, i)
90 journal_file_reset_location(f);
93 static void reset_location(sd_journal *j) {
97 zero(j->current_location);
100 static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
102 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
104 assert(o->object.type == OBJECT_ENTRY);
107 l->seqnum = le64toh(o->entry.seqnum);
108 l->seqnum_id = f->header->seqnum_id;
109 l->realtime = le64toh(o->entry.realtime);
110 l->monotonic = le64toh(o->entry.monotonic);
111 l->boot_id = o->entry.boot_id;
112 l->xor_hash = le64toh(o->entry.xor_hash);
114 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
117 static void set_location(sd_journal *j, JournalFile *f, Object *o) {
122 init_location(&j->current_location, LOCATION_DISCRETE, f, o);
125 j->current_field = 0;
127 /* Let f know its candidate entry was picked. */
128 assert(f->location_type == LOCATION_SEEK);
129 f->location_type = LOCATION_DISCRETE;
132 static int match_is_valid(const void *data, size_t size) {
140 if (startswith(data, "__"))
144 for (p = b; p < b + size; p++) {
152 if (*p >= 'A' && *p <= 'Z')
155 if (*p >= '0' && *p <= '9')
164 static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
165 const uint8_t *a = _a, *b = _b;
168 for (j = 0; j < s && j < t; j++) {
177 assert_not_reached("\"=\" not found");
180 static Match *match_new(Match *p, MatchType t) {
191 LIST_PREPEND(matches, p->matches, m);
197 static void match_free(Match *m) {
201 match_free(m->matches);
204 LIST_REMOVE(matches, m->parent->matches, m);
210 static void match_free_if_empty(Match *m) {
211 if (!m || m->matches)
217 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
218 Match *l3, *l4, *add_here = NULL, *m;
221 assert_return(j, -EINVAL);
222 assert_return(!journal_pid_changed(j), -ECHILD);
223 assert_return(data, -EINVAL);
228 assert_return(match_is_valid(data, size), -EINVAL);
234 * level 4: concrete matches */
237 j->level0 = match_new(NULL, MATCH_AND_TERM);
243 j->level1 = match_new(j->level0, MATCH_OR_TERM);
249 j->level2 = match_new(j->level1, MATCH_AND_TERM);
254 assert(j->level0->type == MATCH_AND_TERM);
255 assert(j->level1->type == MATCH_OR_TERM);
256 assert(j->level2->type == MATCH_AND_TERM);
258 le_hash = htole64(hash64(data, size));
260 LIST_FOREACH(matches, l3, j->level2->matches) {
261 assert(l3->type == MATCH_OR_TERM);
263 LIST_FOREACH(matches, l4, l3->matches) {
264 assert(l4->type == MATCH_DISCRETE);
266 /* Exactly the same match already? Then ignore
268 if (l4->le_hash == le_hash &&
270 memcmp(l4->data, data, size) == 0)
273 /* Same field? Then let's add this to this OR term */
274 if (same_field(data, size, l4->data, l4->size)) {
285 add_here = match_new(j->level2, MATCH_OR_TERM);
290 m = match_new(add_here, MATCH_DISCRETE);
294 m->le_hash = le_hash;
296 m->data = memdup(data, size);
305 match_free_if_empty(add_here);
306 match_free_if_empty(j->level2);
307 match_free_if_empty(j->level1);
308 match_free_if_empty(j->level0);
313 _public_ int sd_journal_add_conjunction(sd_journal *j) {
314 assert_return(j, -EINVAL);
315 assert_return(!journal_pid_changed(j), -ECHILD);
323 if (!j->level1->matches)
332 _public_ int sd_journal_add_disjunction(sd_journal *j) {
333 assert_return(j, -EINVAL);
334 assert_return(!journal_pid_changed(j), -ECHILD);
345 if (!j->level2->matches)
352 static char *match_make_string(Match *m) {
355 bool enclose = false;
358 return strdup("none");
360 if (m->type == MATCH_DISCRETE)
361 return strndup(m->data, m->size);
364 LIST_FOREACH(matches, i, m->matches) {
367 t = match_make_string(i);
374 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
389 r = strjoin("(", p, ")", NULL);
397 char *journal_make_match_string(sd_journal *j) {
400 return match_make_string(j->level0);
403 _public_ void sd_journal_flush_matches(sd_journal *j) {
408 match_free(j->level0);
410 j->level0 = j->level1 = j->level2 = NULL;
415 _pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
421 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
423 if (l->monotonic_set &&
424 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
426 le64toh(ao->entry.realtime) == l->realtime &&
428 le64toh(ao->entry.xor_hash) == l->xor_hash)
432 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
434 a = le64toh(ao->entry.seqnum);
442 if (l->monotonic_set &&
443 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
445 a = le64toh(ao->entry.monotonic);
447 if (a < l->monotonic)
449 if (a > l->monotonic)
453 if (l->realtime_set) {
455 a = le64toh(ao->entry.realtime);
463 if (l->xor_hash_set) {
464 a = le64toh(ao->entry.xor_hash);
475 static int next_for_match(
479 uint64_t after_offset,
480 direction_t direction,
492 if (m->type == MATCH_DISCRETE) {
495 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
499 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
501 } else if (m->type == MATCH_OR_TERM) {
504 /* Find the earliest match beyond after_offset */
506 LIST_FOREACH(matches, i, m->matches) {
509 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
513 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
521 } else if (m->type == MATCH_AND_TERM) {
522 Match *i, *last_moved;
524 /* Always jump to the next matching entry and repeat
525 * this until we find an offset that matches for all
531 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
535 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
536 last_moved = m->matches;
538 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
541 r = next_for_match(j, i, f, np, direction, NULL, &cp);
545 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
546 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
555 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
567 static int find_location_for_match(
571 direction_t direction,
581 if (m->type == MATCH_DISCRETE) {
584 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
588 /* FIXME: missing: find by monotonic */
590 if (j->current_location.type == LOCATION_HEAD)
591 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
592 if (j->current_location.type == LOCATION_TAIL)
593 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
594 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
595 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
596 if (j->current_location.monotonic_set) {
597 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
601 if (j->current_location.realtime_set)
602 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
604 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
606 } else if (m->type == MATCH_OR_TERM) {
611 /* Find the earliest match */
613 LIST_FOREACH(matches, i, m->matches) {
616 r = find_location_for_match(j, i, f, direction, NULL, &cp);
620 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
628 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
643 assert(m->type == MATCH_AND_TERM);
645 /* First jump to the last match, and then find the
646 * next one where all matches match */
651 LIST_FOREACH(matches, i, m->matches) {
654 r = find_location_for_match(j, i, f, direction, NULL, &cp);
658 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
662 return next_for_match(j, m, f, np, direction, ret, offset);
666 static int find_location_with_matches(
669 direction_t direction,
681 /* No matches is simple */
683 if (j->current_location.type == LOCATION_HEAD)
684 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
685 if (j->current_location.type == LOCATION_TAIL)
686 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
687 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
688 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
689 if (j->current_location.monotonic_set) {
690 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
694 if (j->current_location.realtime_set)
695 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
697 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
699 return find_location_for_match(j, j->level0, f, direction, ret, offset);
702 static int next_with_matches(
705 direction_t direction,
720 /* No matches is easy. We simple advance the file
723 return journal_file_next_entry(f, c, cp, direction, ret, offset);
725 /* If we have a match then we look for the next matching entry
726 * with an offset at least one step larger */
727 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
730 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
738 if (f->last_direction == direction && f->current_offset > 0) {
739 cp = f->current_offset;
741 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
745 r = next_with_matches(j, f, direction, &c, &cp);
749 r = find_location_with_matches(j, f, direction, &c, &cp);
754 /* OK, we found the spot, now let's advance until an entry
755 * that is actually different from what we were previously
756 * looking at. This is necessary to handle entries which exist
757 * in two (or more) journal files, and which shall all be
758 * suppressed but one. */
763 if (j->current_location.type == LOCATION_DISCRETE) {
766 k = compare_with_location(f, c, &j->current_location);
768 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
773 journal_file_save_location(f, direction, c, cp);
782 r = next_with_matches(j, f, direction, &c, &cp);
788 static int real_journal_next(sd_journal *j, direction_t direction) {
789 JournalFile *f, *new_file = NULL;
790 uint64_t new_offset = 0;
796 assert_return(j, -EINVAL);
797 assert_return(!journal_pid_changed(j), -ECHILD);
799 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
802 r = next_beyond_location(j, f, direction, &o, &p);
804 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
805 remove_file_real(j, f);
808 f->location_type = LOCATION_TAIL;
817 k = journal_file_compare_locations(f, new_file);
819 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
831 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
835 set_location(j, new_file, o);
840 _public_ int sd_journal_next(sd_journal *j) {
841 return real_journal_next(j, DIRECTION_DOWN);
844 _public_ int sd_journal_previous(sd_journal *j) {
845 return real_journal_next(j, DIRECTION_UP);
848 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
851 assert_return(j, -EINVAL);
852 assert_return(!journal_pid_changed(j), -ECHILD);
855 /* If this is not a discrete skip, then at least
856 * resolve the current location */
857 if (j->current_location.type != LOCATION_DISCRETE)
858 return real_journal_next(j, direction);
864 r = real_journal_next(j, direction);
878 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
879 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
882 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
883 return real_journal_next_skip(j, DIRECTION_UP, skip);
886 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
889 char bid[33], sid[33];
891 assert_return(j, -EINVAL);
892 assert_return(!journal_pid_changed(j), -ECHILD);
893 assert_return(cursor, -EINVAL);
895 if (!j->current_file || j->current_file->current_offset <= 0)
896 return -EADDRNOTAVAIL;
898 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
902 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
903 sd_id128_to_string(o->entry.boot_id, bid);
906 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
907 sid, le64toh(o->entry.seqnum),
908 bid, le64toh(o->entry.monotonic),
909 le64toh(o->entry.realtime),
910 le64toh(o->entry.xor_hash)) < 0)
916 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
917 const char *word, *state;
919 unsigned long long seqnum, monotonic, realtime, xor_hash;
921 seqnum_id_set = false,
924 monotonic_set = false,
925 realtime_set = false,
926 xor_hash_set = false;
927 sd_id128_t seqnum_id, boot_id;
929 assert_return(j, -EINVAL);
930 assert_return(!journal_pid_changed(j), -ECHILD);
931 assert_return(!isempty(cursor), -EINVAL);
933 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
937 if (l < 2 || word[1] != '=')
940 item = strndup(word, l);
947 seqnum_id_set = true;
948 k = sd_id128_from_string(item+2, &seqnum_id);
953 if (sscanf(item+2, "%llx", &seqnum) != 1)
959 k = sd_id128_from_string(item+2, &boot_id);
963 monotonic_set = true;
964 if (sscanf(item+2, "%llx", &monotonic) != 1)
970 if (sscanf(item+2, "%llx", &realtime) != 1)
976 if (sscanf(item+2, "%llx", &xor_hash) != 1)
987 if ((!seqnum_set || !seqnum_id_set) &&
988 (!monotonic_set || !boot_id_set) &&
994 j->current_location.type = LOCATION_SEEK;
997 j->current_location.realtime = (uint64_t) realtime;
998 j->current_location.realtime_set = true;
1001 if (seqnum_set && seqnum_id_set) {
1002 j->current_location.seqnum = (uint64_t) seqnum;
1003 j->current_location.seqnum_id = seqnum_id;
1004 j->current_location.seqnum_set = true;
1007 if (monotonic_set && boot_id_set) {
1008 j->current_location.monotonic = (uint64_t) monotonic;
1009 j->current_location.boot_id = boot_id;
1010 j->current_location.monotonic_set = true;
1014 j->current_location.xor_hash = (uint64_t) xor_hash;
1015 j->current_location.xor_hash_set = true;
1021 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1023 const char *word, *state;
1027 assert_return(j, -EINVAL);
1028 assert_return(!journal_pid_changed(j), -ECHILD);
1029 assert_return(!isempty(cursor), -EINVAL);
1031 if (!j->current_file || j->current_file->current_offset <= 0)
1032 return -EADDRNOTAVAIL;
1034 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1038 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1039 _cleanup_free_ char *item = NULL;
1041 unsigned long long ll;
1044 if (l < 2 || word[1] != '=')
1047 item = strndup(word, l);
1054 k = sd_id128_from_string(item+2, &id);
1057 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1062 if (sscanf(item+2, "%llx", &ll) != 1)
1064 if (ll != le64toh(o->entry.seqnum))
1069 k = sd_id128_from_string(item+2, &id);
1072 if (!sd_id128_equal(id, o->entry.boot_id))
1077 if (sscanf(item+2, "%llx", &ll) != 1)
1079 if (ll != le64toh(o->entry.monotonic))
1084 if (sscanf(item+2, "%llx", &ll) != 1)
1086 if (ll != le64toh(o->entry.realtime))
1091 if (sscanf(item+2, "%llx", &ll) != 1)
1093 if (ll != le64toh(o->entry.xor_hash))
1103 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1104 assert_return(j, -EINVAL);
1105 assert_return(!journal_pid_changed(j), -ECHILD);
1108 j->current_location.type = LOCATION_SEEK;
1109 j->current_location.boot_id = boot_id;
1110 j->current_location.monotonic = usec;
1111 j->current_location.monotonic_set = true;
1116 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1117 assert_return(j, -EINVAL);
1118 assert_return(!journal_pid_changed(j), -ECHILD);
1121 j->current_location.type = LOCATION_SEEK;
1122 j->current_location.realtime = usec;
1123 j->current_location.realtime_set = true;
1128 _public_ int sd_journal_seek_head(sd_journal *j) {
1129 assert_return(j, -EINVAL);
1130 assert_return(!journal_pid_changed(j), -ECHILD);
1133 j->current_location.type = LOCATION_HEAD;
1138 _public_ int sd_journal_seek_tail(sd_journal *j) {
1139 assert_return(j, -EINVAL);
1140 assert_return(!journal_pid_changed(j), -ECHILD);
1143 j->current_location.type = LOCATION_TAIL;
1148 static void check_network(sd_journal *j, int fd) {
1156 if (fstatfs(fd, &sfs) < 0)
1160 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1161 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1162 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1163 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1164 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1167 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1168 const char *full, *tilded, *atted;
1170 full = strappenda(prefix, ".journal");
1171 tilded = strappenda(full, "~");
1172 atted = strappenda(prefix, "@");
1174 return streq(filename, full) ||
1175 streq(filename, tilded) ||
1176 startswith(filename, atted);
1179 static bool file_type_wanted(int flags, const char *filename) {
1180 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1183 /* no flags set → every type is OK */
1184 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1187 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1190 if (flags & SD_JOURNAL_CURRENT_USER) {
1191 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1193 assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid())
1194 < (int) sizeof(prefix));
1196 if (file_has_type_prefix(prefix, filename))
1203 static int add_any_file(sd_journal *j, const char *path) {
1204 JournalFile *f = NULL;
1210 if (ordered_hashmap_get(j->files, path))
1213 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1214 log_warning("Too many open journal files, not adding %s.", path);
1215 return set_put_error(j, -ETOOMANYREFS);
1218 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1222 /* journal_file_dump(f); */
1224 r = ordered_hashmap_put(j->files, f->path, f);
1226 journal_file_close(f);
1230 log_debug("File %s added.", f->path);
1232 check_network(j, f->fd);
1234 j->current_invalidate_counter ++;
1239 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1240 _cleanup_free_ char *path = NULL;
1247 if (j->no_new_files ||
1248 !file_type_wanted(j->flags, filename))
1251 path = strjoin(prefix, "/", filename, NULL);
1255 r = add_any_file(j, path);
1261 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1262 _cleanup_free_ char *path;
1269 path = strjoin(prefix, "/", filename, NULL);
1273 f = ordered_hashmap_get(j->files, path);
1277 remove_file_real(j, f);
1281 static void remove_file_real(sd_journal *j, JournalFile *f) {
1285 ordered_hashmap_remove(j->files, f->path);
1287 log_debug("File %s removed.", f->path);
1289 if (j->current_file == f) {
1290 j->current_file = NULL;
1291 j->current_field = 0;
1294 if (j->unique_file == f) {
1295 /* Jump to the next unique_file or NULL if that one was last */
1296 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
1297 j->unique_offset = 0;
1298 if (!j->unique_file)
1299 j->unique_file_lost = true;
1302 journal_file_close(f);
1304 j->current_invalidate_counter ++;
1307 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1308 _cleanup_free_ char *path = NULL;
1310 _cleanup_closedir_ DIR *d = NULL;
1318 log_debug("Considering %s/%s.", prefix, dirname);
1320 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1321 (sd_id128_from_string(dirname, &id) < 0 ||
1322 sd_id128_get_machine(&mid) < 0 ||
1323 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1326 path = strjoin(prefix, "/", dirname, NULL);
1332 log_debug_errno(errno, "Failed to open %s: %m", path);
1333 if (errno == ENOENT)
1338 m = hashmap_get(j->directories_by_path, path);
1340 m = new0(Directory, 1);
1347 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1352 path = NULL; /* avoid freeing in cleanup */
1353 j->current_invalidate_counter ++;
1355 log_debug("Directory %s added.", m->path);
1357 } else if (m->is_root)
1360 if (m->wd <= 0 && j->inotify_fd >= 0) {
1362 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1363 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1364 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1367 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1368 inotify_rm_watch(j->inotify_fd, m->wd);
1376 if (!de && errno != 0) {
1378 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1384 if (dirent_is_file_with_suffix(de, ".journal") ||
1385 dirent_is_file_with_suffix(de, ".journal~")) {
1386 r = add_file(j, m->path, de->d_name);
1388 log_debug_errno(r, "Failed to add file %s/%s: %m",
1389 m->path, de->d_name);
1390 r = set_put_error(j, r);
1397 check_network(j, dirfd(d));
1402 static int add_root_directory(sd_journal *j, const char *p) {
1403 _cleanup_closedir_ DIR *d = NULL;
1410 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1411 !path_startswith(p, "/run"))
1415 p = strappenda(j->prefix, p);
1421 m = hashmap_get(j->directories_by_path, p);
1423 m = new0(Directory, 1);
1428 m->path = strdup(p);
1434 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1440 j->current_invalidate_counter ++;
1442 log_debug("Root directory %s added.", m->path);
1444 } else if (!m->is_root)
1447 if (m->wd <= 0 && j->inotify_fd >= 0) {
1449 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1450 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1453 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1454 inotify_rm_watch(j->inotify_fd, m->wd);
1457 if (j->no_new_files)
1466 if (!de && errno != 0) {
1468 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1474 if (dirent_is_file_with_suffix(de, ".journal") ||
1475 dirent_is_file_with_suffix(de, ".journal~")) {
1476 r = add_file(j, m->path, de->d_name);
1478 log_debug_errno(r, "Failed to add file %s/%s: %m",
1479 m->path, de->d_name);
1480 r = set_put_error(j, r);
1484 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1485 sd_id128_from_string(de->d_name, &id) >= 0) {
1487 r = add_directory(j, m->path, de->d_name);
1489 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
1493 check_network(j, dirfd(d));
1498 static int remove_directory(sd_journal *j, Directory *d) {
1502 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1504 if (j->inotify_fd >= 0)
1505 inotify_rm_watch(j->inotify_fd, d->wd);
1508 hashmap_remove(j->directories_by_path, d->path);
1511 log_debug("Root directory %s removed.", d->path);
1513 log_debug("Directory %s removed.", d->path);
1521 static int add_search_paths(sd_journal *j) {
1523 const char search_paths[] =
1524 "/run/log/journal\0"
1525 "/var/log/journal\0";
1530 /* We ignore most errors here, since the idea is to only open
1531 * what's actually accessible, and ignore the rest. */
1533 NULSTR_FOREACH(p, search_paths) {
1534 r = add_root_directory(j, p);
1535 if (r < 0 && r != -ENOENT) {
1536 r = set_put_error(j, r);
1545 static int add_current_paths(sd_journal *j) {
1550 assert(j->no_new_files);
1552 /* Simply adds all directories for files we have open as
1553 * "root" directories. We don't expect errors here, so we
1554 * treat them as fatal. */
1556 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
1557 _cleanup_free_ char *dir;
1560 dir = dirname_malloc(f->path);
1564 r = add_root_directory(j, dir);
1566 set_put_error(j, r);
1575 static int allocate_inotify(sd_journal *j) {
1578 if (j->inotify_fd < 0) {
1579 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1580 if (j->inotify_fd < 0)
1584 if (!j->directories_by_wd) {
1585 j->directories_by_wd = hashmap_new(NULL);
1586 if (!j->directories_by_wd)
1593 static sd_journal *journal_new(int flags, const char *path) {
1596 j = new0(sd_journal, 1);
1600 j->original_pid = getpid();
1603 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1606 j->path = strdup(path);
1611 j->files = ordered_hashmap_new(&string_hash_ops);
1612 j->directories_by_path = hashmap_new(&string_hash_ops);
1613 j->mmap = mmap_cache_new();
1614 if (!j->files || !j->directories_by_path || !j->mmap)
1620 sd_journal_close(j);
1624 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1628 assert_return(ret, -EINVAL);
1629 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1631 j = journal_new(flags, NULL);
1635 r = add_search_paths(j);
1643 sd_journal_close(j);
1648 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1649 _cleanup_free_ char *root = NULL, *class = NULL;
1654 assert_return(machine, -EINVAL);
1655 assert_return(ret, -EINVAL);
1656 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1657 assert_return(machine_name_is_valid(machine), -EINVAL);
1659 p = strappenda("/run/systemd/machines/", machine);
1660 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1668 if (!streq_ptr(class, "container"))
1671 j = journal_new(flags, NULL);
1678 r = add_search_paths(j);
1686 sd_journal_close(j);
1690 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1694 assert_return(ret, -EINVAL);
1695 assert_return(path, -EINVAL);
1696 assert_return(flags == 0, -EINVAL);
1698 j = journal_new(flags, path);
1702 r = add_root_directory(j, path);
1704 set_put_error(j, r);
1712 sd_journal_close(j);
1717 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1722 assert_return(ret, -EINVAL);
1723 assert_return(flags == 0, -EINVAL);
1725 j = journal_new(flags, NULL);
1729 STRV_FOREACH(path, paths) {
1730 r = add_any_file(j, *path);
1732 log_error_errno(r, "Failed to open %s: %m", *path);
1737 j->no_new_files = true;
1743 sd_journal_close(j);
1748 _public_ void sd_journal_close(sd_journal *j) {
1755 sd_journal_flush_matches(j);
1757 while ((f = ordered_hashmap_steal_first(j->files)))
1758 journal_file_close(f);
1760 ordered_hashmap_free(j->files);
1762 while ((d = hashmap_first(j->directories_by_path)))
1763 remove_directory(j, d);
1765 while ((d = hashmap_first(j->directories_by_wd)))
1766 remove_directory(j, d);
1768 hashmap_free(j->directories_by_path);
1769 hashmap_free(j->directories_by_wd);
1771 safe_close(j->inotify_fd);
1774 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1775 mmap_cache_unref(j->mmap);
1780 free(j->unique_field);
1781 set_free(j->errors);
1785 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1790 assert_return(j, -EINVAL);
1791 assert_return(!journal_pid_changed(j), -ECHILD);
1792 assert_return(ret, -EINVAL);
1794 f = j->current_file;
1796 return -EADDRNOTAVAIL;
1798 if (f->current_offset <= 0)
1799 return -EADDRNOTAVAIL;
1801 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1805 *ret = le64toh(o->entry.realtime);
1809 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1815 assert_return(j, -EINVAL);
1816 assert_return(!journal_pid_changed(j), -ECHILD);
1818 f = j->current_file;
1820 return -EADDRNOTAVAIL;
1822 if (f->current_offset <= 0)
1823 return -EADDRNOTAVAIL;
1825 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1830 *ret_boot_id = o->entry.boot_id;
1832 r = sd_id128_get_boot(&id);
1836 if (!sd_id128_equal(id, o->entry.boot_id))
1841 *ret = le64toh(o->entry.monotonic);
1846 static bool field_is_valid(const char *field) {
1854 if (startswith(field, "__"))
1857 for (p = field; *p; p++) {
1862 if (*p >= 'A' && *p <= 'Z')
1865 if (*p >= '0' && *p <= '9')
1874 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1877 size_t field_length;
1881 assert_return(j, -EINVAL);
1882 assert_return(!journal_pid_changed(j), -ECHILD);
1883 assert_return(field, -EINVAL);
1884 assert_return(data, -EINVAL);
1885 assert_return(size, -EINVAL);
1886 assert_return(field_is_valid(field), -EINVAL);
1888 f = j->current_file;
1890 return -EADDRNOTAVAIL;
1892 if (f->current_offset <= 0)
1893 return -EADDRNOTAVAIL;
1895 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1899 field_length = strlen(field);
1901 n = journal_file_entry_n_items(o);
1902 for (i = 0; i < n; i++) {
1908 p = le64toh(o->entry.items[i].object_offset);
1909 le_hash = o->entry.items[i].hash;
1910 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1914 if (le_hash != o->data.hash)
1917 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1919 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1921 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1922 if (decompress_startswith(compression,
1924 &f->compress_buffer, &f->compress_buffer_size,
1925 field, field_length, '=')) {
1929 r = decompress_blob(compression,
1931 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1936 *data = f->compress_buffer;
1937 *size = (size_t) rsize;
1942 return -EPROTONOSUPPORT;
1944 } else if (l >= field_length+1 &&
1945 memcmp(o->data.payload, field, field_length) == 0 &&
1946 o->data.payload[field_length] == '=') {
1950 if ((uint64_t) t != l)
1953 *data = o->data.payload;
1959 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1967 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
1972 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1975 /* We can't read objects larger than 4G on a 32bit machine */
1976 if ((uint64_t) t != l)
1979 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1981 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1985 r = decompress_blob(compression,
1986 o->data.payload, l, &f->compress_buffer,
1987 &f->compress_buffer_size, &rsize, j->data_threshold);
1991 *data = f->compress_buffer;
1992 *size = (size_t) rsize;
1994 return -EPROTONOSUPPORT;
1997 *data = o->data.payload;
2004 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2011 assert_return(j, -EINVAL);
2012 assert_return(!journal_pid_changed(j), -ECHILD);
2013 assert_return(data, -EINVAL);
2014 assert_return(size, -EINVAL);
2016 f = j->current_file;
2018 return -EADDRNOTAVAIL;
2020 if (f->current_offset <= 0)
2021 return -EADDRNOTAVAIL;
2023 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2027 n = journal_file_entry_n_items(o);
2028 if (j->current_field >= n)
2031 p = le64toh(o->entry.items[j->current_field].object_offset);
2032 le_hash = o->entry.items[j->current_field].hash;
2033 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2037 if (le_hash != o->data.hash)
2040 r = return_data(j, f, o, data, size);
2044 j->current_field ++;
2049 _public_ void sd_journal_restart_data(sd_journal *j) {
2053 j->current_field = 0;
2056 _public_ int sd_journal_get_fd(sd_journal *j) {
2059 assert_return(j, -EINVAL);
2060 assert_return(!journal_pid_changed(j), -ECHILD);
2062 if (j->inotify_fd >= 0)
2063 return j->inotify_fd;
2065 r = allocate_inotify(j);
2069 /* Iterate through all dirs again, to add them to the
2071 if (j->no_new_files)
2072 r = add_current_paths(j);
2074 r = add_root_directory(j, j->path);
2076 r = add_search_paths(j);
2080 return j->inotify_fd;
2083 _public_ int sd_journal_get_events(sd_journal *j) {
2086 assert_return(j, -EINVAL);
2087 assert_return(!journal_pid_changed(j), -ECHILD);
2089 fd = sd_journal_get_fd(j);
2096 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2099 assert_return(j, -EINVAL);
2100 assert_return(!journal_pid_changed(j), -ECHILD);
2101 assert_return(timeout_usec, -EINVAL);
2103 fd = sd_journal_get_fd(j);
2107 if (!j->on_network) {
2108 *timeout_usec = (uint64_t) -1;
2112 /* If we are on the network we need to regularly check for
2113 * changes manually */
2115 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2119 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2126 /* Is this a subdirectory we watch? */
2127 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2131 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2132 (endswith(e->name, ".journal") ||
2133 endswith(e->name, ".journal~"))) {
2135 /* Event for a journal file */
2137 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2138 r = add_file(j, d->path, e->name);
2140 log_debug_errno(r, "Failed to add file %s/%s: %m",
2142 set_put_error(j, r);
2145 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2147 r = remove_file(j, d->path, e->name);
2149 log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
2152 } else if (!d->is_root && e->len == 0) {
2154 /* Event for a subdirectory */
2156 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2157 r = remove_directory(j, d);
2159 log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
2163 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2165 /* Event for root directory */
2167 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2168 r = add_directory(j, d->path, e->name);
2170 log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
2177 if (e->mask & IN_IGNORED)
2180 log_warning("Unknown inotify event.");
2183 static int determine_change(sd_journal *j) {
2188 b = j->current_invalidate_counter != j->last_invalidate_counter;
2189 j->last_invalidate_counter = j->current_invalidate_counter;
2191 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2194 _public_ int sd_journal_process(sd_journal *j) {
2195 bool got_something = false;
2197 assert_return(j, -EINVAL);
2198 assert_return(!journal_pid_changed(j), -ECHILD);
2200 j->last_process_usec = now(CLOCK_MONOTONIC);
2203 uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event);
2204 struct inotify_event *e;
2207 l = read(j->inotify_fd, buffer, sizeof(buffer));
2209 if (errno == EAGAIN || errno == EINTR)
2210 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2215 got_something = true;
2217 FOREACH_INOTIFY_EVENT(e, buffer, l)
2218 process_inotify_event(j, e);
2222 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2226 assert_return(j, -EINVAL);
2227 assert_return(!journal_pid_changed(j), -ECHILD);
2229 if (j->inotify_fd < 0) {
2231 /* This is the first invocation, hence create the
2233 r = sd_journal_get_fd(j);
2237 /* The journal might have changed since the context
2238 * object was created and we weren't watching before,
2239 * hence don't wait for anything, and return
2241 return determine_change(j);
2244 r = sd_journal_get_timeout(j, &t);
2248 if (t != (uint64_t) -1) {
2251 n = now(CLOCK_MONOTONIC);
2252 t = t > n ? t - n : 0;
2254 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2259 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2260 } while (r == -EINTR);
2265 return sd_journal_process(j);
2268 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2272 uint64_t fmin = 0, tmax = 0;
2275 assert_return(j, -EINVAL);
2276 assert_return(!journal_pid_changed(j), -ECHILD);
2277 assert_return(from || to, -EINVAL);
2278 assert_return(from != to, -EINVAL);
2280 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2283 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2296 fmin = MIN(fr, fmin);
2297 tmax = MAX(t, tmax);
2306 return first ? 0 : 1;
2309 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2315 assert_return(j, -EINVAL);
2316 assert_return(!journal_pid_changed(j), -ECHILD);
2317 assert_return(from || to, -EINVAL);
2318 assert_return(from != to, -EINVAL);
2320 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2323 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2333 *from = MIN(fr, *from);
2348 void journal_print_header(sd_journal *j) {
2351 bool newline = false;
2355 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2361 journal_file_print_header(f);
2365 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2370 assert_return(j, -EINVAL);
2371 assert_return(!journal_pid_changed(j), -ECHILD);
2372 assert_return(bytes, -EINVAL);
2374 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2377 if (fstat(f->fd, &st) < 0)
2380 sum += (uint64_t) st.st_blocks * 512ULL;
2387 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2390 assert_return(j, -EINVAL);
2391 assert_return(!journal_pid_changed(j), -ECHILD);
2392 assert_return(!isempty(field), -EINVAL);
2393 assert_return(field_is_valid(field), -EINVAL);
2399 free(j->unique_field);
2400 j->unique_field = f;
2401 j->unique_file = NULL;
2402 j->unique_offset = 0;
2403 j->unique_file_lost = false;
2408 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2411 assert_return(j, -EINVAL);
2412 assert_return(!journal_pid_changed(j), -ECHILD);
2413 assert_return(data, -EINVAL);
2414 assert_return(l, -EINVAL);
2415 assert_return(j->unique_field, -EINVAL);
2417 k = strlen(j->unique_field);
2419 if (!j->unique_file) {
2420 if (j->unique_file_lost)
2423 j->unique_file = ordered_hashmap_first(j->files);
2424 if (!j->unique_file)
2427 j->unique_offset = 0;
2439 /* Proceed to next data object in the field's linked list */
2440 if (j->unique_offset == 0) {
2441 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2445 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2447 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2451 j->unique_offset = le64toh(o->data.next_field_offset);
2454 /* We reached the end of the list? Then start again, with the next file */
2455 if (j->unique_offset == 0) {
2456 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
2457 if (!j->unique_file)
2463 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2464 * instead, so that we can look at this data object at the same
2465 * time as one on another file */
2466 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
2470 /* Let's do the type check by hand, since we used 0 context above. */
2471 if (o->object.type != OBJECT_DATA) {
2472 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
2473 j->unique_file->path, j->unique_offset,
2474 o->object.type, OBJECT_DATA);
2478 r = return_data(j, j->unique_file, o, &odata, &ol);
2482 /* Check if we have at least the field name and "=". */
2484 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2485 j->unique_file->path, j->unique_offset,
2490 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2491 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2492 j->unique_file->path, j->unique_offset,
2497 /* OK, now let's see if we already returned this data
2498 * object by checking if it exists in the earlier
2499 * traversed files. */
2501 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2505 if (of == j->unique_file)
2508 /* Skip this file it didn't have any fields
2510 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2511 le64toh(of->header->n_fields) <= 0)
2514 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2525 r = return_data(j, j->unique_file, o, data, l);
2533 _public_ void sd_journal_restart_unique(sd_journal *j) {
2537 j->unique_file = NULL;
2538 j->unique_offset = 0;
2539 j->unique_file_lost = false;
2542 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2543 assert_return(j, -EINVAL);
2544 assert_return(!journal_pid_changed(j), -ECHILD);
2546 return !j->on_network;
2549 static char *lookup_field(const char *field, void *userdata) {
2550 sd_journal *j = userdata;
2558 r = sd_journal_get_data(j, field, &data, &size);
2560 size > REPLACE_VAR_MAX)
2561 return strdup(field);
2563 d = strlen(field) + 1;
2565 return strndup((const char*) data + d, size - d);
2568 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2572 _cleanup_free_ char *text = NULL, *cid = NULL;
2576 assert_return(j, -EINVAL);
2577 assert_return(!journal_pid_changed(j), -ECHILD);
2578 assert_return(ret, -EINVAL);
2580 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2584 cid = strndup((const char*) data + 11, size - 11);
2588 r = sd_id128_from_string(cid, &id);
2592 r = catalog_get(CATALOG_DATABASE, id, &text);
2596 t = replace_var(text, lookup_field, j);
2604 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2605 assert_return(ret, -EINVAL);
2607 return catalog_get(CATALOG_DATABASE, id, ret);
2610 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2611 assert_return(j, -EINVAL);
2612 assert_return(!journal_pid_changed(j), -ECHILD);
2614 j->data_threshold = sz;
2618 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2619 assert_return(j, -EINVAL);
2620 assert_return(!journal_pid_changed(j), -ECHILD);
2621 assert_return(sz, -EINVAL);
2623 *sz = j->data_threshold;