sd-journal.c revision 5e6870eab5aa00675e4cd2f914fd1c25642fc964
/*-*- 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 Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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 "path-util.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);
}
static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o, uint64_t offset) {
assert(j);
assert(f);
assert(o);
j->current_file = f;
j->current_field = 0;
f->current_offset = offset;
}
const char *b, *p;
if (size < 2)
return false;
return false;
b = data;
for (p = b; p < b + size; p++) {
if (*p == '=')
return p > b;
if (*p == '_')
continue;
if (*p >= 'A' && *p <= 'Z')
continue;
if (*p >= '0' && *p <= '9')
continue;
return false;
}
return false;
}
size_t j;
for (j = 0; j < s && j < t; j++) {
if (a[j] != b[j])
return false;
if (a[j] == '=')
return true;
}
return true;
}
Match *m;
if (!m)
return NULL;
m->type = t;
if (p) {
m->parent = p;
}
return m;
}
static void match_free(Match *m) {
assert(m);
while (m->matches)
match_free(m->matches);
if (m->parent)
free(m);
}
static void match_free_if_empty(Match *m) {
assert(m);
if (m->matches)
return;
match_free(m);
}
if (!j)
return -EINVAL;
if (!data)
return -EINVAL;
if (size == 0)
return -EINVAL;
/* level 0: OR term
* level 1: AND terms
* level 2: OR terms
* level 3: concrete matches */
if (!j->level0) {
if (!j->level0)
return -ENOMEM;
}
if (!j->level1) {
if (!j->level1)
return -ENOMEM;
}
/* Exactly the same match already? Then ignore
* this addition */
return 0;
/* Same field? Then let's add this to this OR term */
break;
}
}
if (add_here)
break;
}
if (!add_here) {
if (!add_here)
goto fail;
}
if (!m)
goto fail;
if (!m->data)
goto fail;
detach_location(j);
return 0;
fail:
if (add_here)
if (j->level1)
if (j->level0)
return -ENOMEM;
}
Match *m;
assert(j);
if (!j->level0)
return 0;
if (!j->level1)
return 0;
return 0;
if (!m)
return -ENOMEM;
j->level1 = m;
return 0;
}
static char *match_make_string(Match *m) {
char *p, *r;
Match *i;
bool enclose = false;
if (!m)
return strdup("");
if (m->type == MATCH_DISCRETE)
p = NULL;
char *t, *k;
t = match_make_string(i);
if (!t) {
free(p);
return NULL;
}
if (p) {
free(p);
free(t);
if (!k)
return NULL;
p = k;
enclose = true;
} else {
free(p);
p = t;
}
}
if (enclose) {
free(p);
return r;
}
return p;
}
char *journal_make_match_string(sd_journal *j) {
assert(j);
return match_make_string(j->level0);
}
if (!j)
return;
if (j->level0)
match_free(j->level0);
detach_location(j);
}
uint64_t a, b;
int r;
/* The mmap cache might invalidate the object from the first
* file if we look at the one from the second file. Hence
* temporarily copy the header of the first one, and look at
* that only. */
if (r < 0)
/* 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 next_for_match(
sd_journal *j,
Match *m,
JournalFile *f,
int r;
Object *n;
assert(j);
assert(m);
assert(f);
if (m->type == MATCH_DISCRETE) {
if (r <= 0)
return r;
} else if (m->type == MATCH_OR_TERM) {
Match *i;
/* Find the earliest match beyond after_offset */
if (r < 0)
return r;
else if (r > 0) {
}
}
} else if (m->type == MATCH_AND_TERM) {
Match *i;
bool continue_looking;
/* Always jump to the next matching entry and repeat
* this until we fine and offset that matches for all
* matches. */
if (!m->matches)
return 0;
np = 0;
do {
continue_looking = false;
if (np == 0)
else if (direction == DIRECTION_DOWN)
else
if (r <= 0)
return r;
continue_looking = true;
}
}
} while (continue_looking);
}
if (np == 0)
return 0;
if (r < 0)
return r;
if (ret)
*ret = n;
if (offset)
return 1;
}
static int find_location_for_match(
sd_journal *j,
Match *m,
JournalFile *f,
int r;
assert(j);
assert(m);
assert(f);
if (m->type == MATCH_DISCRETE) {
if (r <= 0)
return r;
/* FIXME: missing: find by monotonic */
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);
if (j->current_location.monotonic_set) {
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 r;
}
if (j->current_location.realtime_set)
return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
} else if (m->type == MATCH_OR_TERM) {
Object *n;
Match *i;
/* Find the earliest match */
if (r < 0)
return r;
else if (r > 0) {
}
}
if (np == 0)
return 0;
if (r < 0)
return r;
if (ret)
*ret = n;
if (offset)
return 1;
} else {
Match *i;
/* First jump to the last match, and then find the
* next one where all matches match */
if (!m->matches)
return 0;
if (r <= 0)
return r;
}
}
}
static int find_location_with_matches(
sd_journal *j,
JournalFile *f,
int r;
assert(j);
assert(f);
if (!j->level0) {
/* No matches is simple */
if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
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, ret, offset);
if (r != -ENOENT)
return r;
}
if (j->current_location.realtime_set)
return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
} else
}
static int next_with_matches(
sd_journal *j,
JournalFile *f,
Object *c;
assert(j);
assert(f);
c = *ret;
/* No matches is easy. We simple advance the file
* pointer by one. */
if (!j->level0)
/* If we have a match then we look for the next matching entry
* with an offset at least one step larger */
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;
int r;
assert(j);
assert(f);
if (f->current_offset > 0) {
cp = f->current_offset;
if (r < 0)
return r;
if (r <= 0)
return r;
} else {
if (r <= 0)
return r;
}
/* OK, we found the spot, now let's advance until to an entry
* that is actually different from what we were previously
* looking at. This is necessary to handle entries which exist
* in two (or more) journal files, and which shall all be
* suppressed but one. */
for (;;) {
bool found;
int k;
k = compare_with_location(f, c, &j->current_location);
if (direction == DIRECTION_DOWN)
found = k > 0;
else
found = k < 0;
} else
found = true;
if (found) {
if (ret)
*ret = c;
if (offset)
return 1;
}
if (r <= 0)
return r;
}
}
uint64_t new_offset = 0;
Object *o;
uint64_t p;
Iterator i;
int r;
if (!j)
return -EINVAL;
HASHMAP_FOREACH(f, j->files, i) {
bool found;
r = next_beyond_location(j, f, direction, &o, &p);
if (r < 0) {
continue;
} else if (r == 0)
continue;
if (!new_file)
found = true;
else {
int k;
if (direction == DIRECTION_DOWN)
found = k < 0;
else
found = k > 0;
}
if (found) {
new_file = f;
new_offset = p;
}
}
if (!new_file)
return 0;
if (r < 0)
return r;
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",
return -ENOMEM;
return 1;
}
char *w, *state;
size_t l;
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;
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;
}
int r;
char *w, *state;
size_t l;
Object *o;
if (!j)
return -EINVAL;
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;
unsigned long long ll;
int k = 0;
if (l < 2 || w[1] != '=')
return -EINVAL;
if (!item)
return -ENOMEM;
switch (w[0]) {
case 's':
if (k < 0)
return k;
return 0;
break;
case 'i':
return -EINVAL;
return 0;
break;
case 'b':
if (k < 0)
return k;
return 0;
break;
case 'm':
return -EINVAL;
return 0;
break;
case 't':
return -EINVAL;
return 0;
break;
case 'x':
return -EINVAL;
return 0;
break;
}
}
return 1;
}
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 *path;
int r;
JournalFile *f;
assert(j);
if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
return 0;
if (!path)
return -ENOMEM;
return 0;
}
return 0;
}
if (r < 0) {
return 0;
return r;
}
/* journal_file_dump(f); */
if (r < 0) {
return r;
}
j->current_invalidate_counter ++;
return 0;
}
char *path;
JournalFile *f;
assert(j);
if (!path)
return -ENOMEM;
if (!f)
return 0;
j->current_invalidate_counter ++;
return 0;
}
char *path;
int r;
DIR *d;
Directory *m;
assert(j);
if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
sd_id128_get_machine(&mid) < 0 ||
return 0;
if (!path)
return -ENOMEM;
if (!d) {
return 0;
return -errno;
}
if (!m) {
if (!m) {
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);
return 0;
} else
if (m->wd <= 0 && j->inotify_fd >= 0) {
}
for (;;) {
union dirent_storage buf;
if (r != 0 || !de)
break;
if (r < 0)
}
}
closedir(d);
return 0;
}
static int add_root_directory(sd_journal *j, const char *p) {
DIR *d;
Directory *m;
int r;
assert(j);
assert(p);
if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
!path_startswith(p, "/run"))
return -EINVAL;
d = opendir(p);
if (!d)
return -errno;
m = hashmap_get(j->directories_by_path, p);
if (!m) {
if (!m) {
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);
return 0;
}
if (m->wd <= 0 && j->inotify_fd >= 0) {
}
for (;;) {
union dirent_storage buf;
if (r != 0 || !de)
break;
if (r < 0)
if (r < 0)
}
}
closedir(d);
return 0;
}
assert(j);
if (d->wd > 0) {
if (j->inotify_fd >= 0)
}
if (d->is_root)
else
free(d);
return 0;
}
static int add_search_paths(sd_journal *j) {
const char search_paths[] =
const char *p;
assert(j);
/* We ignore most errors here, since the idea is to only open
* what's actually accessible, and ignore the rest. */
add_root_directory(j, p);
return 0;
}
static int allocate_inotify(sd_journal *j) {
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;
}
return 0;
}
sd_journal *j;
if (!j)
return NULL;
j->inotify_fd = -1;
if (path) {
if (!j->path) {
free(j);
return NULL;
}
}
if (!j->files) {
free(j);
return NULL;
}
if (!j->directories_by_path) {
hashmap_free(j->files);
free(j);
return NULL;
}
j->mmap = mmap_cache_new();
if (!j->mmap) {
hashmap_free(j->files);
free(j);
return NULL;
}
return j;
}
sd_journal *j;
int r;
if (!ret)
return -EINVAL;
if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
return -EINVAL;
if (!j)
return -ENOMEM;
r = add_search_paths(j);
if (r < 0)
goto fail;
*ret = j;
return 0;
fail:
sd_journal_close(j);
return r;
}
sd_journal *j;
int r;
if (!ret)
return -EINVAL;
return -EINVAL;
if (flags != 0)
return -EINVAL;
if (!j)
return -ENOMEM;
r = add_root_directory(j, path);
if (r < 0)
goto fail;
*ret = j;
return 0;
fail:
sd_journal_close(j);
return r;
}
Directory *d;
JournalFile *f;
if (!j)
return;
while ((f = hashmap_steal_first(j->files)))
hashmap_free(j->files);
while ((d = hashmap_first(j->directories_by_path)))
remove_directory(j, d);
while ((d = hashmap_first(j->directories_by_wd)))
remove_directory(j, d);
if (j->inotify_fd >= 0)
if (j->mmap)
mmap_cache_unref(j->mmap);
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;
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;
}
if (ret)
return 0;
}
static bool field_is_valid(const char *field) {
const char *p;
return false;
return false;
for (p = field; *p; p++) {
if (*p == '_')
continue;
if (*p >= 'A' && *p <= 'Z')
continue;
if (*p >= '0' && *p <= '9')
continue;
return false;
}
return true;
}
_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;
if (!field_is_valid(field))
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++) {
uint64_t p, l;
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;
uint64_t p, l, n;
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;
}
int r;
if (!j)
return -EINVAL;
if (j->inotify_fd >= 0)
return j->inotify_fd;
r = allocate_inotify(j);
if (r < 0)
return r;
/* Iterate through all dirs again, to add them to the
* inotify */
if (j->path)
r = add_root_directory(j, j->path);
else
r = add_search_paths(j);
if (r < 0)
return r;
return j->inotify_fd;
}
Directory *d;
int r;
assert(j);
assert(e);
/* Is this a subdirectory we watch? */
if (d) {
/* Event for a journal file */
if (r < 0)
if (r < 0)
}
/* Event for a subdirectory */
r = remove_directory(j, d);
if (r < 0)
}
} else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
/* Event for root directory */
if (r < 0)
}
}
return;
}
if (e->mask & IN_IGNORED)
return;
log_warning("Unknown inotify event.");
}
static int determine_change(sd_journal *j) {
bool b;
assert(j);
b = j->current_invalidate_counter != j->last_invalidate_counter;
return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
}
bool got_something = false;
if (!j)
return -EINVAL;
for (;;) {
struct inotify_event *e;
ssize_t l;
if (l < 0) {
return -errno;
}
got_something = true;
e = (struct inotify_event*) buffer;
while (l > 0) {
process_inotify_event(j, e);
l -= step;
}
}
return determine_change(j);
}
int r;
assert(j);
if (j->inotify_fd < 0) {
/* This is the first invocation, hence create the
* inotify watch */
r = sd_journal_get_fd(j);
if (r < 0)
return r;
/* The journal might have changed since the context
* object was created and we weren't watching before,
* hence don't wait for anything, and return
* immediately. */
return determine_change(j);
}
do {
} while (r == -EINTR);
if (r < 0)
return r;
return sd_journal_process(j);
}
Iterator i;
JournalFile *f;
bool first = true;
int r;
if (!j)
return -EINVAL;
return -EINVAL;
HASHMAP_FOREACH(f, j->files, i) {
r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
if (r == 0)
continue;
if (first) {
if (from)
if (to)
*to = t;
first = false;
} else {
if (from)
if (to)
}
}
return first ? 0 : 1;
}
_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;
int r;
if (!j)
return -EINVAL;
return -EINVAL;
HASHMAP_FOREACH(f, j->files, i) {
if (r == -ENOENT)
continue;
if (r < 0)
return r;
if (r == 0)
continue;
if (first) {
if (from)
if (to)
*to = t;
first = false;
} else {
if (from)
if (to)
}
}
return first ? 0 : 1;
}
void journal_print_header(sd_journal *j) {
Iterator i;
JournalFile *f;
bool newline = false;
assert(j);
HASHMAP_FOREACH(f, j->files, i) {
if (newline)
putchar('\n');
else
newline = true;
}
}
Iterator i;
JournalFile *f;
if (!j)
return -EINVAL;
if (!bytes)
return -EINVAL;
HASHMAP_FOREACH(f, j->files, i) {
return -errno;
}
return 0;
}
/* _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; */
/* } */