Discussion:
[PATCH 0/7] Make "journalctl -M" work with journals inside overlayfs
(too old to reply)
Krzesimir Nowak
2015-06-01 15:28:56 UTC
Permalink
Hi,

When having a container that keeps its journal inside overlayfs,
journalctl from host can't access them anymore. The way to fix, as
proposed by Lennart, was basically journalctl to get file descriptor
of journal directory from machined and go from there. This situation
happens inside rkt containers - currently to get logs from them, we
need to link the journal to host and call journalctl with merge flag,
journalctl -m _HOSTNAME=<machine-id>

So, first three commits are some fixes for minor bugs I noticed or
small refactorings I made during this work.

Fourth patch adds a GetJournal() method to machine1.Manager which
takes a machine name and returns a file descriptor. It works by
entering a mount namespace of machine's leader and getting a
descriptor of /var/log/journal.

Fifth and sixth patches convert the JournalFile from being open()ed
with a path to being openat()ed with dirfd and filename. The fifth
patch wraps the directory fd and path into a refcounted struct, so
many JournalFiles actually hold a ref to JournalDirectory instead of
holding a descriptor. The sixth patch changes the JournalFile to use
JournalDirectory and all the other sources that were using
JournalFile. The patch is quite big because of that, sorry about
that. Maybe you will want to minimize the changes (by preserving
JournalFile's path member).

Seventh patch hooks journalctl to machine1.Manager's GetJournal.

Krzesimir Nowak (7):
nspawn, shared: Factor out sending and receiving fd
nspawn: Close unneeded sockets in outer child
nspawn, machined: Fix comments and error messages related to child
failures
machined: Add getter for machine's journal directory descriptor
journal: Add JournalDirectory
journal: Open JournalFile by dirfd and filename instead of path
journal: Try getting journal fd from machined

Makefile.am | 4 +-
src/journal-remote/journal-remote-write.c | 12 +-
src/journal-remote/journal-remote.c | 34 ++-
src/journal/journal-dir.c | 141 ++++++++++
src/journal/journal-dir.h | 37 +++
src/journal/journal-file.c | 108 +++----
src/journal/journal-file.h | 10 +-
src/journal/journal-internal.h | 3 +-
src/journal/journal-vacuum.c | 27 +-
src/journal/journal-vacuum.h | 3 +-
src/journal/journal-verify.c | 4 +-
src/journal/journalctl.c | 8 +-
src/journal/journald-server.c | 113 +++++---
src/journal/sd-journal.c | 376 ++++++++++++++++++-------
src/journal/test-journal-flush.c | 13 +-
src/journal/test-journal-interleaving.c | 55 ++--
src/journal/test-journal-stream.c | 10 +-
src/journal/test-journal-verify.c | 13 +-
src/journal/test-journal.c | 21 +-
src/machine/machine-dbus.c | 18 +-
src/machine/machined-dbus.c | 112 ++++++++
src/machine/org.freedesktop.machine1.policy.in | 10 +
src/nspawn/nspawn.c | 72 +----
src/shared/util.c | 76 +++++
src/shared/util.h | 4 +
25 files changed, 949 insertions(+), 335 deletions(-)
create mode 100644 src/journal/journal-dir.c
create mode 100644 src/journal/journal-dir.h
--
2.1.0
Krzesimir Nowak
2015-06-01 15:28:57 UTC
Permalink
Right now it can be used to sent rtnl and kmsg descriptors. These
functions will be used later to send journal directory descriptor in
machined.
---
src/nspawn/nspawn.c | 66 +++++++----------------------------------------------
src/shared/util.c | 56 +++++++++++++++++++++++++++++++++++++++++++++
src/shared/util.h | 3 +++
3 files changed, 67 insertions(+), 58 deletions(-)

diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 4211a3d..bd7532c 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -1839,15 +1839,6 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
const char *from, *to;
_cleanup_umask_ mode_t u;
int fd, k;
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;

assert(kmsg_socket >= 0);

@@ -1872,17 +1863,9 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
if (fd < 0)
return log_error_errno(errno, "Failed to open fifo: %m");

- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
-
- mh.msg_controllen = cmsg->cmsg_len;
-
/* Store away the fd in the socket, so that it stays open as
* long as we run the child */
- k = sendmsg(kmsg_socket, &mh, MSG_NOSIGNAL);
+ k = send_fd(kmsg_socket, fd);
safe_close(fd);

if (k < 0)
@@ -1894,20 +1877,11 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
return 0;
}

-static int send_rtnl(int send_fd) {
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
+static int send_rtnl(int sender_fd) {
_cleanup_close_ int fd = -1;
- ssize_t k;
+ int r;

- assert(send_fd >= 0);
+ assert(sender_fd >= 0);

if (!arg_expose_ports)
return 0;
@@ -1916,18 +1890,10 @@ static int send_rtnl(int send_fd) {
if (fd < 0)
return log_error_errno(errno, "Failed to allocate container netlink: %m");

- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
-
- mh.msg_controllen = cmsg->cmsg_len;
-
/* Store away the fd in the socket, so that it stays open as
* long as we run the child */
- k = sendmsg(send_fd, &mh, MSG_NOSIGNAL);
- if (k < 0)
+ r = send_fd(sender_fd, fd);
+ if (r < 0)
return log_error_errno(errno, "Failed to send netlink fd: %m");

return 0;
@@ -2032,18 +1998,8 @@ static int on_address_change(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata)
}

static int watch_rtnl(sd_event *event, int recv_fd, union in_addr_union *exposed, sd_rtnl **ret) {
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
int fd, r;
- ssize_t k;

assert(event);
assert(recv_fd >= 0);
@@ -2052,16 +2008,10 @@ static int watch_rtnl(sd_event *event, int recv_fd, union in_addr_union *exposed
if (!arg_expose_ports)
return 0;

- k = recvmsg(recv_fd, &mh, MSG_NOSIGNAL);
- if (k < 0)
+ r = receive_fd(recv_fd, &fd);
+ if (r < 0)
return log_error_errno(errno, "Failed to recv netlink fd: %m");

- cmsg = CMSG_FIRSTHDR(&mh);
- assert(cmsg->cmsg_level == SOL_SOCKET);
- assert(cmsg->cmsg_type == SCM_RIGHTS);
- assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
- memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
-
r = sd_rtnl_open_fd(&rtnl, fd, 1, RTNLGRP_IPV4_IFADDR);
if (r < 0) {
safe_close(fd);
diff --git a/src/shared/util.c b/src/shared/util.c
index 8a61079..395af7c 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -6046,3 +6046,59 @@ int reset_uid_gid(void) {

return 0;
}
+
+int send_fd(int sender_fd, int fd) {
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+ ssize_t k;
+
+ assert(sender_fd >= 0);
+
+ cmsg = CMSG_FIRSTHDR(&mh);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+
+ mh.msg_controllen = cmsg->cmsg_len;
+ k = sendmsg(sender_fd, &mh, MSG_NOSIGNAL);
+
+ if (k < 0)
+ return -1;
+ return 0;
+}
+
+int receive_fd(int recv_fd, int *fd) {
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+ ssize_t k;
+
+ assert(recv_fd >= 0);
+ assert(fd != NULL);
+
+ k = recvmsg(recv_fd, &mh, MSG_NOSIGNAL);
+ if (k < 0)
+ return -1;
+
+ cmsg = CMSG_FIRSTHDR(&mh);
+ assert(cmsg->cmsg_level == SOL_SOCKET);
+ assert(cmsg->cmsg_type == SCM_RIGHTS);
+ assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
+ memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+
+ return 0;
+}
diff --git a/src/shared/util.h b/src/shared/util.h
index 467ae23..1cfb45f 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -901,3 +901,6 @@ int parse_mode(const char *s, mode_t *ret);
int mount_move_root(const char *path);

int reset_uid_gid(void);
+
+int send_fd(int sender_fd, int fd);
+int receive_fd(int recv_fd, int *fd);
--
2.1.0
Krzesimir Nowak
2015-06-01 15:28:58 UTC
Permalink
---
src/nspawn/nspawn.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index bd7532c..28b79c4 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -4348,6 +4348,8 @@ static int outer_child(
}

pid_socket = safe_close(pid_socket);
+ kmsg_socket = safe_close(kmsg_socket);
+ rtnl_socket = safe_close(rtnl_socket);

return 0;
}
--
2.1.0
Krzesimir Nowak
2015-06-01 15:28:59 UTC
Permalink
---
src/machine/machine-dbus.c | 18 +++++++++---------
src/nspawn/nspawn.c | 4 ++--
2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index 0892479..12f7961 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -327,9 +327,9 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd

r = wait_for_terminate(child, &si);
if (r < 0)
- return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
+ return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");

r = sd_bus_message_close_container(reply);
if (r < 0)
@@ -404,9 +404,9 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s

r = wait_for_terminate(child, &si);
if (r < 0)
- return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
+ return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");

r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
@@ -738,11 +738,11 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu

r = wait_for_terminate(child, &si);
if (r < 0) {
- r = sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
+ r = sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
goto finish;
}
if (si.si_code != CLD_EXITED) {
- r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
+ r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
goto finish;
}
if (si.si_status != EXIT_SUCCESS) {
@@ -750,7 +750,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r))
r = sd_bus_error_set_errnof(error, r, "Failed to mount: %m");
else
- r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client failed.");
+ r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child failed.");
goto finish;
}

@@ -786,7 +786,7 @@ static int machine_operation_done(sd_event_source *s, const siginfo_t *si, void
o->pid = 0;

if (si->si_code != CLD_EXITED) {
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
+ r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
goto fail;
}

@@ -794,7 +794,7 @@ static int machine_operation_done(sd_event_source *s, const siginfo_t *si, void
if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r))
r = sd_bus_error_set_errnof(&error, r, "%m");
else
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Client failed.");
+ r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed.");

goto fail;
}
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 28b79c4..2b397b6 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -4806,8 +4806,8 @@ int main(int argc, char *argv[]) {
}

/* Let the child know that we are ready and wait that the child is completely ready now. */
- if (!barrier_place_and_sync(&barrier)) { /* #5 */
- log_error("Client died too early.");
+ if (!barrier_place_and_sync(&barrier)) { /* #4 */
+ log_error("Child died too early.");
r = -ESRCH;
goto finish;
}
--
2.1.0
Krzesimir Nowak
2015-06-01 15:29:00 UTC
Permalink
Sometimes machine's journal is not accessible by path, so we can ask
machined to provide a descriptor to it.
---
src/machine/machined-dbus.c | 112 +++++++++++++++++++++++++
src/machine/org.freedesktop.machine1.policy.in | 10 +++
2 files changed, 122 insertions(+)

diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 0e971a6..ea5f6f7 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -37,6 +37,7 @@
#include "machined.h"
#include "machine-dbus.h"
#include "formats-util.h"
+#include "process-util.h"

static int property_get_pool_path(
sd_bus *bus,
@@ -840,6 +841,116 @@ static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bu
return bus_image_method_set_limit(message, i, error);
}

+static int get_journal_fd_child(int socket_fd, int mntns_fd, int root_fd) {
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ r = namespace_enter(-1, mntns_fd, -1, root_fd);
+ if (r < 0)
+ return r;
+
+ fd = open("/var/log/journal", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return -errno;
+
+ r = send_fd(socket_fd, fd);
+ return r;
+}
+
+static int get_journal_fd_parent(int socket_fd, pid_t child, sd_bus_error *error, int* journal_fd) {
+ int r;
+ siginfo_t si;
+
+ r = wait_for_terminate(child, &si);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
+ if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+
+ r = receive_fd(socket_fd, journal_fd);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to receive journal fd: %m");
+
+ return 0;
+}
+
+static int get_journal_fd(Machine *machine, sd_bus_error *error, int *journal_fd) {
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ _cleanup_close_ int mntns_fd = -1, root_fd = -1, fd = -1;
+ pid_t child;
+ int r;
+
+ assert(machine);
+ assert(error);
+ assert(journal_fd);
+
+ r = socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pair);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to create pair of sockets: %m");
+
+ r = namespace_open(machine->leader, NULL, &mntns_fd, NULL, &root_fd);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to open leader's namespace(): %m");
+
+ child = fork();
+ if (child < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+
+ if (child == 0) {
+ pair[0] = safe_close(pair[0]);
+ r = get_journal_fd_child(pair[1], mntns_fd, root_fd);
+ pair[1] = safe_close(pair[1]);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+ _exit(EXIT_SUCCESS);
+ }
+
+ pair[1] = safe_close(pair[1]);
+ r = get_journal_fd_parent(pair[0], child, error, journal_fd);
+ return r;
+}
+
+static int method_get_journal(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ int r;
+ Machine *machine;
+ _cleanup_close_ int journal_fd = -1;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.machine1.get-journal",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ if (!machine_name_is_valid(name))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name");
+
+ machine = hashmap_get(m->machines, name);
+ if (!machine)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+
+ r = get_journal_fd(machine, error, &journal_fd);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, "h", journal_fd);
+}
+
const sd_bus_vtable manager_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0),
@@ -869,6 +980,7 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetJournal", "s", "h", method_get_journal, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("MachineNew", "so", 0),
SD_BUS_SIGNAL("MachineRemoved", "so", 0),
SD_BUS_VTABLE_END
diff --git a/src/machine/org.freedesktop.machine1.policy.in b/src/machine/org.freedesktop.machine1.policy.in
index 02714e8..d3a6ebb 100644
--- a/src/machine/org.freedesktop.machine1.policy.in
+++ b/src/machine/org.freedesktop.machine1.policy.in
@@ -46,4 +46,14 @@
</defaults>
</action>

