Add serialization for bios-settings-manager

Tested: Restarted the biosconfig-manager application and
        BaseBIOSTable and PendingAttributes were restored.

Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
Change-Id: Iddbe69ed45a6895a324d236a14b6f21586315c62
diff --git a/include/manager.hpp b/include/manager.hpp
index 702e30f..3182126 100644
--- a/include/manager.hpp
+++ b/include/manager.hpp
@@ -15,10 +15,13 @@
 */
 #pragma once
 
+#include "config.h"
+
 #include <sdbusplus/asio/object_server.hpp>
 #include <sdbusplus/server.hpp>
 #include <xyz/openbmc_project/BIOSConfig/Manager/server.hpp>
 
+#include <filesystem>
 #include <string>
 
 namespace bios_config
@@ -26,8 +29,10 @@
 
 static constexpr auto service = "xyz.openbmc_project.BIOSConfigManager";
 static constexpr auto objectPath = "/xyz/openbmc_project/bios_config/manager";
+constexpr auto biosPersistFile = "biosData";
 
 using Base = sdbusplus::xyz::openbmc_project::BIOSConfig::server::Manager;
+namespace fs = std::filesystem;
 
 /** @class Manager
  *
@@ -131,6 +136,7 @@
 
     sdbusplus::asio::object_server& objServer;
     std::shared_ptr<sdbusplus::asio::connection>& systemBus;
+    std::filesystem::path biosFile;
 };
 
 } // namespace bios_config
diff --git a/include/manager_serialize.hpp b/include/manager_serialize.hpp
new file mode 100644
index 0000000..000314e
--- /dev/null
+++ b/include/manager_serialize.hpp
@@ -0,0 +1,28 @@
+#pragma once

+

+#include "manager.hpp"

+

+#include <filesystem>

+

+namespace bios_config

+{

+

+/** @brief Serialize and persist the bios manager object

+ *

+ *  @param[in] obj - bios manager object

+ *  @param[in] path - path to the file where the bios manager object

+ *                    is to be serialized

+ */

+void serialize(const Manager& obj, const fs::path& path);

+

+/** @brief Deserialize the persisted data and populate the bios manager object

+ *

+ *  @param[in] path - path to the persisted file

+ *  @param[in/out] entry - reference to the bios manager object which is the

+ *                         target of deserialization.

+ *

+ *  @return bool - true if the deserialization was successful, false otherwise.

+ */

+bool deserialize(const fs::path& path, Manager& entry);

+

+} // namespace bios_config
\ No newline at end of file
diff --git a/meson.build b/meson.build
index e99c122..e23a60a 100755
--- a/meson.build
+++ b/meson.build
@@ -16,6 +16,10 @@
 # project uses the same compiler, we can safely ignmore these info notes.
 add_project_arguments('-Wno-psabi', language: 'cpp')
 
+conf_data = configuration_data()
+conf_data.set_quoted('BIOS_PERSIST_PATH', get_option('bios-persist-path'))
+configure_file(output: 'config.h', configuration: conf_data)
+
 boost_args = ['-DBOOST_ALL_NO_LIB',
               '-DBOOST_ASIO_DISABLE_THREADS',
               '-DBOOST_ERROR_CODE_HEADER_ONLY',
@@ -32,7 +36,8 @@
 
 executable('biosconfig-manager',
            'src/manager.cpp',
-        implicit_include_directories: false,
+           'src/manager_serialize.cpp',
+        implicit_include_directories: true,
         include_directories: ['include'],
         dependencies: deps,
         cpp_args : boost_args,
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..f125e96
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1 @@
+option('bios-persist-path', type : 'string', description : 'The filesystem path to persist the bios-settings-manager object', value : '/var/lib/bios-settings-manager')
\ No newline at end of file
diff --git a/src/manager.cpp b/src/manager.cpp
index c4c660d..c173ee4 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -15,6 +15,7 @@
 */
 #include "manager.hpp"
 
+#include "manager_serialize.hpp"
 #include "xyz/openbmc_project/BIOSConfig/Common/error.hpp"
 #include "xyz/openbmc_project/Common/error.hpp"
 
@@ -93,7 +94,9 @@
 Manager::BaseTable Manager::baseBIOSTable(BaseTable value)
 {
     pendingAttributes({});
-    return Base::baseBIOSTable(value, false);
+    auto baseTable = Base::baseBIOSTable(value, false);
+    serialize(*this, biosFile);
+    return baseTable;
 }
 
 Manager::PendingAttributes Manager::pendingAttributes(PendingAttributes value)
@@ -101,7 +104,9 @@
     // Clear the pending attributes
     if (value.empty())
     {
-        return Base::pendingAttributes({}, false);
+        auto pendingAttrs = Base::pendingAttributes({}, false);
+        serialize(*this, biosFile);
+        return pendingAttrs;
     }
 
     // Validate all the BIOS attributes before setting PendingAttributes
@@ -255,7 +260,10 @@
         pendingAttribute.emplace(std::make_pair(pair.first, pair.second));
     }
 
-    return Base::pendingAttributes(pendingAttribute, false);
+    auto pendingAttrs = Base::pendingAttributes(pendingAttribute, false);
+    serialize(*this, biosFile);
+
+    return pendingAttrs;
 }
 
 Manager::Manager(sdbusplus::asio::object_server& objectServer,
@@ -263,7 +271,12 @@
     sdbusplus::xyz::openbmc_project::BIOSConfig::server::Manager(*systemBus,
                                                                  objectPath),
     objServer(objectServer), systemBus(systemBus)
-{}
+{
+    fs::path biosDir(BIOS_PERSIST_PATH);
+    fs::create_directories(biosDir);
+    biosFile = biosDir / biosPersistFile;
+    deserialize(biosFile, *this);
+}
 
 } // namespace bios_config
 
