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/host_console.cpp b/src/host_console.cpp
new file mode 100644
index 0000000..bddc08e
--- /dev/null
+++ b/src/host_console.cpp
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright (C) 2020 YADRO
+
+#include "host_console.hpp"
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <cstring>
+#include <system_error>
+
+/**
+ * @brief Base path to the console's socket.
+ * See obmc-console for details.
+ */
+static constexpr char socketPath[] = "\0obmc-console";
+
+HostConsole::HostConsole(const std::string& socketId) :
+ socketId(socketId), socketFd(-1)
+{}
+
+HostConsole::~HostConsole()
+{
+ if (socketFd != -1)
+ {
+ close(socketFd);
+ }
+}
+
+void HostConsole::connect()
+{
+ if (socketFd != -1)
+ {
+ throw std::runtime_error("Socket already opened");
+ }
+
+ // Create socket
+ socketFd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (socketFd == -1)
+ {
+ std::error_code ec(errno ? errno : EIO, std::generic_category());
+ throw std::system_error(ec, "Unable to create socket");
+ }
+
+ // Set non-blocking mode for socket
+ int opt = 1;
+ if (ioctl(socketFd, FIONBIO, &opt))
+ {
+ std::error_code ec(errno ? errno : EIO, std::generic_category());
+ throw std::system_error(ec, "Unable to set non-blocking mode");
+ }
+
+ // Construct path to the socket file (see obmc-console for details)
+ std::string path(socketPath, socketPath + sizeof(socketPath) - 1);
+ if (!socketId.empty())
+ {
+ path += '.';
+ path += socketId;
+ }
+ if (path.length() > sizeof(sockaddr_un::sun_path))
+ {
+ throw std::invalid_argument("Invalid socket ID");
+ }
+
+ sockaddr_un sa;
+ sa.sun_family = AF_UNIX;
+ memcpy(&sa.sun_path, path.c_str(), path.length());
+
+ // Connect to host's log stream via socket.
+ // The owner of the socket (server) is obmc-console service and
+ // we have a dependency on it written in the systemd unit file, but
+ // we can't guarantee that the socket is initialized at the moment.
+ size_t connectAttempts = 60; // Number of attempts
+ const socklen_t len = sizeof(sa) - sizeof(sa.sun_path) + path.length();
+ int rc = -1;
+ while (connectAttempts--)
+ {
+ rc = ::connect(socketFd, reinterpret_cast<const sockaddr*>(&sa), len);
+ if (!rc)
+ {
+ break;
+ }
+ else
+ {
+ sleep(1); // Make 1 second pause between attempts
+ }
+ }
+ if (rc)
+ {
+ std::string err = "Unable to connect to console";
+ if (!socketId.empty())
+ {
+ err += ' ';
+ err += socketId;
+ }
+ std::error_code ec(errno ? errno : EIO, std::generic_category());
+ throw std::system_error(ec, err);
+ }
+}
+
+size_t HostConsole::read(char* buf, size_t sz) const
+{
+ ssize_t rsz = ::read(socketFd, buf, sz);
+ if (rsz < 0)
+ {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ {
+ // We are in non-blocking mode, so ignore these codes
+ rsz = 0;
+ }
+ else
+ {
+ std::string err = "Unable to read socket";
+ if (!socketId.empty())
+ {
+ err += ' ';
+ err += socketId;
+ }
+ std::error_code ec(errno ? errno : EIO, std::generic_category());
+ throw std::system_error(ec, err);
+ }
+ }
+
+ return static_cast<size_t>(rsz);
+}
+
+HostConsole::operator int() const
+{
+ return socketFd;
+}