#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
+#include "syslog-util.h"
#define JOURNAL_FILES_MAX 7168
#define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
-#define REPLACE_VAR_MAX 256
+/* The maximum size of variable values we'll expand in catalog entries. We bind this to PATH_MAX for now, as
+ * we want to be able to show all officially valid paths at least */
+#define REPLACE_VAR_MAX PATH_MAX
#define DEFAULT_DATA_THRESHOLD (64*1024)
journal_file_reset_location(f);
}
-static void reset_location(sd_journal *j) {
- assert(j);
-
- detach_location(j);
- zero(j->current_location);
-}
-
static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
assert(l);
assert(IN_SET(type, LOCATION_DISCRETE, LOCATION_SEEK));
assert(f);
- assert(o->object.type == OBJECT_ENTRY);
-
- l->type = type;
- l->seqnum = le64toh(o->entry.seqnum);
- l->seqnum_id = f->header->seqnum_id;
- l->realtime = le64toh(o->entry.realtime);
- l->monotonic = le64toh(o->entry.monotonic);
- l->boot_id = o->entry.boot_id;
- l->xor_hash = le64toh(o->entry.xor_hash);
- l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
+ *l = (Location) {
+ .type = type,
+ .seqnum = le64toh(o->entry.seqnum),
+ .seqnum_id = f->header->seqnum_id,
+ .realtime = le64toh(o->entry.realtime),
+ .monotonic = le64toh(o->entry.monotonic),
+ .boot_id = o->entry.boot_id,
+ .xor_hash = le64toh(o->entry.xor_hash),
+ .seqnum_set = true,
+ .realtime_set = true,
+ .monotonic_set = true,
+ .xor_hash_set = true,
+ };
}
static void set_location(sd_journal *j, JournalFile *f, Object *o) {
if (size < 2)
return false;
- if (startswith(data, "__"))
+ if (((char*) data)[0] == '_' && ((char*) data)[1] == '_')
return false;
b = data;
static Match *match_new(Match *p, MatchType t) {
Match *m;
- m = new0(Match, 1);
+ m = new(Match, 1);
if (!m)
return NULL;
- m->type = t;
+ *m = (Match) {
+ .type = t,
+ .parent = p,
+ };
- if (p) {
- m->parent = p;
+ if (p)
LIST_PREPEND(matches, p->matches, m);
- }
return m;
}
_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
Match *l3, *l4, *add_here = NULL, *m;
- le64_t le_hash;
+ uint64_t hash;
assert_return(j, -EINVAL);
assert_return(!journal_pid_changed(j), -ECHILD);
assert(j->level1->type == MATCH_OR_TERM);
assert(j->level2->type == MATCH_AND_TERM);
- le_hash = htole64(hash64(data, size));
+ hash = hash64(data, size);
LIST_FOREACH(matches, l3, j->level2->matches) {
assert(l3->type == MATCH_OR_TERM);
/* Exactly the same match already? Then ignore
* this addition */
- if (l4->le_hash == le_hash &&
+ if (l4->hash == hash &&
l4->size == size &&
memcmp(l4->data, data, size) == 0)
return 0;
if (!m)
goto fail;
- m->le_hash = le_hash;
+ m->hash = hash;
m->size = size;
m->data = memdup(data, size);
if (!m->data)
detach_location(j);
}
-_pure_ static int compare_with_location(JournalFile *f, Location *l) {
+_pure_ static int compare_with_location(const JournalFile *f, const Location *l, const JournalFile *current_file) {
int r;
assert(f);
l->realtime_set &&
f->current_realtime == l->realtime &&
l->xor_hash_set &&
- f->current_xor_hash == l->xor_hash)
+ f->current_xor_hash == l->xor_hash &&
+ f != current_file)
return 0;
if (l->seqnum_set &&
if (m->type == MATCH_DISCRETE) {
uint64_t dp;
- r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
+ r = journal_file_find_data_object_with_hash(f, m->data, m->size, m->hash, NULL, &dp);
if (r <= 0)
return r;
if (m->type == MATCH_DISCRETE) {
uint64_t dp;
- r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
+ r = journal_file_find_data_object_with_hash(f, m->data, m->size, m->hash, NULL, &dp);
if (r <= 0)
return r;
if (j->current_location.type == LOCATION_DISCRETE) {
int k;
- k = compare_with_location(f, &j->current_location);
+ k = compare_with_location(f, &j->current_location, j->current_file);
found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
} else
_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
Object *o;
int r;
- char bid[33], sid[33];
+ char bid[SD_ID128_STRING_MAX], sid[SD_ID128_STRING_MAX];
assert_return(j, -EINVAL);
assert_return(!journal_pid_changed(j), -ECHILD);
!realtime_set)
return -EINVAL;
- reset_location(j);
-
- j->current_location.type = LOCATION_SEEK;
+ detach_location(j);
+ j->current_location = (Location) {
+ .type = LOCATION_SEEK,
+ };
if (realtime_set) {
j->current_location.realtime = (uint64_t) realtime;
assert_return(j, -EINVAL);
assert_return(!journal_pid_changed(j), -ECHILD);
- reset_location(j);
- j->current_location.type = LOCATION_SEEK;
- j->current_location.boot_id = boot_id;
- j->current_location.monotonic = usec;
- j->current_location.monotonic_set = true;
+ detach_location(j);
+
+ j->current_location = (Location) {
+ .type = LOCATION_SEEK,
+ .boot_id = boot_id,
+ .monotonic = usec,
+ .monotonic_set = true,
+ };
return 0;
}
assert_return(j, -EINVAL);
assert_return(!journal_pid_changed(j), -ECHILD);
- reset_location(j);
- j->current_location.type = LOCATION_SEEK;
- j->current_location.realtime = usec;
- j->current_location.realtime_set = true;
+ detach_location(j);
+
+ j->current_location = (Location) {
+ .type = LOCATION_SEEK,
+ .realtime = usec,
+ .realtime_set = true,
+ };
return 0;
}
assert_return(j, -EINVAL);
assert_return(!journal_pid_changed(j), -ECHILD);
- reset_location(j);
- j->current_location.type = LOCATION_HEAD;
+ detach_location(j);
+
+ j->current_location = (Location) {
+ .type = LOCATION_HEAD,
+ };
return 0;
}
assert_return(j, -EINVAL);
assert_return(!journal_pid_changed(j), -ECHILD);
- reset_location(j);
- j->current_location.type = LOCATION_TAIL;
+ detach_location(j);
+
+ j->current_location = (Location) {
+ .type = LOCATION_TAIL,
+ };
return 0;
}
static int dirname_is_machine_id(const char *fn) {
sd_id128_t id, machine;
+ const char *e;
int r;
+ /* Returns true if the specified directory name matches the local machine ID */
+
r = sd_id128_get_machine(&machine);
if (r < 0)
return r;
- r = sd_id128_from_string(fn, &id);
+ e = strchr(fn, '.');
+ if (e) {
+ const char *k;
+
+ /* Looks like it has a namespace suffix. Verify that. */
+ if (!log_namespace_name_valid(e + 1))
+ return false;
+
+ k = strndupa(fn, e - fn);
+ r = sd_id128_from_string(k, &id);
+ } else
+ r = sd_id128_from_string(fn, &id);
if (r < 0)
return r;
return sd_id128_equal(id, machine);
}
+static int dirname_has_namespace(const char *fn, const char *namespace) {
+ const char *e;
+
+ /* Returns true if the specified directory name matches the specified namespace */
+
+ e = strchr(fn, '.');
+ if (e) {
+ const char *k;
+
+ if (!namespace)
+ return false;
+
+ if (!streq(e + 1, namespace))
+ return false;
+
+ k = strndupa(fn, e - fn);
+ return id128_is_valid(k);
+ }
+
+ if (namespace)
+ return false;
+
+ return id128_is_valid(fn);
+}
+
static bool dirent_is_journal_file(const struct dirent *de) {
assert(de);
+ /* Returns true if the specified directory entry looks like a journal file we might be interested in */
+
if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
return false;
endswith(de->d_name, ".journal~");
}
-static bool dirent_is_id128_subdir(const struct dirent *de) {
+static bool dirent_is_journal_subdir(const struct dirent *de) {
+ const char *e, *n;
assert(de);
+ /* returns true if the specified directory entry looks like a directory that might contain journal
+ * files we might be interested in, i.e. is either a 128bit ID or a 128bit ID suffixed by a
+ * namespace. */
+
if (!IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN))
return false;
- return id128_is_valid(de->d_name);
+ e = strchr(de->d_name, '.');
+ if (!e)
+ return id128_is_valid(de->d_name); /* No namespace */
+
+ n = strndupa(de->d_name, e - de->d_name);
+ if (!id128_is_valid(n))
+ return false;
+
+ return log_namespace_name_valid(e + 1);
}
static int directory_open(sd_journal *j, const char *path, DIR **ret) {
if (dirent_is_journal_file(de))
(void) add_file_by_name(j, m->path, de->d_name);
- if (m->is_root && dirent_is_id128_subdir(de))
+ if (m->is_root && dirent_is_journal_subdir(de))
(void) add_directory(j, m->path, de->d_name);
}
}
}
-static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
+static int add_directory(
+ sd_journal *j,
+ const char *prefix,
+ const char *dirname) {
+
_cleanup_free_ char *path = NULL;
_cleanup_closedir_ DIR *d = NULL;
Directory *m;
!((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run")))
return 0;
+ if (dirname &&
+ (!(FLAGS_SET(j->flags, SD_JOURNAL_ALL_NAMESPACES) ||
+ dirname_has_namespace(dirname, j->namespace) > 0 ||
+ (FLAGS_SET(j->flags, SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE) && dirname_has_namespace(dirname, NULL) > 0))))
+ return 0;
+
r = directory_open(j, path, &d);
if (r < 0) {
log_debug_errno(r, "Failed to open directory '%s': %m", path);
m = hashmap_get(j->directories_by_path, path);
if (!m) {
- m = new0(Directory, 1);
+ m = new(Directory, 1);
if (!m) {
r = -ENOMEM;
goto fail;
}
- m->is_root = false;
- m->path = path;
+ *m = (Directory) {
+ .is_root = false,
+ .path = path,
+ };
if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
free(m);
goto fail;
}
} else {
- int dfd;
+ _cleanup_close_ int dfd = -1;
/* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since
* opendir() will take possession of the fd, and close it, which we don't want. */
goto fail;
}
- d = fdopendir(dfd);
+ d = take_fdopendir(&dfd);
if (!d) {
r = -errno;
- safe_close(dfd);
goto fail;
}
return hashmap_ensure_allocated(&j->directories_by_wd, NULL);
}
-static sd_journal *journal_new(int flags, const char *path) {
+static sd_journal *journal_new(int flags, const char *path, const char *namespace) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
j = new0(sd_journal, 1);
j->path = t;
}
+ if (namespace) {
+ j->namespace = strdup(namespace);
+ if (!j->namespace)
+ return NULL;
+ }
+
j->files = ordered_hashmap_new(&path_hash_ops);
if (!j->files)
return NULL;
#define OPEN_ALLOWED_FLAGS \
(SD_JOURNAL_LOCAL_ONLY | \
SD_JOURNAL_RUNTIME_ONLY | \
- SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)
+ SD_JOURNAL_SYSTEM | \
+ SD_JOURNAL_CURRENT_USER | \
+ SD_JOURNAL_ALL_NAMESPACES | \
+ SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE)
-_public_ int sd_journal_open(sd_journal **ret, int flags) {
+_public_ int sd_journal_open_namespace(sd_journal **ret, const char *namespace, int flags) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
int r;
assert_return(ret, -EINVAL);
assert_return((flags & ~OPEN_ALLOWED_FLAGS) == 0, -EINVAL);
- j = journal_new(flags, NULL);
+ j = journal_new(flags, NULL, namespace);
if (!j)
return -ENOMEM;
return 0;
}
+_public_ int sd_journal_open(sd_journal **ret, int flags) {
+ return sd_journal_open_namespace(ret, NULL, flags);
+}
+
#define OPEN_CONTAINER_ALLOWED_FLAGS \
(SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM)
char *p;
int r;
- /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in
+ /* This is deprecated, people should use machined's OpenMachineRootDirectory() call instead in
* combination with sd_journal_open_directory_fd(). */
assert_return(machine, -EINVAL);
if (!streq_ptr(class, "container"))
return -EIO;
- j = journal_new(flags, root);
+ j = journal_new(flags, root, NULL);
if (!j)
return -ENOMEM;
assert_return(path, -EINVAL);
assert_return((flags & ~OPEN_DIRECTORY_ALLOWED_FLAGS) == 0, -EINVAL);
- j = journal_new(flags, path);
+ j = journal_new(flags, path, NULL);
if (!j)
return -ENOMEM;
assert_return(ret, -EINVAL);
assert_return(flags == 0, -EINVAL);
- j = journal_new(flags, NULL);
+ j = journal_new(flags, NULL, NULL);
if (!j)
return -ENOMEM;
if (!S_ISDIR(st.st_mode))
return -EBADFD;
- j = journal_new(flags, NULL);
+ j = journal_new(flags, NULL, NULL);
if (!j)
return -ENOMEM;
assert_return(n_fds > 0, -EBADF);
assert_return(flags == 0, -EINVAL);
- j = journal_new(flags, NULL);
+ j = journal_new(flags, NULL, NULL);
if (!j)
return -ENOMEM;
free(j->path);
free(j->prefix);
+ free(j->namespace);
free(j->unique_field);
free(j->fields_buffer);
free(j);
uint64_t l;
int compression;
- l = le64toh(o->object.size) - offsetof(Object, data.payload);
+ l = le64toh(READ_NOW(o->object.size));
+ if (l < offsetof(Object, data.payload))
+ return -EBADMSG;
+ l -= offsetof(Object, data.payload);
t = (size_t) l;
/* We can't read objects larger than 4G on a 32bit machine */
log_debug("Reiteration complete.");
}
-static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
+static void process_inotify_event(sd_journal *j, const struct inotify_event *e) {
Directory *d;
assert(j);
assert_return(!journal_pid_changed(j), -ECHILD);
if (j->inotify_fd < 0) {
+ Iterator i;
+ JournalFile *f;
/* This is the first invocation, hence create the
* inotify watch */
if (r < 0)
return r;
+ /* Server might have done some vacuuming while we weren't watching.
+ Get rid of the deleted files now so they don't stay around indefinitely. */
+ ORDERED_HASHMAP_FOREACH(f, j->files, i) {
+ r = journal_file_fstat(f);
+ if (r == -EIDRM)
+ remove_file_real(j, f);
+ else if (r < 0) {
+ log_debug_errno(r,"Failed to fstat() journal file '%s' : %m", f->path);
+ continue;
+ }
+ }
+
/* The journal might have changed since the context
* object was created and we weren't watching before,
* hence don't wait for anything, and return