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, 0, DIRECTION_DOWN, ret, offset);
685 if (j->current_location.type == LOCATION_TAIL)
686 return journal_file_next_entry(f, 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, 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,
718 /* No matches is easy. We simple advance the file
721 return journal_file_next_entry(f, cp, direction, ret, offset);
723 /* If we have a match then we look for the next matching entry
724 * with an offset at least one step larger */
725 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
728 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction) {
730 uint64_t cp, n_entries;
736 /* If we hit EOF before, recheck if any new entries arrived. */
737 n_entries = le64toh(f->header->n_entries);
738 if (f->location_type == LOCATION_TAIL && n_entries == f->last_n_entries)
740 f->last_n_entries = n_entries;
742 if (f->last_direction == direction && f->current_offset > 0) {
743 /* LOCATION_SEEK here means we did the work in a previous
744 * iteration and the current location already points to a
745 * candidate entry. */
746 if (f->location_type == LOCATION_SEEK)
749 cp = f->current_offset;
751 r = next_with_matches(j, f, direction, &c, &cp);
755 r = find_location_with_matches(j, f, direction, &c, &cp);
760 /* OK, we found the spot, now let's advance until an entry
761 * that is actually different from what we were previously
762 * looking at. This is necessary to handle entries which exist
763 * in two (or more) journal files, and which shall all be
764 * suppressed but one. */
769 if (j->current_location.type == LOCATION_DISCRETE) {
772 k = compare_with_location(f, c, &j->current_location);
774 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
779 journal_file_save_location(f, direction, c, cp);
783 r = next_with_matches(j, f, direction, &c, &cp);
789 static int real_journal_next(sd_journal *j, direction_t direction) {
790 JournalFile *f, *new_file = NULL;
795 assert_return(j, -EINVAL);
796 assert_return(!journal_pid_changed(j), -ECHILD);
798 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
801 r = next_beyond_location(j, f, direction);
803 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
804 remove_file_real(j, f);
807 f->location_type = LOCATION_TAIL;
816 k = journal_file_compare_locations(f, new_file);
818 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
828 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o);
832 set_location(j, new_file, o);
837 _public_ int sd_journal_next(sd_journal *j) {
838 return real_journal_next(j, DIRECTION_DOWN);
841 _public_ int sd_journal_previous(sd_journal *j) {
842 return real_journal_next(j, DIRECTION_UP);
845 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
848 assert_return(j, -EINVAL);
849 assert_return(!journal_pid_changed(j), -ECHILD);
852 /* If this is not a discrete skip, then at least
853 * resolve the current location */
854 if (j->current_location.type != LOCATION_DISCRETE)
855 return real_journal_next(j, direction);
861 r = real_journal_next(j, direction);
875 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
876 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
879 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
880 return real_journal_next_skip(j, DIRECTION_UP, skip);
883 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
886 char bid[33], sid[33];
888 assert_return(j, -EINVAL);
889 assert_return(!journal_pid_changed(j), -ECHILD);
890 assert_return(cursor, -EINVAL);
892 if (!j->current_file || j->current_file->current_offset <= 0)
893 return -EADDRNOTAVAIL;
895 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
899 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
900 sd_id128_to_string(o->entry.boot_id, bid);
903 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
904 sid, le64toh(o->entry.seqnum),
905 bid, le64toh(o->entry.monotonic),
906 le64toh(o->entry.realtime),
907 le64toh(o->entry.xor_hash)) < 0)
913 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
914 const char *word, *state;
916 unsigned long long seqnum, monotonic, realtime, xor_hash;
918 seqnum_id_set = false,
921 monotonic_set = false,
922 realtime_set = false,
923 xor_hash_set = false;
924 sd_id128_t seqnum_id, boot_id;
926 assert_return(j, -EINVAL);
927 assert_return(!journal_pid_changed(j), -ECHILD);
928 assert_return(!isempty(cursor), -EINVAL);
930 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
934 if (l < 2 || word[1] != '=')
937 item = strndup(word, l);
944 seqnum_id_set = true;
945 k = sd_id128_from_string(item+2, &seqnum_id);
950 if (sscanf(item+2, "%llx", &seqnum) != 1)
956 k = sd_id128_from_string(item+2, &boot_id);
960 monotonic_set = true;
961 if (sscanf(item+2, "%llx", &monotonic) != 1)
967 if (sscanf(item+2, "%llx", &realtime) != 1)
973 if (sscanf(item+2, "%llx", &xor_hash) != 1)
984 if ((!seqnum_set || !seqnum_id_set) &&
985 (!monotonic_set || !boot_id_set) &&
991 j->current_location.type = LOCATION_SEEK;
994 j->current_location.realtime = (uint64_t) realtime;
995 j->current_location.realtime_set = true;
998 if (seqnum_set && seqnum_id_set) {
999 j->current_location.seqnum = (uint64_t) seqnum;
1000 j->current_location.seqnum_id = seqnum_id;
1001 j->current_location.seqnum_set = true;
1004 if (monotonic_set && boot_id_set) {
1005 j->current_location.monotonic = (uint64_t) monotonic;
1006 j->current_location.boot_id = boot_id;
1007 j->current_location.monotonic_set = true;
1011 j->current_location.xor_hash = (uint64_t) xor_hash;
1012 j->current_location.xor_hash_set = true;
1018 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1020 const char *word, *state;
1024 assert_return(j, -EINVAL);
1025 assert_return(!journal_pid_changed(j), -ECHILD);
1026 assert_return(!isempty(cursor), -EINVAL);
1028 if (!j->current_file || j->current_file->current_offset <= 0)
1029 return -EADDRNOTAVAIL;
1031 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1035 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1036 _cleanup_free_ char *item = NULL;
1038 unsigned long long ll;
1041 if (l < 2 || word[1] != '=')
1044 item = strndup(word, l);
1051 k = sd_id128_from_string(item+2, &id);
1054 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1059 if (sscanf(item+2, "%llx", &ll) != 1)
1061 if (ll != le64toh(o->entry.seqnum))
1066 k = sd_id128_from_string(item+2, &id);
1069 if (!sd_id128_equal(id, o->entry.boot_id))
1074 if (sscanf(item+2, "%llx", &ll) != 1)
1076 if (ll != le64toh(o->entry.monotonic))
1081 if (sscanf(item+2, "%llx", &ll) != 1)
1083 if (ll != le64toh(o->entry.realtime))
1088 if (sscanf(item+2, "%llx", &ll) != 1)
1090 if (ll != le64toh(o->entry.xor_hash))
1100 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1101 assert_return(j, -EINVAL);
1102 assert_return(!journal_pid_changed(j), -ECHILD);
1105 j->current_location.type = LOCATION_SEEK;
1106 j->current_location.boot_id = boot_id;
1107 j->current_location.monotonic = usec;
1108 j->current_location.monotonic_set = true;
1113 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1114 assert_return(j, -EINVAL);
1115 assert_return(!journal_pid_changed(j), -ECHILD);
1118 j->current_location.type = LOCATION_SEEK;
1119 j->current_location.realtime = usec;
1120 j->current_location.realtime_set = true;
1125 _public_ int sd_journal_seek_head(sd_journal *j) {
1126 assert_return(j, -EINVAL);
1127 assert_return(!journal_pid_changed(j), -ECHILD);
1130 j->current_location.type = LOCATION_HEAD;
1135 _public_ int sd_journal_seek_tail(sd_journal *j) {
1136 assert_return(j, -EINVAL);
1137 assert_return(!journal_pid_changed(j), -ECHILD);
1140 j->current_location.type = LOCATION_TAIL;
1145 static void check_network(sd_journal *j, int fd) {
1153 if (fstatfs(fd, &sfs) < 0)
1157 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1158 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1159 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1160 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1161 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1164 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1165 const char *full, *tilded, *atted;
1167 full = strappenda(prefix, ".journal");
1168 tilded = strappenda(full, "~");
1169 atted = strappenda(prefix, "@");
1171 return streq(filename, full) ||
1172 streq(filename, tilded) ||
1173 startswith(filename, atted);
1176 static bool file_type_wanted(int flags, const char *filename) {
1177 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1180 /* no flags set → every type is OK */
1181 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1184 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1187 if (flags & SD_JOURNAL_CURRENT_USER) {
1188 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1190 assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid())
1191 < (int) sizeof(prefix));
1193 if (file_has_type_prefix(prefix, filename))
1200 static int add_any_file(sd_journal *j, const char *path) {
1201 JournalFile *f = NULL;
1207 if (ordered_hashmap_get(j->files, path))
1210 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1211 log_warning("Too many open journal files, not adding %s.", path);
1212 return set_put_error(j, -ETOOMANYREFS);
1215 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1219 /* journal_file_dump(f); */
1221 r = ordered_hashmap_put(j->files, f->path, f);
1223 journal_file_close(f);
1227 log_debug("File %s added.", f->path);
1229 check_network(j, f->fd);
1231 j->current_invalidate_counter ++;
1236 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1237 _cleanup_free_ char *path = NULL;
1244 if (j->no_new_files ||
1245 !file_type_wanted(j->flags, filename))
1248 path = strjoin(prefix, "/", filename, NULL);
1252 r = add_any_file(j, path);
1258 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1259 _cleanup_free_ char *path;
1266 path = strjoin(prefix, "/", filename, NULL);
1270 f = ordered_hashmap_get(j->files, path);
1274 remove_file_real(j, f);
1278 static void remove_file_real(sd_journal *j, JournalFile *f) {
1282 ordered_hashmap_remove(j->files, f->path);
1284 log_debug("File %s removed.", f->path);
1286 if (j->current_file == f) {
1287 j->current_file = NULL;
1288 j->current_field = 0;
1291 if (j->unique_file == f) {
1292 /* Jump to the next unique_file or NULL if that one was last */
1293 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
1294 j->unique_offset = 0;
1295 if (!j->unique_file)
1296 j->unique_file_lost = true;
1299 journal_file_close(f);
1301 j->current_invalidate_counter ++;
1304 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1305 _cleanup_free_ char *path = NULL;
1307 _cleanup_closedir_ DIR *d = NULL;
1315 log_debug("Considering %s/%s.", prefix, dirname);
1317 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1318 (sd_id128_from_string(dirname, &id) < 0 ||
1319 sd_id128_get_machine(&mid) < 0 ||
1320 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1323 path = strjoin(prefix, "/", dirname, NULL);
1329 log_debug_errno(errno, "Failed to open %s: %m", path);
1330 if (errno == ENOENT)
1335 m = hashmap_get(j->directories_by_path, path);
1337 m = new0(Directory, 1);
1344 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1349 path = NULL; /* avoid freeing in cleanup */
1350 j->current_invalidate_counter ++;
1352 log_debug("Directory %s added.", m->path);
1354 } else if (m->is_root)
1357 if (m->wd <= 0 && j->inotify_fd >= 0) {
1359 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1360 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1361 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1364 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1365 inotify_rm_watch(j->inotify_fd, m->wd);
1373 if (!de && errno != 0) {
1375 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1381 if (dirent_is_file_with_suffix(de, ".journal") ||
1382 dirent_is_file_with_suffix(de, ".journal~")) {
1383 r = add_file(j, m->path, de->d_name);
1385 log_debug_errno(r, "Failed to add file %s/%s: %m",
1386 m->path, de->d_name);
1387 r = set_put_error(j, r);
1394 check_network(j, dirfd(d));
1399 static int add_root_directory(sd_journal *j, const char *p) {
1400 _cleanup_closedir_ DIR *d = NULL;
1407 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1408 !path_startswith(p, "/run"))
1412 p = strappenda(j->prefix, p);
1418 m = hashmap_get(j->directories_by_path, p);
1420 m = new0(Directory, 1);
1425 m->path = strdup(p);
1431 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1437 j->current_invalidate_counter ++;
1439 log_debug("Root directory %s added.", m->path);
1441 } else if (!m->is_root)
1444 if (m->wd <= 0 && j->inotify_fd >= 0) {
1446 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1447 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1450 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1451 inotify_rm_watch(j->inotify_fd, m->wd);
1454 if (j->no_new_files)
1463 if (!de && errno != 0) {
1465 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1471 if (dirent_is_file_with_suffix(de, ".journal") ||
1472 dirent_is_file_with_suffix(de, ".journal~")) {
1473 r = add_file(j, m->path, de->d_name);
1475 log_debug_errno(r, "Failed to add file %s/%s: %m",
1476 m->path, de->d_name);
1477 r = set_put_error(j, r);
1481 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1482 sd_id128_from_string(de->d_name, &id) >= 0) {
1484 r = add_directory(j, m->path, de->d_name);
1486 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
1490 check_network(j, dirfd(d));
1495 static int remove_directory(sd_journal *j, Directory *d) {
1499 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1501 if (j->inotify_fd >= 0)
1502 inotify_rm_watch(j->inotify_fd, d->wd);
1505 hashmap_remove(j->directories_by_path, d->path);
1508 log_debug("Root directory %s removed.", d->path);
1510 log_debug("Directory %s removed.", d->path);
1518 static int add_search_paths(sd_journal *j) {
1520 const char search_paths[] =
1521 "/run/log/journal\0"
1522 "/var/log/journal\0";
1527 /* We ignore most errors here, since the idea is to only open
1528 * what's actually accessible, and ignore the rest. */
1530 NULSTR_FOREACH(p, search_paths) {
1531 r = add_root_directory(j, p);
1532 if (r < 0 && r != -ENOENT) {
1533 r = set_put_error(j, r);
1542 static int add_current_paths(sd_journal *j) {
1547 assert(j->no_new_files);
1549 /* Simply adds all directories for files we have open as
1550 * "root" directories. We don't expect errors here, so we
1551 * treat them as fatal. */
1553 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
1554 _cleanup_free_ char *dir;
1557 dir = dirname_malloc(f->path);
1561 r = add_root_directory(j, dir);
1563 set_put_error(j, r);
1572 static int allocate_inotify(sd_journal *j) {
1575 if (j->inotify_fd < 0) {
1576 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1577 if (j->inotify_fd < 0)
1581 if (!j->directories_by_wd) {
1582 j->directories_by_wd = hashmap_new(NULL);
1583 if (!j->directories_by_wd)
1590 static sd_journal *journal_new(int flags, const char *path) {
1593 j = new0(sd_journal, 1);
1597 j->original_pid = getpid();
1600 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1603 j->path = strdup(path);
1608 j->files = ordered_hashmap_new(&string_hash_ops);
1609 j->directories_by_path = hashmap_new(&string_hash_ops);
1610 j->mmap = mmap_cache_new();
1611 if (!j->files || !j->directories_by_path || !j->mmap)
1617 sd_journal_close(j);
1621 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1625 assert_return(ret, -EINVAL);
1626 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1628 j = journal_new(flags, NULL);
1632 r = add_search_paths(j);
1640 sd_journal_close(j);
1645 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1646 _cleanup_free_ char *root = NULL, *class = NULL;
1651 assert_return(machine, -EINVAL);
1652 assert_return(ret, -EINVAL);
1653 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1654 assert_return(machine_name_is_valid(machine), -EINVAL);
1656 p = strappenda("/run/systemd/machines/", machine);
1657 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1665 if (!streq_ptr(class, "container"))
1668 j = journal_new(flags, NULL);
1675 r = add_search_paths(j);
1683 sd_journal_close(j);
1687 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1691 assert_return(ret, -EINVAL);
1692 assert_return(path, -EINVAL);
1693 assert_return(flags == 0, -EINVAL);
1695 j = journal_new(flags, path);
1699 r = add_root_directory(j, path);
1701 set_put_error(j, r);
1709 sd_journal_close(j);
1714 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1719 assert_return(ret, -EINVAL);
1720 assert_return(flags == 0, -EINVAL);
1722 j = journal_new(flags, NULL);
1726 STRV_FOREACH(path, paths) {
1727 r = add_any_file(j, *path);
1729 log_error_errno(r, "Failed to open %s: %m", *path);
1734 j->no_new_files = true;
1740 sd_journal_close(j);
1745 _public_ void sd_journal_close(sd_journal *j) {
1752 sd_journal_flush_matches(j);
1754 while ((f = ordered_hashmap_steal_first(j->files)))
1755 journal_file_close(f);
1757 ordered_hashmap_free(j->files);
1759 while ((d = hashmap_first(j->directories_by_path)))
1760 remove_directory(j, d);
1762 while ((d = hashmap_first(j->directories_by_wd)))
1763 remove_directory(j, d);
1765 hashmap_free(j->directories_by_path);
1766 hashmap_free(j->directories_by_wd);
1768 safe_close(j->inotify_fd);
1771 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1772 mmap_cache_unref(j->mmap);
1777 free(j->unique_field);
1778 set_free(j->errors);
1782 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1787 assert_return(j, -EINVAL);
1788 assert_return(!journal_pid_changed(j), -ECHILD);
1789 assert_return(ret, -EINVAL);
1791 f = j->current_file;
1793 return -EADDRNOTAVAIL;
1795 if (f->current_offset <= 0)
1796 return -EADDRNOTAVAIL;
1798 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1802 *ret = le64toh(o->entry.realtime);
1806 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1812 assert_return(j, -EINVAL);
1813 assert_return(!journal_pid_changed(j), -ECHILD);
1815 f = j->current_file;
1817 return -EADDRNOTAVAIL;
1819 if (f->current_offset <= 0)
1820 return -EADDRNOTAVAIL;
1822 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1827 *ret_boot_id = o->entry.boot_id;
1829 r = sd_id128_get_boot(&id);
1833 if (!sd_id128_equal(id, o->entry.boot_id))
1838 *ret = le64toh(o->entry.monotonic);
1843 static bool field_is_valid(const char *field) {
1851 if (startswith(field, "__"))
1854 for (p = field; *p; p++) {
1859 if (*p >= 'A' && *p <= 'Z')
1862 if (*p >= '0' && *p <= '9')
1871 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1874 size_t field_length;
1878 assert_return(j, -EINVAL);
1879 assert_return(!journal_pid_changed(j), -ECHILD);
1880 assert_return(field, -EINVAL);
1881 assert_return(data, -EINVAL);
1882 assert_return(size, -EINVAL);
1883 assert_return(field_is_valid(field), -EINVAL);
1885 f = j->current_file;
1887 return -EADDRNOTAVAIL;
1889 if (f->current_offset <= 0)
1890 return -EADDRNOTAVAIL;
1892 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1896 field_length = strlen(field);
1898 n = journal_file_entry_n_items(o);
1899 for (i = 0; i < n; i++) {
1905 p = le64toh(o->entry.items[i].object_offset);
1906 le_hash = o->entry.items[i].hash;
1907 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1911 if (le_hash != o->data.hash)
1914 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1916 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1918 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1919 if (decompress_startswith(compression,
1921 &f->compress_buffer, &f->compress_buffer_size,
1922 field, field_length, '=')) {
1926 r = decompress_blob(compression,
1928 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1933 *data = f->compress_buffer;
1934 *size = (size_t) rsize;
1939 return -EPROTONOSUPPORT;
1941 } else if (l >= field_length+1 &&
1942 memcmp(o->data.payload, field, field_length) == 0 &&
1943 o->data.payload[field_length] == '=') {
1947 if ((uint64_t) t != l)
1950 *data = o->data.payload;
1956 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1964 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
1969 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1972 /* We can't read objects larger than 4G on a 32bit machine */
1973 if ((uint64_t) t != l)
1976 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1978 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1982 r = decompress_blob(compression,
1983 o->data.payload, l, &f->compress_buffer,
1984 &f->compress_buffer_size, &rsize, j->data_threshold);
1988 *data = f->compress_buffer;
1989 *size = (size_t) rsize;
1991 return -EPROTONOSUPPORT;
1994 *data = o->data.payload;
2001 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2008 assert_return(j, -EINVAL);
2009 assert_return(!journal_pid_changed(j), -ECHILD);
2010 assert_return(data, -EINVAL);
2011 assert_return(size, -EINVAL);
2013 f = j->current_file;
2015 return -EADDRNOTAVAIL;
2017 if (f->current_offset <= 0)
2018 return -EADDRNOTAVAIL;
2020 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2024 n = journal_file_entry_n_items(o);
2025 if (j->current_field >= n)
2028 p = le64toh(o->entry.items[j->current_field].object_offset);
2029 le_hash = o->entry.items[j->current_field].hash;
2030 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2034 if (le_hash != o->data.hash)
2037 r = return_data(j, f, o, data, size);
2041 j->current_field ++;
2046 _public_ void sd_journal_restart_data(sd_journal *j) {
2050 j->current_field = 0;
2053 _public_ int sd_journal_get_fd(sd_journal *j) {
2056 assert_return(j, -EINVAL);
2057 assert_return(!journal_pid_changed(j), -ECHILD);
2059 if (j->inotify_fd >= 0)
2060 return j->inotify_fd;
2062 r = allocate_inotify(j);
2066 /* Iterate through all dirs again, to add them to the
2068 if (j->no_new_files)
2069 r = add_current_paths(j);
2071 r = add_root_directory(j, j->path);
2073 r = add_search_paths(j);
2077 return j->inotify_fd;
2080 _public_ int sd_journal_get_events(sd_journal *j) {
2083 assert_return(j, -EINVAL);
2084 assert_return(!journal_pid_changed(j), -ECHILD);
2086 fd = sd_journal_get_fd(j);
2093 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2096 assert_return(j, -EINVAL);
2097 assert_return(!journal_pid_changed(j), -ECHILD);
2098 assert_return(timeout_usec, -EINVAL);
2100 fd = sd_journal_get_fd(j);
2104 if (!j->on_network) {
2105 *timeout_usec = (uint64_t) -1;
2109 /* If we are on the network we need to regularly check for
2110 * changes manually */
2112 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2116 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2123 /* Is this a subdirectory we watch? */
2124 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2128 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2129 (endswith(e->name, ".journal") ||
2130 endswith(e->name, ".journal~"))) {
2132 /* Event for a journal file */
2134 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2135 r = add_file(j, d->path, e->name);
2137 log_debug_errno(r, "Failed to add file %s/%s: %m",
2139 set_put_error(j, r);
2142 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2144 r = remove_file(j, d->path, e->name);
2146 log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
2149 } else if (!d->is_root && e->len == 0) {
2151 /* Event for a subdirectory */
2153 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2154 r = remove_directory(j, d);
2156 log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
2160 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2162 /* Event for root directory */
2164 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2165 r = add_directory(j, d->path, e->name);
2167 log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
2174 if (e->mask & IN_IGNORED)
2177 log_warning("Unknown inotify event.");
2180 static int determine_change(sd_journal *j) {
2185 b = j->current_invalidate_counter != j->last_invalidate_counter;
2186 j->last_invalidate_counter = j->current_invalidate_counter;
2188 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2191 _public_ int sd_journal_process(sd_journal *j) {
2192 bool got_something = false;
2194 assert_return(j, -EINVAL);
2195 assert_return(!journal_pid_changed(j), -ECHILD);
2197 j->last_process_usec = now(CLOCK_MONOTONIC);
2200 uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event);
2201 struct inotify_event *e;
2204 l = read(j->inotify_fd, buffer, sizeof(buffer));
2206 if (errno == EAGAIN || errno == EINTR)
2207 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2212 got_something = true;
2214 FOREACH_INOTIFY_EVENT(e, buffer, l)
2215 process_inotify_event(j, e);
2219 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2223 assert_return(j, -EINVAL);
2224 assert_return(!journal_pid_changed(j), -ECHILD);
2226 if (j->inotify_fd < 0) {
2228 /* This is the first invocation, hence create the
2230 r = sd_journal_get_fd(j);
2234 /* The journal might have changed since the context
2235 * object was created and we weren't watching before,
2236 * hence don't wait for anything, and return
2238 return determine_change(j);
2241 r = sd_journal_get_timeout(j, &t);
2245 if (t != (uint64_t) -1) {
2248 n = now(CLOCK_MONOTONIC);
2249 t = t > n ? t - n : 0;
2251 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2256 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2257 } while (r == -EINTR);
2262 return sd_journal_process(j);
2265 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2269 uint64_t fmin = 0, tmax = 0;
2272 assert_return(j, -EINVAL);
2273 assert_return(!journal_pid_changed(j), -ECHILD);
2274 assert_return(from || to, -EINVAL);
2275 assert_return(from != to, -EINVAL);
2277 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2280 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2293 fmin = MIN(fr, fmin);
2294 tmax = MAX(t, tmax);
2303 return first ? 0 : 1;
2306 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2312 assert_return(j, -EINVAL);
2313 assert_return(!journal_pid_changed(j), -ECHILD);
2314 assert_return(from || to, -EINVAL);
2315 assert_return(from != to, -EINVAL);
2317 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2320 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2330 *from = MIN(fr, *from);
2345 void journal_print_header(sd_journal *j) {
2348 bool newline = false;
2352 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2358 journal_file_print_header(f);
2362 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2367 assert_return(j, -EINVAL);
2368 assert_return(!journal_pid_changed(j), -ECHILD);
2369 assert_return(bytes, -EINVAL);
2371 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2374 if (fstat(f->fd, &st) < 0)
2377 sum += (uint64_t) st.st_blocks * 512ULL;
2384 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2387 assert_return(j, -EINVAL);
2388 assert_return(!journal_pid_changed(j), -ECHILD);
2389 assert_return(!isempty(field), -EINVAL);
2390 assert_return(field_is_valid(field), -EINVAL);
2396 free(j->unique_field);
2397 j->unique_field = f;
2398 j->unique_file = NULL;
2399 j->unique_offset = 0;
2400 j->unique_file_lost = false;
2405 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2408 assert_return(j, -EINVAL);
2409 assert_return(!journal_pid_changed(j), -ECHILD);
2410 assert_return(data, -EINVAL);
2411 assert_return(l, -EINVAL);
2412 assert_return(j->unique_field, -EINVAL);
2414 k = strlen(j->unique_field);
2416 if (!j->unique_file) {
2417 if (j->unique_file_lost)
2420 j->unique_file = ordered_hashmap_first(j->files);
2421 if (!j->unique_file)
2424 j->unique_offset = 0;
2436 /* Proceed to next data object in the field's linked list */
2437 if (j->unique_offset == 0) {
2438 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2442 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2444 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2448 j->unique_offset = le64toh(o->data.next_field_offset);
2451 /* We reached the end of the list? Then start again, with the next file */
2452 if (j->unique_offset == 0) {
2453 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
2454 if (!j->unique_file)
2460 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2461 * instead, so that we can look at this data object at the same
2462 * time as one on another file */
2463 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
2467 /* Let's do the type check by hand, since we used 0 context above. */
2468 if (o->object.type != OBJECT_DATA) {
2469 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
2470 j->unique_file->path, j->unique_offset,
2471 o->object.type, OBJECT_DATA);
2475 r = return_data(j, j->unique_file, o, &odata, &ol);
2479 /* Check if we have at least the field name and "=". */
2481 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2482 j->unique_file->path, j->unique_offset,
2487 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2488 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2489 j->unique_file->path, j->unique_offset,
2494 /* OK, now let's see if we already returned this data
2495 * object by checking if it exists in the earlier
2496 * traversed files. */
2498 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2502 if (of == j->unique_file)
2505 /* Skip this file it didn't have any fields
2507 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2508 le64toh(of->header->n_fields) <= 0)
2511 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2522 r = return_data(j, j->unique_file, o, data, l);
2530 _public_ void sd_journal_restart_unique(sd_journal *j) {
2534 j->unique_file = NULL;
2535 j->unique_offset = 0;
2536 j->unique_file_lost = false;
2539 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2540 assert_return(j, -EINVAL);
2541 assert_return(!journal_pid_changed(j), -ECHILD);
2543 return !j->on_network;
2546 static char *lookup_field(const char *field, void *userdata) {
2547 sd_journal *j = userdata;
2555 r = sd_journal_get_data(j, field, &data, &size);
2557 size > REPLACE_VAR_MAX)
2558 return strdup(field);
2560 d = strlen(field) + 1;
2562 return strndup((const char*) data + d, size - d);
2565 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2569 _cleanup_free_ char *text = NULL, *cid = NULL;
2573 assert_return(j, -EINVAL);
2574 assert_return(!journal_pid_changed(j), -ECHILD);
2575 assert_return(ret, -EINVAL);
2577 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2581 cid = strndup((const char*) data + 11, size - 11);
2585 r = sd_id128_from_string(cid, &id);
2589 r = catalog_get(CATALOG_DATABASE, id, &text);
2593 t = replace_var(text, lookup_field, j);
2601 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2602 assert_return(ret, -EINVAL);
2604 return catalog_get(CATALOG_DATABASE, id, ret);
2607 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2608 assert_return(j, -EINVAL);
2609 assert_return(!journal_pid_changed(j), -ECHILD);
2611 j->data_threshold = sz;
2615 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2616 assert_return(j, -EINVAL);
2617 assert_return(!journal_pid_changed(j), -ECHILD);
2618 assert_return(sz, -EINVAL);
2620 *sz = j->data_threshold;