+ <action id="org.freedesktop.machine1.get-journal">
+ <_description>Get local container's journal descriptor</_description>
+ <_message>Authentication is required to get local container's journal descriptor.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
</policyconfig>
--
2.1.0
Krzesimir Nowak
2015-06-01 15:29:01 UTC
Permalink
This ref-counted struct holds a path and a descriptor to a
directory. The descriptor should be used for "real" work (openat,
renameat and others) and the path should be used for
logging/debugging.
---
Makefile.am | 2 +
src/journal/journal-dir.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++
src/journal/journal-dir.h | 37 ++++++++++++
3 files changed, 180 insertions(+)
create mode 100644 src/journal/journal-dir.c
create mode 100644 src/journal/journal-dir.h

diff --git a/Makefile.am b/Makefile.am
index 43b819b..32e7ca3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4644,6 +4644,8 @@ libsystemd_journal_internal_la_SOURCES = \
src/systemd/_sd-common.h \
src/journal/journal-file.c \
src/journal/journal-file.h \
+ src/journal/journal-dir.c \
+ src/journal/journal-dir.h \
src/journal/journal-vacuum.c \
src/journal/journal-vacuum.h \
src/journal/journal-verify.c \
diff --git a/src/journal/journal-dir.c b/src/journal/journal-dir.c
new file mode 100644
index 0000000..689e1f0
--- /dev/null
+++ b/src/journal/journal-dir.c
@@ -0,0 +1,141 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Endocode AG
+
+ 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 "journal-dir.h"
+#include "macro.h"
+#include "util.h"
+
+static int journal_directory_new_steal(char *path, int fd, JournalDirectory **dir) {
+ JournalDirectory *d;
+
+ assert(path);
+ assert(fd >= 0);
+ assert(dir);
+
+ d = new0(JournalDirectory, 1);
+ if (!d)
+ return -ENOMEM;
+ d->path = path;
+ d->fd = fd;
+ d->n_ref = 1;
+ *dir = d;
+ return 0;
+}
+
+int journal_directory_open(const char *path, JournalDirectory **dir)
+{
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ assert(path);
+ assert(dir);
+
+ fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ p = strdup(path);
+ if (!p)
+ return -ENOMEM;
+
+ r = journal_directory_new_steal(p, fd, dir);
+ if (r < 0)
+ return r;
+ p = NULL;
+ fd = -1;
+ return 0;
+}
+
+int journal_directory_new(const char *path, int fd, JournalDirectory **dir)
+{
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int dfd = -1;
+ int r;
+
+ assert(path);
+ assert(fd >= 0);
+ assert(dir);
+
+ dfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (dfd < 0)
+ return -errno;
+
+ p = strdup(path);
+ if (!p)
+ return -ENOMEM;
+
+ r = journal_directory_new_steal(p, dfd, dir);
+ if (r < 0)
+ return r;
+ p = NULL;
+ dfd = -1;
+ return 0;
+}
+
+JournalDirectory *journal_directory_ref(JournalDirectory *dir)
+{
+ assert(dir);
+ assert(dir->n_ref > 0);
+
+ dir->n_ref ++;
+ return dir;
+}
+
+JournalDirectory *journal_directory_unref(JournalDirectory *dir)
+{
+ if (dir) {
+ PROTECT_ERRNO;
+
+ assert(dir->n_ref > 0);
+
+ dir->n_ref --;
+ if (!dir->n_ref) {
+ safe_close(dir->fd);
+ free(dir->path);
+ free(dir);
+ }
+ }
+
+ return NULL;
+}
+
+int journal_directory_opendir(JournalDirectory *dir, DIR **de)
+{
+ int fd;
+ DIR* d;
+
+ assert(dir);
+ assert(de);
+
+ fd = fcntl(dir->fd, F_DUPFD_CLOEXEC, 3);
+ if (fd < 0)
+ return -errno;
+
+ d = fdopendir(fd);
+ if (!d) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ *de = d;
+ return 0;
+}
diff --git a/src/journal/journal-dir.h b/src/journal/journal-dir.h
new file mode 100644
index 0000000..65ae229
--- /dev/null
+++ b/src/journal/journal-dir.h
@@ -0,0 +1,37 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Endocode AG
+
+ 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 <sys/types.h>
+#include <dirent.h>
+
+typedef struct JournalDirectory {
+ char *path;
+ int fd;
+ int n_ref;
+} JournalDirectory;
+
+int journal_directory_open(const char *path, JournalDirectory **dir);
+int journal_directory_new(const char *path, int fd, JournalDirectory **dir);
+JournalDirectory *journal_directory_ref(JournalDirectory *dir);
+JournalDirectory *journal_directory_unref(JournalDirectory *dir);
+int journal_directory_opendir(JournalDirectory *dir, DIR **de);
--
2.1.0
Krzesimir Nowak
2015-06-01 15:29:03 UTC
Permalink
---
Makefile.am | 2 +-
src/journal/sd-journal.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 32e7ca3..ee9c29b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4399,8 +4399,8 @@ systemd_journal_upload_CFLAGS = \
$(LIBCURL_CFLAGS)

systemd_journal_upload_LDADD = \
- libsystemd-internal.la \
libsystemd-journal-internal.la \
+ libsystemd-internal.la \
libsystemd-shared.la \
$(LIBCURL_LIBS)

diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index 630cc3a..debd45c 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -28,6 +28,8 @@
#include <sys/vfs.h>
#include <linux/magic.h>

+#include "bus-error.h"
+#include "bus-util.h"
#include "sd-journal.h"
#include "journal-def.h"
#include "journal-file.h"
@@ -1701,6 +1703,52 @@ fail:
return r;
}

+static int try_journal_fd(sd_journal *j, const char *machine) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_bus_close_unref_ sd_bus *bus = NULL;
+ _cleanup_free_ char *p = NULL;
+ int fd;
+ int r;
+
+ r = sd_bus_default_system(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get D-Bus connection: %m");
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "GetJournal",
+ &error,
+ &reply,
+ "s", machine);
+ if (r < 0) {
+ log_error("Failed to get journal fd from machined: %s", bus_error_message(&error, r));
+ return r;
+ }
+
+ r = sd_bus_message_read(reply, "h", &fd);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (fd < 0)
+ return -ENODATA;
+
+ /* TODO: Just some bogus directory name with machine id in it,
+ * so it will look semi-nicely in logs. Is that alright? */
+ p = strjoin("machine://", machine, "/journal", NULL);
+ if (!p)
+ return -ENOMEM;
+
+ r = add_root_directory_with_fd(j, p, fd);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
_cleanup_free_ char *root = NULL, *class = NULL;
sd_journal *j;
@@ -1731,7 +1779,9 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
j->prefix = root;
root = NULL;

- r = add_search_paths(j);
+ r = try_journal_fd(j, machine);
+ if (r == -ENODATA)
+ r = add_search_paths(j);
if (r < 0)
goto fail;
--
2.1.0
systemd github import bot
2015-06-01 16:02:36 UTC
Permalink
Patchset imported to github.
Pull request:
<https://github.com/systemd-devs/systemd/compare/master...systemd-mailing-devs:1433172543-30632-8-git-send-email-krzesimir%40endocode.com>

--
Generated by https://github.com/haraldh/mail2git
Krzesimir Nowak
2015-06-01 15:29:02 UTC
Permalink
That way we can have access to a file that is not accessible by path,
for example to a file in overlayfs in different mount namespace (which
is the case for rkt pods).
---
src/journal-remote/journal-remote-write.c | 12 +-
src/journal-remote/journal-remote.c | 34 ++--
src/journal/journal-file.c | 108 +++++-----
src/journal/journal-file.h | 10 +-
src/journal/journal-internal.h | 3 +-
src/journal/journal-vacuum.c | 27 +--
src/journal/journal-vacuum.h | 3 +-
src/journal/journal-verify.c | 4 +-
src/journal/journalctl.c | 8 +-
src/journal/journald-server.c | 113 +++++++----
src/journal/sd-journal.c | 324 ++++++++++++++++++++----------
src/journal/test-journal-flush.c | 13 +-
src/journal/test-journal-interleaving.c | 55 +++--
src/journal/test-journal-stream.c | 10 +-
src/journal/test-journal-verify.c | 13 +-
src/journal/test-journal.c | 21 +-
src/shared/util.c | 20 ++
src/shared/util.h | 1 +
18 files changed, 515 insertions(+), 264 deletions(-)

