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 7168
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 n_entries = le64toh(f->header->n_entries);
728 /* If we hit EOF before, we don't need to look into this file again
729 * unless direction changed or new entries appeared. */
730 if (f->last_direction == direction && f->location_type == LOCATION_TAIL &&
731 n_entries == f->last_n_entries)
734 f->last_n_entries = n_entries;
736 if (f->last_direction == direction && f->current_offset > 0) {
737 /* LOCATION_SEEK here means we did the work in a previous
738 * iteration and the current location already points to a
739 * candidate entry. */
740 if (f->location_type != LOCATION_SEEK) {
741 r = next_with_matches(j, f, direction, &c, &cp);
745 journal_file_save_location(f, c, cp);
748 f->last_direction = direction;
750 r = find_location_with_matches(j, f, direction, &c, &cp);
754 journal_file_save_location(f, c, cp);
757 /* OK, we found the spot, now let's advance until an entry
758 * that is actually different from what we were previously
759 * looking at. This is necessary to handle entries which exist
760 * in two (or more) journal files, and which shall all be
761 * suppressed but one. */
766 if (j->current_location.type == LOCATION_DISCRETE) {
769 k = compare_with_location(f, &j->current_location);
771 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
778 r = next_with_matches(j, f, direction, &c, &cp);
782 journal_file_save_location(f, c, cp);
786 static int real_journal_next(sd_journal *j, direction_t direction) {
787 JournalFile *f, *new_file = NULL;
792 assert_return(j, -EINVAL);
793 assert_return(!journal_pid_changed(j), -ECHILD);
795 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
798 r = next_beyond_location(j, f, direction);
800 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
801 remove_file_real(j, f);
804 f->location_type = LOCATION_TAIL;
813 k = journal_file_compare_locations(f, new_file);
815 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
825 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o);
829 set_location(j, new_file, o);
834 _public_ int sd_journal_next(sd_journal *j) {
835 return real_journal_next(j, DIRECTION_DOWN);
838 _public_ int sd_journal_previous(sd_journal *j) {
839 return real_journal_next(j, DIRECTION_UP);
842 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
845 assert_return(j, -EINVAL);
846 assert_return(!journal_pid_changed(j), -ECHILD);
849 /* If this is not a discrete skip, then at least
850 * resolve the current location */
851 if (j->current_location.type != LOCATION_DISCRETE)
852 return real_journal_next(j, direction);
858 r = real_journal_next(j, direction);
872 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
873 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
876 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
877 return real_journal_next_skip(j, DIRECTION_UP, skip);
880 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
883 char bid[33], sid[33];
885 assert_return(j, -EINVAL);
886 assert_return(!journal_pid_changed(j), -ECHILD);
887 assert_return(cursor, -EINVAL);
889 if (!j->current_file || j->current_file->current_offset <= 0)
890 return -EADDRNOTAVAIL;
892 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
896 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
897 sd_id128_to_string(o->entry.boot_id, bid);
900 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
901 sid, le64toh(o->entry.seqnum),
902 bid, le64toh(o->entry.monotonic),
903 le64toh(o->entry.realtime),
904 le64toh(o->entry.xor_hash)) < 0)
910 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
911 const char *word, *state;
913 unsigned long long seqnum, monotonic, realtime, xor_hash;
915 seqnum_id_set = false,
918 monotonic_set = false,
919 realtime_set = false,
920 xor_hash_set = false;
921 sd_id128_t seqnum_id, boot_id;
923 assert_return(j, -EINVAL);
924 assert_return(!journal_pid_changed(j), -ECHILD);
925 assert_return(!isempty(cursor), -EINVAL);
927 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
931 if (l < 2 || word[1] != '=')
934 item = strndup(word, l);
941 seqnum_id_set = true;
942 k = sd_id128_from_string(item+2, &seqnum_id);
947 if (sscanf(item+2, "%llx", &seqnum) != 1)
953 k = sd_id128_from_string(item+2, &boot_id);
957 monotonic_set = true;
958 if (sscanf(item+2, "%llx", &monotonic) != 1)
964 if (sscanf(item+2, "%llx", &realtime) != 1)
970 if (sscanf(item+2, "%llx", &xor_hash) != 1)
981 if ((!seqnum_set || !seqnum_id_set) &&
982 (!monotonic_set || !boot_id_set) &&
988 j->current_location.type = LOCATION_SEEK;
991 j->current_location.realtime = (uint64_t) realtime;
992 j->current_location.realtime_set = true;
995 if (seqnum_set && seqnum_id_set) {
996 j->current_location.seqnum = (uint64_t) seqnum;
997 j->current_location.seqnum_id = seqnum_id;
998 j->current_location.seqnum_set = true;
1001 if (monotonic_set && boot_id_set) {
1002 j->current_location.monotonic = (uint64_t) monotonic;
1003 j->current_location.boot_id = boot_id;
1004 j->current_location.monotonic_set = true;
1008 j->current_location.xor_hash = (uint64_t) xor_hash;
1009 j->current_location.xor_hash_set = true;
1015 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1017 const char *word, *state;
1021 assert_return(j, -EINVAL);
1022 assert_return(!journal_pid_changed(j), -ECHILD);
1023 assert_return(!isempty(cursor), -EINVAL);
1025 if (!j->current_file || j->current_file->current_offset <= 0)
1026 return -EADDRNOTAVAIL;
1028 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1032 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1033 _cleanup_free_ char *item = NULL;
1035 unsigned long long ll;
1038 if (l < 2 || word[1] != '=')
1041 item = strndup(word, l);
1048 k = sd_id128_from_string(item+2, &id);
1051 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1056 if (sscanf(item+2, "%llx", &ll) != 1)
1058 if (ll != le64toh(o->entry.seqnum))
1063 k = sd_id128_from_string(item+2, &id);
1066 if (!sd_id128_equal(id, o->entry.boot_id))
1071 if (sscanf(item+2, "%llx", &ll) != 1)
1073 if (ll != le64toh(o->entry.monotonic))
1078 if (sscanf(item+2, "%llx", &ll) != 1)
1080 if (ll != le64toh(o->entry.realtime))
1085 if (sscanf(item+2, "%llx", &ll) != 1)
1087 if (ll != le64toh(o->entry.xor_hash))
1097 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1098 assert_return(j, -EINVAL);
1099 assert_return(!journal_pid_changed(j), -ECHILD);
1102 j->current_location.type = LOCATION_SEEK;
1103 j->current_location.boot_id = boot_id;
1104 j->current_location.monotonic = usec;
1105 j->current_location.monotonic_set = true;
1110 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1111 assert_return(j, -EINVAL);
1112 assert_return(!journal_pid_changed(j), -ECHILD);
1115 j->current_location.type = LOCATION_SEEK;
1116 j->current_location.realtime = usec;
1117 j->current_location.realtime_set = true;
1122 _public_ int sd_journal_seek_head(sd_journal *j) {
1123 assert_return(j, -EINVAL);
1124 assert_return(!journal_pid_changed(j), -ECHILD);
1127 j->current_location.type = LOCATION_HEAD;
1132 _public_ int sd_journal_seek_tail(sd_journal *j) {
1133 assert_return(j, -EINVAL);
1134 assert_return(!journal_pid_changed(j), -ECHILD);
1137 j->current_location.type = LOCATION_TAIL;
1142 static void check_network(sd_journal *j, int fd) {
1150 if (fstatfs(fd, &sfs) < 0)
1154 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1155 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1156 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1157 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1158 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1161 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1162 const char *full, *tilded, *atted;
1164 full = strjoina(prefix, ".journal");
1165 tilded = strjoina(full, "~");
1166 atted = strjoina(prefix, "@");
1168 return streq(filename, full) ||
1169 streq(filename, tilded) ||
1170 startswith(filename, atted);
1173 static bool file_type_wanted(int flags, const char *filename) {
1174 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1177 /* no flags set → every type is OK */
1178 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1181 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1184 if (flags & SD_JOURNAL_CURRENT_USER) {
1185 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1187 xsprintf(prefix, "user-"UID_FMT, getuid());
1189 if (file_has_type_prefix(prefix, filename))
1196 static int add_any_file(sd_journal *j, const char *path) {
1197 JournalFile *f = NULL;
1203 if (ordered_hashmap_get(j->files, path))
1206 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1207 log_warning("Too many open journal files, not adding %s.", path);
1208 return set_put_error(j, -ETOOMANYREFS);
1211 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1215 /* journal_file_dump(f); */
1217 r = ordered_hashmap_put(j->files, f->path, f);
1219 journal_file_close(f);
1223 log_debug("File %s added.", f->path);
1225 check_network(j, f->fd);
1227 j->current_invalidate_counter ++;
1232 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1233 _cleanup_free_ char *path = NULL;
1240 if (j->no_new_files ||
1241 !file_type_wanted(j->flags, filename))
1244 path = strjoin(prefix, "/", filename, NULL);
1248 r = add_any_file(j, path);
1254 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1255 _cleanup_free_ char *path;
1262 path = strjoin(prefix, "/", filename, NULL);
1266 f = ordered_hashmap_get(j->files, path);
1270 remove_file_real(j, f);
1274 static void remove_file_real(sd_journal *j, JournalFile *f) {
1278 ordered_hashmap_remove(j->files, f->path);
1280 log_debug("File %s removed.", f->path);
1282 if (j->current_file == f) {
1283 j->current_file = NULL;
1284 j->current_field = 0;
1287 if (j->unique_file == f) {
1288 /* Jump to the next unique_file or NULL if that one was last */
1289 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
1290 j->unique_offset = 0;
1291 if (!j->unique_file)
1292 j->unique_file_lost = true;
1295 journal_file_close(f);
1297 j->current_invalidate_counter ++;
1300 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1301 _cleanup_free_ char *path = NULL;
1303 _cleanup_closedir_ DIR *d = NULL;
1311 log_debug("Considering %s/%s.", prefix, dirname);
1313 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1314 (sd_id128_from_string(dirname, &id) < 0 ||
1315 sd_id128_get_machine(&mid) < 0 ||
1316 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1319 path = strjoin(prefix, "/", dirname, NULL);
1325 log_debug_errno(errno, "Failed to open %s: %m", path);
1326 if (errno == ENOENT)
1331 m = hashmap_get(j->directories_by_path, path);
1333 m = new0(Directory, 1);
1340 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1345 path = NULL; /* avoid freeing in cleanup */
1346 j->current_invalidate_counter ++;
1348 log_debug("Directory %s added.", m->path);
1350 } else if (m->is_root)
1353 if (m->wd <= 0 && j->inotify_fd >= 0) {
1355 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1356 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1357 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1360 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1361 inotify_rm_watch(j->inotify_fd, m->wd);
1369 if (!de && errno != 0) {
1371 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1377 if (dirent_is_file_with_suffix(de, ".journal") ||
1378 dirent_is_file_with_suffix(de, ".journal~")) {
1379 r = add_file(j, m->path, de->d_name);
1381 log_debug_errno(r, "Failed to add file %s/%s: %m",
1382 m->path, de->d_name);
1383 r = set_put_error(j, r);
1390 check_network(j, dirfd(d));
1395 static int add_root_directory(sd_journal *j, const char *p) {
1396 _cleanup_closedir_ DIR *d = NULL;
1403 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1404 !path_startswith(p, "/run"))
1408 p = strjoina(j->prefix, p);
1414 m = hashmap_get(j->directories_by_path, p);
1416 m = new0(Directory, 1);
1421 m->path = strdup(p);
1427 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1433 j->current_invalidate_counter ++;
1435 log_debug("Root directory %s added.", m->path);
1437 } else if (!m->is_root)
1440 if (m->wd <= 0 && j->inotify_fd >= 0) {
1442 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1443 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1446 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1447 inotify_rm_watch(j->inotify_fd, m->wd);
1450 if (j->no_new_files)
1459 if (!de && errno != 0) {
1461 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1467 if (dirent_is_file_with_suffix(de, ".journal") ||
1468 dirent_is_file_with_suffix(de, ".journal~")) {
1469 r = add_file(j, m->path, de->d_name);
1471 log_debug_errno(r, "Failed to add file %s/%s: %m",
1472 m->path, de->d_name);
1473 r = set_put_error(j, r);
1477 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1478 sd_id128_from_string(de->d_name, &id) >= 0) {
1480 r = add_directory(j, m->path, de->d_name);
1482 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
1486 check_network(j, dirfd(d));
1491 static int remove_directory(sd_journal *j, Directory *d) {
1495 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1497 if (j->inotify_fd >= 0)
1498 inotify_rm_watch(j->inotify_fd, d->wd);
1501 hashmap_remove(j->directories_by_path, d->path);
1504 log_debug("Root directory %s removed.", d->path);
1506 log_debug("Directory %s removed.", d->path);
1514 static int add_search_paths(sd_journal *j) {
1516 const char search_paths[] =
1517 "/run/log/journal\0"
1518 "/var/log/journal\0";
1523 /* We ignore most errors here, since the idea is to only open
1524 * what's actually accessible, and ignore the rest. */
1526 NULSTR_FOREACH(p, search_paths) {
1527 r = add_root_directory(j, p);
1528 if (r < 0 && r != -ENOENT) {
1529 r = set_put_error(j, r);
1538 static int add_current_paths(sd_journal *j) {
1543 assert(j->no_new_files);
1545 /* Simply adds all directories for files we have open as
1546 * "root" directories. We don't expect errors here, so we
1547 * treat them as fatal. */
1549 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
1550 _cleanup_free_ char *dir;
1553 dir = dirname_malloc(f->path);
1557 r = add_root_directory(j, dir);
1559 set_put_error(j, r);
1568 static int allocate_inotify(sd_journal *j) {
1571 if (j->inotify_fd < 0) {
1572 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1573 if (j->inotify_fd < 0)
1577 if (!j->directories_by_wd) {
1578 j->directories_by_wd = hashmap_new(NULL);
1579 if (!j->directories_by_wd)
1586 static sd_journal *journal_new(int flags, const char *path) {
1589 j = new0(sd_journal, 1);
1593 j->original_pid = getpid();
1596 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1599 j->path = strdup(path);
1604 j->files = ordered_hashmap_new(&string_hash_ops);
1605 j->directories_by_path = hashmap_new(&string_hash_ops);
1606 j->mmap = mmap_cache_new();
1607 if (!j->files || !j->directories_by_path || !j->mmap)
1613 sd_journal_close(j);
1617 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1621 assert_return(ret, -EINVAL);
1622 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1624 j = journal_new(flags, NULL);
1628 r = add_search_paths(j);
1636 sd_journal_close(j);
1641 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1642 _cleanup_free_ char *root = NULL, *class = NULL;
1647 assert_return(machine, -EINVAL);
1648 assert_return(ret, -EINVAL);
1649 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1650 assert_return(machine_name_is_valid(machine), -EINVAL);
1652 p = strjoina("/run/systemd/machines/", machine);
1653 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1661 if (!streq_ptr(class, "container"))
1664 j = journal_new(flags, NULL);
1671 r = add_search_paths(j);
1679 sd_journal_close(j);
1683 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1687 assert_return(ret, -EINVAL);
1688 assert_return(path, -EINVAL);
1689 assert_return(flags == 0, -EINVAL);
1691 j = journal_new(flags, path);
1695 r = add_root_directory(j, path);
1697 set_put_error(j, r);
1705 sd_journal_close(j);
1710 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1715 assert_return(ret, -EINVAL);
1716 assert_return(flags == 0, -EINVAL);
1718 j = journal_new(flags, NULL);
1722 STRV_FOREACH(path, paths) {
1723 r = add_any_file(j, *path);
1725 log_error_errno(r, "Failed to open %s: %m", *path);
1730 j->no_new_files = true;
1736 sd_journal_close(j);
1741 _public_ void sd_journal_close(sd_journal *j) {
1748 sd_journal_flush_matches(j);
1750 while ((f = ordered_hashmap_steal_first(j->files)))
1751 journal_file_close(f);
1753 ordered_hashmap_free(j->files);
1755 while ((d = hashmap_first(j->directories_by_path)))
1756 remove_directory(j, d);
1758 while ((d = hashmap_first(j->directories_by_wd)))
1759 remove_directory(j, d);
1761 hashmap_free(j->directories_by_path);
1762 hashmap_free(j->directories_by_wd);
1764 safe_close(j->inotify_fd);
1767 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1768 mmap_cache_unref(j->mmap);
1773 free(j->unique_field);
1774 set_free(j->errors);
1778 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1783 assert_return(j, -EINVAL);
1784 assert_return(!journal_pid_changed(j), -ECHILD);
1785 assert_return(ret, -EINVAL);
1787 f = j->current_file;
1789 return -EADDRNOTAVAIL;
1791 if (f->current_offset <= 0)
1792 return -EADDRNOTAVAIL;
1794 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1798 *ret = le64toh(o->entry.realtime);
1802 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1808 assert_return(j, -EINVAL);
1809 assert_return(!journal_pid_changed(j), -ECHILD);
1811 f = j->current_file;
1813 return -EADDRNOTAVAIL;
1815 if (f->current_offset <= 0)
1816 return -EADDRNOTAVAIL;
1818 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1823 *ret_boot_id = o->entry.boot_id;
1825 r = sd_id128_get_boot(&id);
1829 if (!sd_id128_equal(id, o->entry.boot_id))
1834 *ret = le64toh(o->entry.monotonic);
1839 static bool field_is_valid(const char *field) {
1847 if (startswith(field, "__"))
1850 for (p = field; *p; p++) {
1855 if (*p >= 'A' && *p <= 'Z')
1858 if (*p >= '0' && *p <= '9')
1867 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1870 size_t field_length;
1874 assert_return(j, -EINVAL);
1875 assert_return(!journal_pid_changed(j), -ECHILD);
1876 assert_return(field, -EINVAL);
1877 assert_return(data, -EINVAL);
1878 assert_return(size, -EINVAL);
1879 assert_return(field_is_valid(field), -EINVAL);
1881 f = j->current_file;
1883 return -EADDRNOTAVAIL;
1885 if (f->current_offset <= 0)
1886 return -EADDRNOTAVAIL;
1888 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1892 field_length = strlen(field);
1894 n = journal_file_entry_n_items(o);
1895 for (i = 0; i < n; i++) {
1901 p = le64toh(o->entry.items[i].object_offset);
1902 le_hash = o->entry.items[i].hash;
1903 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1907 if (le_hash != o->data.hash)
1910 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1912 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1914 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1915 if (decompress_startswith(compression,
1917 &f->compress_buffer, &f->compress_buffer_size,
1918 field, field_length, '=')) {
1922 r = decompress_blob(compression,
1924 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1929 *data = f->compress_buffer;
1930 *size = (size_t) rsize;
1935 return -EPROTONOSUPPORT;
1937 } else if (l >= field_length+1 &&
1938 memcmp(o->data.payload, field, field_length) == 0 &&
1939 o->data.payload[field_length] == '=') {
1943 if ((uint64_t) t != l)
1946 *data = o->data.payload;
1952 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1960 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
1965 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1968 /* We can't read objects larger than 4G on a 32bit machine */
1969 if ((uint64_t) t != l)
1972 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1974 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1978 r = decompress_blob(compression,
1979 o->data.payload, l, &f->compress_buffer,
1980 &f->compress_buffer_size, &rsize, j->data_threshold);
1984 *data = f->compress_buffer;
1985 *size = (size_t) rsize;
1987 return -EPROTONOSUPPORT;
1990 *data = o->data.payload;
1997 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2004 assert_return(j, -EINVAL);
2005 assert_return(!journal_pid_changed(j), -ECHILD);
2006 assert_return(data, -EINVAL);
2007 assert_return(size, -EINVAL);
2009 f = j->current_file;
2011 return -EADDRNOTAVAIL;
2013 if (f->current_offset <= 0)
2014 return -EADDRNOTAVAIL;
2016 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2020 n = journal_file_entry_n_items(o);
2021 if (j->current_field >= n)
2024 p = le64toh(o->entry.items[j->current_field].object_offset);
2025 le_hash = o->entry.items[j->current_field].hash;
2026 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2030 if (le_hash != o->data.hash)
2033 r = return_data(j, f, o, data, size);
2037 j->current_field ++;
2042 _public_ void sd_journal_restart_data(sd_journal *j) {
2046 j->current_field = 0;
2049 _public_ int sd_journal_get_fd(sd_journal *j) {
2052 assert_return(j, -EINVAL);
2053 assert_return(!journal_pid_changed(j), -ECHILD);
2055 if (j->inotify_fd >= 0)
2056 return j->inotify_fd;
2058 r = allocate_inotify(j);
2062 /* Iterate through all dirs again, to add them to the
2064 if (j->no_new_files)
2065 r = add_current_paths(j);
2067 r = add_root_directory(j, j->path);
2069 r = add_search_paths(j);
2073 return j->inotify_fd;
2076 _public_ int sd_journal_get_events(sd_journal *j) {
2079 assert_return(j, -EINVAL);
2080 assert_return(!journal_pid_changed(j), -ECHILD);
2082 fd = sd_journal_get_fd(j);
2089 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2092 assert_return(j, -EINVAL);
2093 assert_return(!journal_pid_changed(j), -ECHILD);
2094 assert_return(timeout_usec, -EINVAL);
2096 fd = sd_journal_get_fd(j);
2100 if (!j->on_network) {
2101 *timeout_usec = (uint64_t) -1;
2105 /* If we are on the network we need to regularly check for
2106 * changes manually */
2108 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2112 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2119 /* Is this a subdirectory we watch? */
2120 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2124 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2125 (endswith(e->name, ".journal") ||
2126 endswith(e->name, ".journal~"))) {
2128 /* Event for a journal file */
2130 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2131 r = add_file(j, d->path, e->name);
2133 log_debug_errno(r, "Failed to add file %s/%s: %m",
2135 set_put_error(j, r);
2138 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2140 r = remove_file(j, d->path, e->name);
2142 log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
2145 } else if (!d->is_root && e->len == 0) {
2147 /* Event for a subdirectory */
2149 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2150 r = remove_directory(j, d);
2152 log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
2156 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2158 /* Event for root directory */
2160 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2161 r = add_directory(j, d->path, e->name);
2163 log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
2170 if (e->mask & IN_IGNORED)
2173 log_warning("Unknown inotify event.");
2176 static int determine_change(sd_journal *j) {
2181 b = j->current_invalidate_counter != j->last_invalidate_counter;
2182 j->last_invalidate_counter = j->current_invalidate_counter;
2184 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2187 _public_ int sd_journal_process(sd_journal *j) {
2188 bool got_something = false;
2190 assert_return(j, -EINVAL);
2191 assert_return(!journal_pid_changed(j), -ECHILD);
2193 j->last_process_usec = now(CLOCK_MONOTONIC);
2196 union inotify_event_buffer buffer;
2197 struct inotify_event *e;
2200 l = read(j->inotify_fd, &buffer, sizeof(buffer));
2202 if (errno == EAGAIN || errno == EINTR)
2203 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2208 got_something = true;
2210 FOREACH_INOTIFY_EVENT(e, buffer, l)
2211 process_inotify_event(j, e);
2215 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2219 assert_return(j, -EINVAL);
2220 assert_return(!journal_pid_changed(j), -ECHILD);
2222 if (j->inotify_fd < 0) {
2224 /* This is the first invocation, hence create the
2226 r = sd_journal_get_fd(j);
2230 /* The journal might have changed since the context
2231 * object was created and we weren't watching before,
2232 * hence don't wait for anything, and return
2234 return determine_change(j);
2237 r = sd_journal_get_timeout(j, &t);
2241 if (t != (uint64_t) -1) {
2244 n = now(CLOCK_MONOTONIC);
2245 t = t > n ? t - n : 0;
2247 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2252 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2253 } while (r == -EINTR);
2258 return sd_journal_process(j);
2261 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2265 uint64_t fmin = 0, tmax = 0;
2268 assert_return(j, -EINVAL);
2269 assert_return(!journal_pid_changed(j), -ECHILD);
2270 assert_return(from || to, -EINVAL);
2271 assert_return(from != to, -EINVAL);
2273 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2276 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2289 fmin = MIN(fr, fmin);
2290 tmax = MAX(t, tmax);
2299 return first ? 0 : 1;
2302 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2308 assert_return(j, -EINVAL);
2309 assert_return(!journal_pid_changed(j), -ECHILD);
2310 assert_return(from || to, -EINVAL);
2311 assert_return(from != to, -EINVAL);
2313 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2316 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2326 *from = MIN(fr, *from);
2341 void journal_print_header(sd_journal *j) {
2344 bool newline = false;
2348 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2354 journal_file_print_header(f);
2358 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2363 assert_return(j, -EINVAL);
2364 assert_return(!journal_pid_changed(j), -ECHILD);
2365 assert_return(bytes, -EINVAL);
2367 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2370 if (fstat(f->fd, &st) < 0)
2373 sum += (uint64_t) st.st_blocks * 512ULL;
2380 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2383 assert_return(j, -EINVAL);
2384 assert_return(!journal_pid_changed(j), -ECHILD);
2385 assert_return(!isempty(field), -EINVAL);
2386 assert_return(field_is_valid(field), -EINVAL);
2392 free(j->unique_field);
2393 j->unique_field = f;
2394 j->unique_file = NULL;
2395 j->unique_offset = 0;
2396 j->unique_file_lost = false;
2401 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2404 assert_return(j, -EINVAL);
2405 assert_return(!journal_pid_changed(j), -ECHILD);
2406 assert_return(data, -EINVAL);
2407 assert_return(l, -EINVAL);
2408 assert_return(j->unique_field, -EINVAL);
2410 k = strlen(j->unique_field);
2412 if (!j->unique_file) {
2413 if (j->unique_file_lost)
2416 j->unique_file = ordered_hashmap_first(j->files);
2417 if (!j->unique_file)
2420 j->unique_offset = 0;
2432 /* Proceed to next data object in the field's linked list */
2433 if (j->unique_offset == 0) {
2434 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2438 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2440 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2444 j->unique_offset = le64toh(o->data.next_field_offset);
2447 /* We reached the end of the list? Then start again, with the next file */
2448 if (j->unique_offset == 0) {
2449 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
2450 if (!j->unique_file)
2456 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2457 * instead, so that we can look at this data object at the same
2458 * time as one on another file */
2459 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
2463 /* Let's do the type check by hand, since we used 0 context above. */
2464 if (o->object.type != OBJECT_DATA) {
2465 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
2466 j->unique_file->path, j->unique_offset,
2467 o->object.type, OBJECT_DATA);
2471 r = return_data(j, j->unique_file, o, &odata, &ol);
2475 /* Check if we have at least the field name and "=". */
2477 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2478 j->unique_file->path, j->unique_offset,
2483 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2484 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2485 j->unique_file->path, j->unique_offset,
2490 /* OK, now let's see if we already returned this data
2491 * object by checking if it exists in the earlier
2492 * traversed files. */
2494 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2498 if (of == j->unique_file)
2501 /* Skip this file it didn't have any fields
2503 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2504 le64toh(of->header->n_fields) <= 0)
2507 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2518 r = return_data(j, j->unique_file, o, data, l);
2526 _public_ void sd_journal_restart_unique(sd_journal *j) {
2530 j->unique_file = NULL;
2531 j->unique_offset = 0;
2532 j->unique_file_lost = false;
2535 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2536 assert_return(j, -EINVAL);
2537 assert_return(!journal_pid_changed(j), -ECHILD);
2539 return !j->on_network;
2542 static char *lookup_field(const char *field, void *userdata) {
2543 sd_journal *j = userdata;
2551 r = sd_journal_get_data(j, field, &data, &size);
2553 size > REPLACE_VAR_MAX)
2554 return strdup(field);
2556 d = strlen(field) + 1;
2558 return strndup((const char*) data + d, size - d);
2561 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2565 _cleanup_free_ char *text = NULL, *cid = NULL;
2569 assert_return(j, -EINVAL);
2570 assert_return(!journal_pid_changed(j), -ECHILD);
2571 assert_return(ret, -EINVAL);
2573 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2577 cid = strndup((const char*) data + 11, size - 11);
2581 r = sd_id128_from_string(cid, &id);
2585 r = catalog_get(CATALOG_DATABASE, id, &text);
2589 t = replace_var(text, lookup_field, j);
2597 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2598 assert_return(ret, -EINVAL);
2600 return catalog_get(CATALOG_DATABASE, id, ret);
2603 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2604 assert_return(j, -EINVAL);
2605 assert_return(!journal_pid_changed(j), -ECHILD);
2607 j->data_threshold = sz;
2611 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2612 assert_return(j, -EINVAL);
2613 assert_return(!journal_pid_changed(j), -ECHILD);
2614 assert_return(sz, -EINVAL);
2616 *sz = j->data_threshold;