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, LocationType type, JournalFile *f, Object *o,
118 direction_t direction, uint64_t offset) {
120 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
124 init_location(&j->current_location, type, f, o);
127 j->current_field = 0;
129 f->last_direction = direction;
130 f->current_offset = offset;
132 /* Let f know its candidate entry was picked. */
133 assert(f->location_type == LOCATION_SEEK);
134 f->location_type = LOCATION_DISCRETE;
137 static int match_is_valid(const void *data, size_t size) {
145 if (startswith(data, "__"))
149 for (p = b; p < b + size; p++) {
157 if (*p >= 'A' && *p <= 'Z')
160 if (*p >= '0' && *p <= '9')
169 static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
170 const uint8_t *a = _a, *b = _b;
173 for (j = 0; j < s && j < t; j++) {
182 assert_not_reached("\"=\" not found");
185 static Match *match_new(Match *p, MatchType t) {
196 LIST_PREPEND(matches, p->matches, m);
202 static void match_free(Match *m) {
206 match_free(m->matches);
209 LIST_REMOVE(matches, m->parent->matches, m);
215 static void match_free_if_empty(Match *m) {
216 if (!m || m->matches)
222 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
223 Match *l3, *l4, *add_here = NULL, *m;
226 assert_return(j, -EINVAL);
227 assert_return(!journal_pid_changed(j), -ECHILD);
228 assert_return(data, -EINVAL);
233 assert_return(match_is_valid(data, size), -EINVAL);
239 * level 4: concrete matches */
242 j->level0 = match_new(NULL, MATCH_AND_TERM);
248 j->level1 = match_new(j->level0, MATCH_OR_TERM);
254 j->level2 = match_new(j->level1, MATCH_AND_TERM);
259 assert(j->level0->type == MATCH_AND_TERM);
260 assert(j->level1->type == MATCH_OR_TERM);
261 assert(j->level2->type == MATCH_AND_TERM);
263 le_hash = htole64(hash64(data, size));
265 LIST_FOREACH(matches, l3, j->level2->matches) {
266 assert(l3->type == MATCH_OR_TERM);
268 LIST_FOREACH(matches, l4, l3->matches) {
269 assert(l4->type == MATCH_DISCRETE);
271 /* Exactly the same match already? Then ignore
273 if (l4->le_hash == le_hash &&
275 memcmp(l4->data, data, size) == 0)
278 /* Same field? Then let's add this to this OR term */
279 if (same_field(data, size, l4->data, l4->size)) {
290 add_here = match_new(j->level2, MATCH_OR_TERM);
295 m = match_new(add_here, MATCH_DISCRETE);
299 m->le_hash = le_hash;
301 m->data = memdup(data, size);
310 match_free_if_empty(add_here);
311 match_free_if_empty(j->level2);
312 match_free_if_empty(j->level1);
313 match_free_if_empty(j->level0);
318 _public_ int sd_journal_add_conjunction(sd_journal *j) {
319 assert_return(j, -EINVAL);
320 assert_return(!journal_pid_changed(j), -ECHILD);
328 if (!j->level1->matches)
337 _public_ int sd_journal_add_disjunction(sd_journal *j) {
338 assert_return(j, -EINVAL);
339 assert_return(!journal_pid_changed(j), -ECHILD);
350 if (!j->level2->matches)
357 static char *match_make_string(Match *m) {
360 bool enclose = false;
363 return strdup("none");
365 if (m->type == MATCH_DISCRETE)
366 return strndup(m->data, m->size);
369 LIST_FOREACH(matches, i, m->matches) {
372 t = match_make_string(i);
379 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
394 r = strjoin("(", p, ")", NULL);
402 char *journal_make_match_string(sd_journal *j) {
405 return match_make_string(j->level0);
408 _public_ void sd_journal_flush_matches(sd_journal *j) {
413 match_free(j->level0);
415 j->level0 = j->level1 = j->level2 = NULL;
420 static int compare_entry_order(JournalFile *af, Object *_ao,
421 JournalFile *bf, uint64_t bp) {
431 /* The mmap cache might invalidate the object from the first
432 * file if we look at the one from the second file. Hence
433 * temporarily copy the header of the first one, and look at
435 ao = alloca(offsetof(EntryObject, items));
436 memcpy(ao, _ao, offsetof(EntryObject, items));
438 r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
440 return strcmp(af->path, bf->path);
442 /* We operate on two different files here, hence we can access
443 * two objects at the same time, which we normally can't.
445 * If contents and timestamps match, these entries are
446 * identical, even if the seqnum does not match */
448 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
449 ao->entry.monotonic == bo->entry.monotonic &&
450 ao->entry.realtime == bo->entry.realtime &&
451 ao->entry.xor_hash == bo->entry.xor_hash)
454 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
456 /* If this is from the same seqnum source, compare
458 a = le64toh(ao->entry.seqnum);
459 b = le64toh(bo->entry.seqnum);
466 /* Wow! This is weird, different data but the same
467 * seqnums? Something is borked, but let's make the
468 * best of it and compare by time. */
471 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
473 /* If the boot id matches, compare monotonic time */
474 a = le64toh(ao->entry.monotonic);
475 b = le64toh(bo->entry.monotonic);
483 /* Otherwise, compare UTC time */
484 a = le64toh(ao->entry.realtime);
485 b = le64toh(bo->entry.realtime);
492 /* Finally, compare by contents */
493 a = le64toh(ao->entry.xor_hash);
494 b = le64toh(bo->entry.xor_hash);
504 _pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
510 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
512 if (l->monotonic_set &&
513 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
515 le64toh(ao->entry.realtime) == l->realtime &&
517 le64toh(ao->entry.xor_hash) == l->xor_hash)
521 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
523 a = le64toh(ao->entry.seqnum);
531 if (l->monotonic_set &&
532 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
534 a = le64toh(ao->entry.monotonic);
536 if (a < l->monotonic)
538 if (a > l->monotonic)
542 if (l->realtime_set) {
544 a = le64toh(ao->entry.realtime);
552 if (l->xor_hash_set) {
553 a = le64toh(ao->entry.xor_hash);
564 static int next_for_match(
568 uint64_t after_offset,
569 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 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
590 } else if (m->type == MATCH_OR_TERM) {
593 /* Find the earliest match beyond after_offset */
595 LIST_FOREACH(matches, i, m->matches) {
598 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
602 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
610 } else if (m->type == MATCH_AND_TERM) {
611 Match *i, *last_moved;
613 /* Always jump to the next matching entry and repeat
614 * this until we find an offset that matches for all
620 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
624 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
625 last_moved = m->matches;
627 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
630 r = next_for_match(j, i, f, np, direction, NULL, &cp);
634 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
635 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
644 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
656 static int find_location_for_match(
660 direction_t direction,
670 if (m->type == MATCH_DISCRETE) {
673 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
677 /* FIXME: missing: find by monotonic */
679 if (j->current_location.type == LOCATION_HEAD)
680 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
681 if (j->current_location.type == LOCATION_TAIL)
682 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
683 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
684 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
685 if (j->current_location.monotonic_set) {
686 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
690 if (j->current_location.realtime_set)
691 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
693 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
695 } else if (m->type == MATCH_OR_TERM) {
700 /* Find the earliest match */
702 LIST_FOREACH(matches, i, m->matches) {
705 r = find_location_for_match(j, i, f, direction, NULL, &cp);
709 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
717 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
732 assert(m->type == MATCH_AND_TERM);
734 /* First jump to the last match, and then find the
735 * next one where all matches match */
740 LIST_FOREACH(matches, i, m->matches) {
743 r = find_location_for_match(j, i, f, direction, NULL, &cp);
747 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
751 return next_for_match(j, m, f, np, direction, ret, offset);
755 static int find_location_with_matches(
758 direction_t direction,
770 /* No matches is simple */
772 if (j->current_location.type == LOCATION_HEAD)
773 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
774 if (j->current_location.type == LOCATION_TAIL)
775 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
776 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
777 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
778 if (j->current_location.monotonic_set) {
779 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
783 if (j->current_location.realtime_set)
784 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
786 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
788 return find_location_for_match(j, j->level0, f, direction, ret, offset);
791 static int next_with_matches(
794 direction_t direction,
809 /* No matches is easy. We simple advance the file
812 return journal_file_next_entry(f, c, cp, direction, ret, offset);
814 /* If we have a match then we look for the next matching entry
815 * with an offset at least one step larger */
816 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
819 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
827 if (f->last_direction == direction && f->current_offset > 0) {
828 cp = f->current_offset;
830 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
834 r = next_with_matches(j, f, direction, &c, &cp);
838 r = find_location_with_matches(j, f, direction, &c, &cp);
843 /* OK, we found the spot, now let's advance until an entry
844 * that is actually different from what we were previously
845 * looking at. This is necessary to handle entries which exist
846 * in two (or more) journal files, and which shall all be
847 * suppressed but one. */
852 if (j->current_location.type == LOCATION_DISCRETE) {
855 k = compare_with_location(f, c, &j->current_location);
857 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
862 journal_file_save_location(f, direction, c, cp);
871 r = next_with_matches(j, f, direction, &c, &cp);
877 static int real_journal_next(sd_journal *j, direction_t direction) {
878 JournalFile *f, *new_file = NULL;
879 uint64_t new_offset = 0;
885 assert_return(j, -EINVAL);
886 assert_return(!journal_pid_changed(j), -ECHILD);
888 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
891 r = next_beyond_location(j, f, direction, &o, &p);
893 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
894 remove_file_real(j, f);
897 f->location_type = LOCATION_TAIL;
906 k = compare_entry_order(f, o, new_file, new_offset);
908 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
920 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
924 set_location(j, LOCATION_DISCRETE, new_file, o, direction, new_offset);
929 _public_ int sd_journal_next(sd_journal *j) {
930 return real_journal_next(j, DIRECTION_DOWN);
933 _public_ int sd_journal_previous(sd_journal *j) {
934 return real_journal_next(j, DIRECTION_UP);
937 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
940 assert_return(j, -EINVAL);
941 assert_return(!journal_pid_changed(j), -ECHILD);
944 /* If this is not a discrete skip, then at least
945 * resolve the current location */
946 if (j->current_location.type != LOCATION_DISCRETE)
947 return real_journal_next(j, direction);
953 r = real_journal_next(j, direction);
967 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
968 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
971 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
972 return real_journal_next_skip(j, DIRECTION_UP, skip);
975 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
978 char bid[33], sid[33];
980 assert_return(j, -EINVAL);
981 assert_return(!journal_pid_changed(j), -ECHILD);
982 assert_return(cursor, -EINVAL);
984 if (!j->current_file || j->current_file->current_offset <= 0)
985 return -EADDRNOTAVAIL;
987 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
991 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
992 sd_id128_to_string(o->entry.boot_id, bid);
995 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
996 sid, le64toh(o->entry.seqnum),
997 bid, le64toh(o->entry.monotonic),
998 le64toh(o->entry.realtime),
999 le64toh(o->entry.xor_hash)) < 0)
1005 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
1006 const char *word, *state;
1008 unsigned long long seqnum, monotonic, realtime, xor_hash;
1010 seqnum_id_set = false,
1012 boot_id_set = false,
1013 monotonic_set = false,
1014 realtime_set = false,
1015 xor_hash_set = false;
1016 sd_id128_t seqnum_id, boot_id;
1018 assert_return(j, -EINVAL);
1019 assert_return(!journal_pid_changed(j), -ECHILD);
1020 assert_return(!isempty(cursor), -EINVAL);
1022 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1026 if (l < 2 || word[1] != '=')
1029 item = strndup(word, l);
1036 seqnum_id_set = true;
1037 k = sd_id128_from_string(item+2, &seqnum_id);
1042 if (sscanf(item+2, "%llx", &seqnum) != 1)
1048 k = sd_id128_from_string(item+2, &boot_id);
1052 monotonic_set = true;
1053 if (sscanf(item+2, "%llx", &monotonic) != 1)
1058 realtime_set = true;
1059 if (sscanf(item+2, "%llx", &realtime) != 1)
1064 xor_hash_set = true;
1065 if (sscanf(item+2, "%llx", &xor_hash) != 1)
1076 if ((!seqnum_set || !seqnum_id_set) &&
1077 (!monotonic_set || !boot_id_set) &&
1083 j->current_location.type = LOCATION_SEEK;
1086 j->current_location.realtime = (uint64_t) realtime;
1087 j->current_location.realtime_set = true;
1090 if (seqnum_set && seqnum_id_set) {
1091 j->current_location.seqnum = (uint64_t) seqnum;
1092 j->current_location.seqnum_id = seqnum_id;
1093 j->current_location.seqnum_set = true;
1096 if (monotonic_set && boot_id_set) {
1097 j->current_location.monotonic = (uint64_t) monotonic;
1098 j->current_location.boot_id = boot_id;
1099 j->current_location.monotonic_set = true;
1103 j->current_location.xor_hash = (uint64_t) xor_hash;
1104 j->current_location.xor_hash_set = true;
1110 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1112 const char *word, *state;
1116 assert_return(j, -EINVAL);
1117 assert_return(!journal_pid_changed(j), -ECHILD);
1118 assert_return(!isempty(cursor), -EINVAL);
1120 if (!j->current_file || j->current_file->current_offset <= 0)
1121 return -EADDRNOTAVAIL;
1123 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1127 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1128 _cleanup_free_ char *item = NULL;
1130 unsigned long long ll;
1133 if (l < 2 || word[1] != '=')
1136 item = strndup(word, l);
1143 k = sd_id128_from_string(item+2, &id);
1146 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1151 if (sscanf(item+2, "%llx", &ll) != 1)
1153 if (ll != le64toh(o->entry.seqnum))
1158 k = sd_id128_from_string(item+2, &id);
1161 if (!sd_id128_equal(id, o->entry.boot_id))
1166 if (sscanf(item+2, "%llx", &ll) != 1)
1168 if (ll != le64toh(o->entry.monotonic))
1173 if (sscanf(item+2, "%llx", &ll) != 1)
1175 if (ll != le64toh(o->entry.realtime))
1180 if (sscanf(item+2, "%llx", &ll) != 1)
1182 if (ll != le64toh(o->entry.xor_hash))
1192 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1193 assert_return(j, -EINVAL);
1194 assert_return(!journal_pid_changed(j), -ECHILD);
1197 j->current_location.type = LOCATION_SEEK;
1198 j->current_location.boot_id = boot_id;
1199 j->current_location.monotonic = usec;
1200 j->current_location.monotonic_set = true;
1205 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1206 assert_return(j, -EINVAL);
1207 assert_return(!journal_pid_changed(j), -ECHILD);
1210 j->current_location.type = LOCATION_SEEK;
1211 j->current_location.realtime = usec;
1212 j->current_location.realtime_set = true;
1217 _public_ int sd_journal_seek_head(sd_journal *j) {
1218 assert_return(j, -EINVAL);
1219 assert_return(!journal_pid_changed(j), -ECHILD);
1222 j->current_location.type = LOCATION_HEAD;
1227 _public_ int sd_journal_seek_tail(sd_journal *j) {
1228 assert_return(j, -EINVAL);
1229 assert_return(!journal_pid_changed(j), -ECHILD);
1232 j->current_location.type = LOCATION_TAIL;
1237 static void check_network(sd_journal *j, int fd) {
1245 if (fstatfs(fd, &sfs) < 0)
1249 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1250 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1251 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1252 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1253 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1256 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1257 const char *full, *tilded, *atted;
1259 full = strappenda(prefix, ".journal");
1260 tilded = strappenda(full, "~");
1261 atted = strappenda(prefix, "@");
1263 return streq(filename, full) ||
1264 streq(filename, tilded) ||
1265 startswith(filename, atted);
1268 static bool file_type_wanted(int flags, const char *filename) {
1269 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1272 /* no flags set → every type is OK */
1273 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1276 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1279 if (flags & SD_JOURNAL_CURRENT_USER) {
1280 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1282 assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid())
1283 < (int) sizeof(prefix));
1285 if (file_has_type_prefix(prefix, filename))
1292 static int add_any_file(sd_journal *j, const char *path) {
1293 JournalFile *f = NULL;
1299 if (ordered_hashmap_get(j->files, path))
1302 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1303 log_warning("Too many open journal files, not adding %s.", path);
1304 return set_put_error(j, -ETOOMANYREFS);
1307 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1311 /* journal_file_dump(f); */
1313 r = ordered_hashmap_put(j->files, f->path, f);
1315 journal_file_close(f);
1319 log_debug("File %s added.", f->path);
1321 check_network(j, f->fd);
1323 j->current_invalidate_counter ++;
1328 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1329 _cleanup_free_ char *path = NULL;
1336 if (j->no_new_files ||
1337 !file_type_wanted(j->flags, filename))
1340 path = strjoin(prefix, "/", filename, NULL);
1344 r = add_any_file(j, path);
1350 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1351 _cleanup_free_ char *path;
1358 path = strjoin(prefix, "/", filename, NULL);
1362 f = ordered_hashmap_get(j->files, path);
1366 remove_file_real(j, f);
1370 static void remove_file_real(sd_journal *j, JournalFile *f) {
1374 ordered_hashmap_remove(j->files, f->path);
1376 log_debug("File %s removed.", f->path);
1378 if (j->current_file == f) {
1379 j->current_file = NULL;
1380 j->current_field = 0;
1383 if (j->unique_file == f) {
1384 /* Jump to the next unique_file or NULL if that one was last */
1385 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
1386 j->unique_offset = 0;
1387 if (!j->unique_file)
1388 j->unique_file_lost = true;
1391 journal_file_close(f);
1393 j->current_invalidate_counter ++;
1396 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1397 _cleanup_free_ char *path = NULL;
1399 _cleanup_closedir_ DIR *d = NULL;
1407 log_debug("Considering %s/%s.", prefix, dirname);
1409 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1410 (sd_id128_from_string(dirname, &id) < 0 ||
1411 sd_id128_get_machine(&mid) < 0 ||
1412 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1415 path = strjoin(prefix, "/", dirname, NULL);
1421 log_debug_errno(errno, "Failed to open %s: %m", path);
1422 if (errno == ENOENT)
1427 m = hashmap_get(j->directories_by_path, path);
1429 m = new0(Directory, 1);
1436 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1441 path = NULL; /* avoid freeing in cleanup */
1442 j->current_invalidate_counter ++;
1444 log_debug("Directory %s added.", m->path);
1446 } else if (m->is_root)
1449 if (m->wd <= 0 && j->inotify_fd >= 0) {
1451 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1452 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1453 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
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);
1465 if (!de && errno != 0) {
1467 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1473 if (dirent_is_file_with_suffix(de, ".journal") ||
1474 dirent_is_file_with_suffix(de, ".journal~")) {
1475 r = add_file(j, m->path, de->d_name);
1477 log_debug_errno(r, "Failed to add file %s/%s: %m",
1478 m->path, de->d_name);
1479 r = set_put_error(j, r);
1486 check_network(j, dirfd(d));
1491 static int add_root_directory(sd_journal *j, const char *p) {
1492 _cleanup_closedir_ DIR *d = NULL;
1499 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1500 !path_startswith(p, "/run"))
1504 p = strappenda(j->prefix, p);
1510 m = hashmap_get(j->directories_by_path, p);
1512 m = new0(Directory, 1);
1517 m->path = strdup(p);
1523 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1529 j->current_invalidate_counter ++;
1531 log_debug("Root directory %s added.", m->path);
1533 } else if (!m->is_root)
1536 if (m->wd <= 0 && j->inotify_fd >= 0) {
1538 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1539 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1542 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1543 inotify_rm_watch(j->inotify_fd, m->wd);
1546 if (j->no_new_files)
1555 if (!de && errno != 0) {
1557 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1563 if (dirent_is_file_with_suffix(de, ".journal") ||
1564 dirent_is_file_with_suffix(de, ".journal~")) {
1565 r = add_file(j, m->path, de->d_name);
1567 log_debug_errno(r, "Failed to add file %s/%s: %m",
1568 m->path, de->d_name);
1569 r = set_put_error(j, r);
1573 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1574 sd_id128_from_string(de->d_name, &id) >= 0) {
1576 r = add_directory(j, m->path, de->d_name);
1578 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
1582 check_network(j, dirfd(d));
1587 static int remove_directory(sd_journal *j, Directory *d) {
1591 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1593 if (j->inotify_fd >= 0)
1594 inotify_rm_watch(j->inotify_fd, d->wd);
1597 hashmap_remove(j->directories_by_path, d->path);
1600 log_debug("Root directory %s removed.", d->path);
1602 log_debug("Directory %s removed.", d->path);
1610 static int add_search_paths(sd_journal *j) {
1612 const char search_paths[] =
1613 "/run/log/journal\0"
1614 "/var/log/journal\0";
1619 /* We ignore most errors here, since the idea is to only open
1620 * what's actually accessible, and ignore the rest. */
1622 NULSTR_FOREACH(p, search_paths) {
1623 r = add_root_directory(j, p);
1624 if (r < 0 && r != -ENOENT) {
1625 r = set_put_error(j, r);
1634 static int add_current_paths(sd_journal *j) {
1639 assert(j->no_new_files);
1641 /* Simply adds all directories for files we have open as
1642 * "root" directories. We don't expect errors here, so we
1643 * treat them as fatal. */
1645 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
1646 _cleanup_free_ char *dir;
1649 dir = dirname_malloc(f->path);
1653 r = add_root_directory(j, dir);
1655 set_put_error(j, r);
1664 static int allocate_inotify(sd_journal *j) {
1667 if (j->inotify_fd < 0) {
1668 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1669 if (j->inotify_fd < 0)
1673 if (!j->directories_by_wd) {
1674 j->directories_by_wd = hashmap_new(NULL);
1675 if (!j->directories_by_wd)
1682 static sd_journal *journal_new(int flags, const char *path) {
1685 j = new0(sd_journal, 1);
1689 j->original_pid = getpid();
1692 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1695 j->path = strdup(path);
1700 j->files = ordered_hashmap_new(&string_hash_ops);
1701 j->directories_by_path = hashmap_new(&string_hash_ops);
1702 j->mmap = mmap_cache_new();
1703 if (!j->files || !j->directories_by_path || !j->mmap)
1709 sd_journal_close(j);
1713 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1717 assert_return(ret, -EINVAL);
1718 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1720 j = journal_new(flags, NULL);
1724 r = add_search_paths(j);
1732 sd_journal_close(j);
1737 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1738 _cleanup_free_ char *root = NULL, *class = NULL;
1743 assert_return(machine, -EINVAL);
1744 assert_return(ret, -EINVAL);
1745 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1746 assert_return(machine_name_is_valid(machine), -EINVAL);
1748 p = strappenda("/run/systemd/machines/", machine);
1749 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1757 if (!streq_ptr(class, "container"))
1760 j = journal_new(flags, NULL);
1767 r = add_search_paths(j);
1775 sd_journal_close(j);
1779 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1783 assert_return(ret, -EINVAL);
1784 assert_return(path, -EINVAL);
1785 assert_return(flags == 0, -EINVAL);
1787 j = journal_new(flags, path);
1791 r = add_root_directory(j, path);
1793 set_put_error(j, r);
1801 sd_journal_close(j);
1806 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1811 assert_return(ret, -EINVAL);
1812 assert_return(flags == 0, -EINVAL);
1814 j = journal_new(flags, NULL);
1818 STRV_FOREACH(path, paths) {
1819 r = add_any_file(j, *path);
1821 log_error_errno(r, "Failed to open %s: %m", *path);
1826 j->no_new_files = true;
1832 sd_journal_close(j);
1837 _public_ void sd_journal_close(sd_journal *j) {
1844 sd_journal_flush_matches(j);
1846 while ((f = ordered_hashmap_steal_first(j->files)))
1847 journal_file_close(f);
1849 ordered_hashmap_free(j->files);
1851 while ((d = hashmap_first(j->directories_by_path)))
1852 remove_directory(j, d);
1854 while ((d = hashmap_first(j->directories_by_wd)))
1855 remove_directory(j, d);
1857 hashmap_free(j->directories_by_path);
1858 hashmap_free(j->directories_by_wd);
1860 safe_close(j->inotify_fd);
1863 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1864 mmap_cache_unref(j->mmap);
1869 free(j->unique_field);
1870 set_free(j->errors);
1874 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1879 assert_return(j, -EINVAL);
1880 assert_return(!journal_pid_changed(j), -ECHILD);
1881 assert_return(ret, -EINVAL);
1883 f = j->current_file;
1885 return -EADDRNOTAVAIL;
1887 if (f->current_offset <= 0)
1888 return -EADDRNOTAVAIL;
1890 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1894 *ret = le64toh(o->entry.realtime);
1898 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1904 assert_return(j, -EINVAL);
1905 assert_return(!journal_pid_changed(j), -ECHILD);
1907 f = j->current_file;
1909 return -EADDRNOTAVAIL;
1911 if (f->current_offset <= 0)
1912 return -EADDRNOTAVAIL;
1914 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1919 *ret_boot_id = o->entry.boot_id;
1921 r = sd_id128_get_boot(&id);
1925 if (!sd_id128_equal(id, o->entry.boot_id))
1930 *ret = le64toh(o->entry.monotonic);
1935 static bool field_is_valid(const char *field) {
1943 if (startswith(field, "__"))
1946 for (p = field; *p; p++) {
1951 if (*p >= 'A' && *p <= 'Z')
1954 if (*p >= '0' && *p <= '9')
1963 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1966 size_t field_length;
1970 assert_return(j, -EINVAL);
1971 assert_return(!journal_pid_changed(j), -ECHILD);
1972 assert_return(field, -EINVAL);
1973 assert_return(data, -EINVAL);
1974 assert_return(size, -EINVAL);
1975 assert_return(field_is_valid(field), -EINVAL);
1977 f = j->current_file;
1979 return -EADDRNOTAVAIL;
1981 if (f->current_offset <= 0)
1982 return -EADDRNOTAVAIL;
1984 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1988 field_length = strlen(field);
1990 n = journal_file_entry_n_items(o);
1991 for (i = 0; i < n; i++) {
1997 p = le64toh(o->entry.items[i].object_offset);
1998 le_hash = o->entry.items[i].hash;
1999 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2003 if (le_hash != o->data.hash)
2006 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2008 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2010 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
2011 if (decompress_startswith(compression,
2013 &f->compress_buffer, &f->compress_buffer_size,
2014 field, field_length, '=')) {
2018 r = decompress_blob(compression,
2020 &f->compress_buffer, &f->compress_buffer_size, &rsize,
2025 *data = f->compress_buffer;
2026 *size = (size_t) rsize;
2031 return -EPROTONOSUPPORT;
2033 } else if (l >= field_length+1 &&
2034 memcmp(o->data.payload, field, field_length) == 0 &&
2035 o->data.payload[field_length] == '=') {
2039 if ((uint64_t) t != l)
2042 *data = o->data.payload;
2048 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2056 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
2061 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2064 /* We can't read objects larger than 4G on a 32bit machine */
2065 if ((uint64_t) t != l)
2068 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2070 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
2074 r = decompress_blob(compression,
2075 o->data.payload, l, &f->compress_buffer,
2076 &f->compress_buffer_size, &rsize, j->data_threshold);
2080 *data = f->compress_buffer;
2081 *size = (size_t) rsize;
2083 return -EPROTONOSUPPORT;
2086 *data = o->data.payload;
2093 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2100 assert_return(j, -EINVAL);
2101 assert_return(!journal_pid_changed(j), -ECHILD);
2102 assert_return(data, -EINVAL);
2103 assert_return(size, -EINVAL);
2105 f = j->current_file;
2107 return -EADDRNOTAVAIL;
2109 if (f->current_offset <= 0)
2110 return -EADDRNOTAVAIL;
2112 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2116 n = journal_file_entry_n_items(o);
2117 if (j->current_field >= n)
2120 p = le64toh(o->entry.items[j->current_field].object_offset);
2121 le_hash = o->entry.items[j->current_field].hash;
2122 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2126 if (le_hash != o->data.hash)
2129 r = return_data(j, f, o, data, size);
2133 j->current_field ++;
2138 _public_ void sd_journal_restart_data(sd_journal *j) {
2142 j->current_field = 0;
2145 _public_ int sd_journal_get_fd(sd_journal *j) {
2148 assert_return(j, -EINVAL);
2149 assert_return(!journal_pid_changed(j), -ECHILD);
2151 if (j->inotify_fd >= 0)
2152 return j->inotify_fd;
2154 r = allocate_inotify(j);
2158 /* Iterate through all dirs again, to add them to the
2160 if (j->no_new_files)
2161 r = add_current_paths(j);
2163 r = add_root_directory(j, j->path);
2165 r = add_search_paths(j);
2169 return j->inotify_fd;
2172 _public_ int sd_journal_get_events(sd_journal *j) {
2175 assert_return(j, -EINVAL);
2176 assert_return(!journal_pid_changed(j), -ECHILD);
2178 fd = sd_journal_get_fd(j);
2185 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2188 assert_return(j, -EINVAL);
2189 assert_return(!journal_pid_changed(j), -ECHILD);
2190 assert_return(timeout_usec, -EINVAL);
2192 fd = sd_journal_get_fd(j);
2196 if (!j->on_network) {
2197 *timeout_usec = (uint64_t) -1;
2201 /* If we are on the network we need to regularly check for
2202 * changes manually */
2204 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2208 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2215 /* Is this a subdirectory we watch? */
2216 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2220 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2221 (endswith(e->name, ".journal") ||
2222 endswith(e->name, ".journal~"))) {
2224 /* Event for a journal file */
2226 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2227 r = add_file(j, d->path, e->name);
2229 log_debug_errno(r, "Failed to add file %s/%s: %m",
2231 set_put_error(j, r);
2234 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2236 r = remove_file(j, d->path, e->name);
2238 log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
2241 } else if (!d->is_root && e->len == 0) {
2243 /* Event for a subdirectory */
2245 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2246 r = remove_directory(j, d);
2248 log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
2252 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2254 /* Event for root directory */
2256 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2257 r = add_directory(j, d->path, e->name);
2259 log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
2266 if (e->mask & IN_IGNORED)
2269 log_warning("Unknown inotify event.");
2272 static int determine_change(sd_journal *j) {
2277 b = j->current_invalidate_counter != j->last_invalidate_counter;
2278 j->last_invalidate_counter = j->current_invalidate_counter;
2280 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2283 _public_ int sd_journal_process(sd_journal *j) {
2284 bool got_something = false;
2286 assert_return(j, -EINVAL);
2287 assert_return(!journal_pid_changed(j), -ECHILD);
2289 j->last_process_usec = now(CLOCK_MONOTONIC);
2292 uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event);
2293 struct inotify_event *e;
2296 l = read(j->inotify_fd, buffer, sizeof(buffer));
2298 if (errno == EAGAIN || errno == EINTR)
2299 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2304 got_something = true;
2306 FOREACH_INOTIFY_EVENT(e, buffer, l)
2307 process_inotify_event(j, e);
2311 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2315 assert_return(j, -EINVAL);
2316 assert_return(!journal_pid_changed(j), -ECHILD);
2318 if (j->inotify_fd < 0) {
2320 /* This is the first invocation, hence create the
2322 r = sd_journal_get_fd(j);
2326 /* The journal might have changed since the context
2327 * object was created and we weren't watching before,
2328 * hence don't wait for anything, and return
2330 return determine_change(j);
2333 r = sd_journal_get_timeout(j, &t);
2337 if (t != (uint64_t) -1) {
2340 n = now(CLOCK_MONOTONIC);
2341 t = t > n ? t - n : 0;
2343 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2348 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2349 } while (r == -EINTR);
2354 return sd_journal_process(j);
2357 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2361 uint64_t fmin = 0, tmax = 0;
2364 assert_return(j, -EINVAL);
2365 assert_return(!journal_pid_changed(j), -ECHILD);
2366 assert_return(from || to, -EINVAL);
2367 assert_return(from != to, -EINVAL);
2369 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2372 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2385 fmin = MIN(fr, fmin);
2386 tmax = MAX(t, tmax);
2395 return first ? 0 : 1;
2398 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2404 assert_return(j, -EINVAL);
2405 assert_return(!journal_pid_changed(j), -ECHILD);
2406 assert_return(from || to, -EINVAL);
2407 assert_return(from != to, -EINVAL);
2409 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2412 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2422 *from = MIN(fr, *from);
2437 void journal_print_header(sd_journal *j) {
2440 bool newline = false;
2444 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2450 journal_file_print_header(f);
2454 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2459 assert_return(j, -EINVAL);
2460 assert_return(!journal_pid_changed(j), -ECHILD);
2461 assert_return(bytes, -EINVAL);
2463 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2466 if (fstat(f->fd, &st) < 0)
2469 sum += (uint64_t) st.st_blocks * 512ULL;
2476 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2479 assert_return(j, -EINVAL);
2480 assert_return(!journal_pid_changed(j), -ECHILD);
2481 assert_return(!isempty(field), -EINVAL);
2482 assert_return(field_is_valid(field), -EINVAL);
2488 free(j->unique_field);
2489 j->unique_field = f;
2490 j->unique_file = NULL;
2491 j->unique_offset = 0;
2492 j->unique_file_lost = false;
2497 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2500 assert_return(j, -EINVAL);
2501 assert_return(!journal_pid_changed(j), -ECHILD);
2502 assert_return(data, -EINVAL);
2503 assert_return(l, -EINVAL);
2504 assert_return(j->unique_field, -EINVAL);
2506 k = strlen(j->unique_field);
2508 if (!j->unique_file) {
2509 if (j->unique_file_lost)
2512 j->unique_file = ordered_hashmap_first(j->files);
2513 if (!j->unique_file)
2516 j->unique_offset = 0;
2528 /* Proceed to next data object in the field's linked list */
2529 if (j->unique_offset == 0) {
2530 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2534 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2536 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2540 j->unique_offset = le64toh(o->data.next_field_offset);
2543 /* We reached the end of the list? Then start again, with the next file */
2544 if (j->unique_offset == 0) {
2545 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
2546 if (!j->unique_file)
2552 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2553 * instead, so that we can look at this data object at the same
2554 * time as one on another file */
2555 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
2559 /* Let's do the type check by hand, since we used 0 context above. */
2560 if (o->object.type != OBJECT_DATA) {
2561 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
2562 j->unique_file->path, j->unique_offset,
2563 o->object.type, OBJECT_DATA);
2567 r = return_data(j, j->unique_file, o, &odata, &ol);
2571 /* Check if we have at least the field name and "=". */
2573 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2574 j->unique_file->path, j->unique_offset,
2579 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2580 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2581 j->unique_file->path, j->unique_offset,
2586 /* OK, now let's see if we already returned this data
2587 * object by checking if it exists in the earlier
2588 * traversed files. */
2590 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2594 if (of == j->unique_file)
2597 /* Skip this file it didn't have any fields
2599 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2600 le64toh(of->header->n_fields) <= 0)
2603 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2614 r = return_data(j, j->unique_file, o, data, l);
2622 _public_ void sd_journal_restart_unique(sd_journal *j) {
2626 j->unique_file = NULL;
2627 j->unique_offset = 0;
2628 j->unique_file_lost = false;
2631 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2632 assert_return(j, -EINVAL);
2633 assert_return(!journal_pid_changed(j), -ECHILD);
2635 return !j->on_network;
2638 static char *lookup_field(const char *field, void *userdata) {
2639 sd_journal *j = userdata;
2647 r = sd_journal_get_data(j, field, &data, &size);
2649 size > REPLACE_VAR_MAX)
2650 return strdup(field);
2652 d = strlen(field) + 1;
2654 return strndup((const char*) data + d, size - d);
2657 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2661 _cleanup_free_ char *text = NULL, *cid = NULL;
2665 assert_return(j, -EINVAL);
2666 assert_return(!journal_pid_changed(j), -ECHILD);
2667 assert_return(ret, -EINVAL);
2669 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2673 cid = strndup((const char*) data + 11, size - 11);
2677 r = sd_id128_from_string(cid, &id);
2681 r = catalog_get(CATALOG_DATABASE, id, &text);
2685 t = replace_var(text, lookup_field, j);
2693 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2694 assert_return(ret, -EINVAL);
2696 return catalog_get(CATALOG_DATABASE, id, ret);
2699 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2700 assert_return(j, -EINVAL);
2701 assert_return(!journal_pid_changed(j), -ECHILD);
2703 j->data_threshold = sz;
2707 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2708 assert_return(j, -EINVAL);
2709 assert_return(!journal_pid_changed(j), -ECHILD);
2710 assert_return(sz, -EINVAL);
2712 *sz = j->data_threshold;