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 static bool file_may_have_preceding_entry(JournalFile *f, JournalFile *of, uint64_t op, direction_t direction) {
527 r = journal_file_move_to_object(of, OBJECT_ENTRY, op, &o);
531 if (sd_id128_equal(f->header->seqnum_id, of->header->seqnum_id))
532 return direction == DIRECTION_DOWN ?
533 le64toh(o->entry.seqnum) >= le64toh(f->header->head_entry_seqnum) :
534 le64toh(o->entry.seqnum) <= le64toh(f->header->tail_entry_seqnum);
536 return direction == DIRECTION_DOWN ?
537 le64toh(o->entry.realtime) >= le64toh(f->header->head_entry_realtime) :
538 le64toh(o->entry.realtime) <= le64toh(f->header->tail_entry_realtime);
541 _pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
547 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
549 if (l->monotonic_set &&
550 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
552 le64toh(ao->entry.realtime) == l->realtime &&
554 le64toh(ao->entry.xor_hash) == l->xor_hash)
558 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
560 a = le64toh(ao->entry.seqnum);
568 if (l->monotonic_set &&
569 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
571 a = le64toh(ao->entry.monotonic);
573 if (a < l->monotonic)
575 if (a > l->monotonic)
579 if (l->realtime_set) {
581 a = le64toh(ao->entry.realtime);
589 if (l->xor_hash_set) {
590 a = le64toh(ao->entry.xor_hash);
601 static int next_for_match(
605 uint64_t after_offset,
606 direction_t direction,
618 if (m->type == MATCH_DISCRETE) {
621 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
625 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
627 } else if (m->type == MATCH_OR_TERM) {
630 /* Find the earliest match beyond after_offset */
632 LIST_FOREACH(matches, i, m->matches) {
635 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
639 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
647 } else if (m->type == MATCH_AND_TERM) {
648 Match *i, *last_moved;
650 /* Always jump to the next matching entry and repeat
651 * this until we find an offset that matches for all
657 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
661 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
662 last_moved = m->matches;
664 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
667 r = next_for_match(j, i, f, np, direction, NULL, &cp);
671 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
672 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
681 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
693 static int find_location_for_match(
697 direction_t direction,
707 if (m->type == MATCH_DISCRETE) {
710 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
714 /* FIXME: missing: find by monotonic */
716 if (j->current_location.type == LOCATION_HEAD)
717 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
718 if (j->current_location.type == LOCATION_TAIL)
719 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
720 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
721 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
722 if (j->current_location.monotonic_set) {
723 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
727 if (j->current_location.realtime_set)
728 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
730 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
732 } else if (m->type == MATCH_OR_TERM) {
737 /* Find the earliest match */
739 LIST_FOREACH(matches, i, m->matches) {
742 r = find_location_for_match(j, i, f, direction, NULL, &cp);
746 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
754 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
769 assert(m->type == MATCH_AND_TERM);
771 /* First jump to the last match, and then find the
772 * next one where all matches match */
777 LIST_FOREACH(matches, i, m->matches) {
780 r = find_location_for_match(j, i, f, direction, NULL, &cp);
784 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
788 return next_for_match(j, m, f, np, direction, ret, offset);
792 static int find_location_with_matches(
795 direction_t direction,
807 /* No matches is simple */
809 if (j->current_location.type == LOCATION_HEAD)
810 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
811 if (j->current_location.type == LOCATION_TAIL)
812 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
813 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
814 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
815 if (j->current_location.monotonic_set) {
816 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
820 if (j->current_location.realtime_set)
821 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
823 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
825 return find_location_for_match(j, j->level0, f, direction, ret, offset);
828 static int next_with_matches(
831 direction_t direction,
846 /* No matches is easy. We simple advance the file
849 return journal_file_next_entry(f, c, cp, direction, ret, offset);
851 /* If we have a match then we look for the next matching entry
852 * with an offset at least one step larger */
853 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
856 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
864 if (f->last_direction == direction && f->current_offset > 0) {
865 cp = f->current_offset;
867 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
871 r = next_with_matches(j, f, direction, &c, &cp);
875 r = find_location_with_matches(j, f, direction, &c, &cp);
880 /* OK, we found the spot, now let's advance until an entry
881 * that is actually different from what we were previously
882 * looking at. This is necessary to handle entries which exist
883 * in two (or more) journal files, and which shall all be
884 * suppressed but one. */
889 if (j->current_location.type == LOCATION_DISCRETE) {
892 k = compare_with_location(f, c, &j->current_location);
894 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
906 r = next_with_matches(j, f, direction, &c, &cp);
912 static int real_journal_next(sd_journal *j, direction_t direction) {
913 JournalFile *f, *new_file = NULL;
914 uint64_t new_offset = 0;
920 assert_return(j, -EINVAL);
921 assert_return(!journal_pid_changed(j), -ECHILD);
923 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
926 if (whole_file_precedes_location(f, &j->current_location, direction))
929 if (new_file && !file_may_have_preceding_entry(f, new_file, new_offset, direction))
932 r = next_beyond_location(j, f, direction, &o, &p);
934 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
935 remove_file_real(j, f);
945 k = compare_entry_order(f, o, new_file, new_offset);
947 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
959 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
963 set_location(j, LOCATION_DISCRETE, new_file, o, direction, new_offset);
968 _public_ int sd_journal_next(sd_journal *j) {
969 return real_journal_next(j, DIRECTION_DOWN);
972 _public_ int sd_journal_previous(sd_journal *j) {
973 return real_journal_next(j, DIRECTION_UP);
976 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
979 assert_return(j, -EINVAL);
980 assert_return(!journal_pid_changed(j), -ECHILD);
983 /* If this is not a discrete skip, then at least
984 * resolve the current location */
985 if (j->current_location.type != LOCATION_DISCRETE)
986 return real_journal_next(j, direction);
992 r = real_journal_next(j, direction);
1006 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
1007 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
1010 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
1011 return real_journal_next_skip(j, DIRECTION_UP, skip);
1014 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
1017 char bid[33], sid[33];
1019 assert_return(j, -EINVAL);
1020 assert_return(!journal_pid_changed(j), -ECHILD);
1021 assert_return(cursor, -EINVAL);
1023 if (!j->current_file || j->current_file->current_offset <= 0)
1024 return -EADDRNOTAVAIL;
1026 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1030 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
1031 sd_id128_to_string(o->entry.boot_id, bid);
1033 if (asprintf(cursor,
1034 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
1035 sid, le64toh(o->entry.seqnum),
1036 bid, le64toh(o->entry.monotonic),
1037 le64toh(o->entry.realtime),
1038 le64toh(o->entry.xor_hash)) < 0)
1044 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
1045 const char *word, *state;
1047 unsigned long long seqnum, monotonic, realtime, xor_hash;
1049 seqnum_id_set = false,
1051 boot_id_set = false,
1052 monotonic_set = false,
1053 realtime_set = false,
1054 xor_hash_set = false;
1055 sd_id128_t seqnum_id, boot_id;
1057 assert_return(j, -EINVAL);
1058 assert_return(!journal_pid_changed(j), -ECHILD);
1059 assert_return(!isempty(cursor), -EINVAL);
1061 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1065 if (l < 2 || word[1] != '=')
1068 item = strndup(word, l);
1075 seqnum_id_set = true;
1076 k = sd_id128_from_string(item+2, &seqnum_id);
1081 if (sscanf(item+2, "%llx", &seqnum) != 1)
1087 k = sd_id128_from_string(item+2, &boot_id);
1091 monotonic_set = true;
1092 if (sscanf(item+2, "%llx", &monotonic) != 1)
1097 realtime_set = true;
1098 if (sscanf(item+2, "%llx", &realtime) != 1)
1103 xor_hash_set = true;
1104 if (sscanf(item+2, "%llx", &xor_hash) != 1)
1115 if ((!seqnum_set || !seqnum_id_set) &&
1116 (!monotonic_set || !boot_id_set) &&
1122 j->current_location.type = LOCATION_SEEK;
1125 j->current_location.realtime = (uint64_t) realtime;
1126 j->current_location.realtime_set = true;
1129 if (seqnum_set && seqnum_id_set) {
1130 j->current_location.seqnum = (uint64_t) seqnum;
1131 j->current_location.seqnum_id = seqnum_id;
1132 j->current_location.seqnum_set = true;
1135 if (monotonic_set && boot_id_set) {
1136 j->current_location.monotonic = (uint64_t) monotonic;
1137 j->current_location.boot_id = boot_id;
1138 j->current_location.monotonic_set = true;
1142 j->current_location.xor_hash = (uint64_t) xor_hash;
1143 j->current_location.xor_hash_set = true;
1149 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1151 const char *word, *state;
1155 assert_return(j, -EINVAL);
1156 assert_return(!journal_pid_changed(j), -ECHILD);
1157 assert_return(!isempty(cursor), -EINVAL);
1159 if (!j->current_file || j->current_file->current_offset <= 0)
1160 return -EADDRNOTAVAIL;
1162 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1166 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
1167 _cleanup_free_ char *item = NULL;
1169 unsigned long long ll;
1172 if (l < 2 || word[1] != '=')
1175 item = strndup(word, l);
1182 k = sd_id128_from_string(item+2, &id);
1185 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1190 if (sscanf(item+2, "%llx", &ll) != 1)
1192 if (ll != le64toh(o->entry.seqnum))
1197 k = sd_id128_from_string(item+2, &id);
1200 if (!sd_id128_equal(id, o->entry.boot_id))
1205 if (sscanf(item+2, "%llx", &ll) != 1)
1207 if (ll != le64toh(o->entry.monotonic))
1212 if (sscanf(item+2, "%llx", &ll) != 1)
1214 if (ll != le64toh(o->entry.realtime))
1219 if (sscanf(item+2, "%llx", &ll) != 1)
1221 if (ll != le64toh(o->entry.xor_hash))
1231 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1232 assert_return(j, -EINVAL);
1233 assert_return(!journal_pid_changed(j), -ECHILD);
1236 j->current_location.type = LOCATION_SEEK;
1237 j->current_location.boot_id = boot_id;
1238 j->current_location.monotonic = usec;
1239 j->current_location.monotonic_set = true;
1244 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1245 assert_return(j, -EINVAL);
1246 assert_return(!journal_pid_changed(j), -ECHILD);
1249 j->current_location.type = LOCATION_SEEK;
1250 j->current_location.realtime = usec;
1251 j->current_location.realtime_set = true;
1256 _public_ int sd_journal_seek_head(sd_journal *j) {
1257 assert_return(j, -EINVAL);
1258 assert_return(!journal_pid_changed(j), -ECHILD);
1261 j->current_location.type = LOCATION_HEAD;
1266 _public_ int sd_journal_seek_tail(sd_journal *j) {
1267 assert_return(j, -EINVAL);
1268 assert_return(!journal_pid_changed(j), -ECHILD);
1271 j->current_location.type = LOCATION_TAIL;
1276 static void check_network(sd_journal *j, int fd) {
1284 if (fstatfs(fd, &sfs) < 0)
1288 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1289 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1290 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1291 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1292 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
1295 static bool file_has_type_prefix(const char *prefix, const char *filename) {
1296 const char *full, *tilded, *atted;
1298 full = strappenda(prefix, ".journal");
1299 tilded = strappenda(full, "~");
1300 atted = strappenda(prefix, "@");
1302 return streq(filename, full) ||
1303 streq(filename, tilded) ||
1304 startswith(filename, atted);
1307 static bool file_type_wanted(int flags, const char *filename) {
1308 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1311 /* no flags set → every type is OK */
1312 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1315 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1318 if (flags & SD_JOURNAL_CURRENT_USER) {
1319 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1321 assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid())
1322 < (int) sizeof(prefix));
1324 if (file_has_type_prefix(prefix, filename))
1331 static int add_any_file(sd_journal *j, const char *path) {
1332 JournalFile *f = NULL;
1338 if (ordered_hashmap_get(j->files, path))
1341 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1342 log_warning("Too many open journal files, not adding %s.", path);
1343 return set_put_error(j, -ETOOMANYREFS);
1346 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1350 /* journal_file_dump(f); */
1352 r = ordered_hashmap_put(j->files, f->path, f);
1354 journal_file_close(f);
1358 log_debug("File %s added.", f->path);
1360 check_network(j, f->fd);
1362 j->current_invalidate_counter ++;
1367 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1368 _cleanup_free_ char *path = NULL;
1375 if (j->no_new_files ||
1376 !file_type_wanted(j->flags, filename))
1379 path = strjoin(prefix, "/", filename, NULL);
1383 r = add_any_file(j, path);
1389 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1390 _cleanup_free_ char *path;
1397 path = strjoin(prefix, "/", filename, NULL);
1401 f = ordered_hashmap_get(j->files, path);
1405 remove_file_real(j, f);
1409 static void remove_file_real(sd_journal *j, JournalFile *f) {
1413 ordered_hashmap_remove(j->files, f->path);
1415 log_debug("File %s removed.", f->path);
1417 if (j->current_file == f) {
1418 j->current_file = NULL;
1419 j->current_field = 0;
1422 if (j->unique_file == f) {
1423 /* Jump to the next unique_file or NULL if that one was last */
1424 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
1425 j->unique_offset = 0;
1426 if (!j->unique_file)
1427 j->unique_file_lost = true;
1430 journal_file_close(f);
1432 j->current_invalidate_counter ++;
1435 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1436 _cleanup_free_ char *path = NULL;
1438 _cleanup_closedir_ DIR *d = NULL;
1446 log_debug("Considering %s/%s.", prefix, dirname);
1448 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1449 (sd_id128_from_string(dirname, &id) < 0 ||
1450 sd_id128_get_machine(&mid) < 0 ||
1451 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1454 path = strjoin(prefix, "/", dirname, NULL);
1460 log_debug_errno(errno, "Failed to open %s: %m", path);
1461 if (errno == ENOENT)
1466 m = hashmap_get(j->directories_by_path, path);
1468 m = new0(Directory, 1);
1475 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1480 path = NULL; /* avoid freeing in cleanup */
1481 j->current_invalidate_counter ++;
1483 log_debug("Directory %s added.", m->path);
1485 } else if (m->is_root)
1488 if (m->wd <= 0 && j->inotify_fd >= 0) {
1490 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1491 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1492 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1495 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1496 inotify_rm_watch(j->inotify_fd, m->wd);
1504 if (!de && errno != 0) {
1506 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1512 if (dirent_is_file_with_suffix(de, ".journal") ||
1513 dirent_is_file_with_suffix(de, ".journal~")) {
1514 r = add_file(j, m->path, de->d_name);
1516 log_debug_errno(r, "Failed to add file %s/%s: %m",
1517 m->path, de->d_name);
1518 r = set_put_error(j, r);
1525 check_network(j, dirfd(d));
1530 static int add_root_directory(sd_journal *j, const char *p) {
1531 _cleanup_closedir_ DIR *d = NULL;
1538 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1539 !path_startswith(p, "/run"))
1543 p = strappenda(j->prefix, p);
1549 m = hashmap_get(j->directories_by_path, p);
1551 m = new0(Directory, 1);
1556 m->path = strdup(p);
1562 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1568 j->current_invalidate_counter ++;
1570 log_debug("Root directory %s added.", m->path);
1572 } else if (!m->is_root)
1575 if (m->wd <= 0 && j->inotify_fd >= 0) {
1577 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1578 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1581 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1582 inotify_rm_watch(j->inotify_fd, m->wd);
1585 if (j->no_new_files)
1594 if (!de && errno != 0) {
1596 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
1602 if (dirent_is_file_with_suffix(de, ".journal") ||
1603 dirent_is_file_with_suffix(de, ".journal~")) {
1604 r = add_file(j, m->path, de->d_name);
1606 log_debug_errno(r, "Failed to add file %s/%s: %m",
1607 m->path, de->d_name);
1608 r = set_put_error(j, r);
1612 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1613 sd_id128_from_string(de->d_name, &id) >= 0) {
1615 r = add_directory(j, m->path, de->d_name);
1617 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
1621 check_network(j, dirfd(d));
1626 static int remove_directory(sd_journal *j, Directory *d) {
1630 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1632 if (j->inotify_fd >= 0)
1633 inotify_rm_watch(j->inotify_fd, d->wd);
1636 hashmap_remove(j->directories_by_path, d->path);
1639 log_debug("Root directory %s removed.", d->path);
1641 log_debug("Directory %s removed.", d->path);
1649 static int add_search_paths(sd_journal *j) {
1651 const char search_paths[] =
1652 "/run/log/journal\0"
1653 "/var/log/journal\0";
1658 /* We ignore most errors here, since the idea is to only open
1659 * what's actually accessible, and ignore the rest. */
1661 NULSTR_FOREACH(p, search_paths) {
1662 r = add_root_directory(j, p);
1663 if (r < 0 && r != -ENOENT) {
1664 r = set_put_error(j, r);
1673 static int add_current_paths(sd_journal *j) {
1678 assert(j->no_new_files);
1680 /* Simply adds all directories for files we have open as
1681 * "root" directories. We don't expect errors here, so we
1682 * treat them as fatal. */
1684 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
1685 _cleanup_free_ char *dir;
1688 dir = dirname_malloc(f->path);
1692 r = add_root_directory(j, dir);
1694 set_put_error(j, r);
1703 static int allocate_inotify(sd_journal *j) {
1706 if (j->inotify_fd < 0) {
1707 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1708 if (j->inotify_fd < 0)
1712 if (!j->directories_by_wd) {
1713 j->directories_by_wd = hashmap_new(NULL);
1714 if (!j->directories_by_wd)
1721 static sd_journal *journal_new(int flags, const char *path) {
1724 j = new0(sd_journal, 1);
1728 j->original_pid = getpid();
1731 j->data_threshold = DEFAULT_DATA_THRESHOLD;
1734 j->path = strdup(path);
1739 j->files = ordered_hashmap_new(&string_hash_ops);
1740 j->directories_by_path = hashmap_new(&string_hash_ops);
1741 j->mmap = mmap_cache_new();
1742 if (!j->files || !j->directories_by_path || !j->mmap)
1748 sd_journal_close(j);
1752 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1756 assert_return(ret, -EINVAL);
1757 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
1759 j = journal_new(flags, NULL);
1763 r = add_search_paths(j);
1771 sd_journal_close(j);
1776 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1777 _cleanup_free_ char *root = NULL, *class = NULL;
1782 assert_return(machine, -EINVAL);
1783 assert_return(ret, -EINVAL);
1784 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
1785 assert_return(machine_name_is_valid(machine), -EINVAL);
1787 p = strappenda("/run/systemd/machines/", machine);
1788 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1796 if (!streq_ptr(class, "container"))
1799 j = journal_new(flags, NULL);
1806 r = add_search_paths(j);
1814 sd_journal_close(j);
1818 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1822 assert_return(ret, -EINVAL);
1823 assert_return(path, -EINVAL);
1824 assert_return(flags == 0, -EINVAL);
1826 j = journal_new(flags, path);
1830 r = add_root_directory(j, path);
1832 set_put_error(j, r);
1840 sd_journal_close(j);
1845 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1850 assert_return(ret, -EINVAL);
1851 assert_return(flags == 0, -EINVAL);
1853 j = journal_new(flags, NULL);
1857 STRV_FOREACH(path, paths) {
1858 r = add_any_file(j, *path);
1860 log_error_errno(r, "Failed to open %s: %m", *path);
1865 j->no_new_files = true;
1871 sd_journal_close(j);
1876 _public_ void sd_journal_close(sd_journal *j) {
1883 sd_journal_flush_matches(j);
1885 while ((f = ordered_hashmap_steal_first(j->files)))
1886 journal_file_close(f);
1888 ordered_hashmap_free(j->files);
1890 while ((d = hashmap_first(j->directories_by_path)))
1891 remove_directory(j, d);
1893 while ((d = hashmap_first(j->directories_by_wd)))
1894 remove_directory(j, d);
1896 hashmap_free(j->directories_by_path);
1897 hashmap_free(j->directories_by_wd);
1899 safe_close(j->inotify_fd);
1902 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
1903 mmap_cache_unref(j->mmap);
1908 free(j->unique_field);
1909 set_free(j->errors);
1913 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1918 assert_return(j, -EINVAL);
1919 assert_return(!journal_pid_changed(j), -ECHILD);
1920 assert_return(ret, -EINVAL);
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);
1933 *ret = le64toh(o->entry.realtime);
1937 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1943 assert_return(j, -EINVAL);
1944 assert_return(!journal_pid_changed(j), -ECHILD);
1946 f = j->current_file;
1948 return -EADDRNOTAVAIL;
1950 if (f->current_offset <= 0)
1951 return -EADDRNOTAVAIL;
1953 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1958 *ret_boot_id = o->entry.boot_id;
1960 r = sd_id128_get_boot(&id);
1964 if (!sd_id128_equal(id, o->entry.boot_id))
1969 *ret = le64toh(o->entry.monotonic);
1974 static bool field_is_valid(const char *field) {
1982 if (startswith(field, "__"))
1985 for (p = field; *p; p++) {
1990 if (*p >= 'A' && *p <= 'Z')
1993 if (*p >= '0' && *p <= '9')
2002 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
2005 size_t field_length;
2009 assert_return(j, -EINVAL);
2010 assert_return(!journal_pid_changed(j), -ECHILD);
2011 assert_return(field, -EINVAL);
2012 assert_return(data, -EINVAL);
2013 assert_return(size, -EINVAL);
2014 assert_return(field_is_valid(field), -EINVAL);
2016 f = j->current_file;
2018 return -EADDRNOTAVAIL;
2020 if (f->current_offset <= 0)
2021 return -EADDRNOTAVAIL;
2023 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2027 field_length = strlen(field);
2029 n = journal_file_entry_n_items(o);
2030 for (i = 0; i < n; i++) {
2036 p = le64toh(o->entry.items[i].object_offset);
2037 le_hash = o->entry.items[i].hash;
2038 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2042 if (le_hash != o->data.hash)
2045 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2047 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2049 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
2050 if (decompress_startswith(compression,
2052 &f->compress_buffer, &f->compress_buffer_size,
2053 field, field_length, '=')) {
2057 r = decompress_blob(compression,
2059 &f->compress_buffer, &f->compress_buffer_size, &rsize,
2064 *data = f->compress_buffer;
2065 *size = (size_t) rsize;
2070 return -EPROTONOSUPPORT;
2072 } else if (l >= field_length+1 &&
2073 memcmp(o->data.payload, field, field_length) == 0 &&
2074 o->data.payload[field_length] == '=') {
2078 if ((uint64_t) t != l)
2081 *data = o->data.payload;
2087 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2095 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
2100 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2103 /* We can't read objects larger than 4G on a 32bit machine */
2104 if ((uint64_t) t != l)
2107 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2109 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
2113 r = decompress_blob(compression,
2114 o->data.payload, l, &f->compress_buffer,
2115 &f->compress_buffer_size, &rsize, j->data_threshold);
2119 *data = f->compress_buffer;
2120 *size = (size_t) rsize;
2122 return -EPROTONOSUPPORT;
2125 *data = o->data.payload;
2132 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
2139 assert_return(j, -EINVAL);
2140 assert_return(!journal_pid_changed(j), -ECHILD);
2141 assert_return(data, -EINVAL);
2142 assert_return(size, -EINVAL);
2144 f = j->current_file;
2146 return -EADDRNOTAVAIL;
2148 if (f->current_offset <= 0)
2149 return -EADDRNOTAVAIL;
2151 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2155 n = journal_file_entry_n_items(o);
2156 if (j->current_field >= n)
2159 p = le64toh(o->entry.items[j->current_field].object_offset);
2160 le_hash = o->entry.items[j->current_field].hash;
2161 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2165 if (le_hash != o->data.hash)
2168 r = return_data(j, f, o, data, size);
2172 j->current_field ++;
2177 _public_ void sd_journal_restart_data(sd_journal *j) {
2181 j->current_field = 0;
2184 _public_ int sd_journal_get_fd(sd_journal *j) {
2187 assert_return(j, -EINVAL);
2188 assert_return(!journal_pid_changed(j), -ECHILD);
2190 if (j->inotify_fd >= 0)
2191 return j->inotify_fd;
2193 r = allocate_inotify(j);
2197 /* Iterate through all dirs again, to add them to the
2199 if (j->no_new_files)
2200 r = add_current_paths(j);
2202 r = add_root_directory(j, j->path);
2204 r = add_search_paths(j);
2208 return j->inotify_fd;
2211 _public_ int sd_journal_get_events(sd_journal *j) {
2214 assert_return(j, -EINVAL);
2215 assert_return(!journal_pid_changed(j), -ECHILD);
2217 fd = sd_journal_get_fd(j);
2224 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2227 assert_return(j, -EINVAL);
2228 assert_return(!journal_pid_changed(j), -ECHILD);
2229 assert_return(timeout_usec, -EINVAL);
2231 fd = sd_journal_get_fd(j);
2235 if (!j->on_network) {
2236 *timeout_usec = (uint64_t) -1;
2240 /* If we are on the network we need to regularly check for
2241 * changes manually */
2243 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2247 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2254 /* Is this a subdirectory we watch? */
2255 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2259 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2260 (endswith(e->name, ".journal") ||
2261 endswith(e->name, ".journal~"))) {
2263 /* Event for a journal file */
2265 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2266 r = add_file(j, d->path, e->name);
2268 log_debug_errno(r, "Failed to add file %s/%s: %m",
2270 set_put_error(j, r);
2273 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2275 r = remove_file(j, d->path, e->name);
2277 log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
2280 } else if (!d->is_root && e->len == 0) {
2282 /* Event for a subdirectory */
2284 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2285 r = remove_directory(j, d);
2287 log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
2291 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2293 /* Event for root directory */
2295 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2296 r = add_directory(j, d->path, e->name);
2298 log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
2305 if (e->mask & IN_IGNORED)
2308 log_warning("Unknown inotify event.");
2311 static int determine_change(sd_journal *j) {
2316 b = j->current_invalidate_counter != j->last_invalidate_counter;
2317 j->last_invalidate_counter = j->current_invalidate_counter;
2319 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2322 _public_ int sd_journal_process(sd_journal *j) {
2323 bool got_something = false;
2325 assert_return(j, -EINVAL);
2326 assert_return(!journal_pid_changed(j), -ECHILD);
2328 j->last_process_usec = now(CLOCK_MONOTONIC);
2331 uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event);
2332 struct inotify_event *e;
2335 l = read(j->inotify_fd, buffer, sizeof(buffer));
2337 if (errno == EAGAIN || errno == EINTR)
2338 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2343 got_something = true;
2345 FOREACH_INOTIFY_EVENT(e, buffer, l)
2346 process_inotify_event(j, e);
2350 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2354 assert_return(j, -EINVAL);
2355 assert_return(!journal_pid_changed(j), -ECHILD);
2357 if (j->inotify_fd < 0) {
2359 /* This is the first invocation, hence create the
2361 r = sd_journal_get_fd(j);
2365 /* The journal might have changed since the context
2366 * object was created and we weren't watching before,
2367 * hence don't wait for anything, and return
2369 return determine_change(j);
2372 r = sd_journal_get_timeout(j, &t);
2376 if (t != (uint64_t) -1) {
2379 n = now(CLOCK_MONOTONIC);
2380 t = t > n ? t - n : 0;
2382 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2387 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2388 } while (r == -EINTR);
2393 return sd_journal_process(j);
2396 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2400 uint64_t fmin = 0, tmax = 0;
2403 assert_return(j, -EINVAL);
2404 assert_return(!journal_pid_changed(j), -ECHILD);
2405 assert_return(from || to, -EINVAL);
2406 assert_return(from != to, -EINVAL);
2408 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2411 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2424 fmin = MIN(fr, fmin);
2425 tmax = MAX(t, tmax);
2434 return first ? 0 : 1;
2437 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2443 assert_return(j, -EINVAL);
2444 assert_return(!journal_pid_changed(j), -ECHILD);
2445 assert_return(from || to, -EINVAL);
2446 assert_return(from != to, -EINVAL);
2448 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2451 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2461 *from = MIN(fr, *from);
2476 void journal_print_header(sd_journal *j) {
2479 bool newline = false;
2483 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2489 journal_file_print_header(f);
2493 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2498 assert_return(j, -EINVAL);
2499 assert_return(!journal_pid_changed(j), -ECHILD);
2500 assert_return(bytes, -EINVAL);
2502 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2505 if (fstat(f->fd, &st) < 0)
2508 sum += (uint64_t) st.st_blocks * 512ULL;
2515 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2518 assert_return(j, -EINVAL);
2519 assert_return(!journal_pid_changed(j), -ECHILD);
2520 assert_return(!isempty(field), -EINVAL);
2521 assert_return(field_is_valid(field), -EINVAL);
2527 free(j->unique_field);
2528 j->unique_field = f;
2529 j->unique_file = NULL;
2530 j->unique_offset = 0;
2531 j->unique_file_lost = false;
2536 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2539 assert_return(j, -EINVAL);
2540 assert_return(!journal_pid_changed(j), -ECHILD);
2541 assert_return(data, -EINVAL);
2542 assert_return(l, -EINVAL);
2543 assert_return(j->unique_field, -EINVAL);
2545 k = strlen(j->unique_field);
2547 if (!j->unique_file) {
2548 if (j->unique_file_lost)
2551 j->unique_file = ordered_hashmap_first(j->files);
2552 if (!j->unique_file)
2555 j->unique_offset = 0;
2567 /* Proceed to next data object in the field's linked list */
2568 if (j->unique_offset == 0) {
2569 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2573 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2575 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2579 j->unique_offset = le64toh(o->data.next_field_offset);
2582 /* We reached the end of the list? Then start again, with the next file */
2583 if (j->unique_offset == 0) {
2584 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
2585 if (!j->unique_file)
2591 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2592 * instead, so that we can look at this data object at the same
2593 * time as one on another file */
2594 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
2598 /* Let's do the type check by hand, since we used 0 context above. */
2599 if (o->object.type != OBJECT_DATA) {
2600 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
2601 j->unique_file->path, j->unique_offset,
2602 o->object.type, OBJECT_DATA);
2606 r = return_data(j, j->unique_file, o, &odata, &ol);
2610 /* Check if we have at least the field name and "=". */
2612 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2613 j->unique_file->path, j->unique_offset,
2618 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2619 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2620 j->unique_file->path, j->unique_offset,
2625 /* OK, now let's see if we already returned this data
2626 * object by checking if it exists in the earlier
2627 * traversed files. */
2629 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2633 if (of == j->unique_file)
2636 /* Skip this file it didn't have any fields
2638 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2639 le64toh(of->header->n_fields) <= 0)
2642 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2653 r = return_data(j, j->unique_file, o, data, l);
2661 _public_ void sd_journal_restart_unique(sd_journal *j) {
2665 j->unique_file = NULL;
2666 j->unique_offset = 0;
2667 j->unique_file_lost = false;
2670 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2671 assert_return(j, -EINVAL);
2672 assert_return(!journal_pid_changed(j), -ECHILD);
2674 return !j->on_network;
2677 static char *lookup_field(const char *field, void *userdata) {
2678 sd_journal *j = userdata;
2686 r = sd_journal_get_data(j, field, &data, &size);
2688 size > REPLACE_VAR_MAX)
2689 return strdup(field);
2691 d = strlen(field) + 1;
2693 return strndup((const char*) data + d, size - d);
2696 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2700 _cleanup_free_ char *text = NULL, *cid = NULL;
2704 assert_return(j, -EINVAL);
2705 assert_return(!journal_pid_changed(j), -ECHILD);
2706 assert_return(ret, -EINVAL);
2708 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2712 cid = strndup((const char*) data + 11, size - 11);
2716 r = sd_id128_from_string(cid, &id);
2720 r = catalog_get(CATALOG_DATABASE, id, &text);
2724 t = replace_var(text, lookup_field, j);
2732 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2733 assert_return(ret, -EINVAL);
2735 return catalog_get(CATALOG_DATABASE, id, ret);
2738 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2739 assert_return(j, -EINVAL);
2740 assert_return(!journal_pid_changed(j), -ECHILD);
2742 j->data_threshold = sz;
2746 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2747 assert_return(j, -EINVAL);
2748 assert_return(!journal_pid_changed(j), -ECHILD);
2749 assert_return(sz, -EINVAL);
2751 *sz = j->data_threshold;