Implement config parsing
Add a configuration json parsing function to read the configuration file
from /usr/share/binaryblob/config.json for which system file and location
to serialize/deserialize the binarystore blobs. Add a simple unit test for
parsing configs as well.
Signed-off-by: Kun Yi <kunyi731@gmail.com>
Change-Id: Ie86d622e83991365bc202d659f5860ff01190311
diff --git a/configure.ac b/configure.ac
index 2b09776..5ea68ed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -24,6 +24,7 @@
PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging],, [AC_MSG_ERROR([Could not find phosphor-logging...openbmc/phosphor-logging package required])])
AC_CHECK_HEADER([blobs-ipmid], [AC_MSG_ERROR(["phosphor-ipmi-blobs required and not found."])])
AC_CHECK_HEADER(boost/endian/arithmetic.hpp, [])
+AC_CHECK_HEADER(nlohmann/json.hpp, [])
# Check/set gtest specific functions.
PKG_CHECK_MODULES([GTEST], [gtest], [], [AC_MSG_NOTICE([gtest not found, tests will not build])])
diff --git a/main.cpp b/main.cpp
index 97b3aa9..b5964b8 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,7 +1,12 @@
#include "handler.hpp"
+#include "parse_config.hpp"
+#include "sys_file.hpp"
#include <blobs-ipmid/blobs.hpp>
+#include <exception>
+#include <fstream>
#include <memory>
+#include <phosphor-logging/elog.hpp>
#ifdef __cplusplus
extern "C" {
@@ -17,7 +22,57 @@
}
#endif
+/* Configuration file path */
+constexpr auto blobConfigPath = "/usr/share/binaryblob/config.json";
+
std::unique_ptr<blobs::GenericBlobInterface> createHandler()
{
- return std::make_unique<blobs::BinaryStoreBlobHandler>();
+ using namespace phosphor::logging;
+ using nlohmann::json;
+
+ std::ifstream input(blobConfigPath);
+ json j;
+
+ try
+ {
+ input >> j;
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Failed to parse config into json",
+ entry("ERR=%s", e.what()));
+ return nullptr;
+ }
+
+ // Construct binary blobs from config and add to handler
+ auto handler = std::make_unique<blobs::BinaryStoreBlobHandler>();
+
+ for (const auto& element : j)
+ {
+ conf::BinaryBlobConfig config;
+ try
+ {
+ conf::parseFromConfigFile(element, config);
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Encountered error when parsing config file",
+ entry("ERR=%s", e.what()));
+ return nullptr;
+ }
+
+ log<level::INFO>("Loading from config with",
+ entry("BASE_ID=%s", config.blobBaseId.c_str()),
+ entry("FILE=%s", config.sysFilePath.c_str()),
+ entry("MAX_SIZE=%llx", static_cast<unsigned long long>(
+ config.maxSizeBytes)));
+
+ auto file = std::make_unique<binstore::SysFileImpl>(config.sysFilePath,
+ config.offsetBytes);
+
+ handler->addNewBinaryStore(binstore::BinaryStore::createFromConfig(
+ config.blobBaseId, std::move(file), config.maxSizeBytes));
+ }
+
+ return std::move(handler);
}
diff --git a/parse_config.hpp b/parse_config.hpp
new file mode 100644
index 0000000..b778923
--- /dev/null
+++ b/parse_config.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <cstdint>
+#include <nlohmann/json.hpp>
+#include <string>
+
+using std::uint32_t;
+using json = nlohmann::json;
+
+namespace conf
+{
+
+struct BinaryBlobConfig
+{
+ std::string blobBaseId; // Required
+ std::string sysFilePath; // Required
+ uint32_t offsetBytes; // Optional
+ uint32_t maxSizeBytes; // Optional
+};
+
+/**
+ * @brief Parse parameters from a config json
+ * @param j: input json object
+ * @param config: output BinaryBlobConfig
+ * @throws: exception if config doesn't have required fields
+ */
+static inline void parseFromConfigFile(const json& j, BinaryBlobConfig& config)
+{
+ j.at("blobBaseId").get_to(config.blobBaseId);
+ j.at("sysFilePath").get_to(config.sysFilePath);
+ config.offsetBytes = j.value("offsetBytes", 0);
+ config.maxSizeBytes = j.value("maxSizeBytes", 0);
+}
+
+} // namespace conf
diff --git a/test/Makefile.am b/test/Makefile.am
index 2e36c8e..3d53916 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -13,6 +13,7 @@
# Run all 'check' test programs
check_PROGRAMS = \
+ parse_config_unittest \
sys_file_unittest \
handler_commit_unittest \
handler_open_unittest \
@@ -20,6 +21,8 @@
handler_unittest
TESTS = $(check_PROGRAMS)
+parse_config_unittest_SOURCES = parse_config_unittest.cpp
+
sys_file_unittest_SOURCES = sys_file_unittest.cpp
sys_file_unittest_LDADD = $(top_builddir)/sys_file.o
diff --git a/test/parse_config_unittest.cpp b/test/parse_config_unittest.cpp
new file mode 100644
index 0000000..b450477
--- /dev/null
+++ b/test/parse_config_unittest.cpp
@@ -0,0 +1,66 @@
+#include "parse_config.hpp"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using json = nlohmann::json;
+using namespace conf;
+
+TEST(ParseConfigTest, ExceptionWhenMissingRequiredFields)
+{
+ auto j = R"(
+ {
+ "blobBaseId": "/test/"
+ }
+ )"_json;
+
+ BinaryBlobConfig config;
+
+ EXPECT_THROW(parseFromConfigFile(j, config), std::exception);
+}
+
+TEST(ParseConfigTest, TestSimpleConfig)
+{
+ auto j = R"(
+ {
+ "blobBaseId": "/test/",
+ "sysFilePath": "/sys/fake/path",
+ "offsetBytes": 32,
+ "maxSizeBytes": 2
+ }
+ )"_json;
+
+ BinaryBlobConfig config;
+
+ EXPECT_NO_THROW(parseFromConfigFile(j, config));
+ EXPECT_EQ(config.blobBaseId, "/test/");
+ EXPECT_EQ(config.sysFilePath, "/sys/fake/path");
+ EXPECT_EQ(config.offsetBytes, 32);
+ EXPECT_EQ(config.maxSizeBytes, 2);
+}
+
+TEST(ParseConfigTest, TestConfigArray)
+{
+ auto j = R"(
+ [{
+ "blobBaseId": "/test/",
+ "sysFilePath": "/sys/fake/path",
+ "offsetBytes": 32,
+ "maxSizeBytes": 32
+ },
+ {
+ "blobBaseId": "/test/",
+ "sysFilePath": "/another/path"
+ }]
+ )"_json;
+
+ for (auto& element : j)
+ {
+ BinaryBlobConfig config;
+
+ EXPECT_NO_THROW(parseFromConfigFile(element, config));
+ EXPECT_EQ(config.blobBaseId, "/test/");
+ EXPECT_TRUE(config.offsetBytes == 32 || config.offsetBytes == 0);
+ EXPECT_TRUE(config.maxSizeBytes == 32 || config.maxSizeBytes == 0);
+ }
+}