sd-journal.c revision c4073a27c555aeceac87a3b02a83141cde641a1e
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering This file is part of systemd.
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering Copyright 2011 Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering systemd is free software; you can redistribute it and/or modify it
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering under the terms of the GNU Lesser General Public License as published by
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering (at your option) any later version.
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering systemd is distributed in the hope that it will be useful, but
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering Lesser General Public License for more details.
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering You should have received a copy of the GNU Lesser General Public License
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering#define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering/* We return an error here only if we didn't manage to
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering memorize the real error. */
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic int set_put_error(sd_journal *j, int r) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering k = set_ensure_allocated(&j->errors, trivial_hash_func, trivial_compare_func);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering return set_put(j->errors, INT_TO_PTR(r));
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic void detach_location(sd_journal *j) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic void reset_location(sd_journal *j) {
6ad623a3f77e087e308f334525fd4046811f2a9aLennart Poetteringstatic void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
6ad623a3f77e087e308f334525fd4046811f2a9aLennart Poettering assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
6ad623a3f77e087e308f334525fd4046811f2a9aLennart Poettering l->realtime = le64toh(o->entry.realtime);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering l->monotonic = le64toh(o->entry.monotonic);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering l->xor_hash = le64toh(o->entry.xor_hash);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o, uint64_t offset) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering init_location(&j->current_location, type, f, o);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic int match_is_valid(const void *data, size_t size) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering const char *b, *p;
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering for (p = b; p < b + size; p++) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering if (*p == '=')
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering if (*p == '_')
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering for (j = 0; j < s && j < t; j++) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering if (a[j] != b[j])
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering if (a[j] == '=')
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic Match *match_new(Match *p, MatchType t) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering LIST_PREPEND(Match, matches, p->matches, m);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering LIST_REMOVE(Match, matches, m->parent->matches, m);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic void match_free_if_empty(Match *m) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering /* level 0: AND term
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * level 1: OR terms
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * level 2: AND terms
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * level 3: OR terms
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger * level 4: concrete matches */
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger j->level0 = match_new(NULL, MATCH_AND_TERM);
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger j->level1 = match_new(j->level0, MATCH_OR_TERM);
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger j->level2 = match_new(j->level1, MATCH_AND_TERM);
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger assert(j->level0->type == MATCH_AND_TERM);
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger assert(j->level1->type == MATCH_OR_TERM);
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger assert(j->level2->type == MATCH_AND_TERM);
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger LIST_FOREACH(matches, l3, j->level2->matches) {
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger LIST_FOREACH(matches, l4, l3->matches) {
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger /* Exactly the same match already? Then ignore
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger * this addition */
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering /* Same field? Then let's add this to this OR term */
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering if (same_field(data, size, l4->data, l4->size)) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering add_here = match_new(j->level2, MATCH_OR_TERM);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering_public_ int sd_journal_add_conjunction(sd_journal *j) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering_public_ int sd_journal_add_disjunction(sd_journal *j) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic char *match_make_string(Match *m) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringchar *journal_make_match_string(sd_journal *j) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering_public_ void sd_journal_flush_matches(sd_journal *j) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering j->level0 = j->level1 = j->level2 = NULL;
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic int compare_entry_order(JournalFile *af, Object *_ao,
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering /* The mmap cache might invalidate the object from the first
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * file if we look at the one from the second file. Hence
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * temporarily copy the header of the first one, and look at
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * that only. */
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering ao = alloca(offsetof(EntryObject, items));
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering memcpy(ao, _ao, offsetof(EntryObject, items));
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering /* We operate on two different files here, hence we can access
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * two objects at the same time, which we normally can't.
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * If contents and timestamps match, these entries are
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * identical, even if the seqnum does not match */
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering ao->entry.monotonic == bo->entry.monotonic &&
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering ao->entry.realtime == bo->entry.realtime &&
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering ao->entry.xor_hash == bo->entry.xor_hash)
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering /* If this is from the same seqnum source, compare
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering /* Wow! This is weird, different data but the same
uint64_t a;
assert(l);
if (l->monotonic_set &&
l->realtime_set &&
l->xor_hash_set &&
if (l->seqnum_set &&
if (a < l->seqnum)
if (a > l->seqnum)
if (l->monotonic_set &&
if (a < l->monotonic)
if (a > l->monotonic)
if (l->realtime_set) {
if (a < l->realtime)
if (a > l->realtime)
if (l->xor_hash_set) {
if (a < l->xor_hash)
if (a > l->xor_hash)
static int next_for_match(
sd_journal *j,
Match *m,
JournalFile *f,
Object *n;
assert(j);
assert(m);
assert(f);
Match *i;
Match *i;
bool continue_looking;
if (!m->matches)
np = 0;
continue_looking = false;
if (np == 0)
continue_looking = true;
} while (continue_looking);
if (np == 0)
if (ret)
*ret = n;
if (offset)
static int find_location_for_match(
sd_journal *j,
Match *m,
JournalFile *f,
assert(j);
assert(m);
assert(f);
if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
if (r != -ENOENT)
return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
Object *n;
Match *i;
if (np == 0)
if (ret)
*ret = n;
if (offset)
Match *i;
if (!m->matches)
static int find_location_with_matches(
sd_journal *j,
JournalFile *f,
assert(j);
assert(f);
if (!j->level0) {
if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
if (r != -ENOENT)
return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
static int next_with_matches(
sd_journal *j,
JournalFile *f,
Object *c;
assert(j);
assert(f);
c = *ret;
if (!j->level0)
return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
Object *c;
assert(j);
assert(f);
if (f->current_offset > 0) {
bool found;
found = k > 0;
found = k < 0;
found = true;
if (found) {
if (ret)
*ret = c;
if (offset)
Object *o;
uint64_t p;
Iterator i;
return -EINVAL;
bool found;
if (!new_file)
found = true;
found = k < 0;
found = k > 0;
if (found) {
new_file = f;
new_offset = p;
if (!new_file)
return -EINVAL;
if (skip == 0) {
skip--;
} while (skip > 0);
Object *o;
return -EINVAL;
if (!cursor)
return -EINVAL;
return -EADDRNOTAVAIL;
r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
return -ENOMEM;
char *w, *state;
size_t l;
seqnum_id_set = false,
seqnum_set = false,
boot_id_set = false,
monotonic_set = false,
realtime_set = false,
xor_hash_set = false;
return -EINVAL;
return -EINVAL;
char *item;
return -EINVAL;
if (!item)
return -ENOMEM;
seqnum_id_set = true;
seqnum_set = true;
k = -EINVAL;
boot_id_set = true;
monotonic_set = true;
k = -EINVAL;
realtime_set = true;
k = -EINVAL;
xor_hash_set = true;
k = -EINVAL;
return -EINVAL;
reset_location(j);
if (realtime_set) {
if (xor_hash_set) {
char *w, *state;
size_t l;
Object *o;
return -EINVAL;
return -EINVAL;
return -EADDRNOTAVAIL;
r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
unsigned long long ll;
return -EINVAL;
if (!item)
return -ENOMEM;
return -EINVAL;
return -EINVAL;
return -EINVAL;
return -EINVAL;
return -EINVAL;
reset_location(j);
return -EINVAL;
reset_location(j);
return -EINVAL;
reset_location(j);
return -EINVAL;
reset_location(j);
assert(j);
if (j->on_network)
j->on_network =
JournalFile *f;
assert(j);
if (!path)
return -ENOMEM;
j->current_invalidate_counter ++;
char *path;
JournalFile *f;
assert(j);
if (!path)
return -ENOMEM;
if (j->current_file == f) {
j->current_field = 0;
if (j->unique_file == f) {
j->unique_offset = 0;
j->current_invalidate_counter ++;
Directory *m;
assert(j);
if (!path)
return -ENOMEM;
return -errno;
return -ENOMEM;
m->is_root = false;
free(m);
return -ENOMEM;
j->current_invalidate_counter ++;
} else if (m->is_root)
if (r != 0 || !de)
r = set_put_error(j, r);
Directory *m;
assert(j);
assert(p);
return -EINVAL;
d = opendir(p);
return -errno;
return -ENOMEM;
m->is_root = true;
if (!m->path) {
free(m);
return -ENOMEM;
free(m);
return -ENOMEM;
j->current_invalidate_counter ++;
} else if (!m->is_root)
if (r != 0 || !de)
r = set_put_error(j, r);
assert(j);
if (d->wd > 0) {
if (j->inotify_fd >= 0)
if (d->is_root)
free(d);
const char search_paths[] =
assert(j);
r = add_root_directory(j, p);
if (r < 0 && r != -ENOENT) {
r = set_put_error(j, r);
assert(j);
if (j->inotify_fd < 0) {
if (j->inotify_fd < 0)
return -errno;
if (!j->directories_by_wd) {
if (!j->directories_by_wd)
return -ENOMEM;
sd_journal *j;
return NULL;
if (path) {
if (!j->path)
goto fail;
goto fail;
fail:
sd_journal_close(j);
return NULL;
sd_journal *j;
if (!ret)
return -EINVAL;
return -EINVAL;
return -ENOMEM;
r = add_search_paths(j);
goto fail;
*ret = j;
fail:
sd_journal_close(j);
sd_journal *j;
if (!ret)
return -EINVAL;
if (!path)
return -EINVAL;
if (flags != 0)
return -EINVAL;
return -ENOMEM;
set_put_error(j, r);
goto fail;
*ret = j;
fail:
sd_journal_close(j);
Directory *d;
JournalFile *f;
remove_directory(j, d);
remove_directory(j, d);
if (j->inotify_fd >= 0)
if (j->mmap)
free(j);
Object *o;
JournalFile *f;
return -EINVAL;
if (!ret)
return -EINVAL;
f = j->current_file;
return -EADDRNOTAVAIL;
if (f->current_offset <= 0)
return -EADDRNOTAVAIL;
Object *o;
JournalFile *f;
return -EINVAL;
f = j->current_file;
return -EADDRNOTAVAIL;
if (f->current_offset <= 0)
return -EADDRNOTAVAIL;
if (ret_boot_id)
return -ESTALE;
if (ret)
for (p = field; *p; p++) {
_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
JournalFile *f;
uint64_t i, n;
Object *o;
return -EINVAL;
if (!field)
return -EINVAL;
if (!data)
return -EINVAL;
if (!size)
return -EINVAL;
return -EINVAL;
f = j->current_file;
return -EADDRNOTAVAIL;
if (f->current_offset <= 0)
return -EADDRNOTAVAIL;
n = journal_file_entry_n_items(o);
uint64_t p, l;
size_t t;
return -EBADMSG;
#ifdef HAVE_XZ
j->data_threshold))
return -EBADMSG;
return -EPROTONOSUPPORT;
t = (size_t) l;
if ((uint64_t) t != l)
return -E2BIG;
*size = t;
return -ENOENT;
size_t t;
uint64_t l;
t = (size_t) l;
if ((uint64_t) t != l)
return -E2BIG;
#ifdef HAVE_XZ
if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, j->data_threshold))
return -EBADMSG;
return -EPROTONOSUPPORT;
*size = t;
JournalFile *f;
uint64_t p, n;
Object *o;
return -EINVAL;
if (!data)
return -EINVAL;
if (!size)
return -EINVAL;
f = j->current_file;
return -EADDRNOTAVAIL;
if (f->current_offset <= 0)
return -EADDRNOTAVAIL;
n = journal_file_entry_n_items(o);
if (j->current_field >= n)
return -EBADMSG;
j->current_field ++;
j->current_field = 0;
return -EINVAL;
if (j->inotify_fd >= 0)
return j->inotify_fd;
r = allocate_inotify(j);
if (j->path)
r = add_search_paths(j);
return j->inotify_fd;
int fd;
return -EINVAL;
if (fd < 0)
return fd;
return POLLIN;
int fd;
return -EINVAL;
if (!timeout_usec)
return -EINVAL;
if (fd < 0)
return fd;
if (!j->on_network) {
Directory *d;
assert(j);
assert(e);
set_put_error(j, r);
r = remove_directory(j, d);
} else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
assert(j);
bool got_something = false;
return -EINVAL;
struct inotify_event *e;
ssize_t l;
return -errno;
got_something = true;
process_inotify_event(j, e);
l -= step;
return determine_change(j);
uint64_t t;
assert(j);
if (j->inotify_fd < 0) {
r = sd_journal_get_fd(j);
return determine_change(j);
r = sd_journal_get_timeout(j, &t);
usec_t n;
timeout_usec = t;
} while (r == -EINTR);
return sd_journal_process(j);
Iterator i;
JournalFile *f;
bool first = true;
return -EINVAL;
return -EINVAL;
if (r == -ENOENT)
if (first) {
if (from)
if (to)
*to = t;
first = false;
if (from)
if (to)
_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
Iterator i;
JournalFile *f;
bool first = true;
return -EINVAL;
return -EINVAL;
if (r == -ENOENT)
if (first) {
if (from)
if (to)
*to = t;
first = false;
if (from)
if (to)
Iterator i;
JournalFile *f;
bool newline = false;
assert(j);
if (newline)
newline = true;
Iterator i;
JournalFile *f;
return -EINVAL;
if (!bytes)
return -EINVAL;
return -errno;
return -EINVAL;
return -EINVAL;
return -EINVAL;
return -ENOMEM;
j->unique_field = f;
j->unique_offset = 0;
Object *o;
size_t k;
return -EINVAL;
if (!data)
return -EINVAL;
return -EINVAL;
if (!j->unique_field)
return -EINVAL;
if (!j->unique_file) {
if (!j->unique_file)
j->unique_offset = 0;
Iterator i;
const void *odata;
bool found;
if (j->unique_offset == 0) {
if (j->unique_offset == 0) {
JournalFile *n;
j->unique_file = n;
return -EBADMSG;
found = false;
found = true;
if (found)
j->unique_offset = 0;
return -EINVAL;
return !j->on_network;
const void *data;
assert(j);
const void *data;
return -EINVAL;
if (!ret)
return -EINVAL;
if (!cid)
return -ENOMEM;
return -ENOMEM;
*ret = t;
if (!ret)
return -EINVAL;
return -EINVAL;
return -EINVAL;
if (!sz)
return -EINVAL;