Enable support to handle InternalFailure type dump, during elog restore

Resolves openbmc/openbmc#2078

Change-Id: Iea47b9b7c0cd6cae21642057b21c4e99d85be1e8
Signed-off-by: Jayanth Othayoth <ojayanth@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index f2bec7c..30f0331 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,7 +8,8 @@
 	dump_manager.hpp \
 	dump_utils.hpp \
 	watch.hpp \
-	elog_watch.hpp
+	elog_watch.hpp \
+	dump_serialize.hpp
 
 nobase_nodist_include_HEADERS = \
 	xyz/openbmc_project/Dump/Internal/Create/server.hpp
@@ -23,7 +24,8 @@
 	dump_manager.cpp \
 	watch.cpp \
 	xyz/openbmc_project/Dump/Internal/Create/server.cpp \
-	elog_watch.cpp
+	elog_watch.cpp \
+	dump_serialize.cpp
 
 phosphor_dump_monitor_SOURCES = \
 	watch.cpp \
diff --git a/configure.ac b/configure.ac
index 00a76bf..fa69297 100644
--- a/configure.ac
+++ b/configure.ac
@@ -77,5 +77,11 @@
 
 AC_DEFINE(OBJ_LOGGING, "/xyz/openbmc_project/logging", [The log manager DBus object path.])
 
