Add support for log-handler

This handler is based off of version handler with major differences in
handler_builder

Tested:
created the blackbox blob successfully using this lib.
Read the data from the blob using a host side tool

Signed-off-by: Gaurav Gandhi <gauravgandhi@google.com>
Change-Id: I9ef775af752156a1647453ff3831ef4c0449d546
diff --git a/bmc/log-handler/log_handler.cpp b/bmc/log-handler/log_handler.cpp
new file mode 100644
index 0000000..1883128
--- /dev/null
+++ b/bmc/log-handler/log_handler.cpp
@@ -0,0 +1,227 @@
+// Copyright 2021 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "log_handler.hpp"
+
+#include <algorithm>
+#include <cstring>
+#include <ios>
+#include <limits>
+#include <memory>
+#include <optional>
+#include <utility>
+#include <vector>
+
+namespace ipmi_flash
+{
+
+LogBlobHandler::LogBlobHandler(std::vector<HandlerConfig<ActionPack>>&& configs)
+{
+    for (auto& config : configs)
+    {
+        auto info = std::make_unique<BlobInfo>();
+        info->blobId = std::move(config.blobId);
+        info->actions = std::move(config.actions);
+        info->handler = std::move(config.handler);
+        info->actions->onOpen->setCallback(
+            [infoP = info.get()](TriggerableActionInterface& tai) {
+                auto data =
+                    std::make_shared<std::optional<std::vector<uint8_t>>>();
+                do
+                {
+                    if (tai.status() != ActionStatus::success)
+                    {
+                        fprintf(stderr,
+                                "LogBlobHandler: Log file unit failed for %s\n",
+                                infoP->blobId.c_str());
+                        continue;
+                    }
+                    if (!infoP->handler->open("", std::ios::in))
+                    {
+                        fprintf(
+                            stderr,
+                            "LogBlobHandler: Opening log file failed for %s\n",
+                            infoP->blobId.c_str());
+                        continue;
+                    }
+                    auto d = infoP->handler->read(
+                        0, std::numeric_limits<uint32_t>::max());
+                    infoP->handler->close();
+                    if (!d)
+                    {
+                        fprintf(
+                            stderr,
+                            "LogBlobHandler: Reading log file failed for %s\n",
+                            infoP->blobId.c_str());
+                        continue;
+                    }
+                    *data = std::move(d);
+                } while (false);
+                for (auto sessionP : infoP->sessionsToUpdate)
+                {
+                    sessionP->data = data;
+                }
+                infoP->sessionsToUpdate.clear();
+            });
+        if (!blobInfoMap.try_emplace(info->blobId, std::move(info)).second)
+        {
+            fprintf(stderr,
+                    "LogBlobHandler: Ignoring duplicate config for %s\n",
+                    info->blobId.c_str());
+        }
+    }
+}
+
+bool LogBlobHandler::canHandleBlob(const std::string& path)
+{
+    return blobInfoMap.find(path) != blobInfoMap.end();
+}
+
+std::vector<std::string> LogBlobHandler::getBlobIds()
+{
+    std::vector<std::string> ret;
+    for (const auto& [key, _] : blobInfoMap)
+    {
+        ret.emplace_back(key);
+    }
+    return ret;
+}
+
+/**
+ * deleteBlob - does nothing, always fails
+ */
+bool LogBlobHandler::deleteBlob(const std::string& path)
+{
+    for (const auto& [sessionId, sessionInfo] : sessionInfoMap)
+    {
+        if (sessionInfo->blob->blobId == path)
+        {
+            fprintf(stderr,
+                    "LogBlobHandler: delete %s fail: there is an open session "
+                    "for this blob\n",
+                    path.c_str());
+            return false;
+        }
+    }
+
+    auto* blob = blobInfoMap.at(path).get();
+    if (!blob->actions->onDelete->trigger())
+    {
+        fprintf(stderr,
+                "LogBlobHandler: delete %s fail: onDelete trigger failed\n",
+                path.c_str());
+        return false;
+    }
+    return true;
+}
+
+bool LogBlobHandler::stat(const std::string&, blobs::BlobMeta*)
+{
+    return false;
+}
+
+bool LogBlobHandler::open(uint16_t session, uint16_t flags,
+                          const std::string& path)
+{
+    /* only reads are supported, check if blob is handled and make sure
+     * the blob isn't already opened
+     */
+    if (flags != blobs::read)
+    {
+        fprintf(stderr,
+                "LogBlobHandler: open %s fail: unsupported flags(0x%04X.)\n",
+                path.c_str(), flags);
+        return false;
+    }
+
+    auto info = std::make_unique<SessionInfo>();
+    info->blob = blobInfoMap.at(path).get();
+    info->blob->sessionsToUpdate.emplace(info.get());
+    if (info->blob->sessionsToUpdate.size() == 1 &&
+        !info->blob->actions->onOpen->trigger())
+    {
+        fprintf(stderr, "LogBlobHandler: open %s fail: onOpen trigger failed\n",
+                path.c_str());
+        info->blob->sessionsToUpdate.erase(info.get());
+        return false;
+    }
+
+    sessionInfoMap[session] = std::move(info);
+    return true;
+}
+
+std::vector<uint8_t> LogBlobHandler::read(uint16_t session, uint32_t offset,
+                                          uint32_t requestedSize)
+{
+    auto& data = sessionInfoMap.at(session)->data;
+    if (data == nullptr || !*data)
+    {
+        throw std::runtime_error("LogBlobHandler: Log data not ready for read");
+    }
+    if ((*data)->size() < offset)
+    {
+        return {};
+    }
+    std::vector<uint8_t> ret(
+        std::min<size_t>(requestedSize, (*data)->size() - offset));
+    std::memcpy(&ret[0], &(**data)[offset], ret.size());
+    return ret;
+}
+
+bool LogBlobHandler::close(uint16_t session)
+{
+    auto it = sessionInfoMap.find(session);
+    if (it == sessionInfoMap.end())
+    {
+        return false;
+    }
+    auto& info = *it->second;
+    info.blob->sessionsToUpdate.erase(&info);
+    if (info.blob->sessionsToUpdate.empty())
+    {
+        info.blob->actions->onOpen->abort();
+    }
+    sessionInfoMap.erase(it);
+    return true;
+}
+
+bool LogBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta)
+{
+    const auto& data = sessionInfoMap.at(session)->data;
+    if (data == nullptr)
+    {
+        meta->blobState = blobs::StateFlags::committing;
+        meta->size = 0;
+    }
+    else if (!*data)
+    {
+        meta->blobState = blobs::StateFlags::commit_error;
+        meta->size = 0;
+    }
+    else
+    {
+        meta->blobState =
+            blobs::StateFlags::committed | blobs::StateFlags::open_read;
+        meta->size = (*data)->size();
+    }
+    return true;
+}
+
+bool LogBlobHandler::expire(uint16_t session)
+{
+    close(session);
+    return true;
+}
+
+} // namespace ipmi_flash