sd-journal.c revision d617408ecbe69db69aefddfcb10a6c054ea46ba0
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. */
fb84d8966e5ce49b0c705768bb4da4956c4e529aTom 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) {
9d96e6c3efbe5ef52b2855612d51db52c469beb2Tom Gundersen const char *b, *p;
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen return false;
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen return false;
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen for (p = b; p < b + size; p++) {
7a695d8e1fda59857c4c23bcb50cd1e0aaf4a854Tom Gundersen if (*p == '=')
7a695d8e1fda59857c4c23bcb50cd1e0aaf4a854Tom Gundersen return p > b;
7a695d8e1fda59857c4c23bcb50cd1e0aaf4a854Tom Gundersen if (*p == '_')
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen return false;
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen return false;
3b015d40c19d9338b66bf916d84dec601019c811Tom 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) {
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen if (!m || m->matches)
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom 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);
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen /* level 0: AND term
3b015d40c19d9338b66bf916d84dec601019c811Tom Gundersen * level 1: OR terms
9d96e6c3efbe5ef52b2855612d51db52c469beb2Tom Gundersen * level 2: AND terms
9d96e6c3efbe5ef52b2855612d51db52c469beb2Tom Gundersen * level 3: OR terms
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen * level 4: concrete matches */
if (!j->level0)
return -ENOMEM;
if (!j->level1) {
if (!j->level1)
return -ENOMEM;
if (!j->level2) {
if (!j->level2)
return -ENOMEM;
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) {
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;
switch (item[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 =
assert(j);
r = -ETOOMANYREFS;
goto fail;
goto fail;
goto fail;
j->current_invalidate_counter ++;
fail:
k = set_put_error(j, r);
const char *path;
assert(j);
if (j->no_new_files ||
const char *path;
JournalFile *f;
assert(j);
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) {
r = -ENOMEM;
goto fail;
goto fail;
r = -ENOMEM;
goto fail;
m->is_root = false;
free(m);
r = -ENOMEM;
goto fail;
j->current_invalidate_counter ++;
} else if (m->is_root)
FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) {
fail:
k = set_put_error(j, r);
Directory *m;
assert(j);
assert(p);
return -EINVAL;
if (j->prefix)
d = opendir(p);
goto fail;
r = -ENOMEM;
goto fail;
m->is_root = true;
if (!m->path) {
free(m);
r = -ENOMEM;
goto fail;
free(m);
r = -ENOMEM;
goto fail;
j->current_invalidate_counter ++;
} else if (!m->is_root)
if (j->no_new_files)
FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) {
fail:
k = set_put_error(j, r);
assert(j);
if (d->wd > 0) {
if (j->inotify_fd >= 0)
if (d->is_root)
free(d);
static const char search_paths[] =
assert(j);
(void) add_root_directory(j, p, true);
Iterator i;
JournalFile *f;
assert(j);
if (!dir)
return -ENOMEM;
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;
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);
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;