serialize: Add version purpose

Add store/restore of the version purpose. Only need to store it if the
activation was successful, since those are the only versions that are
restored after BMC reboot.

Tested: Verified a code update with an image of purpose System got its
        value restored instead of being set to BMC by default.

Change-Id: I6414e9f3b992a8c29046b4d3a3d581c20a166cee
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/activation.cpp b/activation.cpp
index 0c18821..2966b2f 100644
--- a/activation.cpp
+++ b/activation.cpp
@@ -134,6 +134,9 @@
             {
                 activationProgress->progress(90);
 
+                storePurpose(
+                    versionId,
+                    parent.versions.find(versionId)->second->purpose());
                 if (!redundancyPriority)
                 {
                     redundancyPriority = std::make_unique<RedundancyPriority>(
@@ -188,6 +191,8 @@
 
         flashWrite();
 
+        storePurpose(versionId,
+                     parent.versions.find(versionId)->second->purpose());
         if (!redundancyPriority)
         {
             redundancyPriority =
diff --git a/item_updater.cpp b/item_updater.cpp
index 7fc9fa3..2cd9c39 100644
--- a/item_updater.cpp
+++ b/item_updater.cpp
@@ -190,6 +190,8 @@
             }
 
             auto purpose = server::Version::VersionPurpose::BMC;
+            restorePurpose(id, purpose);
+
             auto path = fs::path(SOFTWARE_OBJPATH) / id;
 
             // Create functional association if this is the functional
diff --git a/item_updater.hpp b/item_updater.hpp
index 4a4d1b4..52607bc 100644
--- a/item_updater.hpp
+++ b/item_updater.hpp
@@ -148,6 +148,10 @@
      */
     void freeSpace(Activation& caller);
 
+    /** @brief Persistent map of Version D-Bus objects and their
+     * version id */
+    std::map<std::string, std::unique_ptr<VersionClass>> versions;
+
   private:
     /** @brief Callback function for Software.Version match.
      *  @details Creates an Activation D-Bus object.
@@ -208,10 +212,6 @@
      * version id */
     std::map<std::string, std::unique_ptr<Activation>> activations;
 
-    /** @brief Persistent map of Version D-Bus objects and their
-     * version id */
-    std::map<std::string, std::unique_ptr<VersionClass>> versions;
-
     /** @brief sdbusplus signal match for Software.Version */
     sdbusplus::bus::match_t versionMatch;
 
diff --git a/serialize.cpp b/serialize.cpp
index 483cd7a..31ac971 100644
--- a/serialize.cpp
+++ b/serialize.cpp
@@ -19,6 +19,7 @@
 namespace fs = std::experimental::filesystem;
 
 const std::string priorityName = "priority";
+const std::string purposeName = "purpose";
 
 void storePriority(const std::string& versionId, uint8_t priority)
 {
@@ -41,6 +42,27 @@
     oarchive(cereal::make_nvp(priorityName, priority));
 }
 
+void storePurpose(const std::string& versionId, VersionPurpose purpose)
+{
+    auto path = fs::path(PERSIST_DIR) / versionId;
+    if (!fs::is_directory(path))
+    {
+        if (fs::exists(path))
+        {
+            // Delete if it's a non-directory file
+            log<level::WARNING>("Removing non-directory file",
+                                entry("PATH=%s", path.c_str()));
+            fs::remove_all(path);
+        }
+        fs::create_directories(path);
+    }
+    path = path / purposeName;
+
+    std::ofstream os(path.c_str());
+    cereal::JSONOutputArchive oarchive(os);
+    oarchive(cereal::make_nvp(purposeName, purpose));
+}
+
 bool restorePriority(const std::string& versionId, uint8_t& priority)
 {
     auto path = fs::path(PERSIST_DIR) / versionId / priorityName;
@@ -100,6 +122,27 @@
     return false;
 }
 
+bool restorePurpose(const std::string& versionId, VersionPurpose& purpose)
+{
+    auto path = fs::path(PERSIST_DIR) / versionId / purposeName;
+    if (fs::exists(path))
+    {
+        std::ifstream is(path.c_str(), std::ios::in);
+        try
+        {
+            cereal::JSONInputArchive iarchive(is);
+            iarchive(cereal::make_nvp(purposeName, purpose));
+            return true;
+        }
+        catch (cereal::Exception& e)
+        {
+            fs::remove_all(path);
+        }
+    }
+
+    return false;
+}
+
 void removePersistDataDirectory(const std::string& versionId)
 {
     auto path = fs::path(PERSIST_DIR) / versionId;
diff --git a/serialize.hpp b/serialize.hpp
index 9a9df09..08fc504 100644
--- a/serialize.hpp
+++ b/serialize.hpp
@@ -2,6 +2,8 @@
 
 #include "config.h"
 
+#include "version.hpp"
+
 #include <experimental/filesystem>
 
 namespace phosphor
@@ -12,6 +14,8 @@
 {
 
 namespace fs = std::experimental::filesystem;
+using VersionPurpose =
+    sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose;
 
 /** @brief Serialization function - stores priority information to file
  *  @param[in] versionId - The version for which to store information.
@@ -19,6 +23,12 @@
  **/
 void storePriority(const std::string& versionId, uint8_t priority);
 
+/** @brief Serialization function - stores purpose information to file
+ *  @param[in] versionId - The version for which to store information.
+ *  @param[in] purpose - VersionPurpose value for that version.
+ **/
+void storePurpose(const std::string& versionId, VersionPurpose purpose);
+
 /** @brief Serialization function - restores priority information from file
  *  @param[in] versionId - The version for which to retrieve information.
  *  @param[in] priority - RedundancyPriority reference for that version.
@@ -26,6 +36,13 @@
  **/
 bool restorePriority(const std::string& versionId, uint8_t& priority);
 
+/** @brief Serialization function - restores purpose information from file
+ *  @param[in] versionId - The version for which to retrieve information.
+ *  @param[in] purpose - VersionPurpose reference for that version.
+ *  @return true if restore was successful, false if not
+ **/
+bool restorePurpose(const std::string& versionId, VersionPurpose& purpose);
+
 /** @brief Removes the serial directory for a given version.
  *  @param[in] versionId - The version for which to remove a file, if it exists.
  **/