]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/sd-journal.c
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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <sys/inotify.h>
28 #include "sd-journal.h"
29 #include "journal-def.h"
30 #include "journal-file.h"
35 #include "journal-internal.h"
37 #define JOURNAL_FILES_MAX 1024
39 static void detach_location(sd_journal
*j
) {
45 j
->current_file
= NULL
;
48 HASHMAP_FOREACH(f
, j
->files
, i
)
49 f
->current_offset
= 0;
52 static void reset_location(sd_journal
*j
) {
56 zero(j
->current_location
);
59 static void init_location(Location
*l
, JournalFile
*f
, Object
*o
) {
62 assert(o
->object
.type
== OBJECT_ENTRY
);
64 l
->type
= LOCATION_DISCRETE
;
65 l
->seqnum
= le64toh(o
->entry
.seqnum
);
66 l
->seqnum_id
= f
->header
->seqnum_id
;
67 l
->realtime
= le64toh(o
->entry
.realtime
);
68 l
->monotonic
= le64toh(o
->entry
.monotonic
);
69 l
->boot_id
= le64toh(o
->entry
.boot_id
);
70 l
->xor_hash
= le64toh(o
->entry
.xor_hash
);
72 l
->seqnum_set
= l
->realtime_set
= l
->monotonic_set
= l
->xor_hash_set
= true;
75 static void set_location(sd_journal
*j
, JournalFile
*f
, Object
*o
, uint64_t offset
) {
80 init_location(&j
->current_location
, f
, o
);
85 f
->current_offset
= offset
;
88 static int same_field(const void *_a
, size_t s
, const void *_b
, size_t t
) {
89 const uint8_t *a
= _a
, *b
= _b
;
91 bool a_good
= false, b_good
= false, different
= false;
93 for (j
= 0; j
< s
&& j
< t
; j
++) {
102 if (a_good
&& b_good
)
103 return different
? 0 : 1;
109 _public_
int sd_journal_add_match(sd_journal
*j
, const void *data
, size_t size
) {
110 Match
*m
, *after
= NULL
;
120 le_hash
= htole64(hash64(data
, size
));
122 LIST_FOREACH(matches
, m
, j
->matches
) {
125 if (m
->le_hash
== le_hash
&&
127 memcmp(m
->data
, data
, size
) == 0)
130 r
= same_field(data
, size
, m
->data
, m
->size
);
143 m
->data
= malloc(m
->size
);
149 memcpy(m
->data
, data
, size
);
150 m
->le_hash
= le_hash
;
152 /* Matches for the same fields we order adjacent to each
154 LIST_INSERT_AFTER(Match
, matches
, j
->matches
, after
, m
);
162 _public_
void sd_journal_flush_matches(sd_journal
*j
) {
167 Match
*m
= j
->matches
;
169 LIST_REMOVE(Match
, matches
, j
->matches
, m
);
179 static int compare_order(JournalFile
*af
, Object
*ao
,
180 JournalFile
*bf
, Object
*bo
) {
189 /* We operate on two different files here, hence we can access
190 * two objects at the same time, which we normally can't.
192 * If contents and timestamps match, these entries are
193 * identical, even if the seqnum does not match */
195 if (sd_id128_equal(ao
->entry
.boot_id
, bo
->entry
.boot_id
) &&
196 ao
->entry
.monotonic
== bo
->entry
.monotonic
&&
197 ao
->entry
.realtime
== bo
->entry
.realtime
&&
198 ao
->entry
.xor_hash
== bo
->entry
.xor_hash
)
201 if (sd_id128_equal(af
->header
->seqnum_id
, bf
->header
->seqnum_id
)) {
203 /* If this is from the same seqnum source, compare
205 a
= le64toh(ao
->entry
.seqnum
);
206 b
= le64toh(bo
->entry
.seqnum
);
213 /* Wow! This is weird, different data but the same
214 * seqnums? Something is borked, but let's make the
215 * best of it and compare by time. */
218 if (sd_id128_equal(ao
->entry
.boot_id
, bo
->entry
.boot_id
)) {
220 /* If the boot id matches compare monotonic time */
221 a
= le64toh(ao
->entry
.monotonic
);
222 b
= le64toh(bo
->entry
.monotonic
);
230 /* Otherwise compare UTC time */
231 a
= le64toh(ao
->entry
.realtime
);
232 b
= le64toh(ao
->entry
.realtime
);
239 /* Finally, compare by contents */
240 a
= le64toh(ao
->entry
.xor_hash
);
241 b
= le64toh(ao
->entry
.xor_hash
);
251 static int compare_with_location(JournalFile
*af
, Object
*ao
, Location
*l
) {
257 assert(l
->type
== LOCATION_DISCRETE
);
259 if (l
->monotonic_set
&&
260 sd_id128_equal(ao
->entry
.boot_id
, l
->boot_id
) &&
262 le64toh(ao
->entry
.realtime
) == l
->realtime
&&
264 le64toh(ao
->entry
.xor_hash
) == l
->xor_hash
)
268 sd_id128_equal(af
->header
->seqnum_id
, l
->seqnum_id
)) {
270 a
= le64toh(ao
->entry
.seqnum
);
278 if (l
->monotonic_set
&&
279 sd_id128_equal(ao
->entry
.boot_id
, l
->boot_id
)) {
281 a
= le64toh(ao
->entry
.monotonic
);
283 if (a
< l
->monotonic
)
285 if (a
> l
->monotonic
)
289 if (l
->realtime_set
) {
291 a
= le64toh(ao
->entry
.realtime
);
299 if (l
->xor_hash_set
) {
300 a
= le64toh(ao
->entry
.xor_hash
);
311 static int find_location(sd_journal
*j
, JournalFile
*f
, direction_t direction
, Object
**ret
, uint64_t *offset
) {
319 /* No matches is simple */
321 if (j
->current_location
.type
== LOCATION_HEAD
)
322 r
= journal_file_next_entry(f
, NULL
, 0, DIRECTION_DOWN
, &o
, &p
);
323 else if (j
->current_location
.type
== LOCATION_TAIL
)
324 r
= journal_file_next_entry(f
, NULL
, 0, DIRECTION_UP
, &o
, &p
);
325 else if (j
->current_location
.seqnum_set
&&
326 sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
327 r
= journal_file_move_to_entry_by_seqnum(f
, j
->current_location
.seqnum
, direction
, &o
, &p
);
328 else if (j
->current_location
.monotonic_set
)
329 r
= journal_file_move_to_entry_by_monotonic(f
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, &o
, &p
);
330 else if (j
->current_location
.realtime_set
)
331 r
= journal_file_move_to_entry_by_realtime(f
, j
->current_location
.realtime
, direction
, &o
, &p
);
333 r
= journal_file_next_entry(f
, NULL
, 0, direction
, &o
, &p
);
339 Match
*m
, *term_match
= NULL
;
343 /* We have matches, first, let's jump to the monotonic
344 * position if we have any, since it implies a
347 if (j
->current_location
.type
== LOCATION_DISCRETE
&&
348 j
->current_location
.monotonic_set
) {
350 r
= journal_file_move_to_entry_by_monotonic(f
, j
->current_location
.boot_id
, j
->current_location
.monotonic
, direction
, &o
, &p
);
352 return r
== -ENOENT
? 0 : r
;
355 LIST_FOREACH(matches
, m
, j
->matches
) {
359 r
= journal_file_find_data_object_with_hash(f
, m
->data
, m
->size
, m
->le_hash
, &d
, &dp
);
363 if (j
->current_location
.type
== LOCATION_HEAD
)
364 r
= journal_file_next_entry_for_data(f
, NULL
, 0, dp
, DIRECTION_DOWN
, &c
, &cp
);
365 else if (j
->current_location
.type
== LOCATION_TAIL
)
366 r
= journal_file_next_entry_for_data(f
, NULL
, 0, dp
, DIRECTION_UP
, &c
, &cp
);
367 else if (j
->current_location
.seqnum_set
&&
368 sd_id128_equal(j
->current_location
.seqnum_id
, f
->header
->seqnum_id
))
369 r
= journal_file_move_to_entry_by_seqnum_for_data(f
, dp
, j
->current_location
.seqnum
, direction
, &c
, &cp
);
370 else if (j
->current_location
.realtime_set
)
371 r
= journal_file_move_to_entry_by_realtime_for_data(f
, dp
, j
->current_location
.realtime
, direction
, &c
, &cp
);
373 r
= journal_file_next_entry_for_data(f
, NULL
, 0, dp
, direction
, &c
, &cp
);
385 } else if (same_field(term_match
->data
, term_match
->size
, m
->data
, m
->size
)) {
387 /* Same field as previous match... */
390 /* Find the earliest of the OR matches */
393 (direction
== DIRECTION_DOWN
&& cp
< tp
) ||
394 (direction
== DIRECTION_UP
&& cp
> tp
)) {
403 /* Previous term is finished, did anything match? */
407 /* Find the last of the AND matches */
409 (direction
== DIRECTION_DOWN
&& tp
> p
) ||
410 (direction
== DIRECTION_UP
&& tp
< p
)) {
427 /* Last term is finished, did anything match? */
432 (direction
== DIRECTION_DOWN
&& tp
> p
) ||
433 (direction
== DIRECTION_UP
&& tp
< p
)) {
451 static int next_with_matches(sd_journal
*j
, JournalFile
*f
, direction_t direction
, Object
**ret
, uint64_t *offset
) {
465 /* No matches is easy */
467 r
= journal_file_next_entry(f
, c
, cp
, direction
, &c
, &cp
);
478 /* So there are matches we have to adhere to, let's find the
479 * first entry that matches all of them */
483 bool found
, term_result
= false;
484 Match
*m
, *term_match
= NULL
;
487 n
= journal_file_entry_n_items(c
);
489 /* Make sure we don't match the entry we are starting
491 found
= cp
!= *offset
;
494 LIST_FOREACH(matches
, m
, j
->matches
) {
498 /* Let's check if this is the beginning of a
499 * new term, i.e. has a different field prefix
500 * as the preceeding match. */
504 } else if (!same_field(term_match
->data
, term_match
->size
, m
->data
, m
->size
)) {
512 for (k
= 0; k
< n
; k
++)
513 if (c
->entry
.items
[k
].hash
== m
->le_hash
)
517 /* Hmm, didn't find any field that
518 * matched this rule, so ignore this
519 * match. Go on with next match */
525 /* Hmm, so, this field matched, let's remember
526 * where we'd have to try next, in case the other
527 * matches are not OK */
529 r
= journal_file_next_entry_for_data(f
, c
, cp
, le64toh(c
->entry
.items
[k
].object_offset
), direction
, &qo
, &q
);
535 if (direction
== DIRECTION_DOWN
) {
541 if (np
== 0 || q
< np
) {
549 /* Check the last term */
550 if (term_match
&& !term_result
)
553 /* Did this entry match against all matches? */
562 /* Did we find a subsequent entry? */
566 /* Hmm, ok, this entry only matched partially, so
567 * let's try another one */
573 static int next_beyond_location(sd_journal
*j
, JournalFile
*f
, direction_t direction
, Object
**ret
, uint64_t *offset
) {
576 int compare_value
, r
;
581 if (f
->current_offset
> 0) {
582 cp
= f
->current_offset
;
584 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, cp
, &c
);
588 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
594 r
= find_location(j
, f
, direction
, &c
, &cp
);
604 if (j
->current_location
.type
== LOCATION_DISCRETE
) {
607 k
= compare_with_location(f
, c
, &j
->current_location
);
608 if (direction
== DIRECTION_DOWN
)
609 found
= k
>= compare_value
;
611 found
= k
<= -compare_value
;
623 r
= next_with_matches(j
, f
, direction
, &c
, &cp
);
629 static int real_journal_next(sd_journal
*j
, direction_t direction
) {
630 JournalFile
*f
, *new_current
= NULL
;
633 uint64_t new_offset
= 0;
634 Object
*new_entry
= NULL
;
639 HASHMAP_FOREACH(f
, j
->files
, i
) {
644 r
= next_beyond_location(j
, f
, direction
, &o
, &p
);
655 k
= compare_order(f
, o
, new_current
, new_entry
);
657 if (direction
== DIRECTION_DOWN
)
673 set_location(j
, new_current
, new_entry
, new_offset
);
678 _public_
int sd_journal_next(sd_journal
*j
) {
679 return real_journal_next(j
, DIRECTION_DOWN
);
682 _public_
int sd_journal_previous(sd_journal
*j
) {
683 return real_journal_next(j
, DIRECTION_UP
);
686 static int real_journal_next_skip(sd_journal
*j
, direction_t direction
, uint64_t skip
) {
693 /* If this is not a discrete skip, then at least
694 * resolve the current location */
695 if (j
->current_location
.type
!= LOCATION_DISCRETE
)
696 return real_journal_next(j
, direction
);
702 r
= real_journal_next(j
, direction
);
716 _public_
int sd_journal_next_skip(sd_journal
*j
, uint64_t skip
) {
717 return real_journal_next_skip(j
, DIRECTION_DOWN
, skip
);
720 _public_
int sd_journal_previous_skip(sd_journal
*j
, uint64_t skip
) {
721 return real_journal_next_skip(j
, DIRECTION_UP
, skip
);
724 _public_
int sd_journal_get_cursor(sd_journal
*j
, char **cursor
) {
727 char bid
[33], sid
[33];
734 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
735 return -EADDRNOTAVAIL
;
737 r
= journal_file_move_to_object(j
->current_file
, OBJECT_ENTRY
, j
->current_file
->current_offset
, &o
);
741 sd_id128_to_string(j
->current_file
->header
->seqnum_id
, sid
);
742 sd_id128_to_string(o
->entry
.boot_id
, bid
);
745 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
746 sid
, (unsigned long long) le64toh(o
->entry
.seqnum
),
747 bid
, (unsigned long long) le64toh(o
->entry
.monotonic
),
748 (unsigned long long) le64toh(o
->entry
.realtime
),
749 (unsigned long long) le64toh(o
->entry
.xor_hash
),
750 file_name_from_path(j
->current_file
->path
)) < 0)
756 _public_
int sd_journal_seek_cursor(sd_journal
*j
, const char *cursor
) {
760 unsigned long long seqnum
, monotonic
, realtime
, xor_hash
;
762 seqnum_id_set
= false,
765 monotonic_set
= false,
766 realtime_set
= false,
767 xor_hash_set
= false;
768 sd_id128_t seqnum_id
, boot_id
;
775 FOREACH_WORD_SEPARATOR(w
, l
, cursor
, ";", state
) {
779 if (l
< 2 || w
[1] != '=')
782 item
= strndup(w
, l
);
789 seqnum_id_set
= true;
790 k
= sd_id128_from_string(w
+2, &seqnum_id
);
795 if (sscanf(w
+2, "%llx", &seqnum
) != 1)
801 k
= sd_id128_from_string(w
+2, &boot_id
);
805 monotonic_set
= true;
806 if (sscanf(w
+2, "%llx", &monotonic
) != 1)
812 if (sscanf(w
+2, "%llx", &realtime
) != 1)
818 if (sscanf(w
+2, "%llx", &xor_hash
) != 1)
829 if ((!seqnum_set
|| !seqnum_id_set
) &&
830 (!monotonic_set
|| !boot_id_set
) &&
836 j
->current_location
.type
= LOCATION_DISCRETE
;
839 j
->current_location
.realtime
= (uint64_t) realtime
;
840 j
->current_location
.realtime_set
= true;
843 if (seqnum_set
&& seqnum_id_set
) {
844 j
->current_location
.seqnum
= (uint64_t) seqnum
;
845 j
->current_location
.seqnum_id
= seqnum_id
;
846 j
->current_location
.seqnum_set
= true;
849 if (monotonic_set
&& boot_id_set
) {
850 j
->current_location
.monotonic
= (uint64_t) monotonic
;
851 j
->current_location
.boot_id
= boot_id
;
852 j
->current_location
.monotonic_set
= true;
856 j
->current_location
.xor_hash
= (uint64_t) xor_hash
;
857 j
->current_location
.xor_hash_set
= true;
863 _public_
int sd_journal_seek_monotonic_usec(sd_journal
*j
, sd_id128_t boot_id
, uint64_t usec
) {
868 j
->current_location
.type
= LOCATION_DISCRETE
;
869 j
->current_location
.boot_id
= boot_id
;
870 j
->current_location
.monotonic
= usec
;
871 j
->current_location
.monotonic_set
= true;
876 _public_
int sd_journal_seek_realtime_usec(sd_journal
*j
, uint64_t usec
) {
881 j
->current_location
.type
= LOCATION_DISCRETE
;
882 j
->current_location
.realtime
= usec
;
883 j
->current_location
.realtime_set
= true;
888 _public_
int sd_journal_seek_head(sd_journal
*j
) {
893 j
->current_location
.type
= LOCATION_HEAD
;
898 _public_
int sd_journal_seek_tail(sd_journal
*j
) {
903 j
->current_location
.type
= LOCATION_TAIL
;
908 static int add_file(sd_journal
*j
, const char *prefix
, const char *dir
, const char *filename
) {
917 if ((j
->flags
& SD_JOURNAL_SYSTEM_ONLY
) &&
918 !startswith(filename
, "system.journal"))
922 fn
= join(prefix
, "/", dir
, "/", filename
, NULL
);
924 fn
= join(prefix
, "/", filename
, NULL
);
929 if (hashmap_get(j
->files
, fn
)) {
934 if (hashmap_size(j
->files
) >= JOURNAL_FILES_MAX
) {
935 log_debug("Too many open journal files, not adding %s, ignoring.", fn
);
940 r
= journal_file_open(fn
, O_RDONLY
, 0, NULL
, &f
);
950 /* journal_file_dump(f); */
952 r
= hashmap_put(j
->files
, f
->path
, f
);
954 journal_file_close(f
);
958 log_debug("File %s got added.", f
->path
);
963 static int remove_file(sd_journal
*j
, const char *prefix
, const char *dir
, const char *filename
) {
972 fn
= join(prefix
, "/", dir
, "/", filename
, NULL
);
974 fn
= join(prefix
, "/", filename
, NULL
);
979 f
= hashmap_get(j
->files
, fn
);
985 hashmap_remove(j
->files
, f
->path
);
986 journal_file_close(f
);
988 log_debug("File %s got removed.", f
->path
);
992 static int add_directory(sd_journal
*j
, const char *prefix
, const char *dir
) {
1003 if ((j
->flags
& SD_JOURNAL_LOCAL_ONLY
) &&
1004 (sd_id128_from_string(dir
, &id
) < 0 ||
1005 sd_id128_get_machine(&mid
) < 0 ||
1006 !sd_id128_equal(id
, mid
)))
1009 fn
= join(prefix
, "/", dir
, NULL
);
1017 if (errno
== ENOENT
)
1023 wd
= inotify_add_watch(j
->inotify_fd
, fn
,
1024 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1025 IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
|
1026 IN_DONT_FOLLOW
|IN_ONLYDIR
);
1028 if (hashmap_put(j
->inotify_wd_dirs
, INT_TO_PTR(wd
), fn
) < 0)
1029 inotify_rm_watch(j
->inotify_fd
, wd
);
1037 struct dirent buf
, *de
;
1039 r
= readdir_r(d
, &buf
, &de
);
1043 if (!dirent_is_file_with_suffix(de
, ".journal"))
1046 r
= add_file(j
, prefix
, dir
, de
->d_name
);
1048 log_debug("Failed to add file %s/%s/%s: %s", prefix
, dir
, de
->d_name
, strerror(-r
));
1053 log_debug("Directory %s/%s got added.", prefix
, dir
);
1058 static void remove_directory_wd(sd_journal
*j
, int wd
) {
1064 if (j
->inotify_fd
>= 0)
1065 inotify_rm_watch(j
->inotify_fd
, wd
);
1067 p
= hashmap_remove(j
->inotify_wd_dirs
, INT_TO_PTR(wd
));
1070 log_debug("Directory %s got removed.", p
);
1075 static void add_root_wd(sd_journal
*j
, const char *p
) {
1082 wd
= inotify_add_watch(j
->inotify_fd
, p
,
1083 IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
|IN_DELETE
|
1084 IN_DONT_FOLLOW
|IN_ONLYDIR
);
1089 if (!k
|| hashmap_put(j
->inotify_wd_roots
, INT_TO_PTR(wd
), k
) < 0) {
1090 inotify_rm_watch(j
->inotify_fd
, wd
);
1095 static void remove_root_wd(sd_journal
*j
, int wd
) {
1101 if (j
->inotify_fd
>= 0)
1102 inotify_rm_watch(j
->inotify_fd
, wd
);
1104 p
= hashmap_remove(j
->inotify_wd_roots
, INT_TO_PTR(wd
));
1107 log_debug("Root %s got removed.", p
);
1112 _public_
int sd_journal_open(sd_journal
**ret
, int flags
) {
1115 const char search_paths
[] =
1116 "/run/log/journal\0"
1117 "/var/log/journal\0";
1123 if (flags
& ~(SD_JOURNAL_LOCAL_ONLY
|
1124 SD_JOURNAL_RUNTIME_ONLY
|
1125 SD_JOURNAL_SYSTEM_ONLY
))
1128 j
= new0(sd_journal
, 1);
1134 j
->inotify_fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
1135 if (j
->inotify_fd
< 0) {
1140 j
->files
= hashmap_new(string_hash_func
, string_compare_func
);
1146 j
->inotify_wd_dirs
= hashmap_new(trivial_hash_func
, trivial_compare_func
);
1147 j
->inotify_wd_roots
= hashmap_new(trivial_hash_func
, trivial_compare_func
);
1149 if (!j
->inotify_wd_dirs
|| !j
->inotify_wd_roots
) {
1154 /* We ignore most errors here, since the idea is to only open
1155 * what's actually accessible, and ignore the rest. */
1157 NULSTR_FOREACH(p
, search_paths
) {
1160 if ((flags
& SD_JOURNAL_RUNTIME_ONLY
) &&
1161 !path_startswith(p
, "/run"))
1166 if (errno
!= ENOENT
)
1167 log_debug("Failed to open %s: %m", p
);
1174 struct dirent buf
, *de
;
1177 r
= readdir_r(d
, &buf
, &de
);
1181 if (dirent_is_file_with_suffix(de
, ".journal")) {
1182 r
= add_file(j
, p
, NULL
, de
->d_name
);
1184 log_debug("Failed to add file %s/%s: %s", p
, de
->d_name
, strerror(-r
));
1186 } else if ((de
->d_type
== DT_DIR
|| de
->d_type
== DT_UNKNOWN
) &&
1187 sd_id128_from_string(de
->d_name
, &id
) >= 0) {
1189 r
= add_directory(j
, p
, de
->d_name
);
1191 log_debug("Failed to add directory %s/%s: %s", p
, de
->d_name
, strerror(-r
));
1202 sd_journal_close(j
);
1207 _public_
void sd_journal_close(sd_journal
*j
) {
1211 if (j
->inotify_wd_dirs
) {
1214 while ((k
= hashmap_first_key(j
->inotify_wd_dirs
)))
1215 remove_directory_wd(j
, PTR_TO_INT(k
));
1217 hashmap_free(j
->inotify_wd_dirs
);
1220 if (j
->inotify_wd_roots
) {
1223 while ((k
= hashmap_first_key(j
->inotify_wd_roots
)))
1224 remove_root_wd(j
, PTR_TO_INT(k
));
1226 hashmap_free(j
->inotify_wd_roots
);
1232 while ((f
= hashmap_steal_first(j
->files
)))
1233 journal_file_close(f
);
1235 hashmap_free(j
->files
);
1238 sd_journal_flush_matches(j
);
1240 if (j
->inotify_fd
>= 0)
1241 close_nointr_nofail(j
->inotify_fd
);
1246 _public_
int sd_journal_get_realtime_usec(sd_journal
*j
, uint64_t *ret
) {
1256 f
= j
->current_file
;
1258 return -EADDRNOTAVAIL
;
1260 if (f
->current_offset
<= 0)
1261 return -EADDRNOTAVAIL
;
1263 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1267 *ret
= le64toh(o
->entry
.realtime
);
1271 _public_
int sd_journal_get_monotonic_usec(sd_journal
*j
, uint64_t *ret
, sd_id128_t
*ret_boot_id
) {
1282 f
= j
->current_file
;
1284 return -EADDRNOTAVAIL
;
1286 if (f
->current_offset
<= 0)
1287 return -EADDRNOTAVAIL
;
1289 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1294 *ret_boot_id
= o
->entry
.boot_id
;
1296 r
= sd_id128_get_boot(&id
);
1300 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
1304 *ret
= le64toh(o
->entry
.monotonic
);
1308 _public_
int sd_journal_get_data(sd_journal
*j
, const char *field
, const void **data
, size_t *size
) {
1311 size_t field_length
;
1324 if (isempty(field
) || strchr(field
, '='))
1327 f
= j
->current_file
;
1329 return -EADDRNOTAVAIL
;
1331 if (f
->current_offset
<= 0)
1332 return -EADDRNOTAVAIL
;
1334 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1338 field_length
= strlen(field
);
1340 n
= journal_file_entry_n_items(o
);
1341 for (i
= 0; i
< n
; i
++) {
1342 uint64_t p
, l
, le_hash
;
1345 p
= le64toh(o
->entry
.items
[i
].object_offset
);
1346 le_hash
= o
->entry
.items
[i
].hash
;
1347 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
1351 if (le_hash
!= o
->data
.hash
)
1354 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
1356 if (o
->object
.flags
& OBJECT_COMPRESSED
) {
1359 if (uncompress_startswith(o
->data
.payload
, l
,
1360 &f
->compress_buffer
, &f
->compress_buffer_size
,
1361 field
, field_length
, '=')) {
1365 if (!uncompress_blob(o
->data
.payload
, l
,
1366 &f
->compress_buffer
, &f
->compress_buffer_size
, &rsize
))
1369 *data
= f
->compress_buffer
;
1370 *size
= (size_t) rsize
;
1375 return -EPROTONOSUPPORT
;
1378 } else if (l
>= field_length
+1 &&
1379 memcmp(o
->data
.payload
, field
, field_length
) == 0 &&
1380 o
->data
.payload
[field_length
] == '=') {
1384 if ((uint64_t) t
!= l
)
1387 *data
= o
->data
.payload
;
1393 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1401 _public_
int sd_journal_enumerate_data(sd_journal
*j
, const void **data
, size_t *size
) {
1403 uint64_t p
, l
, n
, le_hash
;
1415 f
= j
->current_file
;
1417 return -EADDRNOTAVAIL
;
1419 if (f
->current_offset
<= 0)
1420 return -EADDRNOTAVAIL
;
1422 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, f
->current_offset
, &o
);
1426 n
= journal_file_entry_n_items(o
);
1427 if (j
->current_field
>= n
)
1430 p
= le64toh(o
->entry
.items
[j
->current_field
].object_offset
);
1431 le_hash
= o
->entry
.items
[j
->current_field
].hash
;
1432 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
1436 if (le_hash
!= o
->data
.hash
)
1439 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
1442 /* We can't read objects larger than 4G on a 32bit machine */
1443 if ((uint64_t) t
!= l
)
1446 if (o
->object
.flags
& OBJECT_COMPRESSED
) {
1450 if (!uncompress_blob(o
->data
.payload
, l
, &f
->compress_buffer
, &f
->compress_buffer_size
, &rsize
))
1453 *data
= f
->compress_buffer
;
1454 *size
= (size_t) rsize
;
1456 return -EPROTONOSUPPORT
;
1459 *data
= o
->data
.payload
;
1463 j
->current_field
++;
1468 _public_
void sd_journal_restart_data(sd_journal
*j
) {
1472 j
->current_field
= 0;
1475 _public_
int sd_journal_get_fd(sd_journal
*j
) {
1479 return j
->inotify_fd
;
1482 static void process_inotify_event(sd_journal
*j
, struct inotify_event
*e
) {
1489 /* Is this a subdirectory we watch? */
1490 p
= hashmap_get(j
->inotify_wd_dirs
, INT_TO_PTR(e
->wd
));
1493 if (!(e
->mask
& IN_ISDIR
) && e
->len
> 0 && endswith(e
->name
, ".journal")) {
1495 /* Event for a journal file */
1497 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
)) {
1498 r
= add_file(j
, p
, NULL
, e
->name
);
1500 log_debug("Failed to add file %s/%s: %s", p
, e
->name
, strerror(-r
));
1501 } else if (e
->mask
& (IN_DELETE
|IN_UNMOUNT
)) {
1503 r
= remove_file(j
, p
, NULL
, e
->name
);
1505 log_debug("Failed to remove file %s/%s: %s", p
, e
->name
, strerror(-r
));
1508 } else if (e
->len
== 0) {
1510 /* Event for the directory itself */
1512 if (e
->mask
& (IN_DELETE_SELF
|IN_MOVE_SELF
|IN_UNMOUNT
))
1513 remove_directory_wd(j
, e
->wd
);
1519 /* Must be the root directory then? */
1520 p
= hashmap_get(j
->inotify_wd_roots
, INT_TO_PTR(e
->wd
));
1524 if (!(e
->mask
& IN_ISDIR
) && e
->len
> 0 && endswith(e
->name
, ".journal")) {
1526 /* Event for a journal file */
1528 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
)) {
1529 r
= add_file(j
, p
, NULL
, e
->name
);
1531 log_debug("Failed to add file %s/%s: %s", p
, e
->name
, strerror(-r
));
1532 } else if (e
->mask
& (IN_DELETE
|IN_UNMOUNT
)) {
1534 r
= remove_file(j
, p
, NULL
, e
->name
);
1536 log_debug("Failed to remove file %s/%s: %s", p
, e
->name
, strerror(-r
));
1539 } else if ((e
->mask
& IN_ISDIR
) && e
->len
> 0 && sd_id128_from_string(e
->name
, &id
) >= 0) {
1541 /* Event for subdirectory */
1543 if (e
->mask
& (IN_CREATE
|IN_MOVED_TO
|IN_MODIFY
|IN_ATTRIB
)) {
1545 r
= add_directory(j
, p
, e
->name
);
1547 log_debug("Failed to add directory %s/%s: %s", p
, e
->name
, strerror(-r
));
1554 if (e
->mask
& IN_IGNORED
)
1557 log_warning("Unknown inotify event.");
1560 _public_
int sd_journal_process(sd_journal
*j
) {
1561 uint8_t buffer
[sizeof(struct inotify_event
) + FILENAME_MAX
];
1567 struct inotify_event
*e
;
1570 l
= read(j
->inotify_fd
, buffer
, sizeof(buffer
));
1572 if (errno
== EINTR
|| errno
== EAGAIN
)
1578 e
= (struct inotify_event
*) buffer
;
1582 process_inotify_event(j
, e
);
1584 step
= sizeof(struct inotify_event
) + e
->len
;
1585 assert(step
<= (size_t) l
);
1587 e
= (struct inotify_event
*) ((uint8_t*) e
+ step
);
1593 /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
1595 /* return -EINVAL; */
1597 /* return -EINVAL; */
1599 /* return -ENOTSUP; */
1602 /* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
1604 /* return -EINVAL; */
1606 /* return -EINVAL; */
1608 /* return -EINVAL; */
1610 /* return -ENOTSUP; */
1613 /* _public_ void sd_journal_restart_unique(sd_journal *j) { */