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 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 static bool whole_file_precedes_location(JournalFile *f, Location *l, direction_t direction) {
504 if (l->type != LOCATION_DISCRETE && l->type != LOCATION_SEEK)
507 if (l->seqnum_set && sd_id128_equal(l->seqnum_id, f->header->seqnum_id))
508 return direction == DIRECTION_DOWN ?
509 l->seqnum > le64toh(f->header->tail_entry_seqnum) :
510 l->seqnum < le64toh(f->header->head_entry_seqnum);
513 return direction == DIRECTION_DOWN ?
514 l->realtime > le64toh(f->header->tail_entry_realtime) :
515 l->realtime < le64toh(f->header->head_entry_realtime);
520 _pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
526 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
528 if (l->monotonic_set &&
529 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
531 le64toh(ao->entry.realtime) == l->realtime &&
533 le64toh(ao->entry.xor_hash) == l->xor_hash)
537 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
539 a = le64toh(ao->entry.seqnum);
547 if (l->monotonic_set &&
548 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
550 a = le64toh(ao->entry.monotonic);
552 if (a < l->monotonic)
554 if (a > l->monotonic)
558 if (l->realtime_set) {
560 a = le64toh(ao->entry.realtime);
568 if (l->xor_hash_set) {
569 a = le64toh(ao->entry.xor_hash);
580 static int next_for_match(
584 uint64_t after_offset,
585 direction_t direction,
597 if (m->type == MATCH_DISCRETE) {
600 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
604 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
606 } else if (m->type == MATCH_OR_TERM) {
609 /* Find the earliest match beyond after_offset */
611 LIST_FOREACH(matches, i, m->matches) {
614 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
618 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
626 } else if (m->type == MATCH_AND_TERM) {
627 Match *i, *last_moved;
629 /* Always jump to the next matching entry and repeat
630 * this until we find an offset that matches for all
636 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
640 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
641 last_moved = m->matches;
643 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
646 r = next_for_match(j, i, f, np, direction, NULL, &cp);
650 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
651 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
660 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
672 static int find_location_for_match(
676 direction_t direction,
686 if (m->type == MATCH_DISCRETE) {
689 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
693 /* FIXME: missing: find by monotonic */
695 if (j->current_location.type == LOCATION_HEAD)
696 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
697 if (j->current_location.type == LOCATION_TAIL)
698 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
699 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
700 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
701 if (j->current_location.monotonic_set) {
702 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
706 if (j->current_location.realtime_set)
707 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
709 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
711 } else if (m->type == MATCH_OR_TERM) {
716 /* Find the earliest match */
718 LIST_FOREACH(matches, i, m->matches) {
721 r = find_location_for_match(j, i, f, direction, NULL, &cp);
725 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
733 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
748 assert(m->type == MATCH_AND_TERM);
750 /* First jump to the last match, and then find the
751 * next one where all matches match */
756 LIST_FOREACH(matches, i, m->matches) {
759 r = find_location_for_match(j, i, f, direction, NULL, &cp);
763 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
767 return next_for_match(j, m, f, np, direction, ret, offset);
771 static int find_location_with_matches(
774 direction_t direction,
786 /* No matches is simple */
788 if (j->current_location.type == LOCATION_HEAD)
789 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
790 if (j->current_location.type == LOCATION_TAIL)
791 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
792 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
793 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
794 if (j->current_location.monotonic_set) {
795 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
799 if (j->current_location.realtime_set)
800 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
802 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
804 return find_location_for_match(j, j->level0, f, direction, ret, offset);
807 static int next_with_matches(
810 direction_t direction,
825 /* No matches is easy. We simple advance the file
828 return journal_file_next_entry(f, c, cp, direction, ret, offset);
830 /* If we have a match then we look for the next matching entry
831 * with an offset at least one step larger */
832 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
835 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
843 if (f->last_direction == direction && f->current_offset > 0) {
844 cp = f->current_offset;
846 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
850 r = next_with_matches(j, f, direction, &c, &cp);
854 r = find_location_with_matches(j, f, direction, &c, &cp);
859 /* OK, we found the spot, now let's advance until an entry
860 * that is actually different from what we were previously
861 * looking at. This is necessary to handle entries which exist
862 * in two (or more) journal files, and which shall all be
863 * suppressed but one. */
868 if (j->current_location.type == LOCATION_DISCRETE) {
871 k = compare_with_location(f, c, &j->current_location);
873 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
885 r = next_with_matches(j, f, direction, &c, &cp);
891 static int real_journal_next(sd_journal *j, direction_t direction) {
892 JournalFile *f, *new_file = NULL;
893 uint64_t new_offset = 0;
899 assert_return(j, -EINVAL);
900 assert_return(!journal_pid_changed(j), -ECHILD);
902 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
905 if (whole_file_precedes_location(f, &j->current_location, direction))
908 r = next_beyond_location(j, f, direction, &o, &p);
910 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
911 remove_file_real(j, f);
921 k = compare_entry_order(f, o, new_file, new_offset);
923 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
935 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
939 set_location(j, LOCATION_DISCRETE, new_file, o, direction, new_offset);
944 _public_ int sd_journal_next(sd_journal *j) {
945 return real_journal_next(j, DIRECTION_DOWN);
948 _public_ int sd_journal_previous(sd_journal *j) {
949 return real_journal_next(j, DIRECTION_UP);
952 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
955 assert_return(j, -EINVAL);
956 assert_return(!journal_pid_changed(j), -ECHILD);
959 /* If this is not a discrete skip, then at least
960 * resolve the current location */
961 if (j->current_location.type != LOCATION_DISCRETE)
962 return real_journal_next(j, direction);
968 r = real_journal_next(j, direction);
982 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
983 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
986 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
987 return real_journal_next_skip(j, DIRECTION_UP, skip);
990 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
993 char bid[33], sid[33];
995 assert_return(j, -EINVAL);
996 assert_return(!journal_pid_changed(j), -ECHILD);
997 assert_return(cursor, -EINVAL);
999 if (!j->current_file || j->current_file->current_offset <= 0)
1000 return -EADDRNOTAVAIL;
1002 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1006 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
1007 sd_id128_to_string(o->entry.boot_id, bid);
1009 if (asprintf(cursor,
1010 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
1011 sid, le64toh(o->entry.seqnum),
1012 bid, le64toh(o->entry.monotonic),
1013 le64toh(o->entry.realtime),
1014 le64toh(o->entry.xor_hash)) < 0)
1020 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
1021 const char *word, *state;
1023 unsigned long long seqnum, monotonic, realtime, xor_hash;
1025 seqnum_id_set = false,
1027 boot_id_set = false,
1028 monotonic_set = false,
1029 realtime_set = false,
1030 xor_hash_set = false;
1031 sd_id128_t seqnum_id, boot_id;
1033 assert_return(j, -EINVAL);
1034 assert_return(!journal_pid_changed(j), -ECHILD);
1035 assert_return(!isempty(cursor), -EINVAL);
1037 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1041 if (l < 2 || word[1] != '=')
1044 item = strndup(word, l);
1051 seqnum_id_set = true;
1052 k = sd_id128_from_string(item+2, &seqnum_id);
1057 if (sscanf(item+2, "%llx", &seqnum) != 1)
1063 k = sd_id128_from_string(item+2, &boot_id);
1067 monotonic_set = true;
1068 if (sscanf(item+2, "%llx", &monotonic) != 1)
1073 realtime_set = true;
1074 if (sscanf(item+2, "%llx", &realtime) != 1)
1079 xor_hash_set = true;
1080 if (sscanf(item+2, "%llx", &xor_hash) != 1)
1091 if ((!seqnum_set || !seqnum_id_set) &&
1092 (!monotonic_set || !boot_id_set) &&
1098 j->current_location.type = LOCATION_SEEK;
1101 j->current_location.realtime = (uint64_t) realtime;
1102 j->current_location.realtime_set = true;
1105 if (seqnum_set && seqnum_id_set) {
1106 j->current_location.seqnum = (uint64_t) seqnum;
1107 j->current_location.seqnum_id = seqnum_id;
1108 j->current_location.seqnum_set = true;
1111 if (monotonic_set && boot_id_set) {
1112 j->current_location.monotonic = (uint64_t) monotonic;
1113 j->current_location.boot_id = boot_id;
1114 j->current_location.monotonic_set = true;
1118 j->current_location.xor_hash = (uint64_t) xor_hash;
1119 j->current_location.xor_hash_set = true;
1125 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1127 const char *word, *state;
1131 assert_return(j, -EINVAL);
1132 assert_return(!journal_pid_changed(j), -ECHILD);
1133 assert_return(!isempty(cursor), -EINVAL);
1135 if (!j->current_file || j->current_file->current_offset <= 0)
1136 return -EADDRNOTAVAIL;
1138 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1142 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1143 _cleanup_free_ char *item = NULL;
1145 unsigned long long ll;
1148 if (l < 2 || word[1] != '=')
1151 item = strndup(word, l);
1158 k = sd_id128_from_string(item+2, &id);
1161 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1166 if (sscanf(item+2, "%llx", &ll) != 1)
1168 if (ll != le64toh(o->entry.seqnum))
1173 k = sd_id128_from_string(item+2, &id);
1176 if (!sd_id128_equal(id, o->entry.boot_id))
1181 if (sscanf(item+2, "%llx", &ll) != 1)
1183 if (ll != le64toh(o->entry.monotonic))
1188 if (sscanf(item+2, "%llx", &ll) != 1)
1190 if (ll != le64toh(o->entry.realtime))
1195 if (sscanf(item+2, "%llx", &ll) != 1)
1197 if (ll != le64toh(o->entry.xor_hash))
1207 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1208 assert_return(j, -EINVAL);
1209 assert_return(!journal_pid_changed(j), -ECHILD);
1212 j->current_location.type = LOCATION_SEEK;
1213 j->current_location.boot_id = boot_id;
1214 j->current_location.monotonic = usec;
1215 j->current_location.monotonic_set = true;
1220 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1221 assert_return(j, -EINVAL);
1222 assert_return(!journal_pid_changed(j), -ECHILD);
1225 j->current_location.type = LOCATION_SEEK;
1226 j->current_location.realtime = usec;
1227 j->current_location.realtime_set = true;
1232 _public_ int sd_journal_seek_head(sd_journal *j) {
1233 assert_return(j, -EINVAL);
1234 assert_return(!journal_pid_changed(j), -ECHILD);
1237 j->current_location.type = LOCATION_HEAD;
1242 _public_ int sd_journal_seek_tail(sd_journal *j) {
1243 assert_return(j, -EINVAL);
1244 assert_return(!journal_pid_changed(j), -ECHILD);
1247 j->current_location.type = LOCATION_TAIL;
1252 static void check_network(sd_journal *j, int fd) {
1260 if (fstatfs(fd, &sfs) < 0)
1264 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1265 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1266 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1267 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1268 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1271 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1272 const char *full, *tilded, *atted;
1274 full = strappenda(prefix, ".journal");
1275 tilded = strappenda(full, "~");
1276 atted = strappenda(prefix, "@");
1278 return streq(filename, full) ||
1279 streq(filename, tilded) ||
1280 startswith(filename, atted);
1283 static bool file_type_wanted(int flags, const char *filename) {
1284 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1287 /* no flags set → every type is OK */
1288 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1291 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1294 if (flags & SD_JOURNAL_CURRENT_USER) {
1295 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1297 assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid())
1298 < (int) sizeof(prefix));
1300 if (file_has_type_prefix(prefix, filename))
1307 static int add_any_file(sd_journal *j, const char *path) {
1308 JournalFile *f = NULL;
1314 if (ordered_hashmap_get(j->files, path))
1317 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1318 log_warning("Too many open journal files, not adding %s.", path);
1319 return set_put_error(j, -ETOOMANYREFS);
1322 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1326 /* journal_file_dump(f); */
1328 r = ordered_hashmap_put(j->files, f->path, f);
1330 journal_file_close(f);
1334 log_debug("File %s added.", f->path);
1336 check_network(j, f->fd);
1338 j->current_invalidate_counter ++;
1343 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1344 _cleanup_free_ char *path = NULL;
1351 if (j->no_new_files ||
1352 !file_type_wanted(j->flags, filename))
1355 path = strjoin(prefix, "/", filename, NULL);
1359 r = add_any_file(j, path);
1365 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1366 _cleanup_free_ char *path;
1373 path = strjoin(prefix, "/", filename, NULL);
1377 f = ordered_hashmap_get(j->files, path);
1381 remove_file_real(j, f);
1385 static void remove_file_real(sd_journal *j, JournalFile *f) {
1389 ordered_hashmap_remove(j->files, f->path);
1391 log_debug("File %s removed.", f->path);
1393 if (j->current_file == f) {
1394 j->current_file = NULL;
1395 j->current_field = 0;
1398 if (j->unique_file == f) {
1399 /* Jump to the next unique_file or NULL if that one was last */
1400 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
1401 j->unique_offset = 0;
1402 if (!j->unique_file)
1403 j->unique_file_lost = true;
1406 journal_file_close(f);
1408 j->current_invalidate_counter ++;
1411 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1412 _cleanup_free_ char *path = NULL;
1414 _cleanup_closedir_ DIR *d = NULL;
1422 log_debug("Considering %s/%s.", prefix, dirname);
1424 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1425 (sd_id128_from_string(dirname, &id) < 0 ||
1426 sd_id128_get_machine(&mid) < 0 ||
1427 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1430 path = strjoin(prefix, "/", dirname, NULL);
1436 log_debug_errno(errno, "Failed to open %s: %m", path);
1437 if (errno == ENOENT)
1442 m = hashmap_get(j->directories_by_path, path);
1444 m = new0(Directory, 1);
1451 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1456 path = NULL; /* avoid freeing in cleanup */
1457 j->current_invalidate_counter ++;
1459 log_debug("Directory %s added.", m->path);
1461 } else if (m->is_root)
1464 if (m->wd <= 0 && j->inotify_fd >= 0) {
1466 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1467 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1468 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1471 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1472 inotify_rm_watch(j->inotify_fd, m->wd);
1480 if (!de && errno != 0) {
1482 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1488 if (dirent_is_file_with_suffix(de, ".journal") ||
1489 dirent_is_file_with_suffix(de, ".journal~")) {
1490 r = add_file(j, m->path, de->d_name);
1492 log_debug_errno(r, "Failed to add file %s/%s: %m",
1493 m->path, de->d_name);
1494 r = set_put_error(j, r);
1501 check_network(j, dirfd(d));
1506 static int add_root_directory(sd_journal *j, const char *p) {
1507 _cleanup_closedir_ DIR *d = NULL;
1514 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1515 !path_startswith(p, "/run"))
1519 p = strappenda(j->prefix, p);
1525 m = hashmap_get(j->directories_by_path, p);
1527 m = new0(Directory, 1);
1532 m->path = strdup(p);
1538 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1544 j->current_invalidate_counter ++;
1546 log_debug("Root directory %s added.", m->path);
1548 } else if (!m->is_root)
1551 if (m->wd <= 0 && j->inotify_fd >= 0) {
1553 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1554 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1557 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1558 inotify_rm_watch(j->inotify_fd, m->wd);
1561 if (j->no_new_files)
1570 if (!de && errno != 0) {
1572 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1578 if (dirent_is_file_with_suffix(de, ".journal") ||
1579 dirent_is_file_with_suffix(de, ".journal~")) {
1580 r = add_file(j, m->path, de->d_name);
1582 log_debug_errno(r, "Failed to add file %s/%s: %m",
1583 m->path, de->d_name);
1584 r = set_put_error(j, r);
1588 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1589 sd_id128_from_string(de->d_name, &id) >= 0) {
1591 r = add_directory(j, m->path, de->d_name);
1593 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
1597 check_network(j, dirfd(d));
1602 static int remove_directory(sd_journal *j, Directory *d) {
1606 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1608 if (j->inotify_fd >= 0)
1609 inotify_rm_watch(j->inotify_fd, d->wd);
1612 hashmap_remove(j->directories_by_path, d->path);
1615 log_debug("Root directory %s removed.", d->path);
1617 log_debug("Directory %s removed.", d->path);
1625 static int add_search_paths(sd_journal *j) {
1627 const char search_paths[] =
1628 "/run/log/journal\0"
1629 "/var/log/journal\0";
1634 /* We ignore most errors here, since the idea is to only open
1635 * what's actually accessible, and ignore the rest. */
1637 NULSTR_FOREACH(p, search_paths) {
1638 r = add_root_directory(j, p);
1639 if (r < 0 && r != -ENOENT) {
1640 r = set_put_error(j, r);
1649 static int add_current_paths(sd_journal *j) {
1654 assert(j->no_new_files);
1656 /* Simply adds all directories for files we have open as
1657 * "root" directories. We don't expect errors here, so we
1658 * treat them as fatal. */
1660 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
1661 _cleanup_free_ char *dir;
1664 dir = dirname_malloc(f->path);
1668 r = add_root_directory(j, dir);
1670 set_put_error(j, r);
1679 static int allocate_inotify(sd_journal *j) {
1682 if (j->inotify_fd < 0) {
1683 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1684 if (j->inotify_fd < 0)
1688 if (!j->directories_by_wd) {
1689 j->directories_by_wd = hashmap_new(NULL);
1690 if (!j->directories_by_wd)
1697 static sd_journal *journal_new(int flags, const char *path) {
1700 j = new0(sd_journal, 1);
1704 j->original_pid = getpid();
1707 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1710 j->path = strdup(path);
1715 j->files = ordered_hashmap_new(&string_hash_ops);
1716 j->directories_by_path = hashmap_new(&string_hash_ops);
1717 j->mmap = mmap_cache_new();
1718 if (!j->files || !j->directories_by_path || !j->mmap)
1724 sd_journal_close(j);
1728 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1732 assert_return(ret, -EINVAL);
1733 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1735 j = journal_new(flags, NULL);
1739 r = add_search_paths(j);
1747 sd_journal_close(j);
1752 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1753 _cleanup_free_ char *root = NULL, *class = NULL;
1758 assert_return(machine, -EINVAL);
1759 assert_return(ret, -EINVAL);
1760 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1761 assert_return(machine_name_is_valid(machine), -EINVAL);
1763 p = strappenda("/run/systemd/machines/", machine);
1764 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1772 if (!streq_ptr(class, "container"))
1775 j = journal_new(flags, NULL);
1782 r = add_search_paths(j);
1790 sd_journal_close(j);
1794 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1798 assert_return(ret, -EINVAL);
1799 assert_return(path, -EINVAL);
1800 assert_return(flags == 0, -EINVAL);
1802 j = journal_new(flags, path);
1806 r = add_root_directory(j, path);
1808 set_put_error(j, r);
1816 sd_journal_close(j);
1821 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1826 assert_return(ret, -EINVAL);
1827 assert_return(flags == 0, -EINVAL);
1829 j = journal_new(flags, NULL);
1833 STRV_FOREACH(path, paths) {
1834 r = add_any_file(j, *path);
1836 log_error_errno(r, "Failed to open %s: %m", *path);
1841 j->no_new_files = true;
1847 sd_journal_close(j);
1852 _public_ void sd_journal_close(sd_journal *j) {
1859 sd_journal_flush_matches(j);
1861 while ((f = ordered_hashmap_steal_first(j->files)))
1862 journal_file_close(f);
1864 ordered_hashmap_free(j->files);
1866 while ((d = hashmap_first(j->directories_by_path)))
1867 remove_directory(j, d);
1869 while ((d = hashmap_first(j->directories_by_wd)))
1870 remove_directory(j, d);
1872 hashmap_free(j->directories_by_path);
1873 hashmap_free(j->directories_by_wd);
1875 safe_close(j->inotify_fd);
1878 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1879 mmap_cache_unref(j->mmap);
1884 free(j->unique_field);
1885 set_free(j->errors);
1889 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1894 assert_return(j, -EINVAL);
1895 assert_return(!journal_pid_changed(j), -ECHILD);
1896 assert_return(ret, -EINVAL);
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);
1909 *ret = le64toh(o->entry.realtime);
1913 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1919 assert_return(j, -EINVAL);
1920 assert_return(!journal_pid_changed(j), -ECHILD);
1922 f = j->current_file;
1924 return -EADDRNOTAVAIL;
1926 if (f->current_offset <= 0)
1927 return -EADDRNOTAVAIL;
1929 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1934 *ret_boot_id = o->entry.boot_id;
1936 r = sd_id128_get_boot(&id);
1940 if (!sd_id128_equal(id, o->entry.boot_id))
1945 *ret = le64toh(o->entry.monotonic);
1950 static bool field_is_valid(const char *field) {
1958 if (startswith(field, "__"))
1961 for (p = field; *p; p++) {
1966 if (*p >= 'A' && *p <= 'Z')
1969 if (*p >= '0' && *p <= '9')
1978 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1981 size_t field_length;
1985 assert_return(j, -EINVAL);
1986 assert_return(!journal_pid_changed(j), -ECHILD);
1987 assert_return(field, -EINVAL);
1988 assert_return(data, -EINVAL);
1989 assert_return(size, -EINVAL);
1990 assert_return(field_is_valid(field), -EINVAL);
1992 f = j->current_file;
1994 return -EADDRNOTAVAIL;
1996 if (f->current_offset <= 0)
1997 return -EADDRNOTAVAIL;
1999 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2003 field_length = strlen(field);
2005 n = journal_file_entry_n_items(o);
2006 for (i = 0; i < n; i++) {
2012 p = le64toh(o->entry.items[i].object_offset);
2013 le_hash = o->entry.items[i].hash;
2014 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2018 if (le_hash != o->data.hash)
2021 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2023 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2025 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
2026 if (decompress_startswith(compression,
2028 &f->compress_buffer, &f->compress_buffer_size,
2029 field, field_length, '=')) {
2033 r = decompress_blob(compression,
2035 &f->compress_buffer, &f->compress_buffer_size, &rsize,
2040 *data = f->compress_buffer;
2041 *size = (size_t) rsize;
2046 return -EPROTONOSUPPORT;
2048 } else if (l >= field_length+1 &&
2049 memcmp(o->data.payload, field, field_length) == 0 &&
2050 o->data.payload[field_length] == '=') {
2054 if ((uint64_t) t != l)
2057 *data = o->data.payload;
2063 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2071 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
2076 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2079 /* We can't read objects larger than 4G on a 32bit machine */
2080 if ((uint64_t) t != l)
2083 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2085 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
2089 r = decompress_blob(compression,
2090 o->data.payload, l, &f->compress_buffer,
2091 &f->compress_buffer_size, &rsize, j->data_threshold);
2095 *data = f->compress_buffer;
2096 *size = (size_t) rsize;
2098 return -EPROTONOSUPPORT;
2101 *data = o->data.payload;
2108 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2115 assert_return(j, -EINVAL);
2116 assert_return(!journal_pid_changed(j), -ECHILD);
2117 assert_return(data, -EINVAL);
2118 assert_return(size, -EINVAL);
2120 f = j->current_file;
2122 return -EADDRNOTAVAIL;
2124 if (f->current_offset <= 0)
2125 return -EADDRNOTAVAIL;
2127 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2131 n = journal_file_entry_n_items(o);
2132 if (j->current_field >= n)
2135 p = le64toh(o->entry.items[j->current_field].object_offset);
2136 le_hash = o->entry.items[j->current_field].hash;
2137 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2141 if (le_hash != o->data.hash)
2144 r = return_data(j, f, o, data, size);
2148 j->current_field ++;
2153 _public_ void sd_journal_restart_data(sd_journal *j) {
2157 j->current_field = 0;
2160 _public_ int sd_journal_get_fd(sd_journal *j) {
2163 assert_return(j, -EINVAL);
2164 assert_return(!journal_pid_changed(j), -ECHILD);
2166 if (j->inotify_fd >= 0)
2167 return j->inotify_fd;
2169 r = allocate_inotify(j);
2173 /* Iterate through all dirs again, to add them to the
2175 if (j->no_new_files)
2176 r = add_current_paths(j);
2178 r = add_root_directory(j, j->path);
2180 r = add_search_paths(j);
2184 return j->inotify_fd;
2187 _public_ int sd_journal_get_events(sd_journal *j) {
2190 assert_return(j, -EINVAL);
2191 assert_return(!journal_pid_changed(j), -ECHILD);
2193 fd = sd_journal_get_fd(j);
2200 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2203 assert_return(j, -EINVAL);
2204 assert_return(!journal_pid_changed(j), -ECHILD);
2205 assert_return(timeout_usec, -EINVAL);
2207 fd = sd_journal_get_fd(j);
2211 if (!j->on_network) {
2212 *timeout_usec = (uint64_t) -1;
2216 /* If we are on the network we need to regularly check for
2217 * changes manually */
2219 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2223 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2230 /* Is this a subdirectory we watch? */
2231 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2235 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2236 (endswith(e->name, ".journal") ||
2237 endswith(e->name, ".journal~"))) {
2239 /* Event for a journal file */
2241 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2242 r = add_file(j, d->path, e->name);
2244 log_debug_errno(r, "Failed to add file %s/%s: %m",
2246 set_put_error(j, r);
2249 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2251 r = remove_file(j, d->path, e->name);
2253 log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
2256 } else if (!d->is_root && e->len == 0) {
2258 /* Event for a subdirectory */
2260 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2261 r = remove_directory(j, d);
2263 log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
2267 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2269 /* Event for root directory */
2271 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2272 r = add_directory(j, d->path, e->name);
2274 log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
2281 if (e->mask & IN_IGNORED)
2284 log_warning("Unknown inotify event.");
2287 static int determine_change(sd_journal *j) {
2292 b = j->current_invalidate_counter != j->last_invalidate_counter;
2293 j->last_invalidate_counter = j->current_invalidate_counter;
2295 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2298 _public_ int sd_journal_process(sd_journal *j) {
2299 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
2300 bool got_something = false;
2302 assert_return(j, -EINVAL);
2303 assert_return(!journal_pid_changed(j), -ECHILD);
2305 j->last_process_usec = now(CLOCK_MONOTONIC);
2308 struct inotify_event *e;
2311 l = read(j->inotify_fd, buffer, sizeof(buffer));
2313 if (errno == EAGAIN || errno == EINTR)
2314 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2319 got_something = true;
2321 e = (struct inotify_event*) buffer;
2325 process_inotify_event(j, e);
2327 step = sizeof(struct inotify_event) + e->len;
2328 assert(step <= (size_t) l);
2330 e = (struct inotify_event*) ((uint8_t*) e + step);
2336 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2340 assert_return(j, -EINVAL);
2341 assert_return(!journal_pid_changed(j), -ECHILD);
2343 if (j->inotify_fd < 0) {
2345 /* This is the first invocation, hence create the
2347 r = sd_journal_get_fd(j);
2351 /* The journal might have changed since the context
2352 * object was created and we weren't watching before,
2353 * hence don't wait for anything, and return
2355 return determine_change(j);
2358 r = sd_journal_get_timeout(j, &t);
2362 if (t != (uint64_t) -1) {
2365 n = now(CLOCK_MONOTONIC);
2366 t = t > n ? t - n : 0;
2368 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2373 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2374 } while (r == -EINTR);
2379 return sd_journal_process(j);
2382 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2386 uint64_t fmin = 0, tmax = 0;
2389 assert_return(j, -EINVAL);
2390 assert_return(!journal_pid_changed(j), -ECHILD);
2391 assert_return(from || to, -EINVAL);
2392 assert_return(from != to, -EINVAL);
2394 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2397 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2410 fmin = MIN(fr, fmin);
2411 tmax = MAX(t, tmax);
2420 return first ? 0 : 1;
2423 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2429 assert_return(j, -EINVAL);
2430 assert_return(!journal_pid_changed(j), -ECHILD);
2431 assert_return(from || to, -EINVAL);
2432 assert_return(from != to, -EINVAL);
2434 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2437 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2447 *from = MIN(fr, *from);
2462 void journal_print_header(sd_journal *j) {
2465 bool newline = false;
2469 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2475 journal_file_print_header(f);
2479 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2484 assert_return(j, -EINVAL);
2485 assert_return(!journal_pid_changed(j), -ECHILD);
2486 assert_return(bytes, -EINVAL);
2488 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2491 if (fstat(f->fd, &st) < 0)
2494 sum += (uint64_t) st.st_blocks * 512ULL;
2501 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2504 assert_return(j, -EINVAL);
2505 assert_return(!journal_pid_changed(j), -ECHILD);
2506 assert_return(!isempty(field), -EINVAL);
2507 assert_return(field_is_valid(field), -EINVAL);
2513 free(j->unique_field);
2514 j->unique_field = f;
2515 j->unique_file = NULL;
2516 j->unique_offset = 0;
2517 j->unique_file_lost = false;
2522 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2525 assert_return(j, -EINVAL);
2526 assert_return(!journal_pid_changed(j), -ECHILD);
2527 assert_return(data, -EINVAL);
2528 assert_return(l, -EINVAL);
2529 assert_return(j->unique_field, -EINVAL);
2531 k = strlen(j->unique_field);
2533 if (!j->unique_file) {
2534 if (j->unique_file_lost)
2537 j->unique_file = ordered_hashmap_first(j->files);
2538 if (!j->unique_file)
2541 j->unique_offset = 0;
2552 void *release_cookie;
2554 /* Proceed to next data object in the field's linked list */
2555 if (j->unique_offset == 0) {
2556 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2560 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2562 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2566 j->unique_offset = le64toh(o->data.next_field_offset);
2569 /* We reached the end of the list? Then start again, with the next file */
2570 if (j->unique_offset == 0) {
2571 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
2572 if (!j->unique_file)
2578 /* We do not use the type context here, but 0 instead,
2579 * so that we can look at this data object at the same
2580 * time as one on another file */
2581 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2585 /* Let's do the type check by hand, since we used 0 context above. */
2586 if (o->object.type != OBJECT_DATA) {
2587 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
2588 j->unique_file->path, j->unique_offset,
2589 o->object.type, OBJECT_DATA);
2593 r = journal_file_object_keep(j->unique_file, o, j->unique_offset, &release_cookie);
2597 r = return_data(j, j->unique_file, o, &odata, &ol);
2601 /* Check if we have at least the field name and "=". */
2603 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2604 j->unique_file->path, j->unique_offset,
2609 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2610 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2611 j->unique_file->path, j->unique_offset,
2616 /* OK, now let's see if we already returned this data
2617 * object by checking if it exists in the earlier
2618 * traversed files. */
2620 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2624 if (of == j->unique_file)
2627 /* Skip this file it didn't have any fields
2629 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2630 le64toh(of->header->n_fields) <= 0)
2633 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2641 r = journal_file_object_release(j->unique_file, release_cookie);
2648 r = return_data(j, j->unique_file, o, data, l);
2656 _public_ void sd_journal_restart_unique(sd_journal *j) {
2660 j->unique_file = NULL;
2661 j->unique_offset = 0;
2662 j->unique_file_lost = false;
2665 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2666 assert_return(j, -EINVAL);
2667 assert_return(!journal_pid_changed(j), -ECHILD);
2669 return !j->on_network;
2672 static char *lookup_field(const char *field, void *userdata) {
2673 sd_journal *j = userdata;
2681 r = sd_journal_get_data(j, field, &data, &size);
2683 size > REPLACE_VAR_MAX)
2684 return strdup(field);
2686 d = strlen(field) + 1;
2688 return strndup((const char*) data + d, size - d);
2691 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2695 _cleanup_free_ char *text = NULL, *cid = NULL;
2699 assert_return(j, -EINVAL);
2700 assert_return(!journal_pid_changed(j), -ECHILD);
2701 assert_return(ret, -EINVAL);
2703 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2707 cid = strndup((const char*) data + 11, size - 11);
2711 r = sd_id128_from_string(cid, &id);
2715 r = catalog_get(CATALOG_DATABASE, id, &text);
2719 t = replace_var(text, lookup_field, j);
2727 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2728 assert_return(ret, -EINVAL);
2730 return catalog_get(CATALOG_DATABASE, id, ret);
2733 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2734 assert_return(j, -EINVAL);
2735 assert_return(!journal_pid_changed(j), -ECHILD);
2737 j->data_threshold = sz;
2741 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2742 assert_return(j, -EINVAL);
2743 assert_return(!journal_pid_changed(j), -ECHILD);
2744 assert_return(sz, -EINVAL);
2746 *sz = j->data_threshold;