Move journal sync function to util.cpp
Move it out of log_manager.cpp so other code will be able to call it.
The code itself wasn't changed, though a comment was modified, as it is
now a utility function it may no longer just be called when an error log
is created.
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I33a20c1a68059fd50678464776e88168ce93d030
diff --git a/util.cpp b/util.cpp
index a89e754..da068e6 100644
--- a/util.cpp
+++ b/util.cpp
@@ -17,6 +17,17 @@
#include "util.hpp"
+#include <poll.h>
+#include <sys/inotify.h>
+#include <systemd/sd-bus.h>
+#include <systemd/sd-journal.h>
+#include <unistd.h>
+
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/bus.hpp>
+
+#include <chrono>
+
namespace phosphor::logging::util
{
@@ -43,4 +54,147 @@
return std::nullopt;
}
+void journalSync()
+{
+ bool syncRequested = false;
+ auto fd = -1;
+ auto rc = -1;
+ auto wd = -1;
+ auto bus = sdbusplus::bus::new_default();
+
+ auto start = std::chrono::duration_cast<std::chrono::microseconds>(
+ std::chrono::steady_clock::now().time_since_epoch())
+ .count();
+
+ // Make a request to sync the journal with the SIGRTMIN+1 signal and
+ // block until it finishes, waiting at most 5 seconds.
+ //
+ // Number of loop iterations = 3 for the following reasons:
+ // Iteration #1: Requests a journal sync by killing the journald service.
+ // Iteration #2: Setup an inotify watch to monitor the synced file that
+ // journald updates with the timestamp the last time the
+ // journal was flushed.
+ // Iteration #3: Poll to wait until inotify reports an event which blocks
+ // the error log from being commited until the sync completes.
+ constexpr auto maxRetry = 3;
+ for (int i = 0; i < maxRetry; i++)
+ {
+ // Read timestamp from synced file
+ constexpr auto syncedPath = "/run/systemd/journal/synced";
+ std::ifstream syncedFile(syncedPath);
+ if (syncedFile.fail())
+ {
+ // If the synced file doesn't exist, a sync request will create it.
+ if (errno != ENOENT)
+ {
+ lg2::error(
+ "Failed to open journal synced file {FILENAME}: {ERROR}",
+ "FILENAME", syncedPath, "ERROR", strerror(errno));
+ return;
+ }
+ }
+ else
+ {
+ // Only read the synced file if it exists.
+ // See if a sync happened by now
+ std::string timestampStr;
+ std::getline(syncedFile, timestampStr);
+ auto timestamp = std::stoll(timestampStr);
+ if (timestamp >= start)
+ {
+ break;
+ }
+ }
+
+ // Let's ask for a sync, but only once
+ if (!syncRequested)
+ {
+ syncRequested = true;
+
+ constexpr auto JOURNAL_UNIT = "systemd-journald.service";
+ auto signal = SIGRTMIN + 1;
+
+ auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
+ SYSTEMD_INTERFACE, "KillUnit");
+ method.append(JOURNAL_UNIT, "main", signal);
+ bus.call(method);
+ if (method.is_method_error())
+ {
+ lg2::error("Failed to kill journal service");
+ break;
+ }
+
+ continue;
+ }
+
+ // Let's install the inotify watch, if we didn't do that yet. This watch
+ // monitors the syncedFile for when journald updates it with a newer
+ // timestamp. This means the journal has been flushed.
+ if (fd < 0)
+ {
+ fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+ if (fd < 0)
+ {
+ lg2::error("Failed to create inotify watch: {ERROR}", "ERROR",
+ strerror(errno));
+ return;
+ }
+
+ constexpr auto JOURNAL_RUN_PATH = "/run/systemd/journal";
+ wd = inotify_add_watch(fd, JOURNAL_RUN_PATH,
+ IN_MOVED_TO | IN_DONT_FOLLOW | IN_ONLYDIR);
+ if (wd < 0)
+ {
+ lg2::error("Failed to watch journal directory: {PATH}: {ERROR}",
+ "PATH", JOURNAL_RUN_PATH, "ERROR", strerror(errno));
+ close(fd);
+ return;
+ }
+ continue;
+ }
+
+ // Let's wait until inotify reports an event
+ struct pollfd fds = {
+ fd,
+ POLLIN,
+ 0,
+ };
+ constexpr auto pollTimeout = 5; // 5 seconds
+ rc = poll(&fds, 1, pollTimeout * 1000);
+ if (rc < 0)
+ {
+ lg2::error("Failed to add event: {ERROR}", "ERROR",
+ strerror(errno));
+ inotify_rm_watch(fd, wd);
+ close(fd);
+ return;
+ }
+ else if (rc == 0)
+ {
+ lg2::info("Poll timeout ({TIMEOUT}), no new journal synced data",
+ "TIMEOUT", pollTimeout);
+ break;
+ }
+
+ // Read from the specified file descriptor until there is no new data,
+ // throwing away everything read since the timestamp will be read at the
+ // beginning of the loop.
+ constexpr auto maxBytes = 64;
+ uint8_t buffer[maxBytes];
+ while (read(fd, buffer, maxBytes) > 0)
+ ;
+ }
+
+ if (fd != -1)
+ {
+ if (wd != -1)
+ {
+ inotify_rm_watch(fd, wd);
+ }
+ close(fd);
+ }
+
+ return;
+}
+
} // namespace phosphor::logging::util