/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
#include <errno.h>
#include <fcntl.h>
#include "catalog.h"
#include "compress.h"
#include "dirent-util.h"
+#include "env-file.h"
+#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
#include "list.h"
#include "lookup3.h"
#include "missing.h"
+#include "nulstr-util.h"
#include "path-util.h"
#include "process-util.h"
#include "replace-var.h"
#include "stat-util.h"
-#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
return strdup("none");
if (m->type == MATCH_DISCRETE)
- return strndup(m->data, m->size);
+ return cescape_length(m->data, m->size);
LIST_FOREACH(matches, i, m->matches) {
char *t, *k;
}
_pure_ static int compare_with_location(JournalFile *f, Location *l) {
+ int r;
+
assert(f);
assert(l);
assert(f->location_type == LOCATION_SEEK);
if (l->seqnum_set &&
sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) {
- if (f->current_seqnum < l->seqnum)
- return -1;
- if (f->current_seqnum > l->seqnum)
- return 1;
+ r = CMP(f->current_seqnum, l->seqnum);
+ if (r != 0)
+ return r;
}
if (l->monotonic_set &&
sd_id128_equal(f->current_boot_id, l->boot_id)) {
- if (f->current_monotonic < l->monotonic)
- return -1;
- if (f->current_monotonic > l->monotonic)
- return 1;
+ r = CMP(f->current_monotonic, l->monotonic);
+ if (r != 0)
+ return r;
}
if (l->realtime_set) {
- if (f->current_realtime < l->realtime)
- return -1;
- if (f->current_realtime > l->realtime)
- return 1;
+ r = CMP(f->current_realtime, l->realtime);
+ if (r != 0)
+ return r;
}
if (l->xor_hash_set) {
- if (f->current_xor_hash < l->xor_hash)
- return -1;
- if (f->current_xor_hash > l->xor_hash)
- return 1;
+ r = CMP(f->current_xor_hash, l->xor_hash);
+ if (r != 0)
+ return r;
}
return 0;
tilded = strjoina(full, "~");
atted = strjoina(prefix, "@");
- return streq(filename, full) ||
- streq(filename, tilded) ||
+ return STR_IN_SET(filename, full, tilded) ||
startswith(filename, atted);
}
return path_startswith(path, prefix);
}
+static void track_file_disposition(sd_journal *j, JournalFile *f) {
+ assert(j);
+ assert(f);
+
+ if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run"))
+ j->has_runtime_files = true;
+ else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var"))
+ j->has_persistent_files = true;
+}
+
static const char *skip_slash(const char *p) {
if (!p)
return p;
}
-static int add_any_file(sd_journal *j, int fd, const char *path) {
- JournalFile *f = NULL;
+static int add_any_file(
+ sd_journal *j,
+ int fd,
+ const char *path) {
+
bool close_fd = false;
+ JournalFile *f;
+ struct stat st;
int r, k;
assert(j);
assert(fd >= 0 || path);
- if (path) {
- f = ordered_hashmap_get(j->files, path);
- if (f) {
- /* Mark this file as seen in this generation. This is used to GC old files in
- * process_q_overflow() to detect journal files that are still and discern them from those who
- * are gone. */
- f->last_seen_generation = j->generation;
- return 0;
+ if (fd < 0) {
+ if (j->toplevel_fd >= 0)
+ /* If there's a top-level fd defined make the path relative, explicitly, since otherwise
+ * openat() ignores the first argument. */
+
+ fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ else
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ if (fd < 0) {
+ r = log_debug_errno(errno, "Failed to open journal file %s: %m", path);
+ goto finish;
+ }
+
+ close_fd = true;
+
+ r = fd_nonblock(fd, false);
+ if (r < 0) {
+ r = log_debug_errno(errno, "Failed to turn off O_NONBLOCK for %s: %m", path);
+ goto finish;
}
}
- if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
- log_debug("Too many open journal files, not adding %s.", path);
- r = -ETOOMANYREFS;
- goto fail;
+ if (fstat(fd, &st) < 0) {
+ r = log_debug_errno(errno, "Failed to fstat file '%s': %m", path);
+ goto finish;
}
- if (fd < 0 && j->toplevel_fd >= 0) {
+ r = stat_verify_regular(&st);
+ if (r < 0) {
+ log_debug_errno(r, "Refusing to open '%s', as it is not a regular file.", path);
+ goto finish;
+ }
- /* If there's a top-level fd defined, open the file relative to this now. (Make the path relative,
- * explicitly, since otherwise openat() ignores the first argument.) */
+ f = ordered_hashmap_get(j->files, path);
+ if (f) {
+ if (f->last_stat.st_dev == st.st_dev &&
+ f->last_stat.st_ino == st.st_ino) {
- fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC);
- if (fd < 0) {
- r = log_debug_errno(errno, "Failed to open journal file %s: %m", path);
- goto fail;
+ /* We already track this file, under the same path and with the same device/inode numbers, it's
+ * hence really the same. Mark this file as seen in this generation. This is used to GC old
+ * files in process_q_overflow() to detect journal files that are still there and discern them
+ * from those which are gone. */
+
+ f->last_seen_generation = j->generation;
+ r = 0;
+ goto finish;
}
- close_fd = true;
+ /* So we tracked a file under this name, but it has a different inode/device. In that case, it got
+ * replaced (probably due to rotation?), let's drop it hence from our list. */
+ remove_file_real(j, f);
+ f = NULL;
+ }
+
+ if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
+ log_debug("Too many open journal files, not adding %s.", path);
+ r = -ETOOMANYREFS;
+ goto finish;
}
- r = journal_file_open(fd, path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f);
+ r = journal_file_open(fd, path, O_RDONLY, 0, false, 0, false, NULL, j->mmap, NULL, NULL, &f);
if (r < 0) {
- if (close_fd)
- safe_close(fd);
log_debug_errno(r, "Failed to open journal file %s: %m", path);
- goto fail;
+ goto finish;
}
/* journal_file_dump(f); */
r = ordered_hashmap_put(j->files, f->path, f);
if (r < 0) {
- f->close_fd = close_fd;
+ f->close_fd = false; /* make sure journal_file_close() doesn't close the caller's fd (or our own). We'll let the caller do that, or ourselves */
(void) journal_file_close(f);
- goto fail;
+ goto finish;
}
- f->last_seen_generation = j->generation;
-
- if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run"))
- j->has_runtime_files = true;
- else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var"))
- j->has_persistent_files = true;
+ close_fd = false; /* the fd is now owned by the JournalFile object */
- log_debug("File %s added.", f->path);
+ f->last_seen_generation = j->generation;
+ track_file_disposition(j, f);
check_network(j, f->fd);
j->current_invalidate_counter++;
- return 0;
+ log_debug("File %s added.", f->path);
-fail:
- k = journal_put_error(j, r, path);
- if (k < 0)
- return k;
+ r = 0;
+
+finish:
+ if (close_fd)
+ safe_close(fd);
+
+ if (r < 0) {
+ k = journal_put_error(j, r, path);
+ if (k < 0)
+ return k;
+ }
return r;
}
-static int add_file(sd_journal *j, const char *prefix, const char *filename) {
+static int add_file_by_name(
+ sd_journal *j,
+ const char *prefix,
+ const char *filename) {
+
const char *path;
assert(j);
return add_any_file(j, -1, path);
}
-static void remove_file(sd_journal *j, const char *prefix, const char *filename) {
+static void remove_file_by_name(
+ sd_journal *j,
+ const char *prefix,
+ const char *filename) {
+
const char *path;
JournalFile *f;
assert(j);
assert(f);
- ordered_hashmap_remove(j->files, f->path);
+ (void) ordered_hashmap_remove(j->files, f->path);
log_debug("File %s removed.", f->path);
assert(d);
FOREACH_DIRENT_ALL(de, d, goto fail) {
+
if (dirent_is_journal_file(de))
- (void) add_file(j, m->path, de->d_name);
+ (void) add_file_by_name(j, m->path, de->d_name);
if (m->is_root && dirent_is_id128_subdir(de))
(void) add_directory(j, m->path, de->d_name);
/* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch
* and reenumerates directory contents */
- if (dirname)
- path = strjoin(prefix, "/", dirname);
- else
- path = strdup(prefix);
+ path = path_join(prefix, dirname);
if (!path) {
r = -ENOMEM;
goto fail;
hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
if (j->inotify_fd >= 0)
- inotify_rm_watch(j->inotify_fd, d->wd);
+ (void) inotify_rm_watch(j->inotify_fd, d->wd);
}
hashmap_remove(j->directories_by_path, d->path);
}
static sd_journal *journal_new(int flags, const char *path) {
- sd_journal *j;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
j = new0(sd_journal, 1);
if (!j)
t = strdup(path);
if (!t)
- goto fail;
+ return NULL;
if (flags & SD_JOURNAL_OS_ROOT)
j->prefix = t;
j->files = ordered_hashmap_new(&path_hash_ops);
if (!j->files)
- goto fail;
+ return NULL;
j->files_cache = ordered_hashmap_iterated_cache_new(j->files);
j->directories_by_path = hashmap_new(&path_hash_ops);
j->mmap = mmap_cache_new();
if (!j->files_cache || !j->directories_by_path || !j->mmap)
- goto fail;
-
- return j;
+ return NULL;
-fail:
- sd_journal_close(j);
- return NULL;
+ return TAKE_PTR(j);
}
#define OPEN_ALLOWED_FLAGS \
SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)
_public_ int sd_journal_open(sd_journal **ret, int flags) {
- sd_journal *j;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
int r;
assert_return(ret, -EINVAL);
r = add_search_paths(j);
if (r < 0)
- goto fail;
+ return r;
- *ret = j;
+ *ret = TAKE_PTR(j);
return 0;
-
-fail:
- sd_journal_close(j);
-
- return r;
}
#define OPEN_CONTAINER_ALLOWED_FLAGS \
_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
_cleanup_free_ char *root = NULL, *class = NULL;
- sd_journal *j;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
char *p;
int r;
assert_return(machine_name_is_valid(machine), -EINVAL);
p = strjoina("/run/systemd/machines/", machine);
- r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
+ r = parse_env_file(NULL, p,
+ "ROOT", &root,
+ "CLASS", &class);
if (r == -ENOENT)
return -EHOSTDOWN;
if (r < 0)
r = add_search_paths(j);
if (r < 0)
- goto fail;
+ return r;
- *ret = j;
+ *ret = TAKE_PTR(j);
return 0;
-
-fail:
- sd_journal_close(j);
- return r;
}
#define OPEN_DIRECTORY_ALLOWED_FLAGS \
SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
- sd_journal *j;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
int r;
assert_return(ret, -EINVAL);
else
r = add_root_directory(j, path, false);
if (r < 0)
- goto fail;
+ return r;
- *ret = j;
+ *ret = TAKE_PTR(j);
return 0;
-
-fail:
- sd_journal_close(j);
- return r;
}
_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
- sd_journal *j;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
const char **path;
int r;
STRV_FOREACH(path, paths) {
r = add_any_file(j, -1, *path);
if (r < 0)
- goto fail;
+ return r;
}
j->no_new_files = true;
- *ret = j;
+ *ret = TAKE_PTR(j);
return 0;
-
-fail:
- sd_journal_close(j);
- return r;
}
#define OPEN_DIRECTORY_FD_ALLOWED_FLAGS \
SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
- sd_journal *j;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
struct stat st;
int r;
else
r = add_root_directory(j, NULL, false);
if (r < 0)
- goto fail;
+ return r;
- *ret = j;
+ *ret = TAKE_PTR(j);
return 0;
-
-fail:
- sd_journal_close(j);
- return r;
}
_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) {
Iterator iterator;
JournalFile *f;
- sd_journal *j;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
unsigned i;
int r;
goto fail;
}
- if (!S_ISREG(st.st_mode)) {
- r = -EBADFD;
+ r = stat_verify_regular(&st);
+ if (r < 0)
goto fail;
- }
r = add_any_file(j, fds[i], NULL);
if (r < 0)
j->no_new_files = true;
j->no_inotify = true;
- *ret = j;
+ *ret = TAKE_PTR(j);
return 0;
fail:
ORDERED_HASHMAP_FOREACH(f, j->files, iterator)
f->close_fd = false;
- sd_journal_close(j);
return r;
}
/* Event for a journal file */
if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
- (void) add_file(j, d->path, e->name);
+ (void) add_file_by_name(j, d->path, e->name);
else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT))
- remove_file(j, d->path, e->name);
+ remove_file_by_name(j, d->path, e->name);
} else if (!d->is_root && e->len == 0) {
return r;
/* Let's do the type check by hand, since we used 0 context above. */
- if (o->object.type != OBJECT_DATA) {
- log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
- j->unique_file->path, j->unique_offset,
- o->object.type, OBJECT_DATA);
- return -EBADMSG;
- }
+ if (o->object.type != OBJECT_DATA)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s:offset " OFSfmt ": object has type %d, expected %d",
+ j->unique_file->path,
+ j->unique_offset,
+ o->object.type, OBJECT_DATA);
r = return_data(j, j->unique_file, o, &odata, &ol);
if (r < 0)
return r;
/* Check if we have at least the field name and "=". */
- if (ol <= k) {
- log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
- j->unique_file->path, j->unique_offset,
- ol, k + 1);
- return -EBADMSG;
- }
-
- if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
- log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
- j->unique_file->path, j->unique_offset,
- j->unique_field);
- return -EBADMSG;
- }
+ if (ol <= k)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
+ j->unique_file->path,
+ j->unique_offset, ol, k + 1);
+
+ if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=')
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s:offset " OFSfmt ": object does not start with \"%s=\"",
+ j->unique_file->path,
+ j->unique_offset,
+ j->unique_field);
/* OK, now let's see if we already returned this data
* object by checking if it exists in the earlier
return r;
/* Because we used OBJECT_UNUSED above, we need to do our type check manually */
- if (o->object.type != OBJECT_FIELD) {
- log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD);
- return -EBADMSG;
- }
+ if (o->object.type != OBJECT_FIELD)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s:offset " OFSfmt ": object has type %i, expected %i",
+ f->path, j->fields_offset,
+ o->object.type, OBJECT_FIELD);
sz = le64toh(o->object.size) - offsetof(Object, field.payload);