]>
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"
32 typedef struct Match Match
;
39 LIST_FIELDS(Match
, matches
);
45 JournalFile
*current_file
;
47 LIST_HEAD(Match
, matches
);
50 int sd_journal_add_match(sd_journal
*j
, const char *field
, const void *data
, size_t size
) {
56 assert(data
|| size
== 0);
62 m
->size
= strlen(field
) + 1 + size
;
63 m
->data
= malloc(m
->size
);
69 e
= stpcpy(m
->data
, field
);
71 memcpy(e
, data
, size
);
73 LIST_PREPEND(Match
, matches
, j
->matches
, m
);
77 void sd_journal_flush_matches(sd_journal
*j
) {
81 Match
*m
= j
->matches
;
83 LIST_REMOVE(Match
, matches
, j
->matches
, m
);
89 static int compare_order(JournalFile
*af
, Object
*ao
, uint64_t ap
,
90 JournalFile
*bf
, Object
*bo
, uint64_t bp
) {
94 if (sd_id128_equal(af
->header
->seqnum_id
, bf
->header
->seqnum_id
)) {
96 /* If this is from the same seqnum source, compare
98 a
= le64toh(ao
->entry
.seqnum
);
99 b
= le64toh(bo
->entry
.seqnum
);
102 } else if (sd_id128_equal(ao
->entry
.boot_id
, bo
->entry
.boot_id
)) {
104 /* If the boot id matches compare monotonic time */
105 a
= le64toh(ao
->entry
.monotonic
);
106 b
= le64toh(bo
->entry
.monotonic
);
110 /* Otherwise compare UTC time */
111 a
= le64toh(ao
->entry
.realtime
);
112 b
= le64toh(ao
->entry
.realtime
);
120 int sd_journal_next(sd_journal
*j
) {
121 JournalFile
*f
, *new_current
= NULL
;
124 uint64_t new_offset
= 0;
125 Object
*new_entry
= NULL
;
129 HASHMAP_FOREACH(f
, j
->files
, i
) {
133 if (f
->current_offset
> 0) {
134 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
140 r
= journal_file_next_entry(f
, o
, &o
, &p
);
146 if (!new_current
|| compare_order(new_current
, new_entry
, new_offset
, f
, o
, p
) > 0) {
154 j
->current_file
= new_current
;
155 j
->current_file
->current_offset
= new_offset
;
156 j
->current_file
->current_field
= 0;
163 int sd_journal_previous(sd_journal
*j
) {
164 JournalFile
*f
, *new_current
= NULL
;
167 uint64_t new_offset
= 0;
168 Object
*new_entry
= NULL
;
172 HASHMAP_FOREACH(f
, j
->files
, i
) {
176 if (f
->current_offset
> 0) {
177 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
183 r
= journal_file_prev_entry(f
, o
, &o
, &p
);
189 if (!new_current
|| compare_order(new_current
, new_entry
, new_offset
, f
, o
, p
) > 0) {
197 j
->current_file
= new_current
;
198 j
->current_file
->current_offset
= new_offset
;
199 j
->current_file
->current_field
= 0;
206 int sd_journal_get_cursor(sd_journal
*j
, char **cursor
) {
209 char bid
[33], sid
[33];
214 if (!j
->current_file
|| j
->current_file
->current_offset
<= 0)
215 return -EADDRNOTAVAIL
;
217 r
= journal_file_move_to_object(j
->current_file
, j
->current_file
->current_offset
, OBJECT_ENTRY
, &o
);
221 sd_id128_to_string(j
->current_file
->header
->seqnum_id
, sid
);
222 sd_id128_to_string(o
->entry
.boot_id
, bid
);
225 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
226 sid
, (unsigned long long) le64toh(o
->entry
.seqnum
),
227 bid
, (unsigned long long) le64toh(o
->entry
.monotonic
),
228 (unsigned long long) le64toh(o
->entry
.realtime
),
229 (unsigned long long) le64toh(o
->entry
.xor_hash
),
230 file_name_from_path(j
->current_file
->path
)) < 0)
236 int sd_journal_set_cursor(sd_journal
*j
, const char *cursor
) {
240 static int add_file(sd_journal
*j
, const char *prefix
, const char *dir
, const char *filename
) {
250 fn
= join(prefix
, "/", dir
, "/", filename
, NULL
);
252 fn
= join(prefix
, "/", filename
, NULL
);
257 r
= journal_file_open(fn
, O_RDONLY
, 0, NULL
, &f
);
267 r
= hashmap_put(j
->files
, f
->path
, f
);
269 journal_file_close(f
);
276 static int add_directory(sd_journal
*j
, const char *prefix
, const char *dir
) {
285 fn
= join(prefix
, "/", dir
, NULL
);
300 struct dirent buf
, *de
;
302 r
= readdir_r(d
, &buf
, &de
);
306 if (!dirent_is_file_with_suffix(de
, ".journal"))
309 r
= add_file(j
, prefix
, dir
, de
->d_name
);
311 log_debug("Failed to add file %s/%s/%s: %s", prefix
, dir
, de
->d_name
, strerror(-r
));
319 int sd_journal_open(sd_journal
**ret
) {
322 const char search_paths
[] =
324 "/var/log/journal\0";
329 j
= new0(sd_journal
, 1);
333 j
->files
= hashmap_new(string_hash_func
, string_compare_func
);
339 /* We ignore most errors here, since the idea is to only open
340 * what's actually accessible, and ignore the rest. */
342 NULSTR_FOREACH(p
, search_paths
) {
348 log_debug("Failed to open %s: %m", p
);
353 struct dirent buf
, *de
;
356 r
= readdir_r(d
, &buf
, &de
);
360 if (dirent_is_file_with_suffix(de
, ".journal")) {
361 r
= add_file(j
, p
, NULL
, de
->d_name
);
363 log_debug("Failed to add file %s/%s: %s", p
, de
->d_name
, strerror(-r
));
365 } else if ((de
->d_type
== DT_DIR
|| de
->d_type
== DT_UNKNOWN
) &&
366 sd_id128_from_string(de
->d_name
, &id
) >= 0) {
368 r
= add_directory(j
, p
, de
->d_name
);
370 log_debug("Failed to add directory %s/%s: %s", p
, de
->d_name
, strerror(-r
));
386 void sd_journal_close(sd_journal
*j
) {
392 while ((f
= hashmap_steal_first(j
->files
)))
393 journal_file_close(f
);
395 hashmap_free(j
->files
);
401 int sd_journal_get_realtime_usec(sd_journal
*j
, uint64_t *ret
) {
413 if (f
->current_offset
<= 0)
416 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
420 *ret
= le64toh(o
->entry
.realtime
);
424 int sd_journal_get_monotonic_usec(sd_journal
*j
, uint64_t *ret
) {
437 if (f
->current_offset
<= 0)
440 r
= sd_id128_get_machine(&id
);
444 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
448 if (!sd_id128_equal(id
, o
->entry
.boot_id
))
451 *ret
= le64toh(o
->entry
.monotonic
);
456 int sd_journal_get_field(sd_journal
*j
, const char *field
, const void **data
, size_t *size
) {
468 if (isempty(field
) || strchr(field
, '='))
475 if (f
->current_offset
<= 0)
478 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
482 field_length
= strlen(field
);
484 n
= journal_file_entry_n_items(o
);
485 for (i
= 0; i
< n
; i
++) {
489 p
= le64toh(o
->entry
.items
[i
].object_offset
);
490 r
= journal_file_move_to_object(f
, p
, OBJECT_DATA
, &o
);
494 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
496 if (l
>= field_length
+1 &&
497 memcmp(o
->data
.payload
, field
, field_length
) == 0 &&
498 o
->data
.payload
[field_length
] == '=') {
502 if ((uint64_t) t
!= l
)
505 *data
= o
->data
.payload
;
511 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
519 int sd_journal_iterate_fields(sd_journal
*j
, const void **data
, size_t *size
) {
534 if (f
->current_offset
<= 0)
537 r
= journal_file_move_to_object(f
, f
->current_offset
, OBJECT_ENTRY
, &o
);
541 n
= journal_file_entry_n_items(o
);
542 if (f
->current_field
>= n
)
545 p
= le64toh(o
->entry
.items
[f
->current_field
].object_offset
);
546 r
= journal_file_move_to_object(f
, p
, OBJECT_DATA
, &o
);
550 l
= le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
);
553 /* We can't read objects larger than 4G on a 32bit machine */
554 if ((uint64_t) t
!= l
)
557 *data
= o
->data
.payload
;