diff --git a/src/manager_serialize.cpp b/src/manager_serialize.cpp
new file mode 100644
index 0000000..ef7b1b4
--- /dev/null
+++ b/src/manager_serialize.cpp
@@ -0,0 +1,92 @@
+#include "manager_serialize.hpp"

+

+#include <cereal/archives/binary.hpp>

+#include <cereal/cereal.hpp>

+#include <cereal/types/map.hpp>

+#include <cereal/types/string.hpp>

+#include <cereal/types/tuple.hpp>

+#include <cereal/types/variant.hpp>

+#include <cereal/types/vector.hpp>

+#include <phosphor-logging/log.hpp>

+

+#include <fstream>

+

+namespace bios_config

+{

+

+using namespace phosphor::logging;

+

+/** @brief Function required by Cereal to perform serialization.

+ *

+ *  @tparam Archive - Cereal archive type (binary in this case).

+ *  @param[in] archive - reference to cereal archive.

+ *  @param[in] entry- const reference to bios manager object

+ *  @param[in] version - Class version that enables handling a serialized data

+ *                       across code levels

+ */

+template <class Archive>

+void save(Archive& archive, const Manager& entry,

+          const std::uint32_t /*version*/)

+{

+    archive(entry.sdbusplus::xyz::openbmc_project::BIOSConfig::server::Manager::

+                baseBIOSTable(),

+            entry.sdbusplus::xyz::openbmc_project::BIOSConfig::server::Manager::

+                pendingAttributes());

+}

+

+/** @brief Function required by Cereal to perform deserialization.

+ *

+ *  @tparam Archive - Cereal archive type (binary in our case).

+ *  @param[in] archive - reference to cereal archive.

+ *  @param[out] entry - reference to bios manager object

+ *  @param[in] version - Class version that enables handling a serialized data

+ *                       across code levels

+ */

+template <class Archive>

+void load(Archive& archive, Manager& entry, const std::uint32_t /*version*/)

+{

+    Manager::BaseTable baseTable;

+    Manager::PendingAttributes pendingAttrs;

+

+    archive(baseTable, pendingAttrs);

+    entry.sdbusplus::xyz::openbmc_project::BIOSConfig::server::Manager::

+        baseBIOSTable(baseTable, true);

+    entry.sdbusplus::xyz::openbmc_project::BIOSConfig::server::Manager::

+        pendingAttributes(pendingAttrs, true);

+}

+

+void serialize(const Manager& obj, const fs::path& path)

+{

+    std::ofstream os(path.c_str(), std::ios::out | std::ios::binary);

+    cereal::BinaryOutputArchive oarchive(os);

+    oarchive(obj);

+}

+

+bool deserialize(const fs::path& path, Manager& entry)

+{

+    try

+    {

+        if (fs::exists(path))

+        {

+            std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);

+            cereal::BinaryInputArchive iarchive(is);

+            iarchive(entry);

+            return true;

+        }

+        return false;

+    }

+    catch (cereal::Exception& e)

+    {

+        log<level::ERR>(e.what());

+        fs::remove(path);

+        return false;

+    }

+    catch (const std::length_error& e)

+    {

+        log<level::ERR>(e.what());

+        fs::remove(path);

+        return false;

+    }

+}

+

+} // namespace bios_config
\ No newline at end of file