1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <linux/magic.h>
27 #include <sys/inotify.h>
31 #include "sd-journal.h"
33 #include "alloc-util.h"
36 #include "dirent-util.h"
39 #include "formats-util.h"
42 #include "hostname-util.h"
44 #include "journal-def.h"
45 #include "journal-file.h"
46 #include "journal-internal.h"
50 #include "path-util.h"
51 #include "replace-var.h"
52 #include "stat-util.h"
53 #include "stdio-util.h"
54 #include "string-util.h"
57 #define JOURNAL_FILES_MAX 7168
59 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
61 #define REPLACE_VAR_MAX 256
63 #define DEFAULT_DATA_THRESHOLD (64*1024)
65 static void remove_file_real(sd_journal
*j
, JournalFile
*f
);
67 static bool journal_pid_changed(sd_journal
*j
) {
70 /* We don't support people creating a journal object and
71 * keeping it around over a fork(). Let's complain. */
73 return j
->original_pid
!= getpid();
76 static int journal_put_error(sd_journal
*j
, int r
, const char *path
) {
80 /* Memorize an error we encountered, and store which
81 * file/directory it was generated from. Note that we store
82 * only *one* path per error code, as the error code is the
83 * key into the hashmap, and the path is the value. This means
84 * we keep track only of all error kinds, but not of all error
85 * locations. This has the benefit that the hashmap cannot
88 * We return an error here only if we didn't manage to
89 * memorize the real error. */
94 k
= hashmap_ensure_allocated(&j
->errors
, NULL
);
105 k
= hashmap_put(j
->errors
, INT_TO_PTR(r
), copy
);
118 static void detach_location(sd_journal
*j
) {
124 j
->current_file
= NULL
;
125 j
->current_field
= 0;
127 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
)
128 journal_file_reset_location(f
);
131 static void reset_location(sd_journal
*j
) {
135 zero(j
->current_location
);
138 static void init_location(Location
*l
, LocationType type
, JournalFile
*f
, Object
*o
) {
140 assert(type
== LOCATION_DISCRETE
|| type
== LOCATION_SEEK
);
142 assert(o
->object
.type
== OBJECT_ENTRY
);
145 l
->seqnum
= le64toh(o
->entry
.seqnum
);
146 l
->seqnum_id
= f
->header
->seqnum_id
;
147 l
->realtime
= le64toh(o
->entry
.realtime
);
148 l
->monotonic
= le64toh(o
->entry
.monotonic
);
149 l
->boot_id
= o
->entry
.boot_id
;
150 l
->xor_hash
= le64toh(o
->entry
.xor_hash
);
152 l
->seqnum_set
= l
->realtime_set
= l
->monotonic_set
= l
->xor_hash_set
= true;
155 static void set_location(sd_journal
*j
, JournalFile
*f
, Object
*o
) {
160 init_location(&j
->current_location
, LOCATION_DISCRETE
, f
, o
);
163 j
->current_field
= 0;
165 /* Let f know its candidate entry was picked. */
166 assert(f
->location_type
== LOCATION_SEEK
);
167 f
->location_type
= LOCATION_DISCRETE
;
170 static int match_is_valid(const void *data
, size_t size
) {
178 if (startswith(data
, "__"))
182 for (p
= b
; p
< b
+ size
; p
++) {
190 if (*p
>= 'A' && *p
<= 'Z')
193 if (*p
>= '0' && *p
<= '9')
202 static bool same_field(const void *_a
, size_t s
, const void *_b
, size_t t
) {
203 const uint8_t *a
= _a
, *b
= _b
;
206 for (j
= 0; j
< s
&& j
< t
; j
++) {
215 assert_not_reached("\"=\" not found");
218 static Match
*match_new(Match
*p
, MatchType t
) {
229 LIST_PREPEND(matches
, p
->matches
, m
);
235 static void match_free(Match
*m
) {
239 match_free(m
->matches
);
242 LIST_REMOVE(matches
, m
->parent
->matches
, m
);
248 static void match_free_if_empty(Match
*m
) {
249 if (!m
|| m
->matches
)
255 _public_
int sd_journal_add_match(sd_journal
*j
, const void *data
, size_t size
) {
256 Match
*l3
, *l4
, *add_here
= NULL
, *m
;
259 assert_return(j
, -EINVAL
);
260 assert_return(!journal_pid_changed(j
), -ECHILD
);
261 assert_return(data
, -EINVAL
);
266 assert_return(match_is_valid(data
, size
), -EINVAL
);
272 * level 4: concrete matches */
275 j
->level0
= match_new(NULL
, MATCH_AND_TERM
);
281 j
->level1
= match_new(j
->level0
, MATCH_OR_TERM
);
287 j
->level2
= match_new(j
->level1
, MATCH_AND_TERM
);
292 assert(j
->level0
->type
== MATCH_AND_TERM
);
293 assert(j
->level1
->type
== MATCH_OR_TERM
);
294 assert(j
->level2
->type
== MATCH_AND_TERM
);
296 le_hash
= htole64(hash64(data
, size
));
298 LIST_FOREACH(matches
, l3
, j
->level2
->matches
) {
299 assert(l3
->type
== MATCH_OR_TERM
);
301 LIST_FOREACH(matches
, l4
, l3
->matches
) {
302 assert(l4
->type
== MATCH_DISCRETE
);
304 /* Exactly the same match already? Then ignore
306 if (l4
->le_hash
== le_hash
&&
308 memcmp(l4
->data
, data
, size
) == 0)
311 /* Same field? Then let's add this to this OR term */
312 if (same_field(data
, size
, l4
->data
, l4
->size
)) {
323 add_here
= match_new(j
->level2
, MATCH_OR_TERM
);
328 m
= match_new(add_here
, MATCH_DISCRETE
);
332 m
->le_hash
= le_hash
;
334 m
->data
= memdup(data
, size
);
343 match_free_if_empty(add_here
);
344 match_free_if_empty(j
->level2
);
345 match_free_if_empty(j
->level1
);
346 match_free_if_empty(j
->level0
);
351 _public_
int sd_journal_add_conjunction(sd_journal
*j
) {
352 assert_return(j
, -EINVAL
);
353 assert_return(!journal_pid_changed(j
), -ECHILD
);
361 if (!j
->level1
->matches
)
370 _public_
int sd_journal_add_disjunction(sd_journal
*j
) {
371 assert_return(j
, -EINVAL
);
372 assert_return(!journal_pid_changed(j
), -ECHILD
);
383 if (!j
->level2
->matches
)
390 static char *match_make_string(Match
*m
) {
393 bool enclose
= false;
396 return strdup("none");
398 if (m
->type
== MATCH_DISCRETE
)
399 return strndup(m
->data
, m
->size
);
402 LIST_FOREACH(matches
, i
, m
->matches
) {
405 t
= match_make_string(i
);
412 k
= strjoin(p
, m
->type
== MATCH_OR_TERM
? " OR " : " AND ", t
, NULL
);
427 r
= strjoin("(", p
, ")", NULL
);
435 char *journal_make_match_string(sd_journal
*j
) {
438 return match_make_string(j
->level0
);
441 _public_
void sd_journal_flush_matches(sd_journal
*j
) {
446 match_free(j
->level0
);
448 j
->level0
= j
->level1
= j
->level2
= NULL
;
453 _pure_
static int compare_with_location(JournalFile
*f
, Location
*l
) {
456 assert(f
->location_type
== LOCATION_SEEK
);
457 assert(l
->type
== LOCATION_DISCRETE
|| l
->type
== LOCATION_SEEK
);
459 if (l
->monotonic_set
&&
460 sd_id128_equal(f
->current_boot_id
, l
->boot_id
) &&
462 f
->current_realtime
== l
->realtime
&&
464 f
->current_xor_hash
== l
->xor_hash
)
468 sd_id128_equal(f
->header
->seqnum_id
, l
->seqnum_id
)) {
470 if (f
->current_seqnum
< l
->seqnum
)
472 if (f
->current_seqnum
> l
->seqnum
)
476 if (l
->monotonic_set
&&
477 sd_id128_equal(f
->current_boot_id
, l
->boot_id
)) {
479 if (f
->current_monotonic
< l
->monotonic
)
481 if (f
->current_monotonic
> l
->monotonic
)
485 if (l
->realtime_set
) {
487 if (f
->current_realtime
< l
->realtime
)
489 if (f
->current_realtime
> l
->realtime
)
493 if (l
->xor_hash_set
) {
495 if (f
->current_xor_hash
< l
->xor_hash
)
497 if (f
->current_xor_hash
> l
->xor_hash
)
504 static int next_for_match(
508 uint64_t after_offset
,
509 direction_t direction
,
521 if (m
->type
== MATCH_DISCRETE
) {
524 r
= journal_file_find_data_object_with_hash(f
, m
->data
, m
->size
, le64toh(m
->le_hash
), NULL
, &dp
);
528 return journal_file_move_to_entry_by_offset_for_data(f
, dp
, after_offset
, direction
, ret
, offset
);
530 } else if (m
->type
== MATCH_OR_TERM
) {
533 /* Find the earliest match beyond after_offset */
535 LIST_FOREACH(matches
, i
, m
->matches
) {
538 r
= next_for_match(j
, i
, f
, after_offset
, direction
, NULL
, &cp
);
542 if (np
== 0 || (direction
== DIRECTION_DOWN
? cp
< np
: cp
> np
))
550 } else if (m
->type
== MATCH_AND_TERM
) {
551 Match
*i
, *last_moved
;
553 /* Always jump to the next matching entry and repeat
554 * this until we find an offset that matches for all
560 r
= next_for_match(j
, m
->matches
, f
, after_offset
, direction
, NULL
, &np
);
564 assert(direction
== DIRECTION_DOWN
? np
>= after_offset
: np
<= after_offset
);
565 last_moved
= m
->matches
;
567 LIST_LOOP_BUT_ONE(matches
, i
, m
->matches
, last_moved
) {
570 r
= next_for_match(j
, i
, f
, np
, direction
, NULL
, &cp
);
574 assert(direction
== DIRECTION_DOWN
? cp
>= np
: cp
<= np
);
575 if (direction
== DIRECTION_DOWN
? cp
> np
: cp
< np
) {
584 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, np
, &n
);
596 static int find_location_for_match(
600 direction_t direction
,
610 if (m
->type
== MATCH_DISCRETE
) {
613 r
= journal_file_find_data_object_with_hash(f
, m
->data
, m
->size
, le64toh(m
->le_hash
), NULL
, &dp
);
617 /* FIXME: missing: find by monotonic */
619 if (j
->current_location
.type
== LOCATION_HEAD
)
620 return journal_file_next_entry_for_data(f
, NULL
, 0, dp
, DIRECTION_DOWN
, ret
, offset
);
621 if (j
->current_location
.type
== LOCATION_TAIL
)
622 return journal_file_next_entry_for_data(f
, NULL
, 0, dp
, DIRECTION_UP
, ret
, offset
);
623 if (j
->current_location
.seqnum_set
&& sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
624 return journal_file_move_to_entry_by_seqnum_for_data(f
, dp
, j
->current_location
.seqnum
, direction
, ret
, offset
);
625 if (j
->current_location
.monotonic_set
) {
626 r
= journal_file_move_to_entry_by_monotonic_for_data(f
, dp
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, ret
, offset
);
630 if (j
->current_location
.realtime_set
)
631 return journal_file_move_to_entry_by_realtime_for_data(f
, dp
, j
->current_location
.realtime
, direction
, ret
, offset
);
633 return journal_file_next_entry_for_data(f
, NULL
, 0, dp
, direction
, ret
, offset
);
635 } else if (m
->type
== MATCH_OR_TERM
) {
640 /* Find the earliest match */
642 LIST_FOREACH(matches
, i
, m
->matches
) {
645 r
= find_location_for_match(j
, i
, f
, direction
, NULL
, &cp
);
649 if (np
== 0 || (direction
== DIRECTION_DOWN
? np
> cp
: np
< cp
))
657 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, np
, &n
);
672 assert(m
->type
== MATCH_AND_TERM
);
674 /* First jump to the last match, and then find the
675 * next one where all matches match */
680 LIST_FOREACH(matches
, i
, m
->matches
) {
683 r
= find_location_for_match(j
, i
, f
, direction
, NULL
, &cp
);
687 if (np
== 0 || (direction
== DIRECTION_DOWN
? cp
> np
: cp
< np
))
691 return next_for_match(j
, m
, f
, np
, direction
, ret
, offset
);
695 static int find_location_with_matches(
698 direction_t direction
,
710 /* No matches is simple */
712 if (j
->current_location
.type
== LOCATION_HEAD
)
713 return journal_file_next_entry(f
, 0, DIRECTION_DOWN
, ret
, offset
);
714 if (j
->current_location
.type
== LOCATION_TAIL
)
715 return journal_file_next_entry(f
, 0, DIRECTION_UP
, ret
, offset
);
716 if (j
->current_location
.seqnum_set
&& sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
717 return journal_file_move_to_entry_by_seqnum(f
, j
->current_location
.seqnum
, direction
, ret
, offset
);
718 if (j
->current_location
.monotonic_set
) {
719 r
= journal_file_move_to_entry_by_monotonic(f
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, ret
, offset
);
723 if (j
->current_location
.realtime_set
)
724 return journal_file_move_to_entry_by_realtime(f
, j
->current_location
.realtime
, direction
, ret
, offset
);
726 return journal_file_next_entry(f
, 0, direction
, ret
, offset
);
728 return find_location_for_match(j
, j
->level0
, f
, direction
, ret
, offset
);
731 static int next_with_matches(
734 direction_t direction
,
743 /* No matches is easy. We simple advance the file
746 return journal_file_next_entry(f
, f
->current_offset
, direction
, ret
, offset
);
748 /* If we have a match then we look for the next matching entry
749 * with an offset at least one step larger */
750 return next_for_match(j
, j
->level0
, f
,
751 direction
== DIRECTION_DOWN
? f
->current_offset
+ 1
752 : f
->current_offset
- 1,
753 direction
, ret
, offset
);
756 static int next_beyond_location(sd_journal
*j
, JournalFile
*f
, direction_t direction
) {
758 uint64_t cp
, n_entries
;
764 n_entries
= le64toh(f
->header
->n_entries
);
766 /* If we hit EOF before, we don't need to look into this file again
767 * unless direction changed or new entries appeared. */
768 if (f
->last_direction
== direction
&& f
->location_type
== LOCATION_TAIL
&&
769 n_entries
== f
->last_n_entries
)
772 f
->last_n_entries
= n_entries
;
774 if (f
->last_direction
== direction
&& f
->current_offset
> 0) {
775 /* LOCATION_SEEK here means we did the work in a previous
776 * iteration and the current location already points to a
777 * candidate entry. */
778 if (f
->location_type
!= LOCATION_SEEK
) {
779 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
783 journal_file_save_location(f
, c
, cp
);
786 f
->last_direction
= direction
;
788 r
= find_location_with_matches(j
, f
, direction
, &c
, &cp
);
792 journal_file_save_location(f
, c
, cp
);
795 /* OK, we found the spot, now let's advance until an entry
796 * that is actually different from what we were previously
797 * looking at. This is necessary to handle entries which exist
798 * in two (or more) journal files, and which shall all be
799 * suppressed but one. */
804 if (j
->current_location
.type
== LOCATION_DISCRETE
) {
807 k
= compare_with_location(f
, &j
->current_location
);
809 found
= direction
== DIRECTION_DOWN
? k
> 0 : k
< 0;
816 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
820 journal_file_save_location(f
, c
, cp
);
824 static int real_journal_next(sd_journal
*j
, direction_t direction
) {
825 JournalFile
*f
, *new_file
= NULL
;
830 assert_return(j
, -EINVAL
);
831 assert_return(!journal_pid_changed(j
), -ECHILD
);
833 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
836 r
= next_beyond_location(j
, f
, direction
);
838 log_debug_errno(r
, "Can't iterate through %s, ignoring: %m", f
->path
);
839 remove_file_real(j
, f
);
842 f
->location_type
= LOCATION_TAIL
;
851 k
= journal_file_compare_locations(f
, new_file
);
853 found
= direction
== DIRECTION_DOWN
? k
< 0 : k
> 0;
863 r
= journal_file_move_to_object(new_file
, OBJECT_ENTRY
, new_file
->current_offset
, &o
);
867 set_location(j
, new_file
, o
);
872 _public_
int sd_journal_next(sd_journal
*j
) {
873 return real_journal_next(j
, DIRECTION_DOWN
);
876 _public_
int sd_journal_previous(sd_journal
*j
) {
877 return real_journal_next(j
, DIRECTION_UP
);
880 static int real_journal_next_skip(sd_journal
*j
, direction_t direction
, uint64_t skip
) {
883 assert_return(j
, -EINVAL
);
884 assert_return(!journal_pid_changed(j
), -ECHILD
);
887 /* If this is not a discrete skip, then at least
888 * resolve the current location */
889 if (j
->current_location
.type
!= LOCATION_DISCRETE
)
890 return real_journal_next(j
, direction
);
896 r
= real_journal_next(j
, direction
);
910 _public_
int sd_journal_next_skip(sd_journal
*j
, uint64_t skip
) {
911 return real_journal_next_skip(j
, DIRECTION_DOWN
, skip
);
914 _public_
int sd_journal_previous_skip(sd_journal
*j
, uint64_t skip
) {
915 return real_journal_next_skip(j
, DIRECTION_UP
, skip
);
918 _public_
int sd_journal_get_cursor(sd_journal
*j
, char **cursor
) {
921 char bid
[33], sid
[33];
923 assert_return(j
, -EINVAL
);
924 assert_return(!journal_pid_changed(j
), -ECHILD
);
925 assert_return(cursor
, -EINVAL
);
927 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
928 return -EADDRNOTAVAIL
;
930 r
= journal_file_move_to_object(j
->current_file
, OBJECT_ENTRY
, j
->current_file
->current_offset
, &o
);
934 sd_id128_to_string(j
->current_file
->header
->seqnum_id
, sid
);
935 sd_id128_to_string(o
->entry
.boot_id
, bid
);
938 "s=%s;i=%"PRIx64
";b=%s;m=%"PRIx64
";t=%"PRIx64
";x=%"PRIx64
,
939 sid
, le64toh(o
->entry
.seqnum
),
940 bid
, le64toh(o
->entry
.monotonic
),
941 le64toh(o
->entry
.realtime
),
942 le64toh(o
->entry
.xor_hash
)) < 0)
948 _public_
int sd_journal_seek_cursor(sd_journal
*j
, const char *cursor
) {
949 const char *word
, *state
;
951 unsigned long long seqnum
, monotonic
, realtime
, xor_hash
;
953 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
;
961 assert_return(j
, -EINVAL
);
962 assert_return(!journal_pid_changed(j
), -ECHILD
);
963 assert_return(!isempty(cursor
), -EINVAL
);
965 FOREACH_WORD_SEPARATOR(word
, l
, cursor
, ";", state
) {
969 if (l
< 2 || word
[1] != '=')
972 item
= strndup(word
, l
);
979 seqnum_id_set
= true;
980 k
= sd_id128_from_string(item
+2, &seqnum_id
);
985 if (sscanf(item
+2, "%llx", &seqnum
) != 1)
991 k
= sd_id128_from_string(item
+2, &boot_id
);
995 monotonic_set
= true;
996 if (sscanf(item
+2, "%llx", &monotonic
) != 1)
1001 realtime_set
= true;
1002 if (sscanf(item
+2, "%llx", &realtime
) != 1)
1007 xor_hash_set
= true;
1008 if (sscanf(item
+2, "%llx", &xor_hash
) != 1)
1019 if ((!seqnum_set
|| !seqnum_id_set
) &&
1020 (!monotonic_set
|| !boot_id_set
) &&
1026 j
->current_location
.type
= LOCATION_SEEK
;
1029 j
->current_location
.realtime
= (uint64_t) realtime
;
1030 j
->current_location
.realtime_set
= true;
1033 if (seqnum_set
&& seqnum_id_set
) {
1034 j
->current_location
.seqnum
= (uint64_t) seqnum
;
1035 j
->current_location
.seqnum_id
= seqnum_id
;
1036 j
->current_location
.seqnum_set
= true;
1039 if (monotonic_set
&& boot_id_set
) {
1040 j
->current_location
.monotonic
= (uint64_t) monotonic
;
1041 j
->current_location
.boot_id
= boot_id
;
1042 j
->current_location
.monotonic_set
= true;
1046 j
->current_location
.xor_hash
= (uint64_t) xor_hash
;
1047 j
->current_location
.xor_hash_set
= true;
1053 _public_
int sd_journal_test_cursor(sd_journal
*j
, const char *cursor
) {
1057 assert_return(j
, -EINVAL
);
1058 assert_return(!journal_pid_changed(j
), -ECHILD
);
1059 assert_return(!isempty(cursor
), -EINVAL
);
1061 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
1062 return -EADDRNOTAVAIL
;
1064 r
= journal_file_move_to_object(j
->current_file
, OBJECT_ENTRY
, j
->current_file
->current_offset
, &o
);
1069 _cleanup_free_
char *item
= NULL
;
1070 unsigned long long ll
;
1074 r
= extract_first_word(&cursor
, &item
, ";", EXTRACT_DONT_COALESCE_SEPARATORS
);
1081 if (strlen(item
) < 2 || item
[1] != '=')
1087 k
= sd_id128_from_string(item
+2, &id
);
1090 if (!sd_id128_equal(id
, j
->current_file
->header
->seqnum_id
))
1095 if (sscanf(item
+2, "%llx", &ll
) != 1)
1097 if (ll
!= le64toh(o
->entry
.seqnum
))
1102 k
= sd_id128_from_string(item
+2, &id
);
1105 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
1110 if (sscanf(item
+2, "%llx", &ll
) != 1)
1112 if (ll
!= le64toh(o
->entry
.monotonic
))
1117 if (sscanf(item
+2, "%llx", &ll
) != 1)
1119 if (ll
!= le64toh(o
->entry
.realtime
))
1124 if (sscanf(item
+2, "%llx", &ll
) != 1)
1126 if (ll
!= le64toh(o
->entry
.xor_hash
))
1136 _public_
int sd_journal_seek_monotonic_usec(sd_journal
*j
, sd_id128_t boot_id
, uint64_t usec
) {
1137 assert_return(j
, -EINVAL
);
1138 assert_return(!journal_pid_changed(j
), -ECHILD
);
1141 j
->current_location
.type
= LOCATION_SEEK
;
1142 j
->current_location
.boot_id
= boot_id
;
1143 j
->current_location
.monotonic
= usec
;
1144 j
->current_location
.monotonic_set
= true;
1149 _public_
int sd_journal_seek_realtime_usec(sd_journal
*j
, uint64_t usec
) {
1150 assert_return(j
, -EINVAL
);
1151 assert_return(!journal_pid_changed(j
), -ECHILD
);
1154 j
->current_location
.type
= LOCATION_SEEK
;
1155 j
->current_location
.realtime
= usec
;
1156 j
->current_location
.realtime_set
= true;
1161 _public_
int sd_journal_seek_head(sd_journal
*j
) {
1162 assert_return(j
, -EINVAL
);
1163 assert_return(!journal_pid_changed(j
), -ECHILD
);
1166 j
->current_location
.type
= LOCATION_HEAD
;
1171 _public_
int sd_journal_seek_tail(sd_journal
*j
) {
1172 assert_return(j
, -EINVAL
);
1173 assert_return(!journal_pid_changed(j
), -ECHILD
);
1176 j
->current_location
.type
= LOCATION_TAIL
;
1181 static void check_network(sd_journal
*j
, int fd
) {
1189 if (fstatfs(fd
, &sfs
) < 0)
1193 F_TYPE_EQUAL(sfs
.f_type
, CIFS_MAGIC_NUMBER
) ||
1194 F_TYPE_EQUAL(sfs
.f_type
, CODA_SUPER_MAGIC
) ||
1195 F_TYPE_EQUAL(sfs
.f_type
, NCP_SUPER_MAGIC
) ||
1196 F_TYPE_EQUAL(sfs
.f_type
, NFS_SUPER_MAGIC
) ||
1197 F_TYPE_EQUAL(sfs
.f_type
, SMB_SUPER_MAGIC
);
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 streq(filename
, full
) ||
1208 streq(filename
, tilded
) ||
1209 startswith(filename
, atted
);
1212 static bool file_type_wanted(int flags
, const char *filename
) {
1215 if (!endswith(filename
, ".journal") && !endswith(filename
, ".journal~"))
1218 /* no flags set → every type is OK */
1219 if (!(flags
& (SD_JOURNAL_SYSTEM
| SD_JOURNAL_CURRENT_USER
)))
1222 if (flags
& SD_JOURNAL_SYSTEM
&& file_has_type_prefix("system", filename
))
1225 if (flags
& SD_JOURNAL_CURRENT_USER
) {
1226 char prefix
[5 + DECIMAL_STR_MAX(uid_t
) + 1];
1228 xsprintf(prefix
, "user-"UID_FMT
, getuid());
1230 if (file_has_type_prefix(prefix
, filename
))
1237 static int add_any_file(sd_journal
*j
, const char *path
) {
1238 JournalFile
*f
= NULL
;
1244 if (ordered_hashmap_get(j
->files
, path
))
1247 if (ordered_hashmap_size(j
->files
) >= JOURNAL_FILES_MAX
) {
1248 log_debug("Too many open journal files, not adding %s.", path
);
1253 r
= journal_file_open(path
, O_RDONLY
, 0, false, false, NULL
, j
->mmap
, NULL
, &f
);
1255 log_debug_errno(r
, "Failed to open journal file %s: %m", path
);
1259 /* journal_file_dump(f); */
1261 r
= ordered_hashmap_put(j
->files
, f
->path
, f
);
1263 journal_file_close(f
);
1267 log_debug("File %s added.", f
->path
);
1269 check_network(j
, f
->fd
);
1271 j
->current_invalidate_counter
++;
1276 k
= journal_put_error(j
, r
, path
);
1283 static int add_file(sd_journal
*j
, const char *prefix
, const char *filename
) {
1290 if (j
->no_new_files
||
1291 !file_type_wanted(j
->flags
, filename
))
1294 path
= strjoina(prefix
, "/", filename
);
1295 return add_any_file(j
, path
);
1298 static void remove_file(sd_journal
*j
, const char *prefix
, const char *filename
) {
1306 path
= strjoina(prefix
, "/", filename
);
1307 f
= ordered_hashmap_get(j
->files
, path
);
1311 remove_file_real(j
, f
);
1314 static void remove_file_real(sd_journal
*j
, JournalFile
*f
) {
1318 ordered_hashmap_remove(j
->files
, f
->path
);
1320 log_debug("File %s removed.", f
->path
);
1322 if (j
->current_file
== f
) {
1323 j
->current_file
= NULL
;
1324 j
->current_field
= 0;
1327 if (j
->unique_file
== f
) {
1328 /* Jump to the next unique_file or NULL if that one was last */
1329 j
->unique_file
= ordered_hashmap_next(j
->files
, j
->unique_file
->path
);
1330 j
->unique_offset
= 0;
1331 if (!j
->unique_file
)
1332 j
->unique_file_lost
= true;
1335 journal_file_close(f
);
1337 j
->current_invalidate_counter
++;
1340 static int dirname_is_machine_id(const char *fn
) {
1341 sd_id128_t id
, machine
;
1344 r
= sd_id128_get_machine(&machine
);
1348 r
= sd_id128_from_string(fn
, &id
);
1352 return sd_id128_equal(id
, machine
);
1355 static int add_directory(sd_journal
*j
, const char *prefix
, const char *dirname
) {
1356 _cleanup_free_
char *path
= NULL
;
1357 _cleanup_closedir_
DIR *d
= NULL
;
1358 struct dirent
*de
= NULL
;
1366 log_debug("Considering %s/%s.", prefix
, dirname
);
1368 if ((j
->flags
& SD_JOURNAL_LOCAL_ONLY
) &&
1369 !(dirname_is_machine_id(dirname
) > 0 || path_startswith(prefix
, "/run")))
1372 path
= strjoin(prefix
, "/", dirname
, NULL
);
1380 r
= log_debug_errno(errno
, "Failed to open directory %s: %m", path
);
1384 m
= hashmap_get(j
->directories_by_path
, path
);
1386 m
= new0(Directory
, 1);
1395 if (hashmap_put(j
->directories_by_path
, m
->path
, m
) < 0) {
1401 path
= NULL
; /* avoid freeing in cleanup */
1402 j
->current_invalidate_counter
++;
1404 log_debug("Directory %s added.", m
->path
);
1406 } else if (m
->is_root
)
1409 if (m
->wd
<= 0 && j
->inotify_fd
>= 0) {
1411 m
->wd
= inotify_add_watch(j
->inotify_fd
, m
->path
,
1412 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1413 IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
|IN_MOVED_FROM
|
1416 if (m
->wd
> 0 && hashmap_put(j
->directories_by_wd
, INT_TO_PTR(m
->wd
), m
) < 0)
1417 inotify_rm_watch(j
->inotify_fd
, m
->wd
);
1420 FOREACH_DIRENT_ALL(de
, d
, return log_debug_errno(errno
, "Failed to read directory %s: %m", m
->path
)) {
1422 if (dirent_is_file_with_suffix(de
, ".journal") ||
1423 dirent_is_file_with_suffix(de
, ".journal~"))
1424 (void) add_file(j
, m
->path
, de
->d_name
);
1427 check_network(j
, dirfd(d
));
1432 k
= journal_put_error(j
, r
, path
?: dirname
);
1439 static int add_root_directory(sd_journal
*j
, const char *p
, bool missing_ok
) {
1440 _cleanup_closedir_
DIR *d
= NULL
;
1448 if ((j
->flags
& SD_JOURNAL_RUNTIME_ONLY
) &&
1449 !path_startswith(p
, "/run"))
1453 p
= strjoina(j
->prefix
, p
);
1457 if (errno
== ENOENT
&& missing_ok
)
1460 r
= log_debug_errno(errno
, "Failed to open root directory %s: %m", p
);
1464 m
= hashmap_get(j
->directories_by_path
, p
);
1466 m
= new0(Directory
, 1);
1473 m
->path
= strdup(p
);
1480 if (hashmap_put(j
->directories_by_path
, m
->path
, m
) < 0) {
1487 j
->current_invalidate_counter
++;
1489 log_debug("Root directory %s added.", m
->path
);
1491 } else if (!m
->is_root
)
1494 if (m
->wd
<= 0 && j
->inotify_fd
>= 0) {
1496 m
->wd
= inotify_add_watch(j
->inotify_fd
, m
->path
,
1497 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1500 if (m
->wd
> 0 && hashmap_put(j
->directories_by_wd
, INT_TO_PTR(m
->wd
), m
) < 0)
1501 inotify_rm_watch(j
->inotify_fd
, m
->wd
);
1504 if (j
->no_new_files
)
1507 FOREACH_DIRENT_ALL(de
, d
, return log_debug_errno(errno
, "Failed to read directory %s: %m", m
->path
)) {
1510 if (dirent_is_file_with_suffix(de
, ".journal") ||
1511 dirent_is_file_with_suffix(de
, ".journal~"))
1512 (void) add_file(j
, m
->path
, de
->d_name
);
1513 else if (IN_SET(de
->d_type
, DT_DIR
, DT_LNK
, DT_UNKNOWN
) &&
1514 sd_id128_from_string(de
->d_name
, &id
) >= 0)
1515 (void) add_directory(j
, m
->path
, de
->d_name
);
1518 check_network(j
, dirfd(d
));
1523 k
= journal_put_error(j
, r
, p
);
1530 static void remove_directory(sd_journal
*j
, Directory
*d
) {
1534 hashmap_remove(j
->directories_by_wd
, INT_TO_PTR(d
->wd
));
1536 if (j
->inotify_fd
>= 0)
1537 inotify_rm_watch(j
->inotify_fd
, d
->wd
);
1540 hashmap_remove(j
->directories_by_path
, d
->path
);
1543 log_debug("Root directory %s removed.", d
->path
);
1545 log_debug("Directory %s removed.", d
->path
);
1551 static int add_search_paths(sd_journal
*j
) {
1553 static const char search_paths
[] =
1554 "/run/log/journal\0"
1555 "/var/log/journal\0";
1560 /* We ignore most errors here, since the idea is to only open
1561 * what's actually accessible, and ignore the rest. */
1563 NULSTR_FOREACH(p
, search_paths
)
1564 (void) add_root_directory(j
, p
, true);
1569 static int add_current_paths(sd_journal
*j
) {
1574 assert(j
->no_new_files
);
1576 /* Simply adds all directories for files we have open as
1577 * "root" directories. We don't expect errors here, so we
1578 * treat them as fatal. */
1580 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
1581 _cleanup_free_
char *dir
;
1584 dir
= dirname_malloc(f
->path
);
1588 r
= add_root_directory(j
, dir
, true);
1596 static int allocate_inotify(sd_journal
*j
) {
1599 if (j
->inotify_fd
< 0) {
1600 j
->inotify_fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
1601 if (j
->inotify_fd
< 0)
1605 if (!j
->directories_by_wd
) {
1606 j
->directories_by_wd
= hashmap_new(NULL
);
1607 if (!j
->directories_by_wd
)
1614 static sd_journal
*journal_new(int flags
, const char *path
) {
1617 j
= new0(sd_journal
, 1);
1621 j
->original_pid
= getpid();
1624 j
->data_threshold
= DEFAULT_DATA_THRESHOLD
;
1627 j
->path
= strdup(path
);
1632 j
->files
= ordered_hashmap_new(&string_hash_ops
);
1633 j
->directories_by_path
= hashmap_new(&string_hash_ops
);
1634 j
->mmap
= mmap_cache_new();
1635 if (!j
->files
|| !j
->directories_by_path
|| !j
->mmap
)
1641 sd_journal_close(j
);
1645 _public_
int sd_journal_open(sd_journal
**ret
, int flags
) {
1649 assert_return(ret
, -EINVAL
);
1650 assert_return((flags
& ~(SD_JOURNAL_LOCAL_ONLY
|SD_JOURNAL_RUNTIME_ONLY
|SD_JOURNAL_SYSTEM
|SD_JOURNAL_CURRENT_USER
)) == 0, -EINVAL
);
1652 j
= journal_new(flags
, NULL
);
1656 r
= add_search_paths(j
);
1664 sd_journal_close(j
);
1669 _public_
int sd_journal_open_container(sd_journal
**ret
, const char *machine
, int flags
) {
1670 _cleanup_free_
char *root
= NULL
, *class = NULL
;
1675 assert_return(machine
, -EINVAL
);
1676 assert_return(ret
, -EINVAL
);
1677 assert_return((flags
& ~(SD_JOURNAL_LOCAL_ONLY
|SD_JOURNAL_SYSTEM
)) == 0, -EINVAL
);
1678 assert_return(machine_name_is_valid(machine
), -EINVAL
);
1680 p
= strjoina("/run/systemd/machines/", machine
);
1681 r
= parse_env_file(p
, NEWLINE
, "ROOT", &root
, "CLASS", &class, NULL
);
1689 if (!streq_ptr(class, "container"))
1692 j
= journal_new(flags
, NULL
);
1699 r
= add_search_paths(j
);
1707 sd_journal_close(j
);
1711 _public_
int sd_journal_open_directory(sd_journal
**ret
, const char *path
, int flags
) {
1715 assert_return(ret
, -EINVAL
);
1716 assert_return(path
, -EINVAL
);
1717 assert_return(flags
== 0, -EINVAL
);
1719 j
= journal_new(flags
, path
);
1723 r
= add_root_directory(j
, path
, false);
1731 sd_journal_close(j
);
1736 _public_
int sd_journal_open_files(sd_journal
**ret
, const char **paths
, int flags
) {
1741 assert_return(ret
, -EINVAL
);
1742 assert_return(flags
== 0, -EINVAL
);
1744 j
= journal_new(flags
, NULL
);
1748 STRV_FOREACH(path
, paths
) {
1749 r
= add_any_file(j
, *path
);
1754 j
->no_new_files
= true;
1760 sd_journal_close(j
);
1765 _public_
void sd_journal_close(sd_journal
*j
) {
1773 sd_journal_flush_matches(j
);
1775 while ((f
= ordered_hashmap_steal_first(j
->files
)))
1776 journal_file_close(f
);
1778 ordered_hashmap_free(j
->files
);
1780 while ((d
= hashmap_first(j
->directories_by_path
)))
1781 remove_directory(j
, d
);
1783 while ((d
= hashmap_first(j
->directories_by_wd
)))
1784 remove_directory(j
, d
);
1786 hashmap_free(j
->directories_by_path
);
1787 hashmap_free(j
->directories_by_wd
);
1789 safe_close(j
->inotify_fd
);
1792 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j
->mmap
), mmap_cache_get_missed(j
->mmap
));
1793 mmap_cache_unref(j
->mmap
);
1796 while ((p
= hashmap_steal_first(j
->errors
)))
1798 hashmap_free(j
->errors
);
1802 free(j
->unique_field
);
1806 _public_
int sd_journal_get_realtime_usec(sd_journal
*j
, uint64_t *ret
) {
1811 assert_return(j
, -EINVAL
);
1812 assert_return(!journal_pid_changed(j
), -ECHILD
);
1813 assert_return(ret
, -EINVAL
);
1815 f
= j
->current_file
;
1817 return -EADDRNOTAVAIL
;
1819 if (f
->current_offset
<= 0)
1820 return -EADDRNOTAVAIL
;
1822 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1826 *ret
= le64toh(o
->entry
.realtime
);
1830 _public_
int sd_journal_get_monotonic_usec(sd_journal
*j
, uint64_t *ret
, sd_id128_t
*ret_boot_id
) {
1836 assert_return(j
, -EINVAL
);
1837 assert_return(!journal_pid_changed(j
), -ECHILD
);
1839 f
= j
->current_file
;
1841 return -EADDRNOTAVAIL
;
1843 if (f
->current_offset
<= 0)
1844 return -EADDRNOTAVAIL
;
1846 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1851 *ret_boot_id
= o
->entry
.boot_id
;
1853 r
= sd_id128_get_boot(&id
);
1857 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
1862 *ret
= le64toh(o
->entry
.monotonic
);
1867 static bool field_is_valid(const char *field
) {
1875 if (startswith(field
, "__"))
1878 for (p
= field
; *p
; p
++) {
1883 if (*p
>= 'A' && *p
<= 'Z')
1886 if (*p
>= '0' && *p
<= '9')
1895 _public_
int sd_journal_get_data(sd_journal
*j
, const char *field
, const void **data
, size_t *size
) {
1898 size_t field_length
;
1902 assert_return(j
, -EINVAL
);
1903 assert_return(!journal_pid_changed(j
), -ECHILD
);
1904 assert_return(field
, -EINVAL
);
1905 assert_return(data
, -EINVAL
);
1906 assert_return(size
, -EINVAL
);
1907 assert_return(field_is_valid(field
), -EINVAL
);
1909 f
= j
->current_file
;
1911 return -EADDRNOTAVAIL
;
1913 if (f
->current_offset
<= 0)
1914 return -EADDRNOTAVAIL
;
1916 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1920 field_length
= strlen(field
);
1922 n
= journal_file_entry_n_items(o
);
1923 for (i
= 0; i
< n
; i
++) {
1929 p
= le64toh(o
->entry
.items
[i
].object_offset
);
1930 le_hash
= o
->entry
.items
[i
].hash
;
1931 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
1935 if (le_hash
!= o
->data
.hash
)
1938 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
1940 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
1942 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1943 r
= decompress_startswith(compression
,
1945 &f
->compress_buffer
, &f
->compress_buffer_size
,
1946 field
, field_length
, '=');
1948 log_debug_errno(r
, "Cannot decompress %s object of length %zu at offset "OFSfmt
": %m",
1949 object_compressed_to_string(compression
), l
, p
);
1954 r
= decompress_blob(compression
,
1956 &f
->compress_buffer
, &f
->compress_buffer_size
, &rsize
,
1961 *data
= f
->compress_buffer
;
1962 *size
= (size_t) rsize
;
1967 return -EPROTONOSUPPORT
;
1969 } else if (l
>= field_length
+1 &&
1970 memcmp(o
->data
.payload
, field
, field_length
) == 0 &&
1971 o
->data
.payload
[field_length
] == '=') {
1975 if ((uint64_t) t
!= l
)
1978 *data
= o
->data
.payload
;
1984 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1992 static int return_data(sd_journal
*j
, JournalFile
*f
, Object
*o
, const void **data
, size_t *size
) {
1997 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
2000 /* We can't read objects larger than 4G on a 32bit machine */
2001 if ((uint64_t) t
!= l
)
2004 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
2006 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
2010 r
= decompress_blob(compression
,
2011 o
->data
.payload
, l
, &f
->compress_buffer
,
2012 &f
->compress_buffer_size
, &rsize
, j
->data_threshold
);
2016 *data
= f
->compress_buffer
;
2017 *size
= (size_t) rsize
;
2019 return -EPROTONOSUPPORT
;
2022 *data
= o
->data
.payload
;
2029 _public_
int sd_journal_enumerate_data(sd_journal
*j
, const void **data
, size_t *size
) {
2036 assert_return(j
, -EINVAL
);
2037 assert_return(!journal_pid_changed(j
), -ECHILD
);
2038 assert_return(data
, -EINVAL
);
2039 assert_return(size
, -EINVAL
);
2041 f
= j
->current_file
;
2043 return -EADDRNOTAVAIL
;
2045 if (f
->current_offset
<= 0)
2046 return -EADDRNOTAVAIL
;
2048 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2052 n
= journal_file_entry_n_items(o
);
2053 if (j
->current_field
>= n
)
2056 p
= le64toh(o
->entry
.items
[j
->current_field
].object_offset
);
2057 le_hash
= o
->entry
.items
[j
->current_field
].hash
;
2058 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
2062 if (le_hash
!= o
->data
.hash
)
2065 r
= return_data(j
, f
, o
, data
, size
);
2069 j
->current_field
++;
2074 _public_
void sd_journal_restart_data(sd_journal
*j
) {
2078 j
->current_field
= 0;
2081 _public_
int sd_journal_get_fd(sd_journal
*j
) {
2084 assert_return(j
, -EINVAL
);
2085 assert_return(!journal_pid_changed(j
), -ECHILD
);
2087 if (j
->inotify_fd
>= 0)
2088 return j
->inotify_fd
;
2090 r
= allocate_inotify(j
);
2094 /* Iterate through all dirs again, to add them to the
2096 if (j
->no_new_files
)
2097 r
= add_current_paths(j
);
2099 r
= add_root_directory(j
, j
->path
, true);
2101 r
= add_search_paths(j
);
2105 return j
->inotify_fd
;
2108 _public_
int sd_journal_get_events(sd_journal
*j
) {
2111 assert_return(j
, -EINVAL
);
2112 assert_return(!journal_pid_changed(j
), -ECHILD
);
2114 fd
= sd_journal_get_fd(j
);
2121 _public_
int sd_journal_get_timeout(sd_journal
*j
, uint64_t *timeout_usec
) {
2124 assert_return(j
, -EINVAL
);
2125 assert_return(!journal_pid_changed(j
), -ECHILD
);
2126 assert_return(timeout_usec
, -EINVAL
);
2128 fd
= sd_journal_get_fd(j
);
2132 if (!j
->on_network
) {
2133 *timeout_usec
= (uint64_t) -1;
2137 /* If we are on the network we need to regularly check for
2138 * changes manually */
2140 *timeout_usec
= j
->last_process_usec
+ JOURNAL_FILES_RECHECK_USEC
;
2144 static void process_inotify_event(sd_journal
*j
, struct inotify_event
*e
) {
2150 /* Is this a subdirectory we watch? */
2151 d
= hashmap_get(j
->directories_by_wd
, INT_TO_PTR(e
->wd
));
2155 if (!(e
->mask
& IN_ISDIR
) && e
->len
> 0 &&
2156 (endswith(e
->name
, ".journal") ||
2157 endswith(e
->name
, ".journal~"))) {
2159 /* Event for a journal file */
2161 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
))
2162 (void) add_file(j
, d
->path
, e
->name
);
2163 else if (e
->mask
& (IN_DELETE
|IN_MOVED_FROM
|IN_UNMOUNT
))
2164 remove_file(j
, d
->path
, e
->name
);
2166 } else if (!d
->is_root
&& e
->len
== 0) {
2168 /* Event for a subdirectory */
2170 if (e
->mask
& (IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
))
2171 remove_directory(j
, d
);
2173 } else if (d
->is_root
&& (e
->mask
& IN_ISDIR
) && e
->len
> 0 && sd_id128_from_string(e
->name
, &id
) >= 0) {
2175 /* Event for root directory */
2177 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
))
2178 (void) add_directory(j
, d
->path
, e
->name
);
2184 if (e
->mask
& IN_IGNORED
)
2187 log_debug("Unknown inotify event.");
2190 static int determine_change(sd_journal
*j
) {
2195 b
= j
->current_invalidate_counter
!= j
->last_invalidate_counter
;
2196 j
->last_invalidate_counter
= j
->current_invalidate_counter
;
2198 return b
? SD_JOURNAL_INVALIDATE
: SD_JOURNAL_APPEND
;
2201 _public_
int sd_journal_process(sd_journal
*j
) {
2202 bool got_something
= false;
2204 assert_return(j
, -EINVAL
);
2205 assert_return(!journal_pid_changed(j
), -ECHILD
);
2207 j
->last_process_usec
= now(CLOCK_MONOTONIC
);
2210 union inotify_event_buffer buffer
;
2211 struct inotify_event
*e
;
2214 l
= read(j
->inotify_fd
, &buffer
, sizeof(buffer
));
2216 if (errno
== EAGAIN
|| errno
== EINTR
)
2217 return got_something
? determine_change(j
) : SD_JOURNAL_NOP
;
2222 got_something
= true;
2224 FOREACH_INOTIFY_EVENT(e
, buffer
, l
)
2225 process_inotify_event(j
, e
);
2229 _public_
int sd_journal_wait(sd_journal
*j
, uint64_t timeout_usec
) {
2233 assert_return(j
, -EINVAL
);
2234 assert_return(!journal_pid_changed(j
), -ECHILD
);
2236 if (j
->inotify_fd
< 0) {
2238 /* This is the first invocation, hence create the
2240 r
= sd_journal_get_fd(j
);
2244 /* The journal might have changed since the context
2245 * object was created and we weren't watching before,
2246 * hence don't wait for anything, and return
2248 return determine_change(j
);
2251 r
= sd_journal_get_timeout(j
, &t
);
2255 if (t
!= (uint64_t) -1) {
2258 n
= now(CLOCK_MONOTONIC
);
2259 t
= t
> n
? t
- n
: 0;
2261 if (timeout_usec
== (uint64_t) -1 || timeout_usec
> t
)
2266 r
= fd_wait_for_event(j
->inotify_fd
, POLLIN
, timeout_usec
);
2267 } while (r
== -EINTR
);
2272 return sd_journal_process(j
);
2275 _public_
int sd_journal_get_cutoff_realtime_usec(sd_journal
*j
, uint64_t *from
, uint64_t *to
) {
2279 uint64_t fmin
= 0, tmax
= 0;
2282 assert_return(j
, -EINVAL
);
2283 assert_return(!journal_pid_changed(j
), -ECHILD
);
2284 assert_return(from
|| to
, -EINVAL
);
2285 assert_return(from
!= to
, -EINVAL
);
2287 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
2290 r
= journal_file_get_cutoff_realtime_usec(f
, &fr
, &t
);
2303 fmin
= MIN(fr
, fmin
);
2304 tmax
= MAX(t
, tmax
);
2313 return first
? 0 : 1;
2316 _public_
int sd_journal_get_cutoff_monotonic_usec(sd_journal
*j
, sd_id128_t boot_id
, uint64_t *from
, uint64_t *to
) {
2322 assert_return(j
, -EINVAL
);
2323 assert_return(!journal_pid_changed(j
), -ECHILD
);
2324 assert_return(from
|| to
, -EINVAL
);
2325 assert_return(from
!= to
, -EINVAL
);
2327 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
2330 r
= journal_file_get_cutoff_monotonic_usec(f
, boot_id
, &fr
, &t
);
2340 *from
= MIN(fr
, *from
);
2355 void journal_print_header(sd_journal
*j
) {
2358 bool newline
= false;
2362 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
2368 journal_file_print_header(f
);
2372 _public_
int sd_journal_get_usage(sd_journal
*j
, uint64_t *bytes
) {
2377 assert_return(j
, -EINVAL
);
2378 assert_return(!journal_pid_changed(j
), -ECHILD
);
2379 assert_return(bytes
, -EINVAL
);
2381 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
2384 if (fstat(f
->fd
, &st
) < 0)
2387 sum
+= (uint64_t) st
.st_blocks
* 512ULL;
2394 _public_
int sd_journal_query_unique(sd_journal
*j
, const char *field
) {
2397 assert_return(j
, -EINVAL
);
2398 assert_return(!journal_pid_changed(j
), -ECHILD
);
2399 assert_return(!isempty(field
), -EINVAL
);
2400 assert_return(field_is_valid(field
), -EINVAL
);
2406 free(j
->unique_field
);
2407 j
->unique_field
= f
;
2408 j
->unique_file
= NULL
;
2409 j
->unique_offset
= 0;
2410 j
->unique_file_lost
= false;
2415 _public_
int sd_journal_enumerate_unique(sd_journal
*j
, const void **data
, size_t *l
) {
2418 assert_return(j
, -EINVAL
);
2419 assert_return(!journal_pid_changed(j
), -ECHILD
);
2420 assert_return(data
, -EINVAL
);
2421 assert_return(l
, -EINVAL
);
2422 assert_return(j
->unique_field
, -EINVAL
);
2424 k
= strlen(j
->unique_field
);
2426 if (!j
->unique_file
) {
2427 if (j
->unique_file_lost
)
2430 j
->unique_file
= ordered_hashmap_first(j
->files
);
2431 if (!j
->unique_file
)
2434 j
->unique_offset
= 0;
2446 /* Proceed to next data object in the field's linked list */
2447 if (j
->unique_offset
== 0) {
2448 r
= journal_file_find_field_object(j
->unique_file
, j
->unique_field
, k
, &o
, NULL
);
2452 j
->unique_offset
= r
> 0 ? le64toh(o
->field
.head_data_offset
) : 0;
2454 r
= journal_file_move_to_object(j
->unique_file
, OBJECT_DATA
, j
->unique_offset
, &o
);
2458 j
->unique_offset
= le64toh(o
->data
.next_field_offset
);
2461 /* We reached the end of the list? Then start again, with the next file */
2462 if (j
->unique_offset
== 0) {
2463 j
->unique_file
= ordered_hashmap_next(j
->files
, j
->unique_file
->path
);
2464 if (!j
->unique_file
)
2470 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2471 * instead, so that we can look at this data object at the same
2472 * time as one on another file */
2473 r
= journal_file_move_to_object(j
->unique_file
, OBJECT_UNUSED
, j
->unique_offset
, &o
);
2477 /* Let's do the type check by hand, since we used 0 context above. */
2478 if (o
->object
.type
!= OBJECT_DATA
) {
2479 log_debug("%s:offset " OFSfmt
": object has type %d, expected %d",
2480 j
->unique_file
->path
, j
->unique_offset
,
2481 o
->object
.type
, OBJECT_DATA
);
2485 r
= return_data(j
, j
->unique_file
, o
, &odata
, &ol
);
2489 /* Check if we have at least the field name and "=". */
2491 log_debug("%s:offset " OFSfmt
": object has size %zu, expected at least %zu",
2492 j
->unique_file
->path
, j
->unique_offset
,
2497 if (memcmp(odata
, j
->unique_field
, k
) || ((const char*) odata
)[k
] != '=') {
2498 log_debug("%s:offset " OFSfmt
": object does not start with \"%s=\"",
2499 j
->unique_file
->path
, j
->unique_offset
,
2504 /* OK, now let's see if we already returned this data
2505 * object by checking if it exists in the earlier
2506 * traversed files. */
2508 ORDERED_HASHMAP_FOREACH(of
, j
->files
, i
) {
2512 if (of
== j
->unique_file
)
2515 /* Skip this file it didn't have any fields
2517 if (JOURNAL_HEADER_CONTAINS(of
->header
, n_fields
) &&
2518 le64toh(of
->header
->n_fields
) <= 0)
2521 r
= journal_file_find_data_object_with_hash(of
, odata
, ol
, le64toh(o
->data
.hash
), &oo
, &op
);
2532 r
= return_data(j
, j
->unique_file
, o
, data
, l
);
2540 _public_
void sd_journal_restart_unique(sd_journal
*j
) {
2544 j
->unique_file
= NULL
;
2545 j
->unique_offset
= 0;
2546 j
->unique_file_lost
= false;
2549 _public_
int sd_journal_reliable_fd(sd_journal
*j
) {
2550 assert_return(j
, -EINVAL
);
2551 assert_return(!journal_pid_changed(j
), -ECHILD
);
2553 return !j
->on_network
;
2556 static char *lookup_field(const char *field
, void *userdata
) {
2557 sd_journal
*j
= userdata
;
2565 r
= sd_journal_get_data(j
, field
, &data
, &size
);
2567 size
> REPLACE_VAR_MAX
)
2568 return strdup(field
);
2570 d
= strlen(field
) + 1;
2572 return strndup((const char*) data
+ d
, size
- d
);
2575 _public_
int sd_journal_get_catalog(sd_journal
*j
, char **ret
) {
2579 _cleanup_free_
char *text
= NULL
, *cid
= NULL
;
2583 assert_return(j
, -EINVAL
);
2584 assert_return(!journal_pid_changed(j
), -ECHILD
);
2585 assert_return(ret
, -EINVAL
);
2587 r
= sd_journal_get_data(j
, "MESSAGE_ID", &data
, &size
);
2591 cid
= strndup((const char*) data
+ 11, size
- 11);
2595 r
= sd_id128_from_string(cid
, &id
);
2599 r
= catalog_get(CATALOG_DATABASE
, id
, &text
);
2603 t
= replace_var(text
, lookup_field
, j
);
2611 _public_
int sd_journal_get_catalog_for_message_id(sd_id128_t id
, char **ret
) {
2612 assert_return(ret
, -EINVAL
);
2614 return catalog_get(CATALOG_DATABASE
, id
, ret
);
2617 _public_
int sd_journal_set_data_threshold(sd_journal
*j
, size_t sz
) {
2618 assert_return(j
, -EINVAL
);
2619 assert_return(!journal_pid_changed(j
), -ECHILD
);
2621 j
->data_threshold
= sz
;
2625 _public_
int sd_journal_get_data_threshold(sd_journal
*j
, size_t *sz
) {
2626 assert_return(j
, -EINVAL
);
2627 assert_return(!journal_pid_changed(j
), -ECHILD
);
2628 assert_return(sz
, -EINVAL
);
2630 *sz
= j
->data_threshold
;