sd-journal.c revision f4b4781191e8edfb5690e4447166e3ba7bcb48f5
/*-*- 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 <unistd.h>
#include <fcntl.h>
#include <stddef.h>
#include "sd-journal.h"
#include "journal-def.h"
#include "journal-private.h"
#include "lookup3.h"
#include "list.h"
#include "hashmap.h"
struct sd_journal {
};
void journal_file_close(JournalFile *f) {
assert(f);
if (f->fd >= 0)
close_nointr_nofail(f->fd);
if (f->header)
if (f->hash_table_window)
if (f->bisect_table_window)
if (f->window)
free(f);
}
static int journal_file_init_header(JournalFile *f) {
Header h;
ssize_t k;
int r;
assert(f);
zero(h);
r = sd_id128_randomize(&h.file_id);
if (r < 0)
return r;
if (k < 0)
return -errno;
if (k != sizeof(h))
return -EIO;
return 0;
}
static int journal_file_refresh_header(JournalFile *f) {
int r;
assert(f);
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
static int journal_file_verify_header(JournalFile *f) {
assert(f);
return -EBADMSG;
if (f->header->incompatible_flags != 0)
return -EPROTONOSUPPORT;
if ((uint64_t) f->last_stat.st_size < (le64toh(f->header->arena_offset) + le64toh(f->header->arena_size)))
return -ENODATA;
if (f->writable) {
int r;
r = sd_id128_get_machine(&machine_id);
if (r < 0)
return r;
return -EHOSTDOWN;
if (state == STATE_ONLINE)
else if (state == STATE_ARCHIVED)
return -ESHUTDOWN;
else if (state != STATE_OFFLINE)
}
return 0;
}
assert(f);
return -EINVAL;
/* We assume that this file is not sparse, and we know that
* for sure, since we alway call posix_fallocate()
* ourselves */
old_size =
return 0;
else
available = 0;
return -E2BIG;
}
}
return -E2BIG;
return -errno;
return -errno;
return 0;
}
static int journal_file_map(
JournalFile *f,
void **_window,
void **ret) {
void *window;
assert(f);
if (window == MAP_FAILED)
return -errno;
if (_window)
if (_woffset)
if (_wsize)
return 0;
}
void *p;
int r;
assert(f);
f->window_offset <= offset &&
return 0;
}
if (f->window) {
return -errno;
f->window_size = f->window_offset = 0;
}
if (size < DEFAULT_WINDOW_SIZE) {
/* If the default window size is larger then what was
* asked for extend the mapping a bit in the hope to
* minimize needed remappings later on. We add half
* the window space before and half behind the
* requested mapping */
} else
delta = 0;
r = journal_file_map(f,
& p);
if (r < 0)
return r;
return 0;
}
static bool verify_hash(Object *o) {
uint64_t t;
assert(o);
if (t == OBJECT_DATA) {
}
return true;
}
int r;
void *t;
Object *o;
uint64_t s;
assert(f);
if (r < 0)
return r;
o = (Object*) t;
if (s < sizeof(ObjectHeader))
return -EBADMSG;
if (s > sizeof(ObjectHeader)) {
r = journal_file_move_to(f, offset, s, &t);
if (r < 0)
return r;
o = (Object*) t;
}
if (!verify_hash(o))
return -EBADMSG;
*ret = o;
return 0;
}
uint64_t r;
assert(f);
return r;
}
static int journal_file_append_object(JournalFile *f, uint64_t size, Object **ret, uint64_t *offset) {
int r;
uint64_t p;
void *t;
assert(f);
if (p == 0)
else {
r = journal_file_move_to_object(f, p, &tail);
if (r < 0)
return r;
}
r = journal_file_allocate(f, p, size);
if (r < 0)
return r;
r = journal_file_move_to(f, p, size, &t);
if (r < 0)
return r;
o = (Object*) t;
if (f->header->head_object_offset == 0)
*ret = o;
*offset = p;
return 0;
}
static int journal_file_setup_hash_table(JournalFile *f) {
uint64_t s, p;
Object *o;
int r;
assert(f);
if (r < 0)
return r;
return 0;
}
static int journal_file_setup_bisect_table(JournalFile *f) {
uint64_t s, p;
Object *o;
int r;
assert(f);
if (r < 0)
return r;
return 0;
}
static int journal_file_map_hash_table(JournalFile *f) {
uint64_t s, p;
void *t;
int r;
assert(f);
r = journal_file_map(f,
p, s,
&t);
if (r < 0)
return r;
f->hash_table = t;
return 0;
}
static int journal_file_map_bisect_table(JournalFile *f) {
uint64_t s, p;
void *t;
int r;
assert(f);
r = journal_file_map(f,
p, s,
&t);
if (r < 0)
return r;
f->bisect_table = t;
return 0;
}
static int journal_file_link_data(JournalFile *f, Object *o, uint64_t offset, uint64_t hash_index) {
uint64_t p;
int r;
assert(f);
assert(o);
o->data.next_hash_offset = 0;
if (p == 0) {
/* Only entry in the hash table is easy */
o->data.prev_hash_offset = 0;
} else {
/* Temporarily move back to the previous data object,
* to patch in pointer */
r = journal_file_move_to_object(f, p, &o);
if (r < 0)
return r;
r = journal_file_move_to_object(f, offset, &o);
if (r < 0)
return r;
}
return 0;
}
static int journal_file_append_data(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) {
Object *o;
int r;
assert(f);
while (p != 0) {
/* Look for this data object in the hash table */
r = journal_file_move_to_object(f, p, &o);
if (r < 0)
return r;
return -EBADMSG;
return -EBADMSG;
if (ret)
*ret = o;
if (offset)
*offset = p;
return 0;
}
}
if (r < 0)
return r;
r = journal_file_link_data(f, o, np, h);
if (r < 0)
return r;
if (ret)
*ret = o;
if (offset)
return 0;
}
assert(o);
}
uint64_t p, q;
int r;
assert(f);
assert(o);
if (p == 0)
return -EINVAL;
/* Move to the data object */
r = journal_file_move_to_object(f, p, &o);
if (r < 0)
return r;
return -EBADMSG;
if (q == 0)
else {
uint64_t n, j;
/* Move to previous entry */
r = journal_file_move_to_object(f, q, &o);
if (r < 0)
return r;
return -EBADMSG;
n = journal_file_entry_n_items(o);
for (j = 0; j < n; j++)
break;
if (j >= n)
return -EBADMSG;
}
/* Move back to original entry */
r = journal_file_move_to_object(f, offset, &o);
if (r < 0)
return r;
return 0;
}
uint64_t p, i, n, k, a, b;
int r;
assert(f);
assert(o);
/* Link up the entry itself */
o->entry.next_entry_offset = 0;
if (p == 0)
else {
/* Temporarily move back to the previous entry, to
* patch in pointer */
r = journal_file_move_to_object(f, p, &o);
if (r < 0)
return r;
r = journal_file_move_to_object(f, offset, &o);
if (r < 0)
return r;
}
/* Link up the items */
n = journal_file_entry_n_items(o);
for (i = 0; i < n; i++) {
r = journal_file_link_entry_item(f, o, offset, i);
if (r < 0)
return r;
}
/* Link up the entry in the bisect table */
b = offset / k;
for (; a <= b; a++)
return 0;
}
static int journal_file_append_entry_internal(
JournalFile *f,
const dual_timestamp *ts,
Object *o;
int r;
assert(f);
if (r < 0)
return r;
r = journal_file_link_entry(f, o, np);
if (r < 0)
return r;
if (ret)
*ret = o;
if (offset)
return 0;
}
int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, Object **ret, uint64_t *offset) {
unsigned i;
int r;
assert(f);
if (!items)
return -ENOMEM;
for (i = 0; i < n_iovec; i++) {
uint64_t p;
Object *o;
if (r < 0)
goto finish;
}
return r;
}
Object *o;
int r;
assert(f);
lower = 0;
p = le64toh(f->bisect_table[k]);
if (p == 0) {
upper = k;
continue;
}
r = journal_file_move_to_object(f, p, &o);
if (r < 0)
return r;
return -EBADMSG;
if (ret)
*ret = o;
if (offset)
*offset = p;
return 1;
upper = k;
lower = k+1;
}
if (lower <= 0)
return 0;
/* The object we are looking for is between
* bisect_table[lower-1] and bisect_table[lower] */
for (;;) {
r = journal_file_move_to_object(f, p, &o);
if (r < 0)
return r;
if (ret)
*ret = o;
if (offset)
*offset = p;
return 1;
return 0;
if (o->entry.next_entry_offset == 0)
return 0;
}
return 0;
}
int r;
assert(f);
if (!o)
else {
return -EINVAL;
}
if (np == 0)
return 0;
r = journal_file_move_to_object(f, np, &o);
if (r < 0)
return r;
return -EBADMSG;
if (ret)
*ret = o;
if (offset)
return 1;
}
int r;
assert(f);
if (!o)
else {
return -EINVAL;
}
if (np == 0)
return 0;
r = journal_file_move_to_object(f, np, &o);
if (r < 0)
return r;
return -EBADMSG;
if (ret)
*ret = o;
if (offset)
return 1;
}
int journal_file_find_first_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) {
int r;
assert(f);
while (p != 0) {
Object *o;
r = journal_file_move_to_object(f, p, &o);
if (r < 0)
return r;
return -EBADMSG;
return -EBADMSG;
if (o->data.head_entry_offset == 0)
return 0;
r = journal_file_move_to_object(f, p, &o);
if (r < 0)
return r;
return -EBADMSG;
if (ret)
*ret = o;
if (offset)
*offset = p;
return 1;
}
}
return 0;
}
int journal_file_find_last_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) {
int r;
assert(f);
while (p != 0) {
Object *o;
r = journal_file_move_to_object(f, p, &o);
if (r < 0)
return r;
return -EBADMSG;
return -EBADMSG;
if (o->data.tail_entry_offset == 0)
return 0;
r = journal_file_move_to_object(f, p, &o);
if (r < 0)
return r;
return -EBADMSG;
if (ret)
*ret = o;
if (offset)
*offset = p;
return 1;
}
}
return 0;
}
void journal_file_dump(JournalFile *f) {
char a[33], b[33], c[33];
Object *o;
int r;
uint64_t p;
assert(f);
printf("File ID: %s\n"
"Machine ID: %s\n"
"Boot ID: %s\n"
"Arena size: %llu\n",
while (p != 0) {
r = journal_file_move_to_object(f, p, &o);
if (r < 0)
goto fail;
case OBJECT_UNUSED:
printf("Type: OBJECT_UNUSED\n");
break;
case OBJECT_DATA:
printf("Type: OBJECT_DATA\n");
break;
case OBJECT_ENTRY:
break;
case OBJECT_HASH_TABLE:
printf("Type: OBJECT_HASH_TABLE\n");
break;
case OBJECT_BISECT_TABLE:
printf("Type: OBJECT_BISECT_TABLE\n");
break;
}
p = 0;
else
}
return;
fail:
log_error("File corrupt");
}
int journal_file_open(
const char *fname,
int flags,
JournalFile **ret) {
JournalFile *f;
int r;
bool newly_created = false;
return -EINVAL;
if (!f)
return -ENOMEM;
if (f->fd < 0) {
r = -errno;
goto fail;
}
if (!f->path) {
r = -ENOMEM;
goto fail;
}
r = -errno;
goto fail;
}
newly_created = true;
r = journal_file_init_header(f);
if (r < 0)
goto fail;
r = -errno;
goto fail;
}
}
r = -EIO;
goto fail;
}
if (f->header == MAP_FAILED) {
r = -errno;
goto fail;
}
if (!newly_created) {
r = journal_file_verify_header(f);
if (r < 0)
goto fail;
}
if (f->writable) {
r = journal_file_refresh_header(f);
if (r < 0)
goto fail;
}
if (newly_created) {
r = journal_file_setup_hash_table(f);
if (r < 0)
goto fail;
r = journal_file_setup_bisect_table(f);
if (r < 0)
goto fail;
}
r = journal_file_map_hash_table(f);
if (r < 0)
goto fail;
r = journal_file_map_bisect_table(f);
if (r < 0)
goto fail;
if (ret)
*ret = f;
return 0;
fail:
return r;
}
sd_journal *j;
char *fn;
const char *p;
int r = 0;
const char search_paths[] =
if (!j)
return -ENOMEM;
if (!j->files)
goto fail;
NULSTR_FOREACH(p, search_paths) {
DIR *d;
d = opendir(p);
if (!d) {
r = -errno;
continue;
}
for (;;) {
int k;
JournalFile *f;
if (k != 0) {
if (r == 0)
r = -k;
break;
}
if (!de)
break;
continue;
if (!fn) {
r = -ENOMEM;
closedir(d);
goto fail;
}
if (k < 0) {
if (r == 0)
r = -k;
} else {
if (k < 0) {
closedir(d);
r = k;
goto fail;
}
}
}
}
*ret = j;
return 0;
fail:
sd_journal_close(j);
return r;
};
void sd_journal_close(sd_journal *j) {
assert(j);
if (j->files) {
JournalFile *f;
while ((f = hashmap_steal_first(j->files)))
hashmap_free(j->files);
}
free(j);
}