1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include <linux/magic.h>
9 #include <sys/inotify.h>
13 #include "sd-journal.h"
15 #include "alloc-util.h"
18 #include "dirent-util.h"
23 #include "format-util.h"
26 #include "hostname-util.h"
27 #include "id128-util.h"
28 #include "inotify-util.h"
30 #include "journal-def.h"
31 #include "journal-file.h"
32 #include "journal-internal.h"
35 #include "nulstr-util.h"
36 #include "path-util.h"
37 #include "process-util.h"
38 #include "replace-var.h"
39 #include "stat-util.h"
40 #include "stdio-util.h"
41 #include "string-util.h"
43 #include "syslog-util.h"
45 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
47 /* The maximum size of variable values we'll expand in catalog entries. We bind this to PATH_MAX for now, as
48 * we want to be able to show all officially valid paths at least */
49 #define REPLACE_VAR_MAX PATH_MAX
51 #define DEFAULT_DATA_THRESHOLD (64*1024)
53 static void remove_file_real(sd_journal
*j
, JournalFile
*f
);
55 static bool journal_pid_changed(sd_journal
*j
) {
58 /* We don't support people creating a journal object and
59 * keeping it around over a fork(). Let's complain. */
61 return j
->original_pid
!= getpid_cached();
64 static int journal_put_error(sd_journal
*j
, int r
, const char *path
) {
65 _cleanup_free_
char *copy
= NULL
;
68 /* Memorize an error we encountered, and store which
69 * file/directory it was generated from. Note that we store
70 * only *one* path per error code, as the error code is the
71 * key into the hashmap, and the path is the value. This means
72 * we keep track only of all error kinds, but not of all error
73 * locations. This has the benefit that the hashmap cannot
76 * We return an error here only if we didn't manage to
77 * memorize the real error. */
88 k
= hashmap_ensure_put(&j
->errors
, NULL
, INT_TO_PTR(r
), copy
);
100 static void detach_location(sd_journal
*j
) {
105 j
->current_file
= NULL
;
106 j
->current_field
= 0;
108 ORDERED_HASHMAP_FOREACH(f
, j
->files
)
109 journal_file_reset_location(f
);
112 static void init_location(Location
*l
, LocationType type
, JournalFile
*f
, Object
*o
) {
114 assert(IN_SET(type
, LOCATION_DISCRETE
, LOCATION_SEEK
));
119 .seqnum
= le64toh(o
->entry
.seqnum
),
120 .seqnum_id
= f
->header
->seqnum_id
,
121 .realtime
= le64toh(o
->entry
.realtime
),
122 .monotonic
= le64toh(o
->entry
.monotonic
),
123 .boot_id
= o
->entry
.boot_id
,
124 .xor_hash
= le64toh(o
->entry
.xor_hash
),
126 .realtime_set
= true,
127 .monotonic_set
= true,
128 .xor_hash_set
= true,
132 static void set_location(sd_journal
*j
, JournalFile
*f
, Object
*o
) {
137 init_location(&j
->current_location
, LOCATION_DISCRETE
, f
, o
);
140 j
->current_field
= 0;
142 /* Let f know its candidate entry was picked. */
143 assert(f
->location_type
== LOCATION_SEEK
);
144 f
->location_type
= LOCATION_DISCRETE
;
147 static int match_is_valid(const void *data
, size_t size
) {
148 const char *b
= ASSERT_PTR(data
);
153 if (((char*) data
)[0] == '_' && ((char*) data
)[1] == '_')
156 for (const char *p
= b
; p
< b
+ size
; p
++) {
164 if (*p
>= 'A' && *p
<= 'Z')
167 if (ascii_isdigit(*p
))
176 static bool same_field(const void *_a
, size_t s
, const void *_b
, size_t t
) {
177 const uint8_t *a
= _a
, *b
= _b
;
179 for (size_t j
= 0; j
< s
&& j
< t
; j
++) {
188 assert_not_reached();
191 static Match
*match_new(Match
*p
, MatchType t
) {
204 LIST_PREPEND(matches
, p
->matches
, m
);
209 static Match
*match_free(Match
*m
) {
213 match_free(m
->matches
);
216 LIST_REMOVE(matches
, m
->parent
->matches
, m
);
222 static Match
*match_free_if_empty(Match
*m
) {
223 if (!m
|| m
->matches
)
226 return match_free(m
);
229 _public_
int sd_journal_add_match(sd_journal
*j
, const void *data
, size_t size
) {
230 Match
*add_here
= NULL
, *m
= NULL
;
233 assert_return(j
, -EINVAL
);
234 assert_return(!journal_pid_changed(j
), -ECHILD
);
235 assert_return(data
, -EINVAL
);
240 assert_return(match_is_valid(data
, size
), -EINVAL
);
246 * level 4: concrete matches */
249 j
->level0
= match_new(NULL
, MATCH_AND_TERM
);
255 j
->level1
= match_new(j
->level0
, MATCH_OR_TERM
);
261 j
->level2
= match_new(j
->level1
, MATCH_AND_TERM
);
266 assert(j
->level0
->type
== MATCH_AND_TERM
);
267 assert(j
->level1
->type
== MATCH_OR_TERM
);
268 assert(j
->level2
->type
== MATCH_AND_TERM
);
270 /* Old-style Jenkins (unkeyed) hashing only here. We do not cover new-style siphash (keyed) hashing
271 * here, since it's different for each file, and thus can't be pre-calculated in the Match object. */
272 hash
= jenkins_hash64(data
, size
);
274 LIST_FOREACH(matches
, l3
, j
->level2
->matches
) {
275 assert(l3
->type
== MATCH_OR_TERM
);
277 LIST_FOREACH(matches
, l4
, l3
->matches
) {
278 assert(l4
->type
== MATCH_DISCRETE
);
280 /* Exactly the same match already? Then ignore
282 if (l4
->hash
== hash
&&
284 memcmp(l4
->data
, data
, size
) == 0)
287 /* Same field? Then let's add this to this OR term */
288 if (same_field(data
, size
, l4
->data
, l4
->size
)) {
299 add_here
= match_new(j
->level2
, MATCH_OR_TERM
);
304 m
= match_new(add_here
, MATCH_DISCRETE
);
310 m
->data
= memdup(data
, size
);
320 match_free_if_empty(add_here
);
321 j
->level2
= match_free_if_empty(j
->level2
);
322 j
->level1
= match_free_if_empty(j
->level1
);
323 j
->level0
= match_free_if_empty(j
->level0
);
328 _public_
int sd_journal_add_conjunction(sd_journal
*j
) {
329 assert_return(j
, -EINVAL
);
330 assert_return(!journal_pid_changed(j
), -ECHILD
);
338 if (!j
->level1
->matches
)
347 _public_
int sd_journal_add_disjunction(sd_journal
*j
) {
348 assert_return(j
, -EINVAL
);
349 assert_return(!journal_pid_changed(j
), -ECHILD
);
360 if (!j
->level2
->matches
)
367 static char *match_make_string(Match
*m
) {
368 _cleanup_free_
char *p
= NULL
;
369 bool enclose
= false;
372 return strdup("none");
374 if (m
->type
== MATCH_DISCRETE
)
375 return cescape_length(m
->data
, m
->size
);
377 LIST_FOREACH(matches
, i
, m
->matches
) {
378 _cleanup_free_
char *t
= NULL
;
380 t
= match_make_string(i
);
385 if (!strextend(&p
, m
->type
== MATCH_OR_TERM
? " OR " : " AND ", t
))
394 return strjoin("(", p
, ")");
399 char *journal_make_match_string(sd_journal
*j
) {
402 return match_make_string(j
->level0
);
405 _public_
void sd_journal_flush_matches(sd_journal
*j
) {
410 match_free(j
->level0
);
412 j
->level0
= j
->level1
= j
->level2
= NULL
;
417 _pure_
static int compare_with_location(const JournalFile
*f
, const Location
*l
, const JournalFile
*current_file
) {
422 assert(f
->location_type
== LOCATION_SEEK
);
423 assert(IN_SET(l
->type
, LOCATION_DISCRETE
, LOCATION_SEEK
));
425 if (l
->monotonic_set
&&
426 sd_id128_equal(f
->current_boot_id
, l
->boot_id
) &&
428 f
->current_realtime
== l
->realtime
&&
430 f
->current_xor_hash
== l
->xor_hash
&&
432 sd_id128_equal(f
->header
->seqnum_id
, l
->seqnum_id
) &&
433 f
->current_seqnum
== l
->seqnum
&&
438 sd_id128_equal(f
->header
->seqnum_id
, l
->seqnum_id
)) {
440 r
= CMP(f
->current_seqnum
, l
->seqnum
);
445 if (l
->monotonic_set
&&
446 sd_id128_equal(f
->current_boot_id
, l
->boot_id
)) {
448 r
= CMP(f
->current_monotonic
, l
->monotonic
);
453 if (l
->realtime_set
) {
455 r
= CMP(f
->current_realtime
, l
->realtime
);
460 if (l
->xor_hash_set
) {
462 r
= CMP(f
->current_xor_hash
, l
->xor_hash
);
470 static int next_for_match(
474 uint64_t after_offset
,
475 direction_t direction
,
486 if (m
->type
== MATCH_DISCRETE
) {
490 /* If the keyed hash logic is used, we need to calculate the hash fresh per file. Otherwise
491 * we can use what we pre-calculated. */
492 if (JOURNAL_HEADER_KEYED_HASH(f
->header
))
493 hash
= journal_file_hash_data(f
, m
->data
, m
->size
);
497 r
= journal_file_find_data_object_with_hash(f
, m
->data
, m
->size
, hash
, &d
, NULL
);
501 return journal_file_move_to_entry_by_offset_for_data(f
, d
, after_offset
, direction
, ret
, offset
);
503 } else if (m
->type
== MATCH_OR_TERM
) {
505 /* Find the earliest match beyond after_offset */
507 LIST_FOREACH(matches
, i
, m
->matches
) {
510 r
= next_for_match(j
, i
, f
, after_offset
, direction
, NULL
, &cp
);
514 if (np
== 0 || (direction
== DIRECTION_DOWN
? cp
< np
: cp
> np
))
522 } else if (m
->type
== MATCH_AND_TERM
) {
525 /* Always jump to the next matching entry and repeat
526 * this until we find an offset that matches for all
532 r
= next_for_match(j
, m
->matches
, f
, after_offset
, direction
, NULL
, &np
);
536 assert(direction
== DIRECTION_DOWN
? np
>= after_offset
: np
<= after_offset
);
537 last_moved
= m
->matches
;
539 LIST_LOOP_BUT_ONE(matches
, i
, m
->matches
, last_moved
) {
542 r
= next_for_match(j
, i
, f
, np
, direction
, NULL
, &cp
);
546 assert(direction
== DIRECTION_DOWN
? cp
>= np
: cp
<= np
);
547 if (direction
== DIRECTION_DOWN
? cp
> np
: cp
< np
) {
557 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, np
, ret
);
568 static int find_location_for_match(
572 direction_t direction
,
582 if (m
->type
== MATCH_DISCRETE
) {
586 if (JOURNAL_HEADER_KEYED_HASH(f
->header
))
587 hash
= journal_file_hash_data(f
, m
->data
, m
->size
);
591 r
= journal_file_find_data_object_with_hash(f
, m
->data
, m
->size
, hash
, &d
, &dp
);
595 /* FIXME: missing: find by monotonic */
597 if (j
->current_location
.type
== LOCATION_HEAD
)
598 return journal_file_next_entry_for_data(f
, d
, DIRECTION_DOWN
, ret
, offset
);
599 if (j
->current_location
.type
== LOCATION_TAIL
)
600 return journal_file_next_entry_for_data(f
, d
, DIRECTION_UP
, ret
, offset
);
601 if (j
->current_location
.seqnum_set
&& sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
602 return journal_file_move_to_entry_by_seqnum_for_data(f
, d
, j
->current_location
.seqnum
, direction
, ret
, offset
);
603 if (j
->current_location
.monotonic_set
) {
604 r
= journal_file_move_to_entry_by_monotonic_for_data(f
, d
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, ret
, offset
);
608 /* The data object might have been invalidated. */
609 r
= journal_file_move_to_object(f
, OBJECT_DATA
, dp
, &d
);
613 if (j
->current_location
.realtime_set
)
614 return journal_file_move_to_entry_by_realtime_for_data(f
, d
, j
->current_location
.realtime
, direction
, ret
, offset
);
616 return journal_file_next_entry_for_data(f
, d
, direction
, ret
, offset
);
618 } else if (m
->type
== MATCH_OR_TERM
) {
621 /* Find the earliest match */
623 LIST_FOREACH(matches
, i
, m
->matches
) {
626 r
= find_location_for_match(j
, i
, f
, direction
, NULL
, &cp
);
630 if (np
== 0 || (direction
== DIRECTION_DOWN
? np
> cp
: np
< cp
))
639 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, np
, ret
);
652 assert(m
->type
== MATCH_AND_TERM
);
654 /* First jump to the last match, and then find the
655 * next one where all matches 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
? cp
> np
: cp
< np
))
671 return next_for_match(j
, m
, f
, np
, direction
, ret
, offset
);
675 static int find_location_with_matches(
678 direction_t direction
,
690 /* No matches is simple */
692 if (j
->current_location
.type
== LOCATION_HEAD
)
693 return journal_file_next_entry(f
, 0, DIRECTION_DOWN
, ret
, offset
);
694 if (j
->current_location
.type
== LOCATION_TAIL
)
695 return journal_file_next_entry(f
, 0, DIRECTION_UP
, ret
, offset
);
696 if (j
->current_location
.seqnum_set
&& sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
697 return journal_file_move_to_entry_by_seqnum(f
, j
->current_location
.seqnum
, direction
, ret
, offset
);
698 if (j
->current_location
.monotonic_set
) {
699 r
= journal_file_move_to_entry_by_monotonic(f
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, ret
, offset
);
703 if (j
->current_location
.realtime_set
)
704 return journal_file_move_to_entry_by_realtime(f
, j
->current_location
.realtime
, direction
, ret
, offset
);
706 return journal_file_next_entry(f
, 0, direction
, ret
, offset
);
708 return find_location_for_match(j
, j
->level0
, f
, direction
, ret
, offset
);
711 static int next_with_matches(
714 direction_t direction
,
723 /* No matches is easy. We simple advance the file
726 return journal_file_next_entry(f
, f
->current_offset
, direction
, ret
, offset
);
728 /* If we have a match then we look for the next matching entry
729 * with an offset at least one step larger */
730 return next_for_match(j
, j
->level0
, f
,
731 direction
== DIRECTION_DOWN
? f
->current_offset
+ 1
732 : f
->current_offset
- 1,
733 direction
, ret
, offset
);
736 static int next_beyond_location(sd_journal
*j
, JournalFile
*f
, direction_t direction
) {
738 uint64_t cp
, n_entries
;
744 n_entries
= le64toh(f
->header
->n_entries
);
746 /* If we hit EOF before, we don't need to look into this file again
747 * unless direction changed or new entries appeared. */
748 if (f
->last_direction
== direction
&& f
->location_type
== LOCATION_TAIL
&&
749 n_entries
== f
->last_n_entries
)
752 f
->last_n_entries
= n_entries
;
754 if (f
->last_direction
== direction
&& f
->current_offset
> 0) {
755 /* LOCATION_SEEK here means we did the work in a previous
756 * iteration and the current location already points to a
757 * candidate entry. */
758 if (f
->location_type
!= LOCATION_SEEK
) {
759 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
763 journal_file_save_location(f
, c
, cp
);
766 f
->last_direction
= direction
;
768 r
= find_location_with_matches(j
, f
, direction
, &c
, &cp
);
772 journal_file_save_location(f
, c
, cp
);
775 /* OK, we found the spot, now let's advance until an entry
776 * that is actually different from what we were previously
777 * looking at. This is necessary to handle entries which exist
778 * in two (or more) journal files, and which shall all be
779 * suppressed but one. */
784 if (j
->current_location
.type
== LOCATION_DISCRETE
) {
787 k
= compare_with_location(f
, &j
->current_location
, j
->current_file
);
789 found
= direction
== DIRECTION_DOWN
? k
> 0 : k
< 0;
796 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
800 journal_file_save_location(f
, c
, cp
);
804 static int compare_locations(JournalFile
*af
, JournalFile
*bf
) {
811 assert(af
->location_type
== LOCATION_SEEK
);
812 assert(bf
->location_type
== LOCATION_SEEK
);
814 /* If contents, timestamps and seqnum match, these entries are identical. */
815 if (sd_id128_equal(af
->current_boot_id
, bf
->current_boot_id
) &&
816 af
->current_monotonic
== bf
->current_monotonic
&&
817 af
->current_realtime
== bf
->current_realtime
&&
818 af
->current_xor_hash
== bf
->current_xor_hash
&&
819 sd_id128_equal(af
->header
->seqnum_id
, bf
->header
->seqnum_id
) &&
820 af
->current_seqnum
== bf
->current_seqnum
)
823 if (sd_id128_equal(af
->header
->seqnum_id
, bf
->header
->seqnum_id
)) {
824 /* If this is from the same seqnum source, compare seqnums */
825 r
= CMP(af
->current_seqnum
, bf
->current_seqnum
);
829 /* Wow! This is weird, different data but the same seqnums? Something is borked, but let's
830 * make the best of it and compare by time. */
833 if (sd_id128_equal(af
->current_boot_id
, bf
->current_boot_id
)) {
834 /* If the boot id matches, compare monotonic time */
835 r
= CMP(af
->current_monotonic
, bf
->current_monotonic
);
840 /* Otherwise, compare UTC time */
841 r
= CMP(af
->current_realtime
, bf
->current_realtime
);
845 /* Finally, compare by contents */
846 return CMP(af
->current_xor_hash
, bf
->current_xor_hash
);
849 static int real_journal_next(sd_journal
*j
, direction_t direction
) {
850 JournalFile
*new_file
= NULL
;
856 assert_return(j
, -EINVAL
);
857 assert_return(!journal_pid_changed(j
), -ECHILD
);
859 r
= iterated_cache_get(j
->files_cache
, NULL
, &files
, &n_files
);
863 for (unsigned i
= 0; i
< n_files
; i
++) {
864 JournalFile
*f
= (JournalFile
*)files
[i
];
867 r
= next_beyond_location(j
, f
, direction
);
869 log_debug_errno(r
, "Can't iterate through %s, ignoring: %m", f
->path
);
870 remove_file_real(j
, f
);
873 f
->location_type
= LOCATION_TAIL
;
882 k
= compare_locations(f
, new_file
);
884 found
= direction
== DIRECTION_DOWN
? k
< 0 : k
> 0;
894 r
= journal_file_move_to_object(new_file
, OBJECT_ENTRY
, new_file
->current_offset
, &o
);
898 set_location(j
, new_file
, o
);
903 _public_
int sd_journal_next(sd_journal
*j
) {
904 return real_journal_next(j
, DIRECTION_DOWN
);
907 _public_
int sd_journal_previous(sd_journal
*j
) {
908 return real_journal_next(j
, DIRECTION_UP
);
911 static int real_journal_next_skip(sd_journal
*j
, direction_t direction
, uint64_t skip
) {
914 assert_return(j
, -EINVAL
);
915 assert_return(!journal_pid_changed(j
), -ECHILD
);
916 assert_return(skip
<= INT_MAX
, -ERANGE
);
919 /* If this is not a discrete skip, then at least
920 * resolve the current location */
921 if (j
->current_location
.type
!= LOCATION_DISCRETE
) {
922 r
= real_journal_next(j
, direction
);
931 r
= real_journal_next(j
, direction
);
945 _public_
int sd_journal_next_skip(sd_journal
*j
, uint64_t skip
) {
946 return real_journal_next_skip(j
, DIRECTION_DOWN
, skip
);
949 _public_
int sd_journal_previous_skip(sd_journal
*j
, uint64_t skip
) {
950 return real_journal_next_skip(j
, DIRECTION_UP
, skip
);
953 _public_
int sd_journal_get_cursor(sd_journal
*j
, char **cursor
) {
957 assert_return(j
, -EINVAL
);
958 assert_return(!journal_pid_changed(j
), -ECHILD
);
959 assert_return(cursor
, -EINVAL
);
961 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
962 return -EADDRNOTAVAIL
;
964 r
= journal_file_move_to_object(j
->current_file
, OBJECT_ENTRY
, j
->current_file
->current_offset
, &o
);
969 "s=%s;i=%"PRIx64
";b=%s;m=%"PRIx64
";t=%"PRIx64
";x=%"PRIx64
,
970 SD_ID128_TO_STRING(j
->current_file
->header
->seqnum_id
), le64toh(o
->entry
.seqnum
),
971 SD_ID128_TO_STRING(o
->entry
.boot_id
), le64toh(o
->entry
.monotonic
),
972 le64toh(o
->entry
.realtime
),
973 le64toh(o
->entry
.xor_hash
)) < 0)
979 _public_
int sd_journal_seek_cursor(sd_journal
*j
, const char *cursor
) {
980 unsigned long long seqnum
, monotonic
, realtime
, xor_hash
;
981 bool seqnum_id_set
= false,
984 monotonic_set
= false,
985 realtime_set
= false,
986 xor_hash_set
= false;
987 sd_id128_t seqnum_id
, boot_id
;
990 assert_return(j
, -EINVAL
);
991 assert_return(!journal_pid_changed(j
), -ECHILD
);
992 assert_return(!isempty(cursor
), -EINVAL
);
994 for (const char *p
= cursor
;;) {
995 _cleanup_free_
char *word
= NULL
;
997 r
= extract_first_word(&p
, &word
, ";", EXTRACT_DONT_COALESCE_SEPARATORS
);
1003 if (word
[0] == '\0' || word
[1] != '=')
1008 seqnum_id_set
= true;
1009 r
= sd_id128_from_string(word
+ 2, &seqnum_id
);
1016 if (sscanf(word
+ 2, "%llx", &seqnum
) != 1)
1022 r
= sd_id128_from_string(word
+ 2, &boot_id
);
1028 monotonic_set
= true;
1029 if (sscanf(word
+ 2, "%llx", &monotonic
) != 1)
1034 realtime_set
= true;
1035 if (sscanf(word
+ 2, "%llx", &realtime
) != 1)
1040 xor_hash_set
= true;
1041 if (sscanf(word
+ 2, "%llx", &xor_hash
) != 1)
1047 if ((!seqnum_set
|| !seqnum_id_set
) &&
1048 (!monotonic_set
|| !boot_id_set
) &&
1053 j
->current_location
= (Location
) {
1054 .type
= LOCATION_SEEK
,
1058 j
->current_location
.realtime
= (uint64_t) realtime
;
1059 j
->current_location
.realtime_set
= true;
1062 if (seqnum_set
&& seqnum_id_set
) {
1063 j
->current_location
.seqnum
= (uint64_t) seqnum
;
1064 j
->current_location
.seqnum_id
= seqnum_id
;
1065 j
->current_location
.seqnum_set
= true;
1068 if (monotonic_set
&& boot_id_set
) {
1069 j
->current_location
.monotonic
= (uint64_t) monotonic
;
1070 j
->current_location
.boot_id
= boot_id
;
1071 j
->current_location
.monotonic_set
= true;
1075 j
->current_location
.xor_hash
= (uint64_t) xor_hash
;
1076 j
->current_location
.xor_hash_set
= true;
1082 _public_
int sd_journal_test_cursor(sd_journal
*j
, const char *cursor
) {
1086 assert_return(j
, -EINVAL
);
1087 assert_return(!journal_pid_changed(j
), -ECHILD
);
1088 assert_return(!isempty(cursor
), -EINVAL
);
1090 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
1091 return -EADDRNOTAVAIL
;
1093 r
= journal_file_move_to_object(j
->current_file
, OBJECT_ENTRY
, j
->current_file
->current_offset
, &o
);
1098 _cleanup_free_
char *item
= NULL
;
1099 unsigned long long ll
;
1103 r
= extract_first_word(&cursor
, &item
, ";", EXTRACT_DONT_COALESCE_SEPARATORS
);
1110 if (strlen(item
) < 2 || item
[1] != '=')
1116 k
= sd_id128_from_string(item
+2, &id
);
1119 if (!sd_id128_equal(id
, j
->current_file
->header
->seqnum_id
))
1124 if (sscanf(item
+2, "%llx", &ll
) != 1)
1126 if (ll
!= le64toh(o
->entry
.seqnum
))
1131 k
= sd_id128_from_string(item
+2, &id
);
1134 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
1139 if (sscanf(item
+2, "%llx", &ll
) != 1)
1141 if (ll
!= le64toh(o
->entry
.monotonic
))
1146 if (sscanf(item
+2, "%llx", &ll
) != 1)
1148 if (ll
!= le64toh(o
->entry
.realtime
))
1153 if (sscanf(item
+2, "%llx", &ll
) != 1)
1155 if (ll
!= le64toh(o
->entry
.xor_hash
))
1164 _public_
int sd_journal_seek_monotonic_usec(sd_journal
*j
, sd_id128_t boot_id
, uint64_t usec
) {
1165 assert_return(j
, -EINVAL
);
1166 assert_return(!journal_pid_changed(j
), -ECHILD
);
1170 j
->current_location
= (Location
) {
1171 .type
= LOCATION_SEEK
,
1174 .monotonic_set
= true,
1180 _public_
int sd_journal_seek_realtime_usec(sd_journal
*j
, uint64_t usec
) {
1181 assert_return(j
, -EINVAL
);
1182 assert_return(!journal_pid_changed(j
), -ECHILD
);
1186 j
->current_location
= (Location
) {
1187 .type
= LOCATION_SEEK
,
1189 .realtime_set
= true,
1195 _public_
int sd_journal_seek_head(sd_journal
*j
) {
1196 assert_return(j
, -EINVAL
);
1197 assert_return(!journal_pid_changed(j
), -ECHILD
);
1201 j
->current_location
= (Location
) {
1202 .type
= LOCATION_HEAD
,
1208 _public_
int sd_journal_seek_tail(sd_journal
*j
) {
1209 assert_return(j
, -EINVAL
);
1210 assert_return(!journal_pid_changed(j
), -ECHILD
);
1214 j
->current_location
= (Location
) {
1215 .type
= LOCATION_TAIL
,
1221 static void check_network(sd_journal
*j
, int fd
) {
1227 j
->on_network
= fd_is_network_fs(fd
);
1230 static bool file_has_type_prefix(const char *prefix
, const char *filename
) {
1231 const char *full
, *tilded
, *atted
;
1233 full
= strjoina(prefix
, ".journal");
1234 tilded
= strjoina(full
, "~");
1235 atted
= strjoina(prefix
, "@");
1237 return STR_IN_SET(filename
, full
, tilded
) ||
1238 startswith(filename
, atted
);
1241 static bool file_type_wanted(int flags
, const char *filename
) {
1244 if (!endswith(filename
, ".journal") && !endswith(filename
, ".journal~"))
1247 /* no flags set → every type is OK */
1248 if (!(flags
& (SD_JOURNAL_SYSTEM
| SD_JOURNAL_CURRENT_USER
)))
1251 if (flags
& SD_JOURNAL_SYSTEM
&& file_has_type_prefix("system", filename
))
1254 if (flags
& SD_JOURNAL_CURRENT_USER
) {
1255 char prefix
[5 + DECIMAL_STR_MAX(uid_t
) + 1];
1257 xsprintf(prefix
, "user-"UID_FMT
, getuid());
1259 if (file_has_type_prefix(prefix
, filename
))
1266 static bool path_has_prefix(sd_journal
*j
, const char *path
, const char *prefix
) {
1271 if (j
->toplevel_fd
>= 0)
1274 return path_startswith(path
, prefix
);
1277 static void track_file_disposition(sd_journal
*j
, JournalFile
*f
) {
1281 if (!j
->has_runtime_files
&& path_has_prefix(j
, f
->path
, "/run"))
1282 j
->has_runtime_files
= true;
1283 else if (!j
->has_persistent_files
&& path_has_prefix(j
, f
->path
, "/var"))
1284 j
->has_persistent_files
= true;
1287 static const char *skip_slash(const char *p
) {
1298 static int add_any_file(
1303 _cleanup_close_
int our_fd
= -EBADF
;
1309 assert(fd
>= 0 || path
);
1312 assert(path
); /* For gcc. */
1313 if (j
->toplevel_fd
>= 0)
1314 /* If there's a top-level fd defined make the path relative, explicitly, since otherwise
1315 * openat() ignores the first argument. */
1317 fd
= our_fd
= openat(j
->toplevel_fd
, skip_slash(path
), O_RDONLY
|O_CLOEXEC
|O_NONBLOCK
);
1319 fd
= our_fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NONBLOCK
);
1321 r
= log_debug_errno(errno
, "Failed to open journal file %s: %m", path
);
1325 r
= fd_nonblock(fd
, false);
1327 r
= log_debug_errno(errno
, "Failed to turn off O_NONBLOCK for %s: %m", path
);
1332 if (fstat(fd
, &st
) < 0) {
1333 r
= log_debug_errno(errno
, "Failed to fstat %s: %m", path
?: "fd");
1337 r
= stat_verify_regular(&st
);
1339 log_debug_errno(r
, "Refusing to open %s: %m", path
?: "fd");
1344 f
= ordered_hashmap_get(j
->files
, path
);
1346 if (stat_inode_same(&f
->last_stat
, &st
)) {
1347 /* We already track this file, under the same path and with the same
1348 * device/inode numbers, it's hence really the same. Mark this file as seen
1349 * in this generation. This is used to GC old files in process_q_overflow()
1350 * to detect journal files that are still there and discern them from those
1351 * which are gone. */
1353 f
->last_seen_generation
= j
->generation
;
1357 /* So we tracked a file under this name, but it has a different inode/device. In that
1358 * case, it got replaced (probably due to rotation?), let's drop it hence from our
1360 remove_file_real(j
, f
);
1365 if (ordered_hashmap_size(j
->files
) >= JOURNAL_FILES_MAX
) {
1366 r
= log_debug_errno(SYNTHETIC_ERRNO(ETOOMANYREFS
),
1367 "Too many open journal files, not adding %s.", path
?: "fd");
1371 r
= journal_file_open(fd
, path
, O_RDONLY
, 0, 0, 0, NULL
, j
->mmap
, NULL
, &f
);
1373 log_debug_errno(r
, "Failed to open journal file %s: %m", path
?: "from fd");
1377 /* journal_file_dump(f); */
1379 /* journal_file_open() generates an replacement fname if necessary, so we can use f->path. */
1380 r
= ordered_hashmap_put(j
->files
, f
->path
, f
);
1382 f
->close_fd
= false; /* Make sure journal_file_close() doesn't close the caller's fd
1383 * (or our own). The caller or we will do that ourselves. */
1384 (void) journal_file_close(f
);
1388 TAKE_FD(our_fd
); /* the fd is now owned by the JournalFile object */
1390 f
->last_seen_generation
= j
->generation
;
1392 track_file_disposition(j
, f
);
1393 check_network(j
, f
->fd
);
1395 j
->current_invalidate_counter
++;
1397 log_debug("File %s added.", f
->path
);
1402 (void) journal_put_error(j
, r
, path
); /* path==NULL is OK. */
1406 static int add_file_by_name(
1409 const char *filename
) {
1411 _cleanup_free_
char *path
= NULL
;
1417 if (j
->no_new_files
)
1420 if (!file_type_wanted(j
->flags
, filename
))
1423 path
= path_join(prefix
, filename
);
1427 return add_any_file(j
, -1, path
);
1430 static int remove_file_by_name(
1433 const char *filename
) {
1435 _cleanup_free_
char *path
= NULL
;
1442 path
= path_join(prefix
, filename
);
1446 f
= ordered_hashmap_get(j
->files
, path
);
1450 remove_file_real(j
, f
);
1454 static void remove_file_real(sd_journal
*j
, JournalFile
*f
) {
1458 (void) ordered_hashmap_remove(j
->files
, f
->path
);
1460 log_debug("File %s removed.", f
->path
);
1462 if (j
->current_file
== f
) {
1463 j
->current_file
= NULL
;
1464 j
->current_field
= 0;
1467 if (j
->unique_file
== f
) {
1468 /* Jump to the next unique_file or NULL if that one was last */
1469 j
->unique_file
= ordered_hashmap_next(j
->files
, j
->unique_file
->path
);
1470 j
->unique_offset
= 0;
1471 if (!j
->unique_file
)
1472 j
->unique_file_lost
= true;
1475 if (j
->fields_file
== f
) {
1476 j
->fields_file
= ordered_hashmap_next(j
->files
, j
->fields_file
->path
);
1477 j
->fields_offset
= 0;
1478 if (!j
->fields_file
)
1479 j
->fields_file_lost
= true;
1482 (void) journal_file_close(f
);
1484 j
->current_invalidate_counter
++;
1487 static int dirname_is_machine_id(const char *fn
) {
1488 sd_id128_t id
, machine
;
1492 /* Returns true if the specified directory name matches the local machine ID */
1494 r
= sd_id128_get_machine(&machine
);
1498 e
= strchr(fn
, '.');
1502 /* Looks like it has a namespace suffix. Verify that. */
1503 if (!log_namespace_name_valid(e
+ 1))
1506 k
= strndupa_safe(fn
, e
- fn
);
1507 r
= sd_id128_from_string(k
, &id
);
1509 r
= sd_id128_from_string(fn
, &id
);
1513 return sd_id128_equal(id
, machine
);
1516 static int dirname_has_namespace(const char *fn
, const char *namespace) {
1519 /* Returns true if the specified directory name matches the specified namespace */
1521 e
= strchr(fn
, '.');
1528 if (!streq(e
+ 1, namespace))
1531 k
= strndupa_safe(fn
, e
- fn
);
1532 return id128_is_valid(k
);
1538 return id128_is_valid(fn
);
1541 static bool dirent_is_journal_file(const struct dirent
*de
) {
1544 /* Returns true if the specified directory entry looks like a journal file we might be interested in */
1546 if (!IN_SET(de
->d_type
, DT_REG
, DT_LNK
, DT_UNKNOWN
))
1549 return endswith(de
->d_name
, ".journal") ||
1550 endswith(de
->d_name
, ".journal~");
1553 static bool dirent_is_journal_subdir(const struct dirent
*de
) {
1557 /* returns true if the specified directory entry looks like a directory that might contain journal
1558 * files we might be interested in, i.e. is either a 128bit ID or a 128bit ID suffixed by a
1561 if (!IN_SET(de
->d_type
, DT_DIR
, DT_LNK
, DT_UNKNOWN
))
1564 e
= strchr(de
->d_name
, '.');
1566 return id128_is_valid(de
->d_name
); /* No namespace */
1568 n
= strndupa_safe(de
->d_name
, e
- de
->d_name
);
1569 if (!id128_is_valid(n
))
1572 return log_namespace_name_valid(e
+ 1);
1575 static int directory_open(sd_journal
*j
, const char *path
, DIR **ret
) {
1582 if (j
->toplevel_fd
< 0)
1585 /* Open the specified directory relative to the toplevel fd. Enforce that the path specified is
1586 * relative, by dropping the initial slash */
1587 d
= xopendirat(j
->toplevel_fd
, skip_slash(path
), 0);
1595 static int add_directory(sd_journal
*j
, const char *prefix
, const char *dirname
);
1597 static void directory_enumerate(sd_journal
*j
, Directory
*m
, DIR *d
) {
1602 FOREACH_DIRENT_ALL(de
, d
, goto fail
) {
1603 if (dirent_is_journal_file(de
))
1604 (void) add_file_by_name(j
, m
->path
, de
->d_name
);
1606 if (m
->is_root
&& dirent_is_journal_subdir(de
))
1607 (void) add_directory(j
, m
->path
, de
->d_name
);
1612 log_debug_errno(errno
, "Failed to enumerate directory %s, ignoring: %m", m
->path
);
1615 static void directory_watch(sd_journal
*j
, Directory
*m
, int fd
, uint32_t mask
) {
1622 /* Watch this directory if that's enabled and if it not being watched yet. */
1624 if (m
->wd
> 0) /* Already have a watch? */
1626 if (j
->inotify_fd
< 0) /* Not watching at all? */
1629 m
->wd
= inotify_add_watch_fd(j
->inotify_fd
, fd
, mask
);
1631 log_debug_errno(errno
, "Failed to watch journal directory '%s', ignoring: %m", m
->path
);
1635 r
= hashmap_put(j
->directories_by_wd
, INT_TO_PTR(m
->wd
), m
);
1637 log_debug_errno(r
, "Directory '%s' already being watched under a different path, ignoring: %m", m
->path
);
1639 log_debug_errno(r
, "Failed to add watch for journal directory '%s' to hashmap, ignoring: %m", m
->path
);
1640 (void) inotify_rm_watch(j
->inotify_fd
, m
->wd
);
1645 static int add_directory(
1648 const char *dirname
) {
1650 _cleanup_free_
char *path
= NULL
;
1651 _cleanup_closedir_
DIR *d
= NULL
;
1658 /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch
1659 * and reenumerates directory contents */
1661 path
= path_join(prefix
, dirname
);
1667 log_debug("Considering directory '%s'.", path
);
1669 /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */
1670 if ((j
->flags
& SD_JOURNAL_LOCAL_ONLY
) &&
1671 !((dirname
&& dirname_is_machine_id(dirname
) > 0) || path_has_prefix(j
, path
, "/run")))
1675 (!(FLAGS_SET(j
->flags
, SD_JOURNAL_ALL_NAMESPACES
) ||
1676 dirname_has_namespace(dirname
, j
->namespace) > 0 ||
1677 (FLAGS_SET(j
->flags
, SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE
) && dirname_has_namespace(dirname
, NULL
) > 0))))
1680 r
= directory_open(j
, path
, &d
);
1682 log_debug_errno(r
, "Failed to open directory '%s': %m", path
);
1686 m
= hashmap_get(j
->directories_by_path
, path
);
1688 m
= new(Directory
, 1);
1699 if (hashmap_put(j
->directories_by_path
, m
->path
, m
) < 0) {
1705 path
= NULL
; /* avoid freeing in cleanup */
1706 j
->current_invalidate_counter
++;
1708 log_debug("Directory %s added.", m
->path
);
1710 } else if (m
->is_root
)
1711 return 0; /* Don't 'downgrade' from root directory */
1713 m
->last_seen_generation
= j
->generation
;
1715 directory_watch(j
, m
, dirfd(d
),
1716 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1717 IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
|IN_MOVED_FROM
|
1720 if (!j
->no_new_files
)
1721 directory_enumerate(j
, m
, d
);
1723 check_network(j
, dirfd(d
));
1728 k
= journal_put_error(j
, r
, path
?: prefix
);
1735 static int add_root_directory(sd_journal
*j
, const char *p
, bool missing_ok
) {
1737 _cleanup_closedir_
DIR *d
= NULL
;
1743 /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we
1744 * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially
1745 * populate the set, as well as to update it later. */
1748 /* If there's a path specified, use it. */
1750 log_debug("Considering root directory '%s'.", p
);
1752 if ((j
->flags
& SD_JOURNAL_RUNTIME_ONLY
) &&
1753 !path_has_prefix(j
, p
, "/run"))
1757 p
= strjoina(j
->prefix
, p
);
1759 r
= directory_open(j
, p
, &d
);
1760 if (r
== -ENOENT
&& missing_ok
)
1763 log_debug_errno(r
, "Failed to open root directory %s: %m", p
);
1767 _cleanup_close_
int dfd
= -EBADF
;
1769 /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since
1770 * opendir() will take possession of the fd, and close it, which we don't want. */
1772 p
= "."; /* store this as "." in the directories hashmap */
1774 dfd
= fcntl(j
->toplevel_fd
, F_DUPFD_CLOEXEC
, 3);
1780 d
= take_fdopendir(&dfd
);
1789 m
= hashmap_get(j
->directories_by_path
, p
);
1791 m
= new0(Directory
, 1);
1799 m
->path
= strdup(p
);
1806 if (hashmap_put(j
->directories_by_path
, m
->path
, m
) < 0) {
1813 j
->current_invalidate_counter
++;
1815 log_debug("Root directory %s added.", m
->path
);
1817 } else if (!m
->is_root
)
1820 directory_watch(j
, m
, dirfd(d
),
1821 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1824 if (!j
->no_new_files
)
1825 directory_enumerate(j
, m
, d
);
1827 check_network(j
, dirfd(d
));
1832 k
= journal_put_error(j
, r
, p
);
1839 static void remove_directory(sd_journal
*j
, Directory
*d
) {
1843 hashmap_remove(j
->directories_by_wd
, INT_TO_PTR(d
->wd
));
1845 if (j
->inotify_fd
>= 0)
1846 (void) inotify_rm_watch(j
->inotify_fd
, d
->wd
);
1849 hashmap_remove(j
->directories_by_path
, d
->path
);
1852 log_debug("Root directory %s removed.", d
->path
);
1854 log_debug("Directory %s removed.", d
->path
);
1860 static int add_search_paths(sd_journal
*j
) {
1862 static const char search_paths
[] =
1863 "/run/log/journal\0"
1864 "/var/log/journal\0";
1868 /* We ignore most errors here, since the idea is to only open
1869 * what's actually accessible, and ignore the rest. */
1871 NULSTR_FOREACH(p
, search_paths
)
1872 (void) add_root_directory(j
, p
, true);
1874 if (!(j
->flags
& SD_JOURNAL_LOCAL_ONLY
))
1875 (void) add_root_directory(j
, "/var/log/journal/remote", true);
1880 static int add_current_paths(sd_journal
*j
) {
1884 assert(j
->no_new_files
);
1886 /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we
1887 * treat them as fatal. */
1889 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
1890 _cleanup_free_
char *dir
= NULL
;
1893 r
= path_extract_directory(f
->path
, &dir
);
1897 r
= add_directory(j
, dir
, NULL
);
1905 static int allocate_inotify(sd_journal
*j
) {
1908 if (j
->inotify_fd
< 0) {
1909 j
->inotify_fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
1910 if (j
->inotify_fd
< 0)
1914 return hashmap_ensure_allocated(&j
->directories_by_wd
, NULL
);
1917 static sd_journal
*journal_new(int flags
, const char *path
, const char *namespace) {
1918 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
1920 j
= new(sd_journal
, 1);
1925 .original_pid
= getpid_cached(),
1926 .toplevel_fd
= -EBADF
,
1927 .inotify_fd
= -EBADF
,
1929 .data_threshold
= DEFAULT_DATA_THRESHOLD
,
1939 if (flags
& SD_JOURNAL_OS_ROOT
)
1946 j
->namespace = strdup(namespace);
1951 j
->files
= ordered_hashmap_new(&path_hash_ops
);
1955 j
->files_cache
= ordered_hashmap_iterated_cache_new(j
->files
);
1956 j
->directories_by_path
= hashmap_new(&path_hash_ops
);
1957 j
->mmap
= mmap_cache_new();
1958 if (!j
->files_cache
|| !j
->directories_by_path
|| !j
->mmap
)
1964 #define OPEN_ALLOWED_FLAGS \
1965 (SD_JOURNAL_LOCAL_ONLY | \
1966 SD_JOURNAL_RUNTIME_ONLY | \
1967 SD_JOURNAL_SYSTEM | \
1968 SD_JOURNAL_CURRENT_USER | \
1969 SD_JOURNAL_ALL_NAMESPACES | \
1970 SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE)
1972 _public_
int sd_journal_open_namespace(sd_journal
**ret
, const char *namespace, int flags
) {
1973 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
1976 assert_return(ret
, -EINVAL
);
1977 assert_return((flags
& ~OPEN_ALLOWED_FLAGS
) == 0, -EINVAL
);
1979 j
= journal_new(flags
, NULL
, namespace);
1983 r
= add_search_paths(j
);
1991 _public_
int sd_journal_open(sd_journal
**ret
, int flags
) {
1992 return sd_journal_open_namespace(ret
, NULL
, flags
);
1995 #define OPEN_CONTAINER_ALLOWED_FLAGS \
1996 (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM)
1998 _public_
int sd_journal_open_container(sd_journal
**ret
, const char *machine
, int flags
) {
1999 _cleanup_free_
char *root
= NULL
, *class = NULL
;
2000 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
2004 /* This is deprecated, people should use machined's OpenMachineRootDirectory() call instead in
2005 * combination with sd_journal_open_directory_fd(). */
2007 assert_return(machine
, -EINVAL
);
2008 assert_return(ret
, -EINVAL
);
2009 assert_return((flags
& ~OPEN_CONTAINER_ALLOWED_FLAGS
) == 0, -EINVAL
);
2010 assert_return(hostname_is_valid(machine
, 0), -EINVAL
);
2012 p
= strjoina("/run/systemd/machines/", machine
);
2013 r
= parse_env_file(NULL
, p
,
2023 if (!streq_ptr(class, "container"))
2026 j
= journal_new(flags
, root
, NULL
);
2030 r
= add_search_paths(j
);
2038 #define OPEN_DIRECTORY_ALLOWED_FLAGS \
2039 (SD_JOURNAL_OS_ROOT | \
2040 SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
2042 _public_
int sd_journal_open_directory(sd_journal
**ret
, const char *path
, int flags
) {
2043 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
2046 assert_return(ret
, -EINVAL
);
2047 assert_return(path
, -EINVAL
);
2048 assert_return((flags
& ~OPEN_DIRECTORY_ALLOWED_FLAGS
) == 0, -EINVAL
);
2050 j
= journal_new(flags
, path
, NULL
);
2054 if (flags
& SD_JOURNAL_OS_ROOT
)
2055 r
= add_search_paths(j
);
2057 r
= add_root_directory(j
, path
, false);
2065 _public_
int sd_journal_open_files(sd_journal
**ret
, const char **paths
, int flags
) {
2066 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
2069 assert_return(ret
, -EINVAL
);
2070 assert_return(flags
== 0, -EINVAL
);
2072 j
= journal_new(flags
, NULL
, NULL
);
2076 STRV_FOREACH(path
, paths
) {
2077 r
= add_any_file(j
, -1, *path
);
2082 j
->no_new_files
= true;
2088 #define OPEN_DIRECTORY_FD_ALLOWED_FLAGS \
2089 (SD_JOURNAL_OS_ROOT | \
2090 SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
2092 _public_
int sd_journal_open_directory_fd(sd_journal
**ret
, int fd
, int flags
) {
2093 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
2097 assert_return(ret
, -EINVAL
);
2098 assert_return(fd
>= 0, -EBADF
);
2099 assert_return((flags
& ~OPEN_DIRECTORY_FD_ALLOWED_FLAGS
) == 0, -EINVAL
);
2101 if (fstat(fd
, &st
) < 0)
2104 if (!S_ISDIR(st
.st_mode
))
2107 j
= journal_new(flags
, NULL
, NULL
);
2111 j
->toplevel_fd
= fd
;
2113 if (flags
& SD_JOURNAL_OS_ROOT
)
2114 r
= add_search_paths(j
);
2116 r
= add_root_directory(j
, NULL
, false);
2124 _public_
int sd_journal_open_files_fd(sd_journal
**ret
, int fds
[], unsigned n_fds
, int flags
) {
2126 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
2129 assert_return(ret
, -EINVAL
);
2130 assert_return(n_fds
> 0, -EBADF
);
2131 assert_return(flags
== 0, -EINVAL
);
2133 j
= journal_new(flags
, NULL
, NULL
);
2137 for (unsigned i
= 0; i
< n_fds
; i
++) {
2145 if (fstat(fds
[i
], &st
) < 0) {
2150 r
= stat_verify_regular(&st
);
2154 r
= add_any_file(j
, fds
[i
], NULL
);
2159 j
->no_new_files
= true;
2160 j
->no_inotify
= true;
2166 /* If we fail, make sure we don't take possession of the files we managed to make use of successfully, and they
2168 ORDERED_HASHMAP_FOREACH(f
, j
->files
)
2169 f
->close_fd
= false;
2174 _public_
void sd_journal_close(sd_journal
*j
) {
2180 sd_journal_flush_matches(j
);
2182 ordered_hashmap_free_with_destructor(j
->files
, journal_file_close
);
2183 iterated_cache_free(j
->files_cache
);
2185 while ((d
= hashmap_first(j
->directories_by_path
)))
2186 remove_directory(j
, d
);
2188 while ((d
= hashmap_first(j
->directories_by_wd
)))
2189 remove_directory(j
, d
);
2191 hashmap_free(j
->directories_by_path
);
2192 hashmap_free(j
->directories_by_wd
);
2194 safe_close(j
->inotify_fd
);
2197 mmap_cache_stats_log_debug(j
->mmap
);
2198 mmap_cache_unref(j
->mmap
);
2201 hashmap_free_free(j
->errors
);
2206 free(j
->unique_field
);
2207 free(j
->fields_buffer
);
2211 _public_
int sd_journal_get_realtime_usec(sd_journal
*j
, uint64_t *ret
) {
2216 assert_return(j
, -EINVAL
);
2217 assert_return(!journal_pid_changed(j
), -ECHILD
);
2219 f
= j
->current_file
;
2221 return -EADDRNOTAVAIL
;
2222 if (f
->current_offset
<= 0)
2223 return -EADDRNOTAVAIL
;
2225 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2229 uint64_t t
= le64toh(o
->entry
.realtime
);
2230 if (!VALID_REALTIME(t
))
2239 _public_
int sd_journal_get_monotonic_usec(sd_journal
*j
, uint64_t *ret
, sd_id128_t
*ret_boot_id
) {
2244 assert_return(j
, -EINVAL
);
2245 assert_return(!journal_pid_changed(j
), -ECHILD
);
2247 f
= j
->current_file
;
2249 return -EADDRNOTAVAIL
;
2250 if (f
->current_offset
<= 0)
2251 return -EADDRNOTAVAIL
;
2253 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2258 *ret_boot_id
= o
->entry
.boot_id
;
2262 r
= sd_id128_get_boot(&id
);
2266 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
2270 uint64_t t
= le64toh(o
->entry
.monotonic
);
2271 if (!VALID_MONOTONIC(t
))
2280 _public_
int sd_journal_get_seqnum(
2282 uint64_t *ret_seqnum
,
2283 sd_id128_t
*ret_seqnum_id
) {
2289 assert_return(j
, -EINVAL
);
2290 assert_return(!journal_pid_changed(j
), -ECHILD
);
2292 f
= j
->current_file
;
2294 return -EADDRNOTAVAIL
;
2296 if (f
->current_offset
<= 0)
2297 return -EADDRNOTAVAIL
;
2299 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2304 *ret_seqnum_id
= f
->header
->seqnum_id
;
2306 *ret_seqnum
= le64toh(o
->entry
.seqnum
);
2311 static bool field_is_valid(const char *field
) {
2317 if (startswith(field
, "__"))
2320 for (const char *p
= field
; *p
; p
++) {
2325 if (*p
>= 'A' && *p
<= 'Z')
2328 if (ascii_isdigit(*p
))
2337 _public_
int sd_journal_get_data(sd_journal
*j
, const char *field
, const void **data
, size_t *size
) {
2339 size_t field_length
;
2343 assert_return(j
, -EINVAL
);
2344 assert_return(!journal_pid_changed(j
), -ECHILD
);
2345 assert_return(field
, -EINVAL
);
2346 assert_return(data
, -EINVAL
);
2347 assert_return(size
, -EINVAL
);
2348 assert_return(field_is_valid(field
), -EINVAL
);
2350 f
= j
->current_file
;
2352 return -EADDRNOTAVAIL
;
2354 if (f
->current_offset
<= 0)
2355 return -EADDRNOTAVAIL
;
2357 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2361 field_length
= strlen(field
);
2363 uint64_t n
= journal_file_entry_n_items(f
, o
);
2364 for (uint64_t i
= 0; i
< n
; i
++) {
2369 p
= journal_file_entry_item_object_offset(f
, o
, i
);
2370 r
= journal_file_data_payload(f
, NULL
, p
, field
, field_length
, j
->data_threshold
, &d
, &l
);
2373 if (IN_SET(r
, -EADDRNOTAVAIL
, -EBADMSG
)) {
2374 log_debug_errno(r
, "Entry item %"PRIu64
" data object is bad, skipping over it: %m", i
);
2386 /* journal_file_data_payload() may clear or overwrite cached object. */
2387 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2395 _public_
int sd_journal_enumerate_data(sd_journal
*j
, const void **data
, size_t *size
) {
2400 assert_return(j
, -EINVAL
);
2401 assert_return(!journal_pid_changed(j
), -ECHILD
);
2402 assert_return(data
, -EINVAL
);
2403 assert_return(size
, -EINVAL
);
2405 f
= j
->current_file
;
2407 return -EADDRNOTAVAIL
;
2409 if (f
->current_offset
<= 0)
2410 return -EADDRNOTAVAIL
;
2412 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2416 for (uint64_t n
= journal_file_entry_n_items(f
, o
); j
->current_field
< n
; j
->current_field
++) {
2421 p
= journal_file_entry_item_object_offset(f
, o
, j
->current_field
);
2422 r
= journal_file_data_payload(f
, NULL
, p
, NULL
, 0, j
->data_threshold
, &d
, &l
);
2423 if (IN_SET(r
, -EADDRNOTAVAIL
, -EBADMSG
)) {
2424 log_debug_errno(r
, "Entry item %"PRIu64
" data object is bad, skipping over it: %m", j
->current_field
);
2439 /* journal_file_data_payload() may clear or overwrite cached object. */
2440 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2448 _public_
int sd_journal_enumerate_available_data(sd_journal
*j
, const void **data
, size_t *size
) {
2452 r
= sd_journal_enumerate_data(j
, data
, size
);
2455 if (!JOURNAL_ERRNO_IS_UNAVAILABLE_FIELD(r
))
2457 j
->current_field
++; /* Try with the next field */
2461 _public_
void sd_journal_restart_data(sd_journal
*j
) {
2465 j
->current_field
= 0;
2468 static int reiterate_all_paths(sd_journal
*j
) {
2471 if (j
->no_new_files
)
2472 return add_current_paths(j
);
2474 if (j
->flags
& SD_JOURNAL_OS_ROOT
)
2475 return add_search_paths(j
);
2477 if (j
->toplevel_fd
>= 0)
2478 return add_root_directory(j
, NULL
, false);
2481 return add_root_directory(j
, j
->path
, true);
2483 return add_search_paths(j
);
2486 _public_
int sd_journal_get_fd(sd_journal
*j
) {
2489 assert_return(j
, -EINVAL
);
2490 assert_return(!journal_pid_changed(j
), -ECHILD
);
2493 return -EMEDIUMTYPE
;
2495 if (j
->inotify_fd
>= 0)
2496 return j
->inotify_fd
;
2498 r
= allocate_inotify(j
);
2502 log_debug("Reiterating files to get inotify watches established.");
2504 /* Iterate through all dirs again, to add them to the inotify */
2505 r
= reiterate_all_paths(j
);
2509 return j
->inotify_fd
;
2512 _public_
int sd_journal_get_events(sd_journal
*j
) {
2515 assert_return(j
, -EINVAL
);
2516 assert_return(!journal_pid_changed(j
), -ECHILD
);
2518 fd
= sd_journal_get_fd(j
);
2525 _public_
int sd_journal_get_timeout(sd_journal
*j
, uint64_t *timeout_usec
) {
2528 assert_return(j
, -EINVAL
);
2529 assert_return(!journal_pid_changed(j
), -ECHILD
);
2530 assert_return(timeout_usec
, -EINVAL
);
2532 fd
= sd_journal_get_fd(j
);
2536 if (!j
->on_network
) {
2537 *timeout_usec
= UINT64_MAX
;
2541 /* If we are on the network we need to regularly check for
2542 * changes manually */
2544 *timeout_usec
= j
->last_process_usec
+ JOURNAL_FILES_RECHECK_USEC
;
2548 static void process_q_overflow(sd_journal
*j
) {
2554 /* When the inotify queue overruns we need to enumerate and re-validate all journal files to bring our list
2555 * back in sync with what's on disk. For this we pick a new generation counter value. It'll be assigned to all
2556 * journal files we encounter. All journal files and all directories that don't carry it after reenumeration
2557 * are subject for unloading. */
2559 log_debug("Inotify queue overrun, reiterating everything.");
2562 (void) reiterate_all_paths(j
);
2564 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
2566 if (f
->last_seen_generation
== j
->generation
)
2569 log_debug("File '%s' hasn't been seen in this enumeration, removing.", f
->path
);
2570 remove_file_real(j
, f
);
2573 HASHMAP_FOREACH(m
, j
->directories_by_path
) {
2575 if (m
->last_seen_generation
== j
->generation
)
2578 if (m
->is_root
) /* Never GC root directories */
2581 log_debug("Directory '%s' hasn't been seen in this enumeration, removing.", f
->path
);
2582 remove_directory(j
, m
);
2585 log_debug("Reiteration complete.");
2588 static void process_inotify_event(sd_journal
*j
, const struct inotify_event
*e
) {
2594 if (e
->mask
& IN_Q_OVERFLOW
) {
2595 process_q_overflow(j
);
2599 /* Is this a subdirectory we watch? */
2600 d
= hashmap_get(j
->directories_by_wd
, INT_TO_PTR(e
->wd
));
2602 if (!(e
->mask
& IN_ISDIR
) && e
->len
> 0 &&
2603 (endswith(e
->name
, ".journal") ||
2604 endswith(e
->name
, ".journal~"))) {
2606 /* Event for a journal file */
2608 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
))
2609 (void) add_file_by_name(j
, d
->path
, e
->name
);
2610 else if (e
->mask
& (IN_DELETE
|IN_MOVED_FROM
|IN_UNMOUNT
))
2611 (void) remove_file_by_name(j
, d
->path
, e
->name
);
2613 } else if (!d
->is_root
&& e
->len
== 0) {
2615 /* Event for a subdirectory */
2617 if (e
->mask
& (IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
))
2618 remove_directory(j
, d
);
2620 } else if (d
->is_root
&& (e
->mask
& IN_ISDIR
) && e
->len
> 0 && id128_is_valid(e
->name
)) {
2622 /* Event for root directory */
2624 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
))
2625 (void) add_directory(j
, d
->path
, e
->name
);
2631 if (e
->mask
& IN_IGNORED
)
2634 log_debug("Unexpected inotify event.");
2637 static int determine_change(sd_journal
*j
) {
2642 b
= j
->current_invalidate_counter
!= j
->last_invalidate_counter
;
2643 j
->last_invalidate_counter
= j
->current_invalidate_counter
;
2645 return b
? SD_JOURNAL_INVALIDATE
: SD_JOURNAL_APPEND
;
2648 _public_
int sd_journal_process(sd_journal
*j
) {
2649 bool got_something
= false;
2651 assert_return(j
, -EINVAL
);
2652 assert_return(!journal_pid_changed(j
), -ECHILD
);
2654 if (j
->inotify_fd
< 0) /* We have no inotify fd yet? Then there's noting to process. */
2657 j
->last_process_usec
= now(CLOCK_MONOTONIC
);
2658 j
->last_invalidate_counter
= j
->current_invalidate_counter
;
2661 union inotify_event_buffer buffer
;
2664 l
= read(j
->inotify_fd
, &buffer
, sizeof(buffer
));
2666 if (ERRNO_IS_TRANSIENT(errno
))
2667 return got_something
? determine_change(j
) : SD_JOURNAL_NOP
;
2672 got_something
= true;
2674 FOREACH_INOTIFY_EVENT(e
, buffer
, l
)
2675 process_inotify_event(j
, e
);
2679 _public_
int sd_journal_wait(sd_journal
*j
, uint64_t timeout_usec
) {
2683 assert_return(j
, -EINVAL
);
2684 assert_return(!journal_pid_changed(j
), -ECHILD
);
2686 if (j
->inotify_fd
< 0) {
2689 /* This is the first invocation, hence create the inotify watch */
2690 r
= sd_journal_get_fd(j
);
2694 /* Server might have done some vacuuming while we weren't watching. Get rid of the deleted
2695 * files now so they don't stay around indefinitely. */
2696 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
2697 r
= journal_file_fstat(f
);
2699 remove_file_real(j
, f
);
2701 log_debug_errno(r
, "Failed to fstat() journal file '%s', ignoring: %m", f
->path
);
2704 /* The journal might have changed since the context object was created and we weren't
2705 * watching before, hence don't wait for anything, and return immediately. */
2706 return determine_change(j
);
2709 r
= sd_journal_get_timeout(j
, &t
);
2713 if (t
!= UINT64_MAX
) {
2714 t
= usec_sub_unsigned(t
, now(CLOCK_MONOTONIC
));
2716 if (timeout_usec
== UINT64_MAX
|| timeout_usec
> t
)
2721 r
= fd_wait_for_event(j
->inotify_fd
, POLLIN
, timeout_usec
);
2722 } while (r
== -EINTR
);
2727 return sd_journal_process(j
);
2730 _public_
int sd_journal_get_cutoff_realtime_usec(sd_journal
*j
, uint64_t *from
, uint64_t *to
) {
2733 uint64_t fmin
= 0, tmax
= 0;
2736 assert_return(j
, -EINVAL
);
2737 assert_return(!journal_pid_changed(j
), -ECHILD
);
2738 assert_return(from
|| to
, -EINVAL
);
2739 assert_return(from
!= to
, -EINVAL
);
2741 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
2744 r
= journal_file_get_cutoff_realtime_usec(f
, &fr
, &t
);
2757 fmin
= MIN(fr
, fmin
);
2758 tmax
= MAX(t
, tmax
);
2767 return first
? 0 : 1;
2770 _public_
int sd_journal_get_cutoff_monotonic_usec(
2776 uint64_t from
= UINT64_MAX
, to
= UINT64_MAX
;
2781 assert_return(j
, -EINVAL
);
2782 assert_return(!journal_pid_changed(j
), -ECHILD
);
2783 assert_return(ret_from
!= ret_to
, -EINVAL
);
2785 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
2788 r
= journal_file_get_cutoff_monotonic_usec(f
, boot_id
, &ff
, &tt
);
2797 from
= MIN(ff
, from
);
2814 void journal_print_header(sd_journal
*j
) {
2816 bool newline
= false;
2820 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
2826 journal_file_print_header(f
);
2830 _public_
int sd_journal_get_usage(sd_journal
*j
, uint64_t *ret
) {
2834 assert_return(j
, -EINVAL
);
2835 assert_return(!journal_pid_changed(j
), -ECHILD
);
2836 assert_return(ret
, -EINVAL
);
2838 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
2842 if (fstat(f
->fd
, &st
) < 0)
2845 b
= (uint64_t) st
.st_blocks
;
2846 if (b
> UINT64_MAX
/ 512)
2850 if (sum
> UINT64_MAX
- b
)
2859 _public_
int sd_journal_query_unique(sd_journal
*j
, const char *field
) {
2862 assert_return(j
, -EINVAL
);
2863 assert_return(!journal_pid_changed(j
), -ECHILD
);
2864 assert_return(!isempty(field
), -EINVAL
);
2865 assert_return(field_is_valid(field
), -EINVAL
);
2867 r
= free_and_strdup(&j
->unique_field
, field
);
2871 j
->unique_file
= NULL
;
2872 j
->unique_offset
= 0;
2873 j
->unique_file_lost
= false;
2878 _public_
int sd_journal_enumerate_unique(
2880 const void **ret_data
,
2885 assert_return(j
, -EINVAL
);
2886 assert_return(!journal_pid_changed(j
), -ECHILD
);
2887 assert_return(j
->unique_field
, -EINVAL
);
2889 k
= strlen(j
->unique_field
);
2891 if (!j
->unique_file
) {
2892 if (j
->unique_file_lost
)
2895 j
->unique_file
= ordered_hashmap_first(j
->files
);
2896 if (!j
->unique_file
)
2899 j
->unique_offset
= 0;
2910 /* Proceed to next data object in the field's linked list */
2911 if (j
->unique_offset
== 0) {
2912 r
= journal_file_find_field_object(j
->unique_file
, j
->unique_field
, k
, &o
, NULL
);
2916 j
->unique_offset
= r
> 0 ? le64toh(o
->field
.head_data_offset
) : 0;
2918 r
= journal_file_move_to_object(j
->unique_file
, OBJECT_DATA
, j
->unique_offset
, &o
);
2922 j
->unique_offset
= le64toh(o
->data
.next_field_offset
);
2925 /* We reached the end of the list? Then start again, with the next file */
2926 if (j
->unique_offset
== 0) {
2927 j
->unique_file
= ordered_hashmap_next(j
->files
, j
->unique_file
->path
);
2928 if (!j
->unique_file
)
2934 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2935 * instead, so that we can look at this data object at the same
2936 * time as one on another file */
2937 r
= journal_file_move_to_object(j
->unique_file
, OBJECT_UNUSED
, j
->unique_offset
, &o
);
2941 /* Let's do the type check by hand, since we used 0 context above. */
2942 if (o
->object
.type
!= OBJECT_DATA
)
2943 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
),
2944 "%s:offset " OFSfmt
": object has type %d, expected %d",
2945 j
->unique_file
->path
,
2947 o
->object
.type
, OBJECT_DATA
);
2949 r
= journal_file_data_payload(j
->unique_file
, o
, j
->unique_offset
, NULL
, 0,
2950 j
->data_threshold
, &odata
, &ol
);
2954 /* Check if we have at least the field name and "=". */
2956 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
),
2957 "%s:offset " OFSfmt
": object has size %zu, expected at least %zu",
2958 j
->unique_file
->path
,
2959 j
->unique_offset
, ol
, k
+ 1);
2961 if (memcmp(odata
, j
->unique_field
, k
) != 0 || ((const char*) odata
)[k
] != '=')
2962 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
),
2963 "%s:offset " OFSfmt
": object does not start with \"%s=\"",
2964 j
->unique_file
->path
,
2968 /* OK, now let's see if we already returned this data object by checking if it exists in the
2969 * earlier traversed files. */
2971 ORDERED_HASHMAP_FOREACH(of
, j
->files
) {
2972 if (of
== j
->unique_file
)
2975 /* Skip this file it didn't have any fields indexed */
2976 if (JOURNAL_HEADER_CONTAINS(of
->header
, n_fields
) && le64toh(of
->header
->n_fields
) <= 0)
2979 /* We can reuse the hash from our current file only on old-style journal files
2980 * without keyed hashes. On new-style files we have to calculate the hash anew, to
2981 * take the per-file hash seed into consideration. */
2982 if (!JOURNAL_HEADER_KEYED_HASH(j
->unique_file
->header
) && !JOURNAL_HEADER_KEYED_HASH(of
->header
))
2983 r
= journal_file_find_data_object_with_hash(of
, odata
, ol
, le64toh(o
->data
.hash
), NULL
, NULL
);
2985 r
= journal_file_find_data_object(of
, odata
, ol
, NULL
, NULL
);
3004 _public_
int sd_journal_enumerate_available_unique(sd_journal
*j
, const void **data
, size_t *size
) {
3008 r
= sd_journal_enumerate_unique(j
, data
, size
);
3011 if (!JOURNAL_ERRNO_IS_UNAVAILABLE_FIELD(r
))
3013 /* Try with the next field. sd_journal_enumerate_unique() modifies state, so on the next try
3014 * we will access the next field. */
3018 _public_
void sd_journal_restart_unique(sd_journal
*j
) {
3022 j
->unique_file
= NULL
;
3023 j
->unique_offset
= 0;
3024 j
->unique_file_lost
= false;
3027 _public_
int sd_journal_enumerate_fields(sd_journal
*j
, const char **field
) {
3030 assert_return(j
, -EINVAL
);
3031 assert_return(!journal_pid_changed(j
), -ECHILD
);
3032 assert_return(field
, -EINVAL
);
3034 if (!j
->fields_file
) {
3035 if (j
->fields_file_lost
)
3038 j
->fields_file
= ordered_hashmap_first(j
->files
);
3039 if (!j
->fields_file
)
3042 j
->fields_hash_table_index
= 0;
3043 j
->fields_offset
= 0;
3047 JournalFile
*f
, *of
;
3055 if (j
->fields_offset
== 0) {
3058 /* We are not yet positioned at any field. Let's pick the first one */
3059 r
= journal_file_map_field_hash_table(f
);
3063 m
= le64toh(f
->header
->field_hash_table_size
) / sizeof(HashItem
);
3065 if (j
->fields_hash_table_index
>= m
) {
3066 /* Reached the end of the hash table, go to the next file. */
3071 j
->fields_offset
= le64toh(f
->field_hash_table
[j
->fields_hash_table_index
].head_hash_offset
);
3073 if (j
->fields_offset
!= 0)
3076 /* Empty hash table bucket, go to next one */
3077 j
->fields_hash_table_index
++;
3081 /* Proceed with next file */
3082 j
->fields_file
= ordered_hashmap_next(j
->files
, f
->path
);
3083 if (!j
->fields_file
) {
3088 j
->fields_offset
= 0;
3089 j
->fields_hash_table_index
= 0;
3094 /* We are already positioned at a field. If so, let's figure out the next field from it */
3096 r
= journal_file_move_to_object(f
, OBJECT_FIELD
, j
->fields_offset
, &o
);
3100 j
->fields_offset
= le64toh(o
->field
.next_hash_offset
);
3101 if (j
->fields_offset
== 0) {
3102 /* Reached the end of the hash table chain */
3103 j
->fields_hash_table_index
++;
3108 /* We use OBJECT_UNUSED here, so that the iterator below doesn't remove our mmap window */
3109 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, j
->fields_offset
, &o
);
3113 /* Because we used OBJECT_UNUSED above, we need to do our type check manually */
3114 if (o
->object
.type
!= OBJECT_FIELD
)
3115 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
),
3116 "%s:offset " OFSfmt
": object has type %i, expected %i",
3117 f
->path
, j
->fields_offset
,
3118 o
->object
.type
, OBJECT_FIELD
);
3120 sz
= le64toh(o
->object
.size
) - offsetof(Object
, field
.payload
);
3122 /* Let's see if we already returned this field name before. */
3124 ORDERED_HASHMAP_FOREACH(of
, j
->files
) {
3128 /* Skip this file it didn't have any fields indexed */
3129 if (JOURNAL_HEADER_CONTAINS(of
->header
, n_fields
) && le64toh(of
->header
->n_fields
) <= 0)
3132 if (!JOURNAL_HEADER_KEYED_HASH(f
->header
) && !JOURNAL_HEADER_KEYED_HASH(of
->header
))
3133 r
= journal_file_find_field_object_with_hash(of
, o
->field
.payload
, sz
,
3134 le64toh(o
->field
.hash
), NULL
, NULL
);
3136 r
= journal_file_find_field_object(of
, o
->field
.payload
, sz
, NULL
, NULL
);
3148 /* Check if this is really a valid string containing no NUL byte */
3149 if (memchr(o
->field
.payload
, 0, sz
))
3152 if (j
->data_threshold
> 0 && sz
> j
->data_threshold
)
3153 sz
= j
->data_threshold
;
3155 if (!GREEDY_REALLOC(j
->fields_buffer
, sz
+ 1))
3158 memcpy(j
->fields_buffer
, o
->field
.payload
, sz
);
3159 j
->fields_buffer
[sz
] = 0;
3161 if (!field_is_valid(j
->fields_buffer
))
3164 *field
= j
->fields_buffer
;
3169 _public_
void sd_journal_restart_fields(sd_journal
*j
) {
3173 j
->fields_file
= NULL
;
3174 j
->fields_hash_table_index
= 0;
3175 j
->fields_offset
= 0;
3176 j
->fields_file_lost
= false;
3179 _public_
int sd_journal_reliable_fd(sd_journal
*j
) {
3180 assert_return(j
, -EINVAL
);
3181 assert_return(!journal_pid_changed(j
), -ECHILD
);
3183 return !j
->on_network
;
3186 static char *lookup_field(const char *field
, void *userdata
) {
3187 sd_journal
*j
= ASSERT_PTR(userdata
);
3194 r
= sd_journal_get_data(j
, field
, &data
, &size
);
3196 size
> REPLACE_VAR_MAX
)
3197 return strdup(field
);
3199 d
= strlen(field
) + 1;
3201 return strndup((const char*) data
+ d
, size
- d
);
3204 _public_
int sd_journal_get_catalog(sd_journal
*j
, char **ret
) {
3208 _cleanup_free_
char *text
= NULL
, *cid
= NULL
;
3212 assert_return(j
, -EINVAL
);
3213 assert_return(!journal_pid_changed(j
), -ECHILD
);
3214 assert_return(ret
, -EINVAL
);
3216 r
= sd_journal_get_data(j
, "MESSAGE_ID", &data
, &size
);
3220 cid
= strndup((const char*) data
+ 11, size
- 11);
3224 r
= sd_id128_from_string(cid
, &id
);
3228 r
= catalog_get(CATALOG_DATABASE
, id
, &text
);
3232 t
= replace_var(text
, lookup_field
, j
);
3240 _public_
int sd_journal_get_catalog_for_message_id(sd_id128_t id
, char **ret
) {
3241 assert_return(ret
, -EINVAL
);
3243 return catalog_get(CATALOG_DATABASE
, id
, ret
);
3246 _public_
int sd_journal_set_data_threshold(sd_journal
*j
, size_t sz
) {
3247 assert_return(j
, -EINVAL
);
3248 assert_return(!journal_pid_changed(j
), -ECHILD
);
3250 j
->data_threshold
= sz
;
3254 _public_
int sd_journal_get_data_threshold(sd_journal
*j
, size_t *sz
) {
3255 assert_return(j
, -EINVAL
);
3256 assert_return(!journal_pid_changed(j
), -ECHILD
);
3257 assert_return(sz
, -EINVAL
);
3259 *sz
= j
->data_threshold
;
3263 _public_
int sd_journal_has_runtime_files(sd_journal
*j
) {
3264 assert_return(j
, -EINVAL
);
3266 return j
->has_runtime_files
;
3269 _public_
int sd_journal_has_persistent_files(sd_journal
*j
) {
3270 assert_return(j
, -EINVAL
);
3272 return j
->has_persistent_files
;