mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
Merge pull request #30209 from yuwata/sd-journal-reduce-fstat
sd-journal: potentially reduce number of fstat call
This commit is contained in:
@@ -128,19 +128,19 @@ static int acquire_journal(sd_journal **ret, char **matches) {
|
||||
assert(ret);
|
||||
|
||||
if (arg_directory) {
|
||||
r = sd_journal_open_directory(&j, arg_directory, 0);
|
||||
r = sd_journal_open_directory(&j, arg_directory, SD_JOURNAL_ASSUME_IMMUTABLE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory);
|
||||
} else if (arg_root) {
|
||||
r = sd_journal_open_directory(&j, arg_root, SD_JOURNAL_OS_ROOT);
|
||||
r = sd_journal_open_directory(&j, arg_root, SD_JOURNAL_OS_ROOT | SD_JOURNAL_ASSUME_IMMUTABLE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open journals in root directory: %s: %m", arg_root);
|
||||
} else if (arg_file) {
|
||||
r = sd_journal_open_files(&j, (const char**)arg_file, 0);
|
||||
r = sd_journal_open_files(&j, (const char**)arg_file, SD_JOURNAL_ASSUME_IMMUTABLE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open journal files: %m");
|
||||
} else {
|
||||
r = sd_journal_open(&j, arg_all ? 0 : SD_JOURNAL_LOCAL_ONLY);
|
||||
r = sd_journal_open(&j, arg_all ? 0 : SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open journal: %m");
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
|
||||
/* Out */
|
||||
|
||||
r = sd_journal_open_files(&j, (const char**) STRV_MAKE(name), 0);
|
||||
r = sd_journal_open_files(&j, (const char**) STRV_MAKE(name), SD_JOURNAL_ASSUME_IMMUTABLE);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "sd_journal_open_files([\"%s\"]) failed: %m", name);
|
||||
assert_se(IN_SET(r, -ENOMEM, -EMFILE, -ENFILE, -ENODATA));
|
||||
|
||||
@@ -777,7 +777,7 @@ static int open_journal(sd_journal **j) {
|
||||
else if (arg_file)
|
||||
r = sd_journal_open_files(j, (const char**) arg_file, 0);
|
||||
else if (arg_machine)
|
||||
r = journal_open_machine(j, arg_machine);
|
||||
r = journal_open_machine(j, arg_machine, 0);
|
||||
else
|
||||
r = sd_journal_open_namespace(j, arg_namespace,
|
||||
(arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY) | arg_namespace_flags | arg_journal_type);
|
||||
|
||||
@@ -60,7 +60,7 @@ static int acquire_first_emergency_log_message(char **ret) {
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
|
||||
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open journal: %m");
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ static const char *arg_field = NULL;
|
||||
static bool arg_catalog = false;
|
||||
static bool arg_reverse = false;
|
||||
static int arg_journal_type = 0;
|
||||
static int arg_journal_additional_open_flags = 0;
|
||||
static int arg_namespace_flags = 0;
|
||||
static char *arg_root = NULL;
|
||||
static char *arg_image = NULL;
|
||||
@@ -1153,6 +1154,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_reverse = true;
|
||||
}
|
||||
|
||||
if (!arg_follow)
|
||||
arg_journal_additional_open_flags = SD_JOURNAL_ASSUME_IMMUTABLE;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -2413,21 +2417,21 @@ static int run(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
if (arg_directory)
|
||||
r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
|
||||
r = sd_journal_open_directory(&j, arg_directory, arg_journal_type | arg_journal_additional_open_flags);
|
||||
else if (arg_root)
|
||||
r = sd_journal_open_directory(&j, arg_root, arg_journal_type | SD_JOURNAL_OS_ROOT);
|
||||
r = sd_journal_open_directory(&j, arg_root, arg_journal_type | arg_journal_additional_open_flags | SD_JOURNAL_OS_ROOT);
|
||||
else if (arg_file_stdin)
|
||||
r = sd_journal_open_files_fd(&j, (int[]) { STDIN_FILENO }, 1, 0);
|
||||
r = sd_journal_open_files_fd(&j, (int[]) { STDIN_FILENO }, 1, arg_journal_additional_open_flags);
|
||||
else if (arg_file)
|
||||
r = sd_journal_open_files(&j, (const char**) arg_file, 0);
|
||||
r = sd_journal_open_files(&j, (const char**) arg_file, arg_journal_additional_open_flags);
|
||||
else if (arg_machine)
|
||||
r = journal_open_machine(&j, arg_machine);
|
||||
r = journal_open_machine(&j, arg_machine, arg_journal_additional_open_flags);
|
||||
else
|
||||
r = sd_journal_open_namespace(
|
||||
&j,
|
||||
arg_namespace,
|
||||
(arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY) |
|
||||
arg_namespace_flags | arg_journal_type);
|
||||
arg_namespace_flags | arg_journal_type | arg_journal_additional_open_flags);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal");
|
||||
|
||||
|
||||
@@ -2244,7 +2244,6 @@ static int journal_file_link_entry(
|
||||
f->header->tail_entry_monotonic = o->entry.monotonic;
|
||||
if (JOURNAL_HEADER_CONTAINS(f->header, tail_entry_offset))
|
||||
f->header->tail_entry_offset = htole64(offset);
|
||||
f->newest_mtime = 0; /* we have a new tail entry now, explicitly invalidate newest boot id/timestamp info */
|
||||
|
||||
/* Link up the items */
|
||||
for (uint64_t i = 0; i < n_items; i++) {
|
||||
|
||||
@@ -129,7 +129,8 @@ typedef struct JournalFile {
|
||||
uint64_t newest_monotonic_usec;
|
||||
uint64_t newest_realtime_usec;
|
||||
unsigned newest_boot_id_prioq_idx;
|
||||
usec_t newest_mtime;
|
||||
uint64_t newest_entry_offset;
|
||||
uint8_t newest_state;
|
||||
} JournalFile;
|
||||
|
||||
typedef enum JournalFileFlags {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "journal-def.h"
|
||||
#include "journal-file.h"
|
||||
#include "list.h"
|
||||
#include "set.h"
|
||||
#include "prioq.h"
|
||||
|
||||
#define JOURNAL_FILES_MAX 7168u
|
||||
|
||||
@@ -69,6 +69,11 @@ struct Directory {
|
||||
unsigned last_seen_generation;
|
||||
};
|
||||
|
||||
typedef struct NewestByBootId {
|
||||
sd_id128_t boot_id;
|
||||
Prioq *prioq; /* JournalFile objects ordered by monotonic timestamp of last update. */
|
||||
} NewestByBootId;
|
||||
|
||||
struct sd_journal {
|
||||
int toplevel_fd;
|
||||
|
||||
@@ -79,7 +84,10 @@ struct sd_journal {
|
||||
OrderedHashmap *files;
|
||||
IteratedCache *files_cache;
|
||||
MMapCache *mmap;
|
||||
Hashmap *newest_by_boot_id; /* key: boot_id, value: prioq, ordered by monotonic timestamp of last update */
|
||||
|
||||
/* a bisectable array of NewestByBootId, ordered by boot id. */
|
||||
NewestByBootId *newest_by_boot_id;
|
||||
size_t n_newest_by_boot_id;
|
||||
|
||||
Location current_location;
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "prioq.h"
|
||||
#include "process-util.h"
|
||||
#include "replace-var.h"
|
||||
#include "sort-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
@@ -413,6 +414,99 @@ _public_ void sd_journal_flush_matches(sd_journal *j) {
|
||||
detach_location(j);
|
||||
}
|
||||
|
||||
static int newest_by_boot_id_compare(const NewestByBootId *a, const NewestByBootId *b) {
|
||||
return id128_compare_func(&a->boot_id, &b->boot_id);
|
||||
}
|
||||
|
||||
static void journal_file_unlink_newest_by_boot_id(sd_journal *j, JournalFile *f) {
|
||||
NewestByBootId *found;
|
||||
|
||||
assert(j);
|
||||
assert(f);
|
||||
|
||||
if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) /* not linked currently, hence this is a NOP */
|
||||
return;
|
||||
|
||||
found = typesafe_bsearch(&(NewestByBootId) { .boot_id = f->newest_boot_id },
|
||||
j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
|
||||
assert(found);
|
||||
|
||||
assert_se(prioq_remove(found->prioq, f, &f->newest_boot_id_prioq_idx) > 0);
|
||||
f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
|
||||
|
||||
/* The prioq may be empty, but that should not cause any issue. Let's keep it. */
|
||||
}
|
||||
|
||||
static void journal_clear_newest_by_boot_id(sd_journal *j) {
|
||||
FOREACH_ARRAY(i, j->newest_by_boot_id, j->n_newest_by_boot_id) {
|
||||
JournalFile *f;
|
||||
|
||||
while ((f = prioq_peek(i->prioq)))
|
||||
journal_file_unlink_newest_by_boot_id(j, f);
|
||||
|
||||
prioq_free(i->prioq);
|
||||
}
|
||||
|
||||
j->newest_by_boot_id = mfree(j->newest_by_boot_id);
|
||||
j->n_newest_by_boot_id = 0;
|
||||
}
|
||||
|
||||
static int journal_file_newest_monotonic_compare(const void *a, const void *b) {
|
||||
const JournalFile *x = a, *y = b;
|
||||
|
||||
return -CMP(x->newest_monotonic_usec, y->newest_monotonic_usec); /* Invert order, we want newest first! */
|
||||
}
|
||||
|
||||
static int journal_file_reshuffle_newest_by_boot_id(sd_journal *j, JournalFile *f) {
|
||||
NewestByBootId *found;
|
||||
int r;
|
||||
|
||||
assert(j);
|
||||
assert(f);
|
||||
|
||||
found = typesafe_bsearch(&(NewestByBootId) { .boot_id = f->newest_boot_id },
|
||||
j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
|
||||
if (found) {
|
||||
/* There's already a priority queue for this boot ID */
|
||||
|
||||
if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) {
|
||||
r = prioq_put(found->prioq, f, &f->newest_boot_id_prioq_idx); /* Insert if we aren't in there yet */
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
prioq_reshuffle(found->prioq, f, &f->newest_boot_id_prioq_idx); /* Reshuffle otherwise */
|
||||
|
||||
} else {
|
||||
_cleanup_(prioq_freep) Prioq *q = NULL;
|
||||
|
||||
/* No priority queue yet, then allocate one */
|
||||
|
||||
assert(f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL); /* we can't be a member either */
|
||||
|
||||
q = prioq_new(journal_file_newest_monotonic_compare);
|
||||
if (!q)
|
||||
return -ENOMEM;
|
||||
|
||||
r = prioq_put(q, f, &f->newest_boot_id_prioq_idx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!GREEDY_REALLOC(j->newest_by_boot_id, j->n_newest_by_boot_id + 1)) {
|
||||
f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
j->newest_by_boot_id[j->n_newest_by_boot_id++] = (NewestByBootId) {
|
||||
.boot_id = f->newest_boot_id,
|
||||
.prioq = TAKE_PTR(q),
|
||||
};
|
||||
|
||||
typesafe_qsort(j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int journal_file_find_newest_for_boot_id(
|
||||
sd_journal *j,
|
||||
sd_id128_t id,
|
||||
@@ -427,16 +521,17 @@ static int journal_file_find_newest_for_boot_id(
|
||||
/* Before we use it, let's refresh the timestamp from the header, and reshuffle our prioq
|
||||
* accordingly. We do this only a bunch of times, to not be caught in some update loop. */
|
||||
for (unsigned n_tries = 0;; n_tries++) {
|
||||
NewestByBootId *found;
|
||||
JournalFile *f;
|
||||
Prioq *q;
|
||||
|
||||
q = hashmap_get(j->newest_by_boot_id, &id);
|
||||
if (!q)
|
||||
found = typesafe_bsearch(&(NewestByBootId) { .boot_id = id },
|
||||
j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
|
||||
|
||||
f = found ? prioq_peek(found->prioq) : NULL;
|
||||
if (!f)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ENODATA),
|
||||
"Requested delta for boot ID %s, but we have no information about that boot ID.", SD_ID128_TO_STRING(id));
|
||||
|
||||
assert_se(f = prioq_peek(q)); /* we delete hashmap entries once the prioq is empty, so this must hold */
|
||||
|
||||
if (f == prev || n_tries >= 5) {
|
||||
/* This was already the best answer in the previous run, or we tried too often, use it */
|
||||
*ret = f;
|
||||
@@ -449,6 +544,11 @@ static int journal_file_find_newest_for_boot_id(
|
||||
r = journal_file_read_tail_timestamp(j, f);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read tail timestamp while trying to find newest journal file for boot ID %s.", SD_ID128_TO_STRING(id));
|
||||
if (r == 0) {
|
||||
/* No new entry found. */
|
||||
*ret = f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Refreshing the timestamp we read might have reshuffled the prioq, hence let's check the
|
||||
* prioq again and only use the information once we reached an equilibrium or hit a limit */
|
||||
@@ -2087,7 +2187,8 @@ static sd_journal *journal_new(int flags, const char *path, const char *namespac
|
||||
SD_JOURNAL_SYSTEM | \
|
||||
SD_JOURNAL_CURRENT_USER | \
|
||||
SD_JOURNAL_ALL_NAMESPACES | \
|
||||
SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE)
|
||||
SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE | \
|
||||
SD_JOURNAL_ASSUME_IMMUTABLE)
|
||||
|
||||
_public_ int sd_journal_open_namespace(sd_journal **ret, const char *namespace, int flags) {
|
||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||
@@ -2113,7 +2214,9 @@ _public_ int sd_journal_open(sd_journal **ret, int flags) {
|
||||
}
|
||||
|
||||
#define OPEN_CONTAINER_ALLOWED_FLAGS \
|
||||
(SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM)
|
||||
(SD_JOURNAL_LOCAL_ONLY | \
|
||||
SD_JOURNAL_SYSTEM | \
|
||||
SD_JOURNAL_ASSUME_IMMUTABLE)
|
||||
|
||||
_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
|
||||
_cleanup_free_ char *root = NULL, *class = NULL;
|
||||
@@ -2157,7 +2260,9 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
|
||||
|
||||
#define OPEN_DIRECTORY_ALLOWED_FLAGS \
|
||||
(SD_JOURNAL_OS_ROOT | \
|
||||
SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
|
||||
SD_JOURNAL_SYSTEM | \
|
||||
SD_JOURNAL_CURRENT_USER | \
|
||||
SD_JOURNAL_ASSUME_IMMUTABLE)
|
||||
|
||||
_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
|
||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||
@@ -2182,12 +2287,15 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define OPEN_FILES_ALLOWED_FLAGS \
|
||||
(SD_JOURNAL_ASSUME_IMMUTABLE)
|
||||
|
||||
_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
|
||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||
int r;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(flags == 0, -EINVAL);
|
||||
assert_return((flags & ~OPEN_FILES_ALLOWED_FLAGS) == 0, -EINVAL);
|
||||
|
||||
j = journal_new(flags, NULL, NULL);
|
||||
if (!j)
|
||||
@@ -2209,7 +2317,8 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla
|
||||
(SD_JOURNAL_OS_ROOT | \
|
||||
SD_JOURNAL_SYSTEM | \
|
||||
SD_JOURNAL_CURRENT_USER | \
|
||||
SD_JOURNAL_TAKE_DIRECTORY_FD)
|
||||
SD_JOURNAL_TAKE_DIRECTORY_FD | \
|
||||
SD_JOURNAL_ASSUME_IMMUTABLE)
|
||||
|
||||
_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
|
||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||
@@ -2247,6 +2356,9 @@ _public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define OPEN_FILES_FD_ALLOWED_FLAGS \
|
||||
(SD_JOURNAL_ASSUME_IMMUTABLE)
|
||||
|
||||
_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) {
|
||||
JournalFile *f;
|
||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||
@@ -2254,7 +2366,7 @@ _public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fd
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(n_fds > 0, -EBADF);
|
||||
assert_return(flags == 0, -EINVAL);
|
||||
assert_return((flags & ~OPEN_FILES_FD_ALLOWED_FLAGS) == 0, -EINVAL);
|
||||
|
||||
j = journal_new(flags, NULL, NULL);
|
||||
if (!j)
|
||||
@@ -2298,14 +2410,10 @@ fail:
|
||||
}
|
||||
|
||||
_public_ void sd_journal_close(sd_journal *j) {
|
||||
Prioq *p;
|
||||
|
||||
if (!j || journal_origin_changed(j))
|
||||
return;
|
||||
|
||||
while ((p = hashmap_first(j->newest_by_boot_id)))
|
||||
journal_file_unlink_newest_by_boot_id(j, prioq_peek(p));
|
||||
hashmap_free(j->newest_by_boot_id);
|
||||
journal_clear_newest_by_boot_id(j);
|
||||
|
||||
sd_journal_flush_matches(j);
|
||||
|
||||
@@ -2337,84 +2445,6 @@ _public_ void sd_journal_close(sd_journal *j) {
|
||||
free(j);
|
||||
}
|
||||
|
||||
static void journal_file_unlink_newest_by_boot_id(sd_journal *j, JournalFile *f) {
|
||||
JournalFile *nf;
|
||||
Prioq *p;
|
||||
|
||||
assert(j);
|
||||
assert(f);
|
||||
|
||||
if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) /* not linked currently, hence this is a NOP */
|
||||
return;
|
||||
|
||||
assert_se(p = hashmap_get(j->newest_by_boot_id, &f->newest_boot_id));
|
||||
assert_se(prioq_remove(p, f, &f->newest_boot_id_prioq_idx) > 0);
|
||||
|
||||
nf = prioq_peek(p);
|
||||
if (nf)
|
||||
/* There's still a member in the prioq? Then make sure the hashmap key now points to its
|
||||
* .newest_boot_id field (and not ours!). Not we only replace the memory of the key here, the
|
||||
* value of the key (and the data associated with it) remain the same. */
|
||||
assert_se(hashmap_replace(j->newest_by_boot_id, &nf->newest_boot_id, p) >= 0);
|
||||
else {
|
||||
assert_se(hashmap_remove(j->newest_by_boot_id, &f->newest_boot_id) == p);
|
||||
prioq_free(p);
|
||||
}
|
||||
|
||||
f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
|
||||
}
|
||||
|
||||
static int journal_file_newest_monotonic_compare(const void *a, const void *b) {
|
||||
const JournalFile *x = a, *y = b;
|
||||
|
||||
return -CMP(x->newest_monotonic_usec, y->newest_monotonic_usec); /* Invert order, we want newest first! */
|
||||
}
|
||||
|
||||
static int journal_file_reshuffle_newest_by_boot_id(sd_journal *j, JournalFile *f) {
|
||||
Prioq *p;
|
||||
int r;
|
||||
|
||||
assert(j);
|
||||
assert(f);
|
||||
|
||||
p = hashmap_get(j->newest_by_boot_id, &f->newest_boot_id);
|
||||
if (p) {
|
||||
/* There's already a priority queue for this boot ID */
|
||||
|
||||
if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) {
|
||||
r = prioq_put(p, f, &f->newest_boot_id_prioq_idx); /* Insert if we aren't in there yet */
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
prioq_reshuffle(p, f, &f->newest_boot_id_prioq_idx); /* Reshuffle otherwise */
|
||||
|
||||
} else {
|
||||
_cleanup_(prioq_freep) Prioq *q = NULL;
|
||||
|
||||
/* No priority queue yet, then allocate one */
|
||||
|
||||
assert(f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL); /* we can't be a member either */
|
||||
|
||||
q = prioq_new(journal_file_newest_monotonic_compare);
|
||||
if (!q)
|
||||
return -ENOMEM;
|
||||
|
||||
r = prioq_put(q, f, &f->newest_boot_id_prioq_idx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = hashmap_ensure_put(&j->newest_by_boot_id, &id128_hash_ops, &f->newest_boot_id, q);
|
||||
if (r < 0) {
|
||||
f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
TAKE_PTR(q);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
|
||||
uint64_t offset, mo, rt;
|
||||
sd_id128_t id;
|
||||
@@ -2428,11 +2458,13 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
|
||||
|
||||
/* Tries to read the timestamp of the most recently written entry. */
|
||||
|
||||
r = journal_file_fstat(f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (f->newest_mtime == timespec_load(&f->last_stat.st_mtim))
|
||||
return 0; /* mtime didn't change since last time, don't bother */
|
||||
if (FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE) && f->newest_entry_offset != 0)
|
||||
return 0; /* We have already read the file, and we assume that the file is immutable. */
|
||||
|
||||
if (f->header->state == f->newest_state &&
|
||||
f->header->state == STATE_ARCHIVED &&
|
||||
f->newest_entry_offset != 0)
|
||||
return 0; /* We have already read archived file. */
|
||||
|
||||
if (JOURNAL_HEADER_CONTAINS(f->header, tail_entry_offset)) {
|
||||
offset = le64toh(READ_NOW(f->header->tail_entry_offset));
|
||||
@@ -2443,6 +2475,8 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
|
||||
}
|
||||
if (offset == 0)
|
||||
return -ENODATA; /* not a single object/entry, hence no tail timestamp */
|
||||
if (offset == f->newest_entry_offset)
|
||||
return 0; /* No new entry is added after we read last time. */
|
||||
|
||||
/* Move to the last object in the journal file, in the hope it is an entry (which it usually will
|
||||
* be). If we lack the "tail_entry_offset" field in the header, we specify the type as OBJECT_UNUSED
|
||||
@@ -2452,6 +2486,7 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to move to last object in journal file, ignoring: %m");
|
||||
o = NULL;
|
||||
offset = 0;
|
||||
}
|
||||
if (o && o->object.type == OBJECT_ENTRY) {
|
||||
/* Yay, last object is an entry, let's use the data. */
|
||||
@@ -2469,10 +2504,11 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
|
||||
mo = le64toh(f->header->tail_entry_monotonic);
|
||||
rt = le64toh(f->header->tail_entry_realtime);
|
||||
id = f->header->tail_entry_boot_id;
|
||||
offset = UINT64_MAX;
|
||||
} else {
|
||||
/* Otherwise let's find the last entry manually (this possibly means traversing the
|
||||
* chain of entry arrays, till the end */
|
||||
r = journal_file_next_entry(f, 0, DIRECTION_UP, &o, NULL);
|
||||
r = journal_file_next_entry(f, 0, DIRECTION_UP, &o, offset == 0 ? &offset : NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
@@ -2487,6 +2523,17 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
|
||||
if (mo > rt) /* monotonic clock is further ahead than realtime? that's weird, refuse to use the data */
|
||||
return -ENODATA;
|
||||
|
||||
if (offset == f->newest_entry_offset) {
|
||||
/* Cached data and the current one should be equivalent. */
|
||||
if (!sd_id128_equal(f->newest_machine_id, f->header->machine_id) ||
|
||||
!sd_id128_equal(f->newest_boot_id, id) ||
|
||||
f->newest_monotonic_usec != mo ||
|
||||
f->newest_realtime_usec != rt)
|
||||
return -EBADMSG;
|
||||
|
||||
return 0; /* No new entry is added after we read last time. */
|
||||
}
|
||||
|
||||
if (!sd_id128_equal(f->newest_boot_id, id))
|
||||
journal_file_unlink_newest_by_boot_id(j, f);
|
||||
|
||||
@@ -2494,13 +2541,14 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
|
||||
f->newest_monotonic_usec = mo;
|
||||
f->newest_realtime_usec = rt;
|
||||
f->newest_machine_id = f->header->machine_id;
|
||||
f->newest_mtime = timespec_load(&f->last_stat.st_mtim);
|
||||
f->newest_entry_offset = offset;
|
||||
f->newest_state = f->header->state;
|
||||
|
||||
r = journal_file_reshuffle_newest_by_boot_id(j, f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
return 1; /* Updated. */
|
||||
}
|
||||
|
||||
_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
|
||||
@@ -2771,6 +2819,7 @@ _public_ int sd_journal_get_fd(sd_journal *j) {
|
||||
|
||||
assert_return(j, -EINVAL);
|
||||
assert_return(!journal_origin_changed(j), -ECHILD);
|
||||
assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
|
||||
|
||||
if (j->no_inotify)
|
||||
return -EMEDIUMTYPE;
|
||||
@@ -2797,6 +2846,7 @@ _public_ int sd_journal_get_events(sd_journal *j) {
|
||||
|
||||
assert_return(j, -EINVAL);
|
||||
assert_return(!journal_origin_changed(j), -ECHILD);
|
||||
assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
|
||||
|
||||
fd = sd_journal_get_fd(j);
|
||||
if (fd < 0)
|
||||
@@ -2810,6 +2860,7 @@ _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
|
||||
|
||||
assert_return(j, -EINVAL);
|
||||
assert_return(!journal_origin_changed(j), -ECHILD);
|
||||
assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
|
||||
assert_return(timeout_usec, -EINVAL);
|
||||
|
||||
fd = sd_journal_get_fd(j);
|
||||
@@ -2937,6 +2988,8 @@ _public_ int sd_journal_process(sd_journal *j) {
|
||||
if (j->inotify_fd < 0) /* We have no inotify fd yet? Then there's noting to process. */
|
||||
return 0;
|
||||
|
||||
assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
|
||||
|
||||
j->last_process_usec = now(CLOCK_MONOTONIC);
|
||||
j->last_invalidate_counter = j->current_invalidate_counter;
|
||||
|
||||
@@ -2965,6 +3018,7 @@ _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
|
||||
|
||||
assert_return(j, -EINVAL);
|
||||
assert_return(!journal_origin_changed(j), -ECHILD);
|
||||
assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
|
||||
|
||||
if (j->inotify_fd < 0) {
|
||||
JournalFile *f;
|
||||
|
||||
@@ -15,7 +15,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY) >= 0);
|
||||
assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
|
||||
|
||||
assert_se(sd_journal_add_match(j, "_TRANSPORT=syslog", 0) >= 0);
|
||||
assert_se(sd_journal_add_match(j, "_UID=0", 0) >= 0);
|
||||
|
||||
@@ -36,9 +36,9 @@ static void test_journal_flush_one(int argc, char *argv[]) {
|
||||
assert_se(r >= 0);
|
||||
|
||||
if (argc > 1)
|
||||
r = sd_journal_open_files(&j, (const char **) strv_skip(argv, 1), 0);
|
||||
r = sd_journal_open_files(&j, (const char **) strv_skip(argv, 1), SD_JOURNAL_ASSUME_IMMUTABLE);
|
||||
else
|
||||
r = sd_journal_open(&j, 0);
|
||||
r = sd_journal_open(&j, SD_JOURNAL_ASSUME_IMMUTABLE);
|
||||
assert_se(r == 0);
|
||||
|
||||
sd_journal_set_data_threshold(j, 0);
|
||||
@@ -75,7 +75,7 @@ static void test_journal_flush_one(int argc, char *argv[]) {
|
||||
|
||||
/* Open the new journal before archiving and offlining the file. */
|
||||
sd_journal_close(j);
|
||||
assert_se(sd_journal_open_directory(&j, dn, 0) >= 0);
|
||||
assert_se(sd_journal_open_directory(&j, dn, SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
|
||||
|
||||
/* Read the online journal. */
|
||||
assert_se(sd_journal_seek_tail(j) >= 0);
|
||||
|
||||
@@ -31,12 +31,12 @@ int main(int argc, char *argv[]) {
|
||||
(void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
|
||||
|
||||
for (i = 0; i < I; i++) {
|
||||
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
|
||||
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE);
|
||||
assert_se(r == 0);
|
||||
|
||||
sd_journal_close(j);
|
||||
|
||||
r = sd_journal_open_directory(&j, t, 0);
|
||||
r = sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE);
|
||||
assert_se(r == 0);
|
||||
|
||||
assert_se(sd_journal_seek_head(j) == 0);
|
||||
|
||||
@@ -260,14 +260,14 @@ static void test_skip_one(void (*setup)(void)) {
|
||||
setup();
|
||||
|
||||
/* Seek to head, iterate down. */
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_ret(sd_journal_seek_head(j));
|
||||
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
|
||||
test_check_numbers_down(j, 9);
|
||||
sd_journal_close(j);
|
||||
|
||||
/* Seek to head, iterate down. */
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_ret(sd_journal_seek_head(j));
|
||||
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
|
||||
assert_se(sd_journal_previous(j) == 0); /* no-op */
|
||||
@@ -275,7 +275,7 @@ static void test_skip_one(void (*setup)(void)) {
|
||||
sd_journal_close(j);
|
||||
|
||||
/* Seek to head twice, iterate down. */
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_ret(sd_journal_seek_head(j));
|
||||
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
|
||||
assert_ret(sd_journal_seek_head(j));
|
||||
@@ -284,7 +284,7 @@ static void test_skip_one(void (*setup)(void)) {
|
||||
sd_journal_close(j);
|
||||
|
||||
/* Seek to head, move to previous, then iterate down. */
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_ret(sd_journal_seek_head(j));
|
||||
assert_se(sd_journal_previous(j) == 0); /* no-op */
|
||||
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
|
||||
@@ -292,7 +292,7 @@ static void test_skip_one(void (*setup)(void)) {
|
||||
sd_journal_close(j);
|
||||
|
||||
/* Seek to head, walk several steps, then iterate down. */
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_ret(sd_journal_seek_head(j));
|
||||
assert_se(sd_journal_previous(j) == 0); /* no-op */
|
||||
assert_se(sd_journal_previous(j) == 0); /* no-op */
|
||||
@@ -304,14 +304,14 @@ static void test_skip_one(void (*setup)(void)) {
|
||||
sd_journal_close(j);
|
||||
|
||||
/* Seek to tail, iterate up. */
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_ret(sd_journal_seek_tail(j));
|
||||
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
|
||||
test_check_numbers_up(j, 9);
|
||||
sd_journal_close(j);
|
||||
|
||||
/* Seek to tail twice, iterate up. */
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_ret(sd_journal_seek_tail(j));
|
||||
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
|
||||
assert_ret(sd_journal_seek_tail(j));
|
||||
@@ -320,7 +320,7 @@ static void test_skip_one(void (*setup)(void)) {
|
||||
sd_journal_close(j);
|
||||
|
||||
/* Seek to tail, move to next, then iterate up. */
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_ret(sd_journal_seek_tail(j));
|
||||
assert_se(sd_journal_next(j) == 0); /* no-op */
|
||||
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
|
||||
@@ -328,7 +328,7 @@ static void test_skip_one(void (*setup)(void)) {
|
||||
sd_journal_close(j);
|
||||
|
||||
/* Seek to tail, walk several steps, then iterate up. */
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_ret(sd_journal_seek_tail(j));
|
||||
assert_se(sd_journal_next(j) == 0); /* no-op */
|
||||
assert_se(sd_journal_next(j) == 0); /* no-op */
|
||||
@@ -340,14 +340,14 @@ static void test_skip_one(void (*setup)(void)) {
|
||||
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_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_ret(sd_journal_seek_tail(j));
|
||||
assert_se(sd_journal_previous_skip(j, 9) == 9); /* pointing to the first entry. */
|
||||
test_check_numbers_down(j, 9);
|
||||
sd_journal_close(j);
|
||||
|
||||
/* Seek to tail, skip to head in a more complex way, then iterate down. */
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_ret(sd_journal_seek_tail(j));
|
||||
assert_se(sd_journal_next(j) == 0);
|
||||
assert_se(sd_journal_previous_skip(j, 4) == 4);
|
||||
@@ -366,14 +366,14 @@ static void test_skip_one(void (*setup)(void)) {
|
||||
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_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_ret(sd_journal_seek_head(j));
|
||||
assert_se(sd_journal_next_skip(j, 9) == 9);
|
||||
test_check_numbers_up(j, 9);
|
||||
sd_journal_close(j);
|
||||
|
||||
/* Seek to head, skip to tail in a more complex way, then iterate up. */
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_ret(sd_journal_seek_head(j));
|
||||
assert_se(sd_journal_previous(j) == 0);
|
||||
assert_se(sd_journal_next_skip(j, 4) == 4);
|
||||
@@ -409,14 +409,14 @@ static void test_boot_id_one(void (*setup)(void), size_t n_boots_expected) {
|
||||
|
||||
setup();
|
||||
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_se(journal_get_boots(j, &boots, &n_boots) >= 0);
|
||||
assert_se(boots);
|
||||
assert_se(n_boots == n_boots_expected);
|
||||
sd_journal_close(j);
|
||||
|
||||
FOREACH_ARRAY(b, boots, n_boots) {
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_se(journal_find_boot_by_id(j, b->id) == 1);
|
||||
sd_journal_close(j);
|
||||
}
|
||||
@@ -424,7 +424,7 @@ static void test_boot_id_one(void (*setup)(void), size_t n_boots_expected) {
|
||||
for (int i = - (int) n_boots + 1; i <= (int) n_boots; i++) {
|
||||
sd_id128_t id;
|
||||
|
||||
assert_ret(sd_journal_open_directory(&j, t, 0));
|
||||
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
assert_se(journal_find_boot_by_offset(j, i, &id) == 1);
|
||||
if (i <= 0)
|
||||
assert_se(sd_id128_equal(id, boots[n_boots + i - 1].id));
|
||||
|
||||
@@ -16,7 +16,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
assert_se(sd_journal_open(&j, 0) >= 0);
|
||||
assert_se(sd_journal_open(&j, SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
|
||||
|
||||
assert_se(sd_journal_add_match(j, "foobar", 0) < 0);
|
||||
assert_se(sd_journal_add_match(j, "foobar=waldo", 0) < 0);
|
||||
|
||||
@@ -119,7 +119,7 @@ static void run_test(void) {
|
||||
(void) journal_file_offline_close(two);
|
||||
(void) journal_file_offline_close(three);
|
||||
|
||||
assert_se(sd_journal_open_directory(&j, t, 0) >= 0);
|
||||
assert_se(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
|
||||
|
||||
assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
|
||||
SD_JOURNAL_FOREACH_BACKWARDS(j) {
|
||||
|
||||
@@ -1597,7 +1597,7 @@ static int show_logs(const LinkInfo *info) {
|
||||
if (arg_lines == 0)
|
||||
return 0;
|
||||
|
||||
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
|
||||
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open journal: %m");
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ int journal_access_check_and_warn(sd_journal *j, bool quiet, bool want_other_use
|
||||
return r;
|
||||
}
|
||||
|
||||
int journal_open_machine(sd_journal **ret, const char *machine) {
|
||||
int journal_open_machine(sd_journal **ret, const char *machine, int flags) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
@@ -178,7 +178,7 @@ int journal_open_machine(sd_journal **ret, const char *machine) {
|
||||
if (machine_fd < 0)
|
||||
return log_error_errno(errno, "Failed to duplicate file descriptor: %m");
|
||||
|
||||
r = sd_journal_open_directory_fd(&j, machine_fd, SD_JOURNAL_OS_ROOT | SD_JOURNAL_TAKE_DIRECTORY_FD);
|
||||
r = sd_journal_open_directory_fd(&j, machine_fd, SD_JOURNAL_OS_ROOT | SD_JOURNAL_TAKE_DIRECTORY_FD | flags);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open journal in machine '%s': %m", machine);
|
||||
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
|
||||
int journal_access_blocked(sd_journal *j);
|
||||
int journal_access_check_and_warn(sd_journal *j, bool quiet, bool want_other_users);
|
||||
int journal_open_machine(sd_journal **ret, const char *machine);
|
||||
int journal_open_machine(sd_journal **ret, const char *machine, int flags);
|
||||
|
||||
@@ -453,6 +453,39 @@ static int output_timestamp_realtime(
|
||||
return (int) strlen(buf);
|
||||
}
|
||||
|
||||
static void parse_display_timestamp(
|
||||
sd_journal *j,
|
||||
const char *realtime,
|
||||
const char *monotonic,
|
||||
dual_timestamp *ret_display_ts,
|
||||
sd_id128_t *ret_boot_id) {
|
||||
|
||||
bool realtime_good = false, monotonic_good = false, boot_id_good = false;
|
||||
|
||||
assert(j);
|
||||
assert(ret_display_ts);
|
||||
assert(ret_boot_id);
|
||||
|
||||
if (realtime)
|
||||
realtime_good = safe_atou64(realtime, &ret_display_ts->realtime) >= 0;
|
||||
if (!realtime_good || !VALID_REALTIME(ret_display_ts->realtime))
|
||||
realtime_good = sd_journal_get_realtime_usec(j, &ret_display_ts->realtime) >= 0;
|
||||
if (!realtime_good)
|
||||
ret_display_ts->realtime = USEC_INFINITY;
|
||||
|
||||
if (monotonic)
|
||||
monotonic_good = safe_atou64(monotonic, &ret_display_ts->monotonic) >= 0;
|
||||
if (!monotonic_good || !VALID_MONOTONIC(ret_display_ts->monotonic))
|
||||
monotonic_good = boot_id_good = sd_journal_get_monotonic_usec(j, &ret_display_ts->monotonic, ret_boot_id) >= 0;
|
||||
if (!monotonic_good)
|
||||
ret_display_ts->monotonic = USEC_INFINITY;
|
||||
|
||||
if (!boot_id_good)
|
||||
boot_id_good = sd_journal_get_monotonic_usec(j, NULL, ret_boot_id) >= 0;
|
||||
if (!boot_id_good)
|
||||
*ret_boot_id = SD_ID128_NULL;
|
||||
}
|
||||
|
||||
static int output_short(
|
||||
FILE *f,
|
||||
sd_journal *j,
|
||||
@@ -461,42 +494,43 @@ static int output_short(
|
||||
OutputFlags flags,
|
||||
const Set *output_fields,
|
||||
const size_t highlight[2],
|
||||
const dual_timestamp *display_ts,
|
||||
const sd_id128_t *boot_id,
|
||||
const dual_timestamp *previous_display_ts,
|
||||
const sd_id128_t *previous_boot_id) {
|
||||
dual_timestamp *previous_display_ts, /* in and out */
|
||||
sd_id128_t *previous_boot_id) { /* in and out */
|
||||
|
||||
int r;
|
||||
const void *data;
|
||||
size_t length, n = 0;
|
||||
_cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL,
|
||||
*message = NULL, *priority = NULL, *transport = NULL,
|
||||
*config_file = NULL, *unit = NULL, *user_unit = NULL, *documentation_url = NULL;
|
||||
*config_file = NULL, *unit = NULL, *user_unit = NULL, *documentation_url = NULL,
|
||||
*realtime = NULL, *monotonic = NULL;
|
||||
size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0,
|
||||
priority_len = 0, transport_len = 0, config_file_len = 0,
|
||||
unit_len = 0, user_unit_len = 0, documentation_url_len = 0;
|
||||
dual_timestamp display_ts;
|
||||
sd_id128_t boot_id;
|
||||
int p = LOG_INFO;
|
||||
bool ellipsized = false, audit;
|
||||
const ParseFieldVec fields[] = {
|
||||
PARSE_FIELD_VEC_ENTRY("_PID=", &pid, &pid_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_COMM=", &comm, &comm_len),
|
||||
PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message, &message_len),
|
||||
PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority, &priority_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_TRANSPORT=", &transport, &transport_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname, &hostname_len),
|
||||
PARSE_FIELD_VEC_ENTRY("SYSLOG_PID=", &fake_pid, &fake_pid_len),
|
||||
PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len),
|
||||
PARSE_FIELD_VEC_ENTRY("CONFIG_FILE=", &config_file, &config_file_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_SYSTEMD_UNIT=", &unit, &unit_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_SYSTEMD_USER_UNIT=", &user_unit, &user_unit_len),
|
||||
PARSE_FIELD_VEC_ENTRY("DOCUMENTATION=", &documentation_url, &documentation_url_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_PID=", &pid, &pid_len ),
|
||||
PARSE_FIELD_VEC_ENTRY("_COMM=", &comm, &comm_len ),
|
||||
PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message, &message_len ),
|
||||
PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority, &priority_len ),
|
||||
PARSE_FIELD_VEC_ENTRY("_TRANSPORT=", &transport, &transport_len ),
|
||||
PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname, &hostname_len ),
|
||||
PARSE_FIELD_VEC_ENTRY("SYSLOG_PID=", &fake_pid, &fake_pid_len ),
|
||||
PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len ),
|
||||
PARSE_FIELD_VEC_ENTRY("CONFIG_FILE=", &config_file, &config_file_len ),
|
||||
PARSE_FIELD_VEC_ENTRY("_SYSTEMD_UNIT=", &unit, &unit_len ),
|
||||
PARSE_FIELD_VEC_ENTRY("_SYSTEMD_USER_UNIT=", &user_unit, &user_unit_len ),
|
||||
PARSE_FIELD_VEC_ENTRY("DOCUMENTATION=", &documentation_url, &documentation_url_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, NULL ),
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, NULL ),
|
||||
};
|
||||
size_t highlight_shifted[] = {highlight ? highlight[0] : 0, highlight ? highlight[1] : 0};
|
||||
|
||||
assert(f);
|
||||
assert(j);
|
||||
assert(display_ts);
|
||||
assert(boot_id);
|
||||
assert(previous_display_ts);
|
||||
assert(previous_boot_id);
|
||||
|
||||
@@ -526,6 +560,8 @@ static int output_short(
|
||||
if (identifier && set_contains(j->exclude_syslog_identifiers, identifier))
|
||||
return 0;
|
||||
|
||||
parse_display_timestamp(j, realtime, monotonic, &display_ts, &boot_id);
|
||||
|
||||
if (!(flags & OUTPUT_SHOW_ALL))
|
||||
strip_tab_ansi(&message, &message_len, highlight_shifted);
|
||||
|
||||
@@ -538,9 +574,9 @@ static int output_short(
|
||||
audit = streq_ptr(transport, "audit");
|
||||
|
||||
if (IN_SET(mode, OUTPUT_SHORT_MONOTONIC, OUTPUT_SHORT_DELTA))
|
||||
r = output_timestamp_monotonic(f, mode, display_ts, boot_id, previous_display_ts, previous_boot_id);
|
||||
r = output_timestamp_monotonic(f, mode, &display_ts, &boot_id, previous_display_ts, previous_boot_id);
|
||||
else
|
||||
r = output_timestamp_realtime(f, j, mode, flags, display_ts);
|
||||
r = output_timestamp_realtime(f, j, mode, flags, &display_ts);
|
||||
if (r < 0)
|
||||
return r;
|
||||
n += r;
|
||||
@@ -661,9 +697,51 @@ static int output_short(
|
||||
if (flags & OUTPUT_CATALOG)
|
||||
(void) print_catalog(f, j);
|
||||
|
||||
*previous_display_ts = display_ts;
|
||||
*previous_boot_id = boot_id;
|
||||
|
||||
return ellipsized;
|
||||
}
|
||||
|
||||
static int get_display_timestamp(
|
||||
sd_journal *j,
|
||||
dual_timestamp *ret_display_ts,
|
||||
sd_id128_t *ret_boot_id) {
|
||||
|
||||
const void *data;
|
||||
_cleanup_free_ char *realtime = NULL, *monotonic = NULL;
|
||||
size_t length;
|
||||
const ParseFieldVec message_fields[] = {
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, NULL),
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, NULL),
|
||||
};
|
||||
int r;
|
||||
|
||||
assert(j);
|
||||
assert(ret_display_ts);
|
||||
assert(ret_boot_id);
|
||||
|
||||
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
|
||||
r = parse_fieldv(data, length, message_fields, ELEMENTSOF(message_fields));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (realtime && monotonic)
|
||||
break;
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) parse_display_timestamp(j, realtime, monotonic, ret_display_ts, ret_boot_id);
|
||||
|
||||
/* Restart all data before */
|
||||
sd_journal_restart_data(j);
|
||||
sd_journal_restart_unique(j);
|
||||
sd_journal_restart_fields(j);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int output_verbose(
|
||||
FILE *f,
|
||||
sd_journal *j,
|
||||
@@ -672,35 +750,39 @@ static int output_verbose(
|
||||
OutputFlags flags,
|
||||
const Set *output_fields,
|
||||
const size_t highlight[2],
|
||||
const dual_timestamp *display_ts,
|
||||
const sd_id128_t *boot_id,
|
||||
const dual_timestamp *previous_display_ts,
|
||||
const sd_id128_t *previous_boot_id) {
|
||||
dual_timestamp *previous_display_ts, /* unused */
|
||||
sd_id128_t *previous_boot_id) { /* unused */
|
||||
|
||||
const void *data;
|
||||
size_t length;
|
||||
_cleanup_free_ char *cursor = NULL;
|
||||
char buf[FORMAT_TIMESTAMP_MAX + 7];
|
||||
dual_timestamp display_ts;
|
||||
sd_id128_t boot_id;
|
||||
const char *timestamp;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
assert(j);
|
||||
assert(display_ts);
|
||||
assert(boot_id);
|
||||
assert(previous_display_ts);
|
||||
assert(previous_boot_id);
|
||||
|
||||
(void) sd_journal_set_data_threshold(j, 0);
|
||||
|
||||
if (!VALID_REALTIME(display_ts->realtime))
|
||||
r = get_display_timestamp(j, &display_ts, &boot_id);
|
||||
if (IN_SET(r, -EBADMSG, -EADDRNOTAVAIL)) {
|
||||
log_debug_errno(r, "Skipping message we can't read: %m");
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get journal fields: %m");
|
||||
|
||||
if (!VALID_REALTIME(display_ts.realtime))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid realtime timestamp available");
|
||||
|
||||
r = sd_journal_get_cursor(j, &cursor);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get cursor: %m");
|
||||
|
||||
timestamp = format_timestamp_style(buf, sizeof buf, display_ts->realtime,
|
||||
timestamp = format_timestamp_style(buf, sizeof buf, display_ts.realtime,
|
||||
flags & OUTPUT_UTC ? TIMESTAMP_US_UTC : TIMESTAMP_US);
|
||||
fprintf(f, "%s%s%s %s[%s]%s\n",
|
||||
timestamp && (flags & OUTPUT_COLOR) ? ANSI_UNDERLINE : "",
|
||||
@@ -789,10 +871,8 @@ static int output_export(
|
||||
OutputFlags flags,
|
||||
const Set *output_fields,
|
||||
const size_t highlight[2],
|
||||
const dual_timestamp *display_ts,
|
||||
const sd_id128_t *boot_id,
|
||||
const dual_timestamp *previous_display_ts,
|
||||
const sd_id128_t *previous_boot_id) {
|
||||
dual_timestamp *previous_display_ts, /* unused */
|
||||
sd_id128_t *previous_boot_id) { /* unused */
|
||||
|
||||
sd_id128_t journal_boot_id, seqnum_id;
|
||||
_cleanup_free_ char *cursor = NULL;
|
||||
@@ -803,10 +883,6 @@ static int output_export(
|
||||
int r;
|
||||
|
||||
assert(j);
|
||||
assert(display_ts);
|
||||
assert(boot_id);
|
||||
assert(previous_display_ts);
|
||||
assert(previous_boot_id);
|
||||
|
||||
(void) sd_journal_set_data_threshold(j, 0);
|
||||
|
||||
@@ -1058,10 +1134,8 @@ static int output_json(
|
||||
OutputFlags flags,
|
||||
const Set *output_fields,
|
||||
const size_t highlight[2],
|
||||
const dual_timestamp *display_ts,
|
||||
const sd_id128_t *boot_id,
|
||||
const dual_timestamp *previous_display_ts,
|
||||
const sd_id128_t *previous_boot_id) {
|
||||
dual_timestamp *previous_display_ts, /* unused */
|
||||
sd_id128_t *previous_boot_id) { /* unused */
|
||||
|
||||
char usecbuf[CONST_MAX(DECIMAL_STR_MAX(usec_t), DECIMAL_STR_MAX(uint64_t))];
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *object = NULL;
|
||||
@@ -1076,10 +1150,6 @@ static int output_json(
|
||||
int r;
|
||||
|
||||
assert(j);
|
||||
assert(display_ts);
|
||||
assert(boot_id);
|
||||
assert(previous_display_ts);
|
||||
assert(previous_boot_id);
|
||||
|
||||
(void) sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
|
||||
|
||||
@@ -1241,20 +1311,14 @@ static int output_cat(
|
||||
OutputFlags flags,
|
||||
const Set *output_fields,
|
||||
const size_t highlight[2],
|
||||
const dual_timestamp *display_ts,
|
||||
const sd_id128_t *boot_id,
|
||||
const dual_timestamp *previous_display_ts,
|
||||
const sd_id128_t *previous_boot_id) {
|
||||
dual_timestamp *previous_display_ts, /* unused */
|
||||
sd_id128_t *previous_boot_id) { /* unused */
|
||||
|
||||
int r, prio = LOG_INFO;
|
||||
const char *field;
|
||||
|
||||
assert(j);
|
||||
assert(f);
|
||||
assert(display_ts);
|
||||
assert(boot_id);
|
||||
assert(previous_display_ts);
|
||||
assert(previous_boot_id);
|
||||
|
||||
(void) sd_journal_set_data_threshold(j, 0);
|
||||
|
||||
@@ -1294,63 +1358,6 @@ static int output_cat(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_display_timestamp(
|
||||
sd_journal *j,
|
||||
dual_timestamp *ret_display_ts,
|
||||
sd_id128_t *ret_boot_id) {
|
||||
|
||||
const void *data;
|
||||
_cleanup_free_ char *realtime = NULL, *monotonic = NULL;
|
||||
size_t length = 0, realtime_len = 0, monotonic_len = 0;
|
||||
const ParseFieldVec message_fields[] = {
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len),
|
||||
};
|
||||
int r;
|
||||
bool realtime_good = false, monotonic_good = false, boot_id_good = false;
|
||||
|
||||
assert(j);
|
||||
assert(ret_display_ts);
|
||||
assert(ret_boot_id);
|
||||
|
||||
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
|
||||
r = parse_fieldv(data, length, message_fields, ELEMENTSOF(message_fields));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (realtime && monotonic)
|
||||
break;
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (realtime)
|
||||
realtime_good = safe_atou64(realtime, &ret_display_ts->realtime) >= 0;
|
||||
if (!realtime_good || !VALID_REALTIME(ret_display_ts->realtime))
|
||||
realtime_good = sd_journal_get_realtime_usec(j, &ret_display_ts->realtime) >= 0;
|
||||
if (!realtime_good)
|
||||
ret_display_ts->realtime = USEC_INFINITY;
|
||||
|
||||
if (monotonic)
|
||||
monotonic_good = safe_atou64(monotonic, &ret_display_ts->monotonic) >= 0;
|
||||
if (!monotonic_good || !VALID_MONOTONIC(ret_display_ts->monotonic))
|
||||
monotonic_good = boot_id_good = sd_journal_get_monotonic_usec(j, &ret_display_ts->monotonic, ret_boot_id) >= 0;
|
||||
if (!monotonic_good)
|
||||
ret_display_ts->monotonic = USEC_INFINITY;
|
||||
|
||||
if (!boot_id_good)
|
||||
boot_id_good = sd_journal_get_monotonic_usec(j, NULL, ret_boot_id) >= 0;
|
||||
if (!boot_id_good)
|
||||
*ret_boot_id = SD_ID128_NULL;
|
||||
|
||||
/* Restart all data before */
|
||||
sd_journal_restart_data(j);
|
||||
sd_journal_restart_unique(j);
|
||||
sd_journal_restart_fields(j);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*output_func_t)(
|
||||
FILE *f,
|
||||
sd_journal *j,
|
||||
@@ -1359,10 +1366,8 @@ typedef int (*output_func_t)(
|
||||
OutputFlags flags,
|
||||
const Set *output_fields,
|
||||
const size_t highlight[2],
|
||||
const dual_timestamp *display_ts,
|
||||
const sd_id128_t *boot_id,
|
||||
const dual_timestamp *previous_display_ts,
|
||||
const sd_id128_t *previous_boot_id);
|
||||
dual_timestamp *previous_display_ts,
|
||||
sd_id128_t *previous_boot_id);
|
||||
|
||||
|
||||
static output_func_t output_funcs[_OUTPUT_MODE_MAX] = {
|
||||
@@ -1396,8 +1401,6 @@ int show_journal_entry(
|
||||
dual_timestamp *previous_display_ts,
|
||||
sd_id128_t *previous_boot_id) {
|
||||
|
||||
dual_timestamp display_ts = DUAL_TIMESTAMP_NULL;
|
||||
sd_id128_t boot_id = SD_ID128_NULL;
|
||||
int r;
|
||||
|
||||
assert(mode >= 0);
|
||||
@@ -1408,14 +1411,6 @@ int show_journal_entry(
|
||||
if (n_columns <= 0)
|
||||
n_columns = columns();
|
||||
|
||||
r = get_display_timestamp(j, &display_ts, &boot_id);
|
||||
if (IN_SET(r, -EBADMSG, -EADDRNOTAVAIL)) {
|
||||
log_debug_errno(r, "Skipping message we can't read: %m");
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get journal fields: %m");
|
||||
|
||||
r = output_funcs[mode](
|
||||
f,
|
||||
j,
|
||||
@@ -1424,15 +1419,9 @@ int show_journal_entry(
|
||||
flags,
|
||||
output_fields,
|
||||
highlight,
|
||||
&display_ts,
|
||||
&boot_id,
|
||||
previous_display_ts,
|
||||
previous_boot_id);
|
||||
|
||||
/* Store timestamp and boot ID for next iteration */
|
||||
*previous_display_ts = display_ts;
|
||||
*previous_boot_id = boot_id;
|
||||
|
||||
if (ellipsized && r > 0)
|
||||
*ellipsized = true;
|
||||
|
||||
@@ -1801,7 +1790,10 @@ int show_journal_by_unit(
|
||||
if (how_many <= 0)
|
||||
return 0;
|
||||
|
||||
r = sd_journal_open_namespace(&j, log_namespace, journal_open_flags | SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE);
|
||||
r = sd_journal_open_namespace(&j, log_namespace,
|
||||
journal_open_flags |
|
||||
SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE |
|
||||
SD_JOURNAL_ASSUME_IMMUTABLE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open journal: %m");
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ enum {
|
||||
SD_JOURNAL_ALL_NAMESPACES = 1 << 5, /* Show all namespaces, not just the default or specified one */
|
||||
SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE = 1 << 6, /* Show default namespace in addition to specified one */
|
||||
SD_JOURNAL_TAKE_DIRECTORY_FD = 1 << 7, /* sd_journal_open_directory_fd() will take ownership of the provided file descriptor. */
|
||||
SD_JOURNAL_ASSUME_IMMUTABLE = 1 << 8, /* Assume the opened journal files are immutable. Journal entries added later may be ignored. */
|
||||
|
||||
SD_JOURNAL_SYSTEM_ONLY _sd_deprecated_ = SD_JOURNAL_SYSTEM /* old name */
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user