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, trivial_hash_func, trivial_compare_func);
77 return set_put(j->errors, INT_TO_PTR(r));
80 static void detach_location(sd_journal *j) {
86 j->current_file = NULL;
89 HASHMAP_FOREACH(f, j->files, i)
90 f->current_offset = 0;
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;
133 static int match_is_valid(const void *data, size_t size) {
141 if (startswith(data, "__"))
145 for (p = b; p < b + size; p++) {
153 if (*p >= 'A' && *p <= 'Z')
156 if (*p >= '0' && *p <= '9')
165 static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
166 const uint8_t *a = _a, *b = _b;
169 for (j = 0; j < s && j < t; j++) {
178 assert_not_reached("\"=\" not found");
181 static Match *match_new(Match *p, MatchType t) {
192 LIST_PREPEND(matches, p->matches, m);
198 static void match_free(Match *m) {
202 match_free(m->matches);
205 LIST_REMOVE(matches, m->parent->matches, m);
211 static void match_free_if_empty(Match *m) {
212 if (!m || m->matches)
218 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
219 Match *l3, *l4, *add_here = NULL, *m;
222 assert_return(j, -EINVAL);
223 assert_return(!journal_pid_changed(j), -ECHILD);
224 assert_return(data, -EINVAL);
229 assert_return(match_is_valid(data, size), -EINVAL);
235 * level 4: concrete matches */
238 j->level0 = match_new(NULL, MATCH_AND_TERM);
244 j->level1 = match_new(j->level0, MATCH_OR_TERM);
250 j->level2 = match_new(j->level1, MATCH_AND_TERM);
255 assert(j->level0->type == MATCH_AND_TERM);
256 assert(j->level1->type == MATCH_OR_TERM);
257 assert(j->level2->type == MATCH_AND_TERM);
259 le_hash = htole64(hash64(data, size));
261 LIST_FOREACH(matches, l3, j->level2->matches) {
262 assert(l3->type == MATCH_OR_TERM);
264 LIST_FOREACH(matches, l4, l3->matches) {
265 assert(l4->type == MATCH_DISCRETE);
267 /* Exactly the same match already? Then ignore
269 if (l4->le_hash == le_hash &&
271 memcmp(l4->data, data, size) == 0)
274 /* Same field? Then let's add this to this OR term */
275 if (same_field(data, size, l4->data, l4->size)) {
286 add_here = match_new(j->level2, MATCH_OR_TERM);
291 m = match_new(add_here, MATCH_DISCRETE);
295 m->le_hash = le_hash;
297 m->data = memdup(data, size);
306 match_free_if_empty(add_here);
307 match_free_if_empty(j->level2);
308 match_free_if_empty(j->level1);
309 match_free_if_empty(j->level0);
314 _public_ int sd_journal_add_conjunction(sd_journal *j) {
315 assert_return(j, -EINVAL);
316 assert_return(!journal_pid_changed(j), -ECHILD);
324 if (!j->level1->matches)
333 _public_ int sd_journal_add_disjunction(sd_journal *j) {
334 assert_return(j, -EINVAL);
335 assert_return(!journal_pid_changed(j), -ECHILD);
346 if (!j->level2->matches)
353 static char *match_make_string(Match *m) {
356 bool enclose = false;
359 return strdup("none");
361 if (m->type == MATCH_DISCRETE)
362 return strndup(m->data, m->size);
365 LIST_FOREACH(matches, i, m->matches) {
368 t = match_make_string(i);
375 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
390 r = strjoin("(", p, ")", NULL);
398 char *journal_make_match_string(sd_journal *j) {
401 return match_make_string(j->level0);
404 _public_ void sd_journal_flush_matches(sd_journal *j) {
409 match_free(j->level0);
411 j->level0 = j->level1 = j->level2 = NULL;
416 static int compare_entry_order(JournalFile *af, Object *_ao,
417 JournalFile *bf, uint64_t bp) {
427 /* The mmap cache might invalidate the object from the first
428 * file if we look at the one from the second file. Hence
429 * temporarily copy the header of the first one, and look at
431 ao = alloca(offsetof(EntryObject, items));
432 memcpy(ao, _ao, offsetof(EntryObject, items));
434 r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
436 return strcmp(af->path, bf->path);
438 /* We operate on two different files here, hence we can access
439 * two objects at the same time, which we normally can't.
441 * If contents and timestamps match, these entries are
442 * identical, even if the seqnum does not match */
444 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
445 ao->entry.monotonic == bo->entry.monotonic &&
446 ao->entry.realtime == bo->entry.realtime &&
447 ao->entry.xor_hash == bo->entry.xor_hash)
450 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
452 /* If this is from the same seqnum source, compare
454 a = le64toh(ao->entry.seqnum);
455 b = le64toh(bo->entry.seqnum);
462 /* Wow! This is weird, different data but the same
463 * seqnums? Something is borked, but let's make the
464 * best of it and compare by time. */
467 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
469 /* If the boot id matches, compare monotonic time */
470 a = le64toh(ao->entry.monotonic);
471 b = le64toh(bo->entry.monotonic);
479 /* Otherwise, compare UTC time */
480 a = le64toh(ao->entry.realtime);
481 b = le64toh(bo->entry.realtime);
488 /* Finally, compare by contents */
489 a = le64toh(ao->entry.xor_hash);
490 b = le64toh(bo->entry.xor_hash);
500 _pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
506 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
508 if (l->monotonic_set &&
509 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
511 le64toh(ao->entry.realtime) == l->realtime &&
513 le64toh(ao->entry.xor_hash) == l->xor_hash)
517 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
519 a = le64toh(ao->entry.seqnum);
527 if (l->monotonic_set &&
528 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
530 a = le64toh(ao->entry.monotonic);
532 if (a < l->monotonic)
534 if (a > l->monotonic)
538 if (l->realtime_set) {
540 a = le64toh(ao->entry.realtime);
548 if (l->xor_hash_set) {
549 a = le64toh(ao->entry.xor_hash);
560 static int next_for_match(
564 uint64_t after_offset,
565 direction_t direction,
577 if (m->type == MATCH_DISCRETE) {
580 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
584 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
586 } else if (m->type == MATCH_OR_TERM) {
589 /* Find the earliest match beyond after_offset */
591 LIST_FOREACH(matches, i, m->matches) {
594 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
598 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
606 } else if (m->type == MATCH_AND_TERM) {
607 Match *i, *last_moved;
609 /* Always jump to the next matching entry and repeat
610 * this until we find an offset that matches for all
616 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
620 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
621 last_moved = m->matches;
623 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
626 r = next_for_match(j, i, f, np, direction, NULL, &cp);
630 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
631 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
640 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
652 static int find_location_for_match(
656 direction_t direction,
666 if (m->type == MATCH_DISCRETE) {
669 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
673 /* FIXME: missing: find by monotonic */
675 if (j->current_location.type == LOCATION_HEAD)
676 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
677 if (j->current_location.type == LOCATION_TAIL)
678 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
679 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
680 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
681 if (j->current_location.monotonic_set) {
682 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
686 if (j->current_location.realtime_set)
687 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
689 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
691 } else if (m->type == MATCH_OR_TERM) {
696 /* Find the earliest match */
698 LIST_FOREACH(matches, i, m->matches) {
701 r = find_location_for_match(j, i, f, direction, NULL, &cp);
705 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
713 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
728 assert(m->type == MATCH_AND_TERM);
730 /* First jump to the last match, and then find the
731 * next one where all matches match */
736 LIST_FOREACH(matches, i, m->matches) {
739 r = find_location_for_match(j, i, f, direction, NULL, &cp);
743 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
747 return next_for_match(j, m, f, np, direction, ret, offset);
751 static int find_location_with_matches(
754 direction_t direction,
766 /* No matches is simple */
768 if (j->current_location.type == LOCATION_HEAD)
769 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
770 if (j->current_location.type == LOCATION_TAIL)
771 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
772 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
773 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
774 if (j->current_location.monotonic_set) {
775 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
779 if (j->current_location.realtime_set)
780 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
782 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
784 return find_location_for_match(j, j->level0, f, direction, ret, offset);
787 static int next_with_matches(
790 direction_t direction,
805 /* No matches is easy. We simple advance the file
808 return journal_file_next_entry(f, c, cp, direction, ret, offset);
810 /* If we have a match then we look for the next matching entry
811 * with an offset at least one step larger */
812 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
815 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
823 if (f->last_direction == direction && f->current_offset > 0) {
824 cp = f->current_offset;
826 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
830 r = next_with_matches(j, f, direction, &c, &cp);
834 r = find_location_with_matches(j, f, direction, &c, &cp);
839 /* OK, we found the spot, now let's advance until an entry
840 * that is actually different from what we were previously
841 * looking at. This is necessary to handle entries which exist
842 * in two (or more) journal files, and which shall all be
843 * suppressed but one. */
848 if (j->current_location.type == LOCATION_DISCRETE) {
851 k = compare_with_location(f, c, &j->current_location);
852 if (direction == DIRECTION_DOWN)
867 r = next_with_matches(j, f, direction, &c, &cp);
873 static int real_journal_next(sd_journal *j, direction_t direction) {
874 JournalFile *f, *new_file = NULL;
875 uint64_t new_offset = 0;
881 assert_return(j, -EINVAL);
882 assert_return(!journal_pid_changed(j), -ECHILD);
884 HASHMAP_FOREACH(f, j->files, i) {
887 r = next_beyond_location(j, f, direction, &o, &p);
889 log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
890 remove_file_real(j, f);
900 k = compare_entry_order(f, o, new_file, new_offset);
902 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
914 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
918 set_location(j, LOCATION_DISCRETE, new_file, o, direction, new_offset);
923 _public_ int sd_journal_next(sd_journal *j) {
924 return real_journal_next(j, DIRECTION_DOWN);
927 _public_ int sd_journal_previous(sd_journal *j) {
928 return real_journal_next(j, DIRECTION_UP);
931 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
934 assert_return(j, -EINVAL);
935 assert_return(!journal_pid_changed(j), -ECHILD);
938 /* If this is not a discrete skip, then at least
939 * resolve the current location */
940 if (j->current_location.type != LOCATION_DISCRETE)
941 return real_journal_next(j, direction);
947 r = real_journal_next(j, direction);
961 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
962 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
965 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
966 return real_journal_next_skip(j, DIRECTION_UP, skip);
969 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
972 char bid[33], sid[33];
974 assert_return(j, -EINVAL);
975 assert_return(!journal_pid_changed(j), -ECHILD);
976 assert_return(cursor, -EINVAL);
978 if (!j->current_file || j->current_file->current_offset <= 0)
979 return -EADDRNOTAVAIL;
981 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
985 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
986 sd_id128_to_string(o->entry.boot_id, bid);
989 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
990 sid, le64toh(o->entry.seqnum),
991 bid, le64toh(o->entry.monotonic),
992 le64toh(o->entry.realtime),
993 le64toh(o->entry.xor_hash)) < 0)
999 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
1000 const char *word, *state;
1002 unsigned long long seqnum, monotonic, realtime, xor_hash;
1004 seqnum_id_set = false,
1006 boot_id_set = false,
1007 monotonic_set = false,
1008 realtime_set = false,
1009 xor_hash_set = false;
1010 sd_id128_t seqnum_id, boot_id;
1012 assert_return(j, -EINVAL);
1013 assert_return(!journal_pid_changed(j), -ECHILD);
1014 assert_return(!isempty(cursor), -EINVAL);
1016 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1020 if (l < 2 || word[1] != '=')
1023 item = strndup(word, l);
1030 seqnum_id_set = true;
1031 k = sd_id128_from_string(item+2, &seqnum_id);
1036 if (sscanf(item+2, "%llx", &seqnum) != 1)
1042 k = sd_id128_from_string(item+2, &boot_id);
1046 monotonic_set = true;
1047 if (sscanf(item+2, "%llx", &monotonic) != 1)
1052 realtime_set = true;
1053 if (sscanf(item+2, "%llx", &realtime) != 1)
1058 xor_hash_set = true;
1059 if (sscanf(item+2, "%llx", &xor_hash) != 1)
1070 if ((!seqnum_set || !seqnum_id_set) &&
1071 (!monotonic_set || !boot_id_set) &&
1077 j->current_location.type = LOCATION_SEEK;
1080 j->current_location.realtime = (uint64_t) realtime;
1081 j->current_location.realtime_set = true;
1084 if (seqnum_set && seqnum_id_set) {
1085 j->current_location.seqnum = (uint64_t) seqnum;
1086 j->current_location.seqnum_id = seqnum_id;
1087 j->current_location.seqnum_set = true;
1090 if (monotonic_set && boot_id_set) {
1091 j->current_location.monotonic = (uint64_t) monotonic;
1092 j->current_location.boot_id = boot_id;
1093 j->current_location.monotonic_set = true;
1097 j->current_location.xor_hash = (uint64_t) xor_hash;
1098 j->current_location.xor_hash_set = true;
1104 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1106 const char *word, *state;
1110 assert_return(j, -EINVAL);
1111 assert_return(!journal_pid_changed(j), -ECHILD);
1112 assert_return(!isempty(cursor), -EINVAL);
1114 if (!j->current_file || j->current_file->current_offset <= 0)
1115 return -EADDRNOTAVAIL;
1117 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1121 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1122 _cleanup_free_ char *item = NULL;
1124 unsigned long long ll;
1127 if (l < 2 || word[1] != '=')
1130 item = strndup(word, l);
1137 k = sd_id128_from_string(item+2, &id);
1140 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1145 if (sscanf(item+2, "%llx", &ll) != 1)
1147 if (ll != le64toh(o->entry.seqnum))
1152 k = sd_id128_from_string(item+2, &id);
1155 if (!sd_id128_equal(id, o->entry.boot_id))
1160 if (sscanf(item+2, "%llx", &ll) != 1)
1162 if (ll != le64toh(o->entry.monotonic))
1167 if (sscanf(item+2, "%llx", &ll) != 1)
1169 if (ll != le64toh(o->entry.realtime))
1174 if (sscanf(item+2, "%llx", &ll) != 1)
1176 if (ll != le64toh(o->entry.xor_hash))
1186 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1187 assert_return(j, -EINVAL);
1188 assert_return(!journal_pid_changed(j), -ECHILD);
1191 j->current_location.type = LOCATION_SEEK;
1192 j->current_location.boot_id = boot_id;
1193 j->current_location.monotonic = usec;
1194 j->current_location.monotonic_set = true;
1199 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1200 assert_return(j, -EINVAL);
1201 assert_return(!journal_pid_changed(j), -ECHILD);
1204 j->current_location.type = LOCATION_SEEK;
1205 j->current_location.realtime = usec;
1206 j->current_location.realtime_set = true;
1211 _public_ int sd_journal_seek_head(sd_journal *j) {
1212 assert_return(j, -EINVAL);
1213 assert_return(!journal_pid_changed(j), -ECHILD);
1216 j->current_location.type = LOCATION_HEAD;
1221 _public_ int sd_journal_seek_tail(sd_journal *j) {
1222 assert_return(j, -EINVAL);
1223 assert_return(!journal_pid_changed(j), -ECHILD);
1226 j->current_location.type = LOCATION_TAIL;
1231 static void check_network(sd_journal *j, int fd) {
1239 if (fstatfs(fd, &sfs) < 0)
1243 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1244 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1245 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1246 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1247 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1250 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1251 const char *full, *tilded, *atted;
1253 full = strappenda(prefix, ".journal");
1254 tilded = strappenda(full, "~");
1255 atted = strappenda(prefix, "@");
1257 return streq(filename, full) ||
1258 streq(filename, tilded) ||
1259 startswith(filename, atted);
1262 static bool file_type_wanted(int flags, const char *filename) {
1263 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1266 /* no flags set → every type is OK */
1267 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1270 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1273 if (flags & SD_JOURNAL_CURRENT_USER) {
1274 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1276 assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid())
1277 < (int) sizeof(prefix));
1279 if (file_has_type_prefix(prefix, filename))
1286 static int add_any_file(sd_journal *j, const char *path) {
1287 JournalFile *f = NULL;
1293 if (hashmap_get(j->files, path))
1296 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1297 log_warning("Too many open journal files, not adding %s.", path);
1298 return set_put_error(j, -ETOOMANYREFS);
1301 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1305 /* journal_file_dump(f); */
1307 r = hashmap_put(j->files, f->path, f);
1309 journal_file_close(f);
1313 log_debug("File %s added.", f->path);
1315 check_network(j, f->fd);
1317 j->current_invalidate_counter ++;
1322 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1323 _cleanup_free_ char *path = NULL;
1330 if (j->no_new_files ||
1331 !file_type_wanted(j->flags, filename))
1334 path = strjoin(prefix, "/", filename, NULL);
1338 r = add_any_file(j, path);
1344 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1345 _cleanup_free_ char *path;
1352 path = strjoin(prefix, "/", filename, NULL);
1356 f = hashmap_get(j->files, path);
1360 remove_file_real(j, f);
1364 static void remove_file_real(sd_journal *j, JournalFile *f) {
1368 hashmap_remove(j->files, f->path);
1370 log_debug("File %s removed.", f->path);
1372 if (j->current_file == f) {
1373 j->current_file = NULL;
1374 j->current_field = 0;
1377 if (j->unique_file == f) {
1378 j->unique_file = NULL;
1379 j->unique_offset = 0;
1382 journal_file_close(f);
1384 j->current_invalidate_counter ++;
1387 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1388 _cleanup_free_ char *path = NULL;
1390 _cleanup_closedir_ DIR *d = NULL;
1398 log_debug("Considering %s/%s.", prefix, dirname);
1400 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1401 (sd_id128_from_string(dirname, &id) < 0 ||
1402 sd_id128_get_machine(&mid) < 0 ||
1403 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1406 path = strjoin(prefix, "/", dirname, NULL);
1412 log_debug("Failed to open %s: %m", path);
1413 if (errno == ENOENT)
1418 m = hashmap_get(j->directories_by_path, path);
1420 m = new0(Directory, 1);
1427 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1432 path = NULL; /* avoid freeing in cleanup */
1433 j->current_invalidate_counter ++;
1435 log_debug("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|
1444 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1447 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1448 inotify_rm_watch(j->inotify_fd, m->wd);
1456 if (!de && errno != 0) {
1458 log_debug("Failed to read directory %s: %m", m->path);
1464 if (dirent_is_file_with_suffix(de, ".journal") ||
1465 dirent_is_file_with_suffix(de, ".journal~")) {
1466 r = add_file(j, m->path, de->d_name);
1468 log_debug("Failed to add file %s/%s: %s",
1469 m->path, de->d_name, strerror(-r));
1470 r = set_put_error(j, r);
1477 check_network(j, dirfd(d));
1482 static int add_root_directory(sd_journal *j, const char *p) {
1483 _cleanup_closedir_ DIR *d = NULL;
1490 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1491 !path_startswith(p, "/run"))
1495 p = strappenda(j->prefix, p);
1501 m = hashmap_get(j->directories_by_path, p);
1503 m = new0(Directory, 1);
1508 m->path = strdup(p);
1514 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1520 j->current_invalidate_counter ++;
1522 log_debug("Root directory %s added.", m->path);
1524 } else if (!m->is_root)
1527 if (m->wd <= 0 && j->inotify_fd >= 0) {
1529 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1530 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1533 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1534 inotify_rm_watch(j->inotify_fd, m->wd);
1537 if (j->no_new_files)
1546 if (!de && errno != 0) {
1548 log_debug("Failed to read directory %s: %m", m->path);
1554 if (dirent_is_file_with_suffix(de, ".journal") ||
1555 dirent_is_file_with_suffix(de, ".journal~")) {
1556 r = add_file(j, m->path, de->d_name);
1558 log_debug("Failed to add file %s/%s: %s",
1559 m->path, de->d_name, strerror(-r));
1560 r = set_put_error(j, r);
1564 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1565 sd_id128_from_string(de->d_name, &id) >= 0) {
1567 r = add_directory(j, m->path, de->d_name);
1569 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1573 check_network(j, dirfd(d));
1578 static int remove_directory(sd_journal *j, Directory *d) {
1582 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1584 if (j->inotify_fd >= 0)
1585 inotify_rm_watch(j->inotify_fd, d->wd);
1588 hashmap_remove(j->directories_by_path, d->path);
1591 log_debug("Root directory %s removed.", d->path);
1593 log_debug("Directory %s removed.", d->path);
1601 static int add_search_paths(sd_journal *j) {
1603 const char search_paths[] =
1604 "/run/log/journal\0"
1605 "/var/log/journal\0";
1610 /* We ignore most errors here, since the idea is to only open
1611 * what's actually accessible, and ignore the rest. */
1613 NULSTR_FOREACH(p, search_paths) {
1614 r = add_root_directory(j, p);
1615 if (r < 0 && r != -ENOENT) {
1616 r = set_put_error(j, r);
1625 static int add_current_paths(sd_journal *j) {
1630 assert(j->no_new_files);
1632 /* Simply adds all directories for files we have open as
1633 * "root" directories. We don't expect errors here, so we
1634 * treat them as fatal. */
1636 HASHMAP_FOREACH(f, j->files, i) {
1637 _cleanup_free_ char *dir;
1640 dir = dirname_malloc(f->path);
1644 r = add_root_directory(j, dir);
1646 set_put_error(j, r);
1655 static int allocate_inotify(sd_journal *j) {
1658 if (j->inotify_fd < 0) {
1659 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1660 if (j->inotify_fd < 0)
1664 if (!j->directories_by_wd) {
1665 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1666 if (!j->directories_by_wd)
1673 static sd_journal *journal_new(int flags, const char *path) {
1676 j = new0(sd_journal, 1);
1680 j->original_pid = getpid();
1683 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1686 j->path = strdup(path);
1691 j->files = hashmap_new(string_hash_func, string_compare_func);
1692 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1693 j->mmap = mmap_cache_new();
1694 if (!j->files || !j->directories_by_path || !j->mmap)
1700 sd_journal_close(j);
1704 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1708 assert_return(ret, -EINVAL);
1709 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1711 j = journal_new(flags, NULL);
1715 r = add_search_paths(j);
1723 sd_journal_close(j);
1728 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1729 _cleanup_free_ char *root = NULL, *class = NULL;
1734 assert_return(machine, -EINVAL);
1735 assert_return(ret, -EINVAL);
1736 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1737 assert_return(filename_is_safe(machine), -EINVAL);
1739 p = strappenda("/run/systemd/machines/", machine);
1740 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1748 if (!streq_ptr(class, "container"))
1751 j = journal_new(flags, NULL);
1758 r = add_search_paths(j);
1766 sd_journal_close(j);
1770 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1774 assert_return(ret, -EINVAL);
1775 assert_return(path, -EINVAL);
1776 assert_return(flags == 0, -EINVAL);
1778 j = journal_new(flags, path);
1782 r = add_root_directory(j, path);
1784 set_put_error(j, r);
1792 sd_journal_close(j);
1797 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1802 assert_return(ret, -EINVAL);
1803 assert_return(flags == 0, -EINVAL);
1805 j = journal_new(flags, NULL);
1809 STRV_FOREACH(path, paths) {
1810 r = add_any_file(j, *path);
1812 log_error("Failed to open %s: %s", *path, strerror(-r));
1817 j->no_new_files = true;
1823 sd_journal_close(j);
1828 _public_ void sd_journal_close(sd_journal *j) {
1835 sd_journal_flush_matches(j);
1837 while ((f = hashmap_steal_first(j->files)))
1838 journal_file_close(f);
1840 hashmap_free(j->files);
1842 while ((d = hashmap_first(j->directories_by_path)))
1843 remove_directory(j, d);
1845 while ((d = hashmap_first(j->directories_by_wd)))
1846 remove_directory(j, d);
1848 hashmap_free(j->directories_by_path);
1849 hashmap_free(j->directories_by_wd);
1851 safe_close(j->inotify_fd);
1854 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1855 mmap_cache_unref(j->mmap);
1860 free(j->unique_field);
1861 set_free(j->errors);
1865 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1870 assert_return(j, -EINVAL);
1871 assert_return(!journal_pid_changed(j), -ECHILD);
1872 assert_return(ret, -EINVAL);
1874 f = j->current_file;
1876 return -EADDRNOTAVAIL;
1878 if (f->current_offset <= 0)
1879 return -EADDRNOTAVAIL;
1881 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1885 *ret = le64toh(o->entry.realtime);
1889 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1895 assert_return(j, -EINVAL);
1896 assert_return(!journal_pid_changed(j), -ECHILD);
1898 f = j->current_file;
1900 return -EADDRNOTAVAIL;
1902 if (f->current_offset <= 0)
1903 return -EADDRNOTAVAIL;
1905 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1910 *ret_boot_id = o->entry.boot_id;
1912 r = sd_id128_get_boot(&id);
1916 if (!sd_id128_equal(id, o->entry.boot_id))
1921 *ret = le64toh(o->entry.monotonic);
1926 static bool field_is_valid(const char *field) {
1934 if (startswith(field, "__"))
1937 for (p = field; *p; p++) {
1942 if (*p >= 'A' && *p <= 'Z')
1945 if (*p >= '0' && *p <= '9')
1954 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1957 size_t field_length;
1961 assert_return(j, -EINVAL);
1962 assert_return(!journal_pid_changed(j), -ECHILD);
1963 assert_return(field, -EINVAL);
1964 assert_return(data, -EINVAL);
1965 assert_return(size, -EINVAL);
1966 assert_return(field_is_valid(field), -EINVAL);
1968 f = j->current_file;
1970 return -EADDRNOTAVAIL;
1972 if (f->current_offset <= 0)
1973 return -EADDRNOTAVAIL;
1975 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1979 field_length = strlen(field);
1981 n = journal_file_entry_n_items(o);
1982 for (i = 0; i < n; i++) {
1988 p = le64toh(o->entry.items[i].object_offset);
1989 le_hash = o->entry.items[i].hash;
1990 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1994 if (le_hash != o->data.hash)
1997 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1999 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2001 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
2002 if (decompress_startswith(compression,
2004 &f->compress_buffer, &f->compress_buffer_size,
2005 field, field_length, '=')) {
2009 r = decompress_blob(compression,
2011 &f->compress_buffer, &f->compress_buffer_size, &rsize,
2016 *data = f->compress_buffer;
2017 *size = (size_t) rsize;
2022 return -EPROTONOSUPPORT;
2024 } else if (l >= field_length+1 &&
2025 memcmp(o->data.payload, field, field_length) == 0 &&
2026 o->data.payload[field_length] == '=') {
2030 if ((uint64_t) t != l)
2033 *data = o->data.payload;
2039 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2047 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
2052 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2055 /* We can't read objects larger than 4G on a 32bit machine */
2056 if ((uint64_t) t != l)
2059 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2061 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
2065 r = decompress_blob(compression,
2066 o->data.payload, l, &f->compress_buffer,
2067 &f->compress_buffer_size, &rsize, j->data_threshold);
2071 *data = f->compress_buffer;
2072 *size = (size_t) rsize;
2074 return -EPROTONOSUPPORT;
2077 *data = o->data.payload;
2084 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2091 assert_return(j, -EINVAL);
2092 assert_return(!journal_pid_changed(j), -ECHILD);
2093 assert_return(data, -EINVAL);
2094 assert_return(size, -EINVAL);
2096 f = j->current_file;
2098 return -EADDRNOTAVAIL;
2100 if (f->current_offset <= 0)
2101 return -EADDRNOTAVAIL;
2103 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2107 n = journal_file_entry_n_items(o);
2108 if (j->current_field >= n)
2111 p = le64toh(o->entry.items[j->current_field].object_offset);
2112 le_hash = o->entry.items[j->current_field].hash;
2113 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2117 if (le_hash != o->data.hash)
2120 r = return_data(j, f, o, data, size);
2124 j->current_field ++;
2129 _public_ void sd_journal_restart_data(sd_journal *j) {
2133 j->current_field = 0;
2136 _public_ int sd_journal_get_fd(sd_journal *j) {
2139 assert_return(j, -EINVAL);
2140 assert_return(!journal_pid_changed(j), -ECHILD);
2142 if (j->inotify_fd >= 0)
2143 return j->inotify_fd;
2145 r = allocate_inotify(j);
2149 /* Iterate through all dirs again, to add them to the
2151 if (j->no_new_files)
2152 r = add_current_paths(j);
2154 r = add_root_directory(j, j->path);
2156 r = add_search_paths(j);
2160 return j->inotify_fd;
2163 _public_ int sd_journal_get_events(sd_journal *j) {
2166 assert_return(j, -EINVAL);
2167 assert_return(!journal_pid_changed(j), -ECHILD);
2169 fd = sd_journal_get_fd(j);
2176 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2179 assert_return(j, -EINVAL);
2180 assert_return(!journal_pid_changed(j), -ECHILD);
2181 assert_return(timeout_usec, -EINVAL);
2183 fd = sd_journal_get_fd(j);
2187 if (!j->on_network) {
2188 *timeout_usec = (uint64_t) -1;
2192 /* If we are on the network we need to regularly check for
2193 * changes manually */
2195 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2199 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2206 /* Is this a subdirectory we watch? */
2207 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2211 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2212 (endswith(e->name, ".journal") ||
2213 endswith(e->name, ".journal~"))) {
2215 /* Event for a journal file */
2217 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2218 r = add_file(j, d->path, e->name);
2220 log_debug("Failed to add file %s/%s: %s",
2221 d->path, e->name, strerror(-r));
2222 set_put_error(j, r);
2225 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2227 r = remove_file(j, d->path, e->name);
2229 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
2232 } else if (!d->is_root && e->len == 0) {
2234 /* Event for a subdirectory */
2236 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2237 r = remove_directory(j, d);
2239 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
2243 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2245 /* Event for root directory */
2247 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2248 r = add_directory(j, d->path, e->name);
2250 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
2257 if (e->mask & IN_IGNORED)
2260 log_warning("Unknown inotify event.");
2263 static int determine_change(sd_journal *j) {
2268 b = j->current_invalidate_counter != j->last_invalidate_counter;
2269 j->last_invalidate_counter = j->current_invalidate_counter;
2271 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2274 _public_ int sd_journal_process(sd_journal *j) {
2275 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
2276 bool got_something = false;
2278 assert_return(j, -EINVAL);
2279 assert_return(!journal_pid_changed(j), -ECHILD);
2281 j->last_process_usec = now(CLOCK_MONOTONIC);
2284 struct inotify_event *e;
2287 l = read(j->inotify_fd, buffer, sizeof(buffer));
2289 if (errno == EAGAIN || errno == EINTR)
2290 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2295 got_something = true;
2297 e = (struct inotify_event*) buffer;
2301 process_inotify_event(j, e);
2303 step = sizeof(struct inotify_event) + e->len;
2304 assert(step <= (size_t) l);
2306 e = (struct inotify_event*) ((uint8_t*) e + step);
2312 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2316 assert_return(j, -EINVAL);
2317 assert_return(!journal_pid_changed(j), -ECHILD);
2319 if (j->inotify_fd < 0) {
2321 /* This is the first invocation, hence create the
2323 r = sd_journal_get_fd(j);
2327 /* The journal might have changed since the context
2328 * object was created and we weren't watching before,
2329 * hence don't wait for anything, and return
2331 return determine_change(j);
2334 r = sd_journal_get_timeout(j, &t);
2338 if (t != (uint64_t) -1) {
2341 n = now(CLOCK_MONOTONIC);
2342 t = t > n ? t - n : 0;
2344 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2349 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2350 } while (r == -EINTR);
2355 return sd_journal_process(j);
2358 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2362 uint64_t fmin = 0, tmax = 0;
2365 assert_return(j, -EINVAL);
2366 assert_return(!journal_pid_changed(j), -ECHILD);
2367 assert_return(from || to, -EINVAL);
2368 assert_return(from != to, -EINVAL);
2370 HASHMAP_FOREACH(f, j->files, i) {
2373 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2386 fmin = MIN(fr, fmin);
2387 tmax = MAX(t, tmax);
2396 return first ? 0 : 1;
2399 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2405 assert_return(j, -EINVAL);
2406 assert_return(!journal_pid_changed(j), -ECHILD);
2407 assert_return(from || to, -EINVAL);
2408 assert_return(from != to, -EINVAL);
2410 HASHMAP_FOREACH(f, j->files, i) {
2413 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2423 *from = MIN(fr, *from);
2438 void journal_print_header(sd_journal *j) {
2441 bool newline = false;
2445 HASHMAP_FOREACH(f, j->files, i) {
2451 journal_file_print_header(f);
2455 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2460 assert_return(j, -EINVAL);
2461 assert_return(!journal_pid_changed(j), -ECHILD);
2462 assert_return(bytes, -EINVAL);
2464 HASHMAP_FOREACH(f, j->files, i) {
2467 if (fstat(f->fd, &st) < 0)
2470 sum += (uint64_t) st.st_blocks * 512ULL;
2477 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2480 assert_return(j, -EINVAL);
2481 assert_return(!journal_pid_changed(j), -ECHILD);
2482 assert_return(!isempty(field), -EINVAL);
2483 assert_return(field_is_valid(field), -EINVAL);
2489 free(j->unique_field);
2490 j->unique_field = f;
2491 j->unique_file = NULL;
2492 j->unique_offset = 0;
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 j->unique_file = hashmap_first(j->files);
2510 if (!j->unique_file)
2512 j->unique_offset = 0;
2524 /* Proceed to next data object in the field's linked list */
2525 if (j->unique_offset == 0) {
2526 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2530 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2532 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2536 j->unique_offset = le64toh(o->data.next_field_offset);
2539 /* We reached the end of the list? Then start again, with the next file */
2540 if (j->unique_offset == 0) {
2543 n = hashmap_next(j->files, j->unique_file->path);
2551 /* We do not use the type context here, but 0 instead,
2552 * so that we can look at this data object at the same
2553 * time as one on another file */
2554 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2558 /* Let's do the type check by hand, since we used 0 context above. */
2559 if (o->object.type != OBJECT_DATA) {
2560 log_error("%s:offset " OFSfmt ": object has type %d, expected %d",
2561 j->unique_file->path, j->unique_offset,
2562 o->object.type, OBJECT_DATA);
2566 r = journal_file_object_keep(j->unique_file, o, j->unique_offset);
2570 r = return_data(j, j->unique_file, o, &odata, &ol);
2574 /* OK, now let's see if we already returned this data
2575 * object by checking if it exists in the earlier
2576 * traversed files. */
2578 HASHMAP_FOREACH(of, j->files, i) {
2582 if (of == j->unique_file)
2585 /* Skip this file it didn't have any fields
2587 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2588 le64toh(of->header->n_fields) <= 0)
2591 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2602 r = journal_file_object_release(j->unique_file, o, j->unique_offset);
2606 r = return_data(j, j->unique_file, o, data, l);
2614 _public_ void sd_journal_restart_unique(sd_journal *j) {
2618 j->unique_file = NULL;
2619 j->unique_offset = 0;
2622 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2623 assert_return(j, -EINVAL);
2624 assert_return(!journal_pid_changed(j), -ECHILD);
2626 return !j->on_network;
2629 static char *lookup_field(const char *field, void *userdata) {
2630 sd_journal *j = userdata;
2638 r = sd_journal_get_data(j, field, &data, &size);
2640 size > REPLACE_VAR_MAX)
2641 return strdup(field);
2643 d = strlen(field) + 1;
2645 return strndup((const char*) data + d, size - d);
2648 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2652 _cleanup_free_ char *text = NULL, *cid = NULL;
2656 assert_return(j, -EINVAL);
2657 assert_return(!journal_pid_changed(j), -ECHILD);
2658 assert_return(ret, -EINVAL);
2660 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2664 cid = strndup((const char*) data + 11, size - 11);
2668 r = sd_id128_from_string(cid, &id);
2672 r = catalog_get(CATALOG_DATABASE, id, &text);
2676 t = replace_var(text, lookup_field, j);
2684 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2685 assert_return(ret, -EINVAL);
2687 return catalog_get(CATALOG_DATABASE, id, ret);
2690 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2691 assert_return(j, -EINVAL);
2692 assert_return(!journal_pid_changed(j), -ECHILD);
2694 j->data_threshold = sz;
2698 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2699 assert_return(j, -EINVAL);
2700 assert_return(!journal_pid_changed(j), -ECHILD);
2701 assert_return(sz, -EINVAL);
2703 *sz = j->data_threshold;