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/log_buffer.cpp b/src/log_buffer.cpp
new file mode 100644
index 0000000..f511a95
--- /dev/null
+++ b/src/log_buffer.cpp
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright (C) 2020 YADRO
+
+#include "log_buffer.hpp"
+
+/** @brief Check if a character is EOL symbol. */
+constexpr bool isEol(char c)
+{
+    return c == '\r' || c == '\n';
+}
+
+LogBuffer::LogBuffer(size_t maxSize, size_t maxTime) :
+    lastComplete(true), sizeLimit(maxSize), timeLimit(maxTime)
+{}
+
+void LogBuffer::append(const char* data, size_t sz)
+{
+    // Split raw data into separate messages by EOL symbols (\r or \n).
+    // Stream may not be ended with EOL, so we handle this situation by
+    // lastComplete flag.
+    size_t pos = 0;
+    while (pos < sz)
+    {
+        // Search for EOL ('\r' or '\n')
+        size_t eol = pos;
+        while (eol < sz)
+        {
+            if (isEol(data[eol]))
+            {
+                break;
+            }
+            ++eol;
+        }
+        const bool eolFound = eol < sz;
+        const char* msgText = data + pos;
+        const size_t msgLen = (eolFound ? eol : sz) - pos;
+
+        // Append message to the container
+        if (!lastComplete && !messages.empty())
+        {
+            // The last message is incomplete, add data as part of it
+            messages.back().text.append(msgText, msgLen);
+        }
+        else
+        {
+            Message msg;
+            time(&msg.timeStamp);
+            msg.text.assign(msgText, msgLen);
+            messages.push_back(msg);
+        }
+        lastComplete = eolFound;
+
+        // Move current position and skip EOL character
+        pos = eol + 1;
+        // Handle EOL sequences '\r\n' or '\n\r' as one delimiter
+        if (eolFound && pos < sz && isEol(data[pos]) && data[eol] != data[pos])
+        {
+            ++pos;
+        }
+    }
+
+    shrink();
+}
+
+void LogBuffer::setFullHandler(std::function<void()> cb)
+{
+    fullHandler = cb;
+}
+
+void LogBuffer::clear()
+{
+    messages.clear();
+    lastComplete = true;
+}
+
+bool LogBuffer::empty() const
+{
+    return messages.empty();
+}
+
+LogBuffer::container_t::const_iterator LogBuffer::begin() const
+{
+    return messages.begin();
+}
+
+LogBuffer::container_t::const_iterator LogBuffer::end() const
+{
+    return messages.end();
+}
+
+void LogBuffer::shrink()
+{
+    if (sizeLimit && messages.size() > sizeLimit)
+    {
+        if (fullHandler)
+        {
+            fullHandler();
+        }
+        while (messages.size() > sizeLimit)
+        {
+            messages.pop_front();
+        }
+    }
+    if (timeLimit && !messages.empty())
+    {
+        time_t oldest;
+        time(&oldest);
+        oldest -= timeLimit * 60 /* sec */;
+        if (messages.begin()->timeStamp < oldest)
+        {
+            if (fullHandler)
+            {
+                fullHandler();
+            }
+            while (!messages.empty() && messages.begin()->timeStamp < oldest)
+            {
+                messages.pop_front();
+            }
+        }
+    }
+}