+AC_ARG_VAR(ELOG_ID_PERSIST_PATH, [Path of file for storing elog id's, which have associated dumps])
+AS_IF([test "x$ELOG_ID_PERSIST_PATH" == "x"], \
+    [ELOG_ID_PERSIST_PATH="/var/lib/phosphor-debug-collector/elogid"])
+AC_DEFINE_UNQUOTED([ELOG_ID_PERSIST_PATH], ["$ELOG_ID_PERSIST_PATH"], \
+    [Path of file for storing elog id's, which have associated dumps])
+
 AC_CONFIG_FILES([Makefile])
 AC_OUTPUT
diff --git a/dump_serialize.cpp b/dump_serialize.cpp
new file mode 100644
index 0000000..26990a0
--- /dev/null
+++ b/dump_serialize.cpp
@@ -0,0 +1,35 @@
+#include <cereal/types/set.hpp>
+#include <cereal/archives/binary.hpp>
+#include <fstream>
+
+#include "dump_serialize.hpp"
+
+namespace phosphor
+{
+namespace dump
+{
+namespace elog
+{
+
+void serialize(const ElogList& list, const fs::path& dir)
+{
+    std::ofstream os(dir.c_str(), std::ios::binary);
+    cereal::BinaryOutputArchive oarchive(os);
+    oarchive(list);
+}
+
+bool deserialize(const fs::path& path, ElogList& list)
+{
+    if (fs::exists(path))
+    {
+        std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
+        cereal::BinaryInputArchive iarchive(is);
+        iarchive(list);
+        return true;
+    }
+    return false;
+}
+
+} // namespace elog
+} // namespace dump
+} // namespace phosphor
diff --git a/dump_serialize.hpp b/dump_serialize.hpp
new file mode 100644
index 0000000..ecfc934
--- /dev/null
+++ b/dump_serialize.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <experimental/filesystem>
+
+#include "elog_watch.hpp"
+#include "config.h"
+
+namespace phosphor
+{
+namespace dump
+{
+namespace elog
+{
+
+namespace fs = std::experimental::filesystem;
+
+/** @brief Serialize and persist list of ids.
+ *  @param[in] list - elog id list.
+ *  @param[in] dir - pathname of file where the serialized elog id's will
+ *                   be placed.
+ */
+void serialize(const ElogList& list,
+               const fs::path& dir = fs::path(ELOG_ID_PERSIST_PATH));
+
+/** @brief Deserialze a persisted list of ids into list
+ *  @param[in] path - pathname of persisted error file
+ *  @param[out] list - elog id list
+ *  @return bool - true if the deserialization was successful, false otherwise.
+ */
+bool deserialize(const fs::path& path, ElogList& list);
+
+} // namespace elog
+} // namespace dump
+} // namespace phosphor
diff --git a/elog_watch.cpp b/elog_watch.cpp
index 6c7ff93..5ab65df 100644
--- a/elog_watch.cpp
+++ b/elog_watch.cpp
@@ -3,6 +3,7 @@
 #include "elog_watch.hpp"
 #include "dump_internal.hpp"
 #include "xyz/openbmc_project/Dump/Create/error.hpp"
+#include "dump_serialize.hpp"
 
 namespace phosphor
 {
@@ -23,7 +24,35 @@
 using PropertyMap = std::map<PropertyName, AttributeMap>;
 using LogEntryMsg = std::pair<sdbusplus::message::object_path, PropertyMap>;
 
-void Watch::callback(sdbusplus::message::message& msg)
+Watch::Watch(sdbusplus::bus::bus& bus, IMgr& iMgr):
+    iMgr(iMgr),
+    addMatch(
+        bus,
+        sdbusplus::bus::match::rules::interfacesAdded() +
+        sdbusplus::bus::match::rules::path_namespace(
+            OBJ_LOGGING),
+        std::bind(std::mem_fn(&Watch::addCallback),
+                  this, std::placeholders::_1)),
+    delMatch(
+        bus,
+        sdbusplus::bus::match::rules::interfacesRemoved() +
+        sdbusplus::bus::match::rules::path_namespace(
+            OBJ_LOGGING),
+        std::bind(std::mem_fn(&Watch::delCallback),
+                  this, std::placeholders::_1))
+{
+
+    fs::path file(ELOG_ID_PERSIST_PATH);
+    if (fs::exists(file))
+    {
+        if (!deserialize(ELOG_ID_PERSIST_PATH, elogList))
+        {
+            log<level::ERR>("Error occurred during error id deserialize");
+        }
+    }
+}
+
+void Watch::addCallback(sdbusplus::message::message& msg)
 {
     using Type =
         sdbusplus::xyz::openbmc_project::Dump::Internal::server::Create::Type;
@@ -42,6 +71,15 @@
         return;
     }
 
+    auto eId = getEid(objectPath);
+
+    auto search = elogList.find(eId);
+    if (search != elogList.end())
+    {
+        //elog exists in the list, Skip the dump
+        return;
+    }
+
     auto iter = logEntry.second.find("xyz.openbmc_project.Logging.Entry");
     if (iter == logEntry.second.end())
     {
@@ -62,7 +100,7 @@
         return;
     }
 
-    if (INTERNAL_FAILURE != data)
+    if (data != INTERNAL_FAILURE)
     {
         //Not a InternalFailure, skip
         return;
@@ -73,6 +111,12 @@
 
     try
     {
+        //Save the elog information. This is to avoid dump requests
+        //in elog restore path.
+        elogList.insert(eId);
+
+        phosphor::dump::elog::serialize(elogList);
+
         //Call internal create function to initiate dump
         iMgr.IMgr::create(Type::InternalFailure, fullPaths);
     }
@@ -83,6 +127,26 @@
     return;
 }
 
+void Watch::delCallback(sdbusplus::message::message& msg)
+{
+    sdbusplus::message::object_path logEntry;
+    msg.read(logEntry);
+
+    //Get elog entry message string.
+    std::string objectPath(logEntry);
+
+    //Get elog id
+    auto eId = getEid(objectPath);
+
+    //Delete the elog entry from the list and serialize
+    auto search = elogList.find(eId);
+    if (search != elogList.end())
+    {
+        elogList.erase(search);
+        phosphor::dump::elog::serialize(elogList);
+    }
+}
+
 }//namespace elog
 }//namespace dump
 }//namespace phosphor
diff --git a/elog_watch.hpp b/elog_watch.hpp
index cfdf41a..588ac34 100644
--- a/elog_watch.hpp
+++ b/elog_watch.hpp
@@ -1,9 +1,12 @@
 #pragma once
 
+#include <set>
+#include <cereal/access.hpp>
+
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/server.hpp>
-#include "config.h"
 
+#include "config.h"
 #include "dump_manager.hpp"
 
 namespace phosphor
@@ -14,9 +17,11 @@
 {
 
 using IMgr = phosphor::dump::internal::Manager;
+using EId = uint32_t;
+using ElogList = std::set<EId>;
 
 /** @class Watch
- *  @brief Adds d-bus signal based watch for elog commit.
+ *  @brief Adds d-bus signal based watch for elog add and delete.
  *  @details This implements methods for watching for InternalFailure
  *  type error message and call appropriate function to initiate dump
  */
@@ -30,36 +35,58 @@
         Watch(Watch&&) = default;
         Watch& operator=(Watch&&) = default;
 
-        /** @brief constructs watch for elog commits.
+        /** @brief constructs watch for elog add and delete signals.
          *  @param[in] bus -  The Dbus bus object
          *  @param[in] intMgr - Dump internal Manager object
          */
-        Watch(sdbusplus::bus::bus& bus, IMgr& iMgr):
-            iMgr(iMgr),
-            elogMatch(
-                bus,
-                sdbusplus::bus::match::rules::interfacesAdded() +
-                sdbusplus::bus::match::rules::path_namespace(
-                                              OBJ_LOGGING),
-                std::bind(std::mem_fn(&Watch::callback),
-                          this, std::placeholders::_1))
-        {
-            //Do nothing
-        }
+        Watch(sdbusplus::bus::bus& bus, IMgr& iMgr);
     private:
 
-        /** @brief Callback function for error log commit.
+        friend class cereal::access;
+
+        /** @brief Function required by Cereal to perform serialization.
+         *  @tparam Archive - Cereal archive type (binary in our case).
+         *  @param[in] a - reference to Cereal archive.
+         */
+        template<class Archive>
+        void serialize(Archive& a)
+        {
+            a(elogList);
+        }
+
+        /** @brief Callback function for error log add.
          *  @details InternalError type error message initiates
          *           Internal error type dump request.
          *  @param[in] msg  - Data associated with subscribed signal
          */
-        void callback(sdbusplus::message::message& msg);
+        void addCallback(sdbusplus::message::message& msg);
+
+        /** @brief Callback function for error log delete.
+         *  @param[in] msg  - Data associated with subscribed signal
+         */
+        void delCallback(sdbusplus::message::message& msg);
+
+        /** @brief get elog ID from elog entry object string.
+         *  @param[in] objectPath  - elog entry object path.
+         *  @return - elog id.
+         */
+        inline EId getEid(const std::string& objectPath)
+        {
+            fs::path path(objectPath);
+            return std::stoul(path.filename());
+        }
 
         /**  @brief Dump internal Manager object. */
         IMgr& iMgr;
 
-        /** @brief sdbusplus signal match for elog commit */
-        sdbusplus::bus::match_t elogMatch;
+        /** @brief sdbusplus signal match for elog add */
+        sdbusplus::bus::match_t addMatch;
+
+        /** @brief sdbusplus signal match for elog delete */
+        sdbusplus::bus::match_t delMatch;
+
+        /** @brief List of elog ids, which have associated dumps created */
+        ElogList elogList;
 };
 
 }//namespace elog