BMC: Update u-boot environment variable based on lowest priority

- If a BMC is set to the lowest priority then update the u-boot
  environment variables so that that system boots from that version
  on the next reboot.

Resolves openbmc/openbmc#2284

Change-Id: If0b67b07496f602fa06607bd0685d6394cb8d9fd
Signed-off-by: Saqib Khan <khansa@us.ibm.com>
diff --git a/activation.cpp b/activation.cpp
index 9d2ba4a..d7f9e24 100644
--- a/activation.cpp
+++ b/activation.cpp
@@ -2,6 +2,8 @@
 #include "item_updater.hpp"
 #include "config.h"
 #include "serialize.hpp"
+#include <phosphor-logging/log.hpp>
+
 
 namespace phosphor
 {
@@ -12,6 +14,8 @@
 
 namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server;
 
+using namespace phosphor::logging;
+
 void Activation::subscribeToSystemdSignals()
 {
     auto method = this->bus.new_method_call(SYSTEMD_BUSNAME,
@@ -149,11 +153,41 @@
 
 uint8_t RedundancyPriority::priority(uint8_t value)
 {
-    parent.parent.freePriority(value);
+    parent.parent.freePriority(value, parent.versionId);
     storeToFile(parent.versionId, value);
+
+    if(parent.parent.isLowestPriority(value))
+    {
+        parent.updateUbootEnvVars();
+    }
+
     return softwareServer::RedundancyPriority::priority(value);
 }
 
+// TODO: openbmc/openbmc#2369 Add recovery policy to updateubootvars
+//       unit template.
+// TODO: openbmc/openbmc#2370 Call StartUnit synchronously to handle
+//       Errors more gracefully.
+void Activation::updateUbootEnvVars()
+{
+    auto method = bus.new_method_call(
+            SYSTEMD_BUSNAME,
+            SYSTEMD_PATH,
+            SYSTEMD_INTERFACE,
+            "StartUnit");
+    auto updateEnvVarsFile = "obmc-flash-bmc-updateubootvars@" + versionId +
+            ".service";
+    method.append(updateEnvVarsFile, "replace");
+    auto result = bus.call(method);
+
+    //Check that the bus call didn't result in an error
+    if (result.is_method_error())
+    {
+        log<level::ERR>("Failed to update u-boot env variables",
+                        entry(" %s", SYSTEMD_INTERFACE));
+    }
+}
+
 void Activation::unitStateChange(sdbusplus::message::message& msg)
 {
     if (softwareServer::Activation::activation() !=
diff --git a/activation.hpp b/activation.hpp
index cf4b5c2..a3f2a62 100644
--- a/activation.hpp
+++ b/activation.hpp
@@ -266,6 +266,12 @@
         void unsubscribeFromSystemdSignals();
 
         /**
+         * @brief Updates the uboot variables to point to versionId, so that
+         *        the systems boots from this version on the next boot.
+         */
+        void updateUbootEnvVars();
+
+        /**
          * @brief delete the d-bus object.
          */
         void delete_() override;
diff --git a/item_updater.cpp b/item_updater.cpp
index 34096df..a19aebc 100644
--- a/item_updater.cpp
+++ b/item_updater.cpp
@@ -323,14 +323,15 @@
     return ItemUpdater::ActivationStatus::ready;
 }
 
-void ItemUpdater::freePriority(uint8_t value)
+void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
 {
     //TODO openbmc/openbmc#1896 Improve the performance of this function
     for (const auto& intf : activations)
     {
         if (intf.second->redundancyPriority)
         {
-            if (intf.second->redundancyPriority.get()->priority() == value)
+            if (intf.second->redundancyPriority.get()->priority() == value &&
+                intf.second->versionId != versionId)
             {
                 intf.second->redundancyPriority.get()->priority(value + 1);
             }
@@ -500,6 +501,21 @@
     }
 }
 
+bool ItemUpdater::isLowestPriority(uint8_t value)
+{
+    for (const auto& intf : activations)
+    {
+        if(intf.second->redundancyPriority)
+        {
+            if (intf.second->redundancyPriority.get()->priority() < value)
+            {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
 } // namespace updater
 } // namespace software
 } // namespace phosphor
diff --git a/item_updater.hpp b/item_updater.hpp
index 4b7ddd5..d55e807 100644
--- a/item_updater.hpp
+++ b/item_updater.hpp
@@ -66,10 +66,11 @@
      *  any existing priority with the same value by 1
      *
      *  @param[in] value - The priority that needs to be set free.
-     *
+     *  @param[in] versionId - The Id of the version for which we
+     *                         are trying to free up the priority.
      *  @return None
      */
-    void freePriority(uint8_t value);
+    void freePriority(uint8_t value, const std::string& versionId);
 
     /**
      * @brief Create and populate the active BMC Version.
@@ -98,6 +99,15 @@
      */
     void removeActiveAssociation(const std::string& path);
 
+    /** @brief Determine if the given priority is the lowest
+     *
+     *  @param[in] value - The priority that needs to be checked.
+     *
+     *  @return boolean corresponding to whether the given
+     *      priority is lowest.
+     */
+    bool isLowestPriority(uint8_t value);
+
     private:
         /** @brief Callback function for Software.Version match.
          *  @details Creates an Activation dbus object.