sd-journal.c revision 4a842cadb8d6b30fa9fdc8ff183633c14e02cf96
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering This file is part of systemd.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering Copyright 2011 Lennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering systemd is free software; you can redistribute it and/or modify it
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering under the terms of the GNU Lesser General Public License as published by
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering (at your option) any later version.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering systemd is distributed in the hope that it will be useful, but
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering Lesser General Public License for more details.
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering You should have received a copy of the GNU Lesser General Public License
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringstatic void detach_location(sd_journal *j) {
bb99a35a873c35e80b0b47fe045081022660374dLennart Poetteringstatic void reset_location(sd_journal *j) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringstatic void init_location(Location *l, JournalFile *f, Object *o) {
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering l->realtime = le64toh(o->entry.realtime);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering l->monotonic = le64toh(o->entry.monotonic);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering l->xor_hash = le64toh(o->entry.xor_hash);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poetteringstatic void set_location(sd_journal *j, JournalFile *f, Object *o, uint64_t offset) {
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering init_location(&j->current_location, f, o);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringstatic int match_is_valid(const void *data, size_t size) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering const char *b, *p;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering for (p = b; p < b + size; p++) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (*p == '=')
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (*p == '_')
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringstatic bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering for (j = 0; j < s && j < t; j++) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (a[j] != b[j])
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (a[j] == '=')
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringstatic Match *match_new(Match *p, MatchType t) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering LIST_PREPEND(Match, matches, p->matches, m);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering LIST_REMOVE(Match, matches, m->parent->matches, m);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringstatic void match_free_if_empty(Match *m) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering /* level 0: OR term
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * level 1: AND terms
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering * level 2: OR terms
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering * level 3: concrete matches */
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering j->level0 = match_new(NULL, MATCH_OR_TERM);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering j->level1 = match_new(j->level0, MATCH_AND_TERM);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(j->level0->type == MATCH_OR_TERM);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert(j->level1->type == MATCH_AND_TERM);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering LIST_FOREACH(matches, l2, j->level1->matches) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* Exactly the same match already? Then ignore
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * this addition */
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* Same field? Then let's add this to this OR term */
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (same_field(data, size, l3->data, l3->size)) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering add_here = match_new(j->level1, MATCH_OR_TERM);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering_public_ int sd_journal_add_disjunction(sd_journal *j) {
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering m = match_new(j->level0, MATCH_AND_TERM);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poetteringstatic char *match_make_string(Match *m) {
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poetteringchar *journal_make_match_string(sd_journal *j) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering_public_ void sd_journal_flush_matches(sd_journal *j) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poetteringstatic int compare_order(JournalFile *af, Object *ao,
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering /* We operate on two different files here, hence we can access
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering * two objects at the same time, which we normally can't.
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering * If contents and timestamps match, these entries are
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering * identical, even if the seqnum does not match */
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering ao->entry.monotonic == bo->entry.monotonic &&
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering ao->entry.realtime == bo->entry.realtime &&
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering ao->entry.xor_hash == bo->entry.xor_hash)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering /* If this is from the same seqnum source, compare
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering /* Wow! This is weird, different data but the same
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering * seqnums? Something is borked, but let's make the
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering * best of it and compare by time. */
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering /* If the boot id matches compare monotonic time */
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering /* Otherwise compare UTC time */
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering /* Finally, compare by contents */
bb99a35a873c35e80b0b47fe045081022660374dLennart Poetteringstatic int compare_with_location(JournalFile *af, Object *ao, Location *l) {
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering le64toh(ao->entry.realtime) == l->realtime &&
4cd9a9d9ecf3a8835e21930f3215a5f5b74144beLennart Poettering le64toh(ao->entry.xor_hash) == l->xor_hash)
224f2ee221e77c326d1d7761abb6e812432b2163Lennart Poettering sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* Find the earliest match beyond after_offset */
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering else if (r > 0) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* Always jump to the next matching entry and repeat
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * this until we fine and offset that matches for all
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering r = next_for_match(j, i, f, limit, direction, NULL, &cp);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) &&
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering (np == 0 || (direction == DIRECTION_DOWN ? cp > np : np < cp))) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
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 (path) {
if (!j->path) {
free(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);
if (j->path)
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;