sd-journal.c revision b29ddfcb389127cf00ab41447a721e479fe15713
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen This file is part of systemd.
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen Copyright 2011 Lennart Poettering
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen systemd is free software; you can redistribute it and/or modify it
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen under the terms of the GNU Lesser General Public License as published by
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen the Free Software Foundation; either version 2.1 of the License, or
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen (at your option) any later version.
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen systemd is distributed in the hope that it will be useful, but
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen Lesser General Public License for more details.
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen You should have received a copy of the GNU Lesser General Public License
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen#define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersenstatic void remove_file_real(sd_journal *j, JournalFile *f);
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersenstatic bool journal_pid_changed(sd_journal *j) {
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen /* We don't support people creating a journal object and
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen * keeping it around over a fork(). Let's complain. */
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen/* We return an error here only if we didn't manage to
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen memorize the real error. */
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersenstatic int set_put_error(sd_journal *j, int r) {
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersenstatic void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersenstatic void set_location(sd_journal *j, JournalFile *f, Object *o) {
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen init_location(&j->current_location, LOCATION_DISCRETE, f, o);
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen /* Let f know its candidate entry was picked. */
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersenstatic int match_is_valid(const void *data, size_t size) {
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen const char *b, *p;
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen return false;
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen return false;
9d96e6c3efbe5ef52b2855612d51db52c469beb2Tom Gundersen for (p = b; p < b + size; p++) {
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen if (*p == '=')
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen return p > b;
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen if (*p == '_')
7a695d8e1fda59857c4c23bcb50cd1e0aaf4a854Tom Gundersen return false;
7a695d8e1fda59857c4c23bcb50cd1e0aaf4a854Tom Gundersen return false;
7a695d8e1fda59857c4c23bcb50cd1e0aaf4a854Tom Gundersenstatic bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen for (j = 0; j < s && j < t; j++) {
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen if (a[j] != b[j])
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen return false;
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen if (a[j] == '=')
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersenstatic Match *match_new(Match *p, MatchType t) {
962b0647298b601548b049893931d2477f06ab57Tom Gundersen if (!m || m->matches)
9d96e6c3efbe5ef52b2855612d51db52c469beb2Tom Gundersen_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen assert_return(!journal_pid_changed(j), -ECHILD);
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen assert_return(match_is_valid(data, size), -EINVAL);
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen /* level 0: AND term
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen * level 1: OR terms
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen * level 2: AND terms
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen * level 3: OR terms
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen * level 4: concrete matches */
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen j->level1 = match_new(j->level0, MATCH_OR_TERM);
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen j->level2 = match_new(j->level1, MATCH_AND_TERM);
if (add_here)
if (!add_here) {
if (!add_here)
goto fail;
goto fail;
if (!m->data)
goto fail;
detach_location(j);
fail:
return -ENOMEM;
if (!j->level0)
if (!j->level1)
if (!j->level0)
if (!j->level1)
if (!j->level2)
Match *i;
bool enclose = false;
p = NULL;
t = match_make_string(i);
free(p);
return NULL;
free(p);
free(t);
return NULL;
enclose = true;
if (enclose) {
free(p);
assert(j);
if (j->level0)
detach_location(j);
assert(f);
assert(l);
if (l->monotonic_set &&
l->realtime_set &&
l->xor_hash_set &&
if (l->seqnum_set &&
if (l->monotonic_set &&
if (l->realtime_set) {
if (l->xor_hash_set) {
static int next_for_match(
sd_journal *j,
Match *m,
JournalFile *f,
Object *n;
assert(j);
assert(m);
assert(f);
Match *i;
if (np == 0)
if (!m->matches)
last_moved = i;
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,
assert(j);
assert(f);
if (!j->level0)
Object *c;
assert(j);
assert(f);
bool found;
found = true;
if (found)
Iterator i;
Object *o;
bool found;
remove_file_real(j, f);
if (!new_file)
found = true;
if (found)
new_file = f;
if (!new_file)
if (skip == 0) {
skip--;
} while (skip > 0);
Object *o;
return -EADDRNOTAVAIL;
r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
return -ENOMEM;
size_t l;
seqnum_id_set = false,
seqnum_set = false,
boot_id_set = false,
monotonic_set = false,
realtime_set = false,
xor_hash_set = false;
char *item;
return -EINVAL;
if (!item)
return -ENOMEM;
switch (word[0]) {
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) {
size_t l;
Object *o;
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;
switch (word[0]) {
return -EINVAL;
return -EINVAL;
return -EINVAL;
return -EINVAL;
reset_location(j);
reset_location(j);
reset_location(j);
reset_location(j);
assert(j);
if (j->on_network)
j->on_network =
< (int) sizeof(prefix));
assert(j);
j->current_invalidate_counter ++;
assert(j);
if (j->no_new_files ||
if (!path)
return -ENOMEM;
if (r == -ENOENT)
JournalFile *f;
assert(j);
if (!path)
return -ENOMEM;
remove_file_real(j, f);
assert(j);
assert(f);
if (j->current_file == f) {
j->current_field = 0;
if (j->unique_file == f) {
j->unique_offset = 0;
if (!j->unique_file)
j->unique_file_lost = true;
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)
errno = 0;
r = -errno;
if (!de)
r = set_put_error(j, r);
Directory *m;
assert(j);
assert(p);
return -EINVAL;
if (j->prefix)
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 (j->no_new_files)
errno = 0;
r = -errno;
if (!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);
Iterator i;
JournalFile *f;
assert(j);
if (!dir)
return -ENOMEM;
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;
assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
return -ENOMEM;
r = add_search_paths(j);
goto fail;
*ret = j;
fail:
sd_journal_close(j);
sd_journal *j;
if (r == -ENOENT)
return -EHOSTDOWN;
if (!root)
return -ENODATA;
return -EIO;
return -ENOMEM;
r = add_search_paths(j);
goto fail;
*ret = j;
fail:
sd_journal_close(j);
sd_journal *j;
return -ENOMEM;
set_put_error(j, r);
goto fail;
*ret = j;
fail:
sd_journal_close(j);
sd_journal *j;
const char **path;
return -ENOMEM;
goto fail;
j->no_new_files = true;
*ret = j;
fail:
sd_journal_close(j);
Directory *d;
JournalFile *f;
remove_directory(j, d);
remove_directory(j, d);
if (j->mmap) {
log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
free(j);
Object *o;
JournalFile *f;
f = j->current_file;
return -EADDRNOTAVAIL;
if (f->current_offset <= 0)
return -EADDRNOTAVAIL;
Object *o;
JournalFile *f;
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;
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;
int compression;
return -EBADMSG;
if (compression) {
j->data_threshold);
return -EPROTONOSUPPORT;
t = (size_t) l;
if ((uint64_t) t != l)
return -E2BIG;
*size = t;
return -ENOENT;
size_t t;
uint64_t l;
int compression;
t = (size_t) l;
if ((uint64_t) t != l)
return -E2BIG;
if (compression) {
return -EPROTONOSUPPORT;
*size = t;
JournalFile *f;
uint64_t p, n;
Object *o;
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;
if (j->inotify_fd >= 0)
return j->inotify_fd;
r = allocate_inotify(j);
if (j->no_new_files)
r = add_current_paths(j);
else if (j->path)
r = add_search_paths(j);
return j->inotify_fd;
int fd;
if (fd < 0)
return fd;
return POLLIN;
int fd;
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;
struct inotify_event *e;
ssize_t l;
return -errno;
got_something = true;
process_inotify_event(j, e);
uint64_t t;
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;
if (r == -ENOENT)
if (first) {
tmax = 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 found = false;
if (r == -ENOENT)
if (found) {
if (from)
if (to)
if (from)
if (to)
*to = t;
found = true;
return found;
Iterator i;
JournalFile *f;
bool newline = false;
assert(j);
if (newline)
newline = true;
Iterator i;
JournalFile *f;
return -errno;
return -ENOMEM;
j->unique_field = f;
j->unique_offset = 0;
j->unique_file_lost = false;
size_t k;
if (!j->unique_file) {
if (j->unique_file_lost)
if (!j->unique_file)
j->unique_offset = 0;
Iterator i;
Object *o;
const void *odata;
bool found;
if (j->unique_offset == 0) {
if (j->unique_offset == 0) {
if (!j->unique_file)
return -EBADMSG;
if (ol <= k) {
return -EBADMSG;
j->unique_field);
return -EBADMSG;
found = false;
found = true;
if (found)
j->unique_offset = 0;
j->unique_file_lost = false;
return !j->on_network;
const void *data;
assert(j);
const void *data;
if (!cid)
return -ENOMEM;
return -ENOMEM;
*ret = t;