]>
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 "sd-journal.h"
27 #include "journal-def.h"
28 #include "journal-file.h"
33 typedef struct Match Match
;
40 LIST_FIELDS(Match
, matches
);
46 JournalFile
*current_file
;
47 uint64_t current_field
;
49 LIST_HEAD(Match
, matches
);
53 int sd_journal_add_match(sd_journal
*j
, const void *data
, size_t size
) {
69 m
->data
= malloc(m
->size
);
75 memcpy(m
->data
, data
, size
);
76 m
->le_hash
= hash64(m
->data
, size
);
78 LIST_PREPEND(Match
, matches
, j
->matches
, m
);
84 void sd_journal_flush_matches(sd_journal
*j
) {
88 Match
*m
= j
->matches
;
90 LIST_REMOVE(Match
, matches
, j
->matches
, m
);
98 static int compare_order(JournalFile
*af
, Object
*ao
, uint64_t ap
,
99 JournalFile
*bf
, Object
*bo
, uint64_t bp
) {
103 /* We operate on two different files here, hence we can access
104 * two objects at the same time, which we normally can't.
106 * If contents and timestamps match, these entries are
107 * identical, even if the seqnum does not match */
109 if (sd_id128_equal(ao
->entry
.boot_id
, bo
->entry
.boot_id
) &&
110 ao
->entry
.monotonic
== bo
->entry
.monotonic
&&
111 ao
->entry
.realtime
== bo
->entry
.realtime
&&
112 ao
->entry
.xor_hash
== bo
->entry
.xor_hash
)
115 if (sd_id128_equal(af
->header
->seqnum_id
, bf
->header
->seqnum_id
)) {
117 /* If this is from the same seqnum source, compare
119 a
= le64toh(ao
->entry
.seqnum
);
120 b
= le64toh(bo
->entry
.seqnum
);
127 /* Wow! This is weird, different data but the same
128 * seqnums? Something is borked, but let's make the
129 * best of it and compare by time. */
132 if (sd_id128_equal(ao
->entry
.boot_id
, bo
->entry
.boot_id
)) {
134 /* If the boot id matches compare monotonic time */
135 a
= le64toh(ao
->entry
.monotonic
);
136 b
= le64toh(bo
->entry
.monotonic
);
144 /* Otherwise compare UTC time */
145 a
= le64toh(ao
->entry
.realtime
);
146 b
= le64toh(ao
->entry
.realtime
);
153 /* Finally, compare by contents */
154 a
= le64toh(ao
->entry
.xor_hash
);
155 b
= le64toh(ao
->entry
.xor_hash
);
165 static int move_to_next_with_matches(sd_journal
*j
, JournalFile
*f
, Object
**o
, uint64_t *p
) {
176 /* No matches is easy, just go on to the next entry */
178 if (f
->current_offset
> 0) {
179 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &c
);
185 return journal_file_next_entry(f
, c
, o
, p
);
188 /* So there are matches we have to adhere to, let's find the
189 * first entry that matches all of them */
191 if (f
->current_offset
> 0)
192 cp
= f
->current_offset
;
194 r
= journal_file_find_first_entry(f
, j
->matches
->data
, j
->matches
->size
, &c
, &cp
);
198 /* We can shortcut this if there's only one match */
199 if (j
->n_matches
== 1) {
211 r
= journal_file_move_to_object(f
, cp
, OBJECT_ENTRY
, &c
);
215 n
= journal_file_entry_n_items(c
);
217 /* Make sure we don't match the entry we are starting
219 found
= f
->current_offset
!= cp
;
222 LIST_FOREACH(matches
, m
, j
->matches
) {
225 for (k
= 0; k
< n
; k
++)
226 if (c
->entry
.items
[k
].hash
== m
->le_hash
)
230 /* Hmm, didn't find any field that matched, so ignore
231 * this match. Go on with next match */
237 /* Hmm, so, this field matched, let's remember
238 * where we'd have to try next, in case the other
239 * matches are not OK */
240 q
= le64toh(c
->entry
.items
[k
].next_entry_offset
);
245 /* Did this entry match against all matches? */
252 /* Did we find a subsequent entry? */
256 /* Hmm, ok, this entry only matched partially, so
257 * let's try another one */
262 int sd_journal_next(sd_journal
*j
) {
263 JournalFile
*f
, *new_current
= NULL
;
266 uint64_t new_offset
= 0;
267 Object
*new_entry
= NULL
;
271 HASHMAP_FOREACH(f
, j
->files
, i
) {
275 r
= move_to_next_with_matches(j
, f
, &o
, &p
);
282 compare_order(new_current
, new_entry
, new_offset
, f
, o
, p
) > 0) {
290 j
->current_file
= new_current
;
291 j
->current_file
->current_offset
= new_offset
;
292 j
->current_field
= 0;
294 /* Skip over any identical entries in the other files too */
296 HASHMAP_FOREACH(f
, j
->files
, i
) {
300 if (j
->current_file
== f
)
303 r
= move_to_next_with_matches(j
, f
, &o
, &p
);
309 if (compare_order(new_current
, new_entry
, new_offset
, f
, o
, p
) == 0)
310 f
->current_offset
= p
;
319 int sd_journal_previous(sd_journal
*j
) {
320 JournalFile
*f
, *new_current
= NULL
;
323 uint64_t new_offset
= 0;
324 Object
*new_entry
= NULL
;
328 HASHMAP_FOREACH(f
, j
->files
, i
) {
332 if (f
->current_offset
> 0) {
333 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
339 r
= journal_file_prev_entry(f
, o
, &o
, &p
);
345 if (!new_current
|| compare_order(new_current
, new_entry
, new_offset
, f
, o
, p
) > 0) {
353 j
->current_file
= new_current
;
354 j
->current_file
->current_offset
= new_offset
;
355 j
->current_field
= 0;
357 /* Skip over any identical entries in the other files too */
359 HASHMAP_FOREACH(f
, j
->files
, i
) {
363 if (j
->current_file
== f
)
366 if (f
->current_offset
> 0) {
367 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
373 r
= journal_file_prev_entry(f
, o
, &o
, &p
);
379 if (compare_order(new_current
, new_entry
, new_offset
, f
, o
, p
) == 0)
380 f
->current_offset
= p
;
389 int sd_journal_get_cursor(sd_journal
*j
, char **cursor
) {
392 char bid
[33], sid
[33];
397 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
398 return -EADDRNOTAVAIL
;
400 r
= journal_file_move_to_object(j
->current_file
, j
->current_file
->current_offset
, OBJECT_ENTRY
, &o
);
404 sd_id128_to_string(j
->current_file
->header
->seqnum_id
, sid
);
405 sd_id128_to_string(o
->entry
.boot_id
, bid
);
408 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
409 sid
, (unsigned long long) le64toh(o
->entry
.seqnum
),
410 bid
, (unsigned long long) le64toh(o
->entry
.monotonic
),
411 (unsigned long long) le64toh(o
->entry
.realtime
),
412 (unsigned long long) le64toh(o
->entry
.xor_hash
),
413 file_name_from_path(j
->current_file
->path
)) < 0)
419 int sd_journal_set_cursor(sd_journal
*j
, const char *cursor
) {
423 static int add_file(sd_journal
*j
, const char *prefix
, const char *dir
, const char *filename
) {
433 fn
= join(prefix
, "/", dir
, "/", filename
, NULL
);
435 fn
= join(prefix
, "/", filename
, NULL
);
440 r
= journal_file_open(fn
, O_RDONLY
, 0, NULL
, &f
);
450 r
= hashmap_put(j
->files
, f
->path
, f
);
452 journal_file_close(f
);
459 static int add_directory(sd_journal
*j
, const char *prefix
, const char *dir
) {
468 fn
= join(prefix
, "/", dir
, NULL
);
483 struct dirent buf
, *de
;
485 r
= readdir_r(d
, &buf
, &de
);
489 if (!dirent_is_file_with_suffix(de
, ".journal"))
492 r
= add_file(j
, prefix
, dir
, de
->d_name
);
494 log_debug("Failed to add file %s/%s/%s: %s", prefix
, dir
, de
->d_name
, strerror(-r
));
502 int sd_journal_open(sd_journal
**ret
) {
505 const char search_paths
[] =
507 "/var/log/journal\0";
512 j
= new0(sd_journal
, 1);
516 j
->files
= hashmap_new(string_hash_func
, string_compare_func
);
522 /* We ignore most errors here, since the idea is to only open
523 * what's actually accessible, and ignore the rest. */
525 NULSTR_FOREACH(p
, search_paths
) {
531 log_debug("Failed to open %s: %m", p
);
536 struct dirent buf
, *de
;
539 r
= readdir_r(d
, &buf
, &de
);
543 if (dirent_is_file_with_suffix(de
, ".journal")) {
544 r
= add_file(j
, p
, NULL
, de
->d_name
);
546 log_debug("Failed to add file %s/%s: %s", p
, de
->d_name
, strerror(-r
));
548 } else if ((de
->d_type
== DT_DIR
|| de
->d_type
== DT_UNKNOWN
) &&
549 sd_id128_from_string(de
->d_name
, &id
) >= 0) {
551 r
= add_directory(j
, p
, de
->d_name
);
553 log_debug("Failed to add directory %s/%s: %s", p
, de
->d_name
, strerror(-r
));
569 void sd_journal_close(sd_journal
*j
) {
575 while ((f
= hashmap_steal_first(j
->files
)))
576 journal_file_close(f
);
578 hashmap_free(j
->files
);
581 sd_journal_flush_matches(j
);
586 int sd_journal_get_realtime_usec(sd_journal
*j
, uint64_t *ret
) {
598 if (f
->current_offset
<= 0)
601 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
605 *ret
= le64toh(o
->entry
.realtime
);
609 int sd_journal_get_monotonic_usec(sd_journal
*j
, uint64_t *ret
) {
622 if (f
->current_offset
<= 0)
625 r
= sd_id128_get_boot(&id
);
629 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
633 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
636 *ret
= le64toh(o
->entry
.monotonic
);
641 int sd_journal_get_field(sd_journal
*j
, const char *field
, const void **data
, size_t *size
) {
653 if (isempty(field
) || strchr(field
, '='))
660 if (f
->current_offset
<= 0)
663 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
667 field_length
= strlen(field
);
669 n
= journal_file_entry_n_items(o
);
670 for (i
= 0; i
< n
; i
++) {
674 p
= le64toh(o
->entry
.items
[i
].object_offset
);
675 h
= o
->entry
.items
[j
->current_field
].hash
;
676 r
= journal_file_move_to_object(f
, p
, OBJECT_DATA
, &o
);
680 if (h
!= o
->data
.hash
)
683 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
685 if (l
>= field_length
+1 &&
686 memcmp(o
->data
.payload
, field
, field_length
) == 0 &&
687 o
->data
.payload
[field_length
] == '=') {
691 if ((uint64_t) t
!= l
)
694 *data
= o
->data
.payload
;
700 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
708 int sd_journal_iterate_fields(sd_journal
*j
, const void **data
, size_t *size
) {
723 if (f
->current_offset
<= 0)
726 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
730 n
= journal_file_entry_n_items(o
);
731 if (j
->current_field
>= n
)
734 p
= le64toh(o
->entry
.items
[j
->current_field
].object_offset
);
735 h
= o
->entry
.items
[j
->current_field
].hash
;
736 r
= journal_file_move_to_object(f
, p
, OBJECT_DATA
, &o
);
740 if (h
!= o
->data
.hash
)
743 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
746 /* We can't read objects larger than 4G on a 32bit machine */
747 if ((uint64_t) t
!= l
)
750 *data
= o
->data
.payload
;
758 int sd_journal_seek_head(sd_journal
*j
) {
763 int sd_journal_seek_tail(sd_journal
*j
) {