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 static int compare_entry_order(JournalFile *af, Object *_ao,
416 JournalFile *bf, uint64_t bp) {
426 /* The mmap cache might invalidate the object from the first
427 * file if we look at the one from the second file. Hence
428 * temporarily copy the header of the first one, and look at
430 ao = alloca(offsetof(EntryObject, items));
431 memcpy(ao, _ao, offsetof(EntryObject, items));
433 r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
435 return strcmp(af->path, bf->path);
437 /* We operate on two different files here, hence we can access
438 * two objects at the same time, which we normally can't.
440 * If contents and timestamps match, these entries are
441 * identical, even if the seqnum does not match */
443 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
444 ao->entry.monotonic == bo->entry.monotonic &&
445 ao->entry.realtime == bo->entry.realtime &&
446 ao->entry.xor_hash == bo->entry.xor_hash)
449 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
451 /* If this is from the same seqnum source, compare
453 a = le64toh(ao->entry.seqnum);
454 b = le64toh(bo->entry.seqnum);
461 /* Wow! This is weird, different data but the same
462 * seqnums? Something is borked, but let's make the
463 * best of it and compare by time. */
466 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
468 /* If the boot id matches, compare monotonic time */
469 a = le64toh(ao->entry.monotonic);
470 b = le64toh(bo->entry.monotonic);
478 /* Otherwise, compare UTC time */
479 a = le64toh(ao->entry.realtime);
480 b = le64toh(bo->entry.realtime);
487 /* Finally, compare by contents */
488 a = le64toh(ao->entry.xor_hash);
489 b = le64toh(bo->entry.xor_hash);
499 _pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
505 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
507 if (l->monotonic_set &&
508 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
510 le64toh(ao->entry.realtime) == l->realtime &&
512 le64toh(ao->entry.xor_hash) == l->xor_hash)
516 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
518 a = le64toh(ao->entry.seqnum);
526 if (l->monotonic_set &&
527 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
529 a = le64toh(ao->entry.monotonic);
531 if (a < l->monotonic)
533 if (a > l->monotonic)
537 if (l->realtime_set) {
539 a = le64toh(ao->entry.realtime);
547 if (l->xor_hash_set) {
548 a = le64toh(ao->entry.xor_hash);
559 static int next_for_match(
563 uint64_t after_offset,
564 direction_t direction,
576 if (m->type == MATCH_DISCRETE) {
579 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
583 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
585 } else if (m->type == MATCH_OR_TERM) {
588 /* Find the earliest match beyond after_offset */
590 LIST_FOREACH(matches, i, m->matches) {
593 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
597 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
605 } else if (m->type == MATCH_AND_TERM) {
606 Match *i, *last_moved;
608 /* Always jump to the next matching entry and repeat
609 * this until we find an offset that matches for all
615 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
619 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
620 last_moved = m->matches;
622 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
625 r = next_for_match(j, i, f, np, direction, NULL, &cp);
629 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
630 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
639 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
651 static int find_location_for_match(
655 direction_t direction,
665 if (m->type == MATCH_DISCRETE) {
668 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
672 /* FIXME: missing: find by monotonic */
674 if (j->current_location.type == LOCATION_HEAD)
675 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
676 if (j->current_location.type == LOCATION_TAIL)
677 return journal_file_next_entry_for_data(f, NULL, 0, dp, 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_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
680 if (j->current_location.monotonic_set) {
681 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, 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_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
688 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
690 } else if (m->type == MATCH_OR_TERM) {
695 /* Find the earliest match */
697 LIST_FOREACH(matches, i, m->matches) {
700 r = find_location_for_match(j, i, f, direction, NULL, &cp);
704 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
712 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
727 assert(m->type == MATCH_AND_TERM);
729 /* First jump to the last match, and then find the
730 * next one where all matches match */
735 LIST_FOREACH(matches, i, m->matches) {
738 r = find_location_for_match(j, i, f, direction, NULL, &cp);
742 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
746 return next_for_match(j, m, f, np, direction, ret, offset);
750 static int find_location_with_matches(
753 direction_t direction,
765 /* No matches is simple */
767 if (j->current_location.type == LOCATION_HEAD)
768 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
769 if (j->current_location.type == LOCATION_TAIL)
770 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
771 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
772 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
773 if (j->current_location.monotonic_set) {
774 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
778 if (j->current_location.realtime_set)
779 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
781 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
783 return find_location_for_match(j, j->level0, f, direction, ret, offset);
786 static int next_with_matches(
789 direction_t direction,
804 /* No matches is easy. We simple advance the file
807 return journal_file_next_entry(f, c, cp, direction, ret, offset);
809 /* If we have a match then we look for the next matching entry
810 * with an offset at least one step larger */
811 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
814 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
822 if (f->last_direction == direction && f->current_offset > 0) {
823 cp = f->current_offset;
825 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
829 r = next_with_matches(j, f, direction, &c, &cp);
833 r = find_location_with_matches(j, f, direction, &c, &cp);
838 /* OK, we found the spot, now let's advance until an entry
839 * that is actually different from what we were previously
840 * looking at. This is necessary to handle entries which exist
841 * in two (or more) journal files, and which shall all be
842 * suppressed but one. */
847 if (j->current_location.type == LOCATION_DISCRETE) {
850 k = compare_with_location(f, c, &j->current_location);
852 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
857 journal_file_save_location(f, direction, c, cp);
866 r = next_with_matches(j, f, direction, &c, &cp);
872 static int real_journal_next(sd_journal *j, direction_t direction) {
873 JournalFile *f, *new_file = NULL;
874 uint64_t new_offset = 0;
880 assert_return(j, -EINVAL);
881 assert_return(!journal_pid_changed(j), -ECHILD);
883 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
886 r = next_beyond_location(j, f, direction, &o, &p);
888 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
889 remove_file_real(j, f);
892 f->location_type = LOCATION_TAIL;
901 k = compare_entry_order(f, o, new_file, new_offset);
903 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
915 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
919 set_location(j, new_file, o);
924 _public_ int sd_journal_next(sd_journal *j) {
925 return real_journal_next(j, DIRECTION_DOWN);
928 _public_ int sd_journal_previous(sd_journal *j) {
929 return real_journal_next(j, DIRECTION_UP);
932 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
935 assert_return(j, -EINVAL);
936 assert_return(!journal_pid_changed(j), -ECHILD);
939 /* If this is not a discrete skip, then at least
940 * resolve the current location */
941 if (j->current_location.type != LOCATION_DISCRETE)
942 return real_journal_next(j, direction);
948 r = real_journal_next(j, direction);
962 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
963 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
966 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
967 return real_journal_next_skip(j, DIRECTION_UP, skip);
970 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
973 char bid[33], sid[33];
975 assert_return(j, -EINVAL);
976 assert_return(!journal_pid_changed(j), -ECHILD);
977 assert_return(cursor, -EINVAL);
979 if (!j->current_file || j->current_file->current_offset <= 0)
980 return -EADDRNOTAVAIL;
982 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
986 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
987 sd_id128_to_string(o->entry.boot_id, bid);
990 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
991 sid, le64toh(o->entry.seqnum),
992 bid, le64toh(o->entry.monotonic),
993 le64toh(o->entry.realtime),
994 le64toh(o->entry.xor_hash)) < 0)
1000 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
1001 const char *word, *state;
1003 unsigned long long seqnum, monotonic, realtime, xor_hash;
1005 seqnum_id_set = false,
1007 boot_id_set = false,
1008 monotonic_set = false,
1009 realtime_set = false,
1010 xor_hash_set = false;
1011 sd_id128_t seqnum_id, boot_id;
1013 assert_return(j, -EINVAL);
1014 assert_return(!journal_pid_changed(j), -ECHILD);
1015 assert_return(!isempty(cursor), -EINVAL);
1017 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1021 if (l < 2 || word[1] != '=')
1024 item = strndup(word, l);
1031 seqnum_id_set = true;
1032 k = sd_id128_from_string(item+2, &seqnum_id);
1037 if (sscanf(item+2, "%llx", &seqnum) != 1)
1043 k = sd_id128_from_string(item+2, &boot_id);
1047 monotonic_set = true;
1048 if (sscanf(item+2, "%llx", &monotonic) != 1)
1053 realtime_set = true;
1054 if (sscanf(item+2, "%llx", &realtime) != 1)
1059 xor_hash_set = true;
1060 if (sscanf(item+2, "%llx", &xor_hash) != 1)
1071 if ((!seqnum_set || !seqnum_id_set) &&
1072 (!monotonic_set || !boot_id_set) &&
1078 j->current_location.type = LOCATION_SEEK;
1081 j->current_location.realtime = (uint64_t) realtime;
1082 j->current_location.realtime_set = true;
1085 if (seqnum_set && seqnum_id_set) {
1086 j->current_location.seqnum = (uint64_t) seqnum;
1087 j->current_location.seqnum_id = seqnum_id;
1088 j->current_location.seqnum_set = true;
1091 if (monotonic_set && boot_id_set) {
1092 j->current_location.monotonic = (uint64_t) monotonic;
1093 j->current_location.boot_id = boot_id;
1094 j->current_location.monotonic_set = true;
1098 j->current_location.xor_hash = (uint64_t) xor_hash;
1099 j->current_location.xor_hash_set = true;
1105 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1107 const char *word, *state;
1111 assert_return(j, -EINVAL);
1112 assert_return(!journal_pid_changed(j), -ECHILD);
1113 assert_return(!isempty(cursor), -EINVAL);
1115 if (!j->current_file || j->current_file->current_offset <= 0)
1116 return -EADDRNOTAVAIL;
1118 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1122 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1123 _cleanup_free_ char *item = NULL;
1125 unsigned long long ll;
1128 if (l < 2 || word[1] != '=')
1131 item = strndup(word, l);
1138 k = sd_id128_from_string(item+2, &id);
1141 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1146 if (sscanf(item+2, "%llx", &ll) != 1)
1148 if (ll != le64toh(o->entry.seqnum))
1153 k = sd_id128_from_string(item+2, &id);
1156 if (!sd_id128_equal(id, o->entry.boot_id))
1161 if (sscanf(item+2, "%llx", &ll) != 1)
1163 if (ll != le64toh(o->entry.monotonic))
1168 if (sscanf(item+2, "%llx", &ll) != 1)
1170 if (ll != le64toh(o->entry.realtime))
1175 if (sscanf(item+2, "%llx", &ll) != 1)
1177 if (ll != le64toh(o->entry.xor_hash))
1187 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1188 assert_return(j, -EINVAL);
1189 assert_return(!journal_pid_changed(j), -ECHILD);
1192 j->current_location.type = LOCATION_SEEK;
1193 j->current_location.boot_id = boot_id;
1194 j->current_location.monotonic = usec;
1195 j->current_location.monotonic_set = true;
1200 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1201 assert_return(j, -EINVAL);
1202 assert_return(!journal_pid_changed(j), -ECHILD);
1205 j->current_location.type = LOCATION_SEEK;
1206 j->current_location.realtime = usec;
1207 j->current_location.realtime_set = true;
1212 _public_ int sd_journal_seek_head(sd_journal *j) {
1213 assert_return(j, -EINVAL);
1214 assert_return(!journal_pid_changed(j), -ECHILD);
1217 j->current_location.type = LOCATION_HEAD;
1222 _public_ int sd_journal_seek_tail(sd_journal *j) {
1223 assert_return(j, -EINVAL);
1224 assert_return(!journal_pid_changed(j), -ECHILD);
1227 j->current_location.type = LOCATION_TAIL;
1232 static void check_network(sd_journal *j, int fd) {
1240 if (fstatfs(fd, &sfs) < 0)
1244 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1245 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1246 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1247 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1248 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1251 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1252 const char *full, *tilded, *atted;
1254 full = strappenda(prefix, ".journal");
1255 tilded = strappenda(full, "~");
1256 atted = strappenda(prefix, "@");
1258 return streq(filename, full) ||
1259 streq(filename, tilded) ||
1260 startswith(filename, atted);
1263 static bool file_type_wanted(int flags, const char *filename) {
1264 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1267 /* no flags set → every type is OK */
1268 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1271 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1274 if (flags & SD_JOURNAL_CURRENT_USER) {
1275 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1277 assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid())
1278 < (int) sizeof(prefix));
1280 if (file_has_type_prefix(prefix, filename))
1287 static int add_any_file(sd_journal *j, const char *path) {
1288 JournalFile *f = NULL;
1294 if (ordered_hashmap_get(j->files, path))
1297 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1298 log_warning("Too many open journal files, not adding %s.", path);
1299 return set_put_error(j, -ETOOMANYREFS);
1302 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1306 /* journal_file_dump(f); */
1308 r = ordered_hashmap_put(j->files, f->path, f);
1310 journal_file_close(f);
1314 log_debug("File %s added.", f->path);
1316 check_network(j, f->fd);
1318 j->current_invalidate_counter ++;
1323 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1324 _cleanup_free_ char *path = NULL;
1331 if (j->no_new_files ||
1332 !file_type_wanted(j->flags, filename))
1335 path = strjoin(prefix, "/", filename, NULL);
1339 r = add_any_file(j, path);
1345 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1346 _cleanup_free_ char *path;
1353 path = strjoin(prefix, "/", filename, NULL);
1357 f = ordered_hashmap_get(j->files, path);
1361 remove_file_real(j, f);
1365 static void remove_file_real(sd_journal *j, JournalFile *f) {
1369 ordered_hashmap_remove(j->files, f->path);
1371 log_debug("File %s removed.", f->path);
1373 if (j->current_file == f) {
1374 j->current_file = NULL;
1375 j->current_field = 0;
1378 if (j->unique_file == f) {
1379 /* Jump to the next unique_file or NULL if that one was last */
1380 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
1381 j->unique_offset = 0;
1382 if (!j->unique_file)
1383 j->unique_file_lost = true;
1386 journal_file_close(f);
1388 j->current_invalidate_counter ++;
1391 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1392 _cleanup_free_ char *path = NULL;
1394 _cleanup_closedir_ DIR *d = NULL;
1402 log_debug("Considering %s/%s.", prefix, dirname);
1404 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1405 (sd_id128_from_string(dirname, &id) < 0 ||
1406 sd_id128_get_machine(&mid) < 0 ||
1407 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1410 path = strjoin(prefix, "/", dirname, NULL);
1416 log_debug_errno(errno, "Failed to open %s: %m", path);
1417 if (errno == ENOENT)
1422 m = hashmap_get(j->directories_by_path, path);
1424 m = new0(Directory, 1);
1431 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1436 path = NULL; /* avoid freeing in cleanup */
1437 j->current_invalidate_counter ++;
1439 log_debug("Directory %s added.", m->path);
1441 } else if (m->is_root)
1444 if (m->wd <= 0 && j->inotify_fd >= 0) {
1446 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1447 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1448 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1451 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1452 inotify_rm_watch(j->inotify_fd, m->wd);
1460 if (!de && errno != 0) {
1462 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1468 if (dirent_is_file_with_suffix(de, ".journal") ||
1469 dirent_is_file_with_suffix(de, ".journal~")) {
1470 r = add_file(j, m->path, de->d_name);
1472 log_debug_errno(r, "Failed to add file %s/%s: %m",
1473 m->path, de->d_name);
1474 r = set_put_error(j, r);
1481 check_network(j, dirfd(d));
1486 static int add_root_directory(sd_journal *j, const char *p) {
1487 _cleanup_closedir_ DIR *d = NULL;
1494 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1495 !path_startswith(p, "/run"))
1499 p = strappenda(j->prefix, p);
1505 m = hashmap_get(j->directories_by_path, p);
1507 m = new0(Directory, 1);
1512 m->path = strdup(p);
1518 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1524 j->current_invalidate_counter ++;
1526 log_debug("Root directory %s added.", m->path);
1528 } else if (!m->is_root)
1531 if (m->wd <= 0 && j->inotify_fd >= 0) {
1533 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1534 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1537 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1538 inotify_rm_watch(j->inotify_fd, m->wd);
1541 if (j->no_new_files)
1550 if (!de && errno != 0) {
1552 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1558 if (dirent_is_file_with_suffix(de, ".journal") ||
1559 dirent_is_file_with_suffix(de, ".journal~")) {
1560 r = add_file(j, m->path, de->d_name);
1562 log_debug_errno(r, "Failed to add file %s/%s: %m",
1563 m->path, de->d_name);
1564 r = set_put_error(j, r);
1568 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1569 sd_id128_from_string(de->d_name, &id) >= 0) {
1571 r = add_directory(j, m->path, de->d_name);
1573 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
1577 check_network(j, dirfd(d));
1582 static int remove_directory(sd_journal *j, Directory *d) {
1586 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1588 if (j->inotify_fd >= 0)
1589 inotify_rm_watch(j->inotify_fd, d->wd);
1592 hashmap_remove(j->directories_by_path, d->path);
1595 log_debug("Root directory %s removed.", d->path);
1597 log_debug("Directory %s removed.", d->path);
1605 static int add_search_paths(sd_journal *j) {
1607 const char search_paths[] =
1608 "/run/log/journal\0"
1609 "/var/log/journal\0";
1614 /* We ignore most errors here, since the idea is to only open
1615 * what's actually accessible, and ignore the rest. */
1617 NULSTR_FOREACH(p, search_paths) {
1618 r = add_root_directory(j, p);
1619 if (r < 0 && r != -ENOENT) {
1620 r = set_put_error(j, r);
1629 static int add_current_paths(sd_journal *j) {
1634 assert(j->no_new_files);
1636 /* Simply adds all directories for files we have open as
1637 * "root" directories. We don't expect errors here, so we
1638 * treat them as fatal. */
1640 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
1641 _cleanup_free_ char *dir;
1644 dir = dirname_malloc(f->path);
1648 r = add_root_directory(j, dir);
1650 set_put_error(j, r);
1659 static int allocate_inotify(sd_journal *j) {
1662 if (j->inotify_fd < 0) {
1663 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1664 if (j->inotify_fd < 0)
1668 if (!j->directories_by_wd) {
1669 j->directories_by_wd = hashmap_new(NULL);
1670 if (!j->directories_by_wd)
1677 static sd_journal *journal_new(int flags, const char *path) {
1680 j = new0(sd_journal, 1);
1684 j->original_pid = getpid();
1687 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1690 j->path = strdup(path);
1695 j->files = ordered_hashmap_new(&string_hash_ops);
1696 j->directories_by_path = hashmap_new(&string_hash_ops);
1697 j->mmap = mmap_cache_new();
1698 if (!j->files || !j->directories_by_path || !j->mmap)
1704 sd_journal_close(j);
1708 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1712 assert_return(ret, -EINVAL);
1713 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1715 j = journal_new(flags, NULL);
1719 r = add_search_paths(j);
1727 sd_journal_close(j);
1732 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1733 _cleanup_free_ char *root = NULL, *class = NULL;
1738 assert_return(machine, -EINVAL);
1739 assert_return(ret, -EINVAL);
1740 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1741 assert_return(machine_name_is_valid(machine), -EINVAL);
1743 p = strappenda("/run/systemd/machines/", machine);
1744 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1752 if (!streq_ptr(class, "container"))
1755 j = journal_new(flags, NULL);
1762 r = add_search_paths(j);
1770 sd_journal_close(j);
1774 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1778 assert_return(ret, -EINVAL);
1779 assert_return(path, -EINVAL);
1780 assert_return(flags == 0, -EINVAL);
1782 j = journal_new(flags, path);
1786 r = add_root_directory(j, path);
1788 set_put_error(j, r);
1796 sd_journal_close(j);
1801 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1806 assert_return(ret, -EINVAL);
1807 assert_return(flags == 0, -EINVAL);
1809 j = journal_new(flags, NULL);
1813 STRV_FOREACH(path, paths) {
1814 r = add_any_file(j, *path);
1816 log_error_errno(r, "Failed to open %s: %m", *path);
1821 j->no_new_files = true;
1827 sd_journal_close(j);
1832 _public_ void sd_journal_close(sd_journal *j) {
1839 sd_journal_flush_matches(j);
1841 while ((f = ordered_hashmap_steal_first(j->files)))
1842 journal_file_close(f);
1844 ordered_hashmap_free(j->files);
1846 while ((d = hashmap_first(j->directories_by_path)))
1847 remove_directory(j, d);
1849 while ((d = hashmap_first(j->directories_by_wd)))
1850 remove_directory(j, d);
1852 hashmap_free(j->directories_by_path);
1853 hashmap_free(j->directories_by_wd);
1855 safe_close(j->inotify_fd);
1858 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1859 mmap_cache_unref(j->mmap);
1864 free(j->unique_field);
1865 set_free(j->errors);
1869 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1874 assert_return(j, -EINVAL);
1875 assert_return(!journal_pid_changed(j), -ECHILD);
1876 assert_return(ret, -EINVAL);
1878 f = j->current_file;
1880 return -EADDRNOTAVAIL;
1882 if (f->current_offset <= 0)
1883 return -EADDRNOTAVAIL;
1885 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1889 *ret = le64toh(o->entry.realtime);
1893 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1899 assert_return(j, -EINVAL);
1900 assert_return(!journal_pid_changed(j), -ECHILD);
1902 f = j->current_file;
1904 return -EADDRNOTAVAIL;
1906 if (f->current_offset <= 0)
1907 return -EADDRNOTAVAIL;
1909 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1914 *ret_boot_id = o->entry.boot_id;
1916 r = sd_id128_get_boot(&id);
1920 if (!sd_id128_equal(id, o->entry.boot_id))
1925 *ret = le64toh(o->entry.monotonic);
1930 static bool field_is_valid(const char *field) {
1938 if (startswith(field, "__"))
1941 for (p = field; *p; p++) {
1946 if (*p >= 'A' && *p <= 'Z')
1949 if (*p >= '0' && *p <= '9')
1958 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1961 size_t field_length;
1965 assert_return(j, -EINVAL);
1966 assert_return(!journal_pid_changed(j), -ECHILD);
1967 assert_return(field, -EINVAL);
1968 assert_return(data, -EINVAL);
1969 assert_return(size, -EINVAL);
1970 assert_return(field_is_valid(field), -EINVAL);
1972 f = j->current_file;
1974 return -EADDRNOTAVAIL;
1976 if (f->current_offset <= 0)
1977 return -EADDRNOTAVAIL;
1979 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1983 field_length = strlen(field);
1985 n = journal_file_entry_n_items(o);
1986 for (i = 0; i < n; i++) {
1992 p = le64toh(o->entry.items[i].object_offset);
1993 le_hash = o->entry.items[i].hash;
1994 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1998 if (le_hash != o->data.hash)
2001 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2003 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2005 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
2006 if (decompress_startswith(compression,
2008 &f->compress_buffer, &f->compress_buffer_size,
2009 field, field_length, '=')) {
2013 r = decompress_blob(compression,
2015 &f->compress_buffer, &f->compress_buffer_size, &rsize,
2020 *data = f->compress_buffer;
2021 *size = (size_t) rsize;
2026 return -EPROTONOSUPPORT;
2028 } else if (l >= field_length+1 &&
2029 memcmp(o->data.payload, field, field_length) == 0 &&
2030 o->data.payload[field_length] == '=') {
2034 if ((uint64_t) t != l)
2037 *data = o->data.payload;
2043 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2051 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
2056 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2059 /* We can't read objects larger than 4G on a 32bit machine */
2060 if ((uint64_t) t != l)
2063 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2065 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
2069 r = decompress_blob(compression,
2070 o->data.payload, l, &f->compress_buffer,
2071 &f->compress_buffer_size, &rsize, j->data_threshold);
2075 *data = f->compress_buffer;
2076 *size = (size_t) rsize;
2078 return -EPROTONOSUPPORT;
2081 *data = o->data.payload;
2088 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2095 assert_return(j, -EINVAL);
2096 assert_return(!journal_pid_changed(j), -ECHILD);
2097 assert_return(data, -EINVAL);
2098 assert_return(size, -EINVAL);
2100 f = j->current_file;
2102 return -EADDRNOTAVAIL;
2104 if (f->current_offset <= 0)
2105 return -EADDRNOTAVAIL;
2107 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2111 n = journal_file_entry_n_items(o);
2112 if (j->current_field >= n)
2115 p = le64toh(o->entry.items[j->current_field].object_offset);
2116 le_hash = o->entry.items[j->current_field].hash;
2117 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2121 if (le_hash != o->data.hash)
2124 r = return_data(j, f, o, data, size);
2128 j->current_field ++;
2133 _public_ void sd_journal_restart_data(sd_journal *j) {
2137 j->current_field = 0;
2140 _public_ int sd_journal_get_fd(sd_journal *j) {
2143 assert_return(j, -EINVAL);
2144 assert_return(!journal_pid_changed(j), -ECHILD);
2146 if (j->inotify_fd >= 0)
2147 return j->inotify_fd;
2149 r = allocate_inotify(j);
2153 /* Iterate through all dirs again, to add them to the
2155 if (j->no_new_files)
2156 r = add_current_paths(j);
2158 r = add_root_directory(j, j->path);
2160 r = add_search_paths(j);
2164 return j->inotify_fd;
2167 _public_ int sd_journal_get_events(sd_journal *j) {
2170 assert_return(j, -EINVAL);
2171 assert_return(!journal_pid_changed(j), -ECHILD);
2173 fd = sd_journal_get_fd(j);
2180 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2183 assert_return(j, -EINVAL);
2184 assert_return(!journal_pid_changed(j), -ECHILD);
2185 assert_return(timeout_usec, -EINVAL);
2187 fd = sd_journal_get_fd(j);
2191 if (!j->on_network) {
2192 *timeout_usec = (uint64_t) -1;
2196 /* If we are on the network we need to regularly check for
2197 * changes manually */
2199 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2203 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2210 /* Is this a subdirectory we watch? */
2211 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2215 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2216 (endswith(e->name, ".journal") ||
2217 endswith(e->name, ".journal~"))) {
2219 /* Event for a journal file */
2221 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2222 r = add_file(j, d->path, e->name);
2224 log_debug_errno(r, "Failed to add file %s/%s: %m",
2226 set_put_error(j, r);
2229 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2231 r = remove_file(j, d->path, e->name);
2233 log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
2236 } else if (!d->is_root && e->len == 0) {
2238 /* Event for a subdirectory */
2240 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2241 r = remove_directory(j, d);
2243 log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
2247 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2249 /* Event for root directory */
2251 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2252 r = add_directory(j, d->path, e->name);
2254 log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
2261 if (e->mask & IN_IGNORED)
2264 log_warning("Unknown inotify event.");
2267 static int determine_change(sd_journal *j) {
2272 b = j->current_invalidate_counter != j->last_invalidate_counter;
2273 j->last_invalidate_counter = j->current_invalidate_counter;
2275 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2278 _public_ int sd_journal_process(sd_journal *j) {
2279 bool got_something = false;
2281 assert_return(j, -EINVAL);
2282 assert_return(!journal_pid_changed(j), -ECHILD);
2284 j->last_process_usec = now(CLOCK_MONOTONIC);
2287 uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event);
2288 struct inotify_event *e;
2291 l = read(j->inotify_fd, buffer, sizeof(buffer));
2293 if (errno == EAGAIN || errno == EINTR)
2294 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2299 got_something = true;
2301 FOREACH_INOTIFY_EVENT(e, buffer, l)
2302 process_inotify_event(j, e);
2306 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2310 assert_return(j, -EINVAL);
2311 assert_return(!journal_pid_changed(j), -ECHILD);
2313 if (j->inotify_fd < 0) {
2315 /* This is the first invocation, hence create the
2317 r = sd_journal_get_fd(j);
2321 /* The journal might have changed since the context
2322 * object was created and we weren't watching before,
2323 * hence don't wait for anything, and return
2325 return determine_change(j);
2328 r = sd_journal_get_timeout(j, &t);
2332 if (t != (uint64_t) -1) {
2335 n = now(CLOCK_MONOTONIC);
2336 t = t > n ? t - n : 0;
2338 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2343 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2344 } while (r == -EINTR);
2349 return sd_journal_process(j);
2352 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2356 uint64_t fmin = 0, tmax = 0;
2359 assert_return(j, -EINVAL);
2360 assert_return(!journal_pid_changed(j), -ECHILD);
2361 assert_return(from || to, -EINVAL);
2362 assert_return(from != to, -EINVAL);
2364 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2367 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2380 fmin = MIN(fr, fmin);
2381 tmax = MAX(t, tmax);
2390 return first ? 0 : 1;
2393 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2399 assert_return(j, -EINVAL);
2400 assert_return(!journal_pid_changed(j), -ECHILD);
2401 assert_return(from || to, -EINVAL);
2402 assert_return(from != to, -EINVAL);
2404 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2407 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2417 *from = MIN(fr, *from);
2432 void journal_print_header(sd_journal *j) {
2435 bool newline = false;
2439 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2445 journal_file_print_header(f);
2449 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2454 assert_return(j, -EINVAL);
2455 assert_return(!journal_pid_changed(j), -ECHILD);
2456 assert_return(bytes, -EINVAL);
2458 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2461 if (fstat(f->fd, &st) < 0)
2464 sum += (uint64_t) st.st_blocks * 512ULL;
2471 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2474 assert_return(j, -EINVAL);
2475 assert_return(!journal_pid_changed(j), -ECHILD);
2476 assert_return(!isempty(field), -EINVAL);
2477 assert_return(field_is_valid(field), -EINVAL);
2483 free(j->unique_field);
2484 j->unique_field = f;
2485 j->unique_file = NULL;
2486 j->unique_offset = 0;
2487 j->unique_file_lost = false;
2492 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2495 assert_return(j, -EINVAL);
2496 assert_return(!journal_pid_changed(j), -ECHILD);
2497 assert_return(data, -EINVAL);
2498 assert_return(l, -EINVAL);
2499 assert_return(j->unique_field, -EINVAL);
2501 k = strlen(j->unique_field);
2503 if (!j->unique_file) {
2504 if (j->unique_file_lost)
2507 j->unique_file = ordered_hashmap_first(j->files);
2508 if (!j->unique_file)
2511 j->unique_offset = 0;
2523 /* Proceed to next data object in the field's linked list */
2524 if (j->unique_offset == 0) {
2525 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2529 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2531 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2535 j->unique_offset = le64toh(o->data.next_field_offset);
2538 /* We reached the end of the list? Then start again, with the next file */
2539 if (j->unique_offset == 0) {
2540 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
2541 if (!j->unique_file)
2547 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2548 * instead, so that we can look at this data object at the same
2549 * time as one on another file */
2550 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
2554 /* Let's do the type check by hand, since we used 0 context above. */
2555 if (o->object.type != OBJECT_DATA) {
2556 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
2557 j->unique_file->path, j->unique_offset,
2558 o->object.type, OBJECT_DATA);
2562 r = return_data(j, j->unique_file, o, &odata, &ol);
2566 /* Check if we have at least the field name and "=". */
2568 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2569 j->unique_file->path, j->unique_offset,
2574 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2575 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2576 j->unique_file->path, j->unique_offset,
2581 /* OK, now let's see if we already returned this data
2582 * object by checking if it exists in the earlier
2583 * traversed files. */
2585 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2589 if (of == j->unique_file)
2592 /* Skip this file it didn't have any fields
2594 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2595 le64toh(of->header->n_fields) <= 0)
2598 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2609 r = return_data(j, j->unique_file, o, data, l);
2617 _public_ void sd_journal_restart_unique(sd_journal *j) {
2621 j->unique_file = NULL;
2622 j->unique_offset = 0;
2623 j->unique_file_lost = false;
2626 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2627 assert_return(j, -EINVAL);
2628 assert_return(!journal_pid_changed(j), -ECHILD);
2630 return !j->on_network;
2633 static char *lookup_field(const char *field, void *userdata) {
2634 sd_journal *j = userdata;
2642 r = sd_journal_get_data(j, field, &data, &size);
2644 size > REPLACE_VAR_MAX)
2645 return strdup(field);
2647 d = strlen(field) + 1;
2649 return strndup((const char*) data + d, size - d);
2652 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2656 _cleanup_free_ char *text = NULL, *cid = NULL;
2660 assert_return(j, -EINVAL);
2661 assert_return(!journal_pid_changed(j), -ECHILD);
2662 assert_return(ret, -EINVAL);
2664 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2668 cid = strndup((const char*) data + 11, size - 11);
2672 r = sd_id128_from_string(cid, &id);
2676 r = catalog_get(CATALOG_DATABASE, id, &text);
2680 t = replace_var(text, lookup_field, j);
2688 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2689 assert_return(ret, -EINVAL);
2691 return catalog_get(CATALOG_DATABASE, id, ret);
2694 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2695 assert_return(j, -EINVAL);
2696 assert_return(!journal_pid_changed(j), -ECHILD);
2698 j->data_threshold = sz;
2702 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2703 assert_return(j, -EINVAL);
2704 assert_return(!journal_pid_changed(j), -ECHILD);
2705 assert_return(sz, -EINVAL);
2707 *sz = j->data_threshold;