public_programs += executable(
'systemd-journal-remote',
systemd_journal_remote_sources,
- include_directories : includes,
+ include_directories : journal_includes,
link_with : [libshared,
libsystemd_journal_remote],
dependencies : [threads,
public_programs += executable(
'systemd-journal-gatewayd',
systemd_journal_gatewayd_sources,
- include_directories : includes,
+ include_directories : journal_includes,
link_with : [libshared],
dependencies : [threads,
libmicrohttpd,
#include "alloc-util.h"
#include "journal-remote.h"
-static int do_rotate(JournalFile **f, bool compress, bool seal) {
- int r = journal_file_rotate(f, compress, UINT64_MAX, seal, NULL);
+static int do_rotate(JournaldFile **f, bool compress, bool seal) {
+ int r = journald_file_rotate(f, compress, UINT64_MAX, seal, NULL);
if (r < 0) {
if (*f)
- log_error_errno(r, "Failed to rotate %s: %m", (*f)->path);
+ log_error_errno(r, "Failed to rotate %s: %m", (*f)->file->path);
else
log_error_errno(r, "Failed to create rotated journal: %m");
}
return NULL;
if (w->journal) {
- log_debug("Closing journal file %s.", w->journal->path);
- journal_file_close(w->journal);
+ log_debug("Closing journal file %s.", w->journal->file->path);
+ journald_file_close(w->journal);
}
if (w->server && w->hashmap_key)
assert(iovw);
assert(iovw->count > 0);
- if (journal_file_rotate_suggested(w->journal, 0, LOG_DEBUG)) {
+ if (journal_file_rotate_suggested(w->journal->file, 0, LOG_DEBUG)) {
log_info("%s: Journal header limits reached or header out-of-date, rotating",
- w->journal->path);
+ w->journal->file->path);
r = do_rotate(&w->journal, compress, seal);
if (r < 0)
return r;
}
- r = journal_file_append_entry(w->journal, ts, boot_id,
+ r = journal_file_append_entry(w->journal->file, ts, boot_id,
iovw->iovec, iovw->count,
&w->seqnum, NULL, NULL);
if (r >= 0) {
} else if (r == -EBADMSG)
return r;
- log_debug_errno(r, "%s: Write failed, rotating: %m", w->journal->path);
+ log_debug_errno(r, "%s: Write failed, rotating: %m", w->journal->file->path);
r = do_rotate(&w->journal, compress, seal);
if (r < 0)
return r;
else
- log_debug("%s: Successfully rotated journal", w->journal->path);
+ log_debug("%s: Successfully rotated journal", w->journal->file->path);
log_debug("Retrying write.");
- r = journal_file_append_entry(w->journal, ts, boot_id,
+ r = journal_file_append_entry(w->journal->file, ts, boot_id,
iovw->iovec, iovw->count,
&w->seqnum, NULL, NULL);
if (r < 0)
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include "journal-file.h"
+#include "journald-file.h"
#include "journal-importer.h"
typedef struct RemoteServer RemoteServer;
typedef struct Writer {
- JournalFile *journal;
+ JournaldFile *journal;
JournalMetrics metrics;
MMapCache *mmap;
#include "errno-util.h"
#include "escape.h"
#include "fd-util.h"
-#include "journal-file.h"
+#include "journald-file.h"
#include "journal-remote-write.h"
#include "journal-remote.h"
#include "journald-native.h"
assert_not_reached();
}
- r = journal_file_open_reliably(filename,
- O_RDWR|O_CREAT, 0640,
- s->compress, UINT64_MAX, s->seal,
- &w->metrics,
- w->mmap, NULL,
- NULL, &w->journal);
+ r = journald_file_open_reliably(filename,
+ O_RDWR|O_CREAT, 0640,
+ s->compress, UINT64_MAX, s->seal,
+ &w->metrics,
+ w->mmap, NULL,
+ NULL, &w->journal);
if (r < 0)
return log_error_errno(r, "Failed to open output journal %s: %m", filename);
- log_debug("Opened output file %s", w->journal->path);
+ log_debug("Opened output file %s", w->journal->file->path);
return 0;
}
'systemd-journal-remote',
libsystemd_journal_remote_sources,
include_directories : journal_includes,
+ link_with : libjournal_core,
dependencies : [threads,
libmicrohttpd,
libgnutls,
fuzzers += [
[['src/journal-remote/fuzz-journal-remote.c'],
[libsystemd_journal_remote,
- libshared]],
+ libshared],
+ [],
+ [journal_includes]],
]
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "chattr-util.h"
+#include "fd-util.h"
+#include "format-util.h"
+#include "journal-authenticate.h"
+#include "journald-file.h"
+#include "path-util.h"
+#include "random-util.h"
+#include "set.h"
+#include "sync-util.h"
+
+JournaldFile* journald_file_close(JournaldFile *f) {
+ if (!f)
+ return NULL;
+
+ journal_file_close(f->file);
+
+ return mfree(f);
+}
+
+int journald_file_open(
+ int fd,
+ const char *fname,
+ int flags,
+ mode_t mode,
+ bool compress,
+ uint64_t compress_threshold_bytes,
+ bool seal,
+ JournalMetrics *metrics,
+ MMapCache *mmap_cache,
+ Set *deferred_closes,
+ JournaldFile *template,
+ JournaldFile **ret) {
+ _cleanup_free_ JournaldFile *f = NULL;
+ int r;
+
+ set_clear_with_destructor(deferred_closes, journald_file_close);
+
+ f = new0(JournaldFile, 1);
+ if (!f)
+ return -ENOMEM;
+
+ r = journal_file_open(fd, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics,
+ mmap_cache, template ? template->file : NULL, &f->file);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(f);
+
+ return 0;
+}
+
+
+JournaldFile* journald_file_initiate_close(JournaldFile *f, Set *deferred_closes) {
+ int r;
+
+ assert(f);
+
+ if (deferred_closes) {
+ r = set_put(deferred_closes, f);
+ if (r < 0)
+ log_debug_errno(r, "Failed to add file to deferred close set, closing immediately.");
+ else {
+ (void) journal_file_set_offline(f->file, false);
+ return NULL;
+ }
+ }
+
+ return journald_file_close(f);
+}
+
+int journald_file_rotate(
+ JournaldFile **f,
+ bool compress,
+ uint64_t compress_threshold_bytes,
+ bool seal,
+ Set *deferred_closes) {
+
+ JournaldFile *new_file = NULL;
+ int r;
+
+ assert(f);
+ assert(*f);
+
+ r = journal_file_archive((*f)->file);
+ if (r < 0)
+ return r;
+
+ r = journald_file_open(
+ -1,
+ (*f)->file->path,
+ (*f)->file->flags,
+ (*f)->file->mode,
+ compress,
+ compress_threshold_bytes,
+ seal,
+ NULL, /* metrics */
+ (*f)->file->mmap,
+ deferred_closes,
+ *f, /* template */
+ &new_file);
+
+ journald_file_initiate_close(*f, deferred_closes);
+ *f = new_file;
+
+ return r;
+}
+
+int journald_file_open_reliably(
+ const char *fname,
+ int flags,
+ mode_t mode,
+ bool compress,
+ uint64_t compress_threshold_bytes,
+ bool seal,
+ JournalMetrics *metrics,
+ MMapCache *mmap_cache,
+ Set *deferred_closes,
+ JournaldFile *template,
+ JournaldFile **ret) {
+
+ int r;
+
+ r = journald_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics,
+ mmap_cache, deferred_closes, template, ret);
+ if (!IN_SET(r,
+ -EBADMSG, /* Corrupted */
+ -ENODATA, /* Truncated */
+ -EHOSTDOWN, /* Other machine */
+ -EPROTONOSUPPORT, /* Incompatible feature */
+ -EBUSY, /* Unclean shutdown */
+ -ESHUTDOWN, /* Already archived */
+ -EIO, /* IO error, including SIGBUS on mmap */
+ -EIDRM, /* File has been deleted */
+ -ETXTBSY)) /* File is from the future */
+ return r;
+
+ if ((flags & O_ACCMODE) == O_RDONLY)
+ return r;
+
+ if (!(flags & O_CREAT))
+ return r;
+
+ if (!endswith(fname, ".journal"))
+ return r;
+
+ /* The file is corrupted. Rotate it away and try it again (but only once) */
+ log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
+
+ r = journal_file_dispose(AT_FDCWD, fname);
+ if (r < 0)
+ return r;
+
+ return journald_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics,
+ mmap_cache, deferred_closes, template, ret);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "journal-file.h"
+
+typedef struct {
+ JournalFile *file;
+} JournaldFile;
+
+int journald_file_open(
+ int fd,
+ const char *fname,
+ int flags,
+ mode_t mode,
+ bool compress,
+ uint64_t compress_threshold_bytes,
+ bool seal,
+ JournalMetrics *metrics,
+ MMapCache *mmap_cache,
+ Set *deferred_closes,
+ JournaldFile *template,
+ JournaldFile **ret);
+
+JournaldFile* journald_file_close(JournaldFile *f);
+DEFINE_TRIVIAL_CLEANUP_FUNC(JournaldFile*, journald_file_close);
+
+int journald_file_open_reliably(
+ const char *fname,
+ int flags,
+ mode_t mode,
+ bool compress,
+ uint64_t compress_threshold_bytes,
+ bool seal,
+ JournalMetrics *metrics,
+ MMapCache *mmap_cache,
+ Set *deferred_closes,
+ JournaldFile *template,
+ JournaldFile **ret);
+
+JournaldFile* journald_file_initiate_close(JournaldFile *f, Set *deferred_closes);
+int journald_file_rotate(JournaldFile **f, bool compress, uint64_t compress_threshold_bytes, bool seal, Set *deferred_closes);
#include "id128-util.h"
#include "io-util.h"
#include "journal-authenticate.h"
-#include "journal-file.h"
+#include "journald-file.h"
#include "journal-internal.h"
#include "journal-vacuum.h"
#include "journald-audit.h"
return uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY;
}
-static void server_add_acls(JournalFile *f, uid_t uid) {
+static void server_add_acls(JournaldFile *f, uid_t uid) {
assert(f);
#if HAVE_ACL
if (uid_for_system_journal(uid))
return;
- r = fd_add_uid_acl_permission(f->fd, uid, ACL_READ);
+ r = fd_add_uid_acl_permission(f->file->fd, uid, ACL_READ);
if (r < 0)
- log_warning_errno(r, "Failed to set ACL on %s, ignoring: %m", f->path);
+ log_warning_errno(r, "Failed to set ACL on %s, ignoring: %m", f->file->path);
#endif
}
int flags,
bool seal,
JournalMetrics *metrics,
- JournalFile **ret) {
+ JournaldFile **ret) {
- _cleanup_(journal_file_closep) JournalFile *f = NULL;
+ _cleanup_(journald_file_closep) JournaldFile *f = NULL;
int r;
assert(s);
assert(ret);
if (reliably)
- r = journal_file_open_reliably(fname, flags, 0640, s->compress.enabled, s->compress.threshold_bytes,
- seal, metrics, s->mmap, s->deferred_closes, NULL, &f);
+ r = journald_file_open_reliably(fname, flags, 0640, s->compress.enabled,
+ s->compress.threshold_bytes, seal, metrics, s->mmap,
+ s->deferred_closes, NULL, &f);
else
- r = journal_file_open(-1, fname, flags, 0640, s->compress.enabled, s->compress.threshold_bytes, seal,
- metrics, s->mmap, s->deferred_closes, NULL, &f);
+ r = journald_file_open(-1, fname, flags, 0640, s->compress.enabled,
+ s->compress.threshold_bytes, seal, metrics, s->mmap,
+ s->deferred_closes, NULL, &f);
if (r < 0)
return r;
- r = journal_file_enable_post_change_timer(f, s->event, POST_CHANGE_TIMER_INTERVAL_USEC);
+ r = journal_file_enable_post_change_timer(f->file, s->event, POST_CHANGE_TIMER_INTERVAL_USEC);
if (r < 0)
return r;
return r;
}
-static JournalFile* find_journal(Server *s, uid_t uid) {
+static JournaldFile* find_journal(Server *s, uid_t uid) {
_cleanup_free_ char *p = NULL;
- JournalFile *f;
+ JournaldFile *f;
int r;
assert(s);
/* Too many open? Then let's close one (or more) */
while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) {
assert_se(f = ordered_hashmap_steal_first(s->user_journals));
- (void) journal_file_close(f);
+ (void) journald_file_close(f);
}
r = open_journal(s, true, p, O_RDWR|O_CREAT, s->seal, &s->system_storage.metrics, &f);
r = ordered_hashmap_put(s->user_journals, UID_TO_PTR(uid), f);
if (r < 0) {
- (void) journal_file_close(f);
+ (void) journald_file_close(f);
return s->system_journal;
}
static int do_rotate(
Server *s,
- JournalFile **f,
+ JournaldFile **f,
const char* name,
bool seal,
uint32_t uid) {
if (!*f)
return -EINVAL;
- r = journal_file_rotate(f, s->compress.enabled, s->compress.threshold_bytes, seal, s->deferred_closes);
+ r = journald_file_rotate(f, s->compress.enabled, s->compress.threshold_bytes, seal, s->deferred_closes);
if (r < 0) {
if (*f)
- return log_error_errno(r, "Failed to rotate %s: %m", (*f)->path);
+ return log_error_errno(r, "Failed to rotate %s: %m", (*f)->file->path);
else
return log_error_errno(r, "Failed to create new %s journal: %m", name);
}
}
static void server_process_deferred_closes(Server *s) {
- JournalFile *f;
+ JournaldFile *f;
/* Perform any deferred closes which aren't still offlining. */
SET_FOREACH(f, s->deferred_closes) {
- if (journal_file_is_offlining(f))
+ if (journal_file_is_offlining(f->file))
continue;
(void) set_remove(s->deferred_closes, f);
- (void) journal_file_close(f);
+ (void) journald_file_close(f);
}
}
/* And now, let's close some more until we reach the limit again. */
while (set_size(s->deferred_closes) >= DEFERRED_CLOSES_MAX) {
- JournalFile *f;
+ JournaldFile *f;
assert_se(f = set_steal_first(s->deferred_closes));
- journal_file_close(f);
+ journald_file_close(f);
}
}
_cleanup_close_ int fd = -1;
const char *a, *b;
struct dirent *de;
- JournalFile *f;
+ JournaldFile *f;
uid_t uid;
errno = 0;
server_vacuum_deferred_closes(s);
/* Open the file briefly, so that we can archive it */
- r = journal_file_open(fd,
- full,
- O_RDWR,
- 0640,
- s->compress.enabled,
- s->compress.threshold_bytes,
- s->seal,
- &s->system_storage.metrics,
- s->mmap,
- s->deferred_closes,
- NULL,
- &f);
+ r = journald_file_open(fd,
+ full,
+ O_RDWR,
+ 0640,
+ s->compress.enabled,
+ s->compress.threshold_bytes,
+ s->seal,
+ &s->system_storage.metrics,
+ s->mmap,
+ s->deferred_closes,
+ NULL,
+ &f);
if (r < 0) {
log_warning_errno(r, "Failed to read journal file %s for rotation, trying to move it out of the way: %m", full);
continue;
}
- TAKE_FD(fd); /* Donated to journal_file_open() */
+ TAKE_FD(fd); /* Donated to journald_file_open() */
- r = journal_file_archive(f);
+ r = journal_file_archive(f->file);
if (r < 0)
log_debug_errno(r, "Failed to archive journal file '%s', ignoring: %m", full);
- f = journal_initiate_close(f, s->deferred_closes);
+ journald_file_initiate_close(f, s->deferred_closes);
+ f = NULL;
}
return 0;
}
void server_rotate(Server *s) {
- JournalFile *f;
+ JournaldFile *f;
void *k;
int r;
}
void server_sync(Server *s) {
- JournalFile *f;
+ JournaldFile *f;
int r;
if (s->system_journal) {
- r = journal_file_set_offline(s->system_journal, false);
+ r = journal_file_set_offline(s->system_journal->file, false);
if (r < 0)
log_warning_errno(r, "Failed to sync system journal, ignoring: %m");
}
ORDERED_HASHMAP_FOREACH(f, s->user_journals) {
- r = journal_file_set_offline(f, false);
+ r = journal_file_set_offline(f->file, false);
if (r < 0)
log_warning_errno(r, "Failed to sync user journal, ignoring: %m");
}
static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, size_t n, int priority) {
bool vacuumed = false, rotate = false;
struct dual_timestamp ts;
- JournalFile *f;
+ JournaldFile *f;
int r;
assert(s);
if (!f)
return;
- if (journal_file_rotate_suggested(f, s->max_file_usec, LOG_INFO)) {
- log_info("%s: Journal header limits reached or header out-of-date, rotating.", f->path);
+ if (journal_file_rotate_suggested(f->file, s->max_file_usec, LOG_INFO)) {
+ log_info("%s: Journal header limits reached or header out-of-date, rotating.", f->file->path);
rotate = true;
}
}
s->last_realtime_clock = ts.realtime;
- r = journal_file_append_entry(f, &ts, NULL, iovec, n, &s->seqnum, NULL, NULL);
+ r = journal_file_append_entry(f->file, &ts, NULL, iovec, n, &s->seqnum, NULL, NULL);
if (r >= 0) {
server_schedule_sync(s, priority);
return;
}
- if (vacuumed || !shall_try_append_again(f, r)) {
+ if (vacuumed || !shall_try_append_again(f->file, r)) {
log_error_errno(r, "Failed to write entry (%zu items, %zu bytes), ignoring: %m", n, IOVEC_TOTAL_SIZE(iovec, n));
return;
}
return;
log_debug("Retrying write.");
- r = journal_file_append_entry(f, &ts, NULL, iovec, n, &s->seqnum, NULL, NULL);
+ r = journal_file_append_entry(f->file, &ts, NULL, iovec, n, &s->seqnum, NULL, NULL);
if (r < 0)
log_error_errno(r, "Failed to write entry (%zu items, %zu bytes) despite vacuuming, ignoring: %m", n, IOVEC_TOTAL_SIZE(iovec, n));
else
goto finish;
}
- r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset);
+ r = journal_file_copy_entry(f, s->system_journal->file, o, f->current_offset);
if (r >= 0)
continue;
- if (!shall_try_append_again(s->system_journal, r)) {
+ if (!shall_try_append_again(s->system_journal->file, r)) {
log_error_errno(r, "Can't write entry: %m");
goto finish;
}
}
log_debug("Retrying write.");
- r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset);
+ r = journal_file_copy_entry(f, s->system_journal->file, o, f->current_offset);
if (r < 0) {
log_error_errno(r, "Can't write entry: %m");
goto finish;
finish:
if (s->system_journal)
- journal_file_post_change(s->system_journal);
+ journal_file_post_change(s->system_journal->file);
- s->runtime_journal = journal_file_close(s->runtime_journal);
+ s->runtime_journal = journald_file_close(s->runtime_journal);
if (r >= 0)
(void) rm_rf(s->runtime_storage.path, REMOVE_ROOT);
(void) system_journal_open(s, false, true);
- s->system_journal = journal_file_close(s->system_journal);
- ordered_hashmap_clear_with_destructor(s->user_journals, journal_file_close);
- set_clear_with_destructor(s->deferred_closes, journal_file_close);
+ s->system_journal = journald_file_close(s->system_journal);
+ ordered_hashmap_clear_with_destructor(s->user_journals, journald_file_close);
+ set_clear_with_destructor(s->deferred_closes, journald_file_close);
fn = strjoina(s->runtime_directory, "/flushed");
if (unlink(fn) < 0 && errno != ENOENT)
void server_maybe_append_tags(Server *s) {
#if HAVE_GCRYPT
- JournalFile *f;
+ JournaldFile *f;
usec_t n;
n = now(CLOCK_REALTIME);
if (s->system_journal)
- journal_file_maybe_append_tag(s->system_journal, n);
+ journal_file_maybe_append_tag(s->system_journal->file, n);
ORDERED_HASHMAP_FOREACH(f, s->user_journals)
- journal_file_maybe_append_tag(f, n);
+ journal_file_maybe_append_tag(f->file, n);
#endif
}
free(s->namespace);
free(s->namespace_field);
- set_free_with_destructor(s->deferred_closes, journal_file_close);
+ set_free_with_destructor(s->deferred_closes, journald_file_close);
while (s->stdout_streams)
stdout_stream_free(s->stdout_streams);
client_context_flush_all(s);
- (void) journal_file_close(s->system_journal);
- (void) journal_file_close(s->runtime_journal);
+ (void) journald_file_close(s->system_journal);
+ (void) journald_file_close(s->runtime_journal);
- ordered_hashmap_free_with_destructor(s->user_journals, journal_file_close);
+ ordered_hashmap_free_with_destructor(s->user_journals, journald_file_close);
varlink_server_unref(s->varlink_server);
#include "conf-parser.h"
#include "hashmap.h"
-#include "journal-file.h"
+#include "journald-file.h"
#include "journald-context.h"
#include "journald-rate-limit.h"
#include "journald-stream.h"
sd_event_source *watchdog_event_source;
sd_event_source *idle_event_source;
- JournalFile *runtime_journal;
- JournalFile *system_journal;
+ JournaldFile *runtime_journal;
+ JournaldFile *system_journal;
OrderedHashmap *user_journals;
uint64_t seqnum;
if (server.system_journal) {
usec_t u;
- if (journal_file_next_evolve_usec(server.system_journal, &u)) {
+ if (journal_file_next_evolve_usec(server.system_journal->file, &u)) {
if (n >= u)
t = 0;
else
journald-console.h
journald-context.c
journald-context.h
+ journald-file.c
+ journald-file.h
journald-kmsg.c
journald-kmsg.h
journald-native.c
[libxz,
liblz4,
libselinux]],
+
+ [['src/journal/test-journal.c'],
+ [libjournal_core,
+ libshared]],
+
+ [['src/journal/test-journal-stream.c'],
+ [libjournal_core,
+ libshared]],
+
+ [['src/journal/test-journal-flush.c'],
+ [libjournal_core,
+ libshared]],
+
+ [['src/journal/test-journal-verify.c'],
+ [libjournal_core,
+ libshared]],
+
+ [['src/journal/test-journal-interleaving.c'],
+ [libjournal_core,
+ libshared]],
]
fuzzers += [
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "sd-journal.h"
+
+#include "alloc-util.h"
+#include "chattr-util.h"
+#include "journald-file.h"
+#include "journal-internal.h"
+#include "macro.h"
+#include "path-util.h"
+#include "string-util.h"
+
+int main(int argc, char *argv[]) {
+ _cleanup_free_ char *fn = NULL;
+ char dn[] = "/var/tmp/test-journal-flush.XXXXXX";
+ JournaldFile *new_journal = NULL;
+ sd_journal *j = NULL;
+ unsigned n = 0;
+ int r;
+
+ assert_se(mkdtemp(dn));
+ (void) chattr_path(dn, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
+
+ fn = path_join(dn, "test.journal");
+
+ r = journald_file_open(-1, fn, O_CREAT|O_RDWR, 0644, false, 0, false, NULL, NULL, NULL, NULL, &new_journal);
+ assert_se(r >= 0);
+
+ if (argc > 1)
+ r = sd_journal_open_files(&j, (const char **) strv_skip(argv, 1), 0);
+ else
+ r = sd_journal_open(&j, 0);
+ assert_se(r == 0);
+
+ sd_journal_set_data_threshold(j, 0);
+
+ SD_JOURNAL_FOREACH(j) {
+ Object *o;
+ JournalFile *f;
+
+ f = j->current_file;
+ assert_se(f && f->current_offset > 0);
+
+ r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
+ if (r < 0)
+ log_error_errno(r, "journal_file_move_to_object failed: %m");
+ assert_se(r >= 0);
+
+ r = journal_file_copy_entry(f, new_journal->file, o, f->current_offset);
+ if (r < 0)
+ log_warning_errno(r, "journal_file_copy_entry failed: %m");
+ assert_se(r >= 0 ||
+ IN_SET(r, -EBADMSG, /* corrupted file */
+ -EPROTONOSUPPORT, /* unsupported compression */
+ -EIO)); /* file rotated */
+
+ if (++n >= 10000)
+ break;
+ }
+
+ sd_journal_close(j);
+
+ (void) journald_file_close(new_journal);
+
+ unlink(fn);
+ assert_se(rmdir(dn) == 0);
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "sd-journal.h"
+
+#include "alloc-util.h"
+#include "chattr-util.h"
+#include "io-util.h"
+#include "journald-file.h"
+#include "journal-vacuum.h"
+#include "log.h"
+#include "parse-util.h"
+#include "rm-rf.h"
+#include "tests.h"
+#include "util.h"
+
+/* This program tests skipping around in a multi-file journal. */
+
+static bool arg_keep = false;
+
+_noreturn_ static void log_assert_errno(const char *text, int error, const char *file, int line, const char *func) {
+ log_internal(LOG_CRIT, error, file, line, func,
+ "'%s' failed at %s:%u (%s): %m", text, file, line, func);
+ abort();
+}
+
+#define assert_ret(expr) \
+ do { \
+ int _r_ = (expr); \
+ if (_unlikely_(_r_ < 0)) \
+ log_assert_errno(#expr, -_r_, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__); \
+ } while (false)
+
+static JournaldFile *test_open(const char *name) {
+ JournaldFile *f;
+ assert_ret(journald_file_open(-1, name, O_RDWR|O_CREAT, 0644, true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &f));
+ return f;
+}
+
+static void test_close(JournaldFile *f) {
+ (void) journald_file_close(f);
+}
+
+static void append_number(JournaldFile *f, int n, uint64_t *seqnum) {
+ char *p;
+ dual_timestamp ts;
+ static dual_timestamp previous_ts = {};
+ struct iovec iovec[1];
+
+ dual_timestamp_get(&ts);
+
+ if (ts.monotonic <= previous_ts.monotonic)
+ ts.monotonic = previous_ts.monotonic + 1;
+
+ if (ts.realtime <= previous_ts.realtime)
+ ts.realtime = previous_ts.realtime + 1;
+
+ previous_ts = ts;
+
+ assert_se(asprintf(&p, "NUMBER=%d", n) >= 0);
+ iovec[0] = IOVEC_MAKE_STRING(p);
+ assert_ret(journal_file_append_entry(f->file, &ts, NULL, iovec, 1, seqnum, NULL, NULL));
+ free(p);
+}
+
+static void test_check_number (sd_journal *j, int n) {
+ const void *d;
+ _cleanup_free_ char *k;
+ size_t l;
+ int x;
+
+ assert_ret(sd_journal_get_data(j, "NUMBER", &d, &l));
+ assert_se(k = strndup(d, l));
+ printf("%s\n", k);
+
+ assert_se(safe_atoi(k + 7, &x) >= 0);
+ assert_se(n == x);
+}
+
+static void test_check_numbers_down (sd_journal *j, int count) {
+ int i;
+
+ for (i = 1; i <= count; i++) {
+ int r;
+ test_check_number(j, i);
+ assert_ret(r = sd_journal_next(j));
+ if (i == count)
+ assert_se(r == 0);
+ else
+ assert_se(r == 1);
+ }
+
+}
+
+static void test_check_numbers_up (sd_journal *j, int count) {
+ for (int i = count; i >= 1; i--) {
+ int r;
+ test_check_number(j, i);
+ assert_ret(r = sd_journal_previous(j));
+ if (i == 1)
+ assert_se(r == 0);
+ else
+ assert_se(r == 1);
+ }
+
+}
+
+static void setup_sequential(void) {
+ JournaldFile *one, *two;
+ one = test_open("one.journal");
+ two = test_open("two.journal");
+ append_number(one, 1, NULL);
+ append_number(one, 2, NULL);
+ append_number(two, 3, NULL);
+ append_number(two, 4, NULL);
+ test_close(one);
+ test_close(two);
+}
+
+static void setup_interleaved(void) {
+ JournaldFile *one, *two;
+ one = test_open("one.journal");
+ two = test_open("two.journal");
+ append_number(one, 1, NULL);
+ append_number(two, 2, NULL);
+ append_number(one, 3, NULL);
+ append_number(two, 4, NULL);
+ test_close(one);
+ test_close(two);
+}
+
+static void mkdtemp_chdir_chattr(char *path) {
+ assert_se(mkdtemp(path));
+ assert_se(chdir(path) >= 0);
+
+ /* Speed up things a bit on btrfs, ensuring that CoW is turned off for all files created in our
+ * directory during the test run */
+ (void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
+}
+
+static void test_skip(void (*setup)(void)) {
+ char t[] = "/var/tmp/journal-skip-XXXXXX";
+ sd_journal *j;
+ int r;
+
+ mkdtemp_chdir_chattr(t);
+
+ setup();
+
+ /* Seek to head, iterate down.
+ */
+ assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_seek_head(j));
+ assert_ret(sd_journal_next(j));
+ test_check_numbers_down(j, 4);
+ sd_journal_close(j);
+
+ /* Seek to tail, iterate up.
+ */
+ assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_seek_tail(j));
+ assert_ret(sd_journal_previous(j));
+ test_check_numbers_up(j, 4);
+ sd_journal_close(j);
+
+ /* Seek to tail, skip to head, iterate down.
+ */
+ assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_seek_tail(j));
+ assert_ret(r = sd_journal_previous_skip(j, 4));
+ assert_se(r == 4);
+ test_check_numbers_down(j, 4);
+ sd_journal_close(j);
+
+ /* Seek to head, skip to tail, iterate up.
+ */
+ assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_seek_head(j));
+ assert_ret(r = sd_journal_next_skip(j, 4));
+ assert_se(r == 4);
+ test_check_numbers_up(j, 4);
+ sd_journal_close(j);
+
+ log_info("Done...");
+
+ if (arg_keep)
+ log_info("Not removing %s", t);
+ else {
+ journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
+
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+ }
+
+ puts("------------------------------------------------------------");
+}
+
+static void test_sequence_numbers(void) {
+
+ char t[] = "/var/tmp/journal-seq-XXXXXX";
+ JournaldFile *one, *two;
+ uint64_t seqnum = 0;
+ sd_id128_t seqnum_id;
+
+ mkdtemp_chdir_chattr(t);
+
+ assert_se(journald_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0644,
+ true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &one) == 0);
+
+ append_number(one, 1, &seqnum);
+ printf("seqnum=%"PRIu64"\n", seqnum);
+ assert_se(seqnum == 1);
+ append_number(one, 2, &seqnum);
+ printf("seqnum=%"PRIu64"\n", seqnum);
+ assert_se(seqnum == 2);
+
+ assert_se(one->file->header->state == STATE_ONLINE);
+ assert_se(!sd_id128_equal(one->file->header->file_id, one->file->header->machine_id));
+ assert_se(!sd_id128_equal(one->file->header->file_id, one->file->header->boot_id));
+ assert_se(sd_id128_equal(one->file->header->file_id, one->file->header->seqnum_id));
+
+ memcpy(&seqnum_id, &one->file->header->seqnum_id, sizeof(sd_id128_t));
+
+ assert_se(journald_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0644,
+ true, UINT64_MAX, false, NULL, NULL, NULL, one, &two) == 0);
+
+ assert_se(two->file->header->state == STATE_ONLINE);
+ assert_se(!sd_id128_equal(two->file->header->file_id, one->file->header->file_id));
+ assert_se(sd_id128_equal(one->file->header->machine_id, one->file->header->machine_id));
+ assert_se(sd_id128_equal(one->file->header->boot_id, one->file->header->boot_id));
+ assert_se(sd_id128_equal(one->file->header->seqnum_id, one->file->header->seqnum_id));
+
+ append_number(two, 3, &seqnum);
+ printf("seqnum=%"PRIu64"\n", seqnum);
+ assert_se(seqnum == 3);
+ append_number(two, 4, &seqnum);
+ printf("seqnum=%"PRIu64"\n", seqnum);
+ assert_se(seqnum == 4);
+
+ test_close(two);
+
+ append_number(one, 5, &seqnum);
+ printf("seqnum=%"PRIu64"\n", seqnum);
+ assert_se(seqnum == 5);
+
+ append_number(one, 6, &seqnum);
+ printf("seqnum=%"PRIu64"\n", seqnum);
+ assert_se(seqnum == 6);
+
+ test_close(one);
+
+ /* restart server */
+ seqnum = 0;
+
+ assert_se(journald_file_open(-1, "two.journal", O_RDWR, 0,
+ true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &two) == 0);
+
+ assert_se(sd_id128_equal(two->file->header->seqnum_id, seqnum_id));
+
+ append_number(two, 7, &seqnum);
+ printf("seqnum=%"PRIu64"\n", seqnum);
+ assert_se(seqnum == 5);
+
+ /* So..., here we have the same seqnum in two files with the
+ * same seqnum_id. */
+
+ test_close(two);
+
+ log_info("Done...");
+
+ if (arg_keep)
+ log_info("Not removing %s", t);
+ else {
+ journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
+
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+ }
+}
+
+int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_DEBUG);
+
+ /* journald_file_open requires a valid machine id */
+ if (access("/etc/machine-id", F_OK) != 0)
+ return log_tests_skipped("/etc/machine-id not found");
+
+ arg_keep = argc > 1;
+
+ test_skip(setup_sequential);
+ test_skip(setup_interleaved);
+
+ test_sequence_numbers();
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "sd-journal.h"
+
+#include "alloc-util.h"
+#include "chattr-util.h"
+#include "io-util.h"
+#include "journald-file.h"
+#include "journal-internal.h"
+#include "log.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "rm-rf.h"
+#include "tests.h"
+#include "util.h"
+
+#define N_ENTRIES 200
+
+static void verify_contents(sd_journal *j, unsigned skip) {
+ unsigned i;
+
+ assert_se(j);
+
+ i = 0;
+ SD_JOURNAL_FOREACH(j) {
+ const void *d;
+ char *k, *c;
+ size_t l;
+ unsigned u = 0;
+
+ assert_se(sd_journal_get_cursor(j, &k) >= 0);
+ printf("cursor: %s\n", k);
+ free(k);
+
+ assert_se(sd_journal_get_data(j, "MAGIC", &d, &l) >= 0);
+ printf("\t%.*s\n", (int) l, (const char*) d);
+
+ assert_se(sd_journal_get_data(j, "NUMBER", &d, &l) >= 0);
+ assert_se(k = strndup(d, l));
+ printf("\t%s\n", k);
+
+ if (skip > 0) {
+ assert_se(safe_atou(k + 7, &u) >= 0);
+ assert_se(i == u);
+ i += skip;
+ }
+
+ free(k);
+
+ assert_se(sd_journal_get_cursor(j, &c) >= 0);
+ assert_se(sd_journal_test_cursor(j, c) > 0);
+ free(c);
+ }
+
+ if (skip > 0)
+ assert_se(i == N_ENTRIES);
+}
+
+static void run_test(void) {
+ JournaldFile *one, *two, *three;
+ char t[] = "/var/tmp/journal-stream-XXXXXX";
+ unsigned i;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
+ char *z;
+ const void *data;
+ size_t l;
+ dual_timestamp previous_ts = DUAL_TIMESTAMP_NULL;
+
+ assert_se(mkdtemp(t));
+ assert_se(chdir(t) >= 0);
+ (void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
+
+ assert_se(journald_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &one) == 0);
+ assert_se(journald_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &two) == 0);
+ assert_se(journald_file_open(-1, "three.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &three) == 0);
+
+ for (i = 0; i < N_ENTRIES; i++) {
+ char *p, *q;
+ dual_timestamp ts;
+ struct iovec iovec[2];
+
+ dual_timestamp_get(&ts);
+
+ if (ts.monotonic <= previous_ts.monotonic)
+ ts.monotonic = previous_ts.monotonic + 1;
+
+ if (ts.realtime <= previous_ts.realtime)
+ ts.realtime = previous_ts.realtime + 1;
+
+ previous_ts = ts;
+
+ assert_se(asprintf(&p, "NUMBER=%u", i) >= 0);
+ iovec[0] = IOVEC_MAKE(p, strlen(p));
+
+ assert_se(asprintf(&q, "MAGIC=%s", i % 5 == 0 ? "quux" : "waldo") >= 0);
+
+ iovec[1] = IOVEC_MAKE(q, strlen(q));
+
+ if (i % 10 == 0)
+ assert_se(journal_file_append_entry(three->file, &ts, NULL, iovec, 2, NULL, NULL, NULL) == 0);
+ else {
+ if (i % 3 == 0)
+ assert_se(journal_file_append_entry(two->file, &ts, NULL, iovec, 2, NULL, NULL, NULL) == 0);
+
+ assert_se(journal_file_append_entry(one->file, &ts, NULL, iovec, 2, NULL, NULL, NULL) == 0);
+ }
+
+ free(p);
+ free(q);
+ }
+
+ (void) journald_file_close(one);
+ (void) journald_file_close(two);
+ (void) journald_file_close(three);
+
+ assert_se(sd_journal_open_directory(&j, t, 0) >= 0);
+
+ assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
+ SD_JOURNAL_FOREACH_BACKWARDS(j) {
+ _cleanup_free_ char *c;
+
+ assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0);
+ printf("\t%.*s\n", (int) l, (const char*) data);
+
+ assert_se(sd_journal_get_cursor(j, &c) >= 0);
+ assert_se(sd_journal_test_cursor(j, c) > 0);
+ }
+
+ SD_JOURNAL_FOREACH(j) {
+ _cleanup_free_ char *c;
+
+ assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0);
+ printf("\t%.*s\n", (int) l, (const char*) data);
+
+ assert_se(sd_journal_get_cursor(j, &c) >= 0);
+ assert_se(sd_journal_test_cursor(j, c) > 0);
+ }
+
+ sd_journal_flush_matches(j);
+
+ verify_contents(j, 1);
+
+ printf("NEXT TEST\n");
+ assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
+
+ assert_se(z = journal_make_match_string(j));
+ printf("resulting match expression is: %s\n", z);
+ free(z);
+
+ verify_contents(j, 5);
+
+ printf("NEXT TEST\n");
+ sd_journal_flush_matches(j);
+ assert_se(sd_journal_add_match(j, "MAGIC=waldo", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=10", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=11", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=12", 0) >= 0);
+
+ assert_se(z = journal_make_match_string(j));
+ printf("resulting match expression is: %s\n", z);
+ free(z);
+
+ verify_contents(j, 0);
+
+ assert_se(sd_journal_query_unique(j, "NUMBER") >= 0);
+ SD_JOURNAL_FOREACH_UNIQUE(j, data, l)
+ printf("%.*s\n", (int) l, (const char*) data);
+
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+}
+
+int main(int argc, char *argv[]) {
+
+ /* journald_file_open requires a valid machine id */
+ if (access("/etc/machine-id", F_OK) != 0)
+ return log_tests_skipped("/etc/machine-id not found");
+
+ test_setup_logging(LOG_DEBUG);
+
+ /* Run this test twice. Once with old hashing and once with new hashing */
+ assert_se(setenv("SYSTEMD_JOURNAL_KEYED_HASH", "1", 1) >= 0);
+ run_test();
+
+ assert_se(setenv("SYSTEMD_JOURNAL_KEYED_HASH", "0", 1) >= 0);
+ run_test();
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "chattr-util.h"
+#include "fd-util.h"
+#include "io-util.h"
+#include "journald-file.h"
+#include "journal-verify.h"
+#include "log.h"
+#include "rm-rf.h"
+#include "terminal-util.h"
+#include "tests.h"
+#include "util.h"
+
+#define N_ENTRIES 6000
+#define RANDOM_RANGE 77
+
+static void bit_toggle(const char *fn, uint64_t p) {
+ uint8_t b;
+ ssize_t r;
+ int fd;
+
+ fd = open(fn, O_RDWR|O_CLOEXEC);
+ assert_se(fd >= 0);
+
+ r = pread(fd, &b, 1, p/8);
+ assert_se(r == 1);
+
+ b ^= 1 << (p % 8);
+
+ r = pwrite(fd, &b, 1, p/8);
+ assert_se(r == 1);
+
+ safe_close(fd);
+}
+
+static int raw_verify(const char *fn, const char *verification_key) {
+ JournalFile *f;
+ int r;
+
+ r = journal_file_open(-1, fn, O_RDONLY, 0666, true, UINT64_MAX, !!verification_key, NULL, NULL, NULL, &f);
+ if (r < 0)
+ return r;
+
+ r = journal_file_verify(f, verification_key, NULL, NULL, NULL, false);
+ (void) journal_file_close(f);
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ char t[] = "/var/tmp/journal-XXXXXX";
+ unsigned n;
+ JournalFile *f;
+ JournaldFile *df;
+ const char *verification_key = argv[1];
+ usec_t from = 0, to = 0, total = 0;
+ struct stat st;
+ uint64_t p;
+
+ /* journald_file_open requires a valid machine id */
+ if (access("/etc/machine-id", F_OK) != 0)
+ return log_tests_skipped("/etc/machine-id not found");
+
+ test_setup_logging(LOG_DEBUG);
+
+ assert_se(mkdtemp(t));
+ assert_se(chdir(t) >= 0);
+ (void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
+
+ log_info("Generating...");
+
+ assert_se(journald_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, !!verification_key, NULL, NULL, NULL, NULL, &df) == 0);
+
+ for (n = 0; n < N_ENTRIES; n++) {
+ struct iovec iovec;
+ struct dual_timestamp ts;
+ char *test;
+
+ dual_timestamp_get(&ts);
+
+ assert_se(asprintf(&test, "RANDOM=%lu", random() % RANDOM_RANGE));
+
+ iovec = IOVEC_MAKE_STRING(test);
+
+ assert_se(journal_file_append_entry(df->file, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
+
+ free(test);
+ }
+
+ (void) journald_file_close(df);
+
+ log_info("Verifying...");
+
+ assert_se(journal_file_open(-1, "test.journal", O_RDONLY, 0666, true, UINT64_MAX, !!verification_key, NULL, NULL, NULL, &f) == 0);
+ /* journal_file_print_header(f); */
+ journal_file_dump(f);
+
+ assert_se(journal_file_verify(f, verification_key, &from, &to, &total, true) >= 0);
+
+ if (verification_key && JOURNAL_HEADER_SEALED(f->header))
+ log_info("=> Validated from %s to %s, %s missing",
+ FORMAT_TIMESTAMP(from),
+ FORMAT_TIMESTAMP(to),
+ FORMAT_TIMESPAN(total > to ? total - to : 0, 0));
+
+ (void) journal_file_close(f);
+
+ if (verification_key) {
+ log_info("Toggling bits...");
+
+ assert_se(stat("test.journal", &st) >= 0);
+
+ for (p = 38448*8+0; p < ((uint64_t) st.st_size * 8); p ++) {
+ bit_toggle("test.journal", p);
+
+ log_info("[ %"PRIu64"+%"PRIu64"]", p / 8, p % 8);
+
+ if (raw_verify("test.journal", verification_key) >= 0)
+ log_notice(ANSI_HIGHLIGHT_RED ">>>> %"PRIu64" (bit %"PRIu64") can be toggled without detection." ANSI_NORMAL, p / 8, p % 8);
+
+ bit_toggle("test.journal", p);
+ }
+ }
+
+ log_info("Exiting...");
+
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "chattr-util.h"
+#include "io-util.h"
+#include "journal-authenticate.h"
+#include "journald-file.h"
+#include "journal-vacuum.h"
+#include "log.h"
+#include "rm-rf.h"
+#include "tests.h"
+
+static bool arg_keep = false;
+
+static void mkdtemp_chdir_chattr(char *path) {
+ assert_se(mkdtemp(path));
+ assert_se(chdir(path) >= 0);
+
+ /* Speed up things a bit on btrfs, ensuring that CoW is turned off for all files created in our
+ * directory during the test run */
+ (void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
+}
+
+static void test_non_empty(void) {
+ dual_timestamp ts;
+ JournaldFile *f;
+ struct iovec iovec;
+ static const char test[] = "TEST1=1", test2[] = "TEST2=2";
+ Object *o;
+ uint64_t p;
+ sd_id128_t fake_boot_id;
+ char t[] = "/var/tmp/journal-XXXXXX";
+
+ test_setup_logging(LOG_DEBUG);
+
+ mkdtemp_chdir_chattr(t);
+
+ assert_se(journald_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, true, NULL, NULL, NULL, NULL, &f) == 0);
+
+ assert_se(dual_timestamp_get(&ts));
+ assert_se(sd_id128_randomize(&fake_boot_id) == 0);
+
+ iovec = IOVEC_MAKE_STRING(test);
+ assert_se(journal_file_append_entry(f->file, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
+
+ iovec = IOVEC_MAKE_STRING(test2);
+ assert_se(journal_file_append_entry(f->file, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
+
+ iovec = IOVEC_MAKE_STRING(test);
+ assert_se(journal_file_append_entry(f->file, &ts, &fake_boot_id, &iovec, 1, NULL, NULL, NULL) == 0);
+
+#if HAVE_GCRYPT
+ journal_file_append_tag(f->file);
+#endif
+ journal_file_dump(f->file);
+
+ assert_se(journal_file_next_entry(f->file, 0, DIRECTION_DOWN, &o, &p) == 1);
+ assert_se(le64toh(o->entry.seqnum) == 1);
+
+ assert_se(journal_file_next_entry(f->file, p, DIRECTION_DOWN, &o, &p) == 1);
+ assert_se(le64toh(o->entry.seqnum) == 2);
+
+ assert_se(journal_file_next_entry(f->file, p, DIRECTION_DOWN, &o, &p) == 1);
+ assert_se(le64toh(o->entry.seqnum) == 3);
+ assert_se(sd_id128_equal(o->entry.boot_id, fake_boot_id));
+
+ assert_se(journal_file_next_entry(f->file, p, DIRECTION_DOWN, &o, &p) == 0);
+
+ assert_se(journal_file_next_entry(f->file, 0, DIRECTION_DOWN, &o, &p) == 1);
+ assert_se(le64toh(o->entry.seqnum) == 1);
+
+ assert_se(journal_file_find_data_object(f->file, test, strlen(test), NULL, &p) == 1);
+ assert_se(journal_file_next_entry_for_data(f->file, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1);
+ assert_se(le64toh(o->entry.seqnum) == 1);
+
+ assert_se(journal_file_next_entry_for_data(f->file, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1);
+ assert_se(le64toh(o->entry.seqnum) == 3);
+
+ assert_se(journal_file_find_data_object(f->file, test2, strlen(test2), NULL, &p) == 1);
+ assert_se(journal_file_next_entry_for_data(f->file, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1);
+ assert_se(le64toh(o->entry.seqnum) == 2);
+
+ assert_se(journal_file_next_entry_for_data(f->file, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1);
+ assert_se(le64toh(o->entry.seqnum) == 2);
+
+ assert_se(journal_file_find_data_object(f->file, "quux", 4, NULL, &p) == 0);
+
+ assert_se(journal_file_move_to_entry_by_seqnum(f->file, 1, DIRECTION_DOWN, &o, NULL) == 1);
+ assert_se(le64toh(o->entry.seqnum) == 1);
+
+ assert_se(journal_file_move_to_entry_by_seqnum(f->file, 3, DIRECTION_DOWN, &o, NULL) == 1);
+ assert_se(le64toh(o->entry.seqnum) == 3);
+
+ assert_se(journal_file_move_to_entry_by_seqnum(f->file, 2, DIRECTION_DOWN, &o, NULL) == 1);
+ assert_se(le64toh(o->entry.seqnum) == 2);
+
+ assert_se(journal_file_move_to_entry_by_seqnum(f->file, 10, DIRECTION_DOWN, &o, NULL) == 0);
+
+ journald_file_rotate(&f, true, UINT64_MAX, true, NULL);
+ journald_file_rotate(&f, true, UINT64_MAX, true, NULL);
+
+ (void) journald_file_close(f);
+
+ log_info("Done...");
+
+ if (arg_keep)
+ log_info("Not removing %s", t);
+ else {
+ journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
+
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+ }
+
+ puts("------------------------------------------------------------");
+}
+
+static void test_empty(void) {
+ JournaldFile *f1, *f2, *f3, *f4;
+ char t[] = "/var/tmp/journal-XXXXXX";
+
+ test_setup_logging(LOG_DEBUG);
+
+ mkdtemp_chdir_chattr(t);
+
+ assert_se(journald_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, false, UINT64_MAX, false, NULL, NULL, NULL, NULL, &f1) == 0);
+ assert_se(journald_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &f2) == 0);
+ assert_se(journald_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, 0666, false, UINT64_MAX, true, NULL, NULL, NULL, NULL, &f3) == 0);
+ assert_se(journald_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, true, NULL, NULL, NULL, NULL, &f4) == 0);
+
+ journal_file_print_header(f1->file);
+ puts("");
+ journal_file_print_header(f2->file);
+ puts("");
+ journal_file_print_header(f3->file);
+ puts("");
+ journal_file_print_header(f4->file);
+ puts("");
+
+ log_info("Done...");
+
+ if (arg_keep)
+ log_info("Not removing %s", t);
+ else {
+ journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
+
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+ }
+
+ (void) journald_file_close(f1);
+ (void) journald_file_close(f2);
+ (void) journald_file_close(f3);
+ (void) journald_file_close(f4);
+}
+
+#if HAVE_COMPRESSION
+static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) {
+ dual_timestamp ts;
+ JournaldFile *f;
+ struct iovec iovec;
+ Object *o;
+ uint64_t p;
+ char t[] = "/var/tmp/journal-XXXXXX";
+ char data[2048] = "FIELD=";
+ bool is_compressed;
+ int r;
+
+ assert_se(data_size <= sizeof(data));
+
+ test_setup_logging(LOG_DEBUG);
+
+ mkdtemp_chdir_chattr(t);
+
+ assert_se(journald_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, compress_threshold, true, NULL, NULL, NULL, NULL, &f) == 0);
+
+ dual_timestamp_get(&ts);
+
+ iovec = IOVEC_MAKE(data, data_size);
+ assert_se(journal_file_append_entry(f->file, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
+
+#if HAVE_GCRYPT
+ journal_file_append_tag(f->file);
+#endif
+ journal_file_dump(f->file);
+
+ /* We have to partially reimplement some of the dump logic, because the normal next_entry does the
+ * decompression for us. */
+ p = le64toh(f->file->header->header_size);
+ for (;;) {
+ r = journal_file_move_to_object(f->file, OBJECT_UNUSED, p, &o);
+ assert_se(r == 0);
+ if (o->object.type == OBJECT_DATA)
+ break;
+
+ assert_se(p < le64toh(f->file->header->tail_object_offset));
+ p = p + ALIGN64(le64toh(o->object.size));
+ }
+
+ is_compressed = (o->object.flags & OBJECT_COMPRESSION_MASK) != 0;
+
+ (void) journald_file_close(f);
+
+ log_info("Done...");
+
+ if (arg_keep)
+ log_info("Not removing %s", t);
+ else {
+ journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
+
+ assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+ }
+
+ puts("------------------------------------------------------------");
+
+ return is_compressed;
+}
+
+static void test_min_compress_size(void) {
+ /* Note that XZ will actually fail to compress anything under 80 bytes, so you have to choose the limits
+ * carefully */
+
+ /* DEFAULT_MIN_COMPRESS_SIZE is 512 */
+ assert_se(!check_compressed(UINT64_MAX, 255));
+ assert_se(check_compressed(UINT64_MAX, 513));
+
+ /* compress everything */
+ assert_se(check_compressed(0, 96));
+ assert_se(check_compressed(8, 96));
+
+ /* Ensure we don't try to compress less than 8 bytes */
+ assert_se(!check_compressed(0, 7));
+
+ /* check boundary conditions */
+ assert_se(check_compressed(256, 256));
+ assert_se(!check_compressed(256, 255));
+}
+#endif
+
+int main(int argc, char *argv[]) {
+ arg_keep = argc > 1;
+
+ test_setup_logging(LOG_INFO);
+
+ /* journald_file_open requires a valid machine id */
+ if (access("/etc/machine-id", F_OK) != 0)
+ return log_tests_skipped("/etc/machine-id not found");
+
+ test_non_empty();
+ test_empty();
+#if HAVE_COMPRESSION
+ test_min_compress_size();
+#endif
+
+ return 0;
+}
############################################################
tests += [
- [['src/libsystemd/sd-journal/test-journal.c']],
-
[['src/libsystemd/sd-journal/test-journal-send.c']],
[['src/libsystemd/sd-journal/test-journal-match.c']],
[['src/libsystemd/sd-journal/test-journal-enum.c'],
[], [], [], '', 'timeout=360'],
- [['src/libsystemd/sd-journal/test-journal-stream.c']],
-
- [['src/libsystemd/sd-journal/test-journal-flush.c']],
-
[['src/libsystemd/sd-journal/test-journal-init.c']],
- [['src/libsystemd/sd-journal/test-journal-verify.c']],
-
- [['src/libsystemd/sd-journal/test-journal-interleaving.c']],
-
[['src/libsystemd/sd-journal/test-mmap-cache.c']],
[['src/libsystemd/sd-journal/test-catalog.c']],
bool seal,
JournalMetrics *metrics,
MMapCache *mmap_cache,
- Set *deferred_closes,
JournalFile *template,
JournalFile **ret) {
f->header = h;
if (!newly_created) {
- set_clear_with_destructor(deferred_closes, journal_file_close);
-
r = journal_file_verify_header(f);
if (r < 0)
goto fail;
return 0;
}
-JournalFile* journal_initiate_close(
- JournalFile *f,
- Set *deferred_closes) {
-
- int r;
-
- assert(f);
-
- if (deferred_closes) {
-
- r = set_put(deferred_closes, f);
- if (r < 0)
- log_debug_errno(r, "Failed to add file to deferred close set, closing immediately.");
- else {
- (void) journal_file_set_offline(f, false);
- return NULL;
- }
- }
-
- return journal_file_close(f);
-}
-
-int journal_file_rotate(
- JournalFile **f,
- bool compress,
- uint64_t compress_threshold_bytes,
- bool seal,
- Set *deferred_closes) {
-
- JournalFile *new_file = NULL;
- int r;
-
- assert(f);
- assert(*f);
-
- r = journal_file_archive(*f);
- if (r < 0)
- return r;
-
- r = journal_file_open(
- -1,
- (*f)->path,
- (*f)->flags,
- (*f)->mode,
- compress,
- compress_threshold_bytes,
- seal,
- NULL, /* metrics */
- (*f)->mmap,
- deferred_closes,
- *f, /* template */
- &new_file);
-
- journal_initiate_close(*f, deferred_closes);
- *f = new_file;
-
- return r;
-}
-
int journal_file_dispose(int dir_fd, const char *fname) {
_cleanup_free_ char *p = NULL;
_cleanup_close_ int fd = -1;
return 0;
}
-int journal_file_open_reliably(
- const char *fname,
- int flags,
- mode_t mode,
- bool compress,
- uint64_t compress_threshold_bytes,
- bool seal,
- JournalMetrics *metrics,
- MMapCache *mmap_cache,
- Set *deferred_closes,
- JournalFile *template,
- JournalFile **ret) {
-
- int r;
-
- r = journal_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, mmap_cache,
- deferred_closes, template, ret);
- if (!IN_SET(r,
- -EBADMSG, /* Corrupted */
- -ENODATA, /* Truncated */
- -EHOSTDOWN, /* Other machine */
- -EPROTONOSUPPORT, /* Incompatible feature */
- -EBUSY, /* Unclean shutdown */
- -ESHUTDOWN, /* Already archived */
- -EIO, /* IO error, including SIGBUS on mmap */
- -EIDRM, /* File has been deleted */
- -ETXTBSY)) /* File is from the future */
- return r;
-
- if ((flags & O_ACCMODE) == O_RDONLY)
- return r;
-
- if (!(flags & O_CREAT))
- return r;
-
- if (!endswith(fname, ".journal"))
- return r;
-
- /* The file is corrupted. Rotate it away and try it again (but only once) */
- log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
-
- r = journal_file_dispose(AT_FDCWD, fname);
- if (r < 0)
- return r;
-
- return journal_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, mmap_cache,
- deferred_closes, template, ret);
-}
-
int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p) {
uint64_t q, n, xor_hash = 0;
const sd_id128_t *boot_id;
bool seal,
JournalMetrics *metrics,
MMapCache *mmap_cache,
- Set *deferred_closes,
JournalFile *template,
JournalFile **ret);
int journal_file_fstat(JournalFile *f);
DEFINE_TRIVIAL_CLEANUP_FUNC(JournalFile*, journal_file_close);
-int journal_file_open_reliably(
- const char *fname,
- int flags,
- mode_t mode,
- bool compress,
- uint64_t compress_threshold_bytes,
- bool seal,
- JournalMetrics *metrics,
- MMapCache *mmap_cache,
- Set *deferred_closes,
- JournalFile *template,
- JournalFile **ret);
-
#define ALIGN64(x) (((x) + 7ULL) & ~7ULL)
#define VALID64(x) (((x) & 7ULL) == 0ULL)
void journal_file_print_header(JournalFile *f);
int journal_file_archive(JournalFile *f);
-JournalFile* journal_initiate_close(JournalFile *f, Set *deferred_closes);
-int journal_file_rotate(JournalFile **f, bool compress, uint64_t compress_threshold_bytes, bool seal, Set *deferred_closes);
int journal_file_dispose(int dir_fd, const char *fname);
goto finish;
}
- r = journal_file_open(fd, path, O_RDONLY, 0, false, 0, false, NULL, j->mmap, NULL, NULL, &f);
+ r = journal_file_open(fd, path, O_RDONLY, 0, false, 0, false, NULL, j->mmap, NULL, &f);
if (r < 0) {
log_debug_errno(r, "Failed to open journal file %s: %m", path);
goto finish;
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "sd-journal.h"
-
-#include "alloc-util.h"
-#include "chattr-util.h"
-#include "journal-file.h"
-#include "journal-internal.h"
-#include "macro.h"
-#include "path-util.h"
-#include "string-util.h"
-
-int main(int argc, char *argv[]) {
- _cleanup_free_ char *fn = NULL;
- char dn[] = "/var/tmp/test-journal-flush.XXXXXX";
- JournalFile *new_journal = NULL;
- sd_journal *j = NULL;
- unsigned n = 0;
- int r;
-
- assert_se(mkdtemp(dn));
- (void) chattr_path(dn, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
-
- fn = path_join(dn, "test.journal");
-
- r = journal_file_open(-1, fn, O_CREAT|O_RDWR, 0644, false, 0, false, NULL, NULL, NULL, NULL, &new_journal);
- assert_se(r >= 0);
-
- if (argc > 1)
- r = sd_journal_open_files(&j, (const char **) strv_skip(argv, 1), 0);
- else
- r = sd_journal_open(&j, 0);
- assert_se(r == 0);
-
- sd_journal_set_data_threshold(j, 0);
-
- SD_JOURNAL_FOREACH(j) {
- Object *o;
- JournalFile *f;
-
- f = j->current_file;
- assert_se(f && f->current_offset > 0);
-
- r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
- if (r < 0)
- log_error_errno(r, "journal_file_move_to_object failed: %m");
- assert_se(r >= 0);
-
- r = journal_file_copy_entry(f, new_journal, o, f->current_offset);
- if (r < 0)
- log_warning_errno(r, "journal_file_copy_entry failed: %m");
- assert_se(r >= 0 ||
- IN_SET(r, -EBADMSG, /* corrupted file */
- -EPROTONOSUPPORT, /* unsupported compression */
- -EIO)); /* file rotated */
-
- if (++n >= 10000)
- break;
- }
-
- sd_journal_close(j);
-
- (void) journal_file_close(new_journal);
-
- unlink(fn);
- assert_se(rmdir(dn) == 0);
-
- return 0;
-}
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "sd-journal.h"
-
-#include "alloc-util.h"
-#include "chattr-util.h"
-#include "io-util.h"
-#include "journal-file.h"
-#include "journal-vacuum.h"
-#include "log.h"
-#include "parse-util.h"
-#include "rm-rf.h"
-#include "tests.h"
-#include "util.h"
-
-/* This program tests skipping around in a multi-file journal. */
-
-static bool arg_keep = false;
-
-_noreturn_ static void log_assert_errno(const char *text, int error, const char *file, int line, const char *func) {
- log_internal(LOG_CRIT, error, file, line, func,
- "'%s' failed at %s:%u (%s): %m", text, file, line, func);
- abort();
-}
-
-#define assert_ret(expr) \
- do { \
- int _r_ = (expr); \
- if (_unlikely_(_r_ < 0)) \
- log_assert_errno(#expr, -_r_, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__); \
- } while (false)
-
-static JournalFile *test_open(const char *name) {
- JournalFile *f;
- assert_ret(journal_file_open(-1, name, O_RDWR|O_CREAT, 0644, true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &f));
- return f;
-}
-
-static void test_close(JournalFile *f) {
- (void) journal_file_close (f);
-}
-
-static void append_number(JournalFile *f, int n, uint64_t *seqnum) {
- char *p;
- dual_timestamp ts;
- static dual_timestamp previous_ts = {};
- struct iovec iovec[1];
-
- dual_timestamp_get(&ts);
-
- if (ts.monotonic <= previous_ts.monotonic)
- ts.monotonic = previous_ts.monotonic + 1;
-
- if (ts.realtime <= previous_ts.realtime)
- ts.realtime = previous_ts.realtime + 1;
-
- previous_ts = ts;
-
- assert_se(asprintf(&p, "NUMBER=%d", n) >= 0);
- iovec[0] = IOVEC_MAKE_STRING(p);
- assert_ret(journal_file_append_entry(f, &ts, NULL, iovec, 1, seqnum, NULL, NULL));
- free(p);
-}
-
-static void test_check_number (sd_journal *j, int n) {
- const void *d;
- _cleanup_free_ char *k;
- size_t l;
- int x;
-
- assert_ret(sd_journal_get_data(j, "NUMBER", &d, &l));
- assert_se(k = strndup(d, l));
- printf("%s\n", k);
-
- assert_se(safe_atoi(k + 7, &x) >= 0);
- assert_se(n == x);
-}
-
-static void test_check_numbers_down (sd_journal *j, int count) {
- int i;
-
- for (i = 1; i <= count; i++) {
- int r;
- test_check_number(j, i);
- assert_ret(r = sd_journal_next(j));
- if (i == count)
- assert_se(r == 0);
- else
- assert_se(r == 1);
- }
-
-}
-
-static void test_check_numbers_up (sd_journal *j, int count) {
- for (int i = count; i >= 1; i--) {
- int r;
- test_check_number(j, i);
- assert_ret(r = sd_journal_previous(j));
- if (i == 1)
- assert_se(r == 0);
- else
- assert_se(r == 1);
- }
-
-}
-
-static void setup_sequential(void) {
- JournalFile *one, *two;
- one = test_open("one.journal");
- two = test_open("two.journal");
- append_number(one, 1, NULL);
- append_number(one, 2, NULL);
- append_number(two, 3, NULL);
- append_number(two, 4, NULL);
- test_close(one);
- test_close(two);
-}
-
-static void setup_interleaved(void) {
- JournalFile *one, *two;
- one = test_open("one.journal");
- two = test_open("two.journal");
- append_number(one, 1, NULL);
- append_number(two, 2, NULL);
- append_number(one, 3, NULL);
- append_number(two, 4, NULL);
- test_close(one);
- test_close(two);
-}
-
-static void mkdtemp_chdir_chattr(char *path) {
- assert_se(mkdtemp(path));
- assert_se(chdir(path) >= 0);
-
- /* Speed up things a bit on btrfs, ensuring that CoW is turned off for all files created in our
- * directory during the test run */
- (void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
-}
-
-static void test_skip(void (*setup)(void)) {
- char t[] = "/var/tmp/journal-skip-XXXXXX";
- sd_journal *j;
- int r;
-
- mkdtemp_chdir_chattr(t);
-
- setup();
-
- /* Seek to head, iterate down.
- */
- assert_ret(sd_journal_open_directory(&j, t, 0));
- assert_ret(sd_journal_seek_head(j));
- assert_ret(sd_journal_next(j));
- test_check_numbers_down(j, 4);
- sd_journal_close(j);
-
- /* Seek to tail, iterate up.
- */
- assert_ret(sd_journal_open_directory(&j, t, 0));
- assert_ret(sd_journal_seek_tail(j));
- assert_ret(sd_journal_previous(j));
- test_check_numbers_up(j, 4);
- sd_journal_close(j);
-
- /* Seek to tail, skip to head, iterate down.
- */
- assert_ret(sd_journal_open_directory(&j, t, 0));
- assert_ret(sd_journal_seek_tail(j));
- assert_ret(r = sd_journal_previous_skip(j, 4));
- assert_se(r == 4);
- test_check_numbers_down(j, 4);
- sd_journal_close(j);
-
- /* Seek to head, skip to tail, iterate up.
- */
- assert_ret(sd_journal_open_directory(&j, t, 0));
- assert_ret(sd_journal_seek_head(j));
- assert_ret(r = sd_journal_next_skip(j, 4));
- assert_se(r == 4);
- test_check_numbers_up(j, 4);
- sd_journal_close(j);
-
- log_info("Done...");
-
- if (arg_keep)
- log_info("Not removing %s", t);
- else {
- journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
-
- assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
- }
-
- puts("------------------------------------------------------------");
-}
-
-static void test_sequence_numbers(void) {
-
- char t[] = "/var/tmp/journal-seq-XXXXXX";
- JournalFile *one, *two;
- uint64_t seqnum = 0;
- sd_id128_t seqnum_id;
-
- mkdtemp_chdir_chattr(t);
-
- assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0644,
- true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &one) == 0);
-
- append_number(one, 1, &seqnum);
- printf("seqnum=%"PRIu64"\n", seqnum);
- assert_se(seqnum == 1);
- append_number(one, 2, &seqnum);
- printf("seqnum=%"PRIu64"\n", seqnum);
- assert_se(seqnum == 2);
-
- assert_se(one->header->state == STATE_ONLINE);
- assert_se(!sd_id128_equal(one->header->file_id, one->header->machine_id));
- assert_se(!sd_id128_equal(one->header->file_id, one->header->boot_id));
- assert_se(sd_id128_equal(one->header->file_id, one->header->seqnum_id));
-
- memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t));
-
- assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0644,
- true, UINT64_MAX, false, NULL, NULL, NULL, one, &two) == 0);
-
- assert_se(two->header->state == STATE_ONLINE);
- assert_se(!sd_id128_equal(two->header->file_id, one->header->file_id));
- assert_se(sd_id128_equal(one->header->machine_id, one->header->machine_id));
- assert_se(sd_id128_equal(one->header->boot_id, one->header->boot_id));
- assert_se(sd_id128_equal(one->header->seqnum_id, one->header->seqnum_id));
-
- append_number(two, 3, &seqnum);
- printf("seqnum=%"PRIu64"\n", seqnum);
- assert_se(seqnum == 3);
- append_number(two, 4, &seqnum);
- printf("seqnum=%"PRIu64"\n", seqnum);
- assert_se(seqnum == 4);
-
- test_close(two);
-
- append_number(one, 5, &seqnum);
- printf("seqnum=%"PRIu64"\n", seqnum);
- assert_se(seqnum == 5);
-
- append_number(one, 6, &seqnum);
- printf("seqnum=%"PRIu64"\n", seqnum);
- assert_se(seqnum == 6);
-
- test_close(one);
-
- /* restart server */
- seqnum = 0;
-
- assert_se(journal_file_open(-1, "two.journal", O_RDWR, 0,
- true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &two) == 0);
-
- assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id));
-
- append_number(two, 7, &seqnum);
- printf("seqnum=%"PRIu64"\n", seqnum);
- assert_se(seqnum == 5);
-
- /* So..., here we have the same seqnum in two files with the
- * same seqnum_id. */
-
- test_close(two);
-
- log_info("Done...");
-
- if (arg_keep)
- log_info("Not removing %s", t);
- else {
- journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
-
- assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
- }
-}
-
-int main(int argc, char *argv[]) {
- test_setup_logging(LOG_DEBUG);
-
- /* journal_file_open requires a valid machine id */
- if (access("/etc/machine-id", F_OK) != 0)
- return log_tests_skipped("/etc/machine-id not found");
-
- arg_keep = argc > 1;
-
- test_skip(setup_sequential);
- test_skip(setup_interleaved);
-
- test_sequence_numbers();
-
- return 0;
-}
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "sd-journal.h"
-
-#include "alloc-util.h"
-#include "chattr-util.h"
-#include "io-util.h"
-#include "journal-file.h"
-#include "journal-internal.h"
-#include "log.h"
-#include "macro.h"
-#include "parse-util.h"
-#include "rm-rf.h"
-#include "tests.h"
-#include "util.h"
-
-#define N_ENTRIES 200
-
-static void verify_contents(sd_journal *j, unsigned skip) {
- unsigned i;
-
- assert_se(j);
-
- i = 0;
- SD_JOURNAL_FOREACH(j) {
- const void *d;
- char *k, *c;
- size_t l;
- unsigned u = 0;
-
- assert_se(sd_journal_get_cursor(j, &k) >= 0);
- printf("cursor: %s\n", k);
- free(k);
-
- assert_se(sd_journal_get_data(j, "MAGIC", &d, &l) >= 0);
- printf("\t%.*s\n", (int) l, (const char*) d);
-
- assert_se(sd_journal_get_data(j, "NUMBER", &d, &l) >= 0);
- assert_se(k = strndup(d, l));
- printf("\t%s\n", k);
-
- if (skip > 0) {
- assert_se(safe_atou(k + 7, &u) >= 0);
- assert_se(i == u);
- i += skip;
- }
-
- free(k);
-
- assert_se(sd_journal_get_cursor(j, &c) >= 0);
- assert_se(sd_journal_test_cursor(j, c) > 0);
- free(c);
- }
-
- if (skip > 0)
- assert_se(i == N_ENTRIES);
-}
-
-static void run_test(void) {
- JournalFile *one, *two, *three;
- char t[] = "/var/tmp/journal-stream-XXXXXX";
- unsigned i;
- _cleanup_(sd_journal_closep) sd_journal *j = NULL;
- char *z;
- const void *data;
- size_t l;
- dual_timestamp previous_ts = DUAL_TIMESTAMP_NULL;
-
- assert_se(mkdtemp(t));
- assert_se(chdir(t) >= 0);
- (void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
-
- assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &one) == 0);
- assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &two) == 0);
- assert_se(journal_file_open(-1, "three.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &three) == 0);
-
- for (i = 0; i < N_ENTRIES; i++) {
- char *p, *q;
- dual_timestamp ts;
- struct iovec iovec[2];
-
- dual_timestamp_get(&ts);
-
- if (ts.monotonic <= previous_ts.monotonic)
- ts.monotonic = previous_ts.monotonic + 1;
-
- if (ts.realtime <= previous_ts.realtime)
- ts.realtime = previous_ts.realtime + 1;
-
- previous_ts = ts;
-
- assert_se(asprintf(&p, "NUMBER=%u", i) >= 0);
- iovec[0] = IOVEC_MAKE(p, strlen(p));
-
- assert_se(asprintf(&q, "MAGIC=%s", i % 5 == 0 ? "quux" : "waldo") >= 0);
-
- iovec[1] = IOVEC_MAKE(q, strlen(q));
-
- if (i % 10 == 0)
- assert_se(journal_file_append_entry(three, &ts, NULL, iovec, 2, NULL, NULL, NULL) == 0);
- else {
- if (i % 3 == 0)
- assert_se(journal_file_append_entry(two, &ts, NULL, iovec, 2, NULL, NULL, NULL) == 0);
-
- assert_se(journal_file_append_entry(one, &ts, NULL, iovec, 2, NULL, NULL, NULL) == 0);
- }
-
- free(p);
- free(q);
- }
-
- (void) journal_file_close(one);
- (void) journal_file_close(two);
- (void) journal_file_close(three);
-
- assert_se(sd_journal_open_directory(&j, t, 0) >= 0);
-
- assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
- SD_JOURNAL_FOREACH_BACKWARDS(j) {
- _cleanup_free_ char *c;
-
- assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0);
- printf("\t%.*s\n", (int) l, (const char*) data);
-
- assert_se(sd_journal_get_cursor(j, &c) >= 0);
- assert_se(sd_journal_test_cursor(j, c) > 0);
- }
-
- SD_JOURNAL_FOREACH(j) {
- _cleanup_free_ char *c;
-
- assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0);
- printf("\t%.*s\n", (int) l, (const char*) data);
-
- assert_se(sd_journal_get_cursor(j, &c) >= 0);
- assert_se(sd_journal_test_cursor(j, c) > 0);
- }
-
- sd_journal_flush_matches(j);
-
- verify_contents(j, 1);
-
- printf("NEXT TEST\n");
- assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
-
- assert_se(z = journal_make_match_string(j));
- printf("resulting match expression is: %s\n", z);
- free(z);
-
- verify_contents(j, 5);
-
- printf("NEXT TEST\n");
- sd_journal_flush_matches(j);
- assert_se(sd_journal_add_match(j, "MAGIC=waldo", 0) >= 0);
- assert_se(sd_journal_add_match(j, "NUMBER=10", 0) >= 0);
- assert_se(sd_journal_add_match(j, "NUMBER=11", 0) >= 0);
- assert_se(sd_journal_add_match(j, "NUMBER=12", 0) >= 0);
-
- assert_se(z = journal_make_match_string(j));
- printf("resulting match expression is: %s\n", z);
- free(z);
-
- verify_contents(j, 0);
-
- assert_se(sd_journal_query_unique(j, "NUMBER") >= 0);
- SD_JOURNAL_FOREACH_UNIQUE(j, data, l)
- printf("%.*s\n", (int) l, (const char*) data);
-
- assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
-}
-
-int main(int argc, char *argv[]) {
-
- /* journal_file_open requires a valid machine id */
- if (access("/etc/machine-id", F_OK) != 0)
- return log_tests_skipped("/etc/machine-id not found");
-
- test_setup_logging(LOG_DEBUG);
-
- /* Run this test twice. Once with old hashing and once with new hashing */
- assert_se(setenv("SYSTEMD_JOURNAL_KEYED_HASH", "1", 1) >= 0);
- run_test();
-
- assert_se(setenv("SYSTEMD_JOURNAL_KEYED_HASH", "0", 1) >= 0);
- run_test();
-
- return 0;
-}
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include "chattr-util.h"
-#include "fd-util.h"
-#include "io-util.h"
-#include "journal-file.h"
-#include "journal-verify.h"
-#include "log.h"
-#include "rm-rf.h"
-#include "terminal-util.h"
-#include "tests.h"
-#include "util.h"
-
-#define N_ENTRIES 6000
-#define RANDOM_RANGE 77
-
-static void bit_toggle(const char *fn, uint64_t p) {
- uint8_t b;
- ssize_t r;
- int fd;
-
- fd = open(fn, O_RDWR|O_CLOEXEC);
- assert_se(fd >= 0);
-
- r = pread(fd, &b, 1, p/8);
- assert_se(r == 1);
-
- b ^= 1 << (p % 8);
-
- r = pwrite(fd, &b, 1, p/8);
- assert_se(r == 1);
-
- safe_close(fd);
-}
-
-static int raw_verify(const char *fn, const char *verification_key) {
- JournalFile *f;
- int r;
-
- r = journal_file_open(-1, fn, O_RDONLY, 0666, true, UINT64_MAX, !!verification_key, NULL, NULL, NULL, NULL, &f);
- if (r < 0)
- return r;
-
- r = journal_file_verify(f, verification_key, NULL, NULL, NULL, false);
- (void) journal_file_close(f);
-
- return r;
-}
-
-int main(int argc, char *argv[]) {
- char t[] = "/var/tmp/journal-XXXXXX";
- unsigned n;
- JournalFile *f;
- const char *verification_key = argv[1];
- usec_t from = 0, to = 0, total = 0;
- struct stat st;
- uint64_t p;
-
- /* journal_file_open requires a valid machine id */
- if (access("/etc/machine-id", F_OK) != 0)
- return log_tests_skipped("/etc/machine-id not found");
-
- test_setup_logging(LOG_DEBUG);
-
- assert_se(mkdtemp(t));
- assert_se(chdir(t) >= 0);
- (void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
-
- log_info("Generating...");
-
- assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0);
-
- for (n = 0; n < N_ENTRIES; n++) {
- struct iovec iovec;
- struct dual_timestamp ts;
- char *test;
-
- dual_timestamp_get(&ts);
-
- assert_se(asprintf(&test, "RANDOM=%lu", random() % RANDOM_RANGE));
-
- iovec = IOVEC_MAKE_STRING(test);
-
- assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
-
- free(test);
- }
-
- (void) journal_file_close(f);
-
- log_info("Verifying...");
-
- assert_se(journal_file_open(-1, "test.journal", O_RDONLY, 0666, true, UINT64_MAX, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0);
- /* journal_file_print_header(f); */
- journal_file_dump(f);
-
- assert_se(journal_file_verify(f, verification_key, &from, &to, &total, true) >= 0);
-
- if (verification_key && JOURNAL_HEADER_SEALED(f->header))
- log_info("=> Validated from %s to %s, %s missing",
- FORMAT_TIMESTAMP(from),
- FORMAT_TIMESTAMP(to),
- FORMAT_TIMESPAN(total > to ? total - to : 0, 0));
-
- (void) journal_file_close(f);
-
- if (verification_key) {
- log_info("Toggling bits...");
-
- assert_se(stat("test.journal", &st) >= 0);
-
- for (p = 38448*8+0; p < ((uint64_t) st.st_size * 8); p ++) {
- bit_toggle("test.journal", p);
-
- log_info("[ %"PRIu64"+%"PRIu64"]", p / 8, p % 8);
-
- if (raw_verify("test.journal", verification_key) >= 0)
- log_notice(ANSI_HIGHLIGHT_RED ">>>> %"PRIu64" (bit %"PRIu64") can be toggled without detection." ANSI_NORMAL, p / 8, p % 8);
-
- bit_toggle("test.journal", p);
- }
- }
-
- log_info("Exiting...");
-
- assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
-
- return 0;
-}
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "chattr-util.h"
-#include "io-util.h"
-#include "journal-authenticate.h"
-#include "journal-file.h"
-#include "journal-vacuum.h"
-#include "log.h"
-#include "rm-rf.h"
-#include "tests.h"
-
-static bool arg_keep = false;
-
-static void mkdtemp_chdir_chattr(char *path) {
- assert_se(mkdtemp(path));
- assert_se(chdir(path) >= 0);
-
- /* Speed up things a bit on btrfs, ensuring that CoW is turned off for all files created in our
- * directory during the test run */
- (void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
-}
-
-static void test_non_empty(void) {
- dual_timestamp ts;
- JournalFile *f;
- struct iovec iovec;
- static const char test[] = "TEST1=1", test2[] = "TEST2=2";
- Object *o;
- uint64_t p;
- sd_id128_t fake_boot_id;
- char t[] = "/var/tmp/journal-XXXXXX";
-
- test_setup_logging(LOG_DEBUG);
-
- mkdtemp_chdir_chattr(t);
-
- assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, true, NULL, NULL, NULL, NULL, &f) == 0);
-
- assert_se(dual_timestamp_get(&ts));
- assert_se(sd_id128_randomize(&fake_boot_id) == 0);
-
- iovec = IOVEC_MAKE_STRING(test);
- assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
-
- iovec = IOVEC_MAKE_STRING(test2);
- assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
-
- iovec = IOVEC_MAKE_STRING(test);
- assert_se(journal_file_append_entry(f, &ts, &fake_boot_id, &iovec, 1, NULL, NULL, NULL) == 0);
-
-#if HAVE_GCRYPT
- journal_file_append_tag(f);
-#endif
- journal_file_dump(f);
-
- assert_se(journal_file_next_entry(f, 0, DIRECTION_DOWN, &o, &p) == 1);
- assert_se(le64toh(o->entry.seqnum) == 1);
-
- assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 1);
- assert_se(le64toh(o->entry.seqnum) == 2);
-
- assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 1);
- assert_se(le64toh(o->entry.seqnum) == 3);
- assert_se(sd_id128_equal(o->entry.boot_id, fake_boot_id));
-
- assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 0);
-
- assert_se(journal_file_next_entry(f, 0, DIRECTION_DOWN, &o, &p) == 1);
- assert_se(le64toh(o->entry.seqnum) == 1);
-
- assert_se(journal_file_find_data_object(f, test, strlen(test), NULL, &p) == 1);
- assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1);
- assert_se(le64toh(o->entry.seqnum) == 1);
-
- assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1);
- assert_se(le64toh(o->entry.seqnum) == 3);
-
- assert_se(journal_file_find_data_object(f, test2, strlen(test2), NULL, &p) == 1);
- assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1);
- assert_se(le64toh(o->entry.seqnum) == 2);
-
- assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1);
- assert_se(le64toh(o->entry.seqnum) == 2);
-
- assert_se(journal_file_find_data_object(f, "quux", 4, NULL, &p) == 0);
-
- assert_se(journal_file_move_to_entry_by_seqnum(f, 1, DIRECTION_DOWN, &o, NULL) == 1);
- assert_se(le64toh(o->entry.seqnum) == 1);
-
- assert_se(journal_file_move_to_entry_by_seqnum(f, 3, DIRECTION_DOWN, &o, NULL) == 1);
- assert_se(le64toh(o->entry.seqnum) == 3);
-
- assert_se(journal_file_move_to_entry_by_seqnum(f, 2, DIRECTION_DOWN, &o, NULL) == 1);
- assert_se(le64toh(o->entry.seqnum) == 2);
-
- assert_se(journal_file_move_to_entry_by_seqnum(f, 10, DIRECTION_DOWN, &o, NULL) == 0);
-
- journal_file_rotate(&f, true, UINT64_MAX, true, NULL);
- journal_file_rotate(&f, true, UINT64_MAX, true, NULL);
-
- (void) journal_file_close(f);
-
- log_info("Done...");
-
- if (arg_keep)
- log_info("Not removing %s", t);
- else {
- journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
-
- assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
- }
-
- puts("------------------------------------------------------------");
-}
-
-static void test_empty(void) {
- JournalFile *f1, *f2, *f3, *f4;
- char t[] = "/var/tmp/journal-XXXXXX";
-
- test_setup_logging(LOG_DEBUG);
-
- mkdtemp_chdir_chattr(t);
-
- assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, false, UINT64_MAX, false, NULL, NULL, NULL, NULL, &f1) == 0);
-
- assert_se(journal_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, false, NULL, NULL, NULL, NULL, &f2) == 0);
-
- assert_se(journal_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, 0666, false, UINT64_MAX, true, NULL, NULL, NULL, NULL, &f3) == 0);
-
- assert_se(journal_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, true, NULL, NULL, NULL, NULL, &f4) == 0);
-
- journal_file_print_header(f1);
- puts("");
- journal_file_print_header(f2);
- puts("");
- journal_file_print_header(f3);
- puts("");
- journal_file_print_header(f4);
- puts("");
-
- log_info("Done...");
-
- if (arg_keep)
- log_info("Not removing %s", t);
- else {
- journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
-
- assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
- }
-
- (void) journal_file_close(f1);
- (void) journal_file_close(f2);
- (void) journal_file_close(f3);
- (void) journal_file_close(f4);
-}
-
-#if HAVE_COMPRESSION
-static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) {
- dual_timestamp ts;
- JournalFile *f;
- struct iovec iovec;
- Object *o;
- uint64_t p;
- char t[] = "/var/tmp/journal-XXXXXX";
- char data[2048] = "FIELD=";
- bool is_compressed;
- int r;
-
- assert_se(data_size <= sizeof(data));
-
- test_setup_logging(LOG_DEBUG);
-
- mkdtemp_chdir_chattr(t);
-
- assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, compress_threshold, true, NULL, NULL, NULL, NULL, &f) == 0);
-
- dual_timestamp_get(&ts);
-
- iovec = IOVEC_MAKE(data, data_size);
- assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
-
-#if HAVE_GCRYPT
- journal_file_append_tag(f);
-#endif
- journal_file_dump(f);
-
- /* We have to partially reimplement some of the dump logic, because the normal next_entry does the
- * decompression for us. */
- p = le64toh(f->header->header_size);
- for (;;) {
- r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
- assert_se(r == 0);
- if (o->object.type == OBJECT_DATA)
- break;
-
- assert_se(p < le64toh(f->header->tail_object_offset));
- p = p + ALIGN64(le64toh(o->object.size));
- }
-
- is_compressed = (o->object.flags & OBJECT_COMPRESSION_MASK) != 0;
-
- (void) journal_file_close(f);
-
- log_info("Done...");
-
- if (arg_keep)
- log_info("Not removing %s", t);
- else {
- journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
-
- assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
- }
-
- puts("------------------------------------------------------------");
-
- return is_compressed;
-}
-
-static void test_min_compress_size(void) {
- /* Note that XZ will actually fail to compress anything under 80 bytes, so you have to choose the limits
- * carefully */
-
- /* DEFAULT_MIN_COMPRESS_SIZE is 512 */
- assert_se(!check_compressed(UINT64_MAX, 255));
- assert_se(check_compressed(UINT64_MAX, 513));
-
- /* compress everything */
- assert_se(check_compressed(0, 96));
- assert_se(check_compressed(8, 96));
-
- /* Ensure we don't try to compress less than 8 bytes */
- assert_se(!check_compressed(0, 7));
-
- /* check boundary conditions */
- assert_se(check_compressed(256, 256));
- assert_se(!check_compressed(256, 255));
-}
-#endif
-
-int main(int argc, char *argv[]) {
- arg_keep = argc > 1;
-
- test_setup_logging(LOG_INFO);
-
- /* journal_file_open requires a valid machine id */
- if (access("/etc/machine-id", F_OK) != 0)
- return log_tests_skipped("/etc/machine-id not found");
-
- test_non_empty();
- test_empty();
-#if HAVE_COMPRESSION
- test_min_compress_size();
-#endif
-
- return 0;
-}