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) {
732 uint64_t cp, n_entries;
738 /* If we hit EOF before, recheck if any new entries arrived. */
739 n_entries = le64toh(f->header->n_entries);
740 if (f->location_type == LOCATION_TAIL && n_entries == f->last_n_entries)
742 f->last_n_entries = n_entries;
744 if (f->last_direction == direction && f->current_offset > 0) {
745 /* LOCATION_SEEK here means we did the work in a previous
746 * iteration and the current location already points to a
747 * candidate entry. */
748 if (f->location_type == LOCATION_SEEK)
751 cp = f->current_offset;
753 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
757 r = next_with_matches(j, f, direction, &c, &cp);
761 r = find_location_with_matches(j, f, direction, &c, &cp);
766 /* OK, we found the spot, now let's advance until an entry
767 * that is actually different from what we were previously
768 * looking at. This is necessary to handle entries which exist
769 * in two (or more) journal files, and which shall all be
770 * suppressed but one. */
775 if (j->current_location.type == LOCATION_DISCRETE) {
778 k = compare_with_location(f, c, &j->current_location);
780 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
785 journal_file_save_location(f, direction, c, cp);
789 r = next_with_matches(j, f, direction, &c, &cp);
795 static int real_journal_next(sd_journal *j, direction_t direction) {
796 JournalFile *f, *new_file = NULL;
801 assert_return(j, -EINVAL);
802 assert_return(!journal_pid_changed(j), -ECHILD);
804 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
807 r = next_beyond_location(j, f, direction);
809 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
810 remove_file_real(j, f);
813 f->location_type = LOCATION_TAIL;
822 k = journal_file_compare_locations(f, new_file);
824 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
834 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o);
838 set_location(j, new_file, o);
843 _public_ int sd_journal_next(sd_journal *j) {
844 return real_journal_next(j, DIRECTION_DOWN);
847 _public_ int sd_journal_previous(sd_journal *j) {
848 return real_journal_next(j, DIRECTION_UP);
851 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
854 assert_return(j, -EINVAL);
855 assert_return(!journal_pid_changed(j), -ECHILD);
858 /* If this is not a discrete skip, then at least
859 * resolve the current location */
860 if (j->current_location.type != LOCATION_DISCRETE)
861 return real_journal_next(j, direction);
867 r = real_journal_next(j, direction);
881 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
882 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
885 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
886 return real_journal_next_skip(j, DIRECTION_UP, skip);
889 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
892 char bid[33], sid[33];
894 assert_return(j, -EINVAL);
895 assert_return(!journal_pid_changed(j), -ECHILD);
896 assert_return(cursor, -EINVAL);
898 if (!j->current_file || j->current_file->current_offset <= 0)
899 return -EADDRNOTAVAIL;
901 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
905 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
906 sd_id128_to_string(o->entry.boot_id, bid);
909 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
910 sid, le64toh(o->entry.seqnum),
911 bid, le64toh(o->entry.monotonic),
912 le64toh(o->entry.realtime),
913 le64toh(o->entry.xor_hash)) < 0)
919 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
920 const char *word, *state;
922 unsigned long long seqnum, monotonic, realtime, xor_hash;
924 seqnum_id_set = false,
927 monotonic_set = false,
928 realtime_set = false,
929 xor_hash_set = false;
930 sd_id128_t seqnum_id, boot_id;
932 assert_return(j, -EINVAL);
933 assert_return(!journal_pid_changed(j), -ECHILD);
934 assert_return(!isempty(cursor), -EINVAL);
936 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
940 if (l < 2 || word[1] != '=')
943 item = strndup(word, l);
950 seqnum_id_set = true;
951 k = sd_id128_from_string(item+2, &seqnum_id);
956 if (sscanf(item+2, "%llx", &seqnum) != 1)
962 k = sd_id128_from_string(item+2, &boot_id);
966 monotonic_set = true;
967 if (sscanf(item+2, "%llx", &monotonic) != 1)
973 if (sscanf(item+2, "%llx", &realtime) != 1)
979 if (sscanf(item+2, "%llx", &xor_hash) != 1)
990 if ((!seqnum_set || !seqnum_id_set) &&
991 (!monotonic_set || !boot_id_set) &&
997 j->current_location.type = LOCATION_SEEK;
1000 j->current_location.realtime = (uint64_t) realtime;
1001 j->current_location.realtime_set = true;
1004 if (seqnum_set && seqnum_id_set) {
1005 j->current_location.seqnum = (uint64_t) seqnum;
1006 j->current_location.seqnum_id = seqnum_id;
1007 j->current_location.seqnum_set = true;
1010 if (monotonic_set && boot_id_set) {
1011 j->current_location.monotonic = (uint64_t) monotonic;
1012 j->current_location.boot_id = boot_id;
1013 j->current_location.monotonic_set = true;
1017 j->current_location.xor_hash = (uint64_t) xor_hash;
1018 j->current_location.xor_hash_set = true;
1024 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1026 const char *word, *state;
1030 assert_return(j, -EINVAL);
1031 assert_return(!journal_pid_changed(j), -ECHILD);
1032 assert_return(!isempty(cursor), -EINVAL);
1034 if (!j->current_file || j->current_file->current_offset <= 0)
1035 return -EADDRNOTAVAIL;
1037 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1041 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1042 _cleanup_free_ char *item = NULL;
1044 unsigned long long ll;
1047 if (l < 2 || word[1] != '=')
1050 item = strndup(word, l);
1057 k = sd_id128_from_string(item+2, &id);
1060 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1065 if (sscanf(item+2, "%llx", &ll) != 1)
1067 if (ll != le64toh(o->entry.seqnum))
1072 k = sd_id128_from_string(item+2, &id);
1075 if (!sd_id128_equal(id, o->entry.boot_id))
1080 if (sscanf(item+2, "%llx", &ll) != 1)
1082 if (ll != le64toh(o->entry.monotonic))
1087 if (sscanf(item+2, "%llx", &ll) != 1)
1089 if (ll != le64toh(o->entry.realtime))
1094 if (sscanf(item+2, "%llx", &ll) != 1)
1096 if (ll != le64toh(o->entry.xor_hash))
1106 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1107 assert_return(j, -EINVAL);
1108 assert_return(!journal_pid_changed(j), -ECHILD);
1111 j->current_location.type = LOCATION_SEEK;
1112 j->current_location.boot_id = boot_id;
1113 j->current_location.monotonic = usec;
1114 j->current_location.monotonic_set = true;
1119 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1120 assert_return(j, -EINVAL);
1121 assert_return(!journal_pid_changed(j), -ECHILD);
1124 j->current_location.type = LOCATION_SEEK;
1125 j->current_location.realtime = usec;
1126 j->current_location.realtime_set = true;
1131 _public_ int sd_journal_seek_head(sd_journal *j) {
1132 assert_return(j, -EINVAL);
1133 assert_return(!journal_pid_changed(j), -ECHILD);
1136 j->current_location.type = LOCATION_HEAD;
1141 _public_ int sd_journal_seek_tail(sd_journal *j) {
1142 assert_return(j, -EINVAL);
1143 assert_return(!journal_pid_changed(j), -ECHILD);
1146 j->current_location.type = LOCATION_TAIL;
1151 static void check_network(sd_journal *j, int fd) {
1159 if (fstatfs(fd, &sfs) < 0)
1163 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1164 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1165 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1166 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1167 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1170 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1171 const char *full, *tilded, *atted;
1173 full = strappenda(prefix, ".journal");
1174 tilded = strappenda(full, "~");
1175 atted = strappenda(prefix, "@");
1177 return streq(filename, full) ||
1178 streq(filename, tilded) ||
1179 startswith(filename, atted);
1182 static bool file_type_wanted(int flags, const char *filename) {
1183 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1186 /* no flags set → every type is OK */
1187 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1190 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1193 if (flags & SD_JOURNAL_CURRENT_USER) {
1194 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1196 assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid())
1197 < (int) sizeof(prefix));
1199 if (file_has_type_prefix(prefix, filename))
1206 static int add_any_file(sd_journal *j, const char *path) {
1207 JournalFile *f = NULL;
1213 if (ordered_hashmap_get(j->files, path))
1216 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1217 log_warning("Too many open journal files, not adding %s.", path);
1218 return set_put_error(j, -ETOOMANYREFS);
1221 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1225 /* journal_file_dump(f); */
1227 r = ordered_hashmap_put(j->files, f->path, f);
1229 journal_file_close(f);
1233 log_debug("File %s added.", f->path);
1235 check_network(j, f->fd);
1237 j->current_invalidate_counter ++;
1242 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1243 _cleanup_free_ char *path = NULL;
1250 if (j->no_new_files ||
1251 !file_type_wanted(j->flags, filename))
1254 path = strjoin(prefix, "/", filename, NULL);
1258 r = add_any_file(j, path);
1264 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1265 _cleanup_free_ char *path;
1272 path = strjoin(prefix, "/", filename, NULL);
1276 f = ordered_hashmap_get(j->files, path);
1280 remove_file_real(j, f);
1284 static void remove_file_real(sd_journal *j, JournalFile *f) {
1288 ordered_hashmap_remove(j->files, f->path);
1290 log_debug("File %s removed.", f->path);
1292 if (j->current_file == f) {
1293 j->current_file = NULL;
1294 j->current_field = 0;
1297 if (j->unique_file == f) {
1298 /* Jump to the next unique_file or NULL if that one was last */
1299 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
1300 j->unique_offset = 0;
1301 if (!j->unique_file)
1302 j->unique_file_lost = true;
1305 journal_file_close(f);
1307 j->current_invalidate_counter ++;
1310 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1311 _cleanup_free_ char *path = NULL;
1313 _cleanup_closedir_ DIR *d = NULL;
1321 log_debug("Considering %s/%s.", prefix, dirname);
1323 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1324 (sd_id128_from_string(dirname, &id) < 0 ||
1325 sd_id128_get_machine(&mid) < 0 ||
1326 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1329 path = strjoin(prefix, "/", dirname, NULL);
1335 log_debug_errno(errno, "Failed to open %s: %m", path);
1336 if (errno == ENOENT)
1341 m = hashmap_get(j->directories_by_path, path);
1343 m = new0(Directory, 1);
1350 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1355 path = NULL; /* avoid freeing in cleanup */
1356 j->current_invalidate_counter ++;
1358 log_debug("Directory %s added.", m->path);
1360 } else if (m->is_root)
1363 if (m->wd <= 0 && j->inotify_fd >= 0) {
1365 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1366 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1367 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1370 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1371 inotify_rm_watch(j->inotify_fd, m->wd);
1379 if (!de && errno != 0) {
1381 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1387 if (dirent_is_file_with_suffix(de, ".journal") ||
1388 dirent_is_file_with_suffix(de, ".journal~")) {
1389 r = add_file(j, m->path, de->d_name);
1391 log_debug_errno(r, "Failed to add file %s/%s: %m",
1392 m->path, de->d_name);
1393 r = set_put_error(j, r);
1400 check_network(j, dirfd(d));
1405 static int add_root_directory(sd_journal *j, const char *p) {
1406 _cleanup_closedir_ DIR *d = NULL;
1413 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1414 !path_startswith(p, "/run"))
1418 p = strappenda(j->prefix, p);
1424 m = hashmap_get(j->directories_by_path, p);
1426 m = new0(Directory, 1);
1431 m->path = strdup(p);
1437 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1443 j->current_invalidate_counter ++;
1445 log_debug("Root directory %s added.", m->path);
1447 } else if (!m->is_root)
1450 if (m->wd <= 0 && j->inotify_fd >= 0) {
1452 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1453 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1456 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1457 inotify_rm_watch(j->inotify_fd, m->wd);
1460 if (j->no_new_files)
1469 if (!de && errno != 0) {
1471 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1477 if (dirent_is_file_with_suffix(de, ".journal") ||
1478 dirent_is_file_with_suffix(de, ".journal~")) {
1479 r = add_file(j, m->path, de->d_name);
1481 log_debug_errno(r, "Failed to add file %s/%s: %m",
1482 m->path, de->d_name);
1483 r = set_put_error(j, r);
1487 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1488 sd_id128_from_string(de->d_name, &id) >= 0) {
1490 r = add_directory(j, m->path, de->d_name);
1492 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
1496 check_network(j, dirfd(d));
1501 static int remove_directory(sd_journal *j, Directory *d) {
1505 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1507 if (j->inotify_fd >= 0)
1508 inotify_rm_watch(j->inotify_fd, d->wd);
1511 hashmap_remove(j->directories_by_path, d->path);
1514 log_debug("Root directory %s removed.", d->path);
1516 log_debug("Directory %s removed.", d->path);
1524 static int add_search_paths(sd_journal *j) {
1526 const char search_paths[] =
1527 "/run/log/journal\0"
1528 "/var/log/journal\0";
1533 /* We ignore most errors here, since the idea is to only open
1534 * what's actually accessible, and ignore the rest. */
1536 NULSTR_FOREACH(p, search_paths) {
1537 r = add_root_directory(j, p);
1538 if (r < 0 && r != -ENOENT) {
1539 r = set_put_error(j, r);
1548 static int add_current_paths(sd_journal *j) {
1553 assert(j->no_new_files);
1555 /* Simply adds all directories for files we have open as
1556 * "root" directories. We don't expect errors here, so we
1557 * treat them as fatal. */
1559 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
1560 _cleanup_free_ char *dir;
1563 dir = dirname_malloc(f->path);
1567 r = add_root_directory(j, dir);
1569 set_put_error(j, r);
1578 static int allocate_inotify(sd_journal *j) {
1581 if (j->inotify_fd < 0) {
1582 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1583 if (j->inotify_fd < 0)
1587 if (!j->directories_by_wd) {
1588 j->directories_by_wd = hashmap_new(NULL);
1589 if (!j->directories_by_wd)
1596 static sd_journal *journal_new(int flags, const char *path) {
1599 j = new0(sd_journal, 1);
1603 j->original_pid = getpid();
1606 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1609 j->path = strdup(path);
1614 j->files = ordered_hashmap_new(&string_hash_ops);
1615 j->directories_by_path = hashmap_new(&string_hash_ops);
1616 j->mmap = mmap_cache_new();
1617 if (!j->files || !j->directories_by_path || !j->mmap)
1623 sd_journal_close(j);
1627 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1631 assert_return(ret, -EINVAL);
1632 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1634 j = journal_new(flags, NULL);
1638 r = add_search_paths(j);
1646 sd_journal_close(j);
1651 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1652 _cleanup_free_ char *root = NULL, *class = NULL;
1657 assert_return(machine, -EINVAL);
1658 assert_return(ret, -EINVAL);
1659 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1660 assert_return(machine_name_is_valid(machine), -EINVAL);
1662 p = strappenda("/run/systemd/machines/", machine);
1663 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1671 if (!streq_ptr(class, "container"))
1674 j = journal_new(flags, NULL);
1681 r = add_search_paths(j);
1689 sd_journal_close(j);
1693 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1697 assert_return(ret, -EINVAL);
1698 assert_return(path, -EINVAL);
1699 assert_return(flags == 0, -EINVAL);
1701 j = journal_new(flags, path);
1705 r = add_root_directory(j, path);
1707 set_put_error(j, r);
1715 sd_journal_close(j);
1720 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1725 assert_return(ret, -EINVAL);
1726 assert_return(flags == 0, -EINVAL);
1728 j = journal_new(flags, NULL);
1732 STRV_FOREACH(path, paths) {
1733 r = add_any_file(j, *path);
1735 log_error_errno(r, "Failed to open %s: %m", *path);
1740 j->no_new_files = true;
1746 sd_journal_close(j);
1751 _public_ void sd_journal_close(sd_journal *j) {
1758 sd_journal_flush_matches(j);
1760 while ((f = ordered_hashmap_steal_first(j->files)))
1761 journal_file_close(f);
1763 ordered_hashmap_free(j->files);
1765 while ((d = hashmap_first(j->directories_by_path)))
1766 remove_directory(j, d);
1768 while ((d = hashmap_first(j->directories_by_wd)))
1769 remove_directory(j, d);
1771 hashmap_free(j->directories_by_path);
1772 hashmap_free(j->directories_by_wd);
1774 safe_close(j->inotify_fd);
1777 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1778 mmap_cache_unref(j->mmap);
1783 free(j->unique_field);
1784 set_free(j->errors);
1788 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1793 assert_return(j, -EINVAL);
1794 assert_return(!journal_pid_changed(j), -ECHILD);
1795 assert_return(ret, -EINVAL);
1797 f = j->current_file;
1799 return -EADDRNOTAVAIL;
1801 if (f->current_offset <= 0)
1802 return -EADDRNOTAVAIL;
1804 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1808 *ret = le64toh(o->entry.realtime);
1812 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1818 assert_return(j, -EINVAL);
1819 assert_return(!journal_pid_changed(j), -ECHILD);
1821 f = j->current_file;
1823 return -EADDRNOTAVAIL;
1825 if (f->current_offset <= 0)
1826 return -EADDRNOTAVAIL;
1828 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1833 *ret_boot_id = o->entry.boot_id;
1835 r = sd_id128_get_boot(&id);
1839 if (!sd_id128_equal(id, o->entry.boot_id))
1844 *ret = le64toh(o->entry.monotonic);
1849 static bool field_is_valid(const char *field) {
1857 if (startswith(field, "__"))
1860 for (p = field; *p; p++) {
1865 if (*p >= 'A' && *p <= 'Z')
1868 if (*p >= '0' && *p <= '9')
1877 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1880 size_t field_length;
1884 assert_return(j, -EINVAL);
1885 assert_return(!journal_pid_changed(j), -ECHILD);
1886 assert_return(field, -EINVAL);
1887 assert_return(data, -EINVAL);
1888 assert_return(size, -EINVAL);
1889 assert_return(field_is_valid(field), -EINVAL);
1891 f = j->current_file;
1893 return -EADDRNOTAVAIL;
1895 if (f->current_offset <= 0)
1896 return -EADDRNOTAVAIL;
1898 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1902 field_length = strlen(field);
1904 n = journal_file_entry_n_items(o);
1905 for (i = 0; i < n; i++) {
1911 p = le64toh(o->entry.items[i].object_offset);
1912 le_hash = o->entry.items[i].hash;
1913 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1917 if (le_hash != o->data.hash)
1920 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1922 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1924 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1925 if (decompress_startswith(compression,
1927 &f->compress_buffer, &f->compress_buffer_size,
1928 field, field_length, '=')) {
1932 r = decompress_blob(compression,
1934 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1939 *data = f->compress_buffer;
1940 *size = (size_t) rsize;
1945 return -EPROTONOSUPPORT;
1947 } else if (l >= field_length+1 &&
1948 memcmp(o->data.payload, field, field_length) == 0 &&
1949 o->data.payload[field_length] == '=') {
1953 if ((uint64_t) t != l)
1956 *data = o->data.payload;
1962 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1970 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
1975 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1978 /* We can't read objects larger than 4G on a 32bit machine */
1979 if ((uint64_t) t != l)
1982 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1984 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1988 r = decompress_blob(compression,
1989 o->data.payload, l, &f->compress_buffer,
1990 &f->compress_buffer_size, &rsize, j->data_threshold);
1994 *data = f->compress_buffer;
1995 *size = (size_t) rsize;
1997 return -EPROTONOSUPPORT;
2000 *data = o->data.payload;
2007 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2014 assert_return(j, -EINVAL);
2015 assert_return(!journal_pid_changed(j), -ECHILD);
2016 assert_return(data, -EINVAL);
2017 assert_return(size, -EINVAL);
2019 f = j->current_file;
2021 return -EADDRNOTAVAIL;
2023 if (f->current_offset <= 0)
2024 return -EADDRNOTAVAIL;
2026 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2030 n = journal_file_entry_n_items(o);
2031 if (j->current_field >= n)
2034 p = le64toh(o->entry.items[j->current_field].object_offset);
2035 le_hash = o->entry.items[j->current_field].hash;
2036 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2040 if (le_hash != o->data.hash)
2043 r = return_data(j, f, o, data, size);
2047 j->current_field ++;
2052 _public_ void sd_journal_restart_data(sd_journal *j) {
2056 j->current_field = 0;
2059 _public_ int sd_journal_get_fd(sd_journal *j) {
2062 assert_return(j, -EINVAL);
2063 assert_return(!journal_pid_changed(j), -ECHILD);
2065 if (j->inotify_fd >= 0)
2066 return j->inotify_fd;
2068 r = allocate_inotify(j);
2072 /* Iterate through all dirs again, to add them to the
2074 if (j->no_new_files)
2075 r = add_current_paths(j);
2077 r = add_root_directory(j, j->path);
2079 r = add_search_paths(j);
2083 return j->inotify_fd;
2086 _public_ int sd_journal_get_events(sd_journal *j) {
2089 assert_return(j, -EINVAL);
2090 assert_return(!journal_pid_changed(j), -ECHILD);
2092 fd = sd_journal_get_fd(j);
2099 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2102 assert_return(j, -EINVAL);
2103 assert_return(!journal_pid_changed(j), -ECHILD);
2104 assert_return(timeout_usec, -EINVAL);
2106 fd = sd_journal_get_fd(j);
2110 if (!j->on_network) {
2111 *timeout_usec = (uint64_t) -1;
2115 /* If we are on the network we need to regularly check for
2116 * changes manually */
2118 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2122 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2129 /* Is this a subdirectory we watch? */
2130 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2134 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2135 (endswith(e->name, ".journal") ||
2136 endswith(e->name, ".journal~"))) {
2138 /* Event for a journal file */
2140 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2141 r = add_file(j, d->path, e->name);
2143 log_debug_errno(r, "Failed to add file %s/%s: %m",
2145 set_put_error(j, r);
2148 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2150 r = remove_file(j, d->path, e->name);
2152 log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
2155 } else if (!d->is_root && e->len == 0) {
2157 /* Event for a subdirectory */
2159 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2160 r = remove_directory(j, d);
2162 log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
2166 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2168 /* Event for root directory */
2170 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2171 r = add_directory(j, d->path, e->name);
2173 log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
2180 if (e->mask & IN_IGNORED)
2183 log_warning("Unknown inotify event.");
2186 static int determine_change(sd_journal *j) {
2191 b = j->current_invalidate_counter != j->last_invalidate_counter;
2192 j->last_invalidate_counter = j->current_invalidate_counter;
2194 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2197 _public_ int sd_journal_process(sd_journal *j) {
2198 bool got_something = false;
2200 assert_return(j, -EINVAL);
2201 assert_return(!journal_pid_changed(j), -ECHILD);
2203 j->last_process_usec = now(CLOCK_MONOTONIC);
2206 uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event);
2207 struct inotify_event *e;
2210 l = read(j->inotify_fd, buffer, sizeof(buffer));
2212 if (errno == EAGAIN || errno == EINTR)
2213 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2218 got_something = true;
2220 FOREACH_INOTIFY_EVENT(e, buffer, l)
2221 process_inotify_event(j, e);
2225 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2229 assert_return(j, -EINVAL);
2230 assert_return(!journal_pid_changed(j), -ECHILD);
2232 if (j->inotify_fd < 0) {
2234 /* This is the first invocation, hence create the
2236 r = sd_journal_get_fd(j);
2240 /* The journal might have changed since the context
2241 * object was created and we weren't watching before,
2242 * hence don't wait for anything, and return
2244 return determine_change(j);
2247 r = sd_journal_get_timeout(j, &t);
2251 if (t != (uint64_t) -1) {
2254 n = now(CLOCK_MONOTONIC);
2255 t = t > n ? t - n : 0;
2257 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2262 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2263 } while (r == -EINTR);
2268 return sd_journal_process(j);
2271 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2275 uint64_t fmin = 0, tmax = 0;
2278 assert_return(j, -EINVAL);
2279 assert_return(!journal_pid_changed(j), -ECHILD);
2280 assert_return(from || to, -EINVAL);
2281 assert_return(from != to, -EINVAL);
2283 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2286 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2299 fmin = MIN(fr, fmin);
2300 tmax = MAX(t, tmax);
2309 return first ? 0 : 1;
2312 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2318 assert_return(j, -EINVAL);
2319 assert_return(!journal_pid_changed(j), -ECHILD);
2320 assert_return(from || to, -EINVAL);
2321 assert_return(from != to, -EINVAL);
2323 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2326 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2336 *from = MIN(fr, *from);
2351 void journal_print_header(sd_journal *j) {
2354 bool newline = false;
2358 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2364 journal_file_print_header(f);
2368 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2373 assert_return(j, -EINVAL);
2374 assert_return(!journal_pid_changed(j), -ECHILD);
2375 assert_return(bytes, -EINVAL);
2377 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2380 if (fstat(f->fd, &st) < 0)
2383 sum += (uint64_t) st.st_blocks * 512ULL;
2390 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2393 assert_return(j, -EINVAL);
2394 assert_return(!journal_pid_changed(j), -ECHILD);
2395 assert_return(!isempty(field), -EINVAL);
2396 assert_return(field_is_valid(field), -EINVAL);
2402 free(j->unique_field);
2403 j->unique_field = f;
2404 j->unique_file = NULL;
2405 j->unique_offset = 0;
2406 j->unique_file_lost = false;
2411 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2414 assert_return(j, -EINVAL);
2415 assert_return(!journal_pid_changed(j), -ECHILD);
2416 assert_return(data, -EINVAL);
2417 assert_return(l, -EINVAL);
2418 assert_return(j->unique_field, -EINVAL);
2420 k = strlen(j->unique_field);
2422 if (!j->unique_file) {
2423 if (j->unique_file_lost)
2426 j->unique_file = ordered_hashmap_first(j->files);
2427 if (!j->unique_file)
2430 j->unique_offset = 0;
2442 /* Proceed to next data object in the field's linked list */
2443 if (j->unique_offset == 0) {
2444 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2448 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2450 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2454 j->unique_offset = le64toh(o->data.next_field_offset);
2457 /* We reached the end of the list? Then start again, with the next file */
2458 if (j->unique_offset == 0) {
2459 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
2460 if (!j->unique_file)
2466 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2467 * instead, so that we can look at this data object at the same
2468 * time as one on another file */
2469 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
2473 /* Let's do the type check by hand, since we used 0 context above. */
2474 if (o->object.type != OBJECT_DATA) {
2475 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
2476 j->unique_file->path, j->unique_offset,
2477 o->object.type, OBJECT_DATA);
2481 r = return_data(j, j->unique_file, o, &odata, &ol);
2485 /* Check if we have at least the field name and "=". */
2487 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2488 j->unique_file->path, j->unique_offset,
2493 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2494 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2495 j->unique_file->path, j->unique_offset,
2500 /* OK, now let's see if we already returned this data
2501 * object by checking if it exists in the earlier
2502 * traversed files. */
2504 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2508 if (of == j->unique_file)
2511 /* Skip this file it didn't have any fields
2513 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2514 le64toh(of->header->n_fields) <= 0)
2517 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2528 r = return_data(j, j->unique_file, o, data, l);
2536 _public_ void sd_journal_restart_unique(sd_journal *j) {
2540 j->unique_file = NULL;
2541 j->unique_offset = 0;
2542 j->unique_file_lost = false;
2545 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2546 assert_return(j, -EINVAL);
2547 assert_return(!journal_pid_changed(j), -ECHILD);
2549 return !j->on_network;
2552 static char *lookup_field(const char *field, void *userdata) {
2553 sd_journal *j = userdata;
2561 r = sd_journal_get_data(j, field, &data, &size);
2563 size > REPLACE_VAR_MAX)
2564 return strdup(field);
2566 d = strlen(field) + 1;
2568 return strndup((const char*) data + d, size - d);
2571 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2575 _cleanup_free_ char *text = NULL, *cid = NULL;
2579 assert_return(j, -EINVAL);
2580 assert_return(!journal_pid_changed(j), -ECHILD);
2581 assert_return(ret, -EINVAL);
2583 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2587 cid = strndup((const char*) data + 11, size - 11);
2591 r = sd_id128_from_string(cid, &id);
2595 r = catalog_get(CATALOG_DATABASE, id, &text);
2599 t = replace_var(text, lookup_field, j);
2607 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2608 assert_return(ret, -EINVAL);
2610 return catalog_get(CATALOG_DATABASE, id, ret);
2613 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2614 assert_return(j, -EINVAL);
2615 assert_return(!journal_pid_changed(j), -ECHILD);
2617 j->data_threshold = sz;
2621 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2622 assert_return(j, -EINVAL);
2623 assert_return(!journal_pid_changed(j), -ECHILD);
2624 assert_return(sz, -EINVAL);
2626 *sz = j->data_threshold;