Add multi-host support
This refactoring includes:
- added multi-host mode support;
- added support for graceful shutdown of the service;
- added support to flush the log buffer as it fills;
- D-Bus service xyz.openbmc_project.HostLogger replaced with SIGUSR1
signal handler;
- self diagnostic messages now registered via phosphor-logging;
- added unit tests;
- build system migrated from autotools to meson;
- source code aligned with OpenBMC conventions.
Change-Id: If6c1dfde278af685d8563450543a6587a282c7e4
Signed-off-by: Artem Senichev <a.senichev@yadro.com>
diff --git a/src/service.cpp b/src/service.cpp
new file mode 100644
index 0000000..eda8545
--- /dev/null
+++ b/src/service.cpp
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright (C) 2020 YADRO
+
+#include "service.hpp"
+
+#include <phosphor-logging/log.hpp>
+
+#include <vector>
+
+using namespace phosphor::logging;
+
+// clang-format off
+/** @brief Host state properties. */
+static const DbusLoop::WatchProperties watchProperties{
+ {"xyz.openbmc_project.State.Host", {{
+ "RequestedHostTransition", {
+ "xyz.openbmc_project.State.Host.Transition.On"}}}},
+ {"xyz.openbmc_project.State.OperatingSystem.Status", {{
+ "OperatingSystemState", {
+ "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.BootComplete",
+ "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive"}}}}
+};
+// clang-format on
+
+Service::Service(const Config& config) :
+ config(config), hostConsole(config.socketId),
+ logBuffer(config.bufMaxSize, config.bufMaxTime),
+ fileStorage(config.outDir, config.socketId, config.maxFiles)
+{}
+
+void Service::run()
+{
+ if (config.bufFlushFull)
+ {
+ logBuffer.setFullHandler([this]() { this->flush(); });
+ }
+
+ hostConsole.connect();
+
+ // Add SIGUSR1 signal handler for manual flushing
+ dbusLoop.addSignalHandler(SIGUSR1, [this]() { this->flush(); });
+ // Add SIGTERM signal handler for service shutdown
+ dbusLoop.addSignalHandler(SIGTERM, [this]() { this->dbusLoop.stop(0); });
+
+ // Register callback for socket IO
+ dbusLoop.addIoHandler(hostConsole, [this]() { this->readConsole(); });
+
+ // Register host state watcher
+ if (*config.hostState)
+ {
+ dbusLoop.addPropertyHandler(config.hostState, watchProperties,
+ [this]() { this->flush(); });
+ }
+
+ if (!*config.hostState && !config.bufFlushFull)
+ {
+ log<level::WARNING>("Automatic flush disabled");
+ }
+
+ log<level::DEBUG>("Initialization complete",
+ entry("SocketId=%s", config.socketId),
+ entry("BufMaxSize=%lu", config.bufMaxSize),
+ entry("BufMaxTime=%lu", config.bufMaxTime),
+ entry("BufFlushFull=%s", config.bufFlushFull ? "y" : "n"),
+ entry("HostState=%s", config.hostState),
+ entry("OutDir=%s", config.outDir),
+ entry("MaxFiles=%lu", config.maxFiles));
+
+ // Run D-Bus event loop
+ const int rc = dbusLoop.run();
+ if (!logBuffer.empty())
+ {
+ flush();
+ }
+ if (rc < 0)
+ {
+ std::error_code ec(-rc, std::generic_category());
+ throw std::system_error(ec, "Error in event loop");
+ }
+}
+
+void Service::flush()
+{
+ if (logBuffer.empty())
+ {
+ log<level::INFO>("Ignore flush: buffer is empty");
+ return;
+ }
+ try
+ {
+ const std::string fileName = fileStorage.save(logBuffer);
+ logBuffer.clear();
+
+ std::string msg = "Host logs flushed to ";
+ msg += fileName;
+ log<level::INFO>(msg.c_str());
+ }
+ catch (const std::exception& ex)
+ {
+ log<level::ERR>(ex.what());
+ }
+}
+
+void Service::readConsole()
+{
+ constexpr size_t bufSize = 128; // enough for most line-oriented output
+ std::vector<char> bufData(bufSize);
+ char* buf = bufData.data();
+
+ try
+ {
+ while (const size_t rsz = hostConsole.read(buf, bufSize))
+ {
+ logBuffer.append(buf, rsz);
+ }
+ }
+ catch (const std::system_error& ex)
+ {
+ log<level::ERR>(ex.what());
+ }
+}