diff --git a/src/journal-remote/journal-remote-write.c b/src/journal-remote/journal-remote-write.c
index 99820fa..0fdeb7f 100644
--- a/src/journal-remote/journal-remote-write.c
+++ b/src/journal-remote/journal-remote-write.c
@@ -59,7 +59,7 @@ static int do_rotate(JournalFile **f, bool compress, bool seal) {
int r = journal_file_rotate(f, compress, seal);
if (r < 0) {
if (*f)
- log_error_errno(r, "Failed to rotate %s: %m", (*f)->path);
+ log_error_errno(r, "Failed to rotate %s/%s: %m", (*f)->directory->path, (*f)->filename);
else
log_error_errno(r, "Failed to create rotated journal: %m");
}
@@ -93,7 +93,7 @@ Writer* writer_free(Writer *w) {
return NULL;

if (w->journal) {
- log_debug("Closing journal file %s.", w->journal->path);
+ log_debug("Closing journal file %s/%s.", w->journal->directory->path, w->journal->filename);
journal_file_close(w->journal);
}

@@ -136,8 +136,8 @@ int writer_write(Writer *w,
assert(iovw->count > 0);

if (journal_file_rotate_suggested(w->journal, 0)) {
- log_info("%s: Journal header limits reached or header out-of-date, rotating",
- w->journal->path);
+ log_info("%s/%s: Journal header limits reached or header out-of-date, rotating",
+ w->journal->directory->path, w->journal->filename);
r = do_rotate(&w->journal, compress, seal);
if (r < 0)
return r;
@@ -151,12 +151,12 @@ int writer_write(Writer *w,
return 1;
}

- log_debug_errno(r, "%s: Write failed, rotating: %m", w->journal->path);
+ log_debug_errno(r, "%s/%s: Write failed, rotating: %m", w->journal->directory->path, w->journal->filename);
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/%s: Successfully rotated journal", w->journal->directory->path, w->journal->filename);

log_debug("Retrying write.");
r = journal_file_append_entry(w->journal, ts, iovw->iovec, iovw->count,
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index 911e2a1..1a4c337 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -162,14 +162,20 @@ static int spawn_getter(const char *getter, const char *url) {
#define filename_escape(s) xescape((s), "/ ")

static int open_output(Writer *w, const char* host) {
- _cleanup_free_ char *_output = NULL;
- const char *output;
+ _cleanup_free_ char *directory = NULL;
+ _cleanup_free_ char *filename = NULL;
+ JournalDirectory *dir;
int r;

switch (arg_split_mode) {
- case JOURNAL_WRITE_SPLIT_NONE:
+ case JOURNAL_WRITE_SPLIT_NONE: {
+ const char *output;
+
output = arg_output ?: REMOTE_JOURNAL_PATH "/remote.journal";
+ directory = dirname_malloc(output);
+ filename = basename_malloc(output);
break;
+ }

case JOURNAL_WRITE_SPLIT_HOST: {
_cleanup_free_ char *name;
@@ -180,13 +186,10 @@ static int open_output(Writer *w, const char* host) {
if (!name)
return log_oom();

- r = asprintf(&_output, "%s/remote-%s.journal",
- arg_output ?: REMOTE_JOURNAL_PATH,
- name);
+ directory = strdup(arg_output ?: REMOTE_JOURNAL_PATH);
+ r = asprintf(&filename, "remote-%s.journal", name);
if (r < 0)
return log_oom();
-
- output = _output;
break;
}

@@ -194,17 +197,24 @@ static int open_output(Writer *w, const char* host) {
assert_not_reached("what?");
}

- r = journal_file_open_reliably(output,
+ r = journal_directory_open(directory, &dir);
+ if (r < 0) {
+ log_error_errno(r, "Failed to open journal directory %s: %m",
+ directory);
+ return r;
+ }
+ r = journal_file_open_reliably(dir, filename,
O_RDWR|O_CREAT, 0640,
arg_compress, arg_seal,
&w->metrics,
w->mmap,
NULL, &w->journal);
+ dir = journal_directory_unref(dir);
if (r < 0)
- log_error_errno(r, "Failed to open output journal %s: %m",
- output);
+ log_error_errno(r, "Failed to open output journal %s/%s: %m",
+ directory, filename);
else
- log_debug("Opened output file %s", w->journal->path);
+ log_debug("Opened output file %s/%s", w->journal->directory->path, w->journal->filename);
return r;
}

diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index be6a552..5acc63c 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -154,8 +154,9 @@ void journal_file_close(JournalFile *f) {
(void) btrfs_defrag_fd(f->fd);
}

+ free(f->filename);
+ journal_directory_unref(f->directory);
safe_close(f->fd);
- free(f->path);

if (f->mmap)
mmap_cache_unref(f->mmap);
@@ -258,12 +259,14 @@ static int journal_file_verify_header(JournalFile *f) {
flags = le32toh(f->header->incompatible_flags);
if (flags & ~HEADER_INCOMPATIBLE_SUPPORTED) {
if (flags & ~HEADER_INCOMPATIBLE_ANY)
- log_debug("Journal file %s has unknown incompatible flags %"PRIx32,
- f->path, flags & ~HEADER_INCOMPATIBLE_ANY);
+ log_debug("Journal file %s/%s has unknown incompatible flags %"PRIx32,
+ f->directory->path, f->filename,
+ flags & ~HEADER_INCOMPATIBLE_ANY);
flags = (flags & HEADER_INCOMPATIBLE_ANY) & ~HEADER_INCOMPATIBLE_SUPPORTED;
if (flags)
- log_debug("Journal file %s uses incompatible flags %"PRIx32
- " disabled at compilation time.", f->path, flags);
+ log_debug("Journal file %s/%s uses incompatible flags %"PRIx32
+ " disabled at compilation time.", f->directory->path,
+ f->filename, flags);
return -EPROTONOSUPPORT;
}

@@ -272,12 +275,14 @@ static int journal_file_verify_header(JournalFile *f) {
flags = le32toh(f->header->compatible_flags);
if (f->writable && (flags & ~HEADER_COMPATIBLE_SUPPORTED)) {
if (flags & ~HEADER_COMPATIBLE_ANY)
- log_debug("Journal file %s has unknown compatible flags %"PRIx32,
- f->path, flags & ~HEADER_COMPATIBLE_ANY);
+ log_debug("Journal file %s/%s has unknown compatible flags %"PRIx32,
+ f->directory->path, f->filename,
+ flags & ~HEADER_COMPATIBLE_ANY);
flags = (flags & HEADER_COMPATIBLE_ANY) & ~HEADER_COMPATIBLE_SUPPORTED;
if (flags)
- log_debug("Journal file %s uses compatible flags %"PRIx32
- " disabled at compilation time.", f->path, flags);
+ log_debug("Journal file %s/%s uses compatible flags %"PRIx32
+ " disabled at compilation time.", f->directory->path,
+ f->filename, flags);
return -EPROTONOSUPPORT;
}

@@ -318,12 +323,14 @@ static int journal_file_verify_header(JournalFile *f) {
state = f->header->state;

if (state == STATE_ONLINE) {
- log_debug("Journal file %s is already online. Assuming unclean closing.", f->path);
+ log_debug("Journal file %s/%s is already online. Assuming unclean closing.",
+ f->directory->path, f->filename);
return -EBUSY;
} else if (state == STATE_ARCHIVED)
return -ESHUTDOWN;
else if (state != STATE_OFFLINE) {
- log_debug("Journal file %s has unknown state %i.", f->path, state);
+ log_debug("Journal file %s/%s has unknown state %i.",
+ f->directory->path, f->filename, state);
return -EBUSY;
}
}
@@ -2128,8 +2135,8 @@ int journal_file_next_entry(

if (p > 0 &&
(direction == DIRECTION_DOWN ? ofs <= p : ofs >= p)) {
- log_debug("%s: entry array corrupted at entry %"PRIu64,
- f->path, i);
+ log_debug("%s/%s: entry array corrupted at entry %"PRIu64,
+ f->directory->path, f->filename, i);
return -EBADMSG;
}

@@ -2453,7 +2460,7 @@ void journal_file_print_header(JournalFile *f) {

assert(f);

- printf("File Path: %s\n"
+ printf("File Path: %s/%s\n"
"File ID: %s\n"
"Machine ID: %s\n"
"Boot ID: %s\n"
@@ -2473,7 +2480,7 @@ void journal_file_print_header(JournalFile *f) {
"Tail Monotonic Timestamp: %s\n"
"Objects: %"PRIu64"\n"
"Entry Objects: %"PRIu64"\n",
- f->path,
+ f->directory->path, f->filename,
sd_id128_to_string(f->header->file_id, a),
sd_id128_to_string(f->header->machine_id, b),
sd_id128_to_string(f->header->boot_id, c),
@@ -2550,15 +2557,16 @@ static int journal_file_warn_btrfs(JournalFile *f) {
return 0;
}

- log_notice("Creating journal file %s on a btrfs file system, and copy-on-write is enabled. "
+ log_notice("Creating journal file %s/%s on a btrfs file system, and copy-on-write is enabled. "
"This is likely to slow down journal access substantially, please consider turning "
- "off the copy-on-write file attribute on the journal directory, using chattr +C.", f->path);
+ "off the copy-on-write file attribute on the journal directory, using chattr +C.", f->directory->path, f->filename);

return 1;
}

int journal_file_open(
- const char *fname,
+ JournalDirectory *dir,
+ const char *filename,
int flags,
mode_t mode,
bool compress,
@@ -2568,20 +2576,21 @@ int journal_file_open(
JournalFile *template,
JournalFile **ret) {

+
bool newly_created = false;
JournalFile *f;
void *h;
int r;

- assert(fname);
+ assert(filename);
assert(ret);

if ((flags & O_ACCMODE) != O_RDONLY &&
(flags & O_ACCMODE) != O_RDWR)
return -EINVAL;

- if (!endswith(fname, ".journal") &&
- !endswith(fname, ".journal~"))
+ if (!endswith(filename, ".journal") &&
+ !endswith(filename, ".journal~"))
return -EINVAL;

f = new0(JournalFile, 1);
@@ -2613,19 +2622,19 @@ int journal_file_open(
}
}

- f->path = strdup(fname);
- if (!f->path) {
+ f->chain_cache = ordered_hashmap_new(&uint64_hash_ops);
+ if (!f->chain_cache) {
r = -ENOMEM;
goto fail;
}

- f->chain_cache = ordered_hashmap_new(&uint64_hash_ops);
- if (!f->chain_cache) {
+ f->filename = strdup(filename);
+ if (!f->filename) {
r = -ENOMEM;
goto fail;
}
-
- f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode);
+ f->directory = journal_directory_ref(dir);
+ f->fd = openat(f->directory->fd, f->filename, f->flags|O_CLOEXEC, f->mode);
if (f->fd < 0) {
r = -errno;
goto fail;
@@ -2770,12 +2779,12 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal) {
if (!old_file->writable)
return -EINVAL;

- if (!endswith(old_file->path, ".journal"))
+ if (!endswith(old_file->filename, ".journal"))
return -EINVAL;

- l = strlen(old_file->path);
+ l = strlen(old_file->filename);
r = asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal",
- (int) l - 8, old_file->path,
+ (int) l - 8, old_file->filename,
SD_ID128_FORMAT_VAL(old_file->header->seqnum_id),
le64toh((*f)->header->head_entry_seqnum),
le64toh((*f)->header->head_entry_realtime));
@@ -2785,7 +2794,8 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal) {
/* Try to rename the file to the archived version. If the file
* already was deleted, we'll get ENOENT, let's ignore that
* case. */
- r = rename(old_file->path, p);
+ r = renameat(old_file->directory->fd, old_file->filename,
+ old_file->directory->fd, p);
if (r < 0 && errno != ENOENT)
return -errno;

@@ -2796,7 +2806,7 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal) {
* we archive them */
old_file->defrag_on_close = true;

- r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, old_file, &new_file);
+ r = journal_file_open(old_file->directory, old_file->filename, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, old_file, &new_file);
journal_file_close(old_file);

*f = new_file;
@@ -2804,7 +2814,8 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal) {
}

int journal_file_open_reliably(
- const char *fname,
+ JournalDirectory *dir,
+ const char *filename,
int flags,
mode_t mode,
bool compress,
@@ -2813,12 +2824,11 @@ int journal_file_open_reliably(
MMapCache *mmap_cache,
JournalFile *template,
JournalFile **ret) {
-
int r;
size_t l;
_cleanup_free_ char *p = NULL;

- r = journal_file_open(fname, flags, mode, compress, seal,
+ r = journal_file_open(dir, filename, flags, mode, compress, seal,
metrics, mmap_cache, template, ret);
if (!IN_SET(r,
-EBADMSG, /* corrupted */
@@ -2837,19 +2847,20 @@ int journal_file_open_reliably(
if (!(flags & O_CREAT))
return r;

- if (!endswith(fname, ".journal"))
+ if (!endswith(filename, ".journal"))
return r;

/* The file is corrupted. Rotate it away and try it again (but only once) */

- l = strlen(fname);
+ l = strlen(filename);
if (asprintf(&p, "%.*s@%016"PRIx64 "-%016"PRIx64 ".journal~",
- (int) l - 8, fname,
+ (int) l - 8, filename,
now(CLOCK_REALTIME),
random_u64()) < 0)
return -ENOMEM;

- r = rename(fname, p);
+ r = renameat(dir->fd, filename,
+ dir->fd, p);
if (r < 0)
return -errno;

@@ -2859,10 +2870,10 @@ int journal_file_open_reliably(
(void) chattr_path(p, false, FS_NOCOW_FL);
(void) btrfs_defrag(p);

- log_warning("File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
+ log_warning("File %s/%s corrupted or uncleanly shut down, renaming and replacing.", dir->path, filename);

- return journal_file_open(fname, flags, mode, compress, seal,
- metrics, mmap_cache, template, ret);
+ return journal_file_open(dir, filename, flags, mode, compress,
+ seal, metrics, mmap_cache, template, ret);
}

int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) {
@@ -3093,7 +3104,8 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec) {
/* If we gained new header fields we gained new features,
* hence suggest a rotation */
if (le64toh(f->header->header_size) < sizeof(Header)) {
- log_debug("%s uses an outdated header, suggesting rotation.", f->path);
+ log_debug("%s/%s uses an outdated header, suggesting rotation.",
+ f->directory->path, f->filename);
return true;
}

@@ -3105,8 +3117,9 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec) {

if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
- log_debug("Data hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items, %llu file size, %"PRIu64" bytes per hash table item), suggesting rotation.",
- f->path,
+ log_debug("Data hash table of %s/%s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items, %llu file size, %"PRIu64" bytes per hash table item), suggesting rotation.",
+ f->directory->path,
+ f->filename,
100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
le64toh(f->header->n_data),
le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
@@ -3117,8 +3130,9 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec) {

if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) {
- log_debug("Field hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items), suggesting rotation.",
- f->path,
+ log_debug("Field hash table of %s/%s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items), suggesting rotation.",
+ f->directory->path,
+ f->filename,
100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
le64toh(f->header->n_fields),
le64toh(f->header->field_hash_table_size) / sizeof(HashItem));
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index 403c8f7..9a00de6 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -34,6 +34,7 @@
#include "macro.h"
#include "mmap-cache.h"
#include "hashmap.h"
+#include "journal-dir.h"

typedef struct JournalMetrics {
uint64_t max_use;
@@ -81,7 +82,8 @@ typedef struct JournalFile {
LocationType location_type;
uint64_t last_n_entries;

- char *path;
+ JournalDirectory *directory;
+ char *filename;
struct stat last_stat;
usec_t last_stat_usec;

@@ -125,7 +127,8 @@ typedef struct JournalFile {
} JournalFile;

int journal_file_open(
- const char *fname,
+ JournalDirectory *dir,
+ const char *filename,
int flags,
mode_t mode,
bool compress,
@@ -139,7 +142,8 @@ int journal_file_set_offline(JournalFile *f);
void journal_file_close(JournalFile *j);

int journal_file_open_reliably(
- const char *fname,
+ JournalDirectory *dir,
+ const char *filename,
int flags,
mode_t mode,
bool compress,
diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h
index b51ecdb..90a0f3f 100644
--- a/src/journal/journal-internal.h
+++ b/src/journal/journal-internal.h
@@ -33,6 +33,7 @@
#include "set.h"
#include "journal-file.h"
#include "sd-journal.h"
+#include "journal-dir.h"

typedef struct Match Match;
typedef struct Location Location;
@@ -78,7 +79,7 @@ struct Location {
};

struct Directory {
- char *path;
+ JournalDirectory *d;
int wd;
bool is_root;
};
diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c
index 81a577e..f1134bb 100644
--- a/src/journal/journal-vacuum.c
+++ b/src/journal/journal-vacuum.c
@@ -67,7 +67,7 @@ static int vacuum_compare(const void *_a, const void *_b) {
}

static void patch_realtime(
- const char *dir,
+ JournalDirectory *dir,
const char *fn,
const struct stat *st,
unsigned long long *realtime) {
@@ -101,14 +101,7 @@ static void patch_realtime(
* unfortunately there's currently no sane API to query
* it. Hence let's implement this manually... */

- /* Unfortunately there is is not fgetxattrat(), so we need to
- * go via path here. :-( */
-
- path = strjoin(dir, "/", fn, NULL);
- if (!path)
- return;
-
- if (path_getcrtime(path, &crtime) >= 0) {
+ if (fd_getcrtime_at(dir->fd, fn, &crtime, 0) >= 0) {
if (crtime < *realtime)
*realtime = crtime;
}
@@ -142,7 +135,7 @@ static int journal_file_empty(int dir_fd, const char *name) {
}

int journal_directory_vacuum(
- const char *directory,
+ JournalDirectory *directory,
uint64_t max_use,
usec_t max_retention_usec,
usec_t *oldest_usec,
@@ -170,9 +163,9 @@ int journal_directory_vacuum(
max_retention_usec = retention_limit = 0;
}

- d = opendir(directory);
- if (!d)
- return -errno;
+ r = journal_directory_opendir(directory, &d);
+ if (r < 0)
+ return r;

for (;;) {
struct dirent *de;
@@ -266,10 +259,10 @@ int journal_directory_vacuum(
uint64_t size = 512UL * (uint64_t) st.st_blocks;

if (unlinkat(dirfd(d), p, 0) >= 0) {
- log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted empty archived journal %s/%s (%s).", directory, p, format_bytes(sbytes, sizeof(sbytes), size));
+ log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted empty archived journal %s/%s (%s).", directory->path, p, format_bytes(sbytes, sizeof(sbytes), size));
freed += size;
} else if (errno != ENOENT)
- log_warning_errno(errno, "Failed to delete empty archived journal %s/%s: %m", directory, p);
+ log_warning_errno(errno, "Failed to delete empty archived journal %s/%s: %m", directory->path, p);

free(p);
continue;
@@ -303,7 +296,7 @@ int journal_directory_vacuum(
break;

if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) {
- log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted archived journal %s/%s (%s).", directory, list[i].filename, format_bytes(sbytes, sizeof(sbytes), list[i].usage));
+ log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted archived journal %s/%s (%s).", directory->path, list[i].filename, format_bytes(sbytes, sizeof(sbytes), list[i].usage));
freed += list[i].usage;

if (list[i].usage < sum)
@@ -312,7 +305,7 @@ int journal_directory_vacuum(
sum = 0;

} else if (errno != ENOENT)
- log_warning_errno(errno, "Failed to delete archived journal %s/%s: %m", directory, list[i].filename);
+ log_warning_errno(errno, "Failed to delete archived journal %s/%s: %m", directory->path, list[i].filename);
}

if (oldest_usec && i < n_list && (*oldest_usec == 0 || list[i].realtime < *oldest_usec))
diff --git a/src/journal/journal-vacuum.h b/src/journal/journal-vacuum.h
index c45cc31..cdcdfcd 100644
--- a/src/journal/journal-vacuum.h
+++ b/src/journal/journal-vacuum.h
@@ -21,5 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/

+#include "journal-dir.h"

-int journal_directory_vacuum(const char *directory, uint64_t max_use, usec_t max_retention_usec, usec_t *oldest_usec, bool vacuum);
+int journal_directory_vacuum(JournalDirectory *directory, uint64_t max_use, usec_t max_retention_usec, usec_t *oldest_usec, bool vacuum);
diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
index ce734d8..347920e 100644
--- a/src/journal/journal-verify.c
+++ b/src/journal/journal-verify.c
@@ -1265,8 +1265,8 @@ fail:
if (show_progress)
flush_progress();

- log_error("File corruption detected at %s:"OFSfmt" (of %llu bytes, %"PRIu64"%%).",
- f->path,
+ log_error("File corruption detected at %s/%s:"OFSfmt" (of %llu bytes, %"PRIu64"%%).",
+ f->directory->path, f->filename,
p,
(unsigned long long) f->last_stat.st_size,
100 * p / f->last_stat.st_size);
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 76ec082..8a2d4a1 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -1576,7 +1576,7 @@ static int verify(sd_journal *j) {

#ifdef HAVE_GCRYPT
if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
- log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
+ log_notice("Journal file %s/%s has sealing enabled but verification key has not been passed using --verify-key=.", f->directory->path, f->filename);
#endif

k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
@@ -1584,11 +1584,11 @@ static int verify(sd_journal *j) {
/* If the key was invalid give up right-away. */
return k;
} else if (k < 0) {
- log_warning("FAIL: %s (%s)", f->path, strerror(-k));
+ log_warning("FAIL: %s/%s (%s)", f->directory->path, f->filename, strerror(-k));
r = k;
} else {
char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
- log_info("PASS: %s", f->path);
+ log_info("PASS: %s/%s", f->directory->path, f->filename);

if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
if (validated > 0) {
@@ -1899,7 +1899,7 @@ int main(int argc, char *argv[]) {
if (d->is_root)
continue;

- q = journal_directory_vacuum(d->path, arg_vacuum_size, arg_vacuum_time, NULL, true);
+ q = journal_directory_vacuum(d->d, arg_vacuum_size, arg_vacuum_time, NULL, true);
if (q < 0) {
log_error_errno(q, "Failed to vacuum: %m");
r = q;
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 3353024..6ec0b8e 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -200,7 +200,8 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid) {

r = fchmod(f->fd, 0640);
if (r < 0)
- log_warning_errno(r, "Failed to fix access mode on %s, ignoring: %m", f->path);
+ log_warning_errno(r, "Failed to fix access mode on %s/%s, ignoring: %m",
+ f->directory->path, f->filename);

#ifdef HAVE_ACL
if (uid <= SYSTEM_UID_MAX)
@@ -208,7 +209,8 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid) {

acl = acl_get_fd(f->fd);
if (!acl) {
- log_warning_errno(errno, "Failed to read ACL on %s, ignoring: %m", f->path);
+ log_warning_errno(errno, "Failed to read ACL on %s/%s, ignoring: %m",
+ f->directory->path, f->filename);
return;
}

@@ -218,7 +220,8 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid) {
if (acl_create_entry(&acl, &entry) < 0 ||
acl_set_tag_type(entry, ACL_USER) < 0 ||
acl_set_qualifier(entry, &uid) < 0) {
- log_warning_errno(errno, "Failed to patch ACL on %s, ignoring: %m", f->path);
+ log_warning_errno(errno, "Failed to patch ACL on %s/%s, ignoring: %m",
+ f->directory->path, f->filename);
goto finish;
}
}
@@ -228,12 +231,13 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid) {
if (acl_get_permset(entry, &permset) < 0 ||
acl_add_perm(permset, ACL_READ) < 0 ||
calc_acl_mask_if_needed(&acl) < 0) {
- log_warning_errno(errno, "Failed to patch ACL on %s, ignoring: %m", f->path);
+ log_warning_errno(errno, "Failed to patch ACL on %s/%s, ignoring: %m",
+ f->directory->path, f->filename);
goto finish;
}

if (acl_set_fd(f->fd, acl) < 0)
- log_warning_errno(errno, "Failed to set ACL on %s, ignoring: %m", f->path);
+ log_warning_errno(errno, "Failed to set ACL on %s/%s, ignoring: %m", f->directory->path, f->filename);

finish:
acl_free(acl);
@@ -241,7 +245,9 @@ finish:
}

static JournalFile* find_journal(Server *s, uid_t uid) {
- _cleanup_free_ char *p = NULL;
+ _cleanup_free_ char *directory = NULL;
+ _cleanup_free_ char *filename = NULL;
+ JournalDirectory *dir;
int r;
JournalFile *f;
sd_id128_t machine;
@@ -267,8 +273,15 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
if (f)
return f;

- if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/user-"UID_FMT".journal",
- SD_ID128_FORMAT_VAL(machine), uid) < 0)
+ if (asprintf(&directory, "/var/log/journal/" SD_ID128_FORMAT_STR,
+ SD_ID128_FORMAT_VAL(machine)) < 0)
+ return s->system_journal;
+
+ if (asprintf(&filename, "user-" UID_FMT ".journal", uid) < 0)
+ return s->system_journal;
+
+ r = journal_directory_open(directory, &dir);
+ if (r < 0)
return s->system_journal;

while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) {
@@ -278,7 +291,8 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
journal_file_close(f);
}

- r = journal_file_open_reliably(p, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &f);
+ r = journal_file_open_reliably(dir, filename, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &f);
+ dir = journal_directory_unref(dir);
if (r < 0)
return s->system_journal;

@@ -309,7 +323,8 @@ static int do_rotate(
r = journal_file_rotate(f, s->compress, seal);
if (r < 0)
if (*f)
- log_error_errno(r, "Failed to rotate %s: %m", (*f)->path);
+ log_error_errno(r, "Failed to rotate %s/%s: %m",
+ (*f)->directory->path, (*f)->filename);
else
log_error_errno(r, "Failed to create new %s journal: %m", name);
else
@@ -374,13 +389,20 @@ static void do_vacuum(
JournalMetrics *metrics) {

const char *p;
+ JournalDirectory *dir;
int r;

if (!f)
return;

p = strjoina(path, id);
- r = journal_directory_vacuum(p, metrics->max_use, s->max_retention_usec, &s->oldest_file_usec, false);
+ r = journal_directory_open(p, &dir);
+ if (r < 0 && r != -ENOENT) {
+ log_error_errno(r, "Failed to vacuum %s: %m", p);
+ return;
+ }
+ r = journal_directory_vacuum(dir, metrics->max_use, s->max_retention_usec, &s->oldest_file_usec, false);
+ dir = journal_directory_unref(dir);
if (r < 0 && r != -ENOENT)
log_error_errno(r, "Failed to vacuum %s: %m", p);
}
@@ -467,19 +489,19 @@ static bool shall_try_append_again(JournalFile *f, int r) {
-EIDRM Journal file has been deleted */

if (r == -E2BIG || r == -EFBIG || r == -EDQUOT || r == -ENOSPC)
- log_debug("%s: Allocation limit reached, rotating.", f->path);
+ log_debug("%s/%s: Allocation limit reached, rotating.", f->directory->path, f->filename);
else if (r == -EHOSTDOWN)
- log_info("%s: Journal file from other machine, rotating.", f->path);
+ log_info("%s/%s: Journal file from other machine, rotating.", f->directory->path, f->filename);
else if (r == -EBUSY)
- log_info("%s: Unclean shutdown, rotating.", f->path);
+ log_info("%s/%s: Unclean shutdown, rotating.", f->directory->path, f->filename);
else if (r == -EPROTONOSUPPORT)
- log_info("%s: Unsupported feature, rotating.", f->path);
+ log_info("%s/%s: Unsupported feature, rotating.", f->directory->path, f->filename);
else if (r == -EBADMSG || r == -ENODATA || r == ESHUTDOWN)
- log_warning("%s: Journal file corrupted, rotating.", f->path);
+ log_warning("%s/%s: Journal file corrupted, rotating.", f->directory->path, f->filename);
else if (r == -EIO)
- log_warning("%s: IO error, rotating.", f->path);
+ log_warning("%s/%s: IO error, rotating.", f->directory->path, f->filename);
else if (r == -EIDRM)
- log_warning("%s: Journal file has been deleted, rotating.", f->path);
+ log_warning("%s/%s: Journal file has been deleted, rotating.", f->directory->path, f->filename);
else
return false;

@@ -500,7 +522,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned
return;

if (journal_file_rotate_suggested(f, s->max_file_usec)) {
- log_debug("%s: Journal header limits reached or header out-of-date, rotating.", f->path);
+ log_debug("%s/%s: Journal header limits reached or header out-of-date, rotating.", f->directory->path, f->filename);
server_rotate(s);
server_vacuum(s);
vacuumed = true;
@@ -936,6 +958,8 @@ static int system_journal_open(Server *s, bool flush_requested) {
(flush_requested
|| access("/run/systemd/journal/flushed", F_OK) >= 0)) {

+ JournalDirectory *dir;
+
/* If in auto mode: first try to create the machine
* path, but not the prefix.
*
@@ -947,54 +971,75 @@ static int system_journal_open(Server *s, bool flush_requested) {

fn = strjoina("/var/log/journal/", ids);
(void) mkdir(fn, 0755);
+ r = journal_directory_open(fn, &dir);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to open system journal: %m");
+ } else {

- fn = strjoina(fn, "/system.journal");
- r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &s->system_journal);
+ r = journal_file_open_reliably(dir, "system.journal", O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &s->system_journal);
+ dir = journal_directory_unref(dir);

- if (r >= 0)
- server_fix_perms(s, s->system_journal, 0);
- else if (r < 0) {
- if (r != -ENOENT && r != -EROFS)
- log_warning_errno(r, "Failed to open system journal: %m");
+ if (r >= 0)
+ server_fix_perms(s, s->system_journal, 0);
+ else if (r < 0) {
+ if (r != -ENOENT && r != -EROFS)
+ log_warning_errno(r, "Failed to open system journal: %m");

- r = 0;
+ r = 0;
+ }
}
}

if (!s->runtime_journal &&
(s->storage != STORAGE_NONE)) {

- fn = strjoin("/run/log/journal/", ids, "/system.journal", NULL);
+ fn = strjoin("/run/log/journal/", ids, NULL);
if (!fn)
return -ENOMEM;

if (s->system_journal) {

+ JournalDirectory *dir;
+
/* Try to open the runtime journal, but only
* if it already exists, so that we can flush
* it into the system journal */

- r = journal_file_open(fn, O_RDWR, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
- free(fn);
-
+ r = journal_directory_open(fn, &dir);
if (r < 0) {
if (r != -ENOENT)
log_warning_errno(r, "Failed to open runtime journal: %m");
-
r = 0;
+ } else {
+ r = journal_file_open(dir, "system.journal", O_RDWR, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
+ free(fn);
+ dir = journal_directory_unref(dir);
+
+ if (r < 0) {
+ if (r != -ENOENT)
+ log_warning_errno(r, "Failed to open runtime journal: %m");
+
+ r = 0;
+ }
}

} else {

+ JournalDirectory *dir;
+
/* OK, we really need the runtime journal, so create
* it if necessary. */

(void) mkdir("/run/log", 0755);
(void) mkdir("/run/log/journal", 0755);
- (void) mkdir_parents(fn, 0750);
+ (void) mkdir_p(fn, 0750);

- r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
+ r = journal_directory_open(fn, &dir);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open runtime journal: %m");
+ r = journal_file_open_reliably(dir, "system.journal", O_RDWR|O_CREAT, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
free(fn);
+ dir = journal_directory_unref(dir);

if (r < 0)
return log_error_errno(r, "Failed to open runtime journal: %m");
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index 9f0f71a..630cc3a 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -798,7 +798,8 @@ static int real_journal_next(sd_journal *j, direction_t direction) {

r = next_beyond_location(j, f, direction);
if (r < 0) {
- log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
+ log_debug_errno(r, "Can't iterate through %s/%s, ignoring: %m",
+ f->directory->path, f->filename);
remove_file_real(j, f);
continue;
} else if (r == 0) {
@@ -1194,12 +1195,19 @@ static bool file_type_wanted(int flags, const char *filename) {
return false;
}

-static int add_any_file(sd_journal *j, const char *path) {
+static int add_any_file(sd_journal *j, JournalDirectory *d, const char *filename) {
+ _cleanup_free_ char *path = NULL;
JournalFile *f = NULL;
int r;
+ _cleanup_free_ char *fpath = NULL;

assert(j);
- assert(path);
+ assert(d);
+ assert(filename);
+
+ path = strjoin(d->path, "/", filename, NULL);
+ if (!path)
+ return -ENOMEM;

if (ordered_hashmap_get(j->files, path))
return 0;
@@ -1209,19 +1217,23 @@ static int add_any_file(sd_journal *j, const char *path) {
return set_put_error(j, -ETOOMANYREFS);
}

- r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
+ r = journal_file_open(d, filename, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
if (r < 0)
return r;

/* journal_file_dump(f); */

- r = ordered_hashmap_put(j->files, f->path, f);
+ fpath = strjoin(f->directory->path, "/", f->filename, NULL);
+ if (!fpath)
+ return -ENOMEM;
+ r = ordered_hashmap_put(j->files, fpath, f);
if (r < 0) {
journal_file_close(f);
return r;
}
+ fpath = NULL;

- log_debug("File %s added.", f->path);
+ log_debug("File %s/%s added.", f->directory->path, f->filename);

check_network(j, f->fd);

@@ -1230,37 +1242,32 @@ static int add_any_file(sd_journal *j, const char *path) {
return 0;
}

-static int add_file(sd_journal *j, const char *prefix, const char *filename) {
- _cleanup_free_ char *path = NULL;
+static int add_file(sd_journal *j, JournalDirectory *d, const char *filename) {
int r;

assert(j);
- assert(prefix);
+ assert(d);
assert(filename);

if (j->no_new_files ||
!file_type_wanted(j->flags, filename))
return 0;

- path = strjoin(prefix, "/", filename, NULL);
- if (!path)
- return -ENOMEM;
-
- r = add_any_file(j, path);
+ r = add_any_file(j, d, filename);
if (r == -ENOENT)
return 0;
return r;
}

-static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
+static int remove_file(sd_journal *j, JournalDirectory *d, const char *filename) {
_cleanup_free_ char *path;
JournalFile *f;

assert(j);
- assert(prefix);
+ assert(d);
assert(filename);

- path = strjoin(prefix, "/", filename, NULL);
+ path = strjoin(d->path, "/", filename, NULL);
if (!path)
return -ENOMEM;

@@ -1273,12 +1280,21 @@ static int remove_file(sd_journal *j, const char *prefix, const char *filename)
}

static void remove_file_real(sd_journal *j, JournalFile *f) {
+ char *fpath;
+ _cleanup_free_ char *path = NULL;
+
assert(j);
assert(f);

- ordered_hashmap_remove(j->files, f->path);
+ path = strjoin(f->directory->path, "/", f->filename, NULL);
+ if (!path) {
+ log_warning("Failed to remove %s/%s.", f->directory->path, f->filename);
+ return;
+ }
+ ordered_hashmap_remove2(j->files, path, (void**)&fpath);
+ free(fpath);

- log_debug("File %s removed.", f->path);
+ log_debug("File %s removed.", path);

if (j->current_file == f) {
j->current_file = NULL;
@@ -1287,7 +1303,7 @@ static void remove_file_real(sd_journal *j, JournalFile *f) {

if (j->unique_file == f) {
/* Jump to the next unique_file or NULL if that one was last */
- j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
+ j->unique_file = ordered_hashmap_next(j->files, path);
j->unique_offset = 0;
if (!j->unique_file)
j->unique_file_lost = true;
@@ -1298,7 +1314,39 @@ static void remove_file_real(sd_journal *j, JournalFile *f) {
j->current_invalidate_counter ++;
}

-static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
+static int new_directory(const char* path, int fd, bool is_root, Directory **ret) {
+ Directory *m;
+ JournalDirectory *d;
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ r = journal_directory_new(path, fd, &d);
+ if (r < 0)
+ return r;
+
+ m = new0(Directory, 1);
+ if (!m) {
+ d = journal_directory_unref(d);
+ return -ENOMEM;
+ }
+
+ m->is_root = is_root;
+ m->d = d;
+ *ret = m;
+
+ return 0;
+}
+
+static void free_directory(Directory *d) {
+ if (d) {
+ journal_directory_unref(d->d);
+ free(d);
+ }
+}
+
+static int add_directory(sd_journal *j, JournalDirectory *directory, const char *dirname) {
_cleanup_free_ char *path = NULL;
int r;
_cleanup_closedir_ DIR *d = NULL;
@@ -1306,54 +1354,59 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
Directory *m;

assert(j);
- assert(prefix);
+ assert(directory);
assert(dirname);

- log_debug("Considering %s/%s.", prefix, dirname);
+ log_debug("Considering %s/%s.", directory->path, dirname);

if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
(sd_id128_from_string(dirname, &id) < 0 ||
sd_id128_get_machine(&mid) < 0 ||
- !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
+ !(sd_id128_equal(id, mid) || path_startswith(directory->path, "/run"))))
return 0;

- path = strjoin(prefix, "/", dirname, NULL);
+ path = strjoin(directory->path, "/", dirname, NULL);
if (!path)
return -ENOMEM;

- d = opendir(path);
- if (!d) {
- log_debug_errno(errno, "Failed to open %s: %m", path);
- if (errno == ENOENT)
- return 0;
- return -errno;
- }
-
m = hashmap_get(j->directories_by_path, path);
if (!m) {
- m = new0(Directory, 1);
- if (!m)
- return -ENOMEM;
+ _cleanup_close_ int fd = -1;

- m->is_root = false;
- m->path = path;
+ fd = openat(directory->fd, dirname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;

- if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
- free(m);
+ r = new_directory(path, fd, false, &m);
+ if (r < 0)
+ return r;
+
+ if (hashmap_put(j->directories_by_path, m->d->path, m) < 0) {
+ free_directory(m);
return -ENOMEM;
}

- path = NULL; /* avoid freeing in cleanup */
j->current_invalidate_counter ++;

- log_debug("Directory %s added.", m->path);
+ log_debug("Directory %s added.", m->d->path);

} else if (m->is_root)
return 0;

+ r = journal_directory_opendir(m->d, &d);
+ if (r < 0) {
+ log_debug_errno(errno, "Failed to open %s: %m", m->d->path);
+ if (r == -ENOENT)
+ return 0;
+ return r;
+ }
+
+ /* TODO: we can't watch changes happening in overlayfs in
+ other mount namespace, unless we get something like
+ inotify_add_watch_dirfd. */
if (m->wd <= 0 && j->inotify_fd >= 0) {

- m->wd = inotify_add_watch(j->inotify_fd, m->path,
+ m->wd = inotify_add_watch(j->inotify_fd, m->d->path,
IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
IN_ONLYDIR);
@@ -1369,7 +1422,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
de = readdir(d);
if (!de && errno != 0) {
r = -errno;
- log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
+ log_debug_errno(errno, "Failed to read directory %s: %m", m->d->path);
return r;
}
if (!de)
@@ -1377,10 +1430,10 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)

if (dirent_is_file_with_suffix(de, ".journal") ||
dirent_is_file_with_suffix(de, ".journal~")) {
- r = add_file(j, m->path, de->d_name);
+ r = add_file(j, m->d, de->d_name);
if (r < 0) {
log_debug_errno(r, "Failed to add file %s/%s: %m",
- m->path, de->d_name);
+ m->d->path, de->d_name);
r = set_put_error(j, r);
if (r < 0)
return r;
@@ -1393,54 +1446,43 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
return 0;
}

-static int add_root_directory(sd_journal *j, const char *p) {
+static int add_root_directory_with_fd(sd_journal *j, const char *p, int fd) {
_cleanup_closedir_ DIR *d = NULL;
Directory *m;
int r;

assert(j);
assert(p);
-
- if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
- !path_startswith(p, "/run"))
- return -EINVAL;
-
- if (j->prefix)
- p = strjoina(j->prefix, p);
-
- d = opendir(p);
- if (!d)
- return -errno;
+ assert(fd >= 0);

m = hashmap_get(j->directories_by_path, p);
if (!m) {
- m = new0(Directory, 1);
- if (!m)
- return -ENOMEM;
-
- m->is_root = true;
- m->path = strdup(p);
- if (!m->path) {
- free(m);
- return -ENOMEM;
- }
+ r = new_directory(p, fd, true, &m);
+ if (r < 0)
+ return r;

- if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
- free(m->path);
- free(m);
+ if (hashmap_put(j->directories_by_path, m->d->path, m) < 0) {
+ free_directory(m);
return -ENOMEM;
}

j->current_invalidate_counter ++;

- log_debug("Root directory %s added.", m->path);
+ log_debug("Root directory %s added.", m->d->path);

} else if (!m->is_root)
return 0;

+ r = journal_directory_opendir(m->d, &d);
+ if (r < 0)
+ return r;
+
+ /* TODO: we can't watch changes happening in overlayfs in
+ other mount namespace, unless we get something like
+ inotify_add_watch_dirfd. */
if (m->wd <= 0 && j->inotify_fd >= 0) {

- m->wd = inotify_add_watch(j->inotify_fd, m->path,
+ m->wd = inotify_add_watch(j->inotify_fd, m->d->path,
IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
IN_ONLYDIR);

@@ -1459,7 +1501,7 @@ static int add_root_directory(sd_journal *j, const char *p) {
de = readdir(d);
if (!de && errno != 0) {
r = -errno;
- log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
+ log_debug_errno(errno, "Failed to read directory %s: %m", m->d->path);
return r;
}
if (!de)
@@ -1467,10 +1509,10 @@ static int add_root_directory(sd_journal *j, const char *p) {

if (dirent_is_file_with_suffix(de, ".journal") ||
dirent_is_file_with_suffix(de, ".journal~")) {
- r = add_file(j, m->path, de->d_name);
+ r = add_file(j, m->d, de->d_name);
if (r < 0) {
log_debug_errno(r, "Failed to add file %s/%s: %m",
- m->path, de->d_name);
+ m->d->path, de->d_name);
r = set_put_error(j, r);
if (r < 0)
return r;
@@ -1478,9 +1520,9 @@ static int add_root_directory(sd_journal *j, const char *p) {
} else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
sd_id128_from_string(de->d_name, &id) >= 0) {

- r = add_directory(j, m->path, de->d_name);
+ r = add_directory(j, m->d, de->d_name);
if (r < 0)
- log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
+ log_debug_errno(r, "Failed to add directory %s/%s: %m", m->d->path, de->d_name);
}
}

@@ -1489,6 +1531,26 @@ static int add_root_directory(sd_journal *j, const char *p) {
return 0;
}

+static int add_root_directory(sd_journal *j, const char *p) {
+ _cleanup_close_ int fd = -1;
+
+ assert(j);
+ assert(p);
+
+ if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
+ !path_startswith(p, "/run"))
+ return -EINVAL;
+
+ if (j->prefix)
+ p = strjoina(j->prefix, p);
+
+ fd = open(p, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return add_root_directory_with_fd(j, p, fd);
+}
+
static int remove_directory(sd_journal *j, Directory *d) {
assert(j);

@@ -1499,15 +1561,14 @@ static int remove_directory(sd_journal *j, Directory *d) {
inotify_rm_watch(j->inotify_fd, d->wd);
}

- hashmap_remove(j->directories_by_path, d->path);
+ hashmap_remove(j->directories_by_path, d->d->path);

if (d->is_root)
- log_debug("Root directory %s removed.", d->path);
+ log_debug("Root directory %s removed.", d->d->path);
else
- log_debug("Directory %s removed.", d->path);
+ log_debug("Directory %s removed.", d->d->path);

- free(d->path);
- free(d);
+ free_directory(d);

return 0;
}
@@ -1539,6 +1600,7 @@ static int add_search_paths(sd_journal *j) {
static int add_current_paths(sd_journal *j) {
Iterator i;
JournalFile *f;
+ const char *p;

assert(j);
assert(j->no_new_files);
@@ -1547,11 +1609,11 @@ static int add_current_paths(sd_journal *j) {
* "root" directories. We don't expect errors here, so we
* treat them as fatal. */

- ORDERED_HASHMAP_FOREACH(f, j->files, i) {
+ ORDERED_HASHMAP_FOREACH_KEY(f, p, j->files, i) {
_cleanup_free_ char *dir;
int r;

- dir = dirname_malloc(f->path);
+ dir = dirname_malloc(p);
if (!dir)
return -ENOMEM;

@@ -1712,6 +1774,8 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla
sd_journal *j;
const char **path;
int r;
+ Hashmap *dirs;
+ Directory *dir;

assert_return(ret, -EINVAL);
assert_return(flags == 0, -EINVAL);
@@ -1719,15 +1783,53 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla
j = journal_new(flags, NULL);
if (!j)
return -ENOMEM;
+ dirs = hashmap_new(&string_hash_ops);
+ if (!dirs) {
+ r = -ENOMEM;
+ goto fail;
+ }

STRV_FOREACH(path, paths) {
- r = add_any_file(j, *path);
+ _cleanup_free_ char *d = NULL;
+ _cleanup_free_ char *b = NULL;
+
+ d = dirname_malloc(*path);
+ b = basename_malloc(*path);
+ if (!d || !b)
+ goto fail;
+
+ dir = hashmap_get(dirs, d);
+ if (!dir) {
+ _cleanup_close_ int dirfd = -1;
+
+ dirfd = open(d, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+ if (dirfd < 0) {
+ r = -errno;
+ log_error_errno(errno, "Failed to open directory %s: %m", d);
+ goto fail;
+ }
+ r = new_directory(d, dirfd, false, &dir);
+ if (r < 0)
+ goto fail;
+
+ r = hashmap_put(dirs, dir->d->path, dir);
+ if (r < 0) {
+ free_directory(dir);
+ goto fail;
+ }
+ }
+
+ r = add_any_file(j, dir->d, b);
if (r < 0) {
log_error_errno(r, "Failed to open %s: %m", *path);
goto fail;
}
}

+ while ((dir = hashmap_steal_first(dirs)))
+ free_directory(dir);
+ hashmap_free(dirs);
+
j->no_new_files = true;

*ret = j;
@@ -1736,20 +1838,30 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla
fail:
sd_journal_close(j);

+ while ((dir = hashmap_steal_first(dirs)))
+ free_directory(dir);
+ hashmap_free(dirs);
+
return r;
}

_public_ void sd_journal_close(sd_journal *j) {
Directory *d;
- JournalFile *f;

if (!j)
return;

sd_journal_flush_matches(j);

- while ((f = ordered_hashmap_steal_first(j->files)))
+ while (!ordered_hashmap_isempty(j->files)) {
+ char *p;
+ JournalFile *f;
+
+ p = ordered_hashmap_first_key(j->files);
+ f = ordered_hashmap_steal_first(j->files);
+ free(p);
journal_file_close(f);
+ }

ordered_hashmap_free(j->files);

@@ -2129,18 +2241,18 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
/* Event for a journal file */

if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
- r = add_file(j, d->path, e->name);
+ r = add_file(j, d->d, e->name);
if (r < 0) {
log_debug_errno(r, "Failed to add file %s/%s: %m",
- d->path, e->name);
+ d->d->path, e->name);
set_put_error(j, r);
}

} else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {

- r = remove_file(j, d->path, e->name);
+ r = remove_file(j, d->d, e->name);
if (r < 0)
- log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
+ log_debug_errno(r, "Failed to remove file %s/%s: %m", d->d->path, e->name);
}

} else if (!d->is_root && e->len == 0) {
@@ -2150,7 +2262,7 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
r = remove_directory(j, d);
if (r < 0)
- log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
+ log_debug_errno(r, "Failed to remove directory %s: %m", d->d->path);
}


@@ -2159,9 +2271,9 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
/* Event for root directory */

if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
- r = add_directory(j, d->path, e->name);
+ r = add_directory(j, d->d, e->name);
if (r < 0)
- log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
+ log_debug_errno(r, "Failed to add directory %s/%s: %m", d->d->path, e->name);
}
}

@@ -2447,7 +2559,12 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_

/* We reached the end of the list? Then start again, with the next file */
if (j->unique_offset == 0) {
- j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
+ char *p = strjoin(j->unique_file->directory->path, "/", j->unique_file->filename, NULL);
+
+ if (!p)
+ return -ENOMEM;
+ j->unique_file = ordered_hashmap_next(j->files, p);
+ free(p);
if (!j->unique_file)
return 0;

@@ -2463,8 +2580,9 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_

/* 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,
+ log_debug("%s/%s:offset " OFSfmt ": object has type %d, expected %d",
+ j->unique_file->directory->path, j->unique_file->filename,
+ j->unique_offset,
o->object.type, OBJECT_DATA);
return -EBADMSG;
}
@@ -2475,15 +2593,17 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_

/* 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,
+ log_debug("%s/%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
+ j->unique_file->directory->path, j->unique_file->filename,
+ 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,
+ log_debug("%s/%s:offset " OFSfmt ": object does not start with \"%s=\"",
+ j->unique_file->directory->path, j->unique_file->filename,
+ j->unique_offset,
j->unique_field);
return -EBADMSG;
}
diff --git a/src/journal/test-journal-flush.c b/src/journal/test-journal-flush.c
index 2d4f531..97080fb 100644
--- a/src/journal/test-journal-flush.c
+++ b/src/journal/test-journal-flush.c
@@ -27,17 +27,21 @@
#include "journal-internal.h"

int main(int argc, char *argv[]) {
- _cleanup_free_ char *fn = NULL;
+ const char *fn;
char dn[] = "/var/tmp/test-journal-flush.XXXXXX";
+ JournalDirectory *dir;
JournalFile *new_journal = NULL;
sd_journal *j = NULL;
unsigned n = 0;
int r;

assert_se(mkdtemp(dn));
- fn = strappend(dn, "/test.journal");
+ fn = "test.journal";

- r = journal_file_open(fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, &new_journal);
+ r = journal_directory_open(dn, &dir);
+ assert_se(r >= 0);
+
+ r = journal_file_open(dir, fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, &new_journal);
assert_se(r >= 0);

r = sd_journal_open(&j, 0);
@@ -67,7 +71,8 @@ int main(int argc, char *argv[]) {

journal_file_close(new_journal);

- unlink(fn);
+ unlinkat(dir->fd, fn, 0);
+ dir = journal_directory_unref(dir);
assert_se(rmdir(dn) == 0);

return 0;
diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c
index c2fc123..1bb965f 100644
--- a/src/journal/test-journal-interleaving.c
+++ b/src/journal/test-journal-interleaving.c
@@ -49,16 +49,27 @@ noreturn static void log_assert_errno(const char *text, int eno, const char *fil
log_assert_errno(#expr, -_r_, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
} while (false)

-static JournalFile *test_open(const char *name) {
+static JournalFile *test_open(JournalDirectory *dir, const char *filename) {
JournalFile *f;
- assert_ret(journal_file_open(name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, &f));
+ assert_ret(journal_file_open(dir, filename, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, &f));
return f;
}

+static JournalDirectory *test_open_dir(const char *dirname) {
+ JournalDirectory *dir;
+
+ assert_ret(journal_directory_open(dirname, &dir));
+ return dir;
+}
+
static void test_close(JournalFile *f) {
journal_file_close (f);
}

+static void test_close_dir(JournalDirectory *dir) {
+ journal_directory_unref(dir);
+}
+
static void append_number(JournalFile *f, int n, uint64_t *seqnum) {
char *p;
dual_timestamp ts;
@@ -115,39 +126,48 @@ static void test_check_numbers_up (sd_journal *j, int count) {

}

-static void setup_sequential(void) {
+static JournalDirectory* setup_sequential(void) {
JournalFile *one, *two;
- one = test_open("one.journal");
- two = test_open("two.journal");
+ JournalDirectory *dir;
+
+ dir = test_open_dir(".");
+ one = test_open(dir, "one.journal");
+ two = test_open(dir, "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);
+ return dir;
}

-static void setup_interleaved(void) {
+static JournalDirectory* setup_interleaved(void) {
JournalFile *one, *two;
- one = test_open("one.journal");
- two = test_open("two.journal");
+ JournalDirectory *dir;
+
+ dir = test_open_dir(".");
+ one = test_open(dir, "one.journal");
+ two = test_open(dir, "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);
+ return dir;
}

-static void test_skip(void (*setup)(void)) {
+static void test_skip(JournalDirectory* (*setup)(void)) {
char t[] = "/tmp/journal-skip-XXXXXX";
sd_journal *j;
int r;
+ JournalDirectory *dir;

assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);

- setup();
+ dir = setup();

/* Seek to head, iterate down.
*/
@@ -188,10 +208,11 @@ static void test_skip(void (*setup)(void)) {
if (arg_keep)
log_info("Not removing %s", t);
else {
- journal_directory_vacuum(".", 3000000, 0, NULL, true);
+ journal_directory_vacuum(dir, 3000000, 0, NULL, true);

assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
+ test_close_dir(dir);

puts("------------------------------------------------------------");
}
@@ -200,14 +221,15 @@ static void test_sequence_numbers(void) {

char t[] = "/tmp/journal-seq-XXXXXX";
JournalFile *one, *two;
+ JournalDirectory *dir;
uint64_t seqnum = 0;
sd_id128_t seqnum_id;

assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);

- assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0644,
- true, false, NULL, NULL, NULL, &one) == 0);
+ dir = test_open_dir(".");
+ one = test_open(dir, "one.journal");

append_number(one, 1, &seqnum);
printf("seqnum=%"PRIu64"\n", seqnum);
@@ -223,7 +245,7 @@ static void test_sequence_numbers(void) {

memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t));

- assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0644,
+ assert_se(journal_file_open(dir, "two.journal", O_RDWR|O_CREAT, 0644,
true, false, NULL, NULL, one, &two) == 0);

assert_se(two->header->state == STATE_ONLINE);
@@ -254,7 +276,7 @@ static void test_sequence_numbers(void) {
/* restart server */
seqnum = 0;

- assert_se(journal_file_open("two.journal", O_RDWR, 0,
+ assert_se(journal_file_open(dir, "two.journal", O_RDWR, 0,
true, false, NULL, NULL, NULL, &two) == 0);

assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id));
@@ -273,10 +295,11 @@ static void test_sequence_numbers(void) {
if (arg_keep)
log_info("Not removing %s", t);
else {
- journal_directory_vacuum(".", 3000000, 0, NULL, true);
+ journal_directory_vacuum(dir, 3000000, 0, NULL, true);

assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
+ test_close_dir(dir);
}

int main(int argc, char *argv[]) {
diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c
index e1146c6..ba5a0a8 100644
--- a/src/journal/test-journal-stream.c
+++ b/src/journal/test-journal-stream.c
@@ -74,6 +74,7 @@ static void verify_contents(sd_journal *j, unsigned skip) {

int main(int argc, char *argv[]) {
JournalFile *one, *two, *three;
+ JournalDirectory *dir;
char t[] = "/tmp/journal-stream-XXXXXX";
unsigned i;
_cleanup_journal_close_ sd_journal *j = NULL;
@@ -90,9 +91,12 @@ int main(int argc, char *argv[]) {
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);

- assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, &one) == 0);
- assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, &two) == 0);
- assert_se(journal_file_open("three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, &three) == 0);
+ assert_se(journal_directory_open(".", &dir) == 0);
+
+ assert_se(journal_file_open(dir, "one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, &one) == 0);
+ assert_se(journal_file_open(dir, "two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, &two) == 0);
+ assert_se(journal_file_open(dir, "three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, &three) == 0);
+ dir = journal_directory_unref(dir);

for (i = 0; i < N_ENTRIES; i++) {
char *p, *q;
diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c
index d24502d..2160b8d 100644
--- a/src/journal/test-journal-verify.c
+++ b/src/journal/test-journal-verify.c
@@ -52,11 +52,11 @@ static void bit_toggle(const char *fn, uint64_t p) {
safe_close(fd);
}

-static int raw_verify(const char *fn, const char *verification_key) {
+static int raw_verify(JournalDirectory *dir, const char *fn, const char *verification_key) {
JournalFile *f;
int r;

- r = journal_file_open(fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, &f);
+ r = journal_file_open(dir, fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, &f);
if (r < 0)
return r;

@@ -70,6 +70,7 @@ int main(int argc, char *argv[]) {
char t[] = "/tmp/journal-XXXXXX";
unsigned n;
JournalFile *f;
+ JournalDirectory *dir;
const char *verification_key = argv[1];
usec_t from = 0, to = 0, total = 0;
char a[FORMAT_TIMESTAMP_MAX];
@@ -89,7 +90,8 @@ int main(int argc, char *argv[]) {

log_info("Generating...");

- assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, &f) == 0);
+ assert_se(journal_directory_open(".", &dir) == 0);
+ assert_se(journal_file_open(dir, "test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, &f) == 0);

for (n = 0; n < N_ENTRIES; n++) {
struct iovec iovec;
@@ -112,7 +114,7 @@ int main(int argc, char *argv[]) {

log_info("Verifying...");

- assert_se(journal_file_open("test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, &f) == 0);
+ assert_se(journal_file_open(dir, "test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, &f) == 0);
/* journal_file_print_header(f); */
journal_file_dump(f);

@@ -137,7 +139,7 @@ int main(int argc, char *argv[]) {

log_info("[ %"PRIu64"+%"PRIu64"]", p / 8, p % 8);

- if (raw_verify("test.journal", verification_key) >= 0)
+ if (raw_verify(dir, "test.journal", verification_key) >= 0)
log_notice(ANSI_HIGHLIGHT_RED_ON ">>>> %"PRIu64" (bit %"PRIu64") can be toggled without detection." ANSI_HIGHLIGHT_OFF, p / 8, p % 8);

bit_toggle("test.journal", p);
@@ -145,6 +147,7 @@ int main(int argc, char *argv[]) {
}

log_info("Exiting...");
+ dir = journal_directory_unref(dir);

assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);

diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c
index caaab25..8e6d7bb 100644
--- a/src/journal/test-journal.c
+++ b/src/journal/test-journal.c
@@ -33,6 +33,7 @@ static bool arg_keep = false;
static void test_non_empty(void) {
dual_timestamp ts;
JournalFile *f;
+ JournalDirectory *dir;
struct iovec iovec;
static const char test[] = "TEST1=1", test2[] = "TEST2=2";
Object *o;
@@ -44,7 +45,8 @@ static void test_non_empty(void) {
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);

- assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, &f) == 0);
+ assert_se(journal_directory_open(".", &dir) == 0);
+ assert_se(journal_file_open(dir, "test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, &f) == 0);

dual_timestamp_get(&ts);

@@ -116,16 +118,18 @@ static void test_non_empty(void) {
if (arg_keep)
log_info("Not removing %s", t);
else {
- journal_directory_vacuum(".", 3000000, 0, NULL, true);
+ journal_directory_vacuum(dir, 3000000, 0, NULL, true);

assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}

+ dir = journal_directory_unref(dir);
puts("------------------------------------------------------------");
}

static void test_empty(void) {
JournalFile *f1, *f2, *f3, *f4;
+ JournalDirectory *dir;
char t[] = "/tmp/journal-XXXXXX";

log_set_max_level(LOG_DEBUG);
@@ -133,13 +137,15 @@ static void test_empty(void) {
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);

- assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, &f1) == 0);
+ assert_se(journal_directory_open(".", &dir) == 0);

- assert_se(journal_file_open("test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, &f2) == 0);
+ assert_se(journal_file_open(dir, "test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, &f1) == 0);

- assert_se(journal_file_open("test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, &f3) == 0);
+ assert_se(journal_file_open(dir, "test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, &f2) == 0);

- assert_se(journal_file_open("test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, &f4) == 0);
+ assert_se(journal_file_open(dir, "test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, &f3) == 0);
+
+ assert_se(journal_file_open(dir, "test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, &f4) == 0);

journal_file_print_header(f1);
puts("");
@@ -155,7 +161,7 @@ static void test_empty(void) {
if (arg_keep)
log_info("Not removing %s", t);
else {
- journal_directory_vacuum(".", 3000000, 0, NULL, true);
+ journal_directory_vacuum(dir, 3000000, 0, NULL, true);

assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
@@ -164,6 +170,7 @@ static void test_empty(void) {
journal_file_close(f2);
journal_file_close(f3);
journal_file_close(f4);
+ journal_directory_unref(dir);
}

int main(int argc, char *argv[]) {
diff --git a/src/shared/util.c b/src/shared/util.c
index 395af7c..e27d14e 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -1791,6 +1791,26 @@ int dir_is_empty(const char *path) {
}
}

+char* basename_malloc(const char *path) {
+ char *b, *base, *base2;
+
+ assert(path);
+
+ b = strdup(path);
+ if (!b)
+ return NULL;
+ base = basename(b);
+ assert(base);
+
+ if (base != b) {
+ base2 = strdup(base);
+ free(b);
+ return base2;
+ }
+
+ return base;
+}
+
char* dirname_malloc(const char *path) {
char *d, *dir, *dir2;

diff --git a/src/shared/util.h b/src/shared/util.h
index 1cfb45f..9b5d9af 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -339,6 +339,7 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
bool is_device_path(const char *path);

int dir_is_empty(const char *path);
+char* basename_malloc(const char *path);
char* dirname_malloc(const char *path);

char* lookup_uid(uid_t uid);
--
2.1.0
Lennart Poettering
2015-06-10 23:45:48 UTC
Permalink
On Mon, 01.06.15 17:28, Krzesimir Nowak (***@endocode.com) wrote:

For the sake of the archives: further discussion on this patch set now
takes place on gihub:

https://github.com/systemd/systemd/pull/49
Post by Krzesimir Nowak
Hi,
When having a container that keeps its journal inside overlayfs,
journalctl from host can't access them anymore. The way to fix, as
proposed by Lennart, was basically journalctl to get file descriptor
of journal directory from machined and go from there. This situation
happens inside rkt containers - currently to get logs from them, we
need to link the journal to host and call journalctl with merge flag,
journalctl -m _HOSTNAME=<machine-id>
So, first three commits are some fixes for minor bugs I noticed or
small refactorings I made during this work.
Fourth patch adds a GetJournal() method to machine1.Manager which
takes a machine name and returns a file descriptor. It works by
entering a mount namespace of machine's leader and getting a
descriptor of /var/log/journal.
Fifth and sixth patches convert the JournalFile from being open()ed
with a path to being openat()ed with dirfd and filename. The fifth
patch wraps the directory fd and path into a refcounted struct, so
many JournalFiles actually hold a ref to JournalDirectory instead of
holding a descriptor. The sixth patch changes the JournalFile to use
JournalDirectory and all the other sources that were using
JournalFile. The patch is quite big because of that, sorry about
that. Maybe you will want to minimize the changes (by preserving
JournalFile's path member).
Seventh patch hooks journalctl to machine1.Manager's GetJournal.
nspawn, shared: Factor out sending and receiving fd
nspawn: Close unneeded sockets in outer child
nspawn, machined: Fix comments and error messages related to child
failures
machined: Add getter for machine's journal directory descriptor
journal: Add JournalDirectory
journal: Open JournalFile by dirfd and filename instead of path
journal: Try getting journal fd from machined
Makefile.am | 4 +-
src/journal-remote/journal-remote-write.c | 12 +-
src/journal-remote/journal-remote.c | 34 ++-
src/journal/journal-dir.c | 141 ++++++++++
src/journal/journal-dir.h | 37 +++
src/journal/journal-file.c | 108 +++----
src/journal/journal-file.h | 10 +-
src/journal/journal-internal.h | 3 +-
src/journal/journal-vacuum.c | 27 +-
src/journal/journal-vacuum.h | 3 +-
src/journal/journal-verify.c | 4 +-
src/journal/journalctl.c | 8 +-
src/journal/journald-server.c | 113 +++++---
src/journal/sd-journal.c | 376 ++++++++++++++++++-------
src/journal/test-journal-flush.c | 13 +-
src/journal/test-journal-interleaving.c | 55 ++--
src/journal/test-journal-stream.c | 10 +-
src/journal/test-journal-verify.c | 13 +-
src/journal/test-journal.c | 21 +-
src/machine/machine-dbus.c | 18 +-
src/machine/machined-dbus.c | 112 ++++++++
src/machine/org.freedesktop.machine1.policy.in | 10 +
src/nspawn/nspawn.c | 72 +----
src/shared/util.c | 76 +++++
src/shared/util.h | 4 +
25 files changed, 949 insertions(+), 335 deletions(-)
create mode 100644 src/journal/journal-dir.c
create mode 100644 src/journal/journal-dir.h
--
2.1.0
_______________________________________________
systemd-devel mailing list
http://lists.freedesktop.org/mailman/listinfo/systemd-devel
Lennart
--
Lennart Poettering, Red Hat
Continue reading on narkive:
Loading...