sd-journal.c revision 9f8d29834ba97052403e50ec9b358c0470fa4ceb
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 Poetteringstatic void detach_location(sd_journal *j) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic void reset_location(sd_journal *j) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic void init_location(Location *l, JournalFile *f, Object *o) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart 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, JournalFile *f, Object *o, uint64_t offset) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering init_location(&j->current_location, f, o);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic int match_is_valid(const void *data, size_t size) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering const char *b, *p;
6ad623a3f77e087e308f334525fd4046811f2a9aLennart Poettering for (p = b; p < b + size; p++) {
6ad623a3f77e087e308f334525fd4046811f2a9aLennart Poettering if (*p == '=')
6ad623a3f77e087e308f334525fd4046811f2a9aLennart 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: OR term
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * level 1: AND terms
b3267152783d5784c45010615045d4e8ee459da2Zbigniew Jędrzejewski-Szmek * level 2: OR terms
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * level 3: concrete matches */
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering j->level0 = match_new(NULL, MATCH_OR_TERM);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering j->level1 = match_new(j->level0, MATCH_AND_TERM);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering assert(j->level0->type == MATCH_OR_TERM);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering assert(j->level1->type == MATCH_AND_TERM);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering LIST_FOREACH(matches, l2, j->level1->matches) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering /* Exactly the same match already? Then ignore
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * this addition */
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering /* Same field? Then let's add this to this OR term */
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering if (same_field(data, size, l3->data, l3->size)) {
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger add_here = match_new(j->level1, MATCH_OR_TERM);
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger m = match_new(add_here, MATCH_DISCRETE);
28cb17ef0281efc3a46e5d0e702b0b0ddeaafaa4Filipe Brandenburger_public_ int sd_journal_add_disjunction(sd_journal *j) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering m = match_new(j->level0, MATCH_AND_TERM);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic char *match_make_string(Match *m) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
b3267152783d5784c45010615045d4e8ee459da2Zbigniew Jędrzejewski-Szmek r = strjoin("(", p, ")", NULL);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringchar *journal_make_match_string(sd_journal *j) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering_public_ void sd_journal_flush_matches(sd_journal *j) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic int compare_order(JournalFile *af, Object *ao,
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.
2d49a208f8ad9d1c4e79fa4302451e35d06de707Lennart Poettering * If contents and timestamps match, these entries are
2d49a208f8ad9d1c4e79fa4302451e35d06de707Lennart Poettering * identical, even if the seqnum does not match */
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
b3267152783d5784c45010615045d4e8ee459da2Zbigniew Jędrzejewski-Szmek ao->entry.monotonic == bo->entry.monotonic &&
2d49a208f8ad9d1c4e79fa4302451e35d06de707Lennart Poettering ao->entry.realtime == bo->entry.realtime &&
2d49a208f8ad9d1c4e79fa4302451e35d06de707Lennart Poettering ao->entry.xor_hash == bo->entry.xor_hash)
2d49a208f8ad9d1c4e79fa4302451e35d06de707Lennart 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
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * seqnums? Something is borked, but let's make the
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering * best of it and compare by time. */
2d49a208f8ad9d1c4e79fa4302451e35d06de707Lennart Poettering if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering /* If the boot id matches compare monotonic time */
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering /* Otherwise compare UTC time */
2d49a208f8ad9d1c4e79fa4302451e35d06de707Lennart Poettering /* Finally, compare by contents */
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic int compare_with_location(JournalFile *af, Object *ao, Location *l) {
b3267152783d5784c45010615045d4e8ee459da2Zbigniew Jędrzejewski-Szmek sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
2d49a208f8ad9d1c4e79fa4302451e35d06de707Lennart Poettering le64toh(ao->entry.realtime) == l->realtime &&
2d49a208f8ad9d1c4e79fa4302451e35d06de707Lennart Poettering le64toh(ao->entry.xor_hash) == l->xor_hash)
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
436dd70f5331ec541ebdd84e106abaa63203f6f1Hristo Venev r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
436dd70f5331ec541ebdd84e106abaa63203f6f1Hristo Venev return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
436dd70f5331ec541ebdd84e106abaa63203f6f1Hristo Venev /* Find the earliest match beyond after_offset */
436dd70f5331ec541ebdd84e106abaa63203f6f1Hristo Venev r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
436dd70f5331ec541ebdd84e106abaa63203f6f1Hristo Venev else if (r > 0) {
436dd70f5331ec541ebdd84e106abaa63203f6f1Hristo Venev if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
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)
Iterator i;
return -EINVAL;
Object *o;
uint64_t p;
bool found;
if (!new_current)
found = true;
found = k < 0;
found = k > 0;
if (found) {
new_current = f;
new_entry = o;
new_offset = p;
if (!new_current)
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;
size_t l;
char *state;
seqnum_id_set = false,
seqnum_set = false,
boot_id_set = false,
monotonic_set = false,
realtime_set = false,
xor_hash_set = false;
return -EINVAL;
if (!cursor)
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) {
return -EINVAL;
reset_location(j);
return -EINVAL;
reset_location(j);
return -EINVAL;
reset_location(j);
return -EINVAL;
reset_location(j);
char *path;
JournalFile *f;
assert(j);
if (!path)
return -ENOMEM;
j->current_invalidate_counter ++;
char *path;
JournalFile *f;
assert(j);
if (!path)
return -ENOMEM;
j->current_invalidate_counter ++;
char *path;
DIR *d;
Directory *m;
assert(j);
if (!path)
return -ENOMEM;
return -errno;
closedir(d);
return -ENOMEM;
m->is_root = false;
closedir(d);
free(m);
return -ENOMEM;
j->current_invalidate_counter ++;
} else if (m->is_root) {
closedir(d);
if (r != 0 || !de)
closedir(d);
DIR *d;
Directory *m;
assert(j);
assert(p);
return -EINVAL;
d = opendir(p);
return -errno;
closedir(d);
return -ENOMEM;
m->is_root = true;
if (!m->path) {
closedir(d);
free(m);
return -ENOMEM;
closedir(d);
free(m);
return -ENOMEM;
j->current_invalidate_counter ++;
} else if (!m->is_root) {
closedir(d);
if (r != 0 || !de)
closedir(d);
assert(j);
if (d->wd > 0) {
if (j->inotify_fd >= 0)
if (d->is_root)
free(d);
const char search_paths[] =
assert(j);
add_root_directory(j, p);
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 (!j->files) {
free(j);
return NULL;
if (!j->directories_by_path) {
free(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;
return -EINVAL;
if (flags != 0)
return -EINVAL;
return -ENOMEM;
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)
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
return -EBADMSG;
return -EPROTONOSUPPORT;
t = (size_t) l;
if ((uint64_t) t != l)
return -E2BIG;
*size = t;
return -ENOENT;
JournalFile *f;
uint64_t p, l, n;
Object *o;
size_t t;
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;
t = (size_t) l;
if ((uint64_t) t != l)
return -E2BIG;
#ifdef HAVE_XZ
return -EBADMSG;
return -EPROTONOSUPPORT;
*size = t;
j->current_field ++;
j->current_field = 0;
return -EINVAL;
if (j->inotify_fd >= 0)
return j->inotify_fd;
r = allocate_inotify(j);
r = add_search_paths(j);
return j->inotify_fd;
Directory *d;
assert(j);
assert(e);
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);
assert(j);
if (j->inotify_fd < 0) {
r = sd_journal_get_fd(j);
return determine_change(j);
} 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;