sd-journal.c revision 19a2bd80c18d2845f0e37699c8d8df8c8cb4ed78
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <unistd.h>
#include "sd-journal.h"
#include "journal-def.h"
#include "journal-file.h"
#include "hashmap.h"
#include "list.h"
#include "lookup3.h"
#include "compress.h"
#include "journal-internal.h"
#define JOURNAL_FILES_MAX 1024
static void detach_location(sd_journal *j) {
Iterator i;
JournalFile *f;
assert(j);
j->current_file = NULL;
j->current_field = 0;
HASHMAP_FOREACH(f, j->files, i)
f->current_offset = 0;
}
static void reset_location(sd_journal *j) {
assert(j);
detach_location(j);
zero(j->current_location);
}
assert(l);
assert(f);
l->type = LOCATION_DISCRETE;
}
assert(j);
assert(f);
assert(o);
init_location(&j->current_location, f, o);
j->current_file = f;
j->current_field = 0;
f->current_offset = offset;
}
size_t j;
for (j = 0; j < s && j < t; j++) {
if (a[j] == '=')
a_good = true;
if (b[j] == '=')
b_good = true;
if (a[j] != b[j])
different = true;
return different ? 0 : 1;
}
return -EINVAL;
}
if (!j)
return -EINVAL;
if (!data)
return -EINVAL;
if (size <= 0)
return -EINVAL;
int r;
return 0;
if (r < 0)
return r;
else if (r > 0)
after = m;
}
if (!m)
return -ENOMEM;
if (!m->data) {
free(m);
return -ENOMEM;
}
/* Matches for the same fields we order adjacent to each
* other */
j->n_matches ++;
detach_location(j);
return 0;
}
if (!j)
return;
while (j->matches) {
free(m);
}
j->n_matches = 0;
detach_location(j);
}
uint64_t a, b;
/* We operate on two different files here, hence we can access
* two objects at the same time, which we normally can't.
*
* If contents and timestamps match, these entries are
* identical, even if the seqnum does not match */
return 0;
/* If this is from the same seqnum source, compare
* seqnums */
if (a < b)
return -1;
if (a > b)
return 1;
/* Wow! This is weird, different data but the same
* seqnums? Something is borked, but let's make the
* best of it and compare by time. */
}
/* If the boot id matches compare monotonic time */
if (a < b)
return -1;
if (a > b)
return 1;
}
/* Otherwise compare UTC time */
if (a < b)
return -1;
if (a > b)
return 1;
/* Finally, compare by contents */
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
}
uint64_t a;
assert(l);
if (l->monotonic_set &&
l->realtime_set &&
l->xor_hash_set &&
return 0;
if (l->seqnum_set &&
if (a < l->seqnum)
return -1;
if (a > l->seqnum)
return 1;
}
if (l->monotonic_set &&
if (a < l->monotonic)
return -1;
if (a > l->monotonic)
return 1;
}
if (l->realtime_set) {
if (a < l->realtime)
return -1;
if (a > l->realtime)
return 1;
}
if (l->xor_hash_set) {
if (a < l->xor_hash)
return -1;
if (a > l->xor_hash)
return 1;
}
return 0;
}
static int find_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
uint64_t p = 0;
int r;
assert(j);
if (!j->matches) {
/* No matches is simple */
else if (j->current_location.seqnum_set &&
else if (j->current_location.monotonic_set)
r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
else if (j->current_location.realtime_set)
else
if (r <= 0)
return r;
} else {
/* We have matches, first, let's jump to the monotonic
* position if we have any, since it implies a
* match. */
r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
if (r <= 0)
return r == -ENOENT ? 0 : r;
}
Object *c, *d;
if (r <= 0)
return r;
else if (j->current_location.seqnum_set &&
r = journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, &c, &cp);
else if (j->current_location.realtime_set)
r = journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, &c, &cp);
else
if (r < 0)
return r;
if (!term_match) {
term_match = m;
if (r > 0) {
to = c;
}
/* Same field as previous match... */
if (r > 0) {
/* Find the earliest of the OR matches */
if (!to ||
to = c;
}
}
} else {
/* Previous term is finished, did anything match? */
if (!to)
return 0;
/* Find the last of the AND matches */
if (!o ||
o = to;
p = tp;
}
term_match = m;
if (r > 0) {
to = c;
} else {
tp = 0;
}
}
}
/* Last term is finished, did anything match? */
if (!to)
return 0;
if (!o ||
o = to;
p = tp;
}
if (!o)
return 0;
}
if (ret)
*ret = o;
if (offset)
*offset = p;
return 1;
}
static int next_with_matches(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
int r;
Object *c;
assert(j);
assert(f);
c = *ret;
if (!j->matches) {
/* No matches is easy */
if (r <= 0)
return r;
if (ret)
*ret = c;
if (offset)
return 1;
}
/* So there are matches we have to adhere to, let's find the
* first entry that matches all of them */
for (;;) {
bool found, term_result = false;
n = journal_file_entry_n_items(c);
/* Make sure we don't match the entry we are starting
* from. */
np = 0;
uint64_t q, k;
/* Let's check if this is the beginning of a
* new term, i.e. has a different field prefix
* as the preceeding match. */
if (!term_match) {
term_match = m;
term_result = false;
if (!term_result)
found = false;
term_match = m;
term_result = false;
}
for (k = 0; k < n; k++)
break;
if (k >= n) {
/* Hmm, didn't find any field that
* matched this rule, so ignore this
* match. Go on with next match */
continue;
}
term_result = true;
/* Hmm, so, this field matched, let's remember
* where we'd have to try next, in case the other
* matches are not OK */
r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, &qo, &q);
if (r < 0)
return r;
if (r > 0) {
if (direction == DIRECTION_DOWN) {
if (q > np) {
np = q;
}
} else {
np = q;
}
}
}
}
/* Check the last term */
if (term_match && !term_result)
found = false;
/* Did this entry match against all matches? */
if (found) {
if (ret)
*ret = c;
if (offset)
return 1;
}
/* Did we find a subsequent entry? */
if (np == 0)
return 0;
/* Hmm, ok, this entry only matched partially, so
* let's try another one */
c = npo;
}
}
static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
Object *c;
int compare_value, r;
assert(j);
assert(f);
if (f->current_offset > 0) {
cp = f->current_offset;
if (r < 0)
return r;
if (r <= 0)
return r;
compare_value = 1;
} else {
if (r <= 0)
return r;
compare_value = 0;
}
for (;;) {
bool found;
int k;
k = compare_with_location(f, c, &j->current_location);
if (direction == DIRECTION_DOWN)
found = k >= compare_value;
else
found = k <= -compare_value;
} else
found = true;
if (found) {
if (ret)
*ret = c;
if (offset)
return 1;
}
if (r <= 0)
return r;
}
}
Iterator i;
int r;
uint64_t new_offset = 0;
if (!j)
return -EINVAL;
HASHMAP_FOREACH(f, j->files, i) {
Object *o;
uint64_t p;
bool found;
r = next_beyond_location(j, f, direction, &o, &p);
if (r < 0)
return r;
else if (r == 0)
continue;
if (!new_current)
found = true;
else {
int k;
if (direction == DIRECTION_DOWN)
found = k < 0;
else
found = k > 0;
}
if (found) {
new_current = f;
new_entry = o;
new_offset = p;
}
}
if (!new_current)
return 0;
return 1;
}
return real_journal_next(j, DIRECTION_DOWN);
}
return real_journal_next(j, DIRECTION_UP);
}
int c = 0, r;
if (!j)
return -EINVAL;
if (skip == 0) {
/* If this is not a discrete skip, then at least
* resolve the current location */
return real_journal_next(j, direction);
return 0;
}
do {
r = real_journal_next(j, direction);
if (r < 0)
return r;
if (r == 0)
return c;
skip--;
c++;
} while (skip > 0);
return c;
}
}
}
Object *o;
int r;
if (!j)
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);
if (r < 0)
return r;
"s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
return -ENOMEM;
return 1;
}
char *w;
size_t l;
char *state;
bool
seqnum_id_set = false,
seqnum_set = false,
boot_id_set = false,
monotonic_set = false,
realtime_set = false,
xor_hash_set = false;
if (!j)
return -EINVAL;
if (!cursor)
return -EINVAL;
char *item;
int k = 0;
if (l < 2 || w[1] != '=')
return -EINVAL;
if (!item)
return -ENOMEM;
switch (w[0]) {
case 's':
seqnum_id_set = true;
break;
case 'i':
seqnum_set = true;
k = -EINVAL;
break;
case 'b':
boot_id_set = true;
break;
case 'm':
monotonic_set = true;
k = -EINVAL;
break;
case 't':
realtime_set = true;
k = -EINVAL;
break;
case 'x':
xor_hash_set = true;
k = -EINVAL;
break;
}
if (k < 0)
return k;
}
if ((!seqnum_set || !seqnum_id_set) &&
(!monotonic_set || !boot_id_set) &&
return -EINVAL;
reset_location(j);
if (realtime_set) {
j->current_location.realtime_set = true;
}
if (seqnum_set && seqnum_id_set) {
j->current_location.seqnum_set = true;
}
if (monotonic_set && boot_id_set) {
j->current_location.monotonic_set = true;
}
if (xor_hash_set) {
j->current_location.xor_hash_set = true;
}
return 0;
}
if (!j)
return -EINVAL;
reset_location(j);
j->current_location.monotonic_set = true;
return 0;
}
if (!j)
return -EINVAL;
reset_location(j);
j->current_location.realtime_set = true;
return 0;
}
if (!j)
return -EINVAL;
reset_location(j);
return 0;
}
if (!j)
return -EINVAL;
reset_location(j);
return 0;
}
char *fn;
int r;
JournalFile *f;
assert(j);
if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
return 0;
if (dir)
else
if (!fn)
return -ENOMEM;
return 0;
}
return 0;
}
if (r < 0) {
return 0;
return r;
}
/* journal_file_dump(f); */
if (r < 0) {
return r;
}
return 0;
}
char *fn;
JournalFile *f;
assert(j);
if (dir)
else
if (!fn)
return -ENOMEM;
if (!f)
return 0;
return 0;
}
char *fn;
int r;
DIR *d;
int wd;
assert(j);
if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
sd_id128_get_machine(&mid) < 0 ||
return 0;
if (!fn)
return -ENOMEM;
if (!d) {
return 0;
return -errno;
}
if (wd > 0) {
else
}
for (;;) {
if (r != 0 || !de)
break;
continue;
if (r < 0)
}
closedir(d);
return 0;
}
char *p;
assert(j);
if (j->inotify_fd >= 0)
if (p) {
log_debug("Directory %s got removed.", p);
free(p);
}
}
static void add_root_wd(sd_journal *j, const char *p) {
int wd;
char *k;
assert(j);
assert(p);
if (wd <= 0)
return;
k = strdup(p);
free(k);
}
}
char *p;
assert(j);
if (j->inotify_fd >= 0)
if (p) {
log_debug("Root %s got removed.", p);
free(p);
}
}
sd_journal *j;
const char *p;
const char search_paths[] =
int r;
if (!ret)
return -EINVAL;
if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
return -EINVAL;
if (!j)
return -ENOMEM;
if (j->inotify_fd < 0) {
r = -errno;
goto fail;
}
if (!j->files) {
r = -ENOMEM;
goto fail;
}
if (!j->inotify_wd_dirs || !j->inotify_wd_roots) {
r = -ENOMEM;
goto fail;
}
/* We ignore most errors here, since the idea is to only open
* what's actually accessible, and ignore the rest. */
NULSTR_FOREACH(p, search_paths) {
DIR *d;
if ((flags & SD_JOURNAL_RUNTIME_ONLY) &&
!path_startswith(p, "/run"))
continue;
d = opendir(p);
if (!d) {
log_debug("Failed to open %s: %m", p);
continue;
}
add_root_wd(j, p);
for (;;) {
if (r != 0 || !de)
break;
if (r < 0)
if (r < 0)
}
}
closedir(d);
}
*ret = j;
return 0;
fail:
sd_journal_close(j);
return r;
};
if (!j)
return;
if (j->inotify_wd_dirs) {
void *k;
while ((k = hashmap_first_key(j->inotify_wd_dirs)))
remove_directory_wd(j, PTR_TO_INT(k));
}
if (j->inotify_wd_roots) {
void *k;
while ((k = hashmap_first_key(j->inotify_wd_roots)))
remove_root_wd(j, PTR_TO_INT(k));
}
if (j->files) {
JournalFile *f;
while ((f = hashmap_steal_first(j->files)))
hashmap_free(j->files);
}
if (j->inotify_fd >= 0)
free(j);
}
Object *o;
JournalFile *f;
int r;
if (!j)
return -EINVAL;
if (!ret)
return -EINVAL;
f = j->current_file;
if (!f)
return -EADDRNOTAVAIL;
if (f->current_offset <= 0)
return -EADDRNOTAVAIL;
if (r < 0)
return r;
return 0;
}
Object *o;
JournalFile *f;
int r;
if (!j)
return -EINVAL;
if (!ret)
return -EINVAL;
f = j->current_file;
if (!f)
return -EADDRNOTAVAIL;
if (f->current_offset <= 0)
return -EADDRNOTAVAIL;
if (r < 0)
return r;
if (ret_boot_id)
else {
r = sd_id128_get_boot(&id);
if (r < 0)
return r;
return -ESTALE;
}
return 0;
}
_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
JournalFile *f;
uint64_t i, n;
int r;
Object *o;
if (!j)
return -EINVAL;
if (!field)
return -EINVAL;
if (!data)
return -EINVAL;
if (!size)
return -EINVAL;
return -EINVAL;
f = j->current_file;
if (!f)
return -EADDRNOTAVAIL;
if (f->current_offset <= 0)
return -EADDRNOTAVAIL;
if (r < 0)
return r;
n = journal_file_entry_n_items(o);
for (i = 0; i < n; i++) {
size_t t;
r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
if (r < 0)
return r;
return -EBADMSG;
#ifdef HAVE_XZ
&f->compress_buffer, &f->compress_buffer_size,
return -EBADMSG;
*data = f->compress_buffer;
return 0;
}
#else
return -EPROTONOSUPPORT;
#endif
} else if (l >= field_length+1 &&
t = (size_t) l;
if ((uint64_t) t != l)
return -E2BIG;
*size = t;
return 0;
}
if (r < 0)
return r;
}
return -ENOENT;
}
JournalFile *f;
int r;
Object *o;
size_t t;
if (!j)
return -EINVAL;
if (!data)
return -EINVAL;
if (!size)
return -EINVAL;
f = j->current_file;
if (!f)
return -EADDRNOTAVAIL;
if (f->current_offset <= 0)
return -EADDRNOTAVAIL;
if (r < 0)
return r;
n = journal_file_entry_n_items(o);
if (j->current_field >= n)
return 0;
r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
if (r < 0)
return r;
return -EBADMSG;
t = (size_t) l;
/* We can't read objects larger than 4G on a 32bit machine */
if ((uint64_t) t != l)
return -E2BIG;
#ifdef HAVE_XZ
return -EBADMSG;
*data = f->compress_buffer;
#else
return -EPROTONOSUPPORT;
#endif
} else {
*size = t;
}
j->current_field ++;
return 1;
}
if (!j)
return;
j->current_field = 0;
}
if (!j)
return -EINVAL;
return j->inotify_fd;
}
char *p;
int r;
assert(j);
assert(e);
/* Is this a subdirectory we watch? */
if (p) {
/* Event for a journal file */
if (r < 0)
if (r < 0)
}
} else if (e->len == 0) {
/* Event for the directory itself */
remove_directory_wd(j, e->wd);
}
return;
}
/* Must be the root directory then? */
if (p) {
/* Event for a journal file */
if (r < 0)
if (r < 0)
}
/* Event for subdirectory */
r = add_directory(j, p, e->name);
if (r < 0)
}
}
return;
}
if (e->mask & IN_IGNORED)
return;
log_warning("Unknown inotify event.");
}
if (!j)
return -EINVAL;
for (;;) {
struct inotify_event *e;
ssize_t l;
if (l < 0) {
return 0;
return -errno;
}
e = (struct inotify_event*) buffer;
while (l > 0) {
process_inotify_event(j, e);
l -= step;
}
}
}
/* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
/* if (!j) */
/* return -EINVAL; */
/* if (!field) */
/* return -EINVAL; */
/* return -ENOTSUP; */
/* } */
/* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
/* if (!j) */
/* return -EINVAL; */
/* if (!data) */
/* return -EINVAL; */
/* if (!l) */
/* return -EINVAL; */
/* return -ENOTSUP; */
/* } */
/* _public_ void sd_journal_restart_unique(sd_journal *j) { */
/* if (!j) */
/* return; */
/* } */