1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <sys/inotify.h>
29 #include <linux/magic.h>
31 #include "sd-journal.h"
32 #include "journal-def.h"
33 #include "journal-file.h"
37 #include "path-util.h"
40 #include "journal-internal.h"
43 #include "replace-var.h"
45 #include "formats-util.h"
47 #define JOURNAL_FILES_MAX 7168
49 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
51 #define REPLACE_VAR_MAX 256
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();
66 /* We return an error here only if we didn't manage to
67 memorize the real error. */
68 static int set_put_error(sd_journal
*j
, int r
) {
74 k
= set_ensure_allocated(&j
->errors
, NULL
);
78 return set_put(j
->errors
, INT_TO_PTR(r
));
81 static void detach_location(sd_journal
*j
) {
87 j
->current_file
= NULL
;
90 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
)
91 journal_file_reset_location(f
);
94 static void reset_location(sd_journal
*j
) {
98 zero(j
->current_location
);
101 static void init_location(Location
*l
, LocationType type
, JournalFile
*f
, Object
*o
) {
103 assert(type
== LOCATION_DISCRETE
|| type
== LOCATION_SEEK
);
105 assert(o
->object
.type
== OBJECT_ENTRY
);
108 l
->seqnum
= le64toh(o
->entry
.seqnum
);
109 l
->seqnum_id
= f
->header
->seqnum_id
;
110 l
->realtime
= le64toh(o
->entry
.realtime
);
111 l
->monotonic
= le64toh(o
->entry
.monotonic
);
112 l
->boot_id
= o
->entry
.boot_id
;
113 l
->xor_hash
= le64toh(o
->entry
.xor_hash
);
115 l
->seqnum_set
= l
->realtime_set
= l
->monotonic_set
= l
->xor_hash_set
= true;
118 static void set_location(sd_journal
*j
, JournalFile
*f
, Object
*o
) {
123 init_location(&j
->current_location
, LOCATION_DISCRETE
, f
, o
);
126 j
->current_field
= 0;
128 /* Let f know its candidate entry was picked. */
129 assert(f
->location_type
== LOCATION_SEEK
);
130 f
->location_type
= LOCATION_DISCRETE
;
133 static int match_is_valid(const void *data
, size_t size
) {
141 if (startswith(data
, "__"))
145 for (p
= b
; p
< b
+ size
; p
++) {
153 if (*p
>= 'A' && *p
<= 'Z')
156 if (*p
>= '0' && *p
<= '9')
165 static bool same_field(const void *_a
, size_t s
, const void *_b
, size_t t
) {
166 const uint8_t *a
= _a
, *b
= _b
;
169 for (j
= 0; j
< s
&& j
< t
; j
++) {
178 assert_not_reached("\"=\" not found");
181 static Match
*match_new(Match
*p
, MatchType t
) {
192 LIST_PREPEND(matches
, p
->matches
, m
);
198 static void match_free(Match
*m
) {
202 match_free(m
->matches
);
205 LIST_REMOVE(matches
, m
->parent
->matches
, m
);
211 static void match_free_if_empty(Match
*m
) {
212 if (!m
|| m
->matches
)
218 _public_
int sd_journal_add_match(sd_journal
*j
, const void *data
, size_t size
) {
219 Match
*l3
, *l4
, *add_here
= NULL
, *m
;
222 assert_return(j
, -EINVAL
);
223 assert_return(!journal_pid_changed(j
), -ECHILD
);
224 assert_return(data
, -EINVAL
);
229 assert_return(match_is_valid(data
, size
), -EINVAL
);
235 * level 4: concrete matches */
238 j
->level0
= match_new(NULL
, MATCH_AND_TERM
);
244 j
->level1
= match_new(j
->level0
, MATCH_OR_TERM
);
250 j
->level2
= match_new(j
->level1
, MATCH_AND_TERM
);
255 assert(j
->level0
->type
== MATCH_AND_TERM
);
256 assert(j
->level1
->type
== MATCH_OR_TERM
);
257 assert(j
->level2
->type
== MATCH_AND_TERM
);
259 le_hash
= htole64(hash64(data
, size
));
261 LIST_FOREACH(matches
, l3
, j
->level2
->matches
) {
262 assert(l3
->type
== MATCH_OR_TERM
);
264 LIST_FOREACH(matches
, l4
, l3
->matches
) {
265 assert(l4
->type
== MATCH_DISCRETE
);
267 /* Exactly the same match already? Then ignore
269 if (l4
->le_hash
== le_hash
&&
271 memcmp(l4
->data
, data
, size
) == 0)
274 /* Same field? Then let's add this to this OR term */
275 if (same_field(data
, size
, l4
->data
, l4
->size
)) {
286 add_here
= match_new(j
->level2
, MATCH_OR_TERM
);
291 m
= match_new(add_here
, MATCH_DISCRETE
);
295 m
->le_hash
= le_hash
;
297 m
->data
= memdup(data
, size
);
306 match_free_if_empty(add_here
);
307 match_free_if_empty(j
->level2
);
308 match_free_if_empty(j
->level1
);
309 match_free_if_empty(j
->level0
);
314 _public_
int sd_journal_add_conjunction(sd_journal
*j
) {
315 assert_return(j
, -EINVAL
);
316 assert_return(!journal_pid_changed(j
), -ECHILD
);
324 if (!j
->level1
->matches
)
333 _public_
int sd_journal_add_disjunction(sd_journal
*j
) {
334 assert_return(j
, -EINVAL
);
335 assert_return(!journal_pid_changed(j
), -ECHILD
);
346 if (!j
->level2
->matches
)
353 static char *match_make_string(Match
*m
) {
356 bool enclose
= false;
359 return strdup("none");
361 if (m
->type
== MATCH_DISCRETE
)
362 return strndup(m
->data
, m
->size
);
365 LIST_FOREACH(matches
, i
, m
->matches
) {
368 t
= match_make_string(i
);
375 k
= strjoin(p
, m
->type
== MATCH_OR_TERM
? " OR " : " AND ", t
, NULL
);
390 r
= strjoin("(", p
, ")", NULL
);
398 char *journal_make_match_string(sd_journal
*j
) {
401 return match_make_string(j
->level0
);
404 _public_
void sd_journal_flush_matches(sd_journal
*j
) {
409 match_free(j
->level0
);
411 j
->level0
= j
->level1
= j
->level2
= NULL
;
416 _pure_
static int compare_with_location(JournalFile
*f
, Location
*l
) {
419 assert(f
->location_type
== LOCATION_SEEK
);
420 assert(l
->type
== LOCATION_DISCRETE
|| l
->type
== LOCATION_SEEK
);
422 if (l
->monotonic_set
&&
423 sd_id128_equal(f
->current_boot_id
, l
->boot_id
) &&
425 f
->current_realtime
== l
->realtime
&&
427 f
->current_xor_hash
== l
->xor_hash
)
431 sd_id128_equal(f
->header
->seqnum_id
, l
->seqnum_id
)) {
433 if (f
->current_seqnum
< l
->seqnum
)
435 if (f
->current_seqnum
> l
->seqnum
)
439 if (l
->monotonic_set
&&
440 sd_id128_equal(f
->current_boot_id
, l
->boot_id
)) {
442 if (f
->current_monotonic
< l
->monotonic
)
444 if (f
->current_monotonic
> l
->monotonic
)
448 if (l
->realtime_set
) {
450 if (f
->current_realtime
< l
->realtime
)
452 if (f
->current_realtime
> l
->realtime
)
456 if (l
->xor_hash_set
) {
458 if (f
->current_xor_hash
< l
->xor_hash
)
460 if (f
->current_xor_hash
> l
->xor_hash
)
467 static int next_for_match(
471 uint64_t after_offset
,
472 direction_t direction
,
484 if (m
->type
== MATCH_DISCRETE
) {
487 r
= journal_file_find_data_object_with_hash(f
, m
->data
, m
->size
, le64toh(m
->le_hash
), NULL
, &dp
);
491 return journal_file_move_to_entry_by_offset_for_data(f
, dp
, after_offset
, direction
, ret
, offset
);
493 } else if (m
->type
== MATCH_OR_TERM
) {
496 /* Find the earliest match beyond after_offset */
498 LIST_FOREACH(matches
, i
, m
->matches
) {
501 r
= next_for_match(j
, i
, f
, after_offset
, direction
, NULL
, &cp
);
505 if (np
== 0 || (direction
== DIRECTION_DOWN
? cp
< np
: cp
> np
))
513 } else if (m
->type
== MATCH_AND_TERM
) {
514 Match
*i
, *last_moved
;
516 /* Always jump to the next matching entry and repeat
517 * this until we find an offset that matches for all
523 r
= next_for_match(j
, m
->matches
, f
, after_offset
, direction
, NULL
, &np
);
527 assert(direction
== DIRECTION_DOWN
? np
>= after_offset
: np
<= after_offset
);
528 last_moved
= m
->matches
;
530 LIST_LOOP_BUT_ONE(matches
, i
, m
->matches
, last_moved
) {
533 r
= next_for_match(j
, i
, f
, np
, direction
, NULL
, &cp
);
537 assert(direction
== DIRECTION_DOWN
? cp
>= np
: cp
<= np
);
538 if (direction
== DIRECTION_DOWN
? cp
> np
: cp
< np
) {
547 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, np
, &n
);
559 static int find_location_for_match(
563 direction_t direction
,
573 if (m
->type
== MATCH_DISCRETE
) {
576 r
= journal_file_find_data_object_with_hash(f
, m
->data
, m
->size
, le64toh(m
->le_hash
), NULL
, &dp
);
580 /* FIXME: missing: find by monotonic */
582 if (j
->current_location
.type
== LOCATION_HEAD
)
583 return journal_file_next_entry_for_data(f
, NULL
, 0, dp
, DIRECTION_DOWN
, ret
, offset
);
584 if (j
->current_location
.type
== LOCATION_TAIL
)
585 return journal_file_next_entry_for_data(f
, NULL
, 0, dp
, DIRECTION_UP
, ret
, offset
);
586 if (j
->current_location
.seqnum_set
&& sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
587 return journal_file_move_to_entry_by_seqnum_for_data(f
, dp
, j
->current_location
.seqnum
, direction
, ret
, offset
);
588 if (j
->current_location
.monotonic_set
) {
589 r
= journal_file_move_to_entry_by_monotonic_for_data(f
, dp
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, ret
, offset
);
593 if (j
->current_location
.realtime_set
)
594 return journal_file_move_to_entry_by_realtime_for_data(f
, dp
, j
->current_location
.realtime
, direction
, ret
, offset
);
596 return journal_file_next_entry_for_data(f
, NULL
, 0, dp
, direction
, ret
, offset
);
598 } else if (m
->type
== MATCH_OR_TERM
) {
603 /* Find the earliest match */
605 LIST_FOREACH(matches
, i
, m
->matches
) {
608 r
= find_location_for_match(j
, i
, f
, direction
, NULL
, &cp
);
612 if (np
== 0 || (direction
== DIRECTION_DOWN
? np
> cp
: np
< cp
))
620 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, np
, &n
);
635 assert(m
->type
== MATCH_AND_TERM
);
637 /* First jump to the last match, and then find the
638 * next one where all matches match */
643 LIST_FOREACH(matches
, i
, m
->matches
) {
646 r
= find_location_for_match(j
, i
, f
, direction
, NULL
, &cp
);
650 if (np
== 0 || (direction
== DIRECTION_DOWN
? cp
> np
: cp
< np
))
654 return next_for_match(j
, m
, f
, np
, direction
, ret
, offset
);
658 static int find_location_with_matches(
661 direction_t direction
,
673 /* No matches is simple */
675 if (j
->current_location
.type
== LOCATION_HEAD
)
676 return journal_file_next_entry(f
, 0, DIRECTION_DOWN
, ret
, offset
);
677 if (j
->current_location
.type
== LOCATION_TAIL
)
678 return journal_file_next_entry(f
, 0, DIRECTION_UP
, ret
, offset
);
679 if (j
->current_location
.seqnum_set
&& sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
680 return journal_file_move_to_entry_by_seqnum(f
, j
->current_location
.seqnum
, direction
, ret
, offset
);
681 if (j
->current_location
.monotonic_set
) {
682 r
= journal_file_move_to_entry_by_monotonic(f
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, ret
, offset
);
686 if (j
->current_location
.realtime_set
)
687 return journal_file_move_to_entry_by_realtime(f
, j
->current_location
.realtime
, direction
, ret
, offset
);
689 return journal_file_next_entry(f
, 0, direction
, ret
, offset
);
691 return find_location_for_match(j
, j
->level0
, f
, direction
, ret
, offset
);
694 static int next_with_matches(
697 direction_t direction
,
706 /* No matches is easy. We simple advance the file
709 return journal_file_next_entry(f
, f
->current_offset
, direction
, ret
, offset
);
711 /* If we have a match then we look for the next matching entry
712 * with an offset at least one step larger */
713 return next_for_match(j
, j
->level0
, f
,
714 direction
== DIRECTION_DOWN
? f
->current_offset
+ 1
715 : f
->current_offset
- 1,
716 direction
, ret
, offset
);
719 static int next_beyond_location(sd_journal
*j
, JournalFile
*f
, direction_t direction
) {
721 uint64_t cp
, n_entries
;
727 n_entries
= le64toh(f
->header
->n_entries
);
729 /* If we hit EOF before, we don't need to look into this file again
730 * unless direction changed or new entries appeared. */
731 if (f
->last_direction
== direction
&& f
->location_type
== LOCATION_TAIL
&&
732 n_entries
== f
->last_n_entries
)
735 f
->last_n_entries
= n_entries
;
737 if (f
->last_direction
== direction
&& f
->current_offset
> 0) {
738 /* LOCATION_SEEK here means we did the work in a previous
739 * iteration and the current location already points to a
740 * candidate entry. */
741 if (f
->location_type
!= LOCATION_SEEK
) {
742 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
746 journal_file_save_location(f
, c
, cp
);
749 f
->last_direction
= direction
;
751 r
= find_location_with_matches(j
, f
, direction
, &c
, &cp
);
755 journal_file_save_location(f
, c
, cp
);
758 /* OK, we found the spot, now let's advance until an entry
759 * that is actually different from what we were previously
760 * looking at. This is necessary to handle entries which exist
761 * in two (or more) journal files, and which shall all be
762 * suppressed but one. */
767 if (j
->current_location
.type
== LOCATION_DISCRETE
) {
770 k
= compare_with_location(f
, &j
->current_location
);
772 found
= direction
== DIRECTION_DOWN
? k
> 0 : k
< 0;
779 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
783 journal_file_save_location(f
, c
, cp
);
787 static int real_journal_next(sd_journal
*j
, direction_t direction
) {
788 JournalFile
*f
, *new_file
= NULL
;
793 assert_return(j
, -EINVAL
);
794 assert_return(!journal_pid_changed(j
), -ECHILD
);
796 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
799 r
= next_beyond_location(j
, f
, direction
);
801 log_debug_errno(r
, "Can't iterate through %s, ignoring: %m", f
->path
);
802 remove_file_real(j
, f
);
805 f
->location_type
= LOCATION_TAIL
;
814 k
= journal_file_compare_locations(f
, new_file
);
816 found
= direction
== DIRECTION_DOWN
? k
< 0 : k
> 0;
826 r
= journal_file_move_to_object(new_file
, OBJECT_ENTRY
, new_file
->current_offset
, &o
);
830 set_location(j
, new_file
, o
);
835 _public_
int sd_journal_next(sd_journal
*j
) {
836 return real_journal_next(j
, DIRECTION_DOWN
);
839 _public_
int sd_journal_previous(sd_journal
*j
) {
840 return real_journal_next(j
, DIRECTION_UP
);
843 static int real_journal_next_skip(sd_journal
*j
, direction_t direction
, uint64_t skip
) {
846 assert_return(j
, -EINVAL
);
847 assert_return(!journal_pid_changed(j
), -ECHILD
);
850 /* If this is not a discrete skip, then at least
851 * resolve the current location */
852 if (j
->current_location
.type
!= LOCATION_DISCRETE
)
853 return real_journal_next(j
, direction
);
859 r
= real_journal_next(j
, direction
);
873 _public_
int sd_journal_next_skip(sd_journal
*j
, uint64_t skip
) {
874 return real_journal_next_skip(j
, DIRECTION_DOWN
, skip
);
877 _public_
int sd_journal_previous_skip(sd_journal
*j
, uint64_t skip
) {
878 return real_journal_next_skip(j
, DIRECTION_UP
, skip
);
881 _public_
int sd_journal_get_cursor(sd_journal
*j
, char **cursor
) {
884 char bid
[33], sid
[33];
886 assert_return(j
, -EINVAL
);
887 assert_return(!journal_pid_changed(j
), -ECHILD
);
888 assert_return(cursor
, -EINVAL
);
890 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
891 return -EADDRNOTAVAIL
;
893 r
= journal_file_move_to_object(j
->current_file
, OBJECT_ENTRY
, j
->current_file
->current_offset
, &o
);
897 sd_id128_to_string(j
->current_file
->header
->seqnum_id
, sid
);
898 sd_id128_to_string(o
->entry
.boot_id
, bid
);
901 "s=%s;i=%"PRIx64
";b=%s;m=%"PRIx64
";t=%"PRIx64
";x=%"PRIx64
,
902 sid
, le64toh(o
->entry
.seqnum
),
903 bid
, le64toh(o
->entry
.monotonic
),
904 le64toh(o
->entry
.realtime
),
905 le64toh(o
->entry
.xor_hash
)) < 0)
911 _public_
int sd_journal_seek_cursor(sd_journal
*j
, const char *cursor
) {
912 const char *word
, *state
;
914 unsigned long long seqnum
, monotonic
, realtime
, xor_hash
;
916 seqnum_id_set
= false,
919 monotonic_set
= false,
920 realtime_set
= false,
921 xor_hash_set
= false;
922 sd_id128_t seqnum_id
, boot_id
;
924 assert_return(j
, -EINVAL
);
925 assert_return(!journal_pid_changed(j
), -ECHILD
);
926 assert_return(!isempty(cursor
), -EINVAL
);
928 FOREACH_WORD_SEPARATOR(word
, l
, cursor
, ";", state
) {
932 if (l
< 2 || word
[1] != '=')
935 item
= strndup(word
, l
);
942 seqnum_id_set
= true;
943 k
= sd_id128_from_string(item
+2, &seqnum_id
);
948 if (sscanf(item
+2, "%llx", &seqnum
) != 1)
954 k
= sd_id128_from_string(item
+2, &boot_id
);
958 monotonic_set
= true;
959 if (sscanf(item
+2, "%llx", &monotonic
) != 1)
965 if (sscanf(item
+2, "%llx", &realtime
) != 1)
971 if (sscanf(item
+2, "%llx", &xor_hash
) != 1)
982 if ((!seqnum_set
|| !seqnum_id_set
) &&
983 (!monotonic_set
|| !boot_id_set
) &&
989 j
->current_location
.type
= LOCATION_SEEK
;
992 j
->current_location
.realtime
= (uint64_t) realtime
;
993 j
->current_location
.realtime_set
= true;
996 if (seqnum_set
&& seqnum_id_set
) {
997 j
->current_location
.seqnum
= (uint64_t) seqnum
;
998 j
->current_location
.seqnum_id
= seqnum_id
;
999 j
->current_location
.seqnum_set
= true;
1002 if (monotonic_set
&& boot_id_set
) {
1003 j
->current_location
.monotonic
= (uint64_t) monotonic
;
1004 j
->current_location
.boot_id
= boot_id
;
1005 j
->current_location
.monotonic_set
= true;
1009 j
->current_location
.xor_hash
= (uint64_t) xor_hash
;
1010 j
->current_location
.xor_hash_set
= true;
1016 _public_
int sd_journal_test_cursor(sd_journal
*j
, const char *cursor
) {
1018 const char *word
, *state
;
1022 assert_return(j
, -EINVAL
);
1023 assert_return(!journal_pid_changed(j
), -ECHILD
);
1024 assert_return(!isempty(cursor
), -EINVAL
);
1026 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
1027 return -EADDRNOTAVAIL
;
1029 r
= journal_file_move_to_object(j
->current_file
, OBJECT_ENTRY
, j
->current_file
->current_offset
, &o
);
1033 FOREACH_WORD_SEPARATOR(word
, l
, cursor
, ";", state
) {
1034 _cleanup_free_
char *item
= NULL
;
1036 unsigned long long ll
;
1039 if (l
< 2 || word
[1] != '=')
1042 item
= strndup(word
, l
);
1049 k
= sd_id128_from_string(item
+2, &id
);
1052 if (!sd_id128_equal(id
, j
->current_file
->header
->seqnum_id
))
1057 if (sscanf(item
+2, "%llx", &ll
) != 1)
1059 if (ll
!= le64toh(o
->entry
.seqnum
))
1064 k
= sd_id128_from_string(item
+2, &id
);
1067 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
1072 if (sscanf(item
+2, "%llx", &ll
) != 1)
1074 if (ll
!= le64toh(o
->entry
.monotonic
))
1079 if (sscanf(item
+2, "%llx", &ll
) != 1)
1081 if (ll
!= le64toh(o
->entry
.realtime
))
1086 if (sscanf(item
+2, "%llx", &ll
) != 1)
1088 if (ll
!= le64toh(o
->entry
.xor_hash
))
1098 _public_
int sd_journal_seek_monotonic_usec(sd_journal
*j
, sd_id128_t boot_id
, uint64_t usec
) {
1099 assert_return(j
, -EINVAL
);
1100 assert_return(!journal_pid_changed(j
), -ECHILD
);
1103 j
->current_location
.type
= LOCATION_SEEK
;
1104 j
->current_location
.boot_id
= boot_id
;
1105 j
->current_location
.monotonic
= usec
;
1106 j
->current_location
.monotonic_set
= true;
1111 _public_
int sd_journal_seek_realtime_usec(sd_journal
*j
, uint64_t usec
) {
1112 assert_return(j
, -EINVAL
);
1113 assert_return(!journal_pid_changed(j
), -ECHILD
);
1116 j
->current_location
.type
= LOCATION_SEEK
;
1117 j
->current_location
.realtime
= usec
;
1118 j
->current_location
.realtime_set
= true;
1123 _public_
int sd_journal_seek_head(sd_journal
*j
) {
1124 assert_return(j
, -EINVAL
);
1125 assert_return(!journal_pid_changed(j
), -ECHILD
);
1128 j
->current_location
.type
= LOCATION_HEAD
;
1133 _public_
int sd_journal_seek_tail(sd_journal
*j
) {
1134 assert_return(j
, -EINVAL
);
1135 assert_return(!journal_pid_changed(j
), -ECHILD
);
1138 j
->current_location
.type
= LOCATION_TAIL
;
1143 static void check_network(sd_journal
*j
, int fd
) {
1151 if (fstatfs(fd
, &sfs
) < 0)
1155 F_TYPE_EQUAL(sfs
.f_type
, CIFS_MAGIC_NUMBER
) ||
1156 F_TYPE_EQUAL(sfs
.f_type
, CODA_SUPER_MAGIC
) ||
1157 F_TYPE_EQUAL(sfs
.f_type
, NCP_SUPER_MAGIC
) ||
1158 F_TYPE_EQUAL(sfs
.f_type
, NFS_SUPER_MAGIC
) ||
1159 F_TYPE_EQUAL(sfs
.f_type
, SMB_SUPER_MAGIC
);
1162 static bool file_has_type_prefix(const char *prefix
, const char *filename
) {
1163 const char *full
, *tilded
, *atted
;
1165 full
= strjoina(prefix
, ".journal");
1166 tilded
= strjoina(full
, "~");
1167 atted
= strjoina(prefix
, "@");
1169 return streq(filename
, full
) ||
1170 streq(filename
, tilded
) ||
1171 startswith(filename
, atted
);
1174 static bool file_type_wanted(int flags
, const char *filename
) {
1175 if (!endswith(filename
, ".journal") && !endswith(filename
, ".journal~"))
1178 /* no flags set → every type is OK */
1179 if (!(flags
& (SD_JOURNAL_SYSTEM
| SD_JOURNAL_CURRENT_USER
)))
1182 if (flags
& SD_JOURNAL_SYSTEM
&& file_has_type_prefix("system", filename
))
1185 if (flags
& SD_JOURNAL_CURRENT_USER
) {
1186 char prefix
[5 + DECIMAL_STR_MAX(uid_t
) + 1];
1188 xsprintf(prefix
, "user-"UID_FMT
, getuid());
1190 if (file_has_type_prefix(prefix
, filename
))
1197 static int add_any_file(sd_journal
*j
, const char *path
) {
1198 JournalFile
*f
= NULL
;
1204 if (ordered_hashmap_get(j
->files
, path
))
1207 if (ordered_hashmap_size(j
->files
) >= JOURNAL_FILES_MAX
) {
1208 log_warning("Too many open journal files, not adding %s.", path
);
1209 return set_put_error(j
, -ETOOMANYREFS
);
1212 r
= journal_file_open(path
, O_RDONLY
, 0, false, false, NULL
, j
->mmap
, NULL
, &f
);
1216 /* journal_file_dump(f); */
1218 r
= ordered_hashmap_put(j
->files
, f
->path
, f
);
1220 journal_file_close(f
);
1224 log_debug("File %s added.", f
->path
);
1226 check_network(j
, f
->fd
);
1228 j
->current_invalidate_counter
++;
1233 static int add_file(sd_journal
*j
, const char *prefix
, const char *filename
) {
1234 _cleanup_free_
char *path
= NULL
;
1241 if (j
->no_new_files
||
1242 !file_type_wanted(j
->flags
, filename
))
1245 path
= strjoin(prefix
, "/", filename
, NULL
);
1249 r
= add_any_file(j
, path
);
1255 static int remove_file(sd_journal
*j
, const char *prefix
, const char *filename
) {
1256 _cleanup_free_
char *path
;
1263 path
= strjoin(prefix
, "/", filename
, NULL
);
1267 f
= ordered_hashmap_get(j
->files
, path
);
1271 remove_file_real(j
, f
);
1275 static void remove_file_real(sd_journal
*j
, JournalFile
*f
) {
1279 ordered_hashmap_remove(j
->files
, f
->path
);
1281 log_debug("File %s removed.", f
->path
);
1283 if (j
->current_file
== f
) {
1284 j
->current_file
= NULL
;
1285 j
->current_field
= 0;
1288 if (j
->unique_file
== f
) {
1289 /* Jump to the next unique_file or NULL if that one was last */
1290 j
->unique_file
= ordered_hashmap_next(j
->files
, j
->unique_file
->path
);
1291 j
->unique_offset
= 0;
1292 if (!j
->unique_file
)
1293 j
->unique_file_lost
= true;
1296 journal_file_close(f
);
1298 j
->current_invalidate_counter
++;
1301 static int add_directory(sd_journal
*j
, const char *prefix
, const char *dirname
) {
1302 _cleanup_free_
char *path
= NULL
;
1304 _cleanup_closedir_
DIR *d
= NULL
;
1312 log_debug("Considering %s/%s.", prefix
, dirname
);
1314 if ((j
->flags
& SD_JOURNAL_LOCAL_ONLY
) &&
1315 (sd_id128_from_string(dirname
, &id
) < 0 ||
1316 sd_id128_get_machine(&mid
) < 0 ||
1317 !(sd_id128_equal(id
, mid
) || path_startswith(prefix
, "/run"))))
1320 path
= strjoin(prefix
, "/", dirname
, NULL
);
1326 log_debug_errno(errno
, "Failed to open %s: %m", path
);
1327 if (errno
== ENOENT
)
1332 m
= hashmap_get(j
->directories_by_path
, path
);
1334 m
= new0(Directory
, 1);
1341 if (hashmap_put(j
->directories_by_path
, m
->path
, m
) < 0) {
1346 path
= NULL
; /* avoid freeing in cleanup */
1347 j
->current_invalidate_counter
++;
1349 log_debug("Directory %s added.", m
->path
);
1351 } else if (m
->is_root
)
1354 if (m
->wd
<= 0 && j
->inotify_fd
>= 0) {
1356 m
->wd
= inotify_add_watch(j
->inotify_fd
, m
->path
,
1357 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1358 IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
|IN_MOVED_FROM
|
1361 if (m
->wd
> 0 && hashmap_put(j
->directories_by_wd
, INT_TO_PTR(m
->wd
), m
) < 0)
1362 inotify_rm_watch(j
->inotify_fd
, m
->wd
);
1370 if (!de
&& errno
!= 0) {
1372 log_debug_errno(errno
, "Failed to read directory %s: %m", m
->path
);
1378 if (dirent_is_file_with_suffix(de
, ".journal") ||
1379 dirent_is_file_with_suffix(de
, ".journal~")) {
1380 r
= add_file(j
, m
->path
, de
->d_name
);
1382 log_debug_errno(r
, "Failed to add file %s/%s: %m",
1383 m
->path
, de
->d_name
);
1384 r
= set_put_error(j
, r
);
1391 check_network(j
, dirfd(d
));
1396 static int add_root_directory(sd_journal
*j
, const char *p
) {
1397 _cleanup_closedir_
DIR *d
= NULL
;
1404 if ((j
->flags
& SD_JOURNAL_RUNTIME_ONLY
) &&
1405 !path_startswith(p
, "/run"))
1409 p
= strjoina(j
->prefix
, p
);
1415 m
= hashmap_get(j
->directories_by_path
, p
);
1417 m
= new0(Directory
, 1);
1422 m
->path
= strdup(p
);
1428 if (hashmap_put(j
->directories_by_path
, m
->path
, m
) < 0) {
1434 j
->current_invalidate_counter
++;
1436 log_debug("Root directory %s added.", m
->path
);
1438 } else if (!m
->is_root
)
1441 if (m
->wd
<= 0 && j
->inotify_fd
>= 0) {
1443 m
->wd
= inotify_add_watch(j
->inotify_fd
, m
->path
,
1444 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1447 if (m
->wd
> 0 && hashmap_put(j
->directories_by_wd
, INT_TO_PTR(m
->wd
), m
) < 0)
1448 inotify_rm_watch(j
->inotify_fd
, m
->wd
);
1451 if (j
->no_new_files
)
1460 if (!de
&& errno
!= 0) {
1462 log_debug_errno(errno
, "Failed to read directory %s: %m", m
->path
);
1468 if (dirent_is_file_with_suffix(de
, ".journal") ||
1469 dirent_is_file_with_suffix(de
, ".journal~")) {
1470 r
= add_file(j
, m
->path
, de
->d_name
);
1472 log_debug_errno(r
, "Failed to add file %s/%s: %m",
1473 m
->path
, de
->d_name
);
1474 r
= set_put_error(j
, r
);
1478 } else if ((de
->d_type
== DT_DIR
|| de
->d_type
== DT_LNK
|| de
->d_type
== DT_UNKNOWN
) &&
1479 sd_id128_from_string(de
->d_name
, &id
) >= 0) {
1481 r
= add_directory(j
, m
->path
, de
->d_name
);
1483 log_debug_errno(r
, "Failed to add directory %s/%s: %m", m
->path
, de
->d_name
);
1487 check_network(j
, dirfd(d
));
1492 static void remove_directory(sd_journal
*j
, Directory
*d
) {
1496 hashmap_remove(j
->directories_by_wd
, INT_TO_PTR(d
->wd
));
1498 if (j
->inotify_fd
>= 0)
1499 inotify_rm_watch(j
->inotify_fd
, d
->wd
);
1502 hashmap_remove(j
->directories_by_path
, d
->path
);
1505 log_debug("Root directory %s removed.", d
->path
);
1507 log_debug("Directory %s removed.", d
->path
);
1513 static int add_search_paths(sd_journal
*j
) {
1515 const char search_paths
[] =
1516 "/run/log/journal\0"
1517 "/var/log/journal\0";
1522 /* We ignore most errors here, since the idea is to only open
1523 * what's actually accessible, and ignore the rest. */
1525 NULSTR_FOREACH(p
, search_paths
) {
1526 r
= add_root_directory(j
, p
);
1527 if (r
< 0 && r
!= -ENOENT
) {
1528 r
= set_put_error(j
, r
);
1537 static int add_current_paths(sd_journal
*j
) {
1542 assert(j
->no_new_files
);
1544 /* Simply adds all directories for files we have open as
1545 * "root" directories. We don't expect errors here, so we
1546 * treat them as fatal. */
1548 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
1549 _cleanup_free_
char *dir
;
1552 dir
= dirname_malloc(f
->path
);
1556 r
= add_root_directory(j
, dir
);
1558 set_put_error(j
, r
);
1567 static int allocate_inotify(sd_journal
*j
) {
1570 if (j
->inotify_fd
< 0) {
1571 j
->inotify_fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
1572 if (j
->inotify_fd
< 0)
1576 if (!j
->directories_by_wd
) {
1577 j
->directories_by_wd
= hashmap_new(NULL
);
1578 if (!j
->directories_by_wd
)
1585 static sd_journal
*journal_new(int flags
, const char *path
) {
1588 j
= new0(sd_journal
, 1);
1592 j
->original_pid
= getpid();
1595 j
->data_threshold
= DEFAULT_DATA_THRESHOLD
;
1598 j
->path
= strdup(path
);
1603 j
->files
= ordered_hashmap_new(&string_hash_ops
);
1604 j
->directories_by_path
= hashmap_new(&string_hash_ops
);
1605 j
->mmap
= mmap_cache_new();
1606 if (!j
->files
|| !j
->directories_by_path
|| !j
->mmap
)
1612 sd_journal_close(j
);
1616 _public_
int sd_journal_open(sd_journal
**ret
, int flags
) {
1620 assert_return(ret
, -EINVAL
);
1621 assert_return((flags
& ~(SD_JOURNAL_LOCAL_ONLY
|SD_JOURNAL_RUNTIME_ONLY
|SD_JOURNAL_SYSTEM
|SD_JOURNAL_CURRENT_USER
)) == 0, -EINVAL
);
1623 j
= journal_new(flags
, NULL
);
1627 r
= add_search_paths(j
);
1635 sd_journal_close(j
);
1640 _public_
int sd_journal_open_container(sd_journal
**ret
, const char *machine
, int flags
) {
1641 _cleanup_free_
char *root
= NULL
, *class = NULL
;
1646 assert_return(machine
, -EINVAL
);
1647 assert_return(ret
, -EINVAL
);
1648 assert_return((flags
& ~(SD_JOURNAL_LOCAL_ONLY
|SD_JOURNAL_SYSTEM
)) == 0, -EINVAL
);
1649 assert_return(machine_name_is_valid(machine
), -EINVAL
);
1651 p
= strjoina("/run/systemd/machines/", machine
);
1652 r
= parse_env_file(p
, NEWLINE
, "ROOT", &root
, "CLASS", &class, NULL
);
1660 if (!streq_ptr(class, "container"))
1663 j
= journal_new(flags
, NULL
);
1670 r
= add_search_paths(j
);
1678 sd_journal_close(j
);
1682 _public_
int sd_journal_open_directory(sd_journal
**ret
, const char *path
, int flags
) {
1686 assert_return(ret
, -EINVAL
);
1687 assert_return(path
, -EINVAL
);
1688 assert_return(flags
== 0, -EINVAL
);
1690 j
= journal_new(flags
, path
);
1694 r
= add_root_directory(j
, path
);
1696 set_put_error(j
, r
);
1704 sd_journal_close(j
);
1709 _public_
int sd_journal_open_files(sd_journal
**ret
, const char **paths
, int flags
) {
1714 assert_return(ret
, -EINVAL
);
1715 assert_return(flags
== 0, -EINVAL
);
1717 j
= journal_new(flags
, NULL
);
1721 STRV_FOREACH(path
, paths
) {
1722 r
= add_any_file(j
, *path
);
1724 log_error_errno(r
, "Failed to open %s: %m", *path
);
1729 j
->no_new_files
= true;
1735 sd_journal_close(j
);
1740 _public_
void sd_journal_close(sd_journal
*j
) {
1747 sd_journal_flush_matches(j
);
1749 while ((f
= ordered_hashmap_steal_first(j
->files
)))
1750 journal_file_close(f
);
1752 ordered_hashmap_free(j
->files
);
1754 while ((d
= hashmap_first(j
->directories_by_path
)))
1755 remove_directory(j
, d
);
1757 while ((d
= hashmap_first(j
->directories_by_wd
)))
1758 remove_directory(j
, d
);
1760 hashmap_free(j
->directories_by_path
);
1761 hashmap_free(j
->directories_by_wd
);
1763 safe_close(j
->inotify_fd
);
1766 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j
->mmap
), mmap_cache_get_missed(j
->mmap
));
1767 mmap_cache_unref(j
->mmap
);
1772 free(j
->unique_field
);
1773 set_free(j
->errors
);
1777 _public_
int sd_journal_get_realtime_usec(sd_journal
*j
, uint64_t *ret
) {
1782 assert_return(j
, -EINVAL
);
1783 assert_return(!journal_pid_changed(j
), -ECHILD
);
1784 assert_return(ret
, -EINVAL
);
1786 f
= j
->current_file
;
1788 return -EADDRNOTAVAIL
;
1790 if (f
->current_offset
<= 0)
1791 return -EADDRNOTAVAIL
;
1793 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1797 *ret
= le64toh(o
->entry
.realtime
);
1801 _public_
int sd_journal_get_monotonic_usec(sd_journal
*j
, uint64_t *ret
, sd_id128_t
*ret_boot_id
) {
1807 assert_return(j
, -EINVAL
);
1808 assert_return(!journal_pid_changed(j
), -ECHILD
);
1810 f
= j
->current_file
;
1812 return -EADDRNOTAVAIL
;
1814 if (f
->current_offset
<= 0)
1815 return -EADDRNOTAVAIL
;
1817 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1822 *ret_boot_id
= o
->entry
.boot_id
;
1824 r
= sd_id128_get_boot(&id
);
1828 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
1833 *ret
= le64toh(o
->entry
.monotonic
);
1838 static bool field_is_valid(const char *field
) {
1846 if (startswith(field
, "__"))
1849 for (p
= field
; *p
; p
++) {
1854 if (*p
>= 'A' && *p
<= 'Z')
1857 if (*p
>= '0' && *p
<= '9')
1866 _public_
int sd_journal_get_data(sd_journal
*j
, const char *field
, const void **data
, size_t *size
) {
1869 size_t field_length
;
1873 assert_return(j
, -EINVAL
);
1874 assert_return(!journal_pid_changed(j
), -ECHILD
);
1875 assert_return(field
, -EINVAL
);
1876 assert_return(data
, -EINVAL
);
1877 assert_return(size
, -EINVAL
);
1878 assert_return(field_is_valid(field
), -EINVAL
);
1880 f
= j
->current_file
;
1882 return -EADDRNOTAVAIL
;
1884 if (f
->current_offset
<= 0)
1885 return -EADDRNOTAVAIL
;
1887 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1891 field_length
= strlen(field
);
1893 n
= journal_file_entry_n_items(o
);
1894 for (i
= 0; i
< n
; i
++) {
1900 p
= le64toh(o
->entry
.items
[i
].object_offset
);
1901 le_hash
= o
->entry
.items
[i
].hash
;
1902 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
1906 if (le_hash
!= o
->data
.hash
)
1909 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
1911 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
1913 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1914 if (decompress_startswith(compression
,
1916 &f
->compress_buffer
, &f
->compress_buffer_size
,
1917 field
, field_length
, '=')) {
1921 r
= decompress_blob(compression
,
1923 &f
->compress_buffer
, &f
->compress_buffer_size
, &rsize
,
1928 *data
= f
->compress_buffer
;
1929 *size
= (size_t) rsize
;
1934 return -EPROTONOSUPPORT
;
1936 } else if (l
>= field_length
+1 &&
1937 memcmp(o
->data
.payload
, field
, field_length
) == 0 &&
1938 o
->data
.payload
[field_length
] == '=') {
1942 if ((uint64_t) t
!= l
)
1945 *data
= o
->data
.payload
;
1951 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1959 static int return_data(sd_journal
*j
, JournalFile
*f
, Object
*o
, const void **data
, size_t *size
) {
1964 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
1967 /* We can't read objects larger than 4G on a 32bit machine */
1968 if ((uint64_t) t
!= l
)
1971 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
1973 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1977 r
= decompress_blob(compression
,
1978 o
->data
.payload
, l
, &f
->compress_buffer
,
1979 &f
->compress_buffer_size
, &rsize
, j
->data_threshold
);
1983 *data
= f
->compress_buffer
;
1984 *size
= (size_t) rsize
;
1986 return -EPROTONOSUPPORT
;
1989 *data
= o
->data
.payload
;
1996 _public_
int sd_journal_enumerate_data(sd_journal
*j
, const void **data
, size_t *size
) {
2003 assert_return(j
, -EINVAL
);
2004 assert_return(!journal_pid_changed(j
), -ECHILD
);
2005 assert_return(data
, -EINVAL
);
2006 assert_return(size
, -EINVAL
);
2008 f
= j
->current_file
;
2010 return -EADDRNOTAVAIL
;
2012 if (f
->current_offset
<= 0)
2013 return -EADDRNOTAVAIL
;
2015 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2019 n
= journal_file_entry_n_items(o
);
2020 if (j
->current_field
>= n
)
2023 p
= le64toh(o
->entry
.items
[j
->current_field
].object_offset
);
2024 le_hash
= o
->entry
.items
[j
->current_field
].hash
;
2025 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
2029 if (le_hash
!= o
->data
.hash
)
2032 r
= return_data(j
, f
, o
, data
, size
);
2036 j
->current_field
++;
2041 _public_
void sd_journal_restart_data(sd_journal
*j
) {
2045 j
->current_field
= 0;
2048 _public_
int sd_journal_get_fd(sd_journal
*j
) {
2051 assert_return(j
, -EINVAL
);
2052 assert_return(!journal_pid_changed(j
), -ECHILD
);
2054 if (j
->inotify_fd
>= 0)
2055 return j
->inotify_fd
;
2057 r
= allocate_inotify(j
);
2061 /* Iterate through all dirs again, to add them to the
2063 if (j
->no_new_files
)
2064 r
= add_current_paths(j
);
2066 r
= add_root_directory(j
, j
->path
);
2068 r
= add_search_paths(j
);
2072 return j
->inotify_fd
;
2075 _public_
int sd_journal_get_events(sd_journal
*j
) {
2078 assert_return(j
, -EINVAL
);
2079 assert_return(!journal_pid_changed(j
), -ECHILD
);
2081 fd
= sd_journal_get_fd(j
);
2088 _public_
int sd_journal_get_timeout(sd_journal
*j
, uint64_t *timeout_usec
) {
2091 assert_return(j
, -EINVAL
);
2092 assert_return(!journal_pid_changed(j
), -ECHILD
);
2093 assert_return(timeout_usec
, -EINVAL
);
2095 fd
= sd_journal_get_fd(j
);
2099 if (!j
->on_network
) {
2100 *timeout_usec
= (uint64_t) -1;
2104 /* If we are on the network we need to regularly check for
2105 * changes manually */
2107 *timeout_usec
= j
->last_process_usec
+ JOURNAL_FILES_RECHECK_USEC
;
2111 static void process_inotify_event(sd_journal
*j
, struct inotify_event
*e
) {
2118 /* Is this a subdirectory we watch? */
2119 d
= hashmap_get(j
->directories_by_wd
, INT_TO_PTR(e
->wd
));
2123 if (!(e
->mask
& IN_ISDIR
) && e
->len
> 0 &&
2124 (endswith(e
->name
, ".journal") ||
2125 endswith(e
->name
, ".journal~"))) {
2127 /* Event for a journal file */
2129 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
)) {
2130 r
= add_file(j
, d
->path
, e
->name
);
2132 log_debug_errno(r
, "Failed to add file %s/%s: %m",
2134 set_put_error(j
, r
);
2137 } else if (e
->mask
& (IN_DELETE
|IN_MOVED_FROM
|IN_UNMOUNT
)) {
2139 r
= remove_file(j
, d
->path
, e
->name
);
2141 log_debug_errno(r
, "Failed to remove file %s/%s: %m", d
->path
, e
->name
);
2144 } else if (!d
->is_root
&& e
->len
== 0) {
2146 /* Event for a subdirectory */
2148 if (e
->mask
& (IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
))
2149 remove_directory(j
, d
);
2151 } else if (d
->is_root
&& (e
->mask
& IN_ISDIR
) && e
->len
> 0 && sd_id128_from_string(e
->name
, &id
) >= 0) {
2153 /* Event for root directory */
2155 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
)) {
2156 r
= add_directory(j
, d
->path
, e
->name
);
2158 log_debug_errno(r
, "Failed to add directory %s/%s: %m", d
->path
, e
->name
);
2165 if (e
->mask
& IN_IGNORED
)
2168 log_warning("Unknown inotify event.");
2171 static int determine_change(sd_journal
*j
) {
2176 b
= j
->current_invalidate_counter
!= j
->last_invalidate_counter
;
2177 j
->last_invalidate_counter
= j
->current_invalidate_counter
;
2179 return b
? SD_JOURNAL_INVALIDATE
: SD_JOURNAL_APPEND
;
2182 _public_
int sd_journal_process(sd_journal
*j
) {
2183 bool got_something
= false;
2185 assert_return(j
, -EINVAL
);
2186 assert_return(!journal_pid_changed(j
), -ECHILD
);
2188 j
->last_process_usec
= now(CLOCK_MONOTONIC
);
2191 union inotify_event_buffer buffer
;
2192 struct inotify_event
*e
;
2195 l
= read(j
->inotify_fd
, &buffer
, sizeof(buffer
));
2197 if (errno
== EAGAIN
|| errno
== EINTR
)
2198 return got_something
? determine_change(j
) : SD_JOURNAL_NOP
;
2203 got_something
= true;
2205 FOREACH_INOTIFY_EVENT(e
, buffer
, l
)
2206 process_inotify_event(j
, e
);
2210 _public_
int sd_journal_wait(sd_journal
*j
, uint64_t timeout_usec
) {
2214 assert_return(j
, -EINVAL
);
2215 assert_return(!journal_pid_changed(j
), -ECHILD
);
2217 if (j
->inotify_fd
< 0) {
2219 /* This is the first invocation, hence create the
2221 r
= sd_journal_get_fd(j
);
2225 /* The journal might have changed since the context
2226 * object was created and we weren't watching before,
2227 * hence don't wait for anything, and return
2229 return determine_change(j
);
2232 r
= sd_journal_get_timeout(j
, &t
);
2236 if (t
!= (uint64_t) -1) {
2239 n
= now(CLOCK_MONOTONIC
);
2240 t
= t
> n
? t
- n
: 0;
2242 if (timeout_usec
== (uint64_t) -1 || timeout_usec
> t
)
2247 r
= fd_wait_for_event(j
->inotify_fd
, POLLIN
, timeout_usec
);
2248 } while (r
== -EINTR
);
2253 return sd_journal_process(j
);
2256 _public_
int sd_journal_get_cutoff_realtime_usec(sd_journal
*j
, uint64_t *from
, uint64_t *to
) {
2260 uint64_t fmin
= 0, tmax
= 0;
2263 assert_return(j
, -EINVAL
);
2264 assert_return(!journal_pid_changed(j
), -ECHILD
);
2265 assert_return(from
|| to
, -EINVAL
);
2266 assert_return(from
!= to
, -EINVAL
);
2268 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
2271 r
= journal_file_get_cutoff_realtime_usec(f
, &fr
, &t
);
2284 fmin
= MIN(fr
, fmin
);
2285 tmax
= MAX(t
, tmax
);
2294 return first
? 0 : 1;
2297 _public_
int sd_journal_get_cutoff_monotonic_usec(sd_journal
*j
, sd_id128_t boot_id
, uint64_t *from
, uint64_t *to
) {
2303 assert_return(j
, -EINVAL
);
2304 assert_return(!journal_pid_changed(j
), -ECHILD
);
2305 assert_return(from
|| to
, -EINVAL
);
2306 assert_return(from
!= to
, -EINVAL
);
2308 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
2311 r
= journal_file_get_cutoff_monotonic_usec(f
, boot_id
, &fr
, &t
);
2321 *from
= MIN(fr
, *from
);
2336 void journal_print_header(sd_journal
*j
) {
2339 bool newline
= false;
2343 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
2349 journal_file_print_header(f
);
2353 _public_
int sd_journal_get_usage(sd_journal
*j
, uint64_t *bytes
) {
2358 assert_return(j
, -EINVAL
);
2359 assert_return(!journal_pid_changed(j
), -ECHILD
);
2360 assert_return(bytes
, -EINVAL
);
2362 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
2365 if (fstat(f
->fd
, &st
) < 0)
2368 sum
+= (uint64_t) st
.st_blocks
* 512ULL;
2375 _public_
int sd_journal_query_unique(sd_journal
*j
, const char *field
) {
2378 assert_return(j
, -EINVAL
);
2379 assert_return(!journal_pid_changed(j
), -ECHILD
);
2380 assert_return(!isempty(field
), -EINVAL
);
2381 assert_return(field_is_valid(field
), -EINVAL
);
2387 free(j
->unique_field
);
2388 j
->unique_field
= f
;
2389 j
->unique_file
= NULL
;
2390 j
->unique_offset
= 0;
2391 j
->unique_file_lost
= false;
2396 _public_
int sd_journal_enumerate_unique(sd_journal
*j
, const void **data
, size_t *l
) {
2399 assert_return(j
, -EINVAL
);
2400 assert_return(!journal_pid_changed(j
), -ECHILD
);
2401 assert_return(data
, -EINVAL
);
2402 assert_return(l
, -EINVAL
);
2403 assert_return(j
->unique_field
, -EINVAL
);
2405 k
= strlen(j
->unique_field
);
2407 if (!j
->unique_file
) {
2408 if (j
->unique_file_lost
)
2411 j
->unique_file
= ordered_hashmap_first(j
->files
);
2412 if (!j
->unique_file
)
2415 j
->unique_offset
= 0;
2427 /* Proceed to next data object in the field's linked list */
2428 if (j
->unique_offset
== 0) {
2429 r
= journal_file_find_field_object(j
->unique_file
, j
->unique_field
, k
, &o
, NULL
);
2433 j
->unique_offset
= r
> 0 ? le64toh(o
->field
.head_data_offset
) : 0;
2435 r
= journal_file_move_to_object(j
->unique_file
, OBJECT_DATA
, j
->unique_offset
, &o
);
2439 j
->unique_offset
= le64toh(o
->data
.next_field_offset
);
2442 /* We reached the end of the list? Then start again, with the next file */
2443 if (j
->unique_offset
== 0) {
2444 j
->unique_file
= ordered_hashmap_next(j
->files
, j
->unique_file
->path
);
2445 if (!j
->unique_file
)
2451 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2452 * instead, so that we can look at this data object at the same
2453 * time as one on another file */
2454 r
= journal_file_move_to_object(j
->unique_file
, OBJECT_UNUSED
, j
->unique_offset
, &o
);
2458 /* Let's do the type check by hand, since we used 0 context above. */
2459 if (o
->object
.type
!= OBJECT_DATA
) {
2460 log_debug("%s:offset " OFSfmt
": object has type %d, expected %d",
2461 j
->unique_file
->path
, j
->unique_offset
,
2462 o
->object
.type
, OBJECT_DATA
);
2466 r
= return_data(j
, j
->unique_file
, o
, &odata
, &ol
);
2470 /* Check if we have at least the field name and "=". */
2472 log_debug("%s:offset " OFSfmt
": object has size %zu, expected at least %zu",
2473 j
->unique_file
->path
, j
->unique_offset
,
2478 if (memcmp(odata
, j
->unique_field
, k
) || ((const char*) odata
)[k
] != '=') {
2479 log_debug("%s:offset " OFSfmt
": object does not start with \"%s=\"",
2480 j
->unique_file
->path
, j
->unique_offset
,
2485 /* OK, now let's see if we already returned this data
2486 * object by checking if it exists in the earlier
2487 * traversed files. */
2489 ORDERED_HASHMAP_FOREACH(of
, j
->files
, i
) {
2493 if (of
== j
->unique_file
)
2496 /* Skip this file it didn't have any fields
2498 if (JOURNAL_HEADER_CONTAINS(of
->header
, n_fields
) &&
2499 le64toh(of
->header
->n_fields
) <= 0)
2502 r
= journal_file_find_data_object_with_hash(of
, odata
, ol
, le64toh(o
->data
.hash
), &oo
, &op
);
2513 r
= return_data(j
, j
->unique_file
, o
, data
, l
);
2521 _public_
void sd_journal_restart_unique(sd_journal
*j
) {
2525 j
->unique_file
= NULL
;
2526 j
->unique_offset
= 0;
2527 j
->unique_file_lost
= false;
2530 _public_
int sd_journal_reliable_fd(sd_journal
*j
) {
2531 assert_return(j
, -EINVAL
);
2532 assert_return(!journal_pid_changed(j
), -ECHILD
);
2534 return !j
->on_network
;
2537 static char *lookup_field(const char *field
, void *userdata
) {
2538 sd_journal
*j
= userdata
;
2546 r
= sd_journal_get_data(j
, field
, &data
, &size
);
2548 size
> REPLACE_VAR_MAX
)
2549 return strdup(field
);
2551 d
= strlen(field
) + 1;
2553 return strndup((const char*) data
+ d
, size
- d
);
2556 _public_
int sd_journal_get_catalog(sd_journal
*j
, char **ret
) {
2560 _cleanup_free_
char *text
= NULL
, *cid
= NULL
;
2564 assert_return(j
, -EINVAL
);
2565 assert_return(!journal_pid_changed(j
), -ECHILD
);
2566 assert_return(ret
, -EINVAL
);
2568 r
= sd_journal_get_data(j
, "MESSAGE_ID", &data
, &size
);
2572 cid
= strndup((const char*) data
+ 11, size
- 11);
2576 r
= sd_id128_from_string(cid
, &id
);
2580 r
= catalog_get(CATALOG_DATABASE
, id
, &text
);
2584 t
= replace_var(text
, lookup_field
, j
);
2592 _public_
int sd_journal_get_catalog_for_message_id(sd_id128_t id
, char **ret
) {
2593 assert_return(ret
, -EINVAL
);
2595 return catalog_get(CATALOG_DATABASE
, id
, ret
);
2598 _public_
int sd_journal_set_data_threshold(sd_journal
*j
, size_t sz
) {
2599 assert_return(j
, -EINVAL
);
2600 assert_return(!journal_pid_changed(j
), -ECHILD
);
2602 j
->data_threshold
= sz
;
2606 _public_
int sd_journal_get_data_threshold(sd_journal
*j
, size_t *sz
) {
2607 assert_return(j
, -EINVAL
);
2608 assert_return(!journal_pid_changed(j
), -ECHILD
);
2609 assert_return(sz
, -EINVAL
);
2611 *sz
= j
->data_threshold
;