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_MAX 7168
47 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
49 /* The maximum size of variable values we'll expand in catalog entries. We bind this to PATH_MAX for now, as
50 * we want to be able to show all officially valid paths at least */
51 #define REPLACE_VAR_MAX PATH_MAX
53 #define DEFAULT_DATA_THRESHOLD (64*1024)
55 static void remove_file_real(sd_journal
*j
, JournalFile
*f
);
57 static bool journal_pid_changed(sd_journal
*j
) {
60 /* We don't support people creating a journal object and
61 * keeping it around over a fork(). Let's complain. */
63 return j
->original_pid
!= getpid_cached();
66 static int journal_put_error(sd_journal
*j
, int r
, const char *path
) {
67 _cleanup_free_
char *copy
= NULL
;
70 /* Memorize an error we encountered, and store which
71 * file/directory it was generated from. Note that we store
72 * only *one* path per error code, as the error code is the
73 * key into the hashmap, and the path is the value. This means
74 * we keep track only of all error kinds, but not of all error
75 * locations. This has the benefit that the hashmap cannot
78 * We return an error here only if we didn't manage to
79 * memorize the real error. */
90 k
= hashmap_ensure_put(&j
->errors
, NULL
, INT_TO_PTR(r
), copy
);
102 static void detach_location(sd_journal
*j
) {
107 j
->current_file
= NULL
;
108 j
->current_field
= 0;
110 ORDERED_HASHMAP_FOREACH(f
, j
->files
)
111 journal_file_reset_location(f
);
114 static void init_location(Location
*l
, LocationType type
, JournalFile
*f
, Object
*o
) {
116 assert(IN_SET(type
, LOCATION_DISCRETE
, LOCATION_SEEK
));
121 .seqnum
= le64toh(o
->entry
.seqnum
),
122 .seqnum_id
= f
->header
->seqnum_id
,
123 .realtime
= le64toh(o
->entry
.realtime
),
124 .monotonic
= le64toh(o
->entry
.monotonic
),
125 .boot_id
= o
->entry
.boot_id
,
126 .xor_hash
= le64toh(o
->entry
.xor_hash
),
128 .realtime_set
= true,
129 .monotonic_set
= true,
130 .xor_hash_set
= true,
134 static void set_location(sd_journal
*j
, JournalFile
*f
, Object
*o
) {
139 init_location(&j
->current_location
, LOCATION_DISCRETE
, f
, o
);
142 j
->current_field
= 0;
144 /* Let f know its candidate entry was picked. */
145 assert(f
->location_type
== LOCATION_SEEK
);
146 f
->location_type
= LOCATION_DISCRETE
;
149 static int match_is_valid(const void *data
, size_t size
) {
157 if (((char*) data
)[0] == '_' && ((char*) data
)[1] == '_')
161 for (p
= b
; p
< b
+ size
; p
++) {
169 if (*p
>= 'A' && *p
<= 'Z')
172 if (*p
>= '0' && *p
<= '9')
181 static bool same_field(const void *_a
, size_t s
, const void *_b
, size_t t
) {
182 const uint8_t *a
= _a
, *b
= _b
;
185 for (j
= 0; j
< s
&& j
< t
; j
++) {
194 assert_not_reached();
197 static Match
*match_new(Match
*p
, MatchType t
) {
210 LIST_PREPEND(matches
, p
->matches
, m
);
215 static Match
*match_free(Match
*m
) {
219 match_free(m
->matches
);
222 LIST_REMOVE(matches
, m
->parent
->matches
, m
);
228 static Match
*match_free_if_empty(Match
*m
) {
229 if (!m
|| m
->matches
)
232 return match_free(m
);
235 _public_
int sd_journal_add_match(sd_journal
*j
, const void *data
, size_t size
) {
236 Match
*add_here
= NULL
, *m
= NULL
;
239 assert_return(j
, -EINVAL
);
240 assert_return(!journal_pid_changed(j
), -ECHILD
);
241 assert_return(data
, -EINVAL
);
246 assert_return(match_is_valid(data
, size
), -EINVAL
);
252 * level 4: concrete matches */
255 j
->level0
= match_new(NULL
, MATCH_AND_TERM
);
261 j
->level1
= match_new(j
->level0
, MATCH_OR_TERM
);
267 j
->level2
= match_new(j
->level1
, MATCH_AND_TERM
);
272 assert(j
->level0
->type
== MATCH_AND_TERM
);
273 assert(j
->level1
->type
== MATCH_OR_TERM
);
274 assert(j
->level2
->type
== MATCH_AND_TERM
);
276 /* Old-style Jenkins (unkeyed) hashing only here. We do not cover new-style siphash (keyed) hashing
277 * here, since it's different for each file, and thus can't be pre-calculated in the Match object. */
278 hash
= jenkins_hash64(data
, size
);
280 LIST_FOREACH(matches
, l3
, j
->level2
->matches
) {
281 assert(l3
->type
== MATCH_OR_TERM
);
283 LIST_FOREACH(matches
, l4
, l3
->matches
) {
284 assert(l4
->type
== MATCH_DISCRETE
);
286 /* Exactly the same match already? Then ignore
288 if (l4
->hash
== hash
&&
290 memcmp(l4
->data
, data
, size
) == 0)
293 /* Same field? Then let's add this to this OR term */
294 if (same_field(data
, size
, l4
->data
, l4
->size
)) {
305 add_here
= match_new(j
->level2
, MATCH_OR_TERM
);
310 m
= match_new(add_here
, MATCH_DISCRETE
);
316 m
->data
= memdup(data
, size
);
326 match_free_if_empty(add_here
);
327 j
->level2
= match_free_if_empty(j
->level2
);
328 j
->level1
= match_free_if_empty(j
->level1
);
329 j
->level0
= match_free_if_empty(j
->level0
);
334 _public_
int sd_journal_add_conjunction(sd_journal
*j
) {
335 assert_return(j
, -EINVAL
);
336 assert_return(!journal_pid_changed(j
), -ECHILD
);
344 if (!j
->level1
->matches
)
353 _public_
int sd_journal_add_disjunction(sd_journal
*j
) {
354 assert_return(j
, -EINVAL
);
355 assert_return(!journal_pid_changed(j
), -ECHILD
);
366 if (!j
->level2
->matches
)
373 static char *match_make_string(Match
*m
) {
375 bool enclose
= false;
378 return strdup("none");
380 if (m
->type
== MATCH_DISCRETE
)
381 return cescape_length(m
->data
, m
->size
);
383 LIST_FOREACH(matches
, i
, m
->matches
) {
386 t
= match_make_string(i
);
391 k
= strjoin(p
, m
->type
== MATCH_OR_TERM
? " OR " : " AND ", t
);
406 r
= strjoin("(", p
, ")");
414 char *journal_make_match_string(sd_journal
*j
) {
417 return match_make_string(j
->level0
);
420 _public_
void sd_journal_flush_matches(sd_journal
*j
) {
425 match_free(j
->level0
);
427 j
->level0
= j
->level1
= j
->level2
= NULL
;
432 _pure_
static int compare_with_location(const JournalFile
*f
, const Location
*l
, const JournalFile
*current_file
) {
437 assert(f
->location_type
== LOCATION_SEEK
);
438 assert(IN_SET(l
->type
, LOCATION_DISCRETE
, LOCATION_SEEK
));
440 if (l
->monotonic_set
&&
441 sd_id128_equal(f
->current_boot_id
, l
->boot_id
) &&
443 f
->current_realtime
== l
->realtime
&&
445 f
->current_xor_hash
== l
->xor_hash
&&
447 sd_id128_equal(f
->header
->seqnum_id
, l
->seqnum_id
) &&
448 f
->current_seqnum
== l
->seqnum
&&
453 sd_id128_equal(f
->header
->seqnum_id
, l
->seqnum_id
)) {
455 r
= CMP(f
->current_seqnum
, l
->seqnum
);
460 if (l
->monotonic_set
&&
461 sd_id128_equal(f
->current_boot_id
, l
->boot_id
)) {
463 r
= CMP(f
->current_monotonic
, l
->monotonic
);
468 if (l
->realtime_set
) {
470 r
= CMP(f
->current_realtime
, l
->realtime
);
475 if (l
->xor_hash_set
) {
477 r
= CMP(f
->current_xor_hash
, l
->xor_hash
);
485 static int next_for_match(
489 uint64_t after_offset
,
490 direction_t direction
,
502 if (m
->type
== MATCH_DISCRETE
) {
506 /* If the keyed hash logic is used, we need to calculate the hash fresh per file. Otherwise
507 * we can use what we pre-calculated. */
508 if (JOURNAL_HEADER_KEYED_HASH(f
->header
))
509 hash
= journal_file_hash_data(f
, m
->data
, m
->size
);
513 r
= journal_file_find_data_object_with_hash(f
, m
->data
, m
->size
, hash
, &d
, NULL
);
517 return journal_file_move_to_entry_by_offset_for_data(f
, d
, after_offset
, direction
, ret
, offset
);
519 } else if (m
->type
== MATCH_OR_TERM
) {
521 /* Find the earliest match beyond after_offset */
523 LIST_FOREACH(matches
, i
, m
->matches
) {
526 r
= next_for_match(j
, i
, f
, after_offset
, direction
, NULL
, &cp
);
530 if (np
== 0 || (direction
== DIRECTION_DOWN
? cp
< np
: cp
> np
))
538 } else if (m
->type
== MATCH_AND_TERM
) {
541 /* Always jump to the next matching entry and repeat
542 * this until we find an offset that matches for all
548 r
= next_for_match(j
, m
->matches
, f
, after_offset
, direction
, NULL
, &np
);
552 assert(direction
== DIRECTION_DOWN
? np
>= after_offset
: np
<= after_offset
);
553 last_moved
= m
->matches
;
555 LIST_LOOP_BUT_ONE(matches
, i
, m
->matches
, last_moved
) {
558 r
= next_for_match(j
, i
, f
, np
, direction
, NULL
, &cp
);
562 assert(direction
== DIRECTION_DOWN
? cp
>= np
: cp
<= np
);
563 if (direction
== DIRECTION_DOWN
? cp
> np
: cp
< np
) {
572 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, np
, &n
);
584 static int find_location_for_match(
588 direction_t direction
,
598 if (m
->type
== MATCH_DISCRETE
) {
602 if (JOURNAL_HEADER_KEYED_HASH(f
->header
))
603 hash
= journal_file_hash_data(f
, m
->data
, m
->size
);
607 r
= journal_file_find_data_object_with_hash(f
, m
->data
, m
->size
, hash
, &d
, &dp
);
611 /* FIXME: missing: find by monotonic */
613 if (j
->current_location
.type
== LOCATION_HEAD
)
614 return journal_file_next_entry_for_data(f
, d
, DIRECTION_DOWN
, ret
, offset
);
615 if (j
->current_location
.type
== LOCATION_TAIL
)
616 return journal_file_next_entry_for_data(f
, d
, DIRECTION_UP
, ret
, offset
);
617 if (j
->current_location
.seqnum_set
&& sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
618 return journal_file_move_to_entry_by_seqnum_for_data(f
, d
, j
->current_location
.seqnum
, direction
, ret
, offset
);
619 if (j
->current_location
.monotonic_set
) {
620 r
= journal_file_move_to_entry_by_monotonic_for_data(f
, d
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, ret
, offset
);
624 /* The data object might have been invalidated. */
625 r
= journal_file_move_to_object(f
, OBJECT_DATA
, dp
, &d
);
629 if (j
->current_location
.realtime_set
)
630 return journal_file_move_to_entry_by_realtime_for_data(f
, d
, j
->current_location
.realtime
, direction
, ret
, offset
);
632 return journal_file_next_entry_for_data(f
, d
, direction
, ret
, offset
);
634 } else if (m
->type
== MATCH_OR_TERM
) {
638 /* Find the earliest match */
640 LIST_FOREACH(matches
, i
, m
->matches
) {
643 r
= find_location_for_match(j
, i
, f
, direction
, NULL
, &cp
);
647 if (np
== 0 || (direction
== DIRECTION_DOWN
? np
> cp
: np
< cp
))
655 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, np
, &n
);
669 assert(m
->type
== MATCH_AND_TERM
);
671 /* First jump to the last match, and then find the
672 * next one where all matches match */
677 LIST_FOREACH(matches
, i
, m
->matches
) {
680 r
= find_location_for_match(j
, i
, f
, direction
, NULL
, &cp
);
684 if (np
== 0 || (direction
== DIRECTION_DOWN
? cp
> np
: cp
< np
))
688 return next_for_match(j
, m
, f
, np
, direction
, ret
, offset
);
692 static int find_location_with_matches(
695 direction_t direction
,
707 /* No matches is simple */
709 if (j
->current_location
.type
== LOCATION_HEAD
)
710 return journal_file_next_entry(f
, 0, DIRECTION_DOWN
, ret
, offset
);
711 if (j
->current_location
.type
== LOCATION_TAIL
)
712 return journal_file_next_entry(f
, 0, DIRECTION_UP
, ret
, offset
);
713 if (j
->current_location
.seqnum_set
&& sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
714 return journal_file_move_to_entry_by_seqnum(f
, j
->current_location
.seqnum
, direction
, ret
, offset
);
715 if (j
->current_location
.monotonic_set
) {
716 r
= journal_file_move_to_entry_by_monotonic(f
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, ret
, offset
);
720 if (j
->current_location
.realtime_set
)
721 return journal_file_move_to_entry_by_realtime(f
, j
->current_location
.realtime
, direction
, ret
, offset
);
723 return journal_file_next_entry(f
, 0, direction
, ret
, offset
);
725 return find_location_for_match(j
, j
->level0
, f
, direction
, ret
, offset
);
728 static int next_with_matches(
731 direction_t direction
,
740 /* No matches is easy. We simple advance the file
743 return journal_file_next_entry(f
, f
->current_offset
, direction
, ret
, offset
);
745 /* If we have a match then we look for the next matching entry
746 * with an offset at least one step larger */
747 return next_for_match(j
, j
->level0
, f
,
748 direction
== DIRECTION_DOWN
? f
->current_offset
+ 1
749 : f
->current_offset
- 1,
750 direction
, ret
, offset
);
753 static int next_beyond_location(sd_journal
*j
, JournalFile
*f
, direction_t direction
) {
755 uint64_t cp
, n_entries
;
761 n_entries
= le64toh(f
->header
->n_entries
);
763 /* If we hit EOF before, we don't need to look into this file again
764 * unless direction changed or new entries appeared. */
765 if (f
->last_direction
== direction
&& f
->location_type
== LOCATION_TAIL
&&
766 n_entries
== f
->last_n_entries
)
769 f
->last_n_entries
= n_entries
;
771 if (f
->last_direction
== direction
&& f
->current_offset
> 0) {
772 /* LOCATION_SEEK here means we did the work in a previous
773 * iteration and the current location already points to a
774 * candidate entry. */
775 if (f
->location_type
!= LOCATION_SEEK
) {
776 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
780 journal_file_save_location(f
, c
, cp
);
783 f
->last_direction
= direction
;
785 r
= find_location_with_matches(j
, f
, direction
, &c
, &cp
);
789 journal_file_save_location(f
, c
, cp
);
792 /* OK, we found the spot, now let's advance until an entry
793 * that is actually different from what we were previously
794 * looking at. This is necessary to handle entries which exist
795 * in two (or more) journal files, and which shall all be
796 * suppressed but one. */
801 if (j
->current_location
.type
== LOCATION_DISCRETE
) {
804 k
= compare_with_location(f
, &j
->current_location
, j
->current_file
);
806 found
= direction
== DIRECTION_DOWN
? k
> 0 : k
< 0;
813 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
817 journal_file_save_location(f
, c
, cp
);
821 static int real_journal_next(sd_journal
*j
, direction_t direction
) {
822 JournalFile
*new_file
= NULL
;
828 assert_return(j
, -EINVAL
);
829 assert_return(!journal_pid_changed(j
), -ECHILD
);
831 r
= iterated_cache_get(j
->files_cache
, NULL
, &files
, &n_files
);
835 for (i
= 0; i
< n_files
; i
++) {
836 JournalFile
*f
= (JournalFile
*)files
[i
];
839 r
= next_beyond_location(j
, f
, direction
);
841 log_debug_errno(r
, "Can't iterate through %s, ignoring: %m", f
->path
);
842 remove_file_real(j
, f
);
845 f
->location_type
= LOCATION_TAIL
;
854 k
= journal_file_compare_locations(f
, new_file
);
856 found
= direction
== DIRECTION_DOWN
? k
< 0 : k
> 0;
866 r
= journal_file_move_to_object(new_file
, OBJECT_ENTRY
, new_file
->current_offset
, &o
);
870 set_location(j
, new_file
, o
);
875 _public_
int sd_journal_next(sd_journal
*j
) {
876 return real_journal_next(j
, DIRECTION_DOWN
);
879 _public_
int sd_journal_previous(sd_journal
*j
) {
880 return real_journal_next(j
, DIRECTION_UP
);
883 static int real_journal_next_skip(sd_journal
*j
, direction_t direction
, uint64_t skip
) {
886 assert_return(j
, -EINVAL
);
887 assert_return(!journal_pid_changed(j
), -ECHILD
);
888 assert_return(skip
<= INT_MAX
, -ERANGE
);
891 /* If this is not a discrete skip, then at least
892 * resolve the current location */
893 if (j
->current_location
.type
!= LOCATION_DISCRETE
) {
894 r
= real_journal_next(j
, direction
);
903 r
= real_journal_next(j
, direction
);
917 _public_
int sd_journal_next_skip(sd_journal
*j
, uint64_t skip
) {
918 return real_journal_next_skip(j
, DIRECTION_DOWN
, skip
);
921 _public_
int sd_journal_previous_skip(sd_journal
*j
, uint64_t skip
) {
922 return real_journal_next_skip(j
, DIRECTION_UP
, skip
);
925 _public_
int sd_journal_get_cursor(sd_journal
*j
, char **cursor
) {
929 assert_return(j
, -EINVAL
);
930 assert_return(!journal_pid_changed(j
), -ECHILD
);
931 assert_return(cursor
, -EINVAL
);
933 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
934 return -EADDRNOTAVAIL
;
936 r
= journal_file_move_to_object(j
->current_file
, OBJECT_ENTRY
, j
->current_file
->current_offset
, &o
);
941 "s=%s;i=%"PRIx64
";b=%s;m=%"PRIx64
";t=%"PRIx64
";x=%"PRIx64
,
942 SD_ID128_TO_STRING(j
->current_file
->header
->seqnum_id
), le64toh(o
->entry
.seqnum
),
943 SD_ID128_TO_STRING(o
->entry
.boot_id
), le64toh(o
->entry
.monotonic
),
944 le64toh(o
->entry
.realtime
),
945 le64toh(o
->entry
.xor_hash
)) < 0)
951 _public_
int sd_journal_seek_cursor(sd_journal
*j
, const char *cursor
) {
952 unsigned long long seqnum
, monotonic
, realtime
, xor_hash
;
953 bool seqnum_id_set
= false,
956 monotonic_set
= false,
957 realtime_set
= false,
958 xor_hash_set
= false;
959 sd_id128_t seqnum_id
, boot_id
;
962 assert_return(j
, -EINVAL
);
963 assert_return(!journal_pid_changed(j
), -ECHILD
);
964 assert_return(!isempty(cursor
), -EINVAL
);
966 for (const char *p
= cursor
;;) {
967 _cleanup_free_
char *word
= NULL
;
969 r
= extract_first_word(&p
, &word
, ";", EXTRACT_DONT_COALESCE_SEPARATORS
);
975 if (word
[0] == '\0' || word
[1] != '=')
980 seqnum_id_set
= true;
981 r
= sd_id128_from_string(word
+ 2, &seqnum_id
);
988 if (sscanf(word
+ 2, "%llx", &seqnum
) != 1)
994 r
= sd_id128_from_string(word
+ 2, &boot_id
);
998 monotonic_set
= true;
999 if (sscanf(word
+ 2, "%llx", &monotonic
) != 1)
1004 realtime_set
= true;
1005 if (sscanf(word
+ 2, "%llx", &realtime
) != 1)
1010 xor_hash_set
= true;
1011 if (sscanf(word
+ 2, "%llx", &xor_hash
) != 1)
1017 if ((!seqnum_set
|| !seqnum_id_set
) &&
1018 (!monotonic_set
|| !boot_id_set
) &&
1023 j
->current_location
= (Location
) {
1024 .type
= LOCATION_SEEK
,
1028 j
->current_location
.realtime
= (uint64_t) realtime
;
1029 j
->current_location
.realtime_set
= true;
1032 if (seqnum_set
&& seqnum_id_set
) {
1033 j
->current_location
.seqnum
= (uint64_t) seqnum
;
1034 j
->current_location
.seqnum_id
= seqnum_id
;
1035 j
->current_location
.seqnum_set
= true;
1038 if (monotonic_set
&& boot_id_set
) {
1039 j
->current_location
.monotonic
= (uint64_t) monotonic
;
1040 j
->current_location
.boot_id
= boot_id
;
1041 j
->current_location
.monotonic_set
= true;
1045 j
->current_location
.xor_hash
= (uint64_t) xor_hash
;
1046 j
->current_location
.xor_hash_set
= true;
1052 _public_
int sd_journal_test_cursor(sd_journal
*j
, const char *cursor
) {
1056 assert_return(j
, -EINVAL
);
1057 assert_return(!journal_pid_changed(j
), -ECHILD
);
1058 assert_return(!isempty(cursor
), -EINVAL
);
1060 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
1061 return -EADDRNOTAVAIL
;
1063 r
= journal_file_move_to_object(j
->current_file
, OBJECT_ENTRY
, j
->current_file
->current_offset
, &o
);
1068 _cleanup_free_
char *item
= NULL
;
1069 unsigned long long ll
;
1073 r
= extract_first_word(&cursor
, &item
, ";", EXTRACT_DONT_COALESCE_SEPARATORS
);
1080 if (strlen(item
) < 2 || item
[1] != '=')
1086 k
= sd_id128_from_string(item
+2, &id
);
1089 if (!sd_id128_equal(id
, j
->current_file
->header
->seqnum_id
))
1094 if (sscanf(item
+2, "%llx", &ll
) != 1)
1096 if (ll
!= le64toh(o
->entry
.seqnum
))
1101 k
= sd_id128_from_string(item
+2, &id
);
1104 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
1109 if (sscanf(item
+2, "%llx", &ll
) != 1)
1111 if (ll
!= le64toh(o
->entry
.monotonic
))
1116 if (sscanf(item
+2, "%llx", &ll
) != 1)
1118 if (ll
!= le64toh(o
->entry
.realtime
))
1123 if (sscanf(item
+2, "%llx", &ll
) != 1)
1125 if (ll
!= le64toh(o
->entry
.xor_hash
))
1134 _public_
int sd_journal_seek_monotonic_usec(sd_journal
*j
, sd_id128_t boot_id
, uint64_t usec
) {
1135 assert_return(j
, -EINVAL
);
1136 assert_return(!journal_pid_changed(j
), -ECHILD
);
1140 j
->current_location
= (Location
) {
1141 .type
= LOCATION_SEEK
,
1144 .monotonic_set
= true,
1150 _public_
int sd_journal_seek_realtime_usec(sd_journal
*j
, uint64_t usec
) {
1151 assert_return(j
, -EINVAL
);
1152 assert_return(!journal_pid_changed(j
), -ECHILD
);
1156 j
->current_location
= (Location
) {
1157 .type
= LOCATION_SEEK
,
1159 .realtime_set
= true,
1165 _public_
int sd_journal_seek_head(sd_journal
*j
) {
1166 assert_return(j
, -EINVAL
);
1167 assert_return(!journal_pid_changed(j
), -ECHILD
);
1171 j
->current_location
= (Location
) {
1172 .type
= LOCATION_HEAD
,
1178 _public_
int sd_journal_seek_tail(sd_journal
*j
) {
1179 assert_return(j
, -EINVAL
);
1180 assert_return(!journal_pid_changed(j
), -ECHILD
);
1184 j
->current_location
= (Location
) {
1185 .type
= LOCATION_TAIL
,
1191 static void check_network(sd_journal
*j
, int fd
) {
1197 j
->on_network
= fd_is_network_fs(fd
);
1200 static bool file_has_type_prefix(const char *prefix
, const char *filename
) {
1201 const char *full
, *tilded
, *atted
;
1203 full
= strjoina(prefix
, ".journal");
1204 tilded
= strjoina(full
, "~");
1205 atted
= strjoina(prefix
, "@");
1207 return STR_IN_SET(filename
, full
, tilded
) ||
1208 startswith(filename
, atted
);
1211 static bool file_type_wanted(int flags
, const char *filename
) {
1214 if (!endswith(filename
, ".journal") && !endswith(filename
, ".journal~"))
1217 /* no flags set → every type is OK */
1218 if (!(flags
& (SD_JOURNAL_SYSTEM
| SD_JOURNAL_CURRENT_USER
)))
1221 if (flags
& SD_JOURNAL_SYSTEM
&& file_has_type_prefix("system", filename
))
1224 if (flags
& SD_JOURNAL_CURRENT_USER
) {
1225 char prefix
[5 + DECIMAL_STR_MAX(uid_t
) + 1];
1227 xsprintf(prefix
, "user-"UID_FMT
, getuid());
1229 if (file_has_type_prefix(prefix
, filename
))
1236 static bool path_has_prefix(sd_journal
*j
, const char *path
, const char *prefix
) {
1241 if (j
->toplevel_fd
>= 0)
1244 return path_startswith(path
, prefix
);
1247 static void track_file_disposition(sd_journal
*j
, JournalFile
*f
) {
1251 if (!j
->has_runtime_files
&& path_has_prefix(j
, f
->path
, "/run"))
1252 j
->has_runtime_files
= true;
1253 else if (!j
->has_persistent_files
&& path_has_prefix(j
, f
->path
, "/var"))
1254 j
->has_persistent_files
= true;
1257 static const char *skip_slash(const char *p
) {
1268 static int add_any_file(
1273 bool close_fd
= false;
1279 assert(fd
>= 0 || path
);
1282 if (j
->toplevel_fd
>= 0)
1283 /* If there's a top-level fd defined make the path relative, explicitly, since otherwise
1284 * openat() ignores the first argument. */
1286 fd
= openat(j
->toplevel_fd
, skip_slash(path
), O_RDONLY
|O_CLOEXEC
|O_NONBLOCK
);
1288 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NONBLOCK
);
1290 r
= log_debug_errno(errno
, "Failed to open journal file %s: %m", path
);
1296 r
= fd_nonblock(fd
, false);
1298 r
= log_debug_errno(errno
, "Failed to turn off O_NONBLOCK for %s: %m", path
);
1303 if (fstat(fd
, &st
) < 0) {
1304 r
= log_debug_errno(errno
, "Failed to fstat file '%s': %m", path
);
1308 r
= stat_verify_regular(&st
);
1310 log_debug_errno(r
, "Refusing to open '%s', as it is not a regular file.", path
);
1314 f
= ordered_hashmap_get(j
->files
, path
);
1316 if (stat_inode_same(&f
->last_stat
, &st
)) {
1318 /* We already track this file, under the same path and with the same device/inode numbers, it's
1319 * hence really the same. Mark this file as seen in this generation. This is used to GC old
1320 * files in process_q_overflow() to detect journal files that are still there and discern them
1321 * from those which are gone. */
1323 f
->last_seen_generation
= j
->generation
;
1328 /* So we tracked a file under this name, but it has a different inode/device. In that case, it got
1329 * replaced (probably due to rotation?), let's drop it hence from our list. */
1330 remove_file_real(j
, f
);
1334 if (ordered_hashmap_size(j
->files
) >= JOURNAL_FILES_MAX
) {
1335 log_debug("Too many open journal files, not adding %s.", path
);
1340 r
= journal_file_open(fd
, path
, O_RDONLY
, 0, false, 0, false, NULL
, j
->mmap
, NULL
, &f
);
1342 log_debug_errno(r
, "Failed to open journal file %s: %m", path
);
1346 /* journal_file_dump(f); */
1348 r
= ordered_hashmap_put(j
->files
, f
->path
, f
);
1350 f
->close_fd
= false; /* make sure journal_file_close() doesn't close the caller's fd (or our own). We'll let the caller do that, or ourselves */
1351 (void) journal_file_close(f
);
1355 close_fd
= false; /* the fd is now owned by the JournalFile object */
1357 f
->last_seen_generation
= j
->generation
;
1359 track_file_disposition(j
, f
);
1360 check_network(j
, f
->fd
);
1362 j
->current_invalidate_counter
++;
1364 log_debug("File %s added.", f
->path
);
1373 k
= journal_put_error(j
, r
, path
);
1381 static int add_file_by_name(
1384 const char *filename
) {
1392 if (j
->no_new_files
)
1395 if (!file_type_wanted(j
->flags
, filename
))
1398 path
= prefix_roota(prefix
, filename
);
1399 return add_any_file(j
, -1, path
);
1402 static void remove_file_by_name(
1405 const char *filename
) {
1414 path
= prefix_roota(prefix
, filename
);
1415 f
= ordered_hashmap_get(j
->files
, path
);
1419 remove_file_real(j
, f
);
1422 static void remove_file_real(sd_journal
*j
, JournalFile
*f
) {
1426 (void) ordered_hashmap_remove(j
->files
, f
->path
);
1428 log_debug("File %s removed.", f
->path
);
1430 if (j
->current_file
== f
) {
1431 j
->current_file
= NULL
;
1432 j
->current_field
= 0;
1435 if (j
->unique_file
== f
) {
1436 /* Jump to the next unique_file or NULL if that one was last */
1437 j
->unique_file
= ordered_hashmap_next(j
->files
, j
->unique_file
->path
);
1438 j
->unique_offset
= 0;
1439 if (!j
->unique_file
)
1440 j
->unique_file_lost
= true;
1443 if (j
->fields_file
== f
) {
1444 j
->fields_file
= ordered_hashmap_next(j
->files
, j
->fields_file
->path
);
1445 j
->fields_offset
= 0;
1446 if (!j
->fields_file
)
1447 j
->fields_file_lost
= true;
1450 (void) journal_file_close(f
);
1452 j
->current_invalidate_counter
++;
1455 static int dirname_is_machine_id(const char *fn
) {
1456 sd_id128_t id
, machine
;
1460 /* Returns true if the specified directory name matches the local machine ID */
1462 r
= sd_id128_get_machine(&machine
);
1466 e
= strchr(fn
, '.');
1470 /* Looks like it has a namespace suffix. Verify that. */
1471 if (!log_namespace_name_valid(e
+ 1))
1474 k
= strndupa_safe(fn
, e
- fn
);
1475 r
= sd_id128_from_string(k
, &id
);
1477 r
= sd_id128_from_string(fn
, &id
);
1481 return sd_id128_equal(id
, machine
);
1484 static int dirname_has_namespace(const char *fn
, const char *namespace) {
1487 /* Returns true if the specified directory name matches the specified namespace */
1489 e
= strchr(fn
, '.');
1496 if (!streq(e
+ 1, namespace))
1499 k
= strndupa_safe(fn
, e
- fn
);
1500 return id128_is_valid(k
);
1506 return id128_is_valid(fn
);
1509 static bool dirent_is_journal_file(const struct dirent
*de
) {
1512 /* Returns true if the specified directory entry looks like a journal file we might be interested in */
1514 if (!IN_SET(de
->d_type
, DT_REG
, DT_LNK
, DT_UNKNOWN
))
1517 return endswith(de
->d_name
, ".journal") ||
1518 endswith(de
->d_name
, ".journal~");
1521 static bool dirent_is_journal_subdir(const struct dirent
*de
) {
1525 /* returns true if the specified directory entry looks like a directory that might contain journal
1526 * files we might be interested in, i.e. is either a 128bit ID or a 128bit ID suffixed by a
1529 if (!IN_SET(de
->d_type
, DT_DIR
, DT_LNK
, DT_UNKNOWN
))
1532 e
= strchr(de
->d_name
, '.');
1534 return id128_is_valid(de
->d_name
); /* No namespace */
1536 n
= strndupa_safe(de
->d_name
, e
- de
->d_name
);
1537 if (!id128_is_valid(n
))
1540 return log_namespace_name_valid(e
+ 1);
1543 static int directory_open(sd_journal
*j
, const char *path
, DIR **ret
) {
1550 if (j
->toplevel_fd
< 0)
1553 /* Open the specified directory relative to the toplevel fd. Enforce that the path specified is
1554 * relative, by dropping the initial slash */
1555 d
= xopendirat(j
->toplevel_fd
, skip_slash(path
), 0);
1563 static int add_directory(sd_journal
*j
, const char *prefix
, const char *dirname
);
1565 static void directory_enumerate(sd_journal
*j
, Directory
*m
, DIR *d
) {
1570 FOREACH_DIRENT_ALL(de
, d
, goto fail
) {
1571 if (dirent_is_journal_file(de
))
1572 (void) add_file_by_name(j
, m
->path
, de
->d_name
);
1574 if (m
->is_root
&& dirent_is_journal_subdir(de
))
1575 (void) add_directory(j
, m
->path
, de
->d_name
);
1580 log_debug_errno(errno
, "Failed to enumerate directory %s, ignoring: %m", m
->path
);
1583 static void directory_watch(sd_journal
*j
, Directory
*m
, int fd
, uint32_t mask
) {
1590 /* Watch this directory if that's enabled and if it not being watched yet. */
1592 if (m
->wd
> 0) /* Already have a watch? */
1594 if (j
->inotify_fd
< 0) /* Not watching at all? */
1597 m
->wd
= inotify_add_watch_fd(j
->inotify_fd
, fd
, mask
);
1599 log_debug_errno(errno
, "Failed to watch journal directory '%s', ignoring: %m", m
->path
);
1603 r
= hashmap_put(j
->directories_by_wd
, INT_TO_PTR(m
->wd
), m
);
1605 log_debug_errno(r
, "Directory '%s' already being watched under a different path, ignoring: %m", m
->path
);
1607 log_debug_errno(r
, "Failed to add watch for journal directory '%s' to hashmap, ignoring: %m", m
->path
);
1608 (void) inotify_rm_watch(j
->inotify_fd
, m
->wd
);
1613 static int add_directory(
1616 const char *dirname
) {
1618 _cleanup_free_
char *path
= NULL
;
1619 _cleanup_closedir_
DIR *d
= NULL
;
1626 /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch
1627 * and reenumerates directory contents */
1629 path
= path_join(prefix
, dirname
);
1635 log_debug("Considering directory '%s'.", path
);
1637 /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */
1638 if ((j
->flags
& SD_JOURNAL_LOCAL_ONLY
) &&
1639 !((dirname
&& dirname_is_machine_id(dirname
) > 0) || path_has_prefix(j
, path
, "/run")))
1643 (!(FLAGS_SET(j
->flags
, SD_JOURNAL_ALL_NAMESPACES
) ||
1644 dirname_has_namespace(dirname
, j
->namespace) > 0 ||
1645 (FLAGS_SET(j
->flags
, SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE
) && dirname_has_namespace(dirname
, NULL
) > 0))))
1648 r
= directory_open(j
, path
, &d
);
1650 log_debug_errno(r
, "Failed to open directory '%s': %m", path
);
1654 m
= hashmap_get(j
->directories_by_path
, path
);
1656 m
= new(Directory
, 1);
1667 if (hashmap_put(j
->directories_by_path
, m
->path
, m
) < 0) {
1673 path
= NULL
; /* avoid freeing in cleanup */
1674 j
->current_invalidate_counter
++;
1676 log_debug("Directory %s added.", m
->path
);
1678 } else if (m
->is_root
)
1679 return 0; /* Don't 'downgrade' from root directory */
1681 m
->last_seen_generation
= j
->generation
;
1683 directory_watch(j
, m
, dirfd(d
),
1684 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1685 IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
|IN_MOVED_FROM
|
1688 if (!j
->no_new_files
)
1689 directory_enumerate(j
, m
, d
);
1691 check_network(j
, dirfd(d
));
1696 k
= journal_put_error(j
, r
, path
?: prefix
);
1703 static int add_root_directory(sd_journal
*j
, const char *p
, bool missing_ok
) {
1705 _cleanup_closedir_
DIR *d
= NULL
;
1711 /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we
1712 * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially
1713 * populate the set, as well as to update it later. */
1716 /* If there's a path specified, use it. */
1718 log_debug("Considering root directory '%s'.", p
);
1720 if ((j
->flags
& SD_JOURNAL_RUNTIME_ONLY
) &&
1721 !path_has_prefix(j
, p
, "/run"))
1725 p
= strjoina(j
->prefix
, p
);
1727 r
= directory_open(j
, p
, &d
);
1728 if (r
== -ENOENT
&& missing_ok
)
1731 log_debug_errno(r
, "Failed to open root directory %s: %m", p
);
1735 _cleanup_close_
int dfd
= -1;
1737 /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since
1738 * opendir() will take possession of the fd, and close it, which we don't want. */
1740 p
= "."; /* store this as "." in the directories hashmap */
1742 dfd
= fcntl(j
->toplevel_fd
, F_DUPFD_CLOEXEC
, 3);
1748 d
= take_fdopendir(&dfd
);
1757 m
= hashmap_get(j
->directories_by_path
, p
);
1759 m
= new0(Directory
, 1);
1767 m
->path
= strdup(p
);
1774 if (hashmap_put(j
->directories_by_path
, m
->path
, m
) < 0) {
1781 j
->current_invalidate_counter
++;
1783 log_debug("Root directory %s added.", m
->path
);
1785 } else if (!m
->is_root
)
1788 directory_watch(j
, m
, dirfd(d
),
1789 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1792 if (!j
->no_new_files
)
1793 directory_enumerate(j
, m
, d
);
1795 check_network(j
, dirfd(d
));
1800 k
= journal_put_error(j
, r
, p
);
1807 static void remove_directory(sd_journal
*j
, Directory
*d
) {
1811 hashmap_remove(j
->directories_by_wd
, INT_TO_PTR(d
->wd
));
1813 if (j
->inotify_fd
>= 0)
1814 (void) inotify_rm_watch(j
->inotify_fd
, d
->wd
);
1817 hashmap_remove(j
->directories_by_path
, d
->path
);
1820 log_debug("Root directory %s removed.", d
->path
);
1822 log_debug("Directory %s removed.", d
->path
);
1828 static int add_search_paths(sd_journal
*j
) {
1830 static const char search_paths
[] =
1831 "/run/log/journal\0"
1832 "/var/log/journal\0";
1837 /* We ignore most errors here, since the idea is to only open
1838 * what's actually accessible, and ignore the rest. */
1840 NULSTR_FOREACH(p
, search_paths
)
1841 (void) add_root_directory(j
, p
, true);
1843 if (!(j
->flags
& SD_JOURNAL_LOCAL_ONLY
))
1844 (void) add_root_directory(j
, "/var/log/journal/remote", true);
1849 static int add_current_paths(sd_journal
*j
) {
1853 assert(j
->no_new_files
);
1855 /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we
1856 * treat them as fatal. */
1858 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
1859 _cleanup_free_
char *dir
= NULL
;
1862 dir
= dirname_malloc(f
->path
);
1866 r
= add_directory(j
, dir
, NULL
);
1874 static int allocate_inotify(sd_journal
*j
) {
1877 if (j
->inotify_fd
< 0) {
1878 j
->inotify_fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
1879 if (j
->inotify_fd
< 0)
1883 return hashmap_ensure_allocated(&j
->directories_by_wd
, NULL
);
1886 static sd_journal
*journal_new(int flags
, const char *path
, const char *namespace) {
1887 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
1889 j
= new0(sd_journal
, 1);
1893 j
->original_pid
= getpid_cached();
1894 j
->toplevel_fd
= -1;
1897 j
->data_threshold
= DEFAULT_DATA_THRESHOLD
;
1906 if (flags
& SD_JOURNAL_OS_ROOT
)
1913 j
->namespace = strdup(namespace);
1918 j
->files
= ordered_hashmap_new(&path_hash_ops
);
1922 j
->files_cache
= ordered_hashmap_iterated_cache_new(j
->files
);
1923 j
->directories_by_path
= hashmap_new(&path_hash_ops
);
1924 j
->mmap
= mmap_cache_new();
1925 if (!j
->files_cache
|| !j
->directories_by_path
|| !j
->mmap
)
1931 #define OPEN_ALLOWED_FLAGS \
1932 (SD_JOURNAL_LOCAL_ONLY | \
1933 SD_JOURNAL_RUNTIME_ONLY | \
1934 SD_JOURNAL_SYSTEM | \
1935 SD_JOURNAL_CURRENT_USER | \
1936 SD_JOURNAL_ALL_NAMESPACES | \
1937 SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE)
1939 _public_
int sd_journal_open_namespace(sd_journal
**ret
, const char *namespace, int flags
) {
1940 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
1943 assert_return(ret
, -EINVAL
);
1944 assert_return((flags
& ~OPEN_ALLOWED_FLAGS
) == 0, -EINVAL
);
1946 j
= journal_new(flags
, NULL
, namespace);
1950 r
= add_search_paths(j
);
1958 _public_
int sd_journal_open(sd_journal
**ret
, int flags
) {
1959 return sd_journal_open_namespace(ret
, NULL
, flags
);
1962 #define OPEN_CONTAINER_ALLOWED_FLAGS \
1963 (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM)
1965 _public_
int sd_journal_open_container(sd_journal
**ret
, const char *machine
, int flags
) {
1966 _cleanup_free_
char *root
= NULL
, *class = NULL
;
1967 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
1971 /* This is deprecated, people should use machined's OpenMachineRootDirectory() call instead in
1972 * combination with sd_journal_open_directory_fd(). */
1974 assert_return(machine
, -EINVAL
);
1975 assert_return(ret
, -EINVAL
);
1976 assert_return((flags
& ~OPEN_CONTAINER_ALLOWED_FLAGS
) == 0, -EINVAL
);
1977 assert_return(hostname_is_valid(machine
, 0), -EINVAL
);
1979 p
= strjoina("/run/systemd/machines/", machine
);
1980 r
= parse_env_file(NULL
, p
,
1990 if (!streq_ptr(class, "container"))
1993 j
= journal_new(flags
, root
, NULL
);
1997 r
= add_search_paths(j
);
2005 #define OPEN_DIRECTORY_ALLOWED_FLAGS \
2006 (SD_JOURNAL_OS_ROOT | \
2007 SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
2009 _public_
int sd_journal_open_directory(sd_journal
**ret
, const char *path
, int flags
) {
2010 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
2013 assert_return(ret
, -EINVAL
);
2014 assert_return(path
, -EINVAL
);
2015 assert_return((flags
& ~OPEN_DIRECTORY_ALLOWED_FLAGS
) == 0, -EINVAL
);
2017 j
= journal_new(flags
, path
, NULL
);
2021 if (flags
& SD_JOURNAL_OS_ROOT
)
2022 r
= add_search_paths(j
);
2024 r
= add_root_directory(j
, path
, false);
2032 _public_
int sd_journal_open_files(sd_journal
**ret
, const char **paths
, int flags
) {
2033 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
2036 assert_return(ret
, -EINVAL
);
2037 assert_return(flags
== 0, -EINVAL
);
2039 j
= journal_new(flags
, NULL
, NULL
);
2043 STRV_FOREACH(path
, paths
) {
2044 r
= add_any_file(j
, -1, *path
);
2049 j
->no_new_files
= true;
2055 #define OPEN_DIRECTORY_FD_ALLOWED_FLAGS \
2056 (SD_JOURNAL_OS_ROOT | \
2057 SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
2059 _public_
int sd_journal_open_directory_fd(sd_journal
**ret
, int fd
, int flags
) {
2060 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
2064 assert_return(ret
, -EINVAL
);
2065 assert_return(fd
>= 0, -EBADF
);
2066 assert_return((flags
& ~OPEN_DIRECTORY_FD_ALLOWED_FLAGS
) == 0, -EINVAL
);
2068 if (fstat(fd
, &st
) < 0)
2071 if (!S_ISDIR(st
.st_mode
))
2074 j
= journal_new(flags
, NULL
, NULL
);
2078 j
->toplevel_fd
= fd
;
2080 if (flags
& SD_JOURNAL_OS_ROOT
)
2081 r
= add_search_paths(j
);
2083 r
= add_root_directory(j
, NULL
, false);
2091 _public_
int sd_journal_open_files_fd(sd_journal
**ret
, int fds
[], unsigned n_fds
, int flags
) {
2093 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
2097 assert_return(ret
, -EINVAL
);
2098 assert_return(n_fds
> 0, -EBADF
);
2099 assert_return(flags
== 0, -EINVAL
);
2101 j
= journal_new(flags
, NULL
, NULL
);
2105 for (i
= 0; i
< n_fds
; i
++) {
2113 if (fstat(fds
[i
], &st
) < 0) {
2118 r
= stat_verify_regular(&st
);
2122 r
= add_any_file(j
, fds
[i
], NULL
);
2127 j
->no_new_files
= true;
2128 j
->no_inotify
= true;
2134 /* If we fail, make sure we don't take possession of the files we managed to make use of successfully, and they
2136 ORDERED_HASHMAP_FOREACH(f
, j
->files
)
2137 f
->close_fd
= false;
2142 _public_
void sd_journal_close(sd_journal
*j
) {
2148 sd_journal_flush_matches(j
);
2150 ordered_hashmap_free_with_destructor(j
->files
, journal_file_close
);
2151 iterated_cache_free(j
->files_cache
);
2153 while ((d
= hashmap_first(j
->directories_by_path
)))
2154 remove_directory(j
, d
);
2156 while ((d
= hashmap_first(j
->directories_by_wd
)))
2157 remove_directory(j
, d
);
2159 hashmap_free(j
->directories_by_path
);
2160 hashmap_free(j
->directories_by_wd
);
2162 safe_close(j
->inotify_fd
);
2165 mmap_cache_stats_log_debug(j
->mmap
);
2166 mmap_cache_unref(j
->mmap
);
2169 hashmap_free_free(j
->errors
);
2174 free(j
->unique_field
);
2175 free(j
->fields_buffer
);
2179 _public_
int sd_journal_get_realtime_usec(sd_journal
*j
, uint64_t *ret
) {
2184 assert_return(j
, -EINVAL
);
2185 assert_return(!journal_pid_changed(j
), -ECHILD
);
2186 assert_return(ret
, -EINVAL
);
2188 f
= j
->current_file
;
2190 return -EADDRNOTAVAIL
;
2192 if (f
->current_offset
<= 0)
2193 return -EADDRNOTAVAIL
;
2195 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2199 *ret
= le64toh(o
->entry
.realtime
);
2203 _public_
int sd_journal_get_monotonic_usec(sd_journal
*j
, uint64_t *ret
, sd_id128_t
*ret_boot_id
) {
2208 assert_return(j
, -EINVAL
);
2209 assert_return(!journal_pid_changed(j
), -ECHILD
);
2211 f
= j
->current_file
;
2213 return -EADDRNOTAVAIL
;
2215 if (f
->current_offset
<= 0)
2216 return -EADDRNOTAVAIL
;
2218 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2223 *ret_boot_id
= o
->entry
.boot_id
;
2227 r
= sd_id128_get_boot(&id
);
2231 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
2236 *ret
= le64toh(o
->entry
.monotonic
);
2241 static bool field_is_valid(const char *field
) {
2249 if (startswith(field
, "__"))
2252 for (p
= field
; *p
; p
++) {
2257 if (*p
>= 'A' && *p
<= 'Z')
2260 if (*p
>= '0' && *p
<= '9')
2269 _public_
int sd_journal_get_data(sd_journal
*j
, const char *field
, const void **data
, size_t *size
) {
2272 size_t field_length
;
2276 assert_return(j
, -EINVAL
);
2277 assert_return(!journal_pid_changed(j
), -ECHILD
);
2278 assert_return(field
, -EINVAL
);
2279 assert_return(data
, -EINVAL
);
2280 assert_return(size
, -EINVAL
);
2281 assert_return(field_is_valid(field
), -EINVAL
);
2283 f
= j
->current_file
;
2285 return -EADDRNOTAVAIL
;
2287 if (f
->current_offset
<= 0)
2288 return -EADDRNOTAVAIL
;
2290 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2294 field_length
= strlen(field
);
2296 n
= journal_file_entry_n_items(o
);
2297 for (i
= 0; i
< n
; i
++) {
2303 p
= le64toh(o
->entry
.items
[i
].object_offset
);
2304 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &d
);
2305 if (IN_SET(r
, -EADDRNOTAVAIL
, -EBADMSG
)) {
2306 log_debug_errno(r
, "Entry item %"PRIu64
" data object is bad, skipping over it: %m", i
);
2312 l
= le64toh(d
->object
.size
) - offsetof(Object
, data
.payload
);
2314 compression
= d
->object
.flags
& OBJECT_COMPRESSION_MASK
;
2316 #if HAVE_COMPRESSION
2317 r
= decompress_startswith(compression
,
2319 &f
->compress_buffer
,
2320 field
, field_length
, '=');
2322 log_debug_errno(r
, "Cannot decompress %s object of length %"PRIu64
" at offset "OFSfmt
": %m",
2323 object_compressed_to_string(compression
), l
, p
);
2328 r
= decompress_blob(compression
,
2330 &f
->compress_buffer
, &rsize
,
2335 *data
= f
->compress_buffer
;
2336 *size
= (size_t) rsize
;
2341 return -EPROTONOSUPPORT
;
2343 } else if (l
>= field_length
+1 &&
2344 memcmp(d
->data
.payload
, field
, field_length
) == 0 &&
2345 d
->data
.payload
[field_length
] == '=') {
2349 if ((uint64_t) t
!= l
)
2352 *data
= d
->data
.payload
;
2362 static int return_data(
2366 const void **ret_data
,
2376 l
= le64toh(READ_NOW(o
->object
.size
));
2377 if (l
< offsetof(Object
, data
.payload
))
2379 l
-= offsetof(Object
, data
.payload
);
2381 /* We can't read objects larger than 4G on a 32bit machine */
2383 if ((uint64_t) t
!= l
)
2386 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
2388 #if HAVE_COMPRESSION
2392 r
= decompress_blob(
2395 &f
->compress_buffer
, &rsize
,
2401 *ret_data
= f
->compress_buffer
;
2403 *ret_size
= (size_t) rsize
;
2405 return -EPROTONOSUPPORT
;
2409 *ret_data
= o
->data
.payload
;
2417 _public_
int sd_journal_enumerate_data(sd_journal
*j
, const void **data
, size_t *size
) {
2422 assert_return(j
, -EINVAL
);
2423 assert_return(!journal_pid_changed(j
), -ECHILD
);
2424 assert_return(data
, -EINVAL
);
2425 assert_return(size
, -EINVAL
);
2427 f
= j
->current_file
;
2429 return -EADDRNOTAVAIL
;
2431 if (f
->current_offset
<= 0)
2432 return -EADDRNOTAVAIL
;
2434 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2438 for (uint64_t n
= journal_file_entry_n_items(o
); j
->current_field
< n
; j
->current_field
++) {
2441 p
= le64toh(o
->entry
.items
[j
->current_field
].object_offset
);
2442 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
2443 if (IN_SET(r
, -EADDRNOTAVAIL
, -EBADMSG
)) {
2444 log_debug_errno(r
, "Entry item %"PRIu64
" data object is bad, skipping over it: %m", j
->current_field
);
2450 r
= return_data(j
, f
, o
, data
, size
);
2451 if (r
== -EBADMSG
) {
2452 log_debug("Entry item %"PRIu64
" data payload is bad, skipping over it.", j
->current_field
);
2466 _public_
int sd_journal_enumerate_available_data(sd_journal
*j
, const void **data
, size_t *size
) {
2470 r
= sd_journal_enumerate_data(j
, data
, size
);
2473 if (!JOURNAL_ERRNO_IS_UNAVAILABLE_FIELD(r
))
2475 j
->current_field
++; /* Try with the next field */
2479 _public_
void sd_journal_restart_data(sd_journal
*j
) {
2483 j
->current_field
= 0;
2486 static int reiterate_all_paths(sd_journal
*j
) {
2489 if (j
->no_new_files
)
2490 return add_current_paths(j
);
2492 if (j
->flags
& SD_JOURNAL_OS_ROOT
)
2493 return add_search_paths(j
);
2495 if (j
->toplevel_fd
>= 0)
2496 return add_root_directory(j
, NULL
, false);
2499 return add_root_directory(j
, j
->path
, true);
2501 return add_search_paths(j
);
2504 _public_
int sd_journal_get_fd(sd_journal
*j
) {
2507 assert_return(j
, -EINVAL
);
2508 assert_return(!journal_pid_changed(j
), -ECHILD
);
2511 return -EMEDIUMTYPE
;
2513 if (j
->inotify_fd
>= 0)
2514 return j
->inotify_fd
;
2516 r
= allocate_inotify(j
);
2520 log_debug("Reiterating files to get inotify watches established.");
2522 /* Iterate through all dirs again, to add them to the inotify */
2523 r
= reiterate_all_paths(j
);
2527 return j
->inotify_fd
;
2530 _public_
int sd_journal_get_events(sd_journal
*j
) {
2533 assert_return(j
, -EINVAL
);
2534 assert_return(!journal_pid_changed(j
), -ECHILD
);
2536 fd
= sd_journal_get_fd(j
);
2543 _public_
int sd_journal_get_timeout(sd_journal
*j
, uint64_t *timeout_usec
) {
2546 assert_return(j
, -EINVAL
);
2547 assert_return(!journal_pid_changed(j
), -ECHILD
);
2548 assert_return(timeout_usec
, -EINVAL
);
2550 fd
= sd_journal_get_fd(j
);
2554 if (!j
->on_network
) {
2555 *timeout_usec
= UINT64_MAX
;
2559 /* If we are on the network we need to regularly check for
2560 * changes manually */
2562 *timeout_usec
= j
->last_process_usec
+ JOURNAL_FILES_RECHECK_USEC
;
2566 static void process_q_overflow(sd_journal
*j
) {
2572 /* When the inotify queue overruns we need to enumerate and re-validate all journal files to bring our list
2573 * back in sync with what's on disk. For this we pick a new generation counter value. It'll be assigned to all
2574 * journal files we encounter. All journal files and all directories that don't carry it after reenumeration
2575 * are subject for unloading. */
2577 log_debug("Inotify queue overrun, reiterating everything.");
2580 (void) reiterate_all_paths(j
);
2582 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
2584 if (f
->last_seen_generation
== j
->generation
)
2587 log_debug("File '%s' hasn't been seen in this enumeration, removing.", f
->path
);
2588 remove_file_real(j
, f
);
2591 HASHMAP_FOREACH(m
, j
->directories_by_path
) {
2593 if (m
->last_seen_generation
== j
->generation
)
2596 if (m
->is_root
) /* Never GC root directories */
2599 log_debug("Directory '%s' hasn't been seen in this enumeration, removing.", f
->path
);
2600 remove_directory(j
, m
);
2603 log_debug("Reiteration complete.");
2606 static void process_inotify_event(sd_journal
*j
, const struct inotify_event
*e
) {
2612 if (e
->mask
& IN_Q_OVERFLOW
) {
2613 process_q_overflow(j
);
2617 /* Is this a subdirectory we watch? */
2618 d
= hashmap_get(j
->directories_by_wd
, INT_TO_PTR(e
->wd
));
2620 if (!(e
->mask
& IN_ISDIR
) && e
->len
> 0 &&
2621 (endswith(e
->name
, ".journal") ||
2622 endswith(e
->name
, ".journal~"))) {
2624 /* Event for a journal file */
2626 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
))
2627 (void) add_file_by_name(j
, d
->path
, e
->name
);
2628 else if (e
->mask
& (IN_DELETE
|IN_MOVED_FROM
|IN_UNMOUNT
))
2629 remove_file_by_name(j
, d
->path
, e
->name
);
2631 } else if (!d
->is_root
&& e
->len
== 0) {
2633 /* Event for a subdirectory */
2635 if (e
->mask
& (IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
))
2636 remove_directory(j
, d
);
2638 } else if (d
->is_root
&& (e
->mask
& IN_ISDIR
) && e
->len
> 0 && id128_is_valid(e
->name
)) {
2640 /* Event for root directory */
2642 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
))
2643 (void) add_directory(j
, d
->path
, e
->name
);
2649 if (e
->mask
& IN_IGNORED
)
2652 log_debug("Unexpected inotify event.");
2655 static int determine_change(sd_journal
*j
) {
2660 b
= j
->current_invalidate_counter
!= j
->last_invalidate_counter
;
2661 j
->last_invalidate_counter
= j
->current_invalidate_counter
;
2663 return b
? SD_JOURNAL_INVALIDATE
: SD_JOURNAL_APPEND
;
2666 _public_
int sd_journal_process(sd_journal
*j
) {
2667 bool got_something
= false;
2669 assert_return(j
, -EINVAL
);
2670 assert_return(!journal_pid_changed(j
), -ECHILD
);
2672 if (j
->inotify_fd
< 0) /* We have no inotify fd yet? Then there's noting to process. */
2675 j
->last_process_usec
= now(CLOCK_MONOTONIC
);
2676 j
->last_invalidate_counter
= j
->current_invalidate_counter
;
2679 union inotify_event_buffer buffer
;
2680 struct inotify_event
*e
;
2683 l
= read(j
->inotify_fd
, &buffer
, sizeof(buffer
));
2685 if (ERRNO_IS_TRANSIENT(errno
))
2686 return got_something
? determine_change(j
) : SD_JOURNAL_NOP
;
2691 got_something
= true;
2693 FOREACH_INOTIFY_EVENT(e
, buffer
, l
)
2694 process_inotify_event(j
, e
);
2698 _public_
int sd_journal_wait(sd_journal
*j
, uint64_t timeout_usec
) {
2702 assert_return(j
, -EINVAL
);
2703 assert_return(!journal_pid_changed(j
), -ECHILD
);
2705 if (j
->inotify_fd
< 0) {
2708 /* This is the first invocation, hence create the
2710 r
= sd_journal_get_fd(j
);
2714 /* Server might have done some vacuuming while we weren't watching.
2715 Get rid of the deleted files now so they don't stay around indefinitely. */
2716 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
2717 r
= journal_file_fstat(f
);
2719 remove_file_real(j
, f
);
2721 log_debug_errno(r
,"Failed to fstat() journal file '%s' : %m", f
->path
);
2726 /* The journal might have changed since the context
2727 * object was created and we weren't watching before,
2728 * hence don't wait for anything, and return
2730 return determine_change(j
);
2733 r
= sd_journal_get_timeout(j
, &t
);
2737 if (t
!= UINT64_MAX
) {
2738 t
= usec_sub_unsigned(t
, now(CLOCK_MONOTONIC
));
2740 if (timeout_usec
== UINT64_MAX
|| timeout_usec
> t
)
2745 r
= fd_wait_for_event(j
->inotify_fd
, POLLIN
, timeout_usec
);
2746 } while (r
== -EINTR
);
2751 return sd_journal_process(j
);
2754 _public_
int sd_journal_get_cutoff_realtime_usec(sd_journal
*j
, uint64_t *from
, uint64_t *to
) {
2757 uint64_t fmin
= 0, tmax
= 0;
2760 assert_return(j
, -EINVAL
);
2761 assert_return(!journal_pid_changed(j
), -ECHILD
);
2762 assert_return(from
|| to
, -EINVAL
);
2763 assert_return(from
!= to
, -EINVAL
);
2765 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
2768 r
= journal_file_get_cutoff_realtime_usec(f
, &fr
, &t
);
2781 fmin
= MIN(fr
, fmin
);
2782 tmax
= MAX(t
, tmax
);
2791 return first
? 0 : 1;
2794 _public_
int sd_journal_get_cutoff_monotonic_usec(
2800 uint64_t from
= UINT64_MAX
, to
= UINT64_MAX
;
2805 assert_return(j
, -EINVAL
);
2806 assert_return(!journal_pid_changed(j
), -ECHILD
);
2807 assert_return(ret_from
!= ret_to
, -EINVAL
);
2809 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
2812 r
= journal_file_get_cutoff_monotonic_usec(f
, boot_id
, &ff
, &tt
);
2821 from
= MIN(ff
, from
);
2838 void journal_print_header(sd_journal
*j
) {
2840 bool newline
= false;
2844 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
2850 journal_file_print_header(f
);
2854 _public_
int sd_journal_get_usage(sd_journal
*j
, uint64_t *ret
) {
2858 assert_return(j
, -EINVAL
);
2859 assert_return(!journal_pid_changed(j
), -ECHILD
);
2860 assert_return(ret
, -EINVAL
);
2862 ORDERED_HASHMAP_FOREACH(f
, j
->files
) {
2866 if (fstat(f
->fd
, &st
) < 0)
2869 b
= (uint64_t) st
.st_blocks
;
2870 if (b
> UINT64_MAX
/ 512)
2874 if (sum
> UINT64_MAX
- b
)
2883 _public_
int sd_journal_query_unique(sd_journal
*j
, const char *field
) {
2886 assert_return(j
, -EINVAL
);
2887 assert_return(!journal_pid_changed(j
), -ECHILD
);
2888 assert_return(!isempty(field
), -EINVAL
);
2889 assert_return(field_is_valid(field
), -EINVAL
);
2891 r
= free_and_strdup(&j
->unique_field
, field
);
2895 j
->unique_file
= NULL
;
2896 j
->unique_offset
= 0;
2897 j
->unique_file_lost
= false;
2902 _public_
int sd_journal_enumerate_unique(
2904 const void **ret_data
,
2909 assert_return(j
, -EINVAL
);
2910 assert_return(!journal_pid_changed(j
), -ECHILD
);
2911 assert_return(j
->unique_field
, -EINVAL
);
2913 k
= strlen(j
->unique_field
);
2915 if (!j
->unique_file
) {
2916 if (j
->unique_file_lost
)
2919 j
->unique_file
= ordered_hashmap_first(j
->files
);
2920 if (!j
->unique_file
)
2923 j
->unique_offset
= 0;
2934 /* Proceed to next data object in the field's linked list */
2935 if (j
->unique_offset
== 0) {
2936 r
= journal_file_find_field_object(j
->unique_file
, j
->unique_field
, k
, &o
, NULL
);
2940 j
->unique_offset
= r
> 0 ? le64toh(o
->field
.head_data_offset
) : 0;
2942 r
= journal_file_move_to_object(j
->unique_file
, OBJECT_DATA
, j
->unique_offset
, &o
);
2946 j
->unique_offset
= le64toh(o
->data
.next_field_offset
);
2949 /* We reached the end of the list? Then start again, with the next file */
2950 if (j
->unique_offset
== 0) {
2951 j
->unique_file
= ordered_hashmap_next(j
->files
, j
->unique_file
->path
);
2952 if (!j
->unique_file
)
2958 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2959 * instead, so that we can look at this data object at the same
2960 * time as one on another file */
2961 r
= journal_file_move_to_object(j
->unique_file
, OBJECT_UNUSED
, j
->unique_offset
, &o
);
2965 /* Let's do the type check by hand, since we used 0 context above. */
2966 if (o
->object
.type
!= OBJECT_DATA
)
2967 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
),
2968 "%s:offset " OFSfmt
": object has type %d, expected %d",
2969 j
->unique_file
->path
,
2971 o
->object
.type
, OBJECT_DATA
);
2973 r
= return_data(j
, j
->unique_file
, o
, &odata
, &ol
);
2977 /* Check if we have at least the field name and "=". */
2979 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
),
2980 "%s:offset " OFSfmt
": object has size %zu, expected at least %zu",
2981 j
->unique_file
->path
,
2982 j
->unique_offset
, ol
, k
+ 1);
2984 if (memcmp(odata
, j
->unique_field
, k
) != 0 || ((const char*) odata
)[k
] != '=')
2985 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
),
2986 "%s:offset " OFSfmt
": object does not start with \"%s=\"",
2987 j
->unique_file
->path
,
2991 /* OK, now let's see if we already returned this data object by checking if it exists in the
2992 * earlier traversed files. */
2994 ORDERED_HASHMAP_FOREACH(of
, j
->files
) {
2995 if (of
== j
->unique_file
)
2998 /* Skip this file it didn't have any fields indexed */
2999 if (JOURNAL_HEADER_CONTAINS(of
->header
, n_fields
) && le64toh(of
->header
->n_fields
) <= 0)
3002 /* We can reuse the hash from our current file only on old-style journal files
3003 * without keyed hashes. On new-style files we have to calculate the hash anew, to
3004 * take the per-file hash seed into consideration. */
3005 if (!JOURNAL_HEADER_KEYED_HASH(j
->unique_file
->header
) && !JOURNAL_HEADER_KEYED_HASH(of
->header
))
3006 r
= journal_file_find_data_object_with_hash(of
, odata
, ol
, le64toh(o
->data
.hash
), NULL
, NULL
);
3008 r
= journal_file_find_data_object(of
, odata
, ol
, NULL
, NULL
);
3020 r
= return_data(j
, j
->unique_file
, o
, ret_data
, ret_size
);
3028 _public_
int sd_journal_enumerate_available_unique(sd_journal
*j
, const void **data
, size_t *size
) {
3032 r
= sd_journal_enumerate_unique(j
, data
, size
);
3035 if (!JOURNAL_ERRNO_IS_UNAVAILABLE_FIELD(r
))
3037 /* Try with the next field. sd_journal_enumerate_unique() modifies state, so on the next try
3038 * we will access the next field. */
3042 _public_
void sd_journal_restart_unique(sd_journal
*j
) {
3046 j
->unique_file
= NULL
;
3047 j
->unique_offset
= 0;
3048 j
->unique_file_lost
= false;
3051 _public_
int sd_journal_enumerate_fields(sd_journal
*j
, const char **field
) {
3054 assert_return(j
, -EINVAL
);
3055 assert_return(!journal_pid_changed(j
), -ECHILD
);
3056 assert_return(field
, -EINVAL
);
3058 if (!j
->fields_file
) {
3059 if (j
->fields_file_lost
)
3062 j
->fields_file
= ordered_hashmap_first(j
->files
);
3063 if (!j
->fields_file
)
3066 j
->fields_hash_table_index
= 0;
3067 j
->fields_offset
= 0;
3071 JournalFile
*f
, *of
;
3079 if (j
->fields_offset
== 0) {
3082 /* We are not yet positioned at any field. Let's pick the first one */
3083 r
= journal_file_map_field_hash_table(f
);
3087 m
= le64toh(f
->header
->field_hash_table_size
) / sizeof(HashItem
);
3089 if (j
->fields_hash_table_index
>= m
) {
3090 /* Reached the end of the hash table, go to the next file. */
3095 j
->fields_offset
= le64toh(f
->field_hash_table
[j
->fields_hash_table_index
].head_hash_offset
);
3097 if (j
->fields_offset
!= 0)
3100 /* Empty hash table bucket, go to next one */
3101 j
->fields_hash_table_index
++;
3105 /* Proceed with next file */
3106 j
->fields_file
= ordered_hashmap_next(j
->files
, f
->path
);
3107 if (!j
->fields_file
) {
3112 j
->fields_offset
= 0;
3113 j
->fields_hash_table_index
= 0;
3118 /* We are already positioned at a field. If so, let's figure out the next field from it */
3120 r
= journal_file_move_to_object(f
, OBJECT_FIELD
, j
->fields_offset
, &o
);
3124 j
->fields_offset
= le64toh(o
->field
.next_hash_offset
);
3125 if (j
->fields_offset
== 0) {
3126 /* Reached the end of the hash table chain */
3127 j
->fields_hash_table_index
++;
3132 /* We use OBJECT_UNUSED here, so that the iterator below doesn't remove our mmap window */
3133 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, j
->fields_offset
, &o
);
3137 /* Because we used OBJECT_UNUSED above, we need to do our type check manually */
3138 if (o
->object
.type
!= OBJECT_FIELD
)
3139 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
),
3140 "%s:offset " OFSfmt
": object has type %i, expected %i",
3141 f
->path
, j
->fields_offset
,
3142 o
->object
.type
, OBJECT_FIELD
);
3144 sz
= le64toh(o
->object
.size
) - offsetof(Object
, field
.payload
);
3146 /* Let's see if we already returned this field name before. */
3148 ORDERED_HASHMAP_FOREACH(of
, j
->files
) {
3152 /* Skip this file it didn't have any fields indexed */
3153 if (JOURNAL_HEADER_CONTAINS(of
->header
, n_fields
) && le64toh(of
->header
->n_fields
) <= 0)
3156 if (!JOURNAL_HEADER_KEYED_HASH(f
->header
) && !JOURNAL_HEADER_KEYED_HASH(of
->header
))
3157 r
= journal_file_find_field_object_with_hash(of
, o
->field
.payload
, sz
,
3158 le64toh(o
->field
.hash
), NULL
, NULL
);
3160 r
= journal_file_find_field_object(of
, o
->field
.payload
, sz
, NULL
, NULL
);
3172 /* Check if this is really a valid string containing no NUL byte */
3173 if (memchr(o
->field
.payload
, 0, sz
))
3176 if (j
->data_threshold
> 0 && sz
> j
->data_threshold
)
3177 sz
= j
->data_threshold
;
3179 if (!GREEDY_REALLOC(j
->fields_buffer
, sz
+ 1))
3182 memcpy(j
->fields_buffer
, o
->field
.payload
, sz
);
3183 j
->fields_buffer
[sz
] = 0;
3185 if (!field_is_valid(j
->fields_buffer
))
3188 *field
= j
->fields_buffer
;
3193 _public_
void sd_journal_restart_fields(sd_journal
*j
) {
3197 j
->fields_file
= NULL
;
3198 j
->fields_hash_table_index
= 0;
3199 j
->fields_offset
= 0;
3200 j
->fields_file_lost
= false;
3203 _public_
int sd_journal_reliable_fd(sd_journal
*j
) {
3204 assert_return(j
, -EINVAL
);
3205 assert_return(!journal_pid_changed(j
), -ECHILD
);
3207 return !j
->on_network
;
3210 static char *lookup_field(const char *field
, void *userdata
) {
3211 sd_journal
*j
= userdata
;
3219 r
= sd_journal_get_data(j
, field
, &data
, &size
);
3221 size
> REPLACE_VAR_MAX
)
3222 return strdup(field
);
3224 d
= strlen(field
) + 1;
3226 return strndup((const char*) data
+ d
, size
- d
);
3229 _public_
int sd_journal_get_catalog(sd_journal
*j
, char **ret
) {
3233 _cleanup_free_
char *text
= NULL
, *cid
= NULL
;
3237 assert_return(j
, -EINVAL
);
3238 assert_return(!journal_pid_changed(j
), -ECHILD
);
3239 assert_return(ret
, -EINVAL
);
3241 r
= sd_journal_get_data(j
, "MESSAGE_ID", &data
, &size
);
3245 cid
= strndup((const char*) data
+ 11, size
- 11);
3249 r
= sd_id128_from_string(cid
, &id
);
3253 r
= catalog_get(CATALOG_DATABASE
, id
, &text
);
3257 t
= replace_var(text
, lookup_field
, j
);
3265 _public_
int sd_journal_get_catalog_for_message_id(sd_id128_t id
, char **ret
) {
3266 assert_return(ret
, -EINVAL
);
3268 return catalog_get(CATALOG_DATABASE
, id
, ret
);
3271 _public_
int sd_journal_set_data_threshold(sd_journal
*j
, size_t sz
) {
3272 assert_return(j
, -EINVAL
);
3273 assert_return(!journal_pid_changed(j
), -ECHILD
);
3275 j
->data_threshold
= sz
;
3279 _public_
int sd_journal_get_data_threshold(sd_journal
*j
, size_t *sz
) {
3280 assert_return(j
, -EINVAL
);
3281 assert_return(!journal_pid_changed(j
), -ECHILD
);
3282 assert_return(sz
, -EINVAL
);
3284 *sz
= j
->data_threshold
;
3288 _public_
int sd_journal_has_runtime_files(sd_journal
*j
) {
3289 assert_return(j
, -EINVAL
);
3291 return j
->has_runtime_files
;
3294 _public_
int sd_journal_has_persistent_files(sd_journal
*j
) {
3295 assert_return(j
, -EINVAL
);
3297 return j
->has_persistent_files
;