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"
36 #include "path-util.h"
39 #include "journal-internal.h"
42 #include "replace-var.h"
44 #define JOURNAL_FILES_MAX 1024
46 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
48 #define REPLACE_VAR_MAX 256
50 #define DEFAULT_DATA_THRESHOLD (64*1024)
52 static void detach_location(sd_journal
*j
) {
58 j
->current_file
= NULL
;
61 HASHMAP_FOREACH(f
, j
->files
, i
)
62 f
->current_offset
= 0;
65 static void reset_location(sd_journal
*j
) {
69 zero(j
->current_location
);
72 static void init_location(Location
*l
, LocationType type
, JournalFile
*f
, Object
*o
) {
74 assert(type
== LOCATION_DISCRETE
|| type
== LOCATION_SEEK
);
76 assert(o
->object
.type
== OBJECT_ENTRY
);
79 l
->seqnum
= le64toh(o
->entry
.seqnum
);
80 l
->seqnum_id
= f
->header
->seqnum_id
;
81 l
->realtime
= le64toh(o
->entry
.realtime
);
82 l
->monotonic
= le64toh(o
->entry
.monotonic
);
83 l
->boot_id
= o
->entry
.boot_id
;
84 l
->xor_hash
= le64toh(o
->entry
.xor_hash
);
86 l
->seqnum_set
= l
->realtime_set
= l
->monotonic_set
= l
->xor_hash_set
= true;
89 static void set_location(sd_journal
*j
, LocationType type
, JournalFile
*f
, Object
*o
, uint64_t offset
) {
91 assert(type
== LOCATION_DISCRETE
|| type
== LOCATION_SEEK
);
95 init_location(&j
->current_location
, type
, f
, o
);
100 f
->current_offset
= offset
;
103 static int match_is_valid(const void *data
, size_t size
) {
111 if (startswith(data
, "__"))
115 for (p
= b
; p
< b
+ size
; p
++) {
123 if (*p
>= 'A' && *p
<= 'Z')
126 if (*p
>= '0' && *p
<= '9')
135 static bool same_field(const void *_a
, size_t s
, const void *_b
, size_t t
) {
136 const uint8_t *a
= _a
, *b
= _b
;
139 for (j
= 0; j
< s
&& j
< t
; j
++) {
151 static Match
*match_new(Match
*p
, MatchType t
) {
162 LIST_PREPEND(Match
, matches
, p
->matches
, m
);
168 static void match_free(Match
*m
) {
172 match_free(m
->matches
);
175 LIST_REMOVE(Match
, matches
, m
->parent
->matches
, m
);
181 static void match_free_if_empty(Match
*m
) {
190 _public_
int sd_journal_add_match(sd_journal
*j
, const void *data
, size_t size
) {
191 Match
*l2
, *l3
, *add_here
= NULL
, *m
;
203 if (!match_is_valid(data
, size
))
209 * level 3: concrete matches */
212 j
->level0
= match_new(NULL
, MATCH_OR_TERM
);
218 j
->level1
= match_new(j
->level0
, MATCH_AND_TERM
);
223 assert(j
->level0
->type
== MATCH_OR_TERM
);
224 assert(j
->level1
->type
== MATCH_AND_TERM
);
226 le_hash
= htole64(hash64(data
, size
));
228 LIST_FOREACH(matches
, l2
, j
->level1
->matches
) {
229 assert(l2
->type
== MATCH_OR_TERM
);
231 LIST_FOREACH(matches
, l3
, l2
->matches
) {
232 assert(l3
->type
== MATCH_DISCRETE
);
234 /* Exactly the same match already? Then ignore
236 if (l3
->le_hash
== le_hash
&&
238 memcmp(l3
->data
, data
, size
) == 0)
241 /* Same field? Then let's add this to this OR term */
242 if (same_field(data
, size
, l3
->data
, l3
->size
)) {
253 add_here
= match_new(j
->level1
, MATCH_OR_TERM
);
258 m
= match_new(add_here
, MATCH_DISCRETE
);
262 m
->le_hash
= le_hash
;
264 m
->data
= memdup(data
, size
);
274 match_free_if_empty(add_here
);
277 match_free_if_empty(j
->level1
);
280 match_free_if_empty(j
->level0
);
285 _public_
int sd_journal_add_disjunction(sd_journal
*j
) {
296 if (!j
->level1
->matches
)
299 m
= match_new(j
->level0
, MATCH_AND_TERM
);
307 static char *match_make_string(Match
*m
) {
310 bool enclose
= false;
315 if (m
->type
== MATCH_DISCRETE
)
316 return strndup(m
->data
, m
->size
);
319 LIST_FOREACH(matches
, i
, m
->matches
) {
322 t
= match_make_string(i
);
329 k
= strjoin(p
, m
->type
== MATCH_OR_TERM
? " OR " : " AND ", t
, NULL
);
346 r
= strjoin("(", p
, ")", NULL
);
354 char *journal_make_match_string(sd_journal
*j
) {
357 return match_make_string(j
->level0
);
360 _public_
void sd_journal_flush_matches(sd_journal
*j
) {
366 match_free(j
->level0
);
368 j
->level0
= j
->level1
= NULL
;
373 static int compare_entry_order(JournalFile
*af
, Object
*_ao
,
374 JournalFile
*bf
, uint64_t bp
) {
384 /* The mmap cache might invalidate the object from the first
385 * file if we look at the one from the second file. Hence
386 * temporarily copy the header of the first one, and look at
388 ao
= alloca(offsetof(EntryObject
, items
));
389 memcpy(ao
, _ao
, offsetof(EntryObject
, items
));
391 r
= journal_file_move_to_object(bf
, OBJECT_ENTRY
, bp
, &bo
);
393 return strcmp(af
->path
, bf
->path
);
395 /* We operate on two different files here, hence we can access
396 * two objects at the same time, which we normally can't.
398 * If contents and timestamps match, these entries are
399 * identical, even if the seqnum does not match */
401 if (sd_id128_equal(ao
->entry
.boot_id
, bo
->entry
.boot_id
) &&
402 ao
->entry
.monotonic
== bo
->entry
.monotonic
&&
403 ao
->entry
.realtime
== bo
->entry
.realtime
&&
404 ao
->entry
.xor_hash
== bo
->entry
.xor_hash
)
407 if (sd_id128_equal(af
->header
->seqnum_id
, bf
->header
->seqnum_id
)) {
409 /* If this is from the same seqnum source, compare
411 a
= le64toh(ao
->entry
.seqnum
);
412 b
= le64toh(bo
->entry
.seqnum
);
419 /* Wow! This is weird, different data but the same
420 * seqnums? Something is borked, but let's make the
421 * best of it and compare by time. */
424 if (sd_id128_equal(ao
->entry
.boot_id
, bo
->entry
.boot_id
)) {
426 /* If the boot id matches compare monotonic time */
427 a
= le64toh(ao
->entry
.monotonic
);
428 b
= le64toh(bo
->entry
.monotonic
);
436 /* Otherwise compare UTC time */
437 a
= le64toh(ao
->entry
.realtime
);
438 b
= le64toh(bo
->entry
.realtime
);
445 /* Finally, compare by contents */
446 a
= le64toh(ao
->entry
.xor_hash
);
447 b
= le64toh(bo
->entry
.xor_hash
);
457 static int compare_with_location(JournalFile
*af
, Object
*ao
, Location
*l
) {
463 assert(l
->type
== LOCATION_DISCRETE
|| l
->type
== LOCATION_SEEK
);
465 if (l
->monotonic_set
&&
466 sd_id128_equal(ao
->entry
.boot_id
, l
->boot_id
) &&
468 le64toh(ao
->entry
.realtime
) == l
->realtime
&&
470 le64toh(ao
->entry
.xor_hash
) == l
->xor_hash
)
474 sd_id128_equal(af
->header
->seqnum_id
, l
->seqnum_id
)) {
476 a
= le64toh(ao
->entry
.seqnum
);
484 if (l
->monotonic_set
&&
485 sd_id128_equal(ao
->entry
.boot_id
, l
->boot_id
)) {
487 a
= le64toh(ao
->entry
.monotonic
);
489 if (a
< l
->monotonic
)
491 if (a
> l
->monotonic
)
495 if (l
->realtime_set
) {
497 a
= le64toh(ao
->entry
.realtime
);
505 if (l
->xor_hash_set
) {
506 a
= le64toh(ao
->entry
.xor_hash
);
517 static int next_for_match(
521 uint64_t after_offset
,
522 direction_t direction
,
534 if (m
->type
== MATCH_DISCRETE
) {
537 r
= journal_file_find_data_object_with_hash(f
, m
->data
, m
->size
, le64toh(m
->le_hash
), NULL
, &dp
);
541 return journal_file_move_to_entry_by_offset_for_data(f
, dp
, after_offset
, direction
, ret
, offset
);
543 } else if (m
->type
== MATCH_OR_TERM
) {
546 /* Find the earliest match beyond after_offset */
548 LIST_FOREACH(matches
, i
, m
->matches
) {
551 r
= next_for_match(j
, i
, f
, after_offset
, direction
, NULL
, &cp
);
555 if (np
== 0 || (direction
== DIRECTION_DOWN
? np
> cp
: np
< cp
))
560 } else if (m
->type
== MATCH_AND_TERM
) {
562 bool continue_looking
;
564 /* Always jump to the next matching entry and repeat
565 * this until we fine and offset that matches for all
573 continue_looking
= false;
575 LIST_FOREACH(matches
, i
, m
->matches
) {
579 limit
= after_offset
;
580 else if (direction
== DIRECTION_DOWN
)
581 limit
= MAX(np
, after_offset
);
583 limit
= MIN(np
, after_offset
);
585 r
= next_for_match(j
, i
, f
, limit
, direction
, NULL
, &cp
);
589 if ((direction
== DIRECTION_DOWN
? cp
>= after_offset
: cp
<= after_offset
) &&
590 (np
== 0 || (direction
== DIRECTION_DOWN
? cp
> np
: np
< cp
))) {
592 continue_looking
= true;
596 } while (continue_looking
);
602 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, np
, &n
);
614 static int find_location_for_match(
618 direction_t direction
,
628 if (m
->type
== MATCH_DISCRETE
) {
631 r
= journal_file_find_data_object_with_hash(f
, m
->data
, m
->size
, le64toh(m
->le_hash
), NULL
, &dp
);
635 /* FIXME: missing: find by monotonic */
637 if (j
->current_location
.type
== LOCATION_HEAD
)
638 return journal_file_next_entry_for_data(f
, NULL
, 0, dp
, DIRECTION_DOWN
, ret
, offset
);
639 if (j
->current_location
.type
== LOCATION_TAIL
)
640 return journal_file_next_entry_for_data(f
, NULL
, 0, dp
, DIRECTION_UP
, ret
, offset
);
641 if (j
->current_location
.seqnum_set
&& sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
642 return journal_file_move_to_entry_by_seqnum_for_data(f
, dp
, j
->current_location
.seqnum
, direction
, ret
, offset
);
643 if (j
->current_location
.monotonic_set
) {
644 r
= journal_file_move_to_entry_by_monotonic_for_data(f
, dp
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, ret
, offset
);
648 if (j
->current_location
.realtime_set
)
649 return journal_file_move_to_entry_by_realtime_for_data(f
, dp
, j
->current_location
.realtime
, direction
, ret
, offset
);
651 return journal_file_next_entry_for_data(f
, NULL
, 0, dp
, direction
, ret
, offset
);
653 } else if (m
->type
== MATCH_OR_TERM
) {
658 /* Find the earliest match */
660 LIST_FOREACH(matches
, i
, m
->matches
) {
663 r
= find_location_for_match(j
, i
, f
, direction
, NULL
, &cp
);
667 if (np
== 0 || (direction
== DIRECTION_DOWN
? np
> cp
: np
< cp
))
675 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, np
, &n
);
690 assert(m
->type
== MATCH_AND_TERM
);
692 /* First jump to the last match, and then find the
693 * next one where all matches match */
698 LIST_FOREACH(matches
, i
, m
->matches
) {
701 r
= find_location_for_match(j
, i
, f
, direction
, NULL
, &cp
);
705 if (np
== 0 || (direction
== DIRECTION_DOWN
? np
< cp
: np
> cp
))
709 return next_for_match(j
, m
, f
, np
, direction
, ret
, offset
);
713 static int find_location_with_matches(
716 direction_t direction
,
728 /* No matches is simple */
730 if (j
->current_location
.type
== LOCATION_HEAD
)
731 return journal_file_next_entry(f
, NULL
, 0, DIRECTION_DOWN
, ret
, offset
);
732 if (j
->current_location
.type
== LOCATION_TAIL
)
733 return journal_file_next_entry(f
, NULL
, 0, DIRECTION_UP
, ret
, offset
);
734 if (j
->current_location
.seqnum_set
&& sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
735 return journal_file_move_to_entry_by_seqnum(f
, j
->current_location
.seqnum
, direction
, ret
, offset
);
736 if (j
->current_location
.monotonic_set
) {
737 r
= journal_file_move_to_entry_by_monotonic(f
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, ret
, offset
);
741 if (j
->current_location
.realtime_set
)
742 return journal_file_move_to_entry_by_realtime(f
, j
->current_location
.realtime
, direction
, ret
, offset
);
744 return journal_file_next_entry(f
, NULL
, 0, direction
, ret
, offset
);
746 return find_location_for_match(j
, j
->level0
, f
, direction
, ret
, offset
);
749 static int next_with_matches(
752 direction_t direction
,
767 /* No matches is easy. We simple advance the file
770 return journal_file_next_entry(f
, c
, cp
, direction
, ret
, offset
);
772 /* If we have a match then we look for the next matching entry
773 * with an offset at least one step larger */
774 return next_for_match(j
, j
->level0
, f
, direction
== DIRECTION_DOWN
? cp
+1 : cp
-1, direction
, ret
, offset
);
777 static int next_beyond_location(sd_journal
*j
, JournalFile
*f
, direction_t direction
, Object
**ret
, uint64_t *offset
) {
785 if (f
->current_offset
> 0) {
786 cp
= f
->current_offset
;
788 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, cp
, &c
);
792 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
796 r
= find_location_with_matches(j
, f
, direction
, &c
, &cp
);
801 /* OK, we found the spot, now let's advance until to an entry
802 * that is actually different from what we were previously
803 * looking at. This is necessary to handle entries which exist
804 * in two (or more) journal files, and which shall all be
805 * suppressed but one. */
810 if (j
->current_location
.type
== LOCATION_DISCRETE
) {
813 k
= compare_with_location(f
, c
, &j
->current_location
);
814 if (direction
== DIRECTION_DOWN
)
829 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
835 static int real_journal_next(sd_journal
*j
, direction_t direction
) {
836 JournalFile
*f
, *new_file
= NULL
;
837 uint64_t new_offset
= 0;
846 HASHMAP_FOREACH(f
, j
->files
, i
) {
849 r
= next_beyond_location(j
, f
, direction
, &o
, &p
);
851 log_debug("Can't iterate through %s, ignoring: %s", f
->path
, strerror(-r
));
861 k
= compare_entry_order(f
, o
, new_file
, new_offset
);
863 if (direction
== DIRECTION_DOWN
)
878 r
= journal_file_move_to_object(new_file
, OBJECT_ENTRY
, new_offset
, &o
);
882 set_location(j
, LOCATION_DISCRETE
, new_file
, o
, new_offset
);
887 _public_
int sd_journal_next(sd_journal
*j
) {
888 return real_journal_next(j
, DIRECTION_DOWN
);
891 _public_
int sd_journal_previous(sd_journal
*j
) {
892 return real_journal_next(j
, DIRECTION_UP
);
895 static int real_journal_next_skip(sd_journal
*j
, direction_t direction
, uint64_t skip
) {
902 /* If this is not a discrete skip, then at least
903 * resolve the current location */
904 if (j
->current_location
.type
!= LOCATION_DISCRETE
)
905 return real_journal_next(j
, direction
);
911 r
= real_journal_next(j
, direction
);
925 _public_
int sd_journal_next_skip(sd_journal
*j
, uint64_t skip
) {
926 return real_journal_next_skip(j
, DIRECTION_DOWN
, skip
);
929 _public_
int sd_journal_previous_skip(sd_journal
*j
, uint64_t skip
) {
930 return real_journal_next_skip(j
, DIRECTION_UP
, skip
);
933 _public_
int sd_journal_get_cursor(sd_journal
*j
, char **cursor
) {
936 char bid
[33], sid
[33];
943 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
944 return -EADDRNOTAVAIL
;
946 r
= journal_file_move_to_object(j
->current_file
, OBJECT_ENTRY
, j
->current_file
->current_offset
, &o
);
950 sd_id128_to_string(j
->current_file
->header
->seqnum_id
, sid
);
951 sd_id128_to_string(o
->entry
.boot_id
, bid
);
954 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx",
955 sid
, (unsigned long long) le64toh(o
->entry
.seqnum
),
956 bid
, (unsigned long long) le64toh(o
->entry
.monotonic
),
957 (unsigned long long) le64toh(o
->entry
.realtime
),
958 (unsigned long long) le64toh(o
->entry
.xor_hash
)) < 0)
964 _public_
int sd_journal_seek_cursor(sd_journal
*j
, const char *cursor
) {
967 unsigned long long seqnum
, monotonic
, realtime
, xor_hash
;
969 seqnum_id_set
= false,
972 monotonic_set
= false,
973 realtime_set
= false,
974 xor_hash_set
= false;
975 sd_id128_t seqnum_id
, boot_id
;
982 FOREACH_WORD_SEPARATOR(w
, l
, cursor
, ";", state
) {
986 if (l
< 2 || w
[1] != '=')
989 item
= strndup(w
, l
);
996 seqnum_id_set
= true;
997 k
= sd_id128_from_string(item
+2, &seqnum_id
);
1002 if (sscanf(item
+2, "%llx", &seqnum
) != 1)
1008 k
= sd_id128_from_string(item
+2, &boot_id
);
1012 monotonic_set
= true;
1013 if (sscanf(item
+2, "%llx", &monotonic
) != 1)
1018 realtime_set
= true;
1019 if (sscanf(item
+2, "%llx", &realtime
) != 1)
1024 xor_hash_set
= true;
1025 if (sscanf(item
+2, "%llx", &xor_hash
) != 1)
1036 if ((!seqnum_set
|| !seqnum_id_set
) &&
1037 (!monotonic_set
|| !boot_id_set
) &&
1043 j
->current_location
.type
= LOCATION_SEEK
;
1046 j
->current_location
.realtime
= (uint64_t) realtime
;
1047 j
->current_location
.realtime_set
= true;
1050 if (seqnum_set
&& seqnum_id_set
) {
1051 j
->current_location
.seqnum
= (uint64_t) seqnum
;
1052 j
->current_location
.seqnum_id
= seqnum_id
;
1053 j
->current_location
.seqnum_set
= true;
1056 if (monotonic_set
&& boot_id_set
) {
1057 j
->current_location
.monotonic
= (uint64_t) monotonic
;
1058 j
->current_location
.boot_id
= boot_id
;
1059 j
->current_location
.monotonic_set
= true;
1063 j
->current_location
.xor_hash
= (uint64_t) xor_hash
;
1064 j
->current_location
.xor_hash_set
= true;
1070 _public_
int sd_journal_test_cursor(sd_journal
*j
, const char *cursor
) {
1078 if (isempty(cursor
))
1081 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
1082 return -EADDRNOTAVAIL
;
1084 r
= journal_file_move_to_object(j
->current_file
, OBJECT_ENTRY
, j
->current_file
->current_offset
, &o
);
1088 FOREACH_WORD_SEPARATOR(w
, l
, cursor
, ";", state
) {
1089 _cleanup_free_
char *item
= NULL
;
1091 unsigned long long ll
;
1094 if (l
< 2 || w
[1] != '=')
1097 item
= strndup(w
, l
);
1104 k
= sd_id128_from_string(item
+2, &id
);
1107 if (!sd_id128_equal(id
, j
->current_file
->header
->seqnum_id
))
1112 if (sscanf(item
+2, "%llx", &ll
) != 1)
1114 if (ll
!= le64toh(o
->entry
.seqnum
))
1119 k
= sd_id128_from_string(item
+2, &id
);
1122 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
1127 if (sscanf(item
+2, "%llx", &ll
) != 1)
1129 if (ll
!= le64toh(o
->entry
.monotonic
))
1134 if (sscanf(item
+2, "%llx", &ll
) != 1)
1136 if (ll
!= le64toh(o
->entry
.realtime
))
1141 if (sscanf(item
+2, "%llx", &ll
) != 1)
1143 if (ll
!= le64toh(o
->entry
.xor_hash
))
1153 _public_
int sd_journal_seek_monotonic_usec(sd_journal
*j
, sd_id128_t boot_id
, uint64_t usec
) {
1158 j
->current_location
.type
= LOCATION_SEEK
;
1159 j
->current_location
.boot_id
= boot_id
;
1160 j
->current_location
.monotonic
= usec
;
1161 j
->current_location
.monotonic_set
= true;
1166 _public_
int sd_journal_seek_realtime_usec(sd_journal
*j
, uint64_t usec
) {
1171 j
->current_location
.type
= LOCATION_SEEK
;
1172 j
->current_location
.realtime
= usec
;
1173 j
->current_location
.realtime_set
= true;
1178 _public_
int sd_journal_seek_head(sd_journal
*j
) {
1183 j
->current_location
.type
= LOCATION_HEAD
;
1188 _public_
int sd_journal_seek_tail(sd_journal
*j
) {
1193 j
->current_location
.type
= LOCATION_TAIL
;
1198 static void check_network(sd_journal
*j
, int fd
) {
1206 if (fstatfs(fd
, &sfs
) < 0)
1210 (long)sfs
.f_type
== (long)CIFS_MAGIC_NUMBER
||
1211 sfs
.f_type
== CODA_SUPER_MAGIC
||
1212 sfs
.f_type
== NCP_SUPER_MAGIC
||
1213 sfs
.f_type
== NFS_SUPER_MAGIC
||
1214 sfs
.f_type
== SMB_SUPER_MAGIC
;
1217 static int add_file(sd_journal
*j
, const char *prefix
, const char *filename
) {
1226 if ((j
->flags
& SD_JOURNAL_SYSTEM_ONLY
) &&
1227 !(streq(filename
, "system.journal") ||
1228 streq(filename
, "system.journal~") ||
1229 (startswith(filename
, "system@") &&
1230 (endswith(filename
, ".journal") || endswith(filename
, ".journal~")))))
1233 path
= strjoin(prefix
, "/", filename
, NULL
);
1237 if (hashmap_get(j
->files
, path
)) {
1242 if (hashmap_size(j
->files
) >= JOURNAL_FILES_MAX
) {
1243 log_debug("Too many open journal files, not adding %s, ignoring.", path
);
1248 r
= journal_file_open(path
, O_RDONLY
, 0, false, false, NULL
, j
->mmap
, NULL
, &f
);
1252 if (errno
== ENOENT
)
1258 /* journal_file_dump(f); */
1260 r
= hashmap_put(j
->files
, f
->path
, f
);
1262 journal_file_close(f
);
1266 check_network(j
, f
->fd
);
1268 j
->current_invalidate_counter
++;
1270 log_debug("File %s got added.", f
->path
);
1275 static int remove_file(sd_journal
*j
, const char *prefix
, const char *filename
) {
1283 path
= strjoin(prefix
, "/", filename
, NULL
);
1287 f
= hashmap_get(j
->files
, path
);
1292 hashmap_remove(j
->files
, f
->path
);
1294 log_debug("File %s got removed.", f
->path
);
1296 if (j
->current_file
== f
) {
1297 j
->current_file
= NULL
;
1298 j
->current_field
= 0;
1301 if (j
->unique_file
== f
) {
1302 j
->unique_file
= NULL
;
1303 j
->unique_offset
= 0;
1306 journal_file_close(f
);
1308 j
->current_invalidate_counter
++;
1313 static int add_directory(sd_journal
*j
, const char *prefix
, const char *dirname
) {
1324 log_debug("Considering %s/%s.", prefix
, dirname
);
1326 if ((j
->flags
& SD_JOURNAL_LOCAL_ONLY
) &&
1327 (sd_id128_from_string(dirname
, &id
) < 0 ||
1328 sd_id128_get_machine(&mid
) < 0 ||
1329 !(sd_id128_equal(id
, mid
) || path_startswith(prefix
, "/run"))))
1332 path
= strjoin(prefix
, "/", dirname
, NULL
);
1338 log_debug("Failed to open %s: %m", path
);
1341 if (errno
== ENOENT
)
1346 m
= hashmap_get(j
->directories_by_path
, path
);
1348 m
= new0(Directory
, 1);
1358 if (hashmap_put(j
->directories_by_path
, m
->path
, m
) < 0) {
1365 j
->current_invalidate_counter
++;
1367 log_debug("Directory %s got added.", m
->path
);
1369 } else if (m
->is_root
) {
1376 if (m
->wd
<= 0 && j
->inotify_fd
>= 0) {
1378 m
->wd
= inotify_add_watch(j
->inotify_fd
, m
->path
,
1379 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1380 IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
|IN_MOVED_FROM
|
1383 if (m
->wd
> 0 && hashmap_put(j
->directories_by_wd
, INT_TO_PTR(m
->wd
), m
) < 0)
1384 inotify_rm_watch(j
->inotify_fd
, m
->wd
);
1389 union dirent_storage buf
;
1391 r
= readdir_r(d
, &buf
.de
, &de
);
1395 if (dirent_is_file_with_suffix(de
, ".journal") ||
1396 dirent_is_file_with_suffix(de
, ".journal~")) {
1397 r
= add_file(j
, m
->path
, de
->d_name
);
1399 log_debug("Failed to add file %s/%s: %s", m
->path
, de
->d_name
, strerror(-r
));
1403 check_network(j
, dirfd(d
));
1410 static int add_root_directory(sd_journal
*j
, const char *p
) {
1418 if ((j
->flags
& SD_JOURNAL_RUNTIME_ONLY
) &&
1419 !path_startswith(p
, "/run"))
1426 m
= hashmap_get(j
->directories_by_path
, p
);
1428 m
= new0(Directory
, 1);
1435 m
->path
= strdup(p
);
1442 if (hashmap_put(j
->directories_by_path
, m
->path
, m
) < 0) {
1449 j
->current_invalidate_counter
++;
1451 log_debug("Root directory %s got added.", m
->path
);
1453 } else if (!m
->is_root
) {
1458 if (m
->wd
<= 0 && j
->inotify_fd
>= 0) {
1460 m
->wd
= inotify_add_watch(j
->inotify_fd
, m
->path
,
1461 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1464 if (m
->wd
> 0 && hashmap_put(j
->directories_by_wd
, INT_TO_PTR(m
->wd
), m
) < 0)
1465 inotify_rm_watch(j
->inotify_fd
, m
->wd
);
1470 union dirent_storage buf
;
1473 r
= readdir_r(d
, &buf
.de
, &de
);
1477 if (dirent_is_file_with_suffix(de
, ".journal") ||
1478 dirent_is_file_with_suffix(de
, ".journal~")) {
1479 r
= add_file(j
, m
->path
, de
->d_name
);
1481 log_debug("Failed to add file %s/%s: %s", m
->path
, de
->d_name
, strerror(-r
));
1483 } else if ((de
->d_type
== DT_DIR
|| de
->d_type
== DT_LNK
|| de
->d_type
== DT_UNKNOWN
) &&
1484 sd_id128_from_string(de
->d_name
, &id
) >= 0) {
1486 r
= add_directory(j
, m
->path
, de
->d_name
);
1488 log_debug("Failed to add directory %s/%s: %s", m
->path
, de
->d_name
, strerror(-r
));
1492 check_network(j
, dirfd(d
));
1499 static int remove_directory(sd_journal
*j
, Directory
*d
) {
1503 hashmap_remove(j
->directories_by_wd
, INT_TO_PTR(d
->wd
));
1505 if (j
->inotify_fd
>= 0)
1506 inotify_rm_watch(j
->inotify_fd
, d
->wd
);
1509 hashmap_remove(j
->directories_by_path
, d
->path
);
1512 log_debug("Root directory %s got removed.", d
->path
);
1514 log_debug("Directory %s got removed.", d
->path
);
1522 static int add_search_paths(sd_journal
*j
) {
1524 const char search_paths
[] =
1525 "/run/log/journal\0"
1526 "/var/log/journal\0";
1531 /* We ignore most errors here, since the idea is to only open
1532 * what's actually accessible, and ignore the rest. */
1534 NULSTR_FOREACH(p
, search_paths
)
1535 add_root_directory(j
, p
);
1540 static int allocate_inotify(sd_journal
*j
) {
1543 if (j
->inotify_fd
< 0) {
1544 j
->inotify_fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
1545 if (j
->inotify_fd
< 0)
1549 if (!j
->directories_by_wd
) {
1550 j
->directories_by_wd
= hashmap_new(trivial_hash_func
, trivial_compare_func
);
1551 if (!j
->directories_by_wd
)
1558 static sd_journal
*journal_new(int flags
, const char *path
) {
1561 j
= new0(sd_journal
, 1);
1567 j
->data_threshold
= DEFAULT_DATA_THRESHOLD
;
1570 j
->path
= strdup(path
);
1575 j
->files
= hashmap_new(string_hash_func
, string_compare_func
);
1576 j
->directories_by_path
= hashmap_new(string_hash_func
, string_compare_func
);
1577 j
->mmap
= mmap_cache_new();
1578 if (!j
->files
|| !j
->directories_by_path
|| !j
->mmap
)
1584 sd_journal_close(j
);
1588 _public_
int sd_journal_open(sd_journal
**ret
, int flags
) {
1595 if (flags
& ~(SD_JOURNAL_LOCAL_ONLY
|
1596 SD_JOURNAL_RUNTIME_ONLY
|
1597 SD_JOURNAL_SYSTEM_ONLY
))
1600 j
= journal_new(flags
, NULL
);
1604 r
= add_search_paths(j
);
1612 sd_journal_close(j
);
1617 _public_
int sd_journal_open_directory(sd_journal
**ret
, const char *path
, int flags
) {
1624 if (!path
|| !path_is_absolute(path
))
1630 j
= journal_new(flags
, path
);
1634 r
= add_root_directory(j
, path
);
1642 sd_journal_close(j
);
1647 _public_
void sd_journal_close(sd_journal
*j
) {
1654 while ((f
= hashmap_steal_first(j
->files
)))
1655 journal_file_close(f
);
1657 hashmap_free(j
->files
);
1659 while ((d
= hashmap_first(j
->directories_by_path
)))
1660 remove_directory(j
, d
);
1662 while ((d
= hashmap_first(j
->directories_by_wd
)))
1663 remove_directory(j
, d
);
1665 hashmap_free(j
->directories_by_path
);
1666 hashmap_free(j
->directories_by_wd
);
1668 if (j
->inotify_fd
>= 0)
1669 close_nointr_nofail(j
->inotify_fd
);
1671 sd_journal_flush_matches(j
);
1674 mmap_cache_unref(j
->mmap
);
1677 free(j
->unique_field
);
1681 _public_
int sd_journal_get_realtime_usec(sd_journal
*j
, uint64_t *ret
) {
1691 f
= j
->current_file
;
1693 return -EADDRNOTAVAIL
;
1695 if (f
->current_offset
<= 0)
1696 return -EADDRNOTAVAIL
;
1698 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1702 *ret
= le64toh(o
->entry
.realtime
);
1706 _public_
int sd_journal_get_monotonic_usec(sd_journal
*j
, uint64_t *ret
, sd_id128_t
*ret_boot_id
) {
1715 f
= j
->current_file
;
1717 return -EADDRNOTAVAIL
;
1719 if (f
->current_offset
<= 0)
1720 return -EADDRNOTAVAIL
;
1722 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1727 *ret_boot_id
= o
->entry
.boot_id
;
1729 r
= sd_id128_get_boot(&id
);
1733 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
1738 *ret
= le64toh(o
->entry
.monotonic
);
1743 static bool field_is_valid(const char *field
) {
1751 if (startswith(field
, "__"))
1754 for (p
= field
; *p
; p
++) {
1759 if (*p
>= 'A' && *p
<= 'Z')
1762 if (*p
>= '0' && *p
<= '9')
1771 _public_
int sd_journal_get_data(sd_journal
*j
, const char *field
, const void **data
, size_t *size
) {
1774 size_t field_length
;
1787 if (!field_is_valid(field
))
1790 f
= j
->current_file
;
1792 return -EADDRNOTAVAIL
;
1794 if (f
->current_offset
<= 0)
1795 return -EADDRNOTAVAIL
;
1797 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1801 field_length
= strlen(field
);
1803 n
= journal_file_entry_n_items(o
);
1804 for (i
= 0; i
< n
; i
++) {
1809 p
= le64toh(o
->entry
.items
[i
].object_offset
);
1810 le_hash
= o
->entry
.items
[i
].hash
;
1811 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
1815 if (le_hash
!= o
->data
.hash
)
1818 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
1820 if (o
->object
.flags
& OBJECT_COMPRESSED
) {
1823 if (uncompress_startswith(o
->data
.payload
, l
,
1824 &f
->compress_buffer
, &f
->compress_buffer_size
,
1825 field
, field_length
, '=')) {
1829 if (!uncompress_blob(o
->data
.payload
, l
,
1830 &f
->compress_buffer
, &f
->compress_buffer_size
, &rsize
,
1834 *data
= f
->compress_buffer
;
1835 *size
= (size_t) rsize
;
1840 return -EPROTONOSUPPORT
;
1843 } else if (l
>= field_length
+1 &&
1844 memcmp(o
->data
.payload
, field
, field_length
) == 0 &&
1845 o
->data
.payload
[field_length
] == '=') {
1849 if ((uint64_t) t
!= l
)
1852 *data
= o
->data
.payload
;
1858 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1866 static int return_data(sd_journal
*j
, JournalFile
*f
, Object
*o
, const void **data
, size_t *size
) {
1870 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
1873 /* We can't read objects larger than 4G on a 32bit machine */
1874 if ((uint64_t) t
!= l
)
1877 if (o
->object
.flags
& OBJECT_COMPRESSED
) {
1881 if (!uncompress_blob(o
->data
.payload
, l
, &f
->compress_buffer
, &f
->compress_buffer_size
, &rsize
, j
->data_threshold
))
1884 *data
= f
->compress_buffer
;
1885 *size
= (size_t) rsize
;
1887 return -EPROTONOSUPPORT
;
1890 *data
= o
->data
.payload
;
1897 _public_
int sd_journal_enumerate_data(sd_journal
*j
, const void **data
, size_t *size
) {
1911 f
= j
->current_file
;
1913 return -EADDRNOTAVAIL
;
1915 if (f
->current_offset
<= 0)
1916 return -EADDRNOTAVAIL
;
1918 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1922 n
= journal_file_entry_n_items(o
);
1923 if (j
->current_field
>= n
)
1926 p
= le64toh(o
->entry
.items
[j
->current_field
].object_offset
);
1927 le_hash
= o
->entry
.items
[j
->current_field
].hash
;
1928 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
1932 if (le_hash
!= o
->data
.hash
)
1935 r
= return_data(j
, f
, o
, data
, size
);
1939 j
->current_field
++;
1944 _public_
void sd_journal_restart_data(sd_journal
*j
) {
1948 j
->current_field
= 0;
1951 _public_
int sd_journal_get_fd(sd_journal
*j
) {
1957 if (j
->inotify_fd
>= 0)
1958 return j
->inotify_fd
;
1960 r
= allocate_inotify(j
);
1964 /* Iterate through all dirs again, to add them to the
1967 r
= add_root_directory(j
, j
->path
);
1969 r
= add_search_paths(j
);
1973 return j
->inotify_fd
;
1976 static void process_inotify_event(sd_journal
*j
, struct inotify_event
*e
) {
1983 /* Is this a subdirectory we watch? */
1984 d
= hashmap_get(j
->directories_by_wd
, INT_TO_PTR(e
->wd
));
1988 if (!(e
->mask
& IN_ISDIR
) && e
->len
> 0 &&
1989 (endswith(e
->name
, ".journal") ||
1990 endswith(e
->name
, ".journal~"))) {
1992 /* Event for a journal file */
1994 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
)) {
1995 r
= add_file(j
, d
->path
, e
->name
);
1997 log_debug("Failed to add file %s/%s: %s", d
->path
, e
->name
, strerror(-r
));
1999 } else if (e
->mask
& (IN_DELETE
|IN_MOVED_FROM
|IN_UNMOUNT
)) {
2001 r
= remove_file(j
, d
->path
, e
->name
);
2003 log_debug("Failed to remove file %s/%s: %s", d
->path
, e
->name
, strerror(-r
));
2006 } else if (!d
->is_root
&& e
->len
== 0) {
2008 /* Event for a subdirectory */
2010 if (e
->mask
& (IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
)) {
2011 r
= remove_directory(j
, d
);
2013 log_debug("Failed to remove directory %s: %s", d
->path
, strerror(-r
));
2017 } else if (d
->is_root
&& (e
->mask
& IN_ISDIR
) && e
->len
> 0 && sd_id128_from_string(e
->name
, &id
) >= 0) {
2019 /* Event for root directory */
2021 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
)) {
2022 r
= add_directory(j
, d
->path
, e
->name
);
2024 log_debug("Failed to add directory %s/%s: %s", d
->path
, e
->name
, strerror(-r
));
2031 if (e
->mask
& IN_IGNORED
)
2034 log_warning("Unknown inotify event.");
2037 static int determine_change(sd_journal
*j
) {
2042 b
= j
->current_invalidate_counter
!= j
->last_invalidate_counter
;
2043 j
->last_invalidate_counter
= j
->current_invalidate_counter
;
2045 return b
? SD_JOURNAL_INVALIDATE
: SD_JOURNAL_APPEND
;
2048 _public_
int sd_journal_process(sd_journal
*j
) {
2049 uint8_t buffer
[sizeof(struct inotify_event
) + FILENAME_MAX
] _alignas_(struct inotify_event
);
2050 bool got_something
= false;
2056 struct inotify_event
*e
;
2059 l
= read(j
->inotify_fd
, buffer
, sizeof(buffer
));
2061 if (errno
== EAGAIN
|| errno
== EINTR
)
2062 return got_something
? determine_change(j
) : SD_JOURNAL_NOP
;
2067 got_something
= true;
2069 e
= (struct inotify_event
*) buffer
;
2073 process_inotify_event(j
, e
);
2075 step
= sizeof(struct inotify_event
) + e
->len
;
2076 assert(step
<= (size_t) l
);
2078 e
= (struct inotify_event
*) ((uint8_t*) e
+ step
);
2083 return determine_change(j
);
2086 _public_
int sd_journal_wait(sd_journal
*j
, uint64_t timeout_usec
) {
2091 if (j
->inotify_fd
< 0) {
2093 /* This is the first invocation, hence create the
2095 r
= sd_journal_get_fd(j
);
2099 /* The journal might have changed since the context
2100 * object was created and we weren't watching before,
2101 * hence don't wait for anything, and return
2103 return determine_change(j
);
2106 if (j
->on_network
) {
2107 /* If we are on the network we need to regularly check
2108 * for changes manually */
2110 if (timeout_usec
== (uint64_t) -1 || timeout_usec
> JOURNAL_FILES_RECHECK_USEC
)
2111 timeout_usec
= JOURNAL_FILES_RECHECK_USEC
;
2115 r
= fd_wait_for_event(j
->inotify_fd
, POLLIN
, timeout_usec
);
2116 } while (r
== -EINTR
);
2121 return sd_journal_process(j
);
2124 _public_
int sd_journal_get_cutoff_realtime_usec(sd_journal
*j
, uint64_t *from
, uint64_t *to
) {
2135 HASHMAP_FOREACH(f
, j
->files
, i
) {
2138 r
= journal_file_get_cutoff_realtime_usec(f
, &fr
, &t
);
2154 *from
= MIN(fr
, *from
);
2160 return first
? 0 : 1;
2163 _public_
int sd_journal_get_cutoff_monotonic_usec(sd_journal
*j
, sd_id128_t boot_id
, uint64_t *from
, uint64_t *to
) {
2174 HASHMAP_FOREACH(f
, j
->files
, i
) {
2177 r
= journal_file_get_cutoff_monotonic_usec(f
, boot_id
, &fr
, &t
);
2193 *from
= MIN(fr
, *from
);
2199 return first
? 0 : 1;
2202 void journal_print_header(sd_journal
*j
) {
2205 bool newline
= false;
2209 HASHMAP_FOREACH(f
, j
->files
, i
) {
2215 journal_file_print_header(f
);
2219 _public_
int sd_journal_get_usage(sd_journal
*j
, uint64_t *bytes
) {
2229 HASHMAP_FOREACH(f
, j
->files
, i
) {
2232 if (fstat(f
->fd
, &st
) < 0)
2235 sum
+= (uint64_t) st
.st_blocks
* 512ULL;
2242 _public_
int sd_journal_query_unique(sd_journal
*j
, const char *field
) {
2249 if (!field_is_valid(field
))
2256 free(j
->unique_field
);
2257 j
->unique_field
= f
;
2258 j
->unique_file
= NULL
;
2259 j
->unique_offset
= 0;
2264 _public_
int sd_journal_enumerate_unique(sd_journal
*j
, const void **data
, size_t *l
) {
2275 if (!j
->unique_field
)
2278 k
= strlen(j
->unique_field
);
2280 if (!j
->unique_file
) {
2281 j
->unique_file
= hashmap_first(j
->files
);
2282 if (!j
->unique_file
)
2284 j
->unique_offset
= 0;
2294 /* Proceed to next data object in the field's linked list */
2295 if (j
->unique_offset
== 0) {
2296 r
= journal_file_find_field_object(j
->unique_file
, j
->unique_field
, k
, &o
, NULL
);
2300 j
->unique_offset
= r
> 0 ? le64toh(o
->field
.head_data_offset
) : 0;
2302 r
= journal_file_move_to_object(j
->unique_file
, OBJECT_DATA
, j
->unique_offset
, &o
);
2306 j
->unique_offset
= le64toh(o
->data
.next_field_offset
);
2309 /* We reached the end of the list? Then start again, with the next file */
2310 if (j
->unique_offset
== 0) {
2313 n
= hashmap_next(j
->files
, j
->unique_file
->path
);
2321 /* We do not use the type context here, but 0 instead,
2322 * so that we can look at this data object at the same
2323 * time as one on another file */
2324 r
= journal_file_move_to_object(j
->unique_file
, 0, j
->unique_offset
, &o
);
2328 /* Let's do the type check by hand, since we used 0 context above. */
2329 if (o
->object
.type
!= OBJECT_DATA
)
2332 r
= return_data(j
, j
->unique_file
, o
, &odata
, &ol
);
2336 /* OK, now let's see if we already returned this data
2337 * object by checking if it exists in the earlier
2338 * traversed files. */
2340 HASHMAP_FOREACH(of
, j
->files
, i
) {
2344 if (of
== j
->unique_file
)
2347 /* Skip this file it didn't have any fields
2349 if (JOURNAL_HEADER_CONTAINS(of
->header
, n_fields
) &&
2350 le64toh(of
->header
->n_fields
) <= 0)
2353 r
= journal_file_find_data_object_with_hash(of
, odata
, ol
, le64toh(o
->data
.hash
), &oo
, &op
);
2364 r
= return_data(j
, j
->unique_file
, o
, data
, l
);
2372 _public_
void sd_journal_restart_unique(sd_journal
*j
) {
2376 j
->unique_file
= NULL
;
2377 j
->unique_offset
= 0;
2380 _public_
int sd_journal_reliable_fd(sd_journal
*j
) {
2384 return !j
->on_network
;
2387 static char *lookup_field(const char *field
, void *userdata
) {
2388 sd_journal
*j
= userdata
;
2396 r
= sd_journal_get_data(j
, field
, &data
, &size
);
2398 size
> REPLACE_VAR_MAX
)
2399 return strdup(field
);
2401 d
= strlen(field
) + 1;
2403 return strndup((const char*) data
+ d
, size
- d
);
2406 _public_
int sd_journal_get_catalog(sd_journal
*j
, char **ret
) {
2410 _cleanup_free_
char *text
= NULL
, *cid
= NULL
;
2419 r
= sd_journal_get_data(j
, "MESSAGE_ID", &data
, &size
);
2423 cid
= strndup((const char*) data
+ 11, size
- 11);
2427 r
= sd_id128_from_string(cid
, &id
);
2431 r
= catalog_get(id
, &text
);
2435 t
= replace_var(text
, lookup_field
, j
);
2443 _public_
int sd_journal_get_catalog_for_message_id(sd_id128_t id
, char **ret
) {
2447 return catalog_get(id
, ret
);
2450 _public_
int sd_journal_set_data_threshold(sd_journal
*j
, size_t sz
) {
2454 j
->data_threshold
= sz
;
2458 _public_
int sd_journal_get_data_threshold(sd_journal
*j
, size_t *sz
) {
2464 *sz
= j
->data_threshold
;