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"
36 #include "formats-util.h"
38 #include "hostname-util.h"
39 #include "journal-def.h"
40 #include "journal-file.h"
41 #include "journal-internal.h"
45 #include "path-util.h"
46 #include "replace-var.h"
47 #include "string-util.h"
50 #define JOURNAL_FILES_MAX 7168
52 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
54 #define REPLACE_VAR_MAX 256
56 #define DEFAULT_DATA_THRESHOLD (64*1024)
58 static void remove_file_real(sd_journal
*j
, JournalFile
*f
);
60 static bool journal_pid_changed(sd_journal
*j
) {
63 /* We don't support people creating a journal object and
64 * keeping it around over a fork(). Let's complain. */
66 return j
->original_pid
!= getpid();
69 /* We return an error here only if we didn't manage to
70 memorize the real error. */
71 static int set_put_error(sd_journal
*j
, int r
) {
77 k
= set_ensure_allocated(&j
->errors
, NULL
);
81 return set_put(j
->errors
, INT_TO_PTR(r
));
84 static void detach_location(sd_journal
*j
) {
90 j
->current_file
= NULL
;
93 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
)
94 journal_file_reset_location(f
);
97 static void reset_location(sd_journal
*j
) {
101 zero(j
->current_location
);
104 static void init_location(Location
*l
, LocationType type
, JournalFile
*f
, Object
*o
) {
106 assert(type
== LOCATION_DISCRETE
|| type
== LOCATION_SEEK
);
108 assert(o
->object
.type
== OBJECT_ENTRY
);
111 l
->seqnum
= le64toh(o
->entry
.seqnum
);
112 l
->seqnum_id
= f
->header
->seqnum_id
;
113 l
->realtime
= le64toh(o
->entry
.realtime
);
114 l
->monotonic
= le64toh(o
->entry
.monotonic
);
115 l
->boot_id
= o
->entry
.boot_id
;
116 l
->xor_hash
= le64toh(o
->entry
.xor_hash
);
118 l
->seqnum_set
= l
->realtime_set
= l
->monotonic_set
= l
->xor_hash_set
= true;
121 static void set_location(sd_journal
*j
, JournalFile
*f
, Object
*o
) {
126 init_location(&j
->current_location
, LOCATION_DISCRETE
, f
, o
);
129 j
->current_field
= 0;
131 /* Let f know its candidate entry was picked. */
132 assert(f
->location_type
== LOCATION_SEEK
);
133 f
->location_type
= LOCATION_DISCRETE
;
136 static int match_is_valid(const void *data
, size_t size
) {
144 if (startswith(data
, "__"))
148 for (p
= b
; p
< b
+ size
; p
++) {
156 if (*p
>= 'A' && *p
<= 'Z')
159 if (*p
>= '0' && *p
<= '9')
168 static bool same_field(const void *_a
, size_t s
, const void *_b
, size_t t
) {
169 const uint8_t *a
= _a
, *b
= _b
;
172 for (j
= 0; j
< s
&& j
< t
; j
++) {
181 assert_not_reached("\"=\" not found");
184 static Match
*match_new(Match
*p
, MatchType t
) {
195 LIST_PREPEND(matches
, p
->matches
, m
);
201 static void match_free(Match
*m
) {
205 match_free(m
->matches
);
208 LIST_REMOVE(matches
, m
->parent
->matches
, m
);
214 static void match_free_if_empty(Match
*m
) {
215 if (!m
|| m
->matches
)
221 _public_
int sd_journal_add_match(sd_journal
*j
, const void *data
, size_t size
) {
222 Match
*l3
, *l4
, *add_here
= NULL
, *m
;
225 assert_return(j
, -EINVAL
);
226 assert_return(!journal_pid_changed(j
), -ECHILD
);
227 assert_return(data
, -EINVAL
);
232 assert_return(match_is_valid(data
, size
), -EINVAL
);
238 * level 4: concrete matches */
241 j
->level0
= match_new(NULL
, MATCH_AND_TERM
);
247 j
->level1
= match_new(j
->level0
, MATCH_OR_TERM
);
253 j
->level2
= match_new(j
->level1
, MATCH_AND_TERM
);
258 assert(j
->level0
->type
== MATCH_AND_TERM
);
259 assert(j
->level1
->type
== MATCH_OR_TERM
);
260 assert(j
->level2
->type
== MATCH_AND_TERM
);
262 le_hash
= htole64(hash64(data
, size
));
264 LIST_FOREACH(matches
, l3
, j
->level2
->matches
) {
265 assert(l3
->type
== MATCH_OR_TERM
);
267 LIST_FOREACH(matches
, l4
, l3
->matches
) {
268 assert(l4
->type
== MATCH_DISCRETE
);
270 /* Exactly the same match already? Then ignore
272 if (l4
->le_hash
== le_hash
&&
274 memcmp(l4
->data
, data
, size
) == 0)
277 /* Same field? Then let's add this to this OR term */
278 if (same_field(data
, size
, l4
->data
, l4
->size
)) {
289 add_here
= match_new(j
->level2
, MATCH_OR_TERM
);
294 m
= match_new(add_here
, MATCH_DISCRETE
);
298 m
->le_hash
= le_hash
;
300 m
->data
= memdup(data
, size
);
309 match_free_if_empty(add_here
);
310 match_free_if_empty(j
->level2
);
311 match_free_if_empty(j
->level1
);
312 match_free_if_empty(j
->level0
);
317 _public_
int sd_journal_add_conjunction(sd_journal
*j
) {
318 assert_return(j
, -EINVAL
);
319 assert_return(!journal_pid_changed(j
), -ECHILD
);
327 if (!j
->level1
->matches
)
336 _public_
int sd_journal_add_disjunction(sd_journal
*j
) {
337 assert_return(j
, -EINVAL
);
338 assert_return(!journal_pid_changed(j
), -ECHILD
);
349 if (!j
->level2
->matches
)
356 static char *match_make_string(Match
*m
) {
359 bool enclose
= false;
362 return strdup("none");
364 if (m
->type
== MATCH_DISCRETE
)
365 return strndup(m
->data
, m
->size
);
368 LIST_FOREACH(matches
, i
, m
->matches
) {
371 t
= match_make_string(i
);
378 k
= strjoin(p
, m
->type
== MATCH_OR_TERM
? " OR " : " AND ", t
, NULL
);
393 r
= strjoin("(", p
, ")", NULL
);
401 char *journal_make_match_string(sd_journal
*j
) {
404 return match_make_string(j
->level0
);
407 _public_
void sd_journal_flush_matches(sd_journal
*j
) {
412 match_free(j
->level0
);
414 j
->level0
= j
->level1
= j
->level2
= NULL
;
419 _pure_
static int compare_with_location(JournalFile
*f
, Location
*l
) {
422 assert(f
->location_type
== LOCATION_SEEK
);
423 assert(l
->type
== LOCATION_DISCRETE
|| l
->type
== LOCATION_SEEK
);
425 if (l
->monotonic_set
&&
426 sd_id128_equal(f
->current_boot_id
, l
->boot_id
) &&
428 f
->current_realtime
== l
->realtime
&&
430 f
->current_xor_hash
== l
->xor_hash
)
434 sd_id128_equal(f
->header
->seqnum_id
, l
->seqnum_id
)) {
436 if (f
->current_seqnum
< l
->seqnum
)
438 if (f
->current_seqnum
> l
->seqnum
)
442 if (l
->monotonic_set
&&
443 sd_id128_equal(f
->current_boot_id
, l
->boot_id
)) {
445 if (f
->current_monotonic
< l
->monotonic
)
447 if (f
->current_monotonic
> l
->monotonic
)
451 if (l
->realtime_set
) {
453 if (f
->current_realtime
< l
->realtime
)
455 if (f
->current_realtime
> l
->realtime
)
459 if (l
->xor_hash_set
) {
461 if (f
->current_xor_hash
< l
->xor_hash
)
463 if (f
->current_xor_hash
> l
->xor_hash
)
470 static int next_for_match(
474 uint64_t after_offset
,
475 direction_t direction
,
487 if (m
->type
== MATCH_DISCRETE
) {
490 r
= journal_file_find_data_object_with_hash(f
, m
->data
, m
->size
, le64toh(m
->le_hash
), NULL
, &dp
);
494 return journal_file_move_to_entry_by_offset_for_data(f
, dp
, after_offset
, direction
, ret
, offset
);
496 } else if (m
->type
== MATCH_OR_TERM
) {
499 /* Find the earliest match beyond after_offset */
501 LIST_FOREACH(matches
, i
, m
->matches
) {
504 r
= next_for_match(j
, i
, f
, after_offset
, direction
, NULL
, &cp
);
508 if (np
== 0 || (direction
== DIRECTION_DOWN
? cp
< np
: cp
> np
))
516 } else if (m
->type
== MATCH_AND_TERM
) {
517 Match
*i
, *last_moved
;
519 /* Always jump to the next matching entry and repeat
520 * this until we find an offset that matches for all
526 r
= next_for_match(j
, m
->matches
, f
, after_offset
, direction
, NULL
, &np
);
530 assert(direction
== DIRECTION_DOWN
? np
>= after_offset
: np
<= after_offset
);
531 last_moved
= m
->matches
;
533 LIST_LOOP_BUT_ONE(matches
, i
, m
->matches
, last_moved
) {
536 r
= next_for_match(j
, i
, f
, np
, direction
, NULL
, &cp
);
540 assert(direction
== DIRECTION_DOWN
? cp
>= np
: cp
<= np
);
541 if (direction
== DIRECTION_DOWN
? cp
> np
: cp
< np
) {
550 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, np
, &n
);
562 static int find_location_for_match(
566 direction_t direction
,
576 if (m
->type
== MATCH_DISCRETE
) {
579 r
= journal_file_find_data_object_with_hash(f
, m
->data
, m
->size
, le64toh(m
->le_hash
), NULL
, &dp
);
583 /* FIXME: missing: find by monotonic */
585 if (j
->current_location
.type
== LOCATION_HEAD
)
586 return journal_file_next_entry_for_data(f
, NULL
, 0, dp
, DIRECTION_DOWN
, ret
, offset
);
587 if (j
->current_location
.type
== LOCATION_TAIL
)
588 return journal_file_next_entry_for_data(f
, NULL
, 0, dp
, DIRECTION_UP
, ret
, offset
);
589 if (j
->current_location
.seqnum_set
&& sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
590 return journal_file_move_to_entry_by_seqnum_for_data(f
, dp
, j
->current_location
.seqnum
, direction
, ret
, offset
);
591 if (j
->current_location
.monotonic_set
) {
592 r
= journal_file_move_to_entry_by_monotonic_for_data(f
, dp
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, ret
, offset
);
596 if (j
->current_location
.realtime_set
)
597 return journal_file_move_to_entry_by_realtime_for_data(f
, dp
, j
->current_location
.realtime
, direction
, ret
, offset
);
599 return journal_file_next_entry_for_data(f
, NULL
, 0, dp
, direction
, ret
, offset
);
601 } else if (m
->type
== MATCH_OR_TERM
) {
606 /* Find the earliest match */
608 LIST_FOREACH(matches
, i
, m
->matches
) {
611 r
= find_location_for_match(j
, i
, f
, direction
, NULL
, &cp
);
615 if (np
== 0 || (direction
== DIRECTION_DOWN
? np
> cp
: np
< cp
))
623 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, np
, &n
);
638 assert(m
->type
== MATCH_AND_TERM
);
640 /* First jump to the last match, and then find the
641 * next one where all matches match */
646 LIST_FOREACH(matches
, i
, m
->matches
) {
649 r
= find_location_for_match(j
, i
, f
, direction
, NULL
, &cp
);
653 if (np
== 0 || (direction
== DIRECTION_DOWN
? cp
> np
: cp
< np
))
657 return next_for_match(j
, m
, f
, np
, direction
, ret
, offset
);
661 static int find_location_with_matches(
664 direction_t direction
,
676 /* No matches is simple */
678 if (j
->current_location
.type
== LOCATION_HEAD
)
679 return journal_file_next_entry(f
, 0, DIRECTION_DOWN
, ret
, offset
);
680 if (j
->current_location
.type
== LOCATION_TAIL
)
681 return journal_file_next_entry(f
, 0, DIRECTION_UP
, ret
, offset
);
682 if (j
->current_location
.seqnum_set
&& sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
683 return journal_file_move_to_entry_by_seqnum(f
, j
->current_location
.seqnum
, direction
, ret
, offset
);
684 if (j
->current_location
.monotonic_set
) {
685 r
= journal_file_move_to_entry_by_monotonic(f
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, ret
, offset
);
689 if (j
->current_location
.realtime_set
)
690 return journal_file_move_to_entry_by_realtime(f
, j
->current_location
.realtime
, direction
, ret
, offset
);
692 return journal_file_next_entry(f
, 0, direction
, ret
, offset
);
694 return find_location_for_match(j
, j
->level0
, f
, direction
, ret
, offset
);
697 static int next_with_matches(
700 direction_t direction
,
709 /* No matches is easy. We simple advance the file
712 return journal_file_next_entry(f
, f
->current_offset
, direction
, ret
, offset
);
714 /* If we have a match then we look for the next matching entry
715 * with an offset at least one step larger */
716 return next_for_match(j
, j
->level0
, f
,
717 direction
== DIRECTION_DOWN
? f
->current_offset
+ 1
718 : f
->current_offset
- 1,
719 direction
, ret
, offset
);
722 static int next_beyond_location(sd_journal
*j
, JournalFile
*f
, direction_t direction
) {
724 uint64_t cp
, n_entries
;
730 n_entries
= le64toh(f
->header
->n_entries
);
732 /* If we hit EOF before, we don't need to look into this file again
733 * unless direction changed or new entries appeared. */
734 if (f
->last_direction
== direction
&& f
->location_type
== LOCATION_TAIL
&&
735 n_entries
== f
->last_n_entries
)
738 f
->last_n_entries
= n_entries
;
740 if (f
->last_direction
== direction
&& f
->current_offset
> 0) {
741 /* LOCATION_SEEK here means we did the work in a previous
742 * iteration and the current location already points to a
743 * candidate entry. */
744 if (f
->location_type
!= LOCATION_SEEK
) {
745 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
749 journal_file_save_location(f
, c
, cp
);
752 f
->last_direction
= direction
;
754 r
= find_location_with_matches(j
, f
, direction
, &c
, &cp
);
758 journal_file_save_location(f
, c
, cp
);
761 /* OK, we found the spot, now let's advance until an entry
762 * that is actually different from what we were previously
763 * looking at. This is necessary to handle entries which exist
764 * in two (or more) journal files, and which shall all be
765 * suppressed but one. */
770 if (j
->current_location
.type
== LOCATION_DISCRETE
) {
773 k
= compare_with_location(f
, &j
->current_location
);
775 found
= direction
== DIRECTION_DOWN
? k
> 0 : k
< 0;
782 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
786 journal_file_save_location(f
, c
, cp
);
790 static int real_journal_next(sd_journal
*j
, direction_t direction
) {
791 JournalFile
*f
, *new_file
= NULL
;
796 assert_return(j
, -EINVAL
);
797 assert_return(!journal_pid_changed(j
), -ECHILD
);
799 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
802 r
= next_beyond_location(j
, f
, direction
);
804 log_debug_errno(r
, "Can't iterate through %s, ignoring: %m", f
->path
);
805 remove_file_real(j
, f
);
808 f
->location_type
= LOCATION_TAIL
;
817 k
= journal_file_compare_locations(f
, new_file
);
819 found
= direction
== DIRECTION_DOWN
? k
< 0 : k
> 0;
829 r
= journal_file_move_to_object(new_file
, OBJECT_ENTRY
, new_file
->current_offset
, &o
);
833 set_location(j
, new_file
, o
);
838 _public_
int sd_journal_next(sd_journal
*j
) {
839 return real_journal_next(j
, DIRECTION_DOWN
);
842 _public_
int sd_journal_previous(sd_journal
*j
) {
843 return real_journal_next(j
, DIRECTION_UP
);
846 static int real_journal_next_skip(sd_journal
*j
, direction_t direction
, uint64_t skip
) {
849 assert_return(j
, -EINVAL
);
850 assert_return(!journal_pid_changed(j
), -ECHILD
);
853 /* If this is not a discrete skip, then at least
854 * resolve the current location */
855 if (j
->current_location
.type
!= LOCATION_DISCRETE
)
856 return real_journal_next(j
, direction
);
862 r
= real_journal_next(j
, direction
);
876 _public_
int sd_journal_next_skip(sd_journal
*j
, uint64_t skip
) {
877 return real_journal_next_skip(j
, DIRECTION_DOWN
, skip
);
880 _public_
int sd_journal_previous_skip(sd_journal
*j
, uint64_t skip
) {
881 return real_journal_next_skip(j
, DIRECTION_UP
, skip
);
884 _public_
int sd_journal_get_cursor(sd_journal
*j
, char **cursor
) {
887 char bid
[33], sid
[33];
889 assert_return(j
, -EINVAL
);
890 assert_return(!journal_pid_changed(j
), -ECHILD
);
891 assert_return(cursor
, -EINVAL
);
893 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
894 return -EADDRNOTAVAIL
;
896 r
= journal_file_move_to_object(j
->current_file
, OBJECT_ENTRY
, j
->current_file
->current_offset
, &o
);
900 sd_id128_to_string(j
->current_file
->header
->seqnum_id
, sid
);
901 sd_id128_to_string(o
->entry
.boot_id
, bid
);
904 "s=%s;i=%"PRIx64
";b=%s;m=%"PRIx64
";t=%"PRIx64
";x=%"PRIx64
,
905 sid
, le64toh(o
->entry
.seqnum
),
906 bid
, le64toh(o
->entry
.monotonic
),
907 le64toh(o
->entry
.realtime
),
908 le64toh(o
->entry
.xor_hash
)) < 0)
914 _public_
int sd_journal_seek_cursor(sd_journal
*j
, const char *cursor
) {
915 const char *word
, *state
;
917 unsigned long long seqnum
, monotonic
, realtime
, xor_hash
;
919 seqnum_id_set
= false,
922 monotonic_set
= false,
923 realtime_set
= false,
924 xor_hash_set
= false;
925 sd_id128_t seqnum_id
, boot_id
;
927 assert_return(j
, -EINVAL
);
928 assert_return(!journal_pid_changed(j
), -ECHILD
);
929 assert_return(!isempty(cursor
), -EINVAL
);
931 FOREACH_WORD_SEPARATOR(word
, l
, cursor
, ";", state
) {
935 if (l
< 2 || word
[1] != '=')
938 item
= strndup(word
, l
);
945 seqnum_id_set
= true;
946 k
= sd_id128_from_string(item
+2, &seqnum_id
);
951 if (sscanf(item
+2, "%llx", &seqnum
) != 1)
957 k
= sd_id128_from_string(item
+2, &boot_id
);
961 monotonic_set
= true;
962 if (sscanf(item
+2, "%llx", &monotonic
) != 1)
968 if (sscanf(item
+2, "%llx", &realtime
) != 1)
974 if (sscanf(item
+2, "%llx", &xor_hash
) != 1)
985 if ((!seqnum_set
|| !seqnum_id_set
) &&
986 (!monotonic_set
|| !boot_id_set
) &&
992 j
->current_location
.type
= LOCATION_SEEK
;
995 j
->current_location
.realtime
= (uint64_t) realtime
;
996 j
->current_location
.realtime_set
= true;
999 if (seqnum_set
&& seqnum_id_set
) {
1000 j
->current_location
.seqnum
= (uint64_t) seqnum
;
1001 j
->current_location
.seqnum_id
= seqnum_id
;
1002 j
->current_location
.seqnum_set
= true;
1005 if (monotonic_set
&& boot_id_set
) {
1006 j
->current_location
.monotonic
= (uint64_t) monotonic
;
1007 j
->current_location
.boot_id
= boot_id
;
1008 j
->current_location
.monotonic_set
= true;
1012 j
->current_location
.xor_hash
= (uint64_t) xor_hash
;
1013 j
->current_location
.xor_hash_set
= true;
1019 _public_
int sd_journal_test_cursor(sd_journal
*j
, const char *cursor
) {
1021 const char *word
, *state
;
1025 assert_return(j
, -EINVAL
);
1026 assert_return(!journal_pid_changed(j
), -ECHILD
);
1027 assert_return(!isempty(cursor
), -EINVAL
);
1029 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
1030 return -EADDRNOTAVAIL
;
1032 r
= journal_file_move_to_object(j
->current_file
, OBJECT_ENTRY
, j
->current_file
->current_offset
, &o
);
1036 FOREACH_WORD_SEPARATOR(word
, l
, cursor
, ";", state
) {
1037 _cleanup_free_
char *item
= NULL
;
1039 unsigned long long ll
;
1042 if (l
< 2 || word
[1] != '=')
1045 item
= strndup(word
, l
);
1052 k
= sd_id128_from_string(item
+2, &id
);
1055 if (!sd_id128_equal(id
, j
->current_file
->header
->seqnum_id
))
1060 if (sscanf(item
+2, "%llx", &ll
) != 1)
1062 if (ll
!= le64toh(o
->entry
.seqnum
))
1067 k
= sd_id128_from_string(item
+2, &id
);
1070 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
1075 if (sscanf(item
+2, "%llx", &ll
) != 1)
1077 if (ll
!= le64toh(o
->entry
.monotonic
))
1082 if (sscanf(item
+2, "%llx", &ll
) != 1)
1084 if (ll
!= le64toh(o
->entry
.realtime
))
1089 if (sscanf(item
+2, "%llx", &ll
) != 1)
1091 if (ll
!= le64toh(o
->entry
.xor_hash
))
1101 _public_
int sd_journal_seek_monotonic_usec(sd_journal
*j
, sd_id128_t boot_id
, uint64_t usec
) {
1102 assert_return(j
, -EINVAL
);
1103 assert_return(!journal_pid_changed(j
), -ECHILD
);
1106 j
->current_location
.type
= LOCATION_SEEK
;
1107 j
->current_location
.boot_id
= boot_id
;
1108 j
->current_location
.monotonic
= usec
;
1109 j
->current_location
.monotonic_set
= true;
1114 _public_
int sd_journal_seek_realtime_usec(sd_journal
*j
, uint64_t usec
) {
1115 assert_return(j
, -EINVAL
);
1116 assert_return(!journal_pid_changed(j
), -ECHILD
);
1119 j
->current_location
.type
= LOCATION_SEEK
;
1120 j
->current_location
.realtime
= usec
;
1121 j
->current_location
.realtime_set
= true;
1126 _public_
int sd_journal_seek_head(sd_journal
*j
) {
1127 assert_return(j
, -EINVAL
);
1128 assert_return(!journal_pid_changed(j
), -ECHILD
);
1131 j
->current_location
.type
= LOCATION_HEAD
;
1136 _public_
int sd_journal_seek_tail(sd_journal
*j
) {
1137 assert_return(j
, -EINVAL
);
1138 assert_return(!journal_pid_changed(j
), -ECHILD
);
1141 j
->current_location
.type
= LOCATION_TAIL
;
1146 static void check_network(sd_journal
*j
, int fd
) {
1154 if (fstatfs(fd
, &sfs
) < 0)
1158 F_TYPE_EQUAL(sfs
.f_type
, CIFS_MAGIC_NUMBER
) ||
1159 F_TYPE_EQUAL(sfs
.f_type
, CODA_SUPER_MAGIC
) ||
1160 F_TYPE_EQUAL(sfs
.f_type
, NCP_SUPER_MAGIC
) ||
1161 F_TYPE_EQUAL(sfs
.f_type
, NFS_SUPER_MAGIC
) ||
1162 F_TYPE_EQUAL(sfs
.f_type
, SMB_SUPER_MAGIC
);
1165 static bool file_has_type_prefix(const char *prefix
, const char *filename
) {
1166 const char *full
, *tilded
, *atted
;
1168 full
= strjoina(prefix
, ".journal");
1169 tilded
= strjoina(full
, "~");
1170 atted
= strjoina(prefix
, "@");
1172 return streq(filename
, full
) ||
1173 streq(filename
, tilded
) ||
1174 startswith(filename
, atted
);
1177 static bool file_type_wanted(int flags
, const char *filename
) {
1178 if (!endswith(filename
, ".journal") && !endswith(filename
, ".journal~"))
1181 /* no flags set → every type is OK */
1182 if (!(flags
& (SD_JOURNAL_SYSTEM
| SD_JOURNAL_CURRENT_USER
)))
1185 if (flags
& SD_JOURNAL_SYSTEM
&& file_has_type_prefix("system", filename
))
1188 if (flags
& SD_JOURNAL_CURRENT_USER
) {
1189 char prefix
[5 + DECIMAL_STR_MAX(uid_t
) + 1];
1191 xsprintf(prefix
, "user-"UID_FMT
, getuid());
1193 if (file_has_type_prefix(prefix
, filename
))
1200 static int add_any_file(sd_journal
*j
, const char *path
) {
1201 JournalFile
*f
= NULL
;
1207 if (ordered_hashmap_get(j
->files
, path
))
1210 if (ordered_hashmap_size(j
->files
) >= JOURNAL_FILES_MAX
) {
1211 log_warning("Too many open journal files, not adding %s.", path
);
1212 return set_put_error(j
, -ETOOMANYREFS
);
1215 r
= journal_file_open(path
, O_RDONLY
, 0, false, false, NULL
, j
->mmap
, NULL
, &f
);
1219 /* journal_file_dump(f); */
1221 r
= ordered_hashmap_put(j
->files
, f
->path
, f
);
1223 journal_file_close(f
);
1227 log_debug("File %s added.", f
->path
);
1229 check_network(j
, f
->fd
);
1231 j
->current_invalidate_counter
++;
1236 static int add_file(sd_journal
*j
, const char *prefix
, const char *filename
) {
1237 _cleanup_free_
char *path
= NULL
;
1244 if (j
->no_new_files
||
1245 !file_type_wanted(j
->flags
, filename
))
1248 path
= strjoin(prefix
, "/", filename
, NULL
);
1252 r
= add_any_file(j
, path
);
1258 static int remove_file(sd_journal
*j
, const char *prefix
, const char *filename
) {
1259 _cleanup_free_
char *path
;
1266 path
= strjoin(prefix
, "/", filename
, NULL
);
1270 f
= ordered_hashmap_get(j
->files
, path
);
1274 remove_file_real(j
, f
);
1278 static void remove_file_real(sd_journal
*j
, JournalFile
*f
) {
1282 ordered_hashmap_remove(j
->files
, f
->path
);
1284 log_debug("File %s removed.", f
->path
);
1286 if (j
->current_file
== f
) {
1287 j
->current_file
= NULL
;
1288 j
->current_field
= 0;
1291 if (j
->unique_file
== f
) {
1292 /* Jump to the next unique_file or NULL if that one was last */
1293 j
->unique_file
= ordered_hashmap_next(j
->files
, j
->unique_file
->path
);
1294 j
->unique_offset
= 0;
1295 if (!j
->unique_file
)
1296 j
->unique_file_lost
= true;
1299 journal_file_close(f
);
1301 j
->current_invalidate_counter
++;
1304 static int add_directory(sd_journal
*j
, const char *prefix
, const char *dirname
) {
1305 _cleanup_free_
char *path
= NULL
;
1307 _cleanup_closedir_
DIR *d
= NULL
;
1315 log_debug("Considering %s/%s.", prefix
, dirname
);
1317 if ((j
->flags
& SD_JOURNAL_LOCAL_ONLY
) &&
1318 (sd_id128_from_string(dirname
, &id
) < 0 ||
1319 sd_id128_get_machine(&mid
) < 0 ||
1320 !(sd_id128_equal(id
, mid
) || path_startswith(prefix
, "/run"))))
1323 path
= strjoin(prefix
, "/", dirname
, NULL
);
1329 log_debug_errno(errno
, "Failed to open %s: %m", path
);
1330 if (errno
== ENOENT
)
1335 m
= hashmap_get(j
->directories_by_path
, path
);
1337 m
= new0(Directory
, 1);
1344 if (hashmap_put(j
->directories_by_path
, m
->path
, m
) < 0) {
1349 path
= NULL
; /* avoid freeing in cleanup */
1350 j
->current_invalidate_counter
++;
1352 log_debug("Directory %s added.", m
->path
);
1354 } else if (m
->is_root
)
1357 if (m
->wd
<= 0 && j
->inotify_fd
>= 0) {
1359 m
->wd
= inotify_add_watch(j
->inotify_fd
, m
->path
,
1360 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1361 IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
|IN_MOVED_FROM
|
1364 if (m
->wd
> 0 && hashmap_put(j
->directories_by_wd
, INT_TO_PTR(m
->wd
), m
) < 0)
1365 inotify_rm_watch(j
->inotify_fd
, m
->wd
);
1373 if (!de
&& errno
!= 0) {
1375 log_debug_errno(errno
, "Failed to read directory %s: %m", m
->path
);
1381 if (dirent_is_file_with_suffix(de
, ".journal") ||
1382 dirent_is_file_with_suffix(de
, ".journal~")) {
1383 r
= add_file(j
, m
->path
, de
->d_name
);
1385 log_debug_errno(r
, "Failed to add file %s/%s: %m",
1386 m
->path
, de
->d_name
);
1387 r
= set_put_error(j
, r
);
1394 check_network(j
, dirfd(d
));
1399 static int add_root_directory(sd_journal
*j
, const char *p
) {
1400 _cleanup_closedir_
DIR *d
= NULL
;
1407 if ((j
->flags
& SD_JOURNAL_RUNTIME_ONLY
) &&
1408 !path_startswith(p
, "/run"))
1412 p
= strjoina(j
->prefix
, p
);
1418 m
= hashmap_get(j
->directories_by_path
, p
);
1420 m
= new0(Directory
, 1);
1425 m
->path
= strdup(p
);
1431 if (hashmap_put(j
->directories_by_path
, m
->path
, m
) < 0) {
1437 j
->current_invalidate_counter
++;
1439 log_debug("Root directory %s added.", m
->path
);
1441 } else if (!m
->is_root
)
1444 if (m
->wd
<= 0 && j
->inotify_fd
>= 0) {
1446 m
->wd
= inotify_add_watch(j
->inotify_fd
, m
->path
,
1447 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1450 if (m
->wd
> 0 && hashmap_put(j
->directories_by_wd
, INT_TO_PTR(m
->wd
), m
) < 0)
1451 inotify_rm_watch(j
->inotify_fd
, m
->wd
);
1454 if (j
->no_new_files
)
1463 if (!de
&& errno
!= 0) {
1465 log_debug_errno(errno
, "Failed to read directory %s: %m", m
->path
);
1471 if (dirent_is_file_with_suffix(de
, ".journal") ||
1472 dirent_is_file_with_suffix(de
, ".journal~")) {
1473 r
= add_file(j
, m
->path
, de
->d_name
);
1475 log_debug_errno(r
, "Failed to add file %s/%s: %m",
1476 m
->path
, de
->d_name
);
1477 r
= set_put_error(j
, r
);
1481 } else if ((de
->d_type
== DT_DIR
|| de
->d_type
== DT_LNK
|| de
->d_type
== DT_UNKNOWN
) &&
1482 sd_id128_from_string(de
->d_name
, &id
) >= 0) {
1484 r
= add_directory(j
, m
->path
, de
->d_name
);
1486 log_debug_errno(r
, "Failed to add directory %s/%s: %m", m
->path
, de
->d_name
);
1490 check_network(j
, dirfd(d
));
1495 static void remove_directory(sd_journal
*j
, Directory
*d
) {
1499 hashmap_remove(j
->directories_by_wd
, INT_TO_PTR(d
->wd
));
1501 if (j
->inotify_fd
>= 0)
1502 inotify_rm_watch(j
->inotify_fd
, d
->wd
);
1505 hashmap_remove(j
->directories_by_path
, d
->path
);
1508 log_debug("Root directory %s removed.", d
->path
);
1510 log_debug("Directory %s removed.", d
->path
);
1516 static int add_search_paths(sd_journal
*j
) {
1518 const char search_paths
[] =
1519 "/run/log/journal\0"
1520 "/var/log/journal\0";
1525 /* We ignore most errors here, since the idea is to only open
1526 * what's actually accessible, and ignore the rest. */
1528 NULSTR_FOREACH(p
, search_paths
) {
1529 r
= add_root_directory(j
, p
);
1530 if (r
< 0 && r
!= -ENOENT
) {
1531 r
= set_put_error(j
, r
);
1540 static int add_current_paths(sd_journal
*j
) {
1545 assert(j
->no_new_files
);
1547 /* Simply adds all directories for files we have open as
1548 * "root" directories. We don't expect errors here, so we
1549 * treat them as fatal. */
1551 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
1552 _cleanup_free_
char *dir
;
1555 dir
= dirname_malloc(f
->path
);
1559 r
= add_root_directory(j
, dir
);
1561 set_put_error(j
, r
);
1570 static int allocate_inotify(sd_journal
*j
) {
1573 if (j
->inotify_fd
< 0) {
1574 j
->inotify_fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
1575 if (j
->inotify_fd
< 0)
1579 if (!j
->directories_by_wd
) {
1580 j
->directories_by_wd
= hashmap_new(NULL
);
1581 if (!j
->directories_by_wd
)
1588 static sd_journal
*journal_new(int flags
, const char *path
) {
1591 j
= new0(sd_journal
, 1);
1595 j
->original_pid
= getpid();
1598 j
->data_threshold
= DEFAULT_DATA_THRESHOLD
;
1601 j
->path
= strdup(path
);
1606 j
->files
= ordered_hashmap_new(&string_hash_ops
);
1607 j
->directories_by_path
= hashmap_new(&string_hash_ops
);
1608 j
->mmap
= mmap_cache_new();
1609 if (!j
->files
|| !j
->directories_by_path
|| !j
->mmap
)
1615 sd_journal_close(j
);
1619 _public_
int sd_journal_open(sd_journal
**ret
, int flags
) {
1623 assert_return(ret
, -EINVAL
);
1624 assert_return((flags
& ~(SD_JOURNAL_LOCAL_ONLY
|SD_JOURNAL_RUNTIME_ONLY
|SD_JOURNAL_SYSTEM
|SD_JOURNAL_CURRENT_USER
)) == 0, -EINVAL
);
1626 j
= journal_new(flags
, NULL
);
1630 r
= add_search_paths(j
);
1638 sd_journal_close(j
);
1643 _public_
int sd_journal_open_container(sd_journal
**ret
, const char *machine
, int flags
) {
1644 _cleanup_free_
char *root
= NULL
, *class = NULL
;
1649 assert_return(machine
, -EINVAL
);
1650 assert_return(ret
, -EINVAL
);
1651 assert_return((flags
& ~(SD_JOURNAL_LOCAL_ONLY
|SD_JOURNAL_SYSTEM
)) == 0, -EINVAL
);
1652 assert_return(machine_name_is_valid(machine
), -EINVAL
);
1654 p
= strjoina("/run/systemd/machines/", machine
);
1655 r
= parse_env_file(p
, NEWLINE
, "ROOT", &root
, "CLASS", &class, NULL
);
1663 if (!streq_ptr(class, "container"))
1666 j
= journal_new(flags
, NULL
);
1673 r
= add_search_paths(j
);
1681 sd_journal_close(j
);
1685 _public_
int sd_journal_open_directory(sd_journal
**ret
, const char *path
, int flags
) {
1689 assert_return(ret
, -EINVAL
);
1690 assert_return(path
, -EINVAL
);
1691 assert_return(flags
== 0, -EINVAL
);
1693 j
= journal_new(flags
, path
);
1697 r
= add_root_directory(j
, path
);
1699 set_put_error(j
, r
);
1707 sd_journal_close(j
);
1712 _public_
int sd_journal_open_files(sd_journal
**ret
, const char **paths
, int flags
) {
1717 assert_return(ret
, -EINVAL
);
1718 assert_return(flags
== 0, -EINVAL
);
1720 j
= journal_new(flags
, NULL
);
1724 STRV_FOREACH(path
, paths
) {
1725 r
= add_any_file(j
, *path
);
1727 log_error_errno(r
, "Failed to open %s: %m", *path
);
1732 j
->no_new_files
= true;
1738 sd_journal_close(j
);
1743 _public_
void sd_journal_close(sd_journal
*j
) {
1750 sd_journal_flush_matches(j
);
1752 while ((f
= ordered_hashmap_steal_first(j
->files
)))
1753 journal_file_close(f
);
1755 ordered_hashmap_free(j
->files
);
1757 while ((d
= hashmap_first(j
->directories_by_path
)))
1758 remove_directory(j
, d
);
1760 while ((d
= hashmap_first(j
->directories_by_wd
)))
1761 remove_directory(j
, d
);
1763 hashmap_free(j
->directories_by_path
);
1764 hashmap_free(j
->directories_by_wd
);
1766 safe_close(j
->inotify_fd
);
1769 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j
->mmap
), mmap_cache_get_missed(j
->mmap
));
1770 mmap_cache_unref(j
->mmap
);
1775 free(j
->unique_field
);
1776 set_free(j
->errors
);
1780 _public_
int sd_journal_get_realtime_usec(sd_journal
*j
, uint64_t *ret
) {
1785 assert_return(j
, -EINVAL
);
1786 assert_return(!journal_pid_changed(j
), -ECHILD
);
1787 assert_return(ret
, -EINVAL
);
1789 f
= j
->current_file
;
1791 return -EADDRNOTAVAIL
;
1793 if (f
->current_offset
<= 0)
1794 return -EADDRNOTAVAIL
;
1796 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1800 *ret
= le64toh(o
->entry
.realtime
);
1804 _public_
int sd_journal_get_monotonic_usec(sd_journal
*j
, uint64_t *ret
, sd_id128_t
*ret_boot_id
) {
1810 assert_return(j
, -EINVAL
);
1811 assert_return(!journal_pid_changed(j
), -ECHILD
);
1813 f
= j
->current_file
;
1815 return -EADDRNOTAVAIL
;
1817 if (f
->current_offset
<= 0)
1818 return -EADDRNOTAVAIL
;
1820 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1825 *ret_boot_id
= o
->entry
.boot_id
;
1827 r
= sd_id128_get_boot(&id
);
1831 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
1836 *ret
= le64toh(o
->entry
.monotonic
);
1841 static bool field_is_valid(const char *field
) {
1849 if (startswith(field
, "__"))
1852 for (p
= field
; *p
; p
++) {
1857 if (*p
>= 'A' && *p
<= 'Z')
1860 if (*p
>= '0' && *p
<= '9')
1869 _public_
int sd_journal_get_data(sd_journal
*j
, const char *field
, const void **data
, size_t *size
) {
1872 size_t field_length
;
1876 assert_return(j
, -EINVAL
);
1877 assert_return(!journal_pid_changed(j
), -ECHILD
);
1878 assert_return(field
, -EINVAL
);
1879 assert_return(data
, -EINVAL
);
1880 assert_return(size
, -EINVAL
);
1881 assert_return(field_is_valid(field
), -EINVAL
);
1883 f
= j
->current_file
;
1885 return -EADDRNOTAVAIL
;
1887 if (f
->current_offset
<= 0)
1888 return -EADDRNOTAVAIL
;
1890 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1894 field_length
= strlen(field
);
1896 n
= journal_file_entry_n_items(o
);
1897 for (i
= 0; i
< n
; i
++) {
1903 p
= le64toh(o
->entry
.items
[i
].object_offset
);
1904 le_hash
= o
->entry
.items
[i
].hash
;
1905 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
1909 if (le_hash
!= o
->data
.hash
)
1912 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
1914 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
1916 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1917 if (decompress_startswith(compression
,
1919 &f
->compress_buffer
, &f
->compress_buffer_size
,
1920 field
, field_length
, '=')) {
1924 r
= decompress_blob(compression
,
1926 &f
->compress_buffer
, &f
->compress_buffer_size
, &rsize
,
1931 *data
= f
->compress_buffer
;
1932 *size
= (size_t) rsize
;
1937 return -EPROTONOSUPPORT
;
1939 } else if (l
>= field_length
+1 &&
1940 memcmp(o
->data
.payload
, field
, field_length
) == 0 &&
1941 o
->data
.payload
[field_length
] == '=') {
1945 if ((uint64_t) t
!= l
)
1948 *data
= o
->data
.payload
;
1954 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1962 static int return_data(sd_journal
*j
, JournalFile
*f
, Object
*o
, const void **data
, size_t *size
) {
1967 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
1970 /* We can't read objects larger than 4G on a 32bit machine */
1971 if ((uint64_t) t
!= l
)
1974 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
1976 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
1980 r
= decompress_blob(compression
,
1981 o
->data
.payload
, l
, &f
->compress_buffer
,
1982 &f
->compress_buffer_size
, &rsize
, j
->data_threshold
);
1986 *data
= f
->compress_buffer
;
1987 *size
= (size_t) rsize
;
1989 return -EPROTONOSUPPORT
;
1992 *data
= o
->data
.payload
;
1999 _public_
int sd_journal_enumerate_data(sd_journal
*j
, const void **data
, size_t *size
) {
2006 assert_return(j
, -EINVAL
);
2007 assert_return(!journal_pid_changed(j
), -ECHILD
);
2008 assert_return(data
, -EINVAL
);
2009 assert_return(size
, -EINVAL
);
2011 f
= j
->current_file
;
2013 return -EADDRNOTAVAIL
;
2015 if (f
->current_offset
<= 0)
2016 return -EADDRNOTAVAIL
;
2018 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
2022 n
= journal_file_entry_n_items(o
);
2023 if (j
->current_field
>= n
)
2026 p
= le64toh(o
->entry
.items
[j
->current_field
].object_offset
);
2027 le_hash
= o
->entry
.items
[j
->current_field
].hash
;
2028 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
2032 if (le_hash
!= o
->data
.hash
)
2035 r
= return_data(j
, f
, o
, data
, size
);
2039 j
->current_field
++;
2044 _public_
void sd_journal_restart_data(sd_journal
*j
) {
2048 j
->current_field
= 0;
2051 _public_
int sd_journal_get_fd(sd_journal
*j
) {
2054 assert_return(j
, -EINVAL
);
2055 assert_return(!journal_pid_changed(j
), -ECHILD
);
2057 if (j
->inotify_fd
>= 0)
2058 return j
->inotify_fd
;
2060 r
= allocate_inotify(j
);
2064 /* Iterate through all dirs again, to add them to the
2066 if (j
->no_new_files
)
2067 r
= add_current_paths(j
);
2069 r
= add_root_directory(j
, j
->path
);
2071 r
= add_search_paths(j
);
2075 return j
->inotify_fd
;
2078 _public_
int sd_journal_get_events(sd_journal
*j
) {
2081 assert_return(j
, -EINVAL
);
2082 assert_return(!journal_pid_changed(j
), -ECHILD
);
2084 fd
= sd_journal_get_fd(j
);
2091 _public_
int sd_journal_get_timeout(sd_journal
*j
, uint64_t *timeout_usec
) {
2094 assert_return(j
, -EINVAL
);
2095 assert_return(!journal_pid_changed(j
), -ECHILD
);
2096 assert_return(timeout_usec
, -EINVAL
);
2098 fd
= sd_journal_get_fd(j
);
2102 if (!j
->on_network
) {
2103 *timeout_usec
= (uint64_t) -1;
2107 /* If we are on the network we need to regularly check for
2108 * changes manually */
2110 *timeout_usec
= j
->last_process_usec
+ JOURNAL_FILES_RECHECK_USEC
;
2114 static void process_inotify_event(sd_journal
*j
, struct inotify_event
*e
) {
2121 /* Is this a subdirectory we watch? */
2122 d
= hashmap_get(j
->directories_by_wd
, INT_TO_PTR(e
->wd
));
2126 if (!(e
->mask
& IN_ISDIR
) && e
->len
> 0 &&
2127 (endswith(e
->name
, ".journal") ||
2128 endswith(e
->name
, ".journal~"))) {
2130 /* Event for a journal file */
2132 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
)) {
2133 r
= add_file(j
, d
->path
, e
->name
);
2135 log_debug_errno(r
, "Failed to add file %s/%s: %m",
2137 set_put_error(j
, r
);
2140 } else if (e
->mask
& (IN_DELETE
|IN_MOVED_FROM
|IN_UNMOUNT
)) {
2142 r
= remove_file(j
, d
->path
, e
->name
);
2144 log_debug_errno(r
, "Failed to remove file %s/%s: %m", d
->path
, e
->name
);
2147 } else if (!d
->is_root
&& e
->len
== 0) {
2149 /* Event for a subdirectory */
2151 if (e
->mask
& (IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
))
2152 remove_directory(j
, d
);
2154 } else if (d
->is_root
&& (e
->mask
& IN_ISDIR
) && e
->len
> 0 && sd_id128_from_string(e
->name
, &id
) >= 0) {
2156 /* Event for root directory */
2158 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
)) {
2159 r
= add_directory(j
, d
->path
, e
->name
);
2161 log_debug_errno(r
, "Failed to add directory %s/%s: %m", d
->path
, e
->name
);
2168 if (e
->mask
& IN_IGNORED
)
2171 log_warning("Unknown inotify event.");
2174 static int determine_change(sd_journal
*j
) {
2179 b
= j
->current_invalidate_counter
!= j
->last_invalidate_counter
;
2180 j
->last_invalidate_counter
= j
->current_invalidate_counter
;
2182 return b
? SD_JOURNAL_INVALIDATE
: SD_JOURNAL_APPEND
;
2185 _public_
int sd_journal_process(sd_journal
*j
) {
2186 bool got_something
= false;
2188 assert_return(j
, -EINVAL
);
2189 assert_return(!journal_pid_changed(j
), -ECHILD
);
2191 j
->last_process_usec
= now(CLOCK_MONOTONIC
);
2194 union inotify_event_buffer buffer
;
2195 struct inotify_event
*e
;
2198 l
= read(j
->inotify_fd
, &buffer
, sizeof(buffer
));
2200 if (errno
== EAGAIN
|| errno
== EINTR
)
2201 return got_something
? determine_change(j
) : SD_JOURNAL_NOP
;
2206 got_something
= true;
2208 FOREACH_INOTIFY_EVENT(e
, buffer
, l
)
2209 process_inotify_event(j
, e
);
2213 _public_
int sd_journal_wait(sd_journal
*j
, uint64_t timeout_usec
) {
2217 assert_return(j
, -EINVAL
);
2218 assert_return(!journal_pid_changed(j
), -ECHILD
);
2220 if (j
->inotify_fd
< 0) {
2222 /* This is the first invocation, hence create the
2224 r
= sd_journal_get_fd(j
);
2228 /* The journal might have changed since the context
2229 * object was created and we weren't watching before,
2230 * hence don't wait for anything, and return
2232 return determine_change(j
);
2235 r
= sd_journal_get_timeout(j
, &t
);
2239 if (t
!= (uint64_t) -1) {
2242 n
= now(CLOCK_MONOTONIC
);
2243 t
= t
> n
? t
- n
: 0;
2245 if (timeout_usec
== (uint64_t) -1 || timeout_usec
> t
)
2250 r
= fd_wait_for_event(j
->inotify_fd
, POLLIN
, timeout_usec
);
2251 } while (r
== -EINTR
);
2256 return sd_journal_process(j
);
2259 _public_
int sd_journal_get_cutoff_realtime_usec(sd_journal
*j
, uint64_t *from
, uint64_t *to
) {
2263 uint64_t fmin
= 0, tmax
= 0;
2266 assert_return(j
, -EINVAL
);
2267 assert_return(!journal_pid_changed(j
), -ECHILD
);
2268 assert_return(from
|| to
, -EINVAL
);
2269 assert_return(from
!= to
, -EINVAL
);
2271 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
2274 r
= journal_file_get_cutoff_realtime_usec(f
, &fr
, &t
);
2287 fmin
= MIN(fr
, fmin
);
2288 tmax
= MAX(t
, tmax
);
2297 return first
? 0 : 1;
2300 _public_
int sd_journal_get_cutoff_monotonic_usec(sd_journal
*j
, sd_id128_t boot_id
, uint64_t *from
, uint64_t *to
) {
2306 assert_return(j
, -EINVAL
);
2307 assert_return(!journal_pid_changed(j
), -ECHILD
);
2308 assert_return(from
|| to
, -EINVAL
);
2309 assert_return(from
!= to
, -EINVAL
);
2311 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
2314 r
= journal_file_get_cutoff_monotonic_usec(f
, boot_id
, &fr
, &t
);
2324 *from
= MIN(fr
, *from
);
2339 void journal_print_header(sd_journal
*j
) {
2342 bool newline
= false;
2346 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
2352 journal_file_print_header(f
);
2356 _public_
int sd_journal_get_usage(sd_journal
*j
, uint64_t *bytes
) {
2361 assert_return(j
, -EINVAL
);
2362 assert_return(!journal_pid_changed(j
), -ECHILD
);
2363 assert_return(bytes
, -EINVAL
);
2365 ORDERED_HASHMAP_FOREACH(f
, j
->files
, i
) {
2368 if (fstat(f
->fd
, &st
) < 0)
2371 sum
+= (uint64_t) st
.st_blocks
* 512ULL;
2378 _public_
int sd_journal_query_unique(sd_journal
*j
, const char *field
) {
2381 assert_return(j
, -EINVAL
);
2382 assert_return(!journal_pid_changed(j
), -ECHILD
);
2383 assert_return(!isempty(field
), -EINVAL
);
2384 assert_return(field_is_valid(field
), -EINVAL
);
2390 free(j
->unique_field
);
2391 j
->unique_field
= f
;
2392 j
->unique_file
= NULL
;
2393 j
->unique_offset
= 0;
2394 j
->unique_file_lost
= false;
2399 _public_
int sd_journal_enumerate_unique(sd_journal
*j
, const void **data
, size_t *l
) {
2402 assert_return(j
, -EINVAL
);
2403 assert_return(!journal_pid_changed(j
), -ECHILD
);
2404 assert_return(data
, -EINVAL
);
2405 assert_return(l
, -EINVAL
);
2406 assert_return(j
->unique_field
, -EINVAL
);
2408 k
= strlen(j
->unique_field
);
2410 if (!j
->unique_file
) {
2411 if (j
->unique_file_lost
)
2414 j
->unique_file
= ordered_hashmap_first(j
->files
);
2415 if (!j
->unique_file
)
2418 j
->unique_offset
= 0;
2430 /* Proceed to next data object in the field's linked list */
2431 if (j
->unique_offset
== 0) {
2432 r
= journal_file_find_field_object(j
->unique_file
, j
->unique_field
, k
, &o
, NULL
);
2436 j
->unique_offset
= r
> 0 ? le64toh(o
->field
.head_data_offset
) : 0;
2438 r
= journal_file_move_to_object(j
->unique_file
, OBJECT_DATA
, j
->unique_offset
, &o
);
2442 j
->unique_offset
= le64toh(o
->data
.next_field_offset
);
2445 /* We reached the end of the list? Then start again, with the next file */
2446 if (j
->unique_offset
== 0) {
2447 j
->unique_file
= ordered_hashmap_next(j
->files
, j
->unique_file
->path
);
2448 if (!j
->unique_file
)
2454 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2455 * instead, so that we can look at this data object at the same
2456 * time as one on another file */
2457 r
= journal_file_move_to_object(j
->unique_file
, OBJECT_UNUSED
, j
->unique_offset
, &o
);
2461 /* Let's do the type check by hand, since we used 0 context above. */
2462 if (o
->object
.type
!= OBJECT_DATA
) {
2463 log_debug("%s:offset " OFSfmt
": object has type %d, expected %d",
2464 j
->unique_file
->path
, j
->unique_offset
,
2465 o
->object
.type
, OBJECT_DATA
);
2469 r
= return_data(j
, j
->unique_file
, o
, &odata
, &ol
);
2473 /* Check if we have at least the field name and "=". */
2475 log_debug("%s:offset " OFSfmt
": object has size %zu, expected at least %zu",
2476 j
->unique_file
->path
, j
->unique_offset
,
2481 if (memcmp(odata
, j
->unique_field
, k
) || ((const char*) odata
)[k
] != '=') {
2482 log_debug("%s:offset " OFSfmt
": object does not start with \"%s=\"",
2483 j
->unique_file
->path
, j
->unique_offset
,
2488 /* OK, now let's see if we already returned this data
2489 * object by checking if it exists in the earlier
2490 * traversed files. */
2492 ORDERED_HASHMAP_FOREACH(of
, j
->files
, i
) {
2496 if (of
== j
->unique_file
)
2499 /* Skip this file it didn't have any fields
2501 if (JOURNAL_HEADER_CONTAINS(of
->header
, n_fields
) &&
2502 le64toh(of
->header
->n_fields
) <= 0)
2505 r
= journal_file_find_data_object_with_hash(of
, odata
, ol
, le64toh(o
->data
.hash
), &oo
, &op
);
2516 r
= return_data(j
, j
->unique_file
, o
, data
, l
);
2524 _public_
void sd_journal_restart_unique(sd_journal
*j
) {
2528 j
->unique_file
= NULL
;
2529 j
->unique_offset
= 0;
2530 j
->unique_file_lost
= false;
2533 _public_
int sd_journal_reliable_fd(sd_journal
*j
) {
2534 assert_return(j
, -EINVAL
);
2535 assert_return(!journal_pid_changed(j
), -ECHILD
);
2537 return !j
->on_network
;
2540 static char *lookup_field(const char *field
, void *userdata
) {
2541 sd_journal
*j
= userdata
;
2549 r
= sd_journal_get_data(j
, field
, &data
, &size
);
2551 size
> REPLACE_VAR_MAX
)
2552 return strdup(field
);
2554 d
= strlen(field
) + 1;
2556 return strndup((const char*) data
+ d
, size
- d
);
2559 _public_
int sd_journal_get_catalog(sd_journal
*j
, char **ret
) {
2563 _cleanup_free_
char *text
= NULL
, *cid
= NULL
;
2567 assert_return(j
, -EINVAL
);
2568 assert_return(!journal_pid_changed(j
), -ECHILD
);
2569 assert_return(ret
, -EINVAL
);
2571 r
= sd_journal_get_data(j
, "MESSAGE_ID", &data
, &size
);
2575 cid
= strndup((const char*) data
+ 11, size
- 11);
2579 r
= sd_id128_from_string(cid
, &id
);
2583 r
= catalog_get(CATALOG_DATABASE
, id
, &text
);
2587 t
= replace_var(text
, lookup_field
, j
);
2595 _public_
int sd_journal_get_catalog_for_message_id(sd_id128_t id
, char **ret
) {
2596 assert_return(ret
, -EINVAL
);
2598 return catalog_get(CATALOG_DATABASE
, id
, ret
);
2601 _public_
int sd_journal_set_data_threshold(sd_journal
*j
, size_t sz
) {
2602 assert_return(j
, -EINVAL
);
2603 assert_return(!journal_pid_changed(j
), -ECHILD
);
2605 j
->data_threshold
= sz
;
2609 _public_
int sd_journal_get_data_threshold(sd_journal
*j
, size_t *sz
) {
2610 assert_return(j
, -EINVAL
);
2611 assert_return(!journal_pid_changed(j
), -ECHILD
);
2612 assert_return(sz
, -EINVAL
);
2614 *sz
= j
->data_threshold
;