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 *f, Location *l) {
418 assert(f->location_type == LOCATION_SEEK);
419 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
421 if (l->monotonic_set &&
422 sd_id128_equal(f->current_boot_id, l->boot_id) &&
424 f->current_realtime == l->realtime &&
426 f->current_xor_hash == l->xor_hash)
430 sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) {
432 if (f->current_seqnum < l->seqnum)
434 if (f->current_seqnum > l->seqnum)
438 if (l->monotonic_set &&
439 sd_id128_equal(f->current_boot_id, l->boot_id)) {
441 if (f->current_monotonic < l->monotonic)
443 if (f->current_monotonic > l->monotonic)
447 if (l->realtime_set) {
449 if (f->current_realtime < l->realtime)
451 if (f->current_realtime > l->realtime)
455 if (l->xor_hash_set) {
457 if (f->current_xor_hash < l->xor_hash)
459 if (f->current_xor_hash > l->xor_hash)
466 static int next_for_match(
470 uint64_t after_offset,
471 direction_t direction,
483 if (m->type == MATCH_DISCRETE) {
486 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
490 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
492 } else if (m->type == MATCH_OR_TERM) {
495 /* Find the earliest match beyond after_offset */
497 LIST_FOREACH(matches, i, m->matches) {
500 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
504 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
512 } else if (m->type == MATCH_AND_TERM) {
513 Match *i, *last_moved;
515 /* Always jump to the next matching entry and repeat
516 * this until we find an offset that matches for all
522 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
526 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
527 last_moved = m->matches;
529 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
532 r = next_for_match(j, i, f, np, direction, NULL, &cp);
536 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
537 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
546 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
558 static int find_location_for_match(
562 direction_t direction,
572 if (m->type == MATCH_DISCRETE) {
575 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
579 /* FIXME: missing: find by monotonic */
581 if (j->current_location.type == LOCATION_HEAD)
582 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
583 if (j->current_location.type == LOCATION_TAIL)
584 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
585 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
586 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
587 if (j->current_location.monotonic_set) {
588 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
592 if (j->current_location.realtime_set)
593 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
595 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
597 } else if (m->type == MATCH_OR_TERM) {
602 /* Find the earliest match */
604 LIST_FOREACH(matches, i, m->matches) {
607 r = find_location_for_match(j, i, f, direction, NULL, &cp);
611 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
619 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
634 assert(m->type == MATCH_AND_TERM);
636 /* First jump to the last match, and then find the
637 * next one where all matches match */
642 LIST_FOREACH(matches, i, m->matches) {
645 r = find_location_for_match(j, i, f, direction, NULL, &cp);
649 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
653 return next_for_match(j, m, f, np, direction, ret, offset);
657 static int find_location_with_matches(
660 direction_t direction,
672 /* No matches is simple */
674 if (j->current_location.type == LOCATION_HEAD)
675 return journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset);
676 if (j->current_location.type == LOCATION_TAIL)
677 return journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset);
678 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
679 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
680 if (j->current_location.monotonic_set) {
681 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
685 if (j->current_location.realtime_set)
686 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
688 return journal_file_next_entry(f, 0, direction, ret, offset);
690 return find_location_for_match(j, j->level0, f, direction, ret, offset);
693 static int next_with_matches(
696 direction_t direction,
705 /* No matches is easy. We simple advance the file
708 return journal_file_next_entry(f, f->current_offset, direction, ret, offset);
710 /* If we have a match then we look for the next matching entry
711 * with an offset at least one step larger */
712 return next_for_match(j, j->level0, f,
713 direction == DIRECTION_DOWN ? f->current_offset + 1
714 : f->current_offset - 1,
715 direction, ret, offset);
718 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction) {
720 uint64_t cp, n_entries;
726 if (f->last_direction == direction && f->current_offset > 0) {
727 /* If we hit EOF before, recheck if any new entries arrived. */
728 n_entries = le64toh(f->header->n_entries);
729 if (f->location_type == LOCATION_TAIL && n_entries == f->last_n_entries)
731 f->last_n_entries = n_entries;
733 /* LOCATION_SEEK here means we did the work in a previous
734 * iteration and the current location already points to a
735 * candidate entry. */
736 if (f->location_type != LOCATION_SEEK) {
737 r = next_with_matches(j, f, direction, &c, &cp);
741 journal_file_save_location(f, direction, c, cp);
744 r = find_location_with_matches(j, f, direction, &c, &cp);
748 journal_file_save_location(f, direction, c, cp);
751 /* OK, we found the spot, now let's advance until an entry
752 * that is actually different from what we were previously
753 * looking at. This is necessary to handle entries which exist
754 * in two (or more) journal files, and which shall all be
755 * suppressed but one. */
760 if (j->current_location.type == LOCATION_DISCRETE) {
763 k = compare_with_location(f, &j->current_location);
765 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
772 r = next_with_matches(j, f, direction, &c, &cp);
776 journal_file_save_location(f, direction, c, cp);
780 static int real_journal_next(sd_journal *j, direction_t direction) {
781 JournalFile *f, *new_file = NULL;
786 assert_return(j, -EINVAL);
787 assert_return(!journal_pid_changed(j), -ECHILD);
789 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
792 r = next_beyond_location(j, f, direction);
794 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
795 remove_file_real(j, f);
798 f->location_type = LOCATION_TAIL;
807 k = journal_file_compare_locations(f, new_file);
809 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
819 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o);
823 set_location(j, new_file, o);
828 _public_ int sd_journal_next(sd_journal *j) {
829 return real_journal_next(j, DIRECTION_DOWN);
832 _public_ int sd_journal_previous(sd_journal *j) {
833 return real_journal_next(j, DIRECTION_UP);
836 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
839 assert_return(j, -EINVAL);
840 assert_return(!journal_pid_changed(j), -ECHILD);
843 /* If this is not a discrete skip, then at least
844 * resolve the current location */
845 if (j->current_location.type != LOCATION_DISCRETE)
846 return real_journal_next(j, direction);
852 r = real_journal_next(j, direction);
866 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
867 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
870 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
871 return real_journal_next_skip(j, DIRECTION_UP, skip);
874 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
877 char bid[33], sid[33];
879 assert_return(j, -EINVAL);
880 assert_return(!journal_pid_changed(j), -ECHILD);
881 assert_return(cursor, -EINVAL);
883 if (!j->current_file || j->current_file->current_offset <= 0)
884 return -EADDRNOTAVAIL;
886 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
890 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
891 sd_id128_to_string(o->entry.boot_id, bid);
894 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
895 sid, le64toh(o->entry.seqnum),
896 bid, le64toh(o->entry.monotonic),
897 le64toh(o->entry.realtime),
898 le64toh(o->entry.xor_hash)) < 0)
904 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
905 const char *word, *state;
907 unsigned long long seqnum, monotonic, realtime, xor_hash;
909 seqnum_id_set = false,
912 monotonic_set = false,
913 realtime_set = false,
914 xor_hash_set = false;
915 sd_id128_t seqnum_id, boot_id;
917 assert_return(j, -EINVAL);
918 assert_return(!journal_pid_changed(j), -ECHILD);
919 assert_return(!isempty(cursor), -EINVAL);
921 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
925 if (l < 2 || word[1] != '=')
928 item = strndup(word, l);
935 seqnum_id_set = true;
936 k = sd_id128_from_string(item+2, &seqnum_id);
941 if (sscanf(item+2, "%llx", &seqnum) != 1)
947 k = sd_id128_from_string(item+2, &boot_id);
951 monotonic_set = true;
952 if (sscanf(item+2, "%llx", &monotonic) != 1)
958 if (sscanf(item+2, "%llx", &realtime) != 1)
964 if (sscanf(item+2, "%llx", &xor_hash) != 1)
975 if ((!seqnum_set || !seqnum_id_set) &&
976 (!monotonic_set || !boot_id_set) &&
982 j->current_location.type = LOCATION_SEEK;
985 j->current_location.realtime = (uint64_t) realtime;
986 j->current_location.realtime_set = true;
989 if (seqnum_set && seqnum_id_set) {
990 j->current_location.seqnum = (uint64_t) seqnum;
991 j->current_location.seqnum_id = seqnum_id;
992 j->current_location.seqnum_set = true;
995 if (monotonic_set && boot_id_set) {
996 j->current_location.monotonic = (uint64_t) monotonic;
997 j->current_location.boot_id = boot_id;
998 j->current_location.monotonic_set = true;
1002 j->current_location.xor_hash = (uint64_t) xor_hash;
1003 j->current_location.xor_hash_set = true;
1009 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1011 const char *word, *state;
1015 assert_return(j, -EINVAL);
1016 assert_return(!journal_pid_changed(j), -ECHILD);
1017 assert_return(!isempty(cursor), -EINVAL);
1019 if (!j->current_file || j->current_file->current_offset <= 0)
1020 return -EADDRNOTAVAIL;
1022 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1026 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1027 _cleanup_free_ char *item = NULL;
1029 unsigned long long ll;
1032 if (l < 2 || word[1] != '=')
1035 item = strndup(word, l);
1042 k = sd_id128_from_string(item+2, &id);
1045 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1050 if (sscanf(item+2, "%llx", &ll) != 1)
1052 if (ll != le64toh(o->entry.seqnum))
1057 k = sd_id128_from_string(item+2, &id);
1060 if (!sd_id128_equal(id, o->entry.boot_id))
1065 if (sscanf(item+2, "%llx", &ll) != 1)
1067 if (ll != le64toh(o->entry.monotonic))
1072 if (sscanf(item+2, "%llx", &ll) != 1)
1074 if (ll != le64toh(o->entry.realtime))
1079 if (sscanf(item+2, "%llx", &ll) != 1)
1081 if (ll != le64toh(o->entry.xor_hash))
1091 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1092 assert_return(j, -EINVAL);
1093 assert_return(!journal_pid_changed(j), -ECHILD);
1096 j->current_location.type = LOCATION_SEEK;
1097 j->current_location.boot_id = boot_id;
1098 j->current_location.monotonic = usec;
1099 j->current_location.monotonic_set = true;
1104 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1105 assert_return(j, -EINVAL);
1106 assert_return(!journal_pid_changed(j), -ECHILD);
1109 j->current_location.type = LOCATION_SEEK;
1110 j->current_location.realtime = usec;
1111 j->current_location.realtime_set = true;
1116 _public_ int sd_journal_seek_head(sd_journal *j) {
1117 assert_return(j, -EINVAL);
1118 assert_return(!journal_pid_changed(j), -ECHILD);
1121 j->current_location.type = LOCATION_HEAD;
1126 _public_ int sd_journal_seek_tail(sd_journal *j) {
1127 assert_return(j, -EINVAL);
1128 assert_return(!journal_pid_changed(j), -ECHILD);
1131 j->current_location.type = LOCATION_TAIL;
1136 static void check_network(sd_journal *j, int fd) {
1144 if (fstatfs(fd, &sfs) < 0)
1148 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1149 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1150 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1151 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1152 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1155 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1156 const char *full, *tilded, *atted;
1158 full = strappenda(prefix, ".journal");
1159 tilded = strappenda(full, "~");
1160 atted = strappenda(prefix, "@");
1162 return streq(filename, full) ||
1163 streq(filename, tilded) ||
1164 startswith(filename, atted);
1167 static bool file_type_wanted(int flags, const char *filename) {
1168 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1171 /* no flags set → every type is OK */
1172 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1175 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1178 if (flags & SD_JOURNAL_CURRENT_USER) {
1179 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1181 assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid())
1182 < (int) sizeof(prefix));
1184 if (file_has_type_prefix(prefix, filename))
1191 static int add_any_file(sd_journal *j, const char *path) {
1192 JournalFile *f = NULL;
1198 if (ordered_hashmap_get(j->files, path))
1201 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1202 log_warning("Too many open journal files, not adding %s.", path);
1203 return set_put_error(j, -ETOOMANYREFS);
1206 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1210 /* journal_file_dump(f); */
1212 r = ordered_hashmap_put(j->files, f->path, f);
1214 journal_file_close(f);
1218 log_debug("File %s added.", f->path);
1220 check_network(j, f->fd);
1222 j->current_invalidate_counter ++;
1227 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1228 _cleanup_free_ char *path = NULL;
1235 if (j->no_new_files ||
1236 !file_type_wanted(j->flags, filename))
1239 path = strjoin(prefix, "/", filename, NULL);
1243 r = add_any_file(j, path);
1249 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1250 _cleanup_free_ char *path;
1257 path = strjoin(prefix, "/", filename, NULL);
1261 f = ordered_hashmap_get(j->files, path);
1265 remove_file_real(j, f);
1269 static void remove_file_real(sd_journal *j, JournalFile *f) {
1273 ordered_hashmap_remove(j->files, f->path);
1275 log_debug("File %s removed.", f->path);
1277 if (j->current_file == f) {
1278 j->current_file = NULL;
1279 j->current_field = 0;
1282 if (j->unique_file == f) {
1283 /* Jump to the next unique_file or NULL if that one was last */
1284 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
1285 j->unique_offset = 0;
1286 if (!j->unique_file)
1287 j->unique_file_lost = true;
1290 journal_file_close(f);
1292 j->current_invalidate_counter ++;
1295 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1296 _cleanup_free_ char *path = NULL;
1298 _cleanup_closedir_ DIR *d = NULL;
1306 log_debug("Considering %s/%s.", prefix, dirname);
1308 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1309 (sd_id128_from_string(dirname, &id) < 0 ||
1310 sd_id128_get_machine(&mid) < 0 ||
1311 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1314 path = strjoin(prefix, "/", dirname, NULL);
1320 log_debug_errno(errno, "Failed to open %s: %m", path);
1321 if (errno == ENOENT)
1326 m = hashmap_get(j->directories_by_path, path);
1328 m = new0(Directory, 1);
1335 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1340 path = NULL; /* avoid freeing in cleanup */
1341 j->current_invalidate_counter ++;
1343 log_debug("Directory %s added.", m->path);
1345 } else if (m->is_root)
1348 if (m->wd <= 0 && j->inotify_fd >= 0) {
1350 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1351 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1352 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1355 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1356 inotify_rm_watch(j->inotify_fd, m->wd);
1364 if (!de && errno != 0) {
1366 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1372 if (dirent_is_file_with_suffix(de, ".journal") ||
1373 dirent_is_file_with_suffix(de, ".journal~")) {
1374 r = add_file(j, m->path, de->d_name);
1376 log_debug_errno(r, "Failed to add file %s/%s: %m",
1377 m->path, de->d_name);
1378 r = set_put_error(j, r);
1385 check_network(j, dirfd(d));
1390 static int add_root_directory(sd_journal *j, const char *p) {
1391 _cleanup_closedir_ DIR *d = NULL;
1398 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1399 !path_startswith(p, "/run"))
1403 p = strappenda(j->prefix, p);
1409 m = hashmap_get(j->directories_by_path, p);
1411 m = new0(Directory, 1);
1416 m->path = strdup(p);
1422 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1428 j->current_invalidate_counter ++;
1430 log_debug("Root directory %s added.", m->path);
1432 } else if (!m->is_root)
1435 if (m->wd <= 0 && j->inotify_fd >= 0) {
1437 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1438 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1441 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1442 inotify_rm_watch(j->inotify_fd, m->wd);
1445 if (j->no_new_files)
1454 if (!de && errno != 0) {
1456 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1462 if (dirent_is_file_with_suffix(de, ".journal") ||
1463 dirent_is_file_with_suffix(de, ".journal~")) {
1464 r = add_file(j, m->path, de->d_name);
1466 log_debug_errno(r, "Failed to add file %s/%s: %m",
1467 m->path, de->d_name);
1468 r = set_put_error(j, r);
1472 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1473 sd_id128_from_string(de->d_name, &id) >= 0) {
1475 r = add_directory(j, m->path, de->d_name);
1477 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
1481 check_network(j, dirfd(d));
1486 static int remove_directory(sd_journal *j, Directory *d) {
1490 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1492 if (j->inotify_fd >= 0)
1493 inotify_rm_watch(j->inotify_fd, d->wd);
1496 hashmap_remove(j->directories_by_path, d->path);
1499 log_debug("Root directory %s removed.", d->path);
1501 log_debug("Directory %s removed.", d->path);
1509 static int add_search_paths(sd_journal *j) {
1511 const char search_paths[] =
1512 "/run/log/journal\0"
1513 "/var/log/journal\0";
1518 /* We ignore most errors here, since the idea is to only open
1519 * what's actually accessible, and ignore the rest. */
1521 NULSTR_FOREACH(p, search_paths) {
1522 r = add_root_directory(j, p);
1523 if (r < 0 && r != -ENOENT) {
1524 r = set_put_error(j, r);
1533 static int add_current_paths(sd_journal *j) {
1538 assert(j->no_new_files);
1540 /* Simply adds all directories for files we have open as
1541 * "root" directories. We don't expect errors here, so we
1542 * treat them as fatal. */
1544 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
1545 _cleanup_free_ char *dir;
1548 dir = dirname_malloc(f->path);
1552 r = add_root_directory(j, dir);
1554 set_put_error(j, r);
1563 static int allocate_inotify(sd_journal *j) {
1566 if (j->inotify_fd < 0) {
1567 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1568 if (j->inotify_fd < 0)
1572 if (!j->directories_by_wd) {
1573 j->directories_by_wd = hashmap_new(NULL);
1574 if (!j->directories_by_wd)
1581 static sd_journal *journal_new(int flags, const char *path) {
1584 j = new0(sd_journal, 1);
1588 j->original_pid = getpid();
1591 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1594 j->path = strdup(path);
1599 j->files = ordered_hashmap_new(&string_hash_ops);
1600 j->directories_by_path = hashmap_new(&string_hash_ops);
1601 j->mmap = mmap_cache_new();
1602 if (!j->files || !j->directories_by_path || !j->mmap)
1608 sd_journal_close(j);
1612 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1616 assert_return(ret, -EINVAL);
1617 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1619 j = journal_new(flags, NULL);
1623 r = add_search_paths(j);
1631 sd_journal_close(j);
1636 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1637 _cleanup_free_ char *root = NULL, *class = NULL;
1642 assert_return(machine, -EINVAL);
1643 assert_return(ret, -EINVAL);
1644 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1645 assert_return(machine_name_is_valid(machine), -EINVAL);
1647 p = strappenda("/run/systemd/machines/", machine);
1648 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1656 if (!streq_ptr(class, "container"))
1659 j = journal_new(flags, NULL);
1666 r = add_search_paths(j);
1674 sd_journal_close(j);
1678 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1682 assert_return(ret, -EINVAL);
1683 assert_return(path, -EINVAL);
1684 assert_return(flags == 0, -EINVAL);
1686 j = journal_new(flags, path);
1690 r = add_root_directory(j, path);
1692 set_put_error(j, r);
1700 sd_journal_close(j);
1705 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1710 assert_return(ret, -EINVAL);
1711 assert_return(flags == 0, -EINVAL);
1713 j = journal_new(flags, NULL);
1717 STRV_FOREACH(path, paths) {
1718 r = add_any_file(j, *path);
1720 log_error_errno(r, "Failed to open %s: %m", *path);
1725 j->no_new_files = true;
1731 sd_journal_close(j);
1736 _public_ void sd_journal_close(sd_journal *j) {
1743 sd_journal_flush_matches(j);
1745 while ((f = ordered_hashmap_steal_first(j->files)))
1746 journal_file_close(f);
1748 ordered_hashmap_free(j->files);
1750 while ((d = hashmap_first(j->directories_by_path)))
1751 remove_directory(j, d);
1753 while ((d = hashmap_first(j->directories_by_wd)))
1754 remove_directory(j, d);
1756 hashmap_free(j->directories_by_path);
1757 hashmap_free(j->directories_by_wd);
1759 safe_close(j->inotify_fd);
1762 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1763 mmap_cache_unref(j->mmap);
1768 free(j->unique_field);
1769 set_free(j->errors);
1773 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1778 assert_return(j, -EINVAL);
1779 assert_return(!journal_pid_changed(j), -ECHILD);
1780 assert_return(ret, -EINVAL);
1782 f = j->current_file;
1784 return -EADDRNOTAVAIL;
1786 if (f->current_offset <= 0)
1787 return -EADDRNOTAVAIL;
1789 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1793 *ret = le64toh(o->entry.realtime);
1797 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1803 assert_return(j, -EINVAL);
1804 assert_return(!journal_pid_changed(j), -ECHILD);
1806 f = j->current_file;
1808 return -EADDRNOTAVAIL;
1810 if (f->current_offset <= 0)
1811 return -EADDRNOTAVAIL;
1813 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1818 *ret_boot_id = o->entry.boot_id;
1820 r = sd_id128_get_boot(&id);
1824 if (!sd_id128_equal(id, o->entry.boot_id))
1829 *ret = le64toh(o->entry.monotonic);
1834 static bool field_is_valid(const char *field) {
1842 if (startswith(field, "__"))
1845 for (p = field; *p; p++) {
1850 if (*p >= 'A' && *p <= 'Z')
1853 if (*p >= '0' && *p <= '9')
1862 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1865 size_t field_length;
1869 assert_return(j, -EINVAL);
1870 assert_return(!journal_pid_changed(j), -ECHILD);
1871 assert_return(field, -EINVAL);
1872 assert_return(data, -EINVAL);
1873 assert_return(size, -EINVAL);
1874 assert_return(field_is_valid(field), -EINVAL);
1876 f = j->current_file;
1878 return -EADDRNOTAVAIL;
1880 if (f->current_offset <= 0)
1881 return -EADDRNOTAVAIL;
1883 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1887 field_length = strlen(field);
1889 n = journal_file_entry_n_items(o);
1890 for (i = 0; i < n; i++) {
1896 p = le64toh(o->entry.items[i].object_offset);
1897 le_hash = o->entry.items[i].hash;
1898 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1902 if (le_hash != o->data.hash)
1905 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1907 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1909 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1910 if (decompress_startswith(compression,
1912 &f->compress_buffer, &f->compress_buffer_size,
1913 field, field_length, '=')) {
1917 r = decompress_blob(compression,
1919 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1924 *data = f->compress_buffer;
1925 *size = (size_t) rsize;
1930 return -EPROTONOSUPPORT;
1932 } else if (l >= field_length+1 &&
1933 memcmp(o->data.payload, field, field_length) == 0 &&
1934 o->data.payload[field_length] == '=') {
1938 if ((uint64_t) t != l)
1941 *data = o->data.payload;
1947 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1955 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
1960 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1963 /* We can't read objects larger than 4G on a 32bit machine */
1964 if ((uint64_t) t != l)
1967 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1969 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1973 r = decompress_blob(compression,
1974 o->data.payload, l, &f->compress_buffer,
1975 &f->compress_buffer_size, &rsize, j->data_threshold);
1979 *data = f->compress_buffer;
1980 *size = (size_t) rsize;
1982 return -EPROTONOSUPPORT;
1985 *data = o->data.payload;
1992 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1999 assert_return(j, -EINVAL);
2000 assert_return(!journal_pid_changed(j), -ECHILD);
2001 assert_return(data, -EINVAL);
2002 assert_return(size, -EINVAL);
2004 f = j->current_file;
2006 return -EADDRNOTAVAIL;
2008 if (f->current_offset <= 0)
2009 return -EADDRNOTAVAIL;
2011 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2015 n = journal_file_entry_n_items(o);
2016 if (j->current_field >= n)
2019 p = le64toh(o->entry.items[j->current_field].object_offset);
2020 le_hash = o->entry.items[j->current_field].hash;
2021 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2025 if (le_hash != o->data.hash)
2028 r = return_data(j, f, o, data, size);
2032 j->current_field ++;
2037 _public_ void sd_journal_restart_data(sd_journal *j) {
2041 j->current_field = 0;
2044 _public_ int sd_journal_get_fd(sd_journal *j) {
2047 assert_return(j, -EINVAL);
2048 assert_return(!journal_pid_changed(j), -ECHILD);
2050 if (j->inotify_fd >= 0)
2051 return j->inotify_fd;
2053 r = allocate_inotify(j);
2057 /* Iterate through all dirs again, to add them to the
2059 if (j->no_new_files)
2060 r = add_current_paths(j);
2062 r = add_root_directory(j, j->path);
2064 r = add_search_paths(j);
2068 return j->inotify_fd;
2071 _public_ int sd_journal_get_events(sd_journal *j) {
2074 assert_return(j, -EINVAL);
2075 assert_return(!journal_pid_changed(j), -ECHILD);
2077 fd = sd_journal_get_fd(j);
2084 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2087 assert_return(j, -EINVAL);
2088 assert_return(!journal_pid_changed(j), -ECHILD);
2089 assert_return(timeout_usec, -EINVAL);
2091 fd = sd_journal_get_fd(j);
2095 if (!j->on_network) {
2096 *timeout_usec = (uint64_t) -1;
2100 /* If we are on the network we need to regularly check for
2101 * changes manually */
2103 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2107 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2114 /* Is this a subdirectory we watch? */
2115 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2119 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2120 (endswith(e->name, ".journal") ||
2121 endswith(e->name, ".journal~"))) {
2123 /* Event for a journal file */
2125 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2126 r = add_file(j, d->path, e->name);
2128 log_debug_errno(r, "Failed to add file %s/%s: %m",
2130 set_put_error(j, r);
2133 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2135 r = remove_file(j, d->path, e->name);
2137 log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
2140 } else if (!d->is_root && e->len == 0) {
2142 /* Event for a subdirectory */
2144 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2145 r = remove_directory(j, d);
2147 log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
2151 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2153 /* Event for root directory */
2155 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2156 r = add_directory(j, d->path, e->name);
2158 log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
2165 if (e->mask & IN_IGNORED)
2168 log_warning("Unknown inotify event.");
2171 static int determine_change(sd_journal *j) {
2176 b = j->current_invalidate_counter != j->last_invalidate_counter;
2177 j->last_invalidate_counter = j->current_invalidate_counter;
2179 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2182 _public_ int sd_journal_process(sd_journal *j) {
2183 bool got_something = false;
2185 assert_return(j, -EINVAL);
2186 assert_return(!journal_pid_changed(j), -ECHILD);
2188 j->last_process_usec = now(CLOCK_MONOTONIC);
2191 union inotify_event_buffer buffer;
2192 struct inotify_event *e;
2195 l = read(j->inotify_fd, &buffer, sizeof(buffer));
2197 if (errno == EAGAIN || errno == EINTR)
2198 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2203 got_something = true;
2205 FOREACH_INOTIFY_EVENT(e, buffer, l)
2206 process_inotify_event(j, e);
2210 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2214 assert_return(j, -EINVAL);
2215 assert_return(!journal_pid_changed(j), -ECHILD);
2217 if (j->inotify_fd < 0) {
2219 /* This is the first invocation, hence create the
2221 r = sd_journal_get_fd(j);
2225 /* The journal might have changed since the context
2226 * object was created and we weren't watching before,
2227 * hence don't wait for anything, and return
2229 return determine_change(j);
2232 r = sd_journal_get_timeout(j, &t);
2236 if (t != (uint64_t) -1) {
2239 n = now(CLOCK_MONOTONIC);
2240 t = t > n ? t - n : 0;
2242 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2247 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2248 } while (r == -EINTR);
2253 return sd_journal_process(j);
2256 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2260 uint64_t fmin = 0, tmax = 0;
2263 assert_return(j, -EINVAL);
2264 assert_return(!journal_pid_changed(j), -ECHILD);
2265 assert_return(from || to, -EINVAL);
2266 assert_return(from != to, -EINVAL);
2268 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2271 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2284 fmin = MIN(fr, fmin);
2285 tmax = MAX(t, tmax);
2294 return first ? 0 : 1;
2297 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2303 assert_return(j, -EINVAL);
2304 assert_return(!journal_pid_changed(j), -ECHILD);
2305 assert_return(from || to, -EINVAL);
2306 assert_return(from != to, -EINVAL);
2308 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2311 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2321 *from = MIN(fr, *from);
2336 void journal_print_header(sd_journal *j) {
2339 bool newline = false;
2343 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2349 journal_file_print_header(f);
2353 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2358 assert_return(j, -EINVAL);
2359 assert_return(!journal_pid_changed(j), -ECHILD);
2360 assert_return(bytes, -EINVAL);
2362 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2365 if (fstat(f->fd, &st) < 0)
2368 sum += (uint64_t) st.st_blocks * 512ULL;
2375 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2378 assert_return(j, -EINVAL);
2379 assert_return(!journal_pid_changed(j), -ECHILD);
2380 assert_return(!isempty(field), -EINVAL);
2381 assert_return(field_is_valid(field), -EINVAL);
2387 free(j->unique_field);
2388 j->unique_field = f;
2389 j->unique_file = NULL;
2390 j->unique_offset = 0;
2391 j->unique_file_lost = false;
2396 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2399 assert_return(j, -EINVAL);
2400 assert_return(!journal_pid_changed(j), -ECHILD);
2401 assert_return(data, -EINVAL);
2402 assert_return(l, -EINVAL);
2403 assert_return(j->unique_field, -EINVAL);
2405 k = strlen(j->unique_field);
2407 if (!j->unique_file) {
2408 if (j->unique_file_lost)
2411 j->unique_file = ordered_hashmap_first(j->files);
2412 if (!j->unique_file)
2415 j->unique_offset = 0;
2427 /* Proceed to next data object in the field's linked list */
2428 if (j->unique_offset == 0) {
2429 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2433 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2435 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2439 j->unique_offset = le64toh(o->data.next_field_offset);
2442 /* We reached the end of the list? Then start again, with the next file */
2443 if (j->unique_offset == 0) {
2444 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
2445 if (!j->unique_file)
2451 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2452 * instead, so that we can look at this data object at the same
2453 * time as one on another file */
2454 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
2458 /* Let's do the type check by hand, since we used 0 context above. */
2459 if (o->object.type != OBJECT_DATA) {
2460 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
2461 j->unique_file->path, j->unique_offset,
2462 o->object.type, OBJECT_DATA);
2466 r = return_data(j, j->unique_file, o, &odata, &ol);
2470 /* Check if we have at least the field name and "=". */
2472 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2473 j->unique_file->path, j->unique_offset,
2478 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2479 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2480 j->unique_file->path, j->unique_offset,
2485 /* OK, now let's see if we already returned this data
2486 * object by checking if it exists in the earlier
2487 * traversed files. */
2489 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2493 if (of == j->unique_file)
2496 /* Skip this file it didn't have any fields
2498 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2499 le64toh(of->header->n_fields) <= 0)
2502 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2513 r = return_data(j, j->unique_file, o, data, l);
2521 _public_ void sd_journal_restart_unique(sd_journal *j) {
2525 j->unique_file = NULL;
2526 j->unique_offset = 0;
2527 j->unique_file_lost = false;
2530 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2531 assert_return(j, -EINVAL);
2532 assert_return(!journal_pid_changed(j), -ECHILD);
2534 return !j->on_network;
2537 static char *lookup_field(const char *field, void *userdata) {
2538 sd_journal *j = userdata;
2546 r = sd_journal_get_data(j, field, &data, &size);
2548 size > REPLACE_VAR_MAX)
2549 return strdup(field);
2551 d = strlen(field) + 1;
2553 return strndup((const char*) data + d, size - d);
2556 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2560 _cleanup_free_ char *text = NULL, *cid = NULL;
2564 assert_return(j, -EINVAL);
2565 assert_return(!journal_pid_changed(j), -ECHILD);
2566 assert_return(ret, -EINVAL);
2568 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2572 cid = strndup((const char*) data + 11, size - 11);
2576 r = sd_id128_from_string(cid, &id);
2580 r = catalog_get(CATALOG_DATABASE, id, &text);
2584 t = replace_var(text, lookup_field, j);
2592 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2593 assert_return(ret, -EINVAL);
2595 return catalog_get(CATALOG_DATABASE, id, ret);
2598 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2599 assert_return(j, -EINVAL);
2600 assert_return(!journal_pid_changed(j), -ECHILD);
2602 j->data_threshold = sz;
2606 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2607 assert_return(j, -EINVAL);
2608 assert_return(!journal_pid_changed(j), -ECHILD);
2609 assert_return(sz, -EINVAL);
2611 *sz = j->data_threshold;