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;
46 uint64_t current_field;
48 LIST_HEAD(Match, matches);
51 int sd_journal_add_match(sd_journal *j, const char *field, const void *data, size_t size) {
57 assert(data || size == 0);
63 m->size = strlen(field) + 1 + size;
64 m->data = malloc(m->size);
70 e = stpcpy(m->data, field);
72 memcpy(e, data, size);
74 LIST_PREPEND(Match, matches, j->matches, m);
78 void sd_journal_flush_matches(sd_journal *j) {
82 Match *m = j->matches;
84 LIST_REMOVE(Match, matches, j->matches, m);
90 static int compare_order(JournalFile *af, Object *ao, uint64_t ap,
91 JournalFile *bf, Object *bo, uint64_t bp) {
95 /* We operate on two different files here, hence we can access
96 * two objects at the same time, which we normally can't */
98 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
100 /* If this is from the same seqnum source, compare
102 a = le64toh(ao->entry.seqnum);
103 b = le64toh(bo->entry.seqnum);
111 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
113 /* If the boot id matches compare monotonic time */
114 a = le64toh(ao->entry.monotonic);
115 b = le64toh(bo->entry.monotonic);
123 /* Otherwise compare UTC time */
124 a = le64toh(ao->entry.realtime);
125 b = le64toh(ao->entry.realtime);
132 /* Finally, compare by contents */
133 a = le64toh(ao->entry.xor_hash);
134 b = le64toh(ao->entry.xor_hash);
144 int sd_journal_next(sd_journal *j) {
145 JournalFile *f, *new_current = NULL;
148 uint64_t new_offset = 0;
149 Object *new_entry = NULL;
153 HASHMAP_FOREACH(f, j->files, i) {
157 if (f->current_offset > 0) {
158 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
164 r = journal_file_next_entry(f, o, &o, &p);
171 compare_order(new_current, new_entry, new_offset, f, o, p) > 0) {
179 j->current_file = new_current;
180 j->current_file->current_offset = new_offset;
181 j->current_field = 0;
183 /* Skip over any identical entries in the other files too */
185 HASHMAP_FOREACH(f, j->files, i) {
189 if (j->current_file == f)
192 if (f->current_offset > 0) {
193 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
199 r = journal_file_next_entry(f, o, &o, &p);
205 if (compare_order(new_current, new_entry, new_offset, f, o, p) == 0)
206 f->current_offset = p;
215 int sd_journal_previous(sd_journal *j) {
216 JournalFile *f, *new_current = NULL;
219 uint64_t new_offset = 0;
220 Object *new_entry = NULL;
224 HASHMAP_FOREACH(f, j->files, i) {
228 if (f->current_offset > 0) {
229 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
235 r = journal_file_prev_entry(f, o, &o, &p);
241 if (!new_current || compare_order(new_current, new_entry, new_offset, f, o, p) > 0) {
249 j->current_file = new_current;
250 j->current_file->current_offset = new_offset;
251 j->current_field = 0;
253 /* Skip over any identical entries in the other files too */
255 HASHMAP_FOREACH(f, j->files, i) {
259 if (j->current_file == f)
262 if (f->current_offset > 0) {
263 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
269 r = journal_file_prev_entry(f, o, &o, &p);
275 if (compare_order(new_current, new_entry, new_offset, f, o, p) == 0)
276 f->current_offset = p;
285 int sd_journal_get_cursor(sd_journal *j, char **cursor) {
288 char bid[33], sid[33];
293 if (!j->current_file || j->current_file->current_offset <= 0)
294 return -EADDRNOTAVAIL;
296 r = journal_file_move_to_object(j->current_file, j->current_file->current_offset, OBJECT_ENTRY, &o);
300 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
301 sd_id128_to_string(o->entry.boot_id, bid);
304 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
305 sid, (unsigned long long) le64toh(o->entry.seqnum),
306 bid, (unsigned long long) le64toh(o->entry.monotonic),
307 (unsigned long long) le64toh(o->entry.realtime),
308 (unsigned long long) le64toh(o->entry.xor_hash),
309 file_name_from_path(j->current_file->path)) < 0)
315 int sd_journal_set_cursor(sd_journal *j, const char *cursor) {
319 static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
329 fn = join(prefix, "/", dir, "/", filename, NULL);
331 fn = join(prefix, "/", filename, NULL);
336 r = journal_file_open(fn, O_RDONLY, 0, NULL, &f);
346 r = hashmap_put(j->files, f->path, f);
348 journal_file_close(f);
355 static int add_directory(sd_journal *j, const char *prefix, const char *dir) {
364 fn = join(prefix, "/", dir, NULL);
379 struct dirent buf, *de;
381 r = readdir_r(d, &buf, &de);
385 if (!dirent_is_file_with_suffix(de, ".journal"))
388 r = add_file(j, prefix, dir, de->d_name);
390 log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r));
398 int sd_journal_open(sd_journal **ret) {
401 const char search_paths[] =
403 "/var/log/journal\0";
408 j = new0(sd_journal, 1);
412 j->files = hashmap_new(string_hash_func, string_compare_func);
418 /* We ignore most errors here, since the idea is to only open
419 * what's actually accessible, and ignore the rest. */
421 NULSTR_FOREACH(p, search_paths) {
427 log_debug("Failed to open %s: %m", p);
432 struct dirent buf, *de;
435 r = readdir_r(d, &buf, &de);
439 if (dirent_is_file_with_suffix(de, ".journal")) {
440 r = add_file(j, p, NULL, de->d_name);
442 log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r));
444 } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
445 sd_id128_from_string(de->d_name, &id) >= 0) {
447 r = add_directory(j, p, de->d_name);
449 log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r));
465 void sd_journal_close(sd_journal *j) {
471 while ((f = hashmap_steal_first(j->files)))
472 journal_file_close(f);
474 hashmap_free(j->files);
480 int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
492 if (f->current_offset <= 0)
495 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
499 *ret = le64toh(o->entry.realtime);
503 int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret) {
516 if (f->current_offset <= 0)
519 r = sd_id128_get_machine(&id);
523 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
527 if (!sd_id128_equal(id, o->entry.boot_id))
530 *ret = le64toh(o->entry.monotonic);
535 int sd_journal_get_field(sd_journal *j, const char *field, const void **data, size_t *size) {
547 if (isempty(field) || strchr(field, '='))
554 if (f->current_offset <= 0)
557 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
561 field_length = strlen(field);
563 n = journal_file_entry_n_items(o);
564 for (i = 0; i < n; i++) {
568 p = le64toh(o->entry.items[i].object_offset);
569 r = journal_file_move_to_object(f, p, OBJECT_DATA, &o);
573 l = le64toh(o->object.size) - offsetof(Object, data.payload);
575 if (l >= field_length+1 &&
576 memcmp(o->data.payload, field, field_length) == 0 &&
577 o->data.payload[field_length] == '=') {
581 if ((uint64_t) t != l)
584 *data = o->data.payload;
590 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
598 int sd_journal_iterate_fields(sd_journal *j, const void **data, size_t *size) {
613 if (f->current_offset <= 0)
616 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
620 n = journal_file_entry_n_items(o);
621 if (j->current_field >= n)
624 p = le64toh(o->entry.items[j->current_field].object_offset);
625 r = journal_file_move_to_object(f, p, OBJECT_DATA, &o);
629 l = le64toh(o->object.size) - offsetof(Object, data.payload);
632 /* We can't read objects larger than 4G on a 32bit machine */
633 if ((uint64_t) t != l)
636 *data = o->data.payload;
644 int sd_journal_seek_head(sd_journal *j) {
649 int sd_journal_seek_tail(sd_journal *j) {