Code Update: ApplyTime software manager support

Get the requested image apply time value provided through the
UpdateService redfish schema. If the apply time value is Immediate, then
BMC reboot will be triggered just after the new image activation. The
default apply time value is OnReset in which the new image remains at
Active state and user has to manualy reboot the BMC for applying the new
image.

Tested: Verified that BMC is getting rebooted if the apply time value is
Immediate. OnReset scenario was also tested in which new image remained at
Active state as code update application did not trigger the BMC reboot.

Signed-off-by: Jayashankar Padath <jayashankar.padath@in.ibm.com>
Change-Id: I5efb51f7f36e196d6113cfca6d37c8c6bef70aa2
diff --git a/activation.cpp b/activation.cpp
index 73acc3d..0c18821 100644
--- a/activation.cpp
+++ b/activation.cpp
@@ -4,15 +4,14 @@
 #include "item_updater.hpp"
 #include "serialize.hpp"
 
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
 #include <phosphor-logging/log.hpp>
 #include <sdbusplus/exception.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
 
 #ifdef WANT_SIGNATURE_VERIFY
 #include "image_verify.hpp"
-
-#include <phosphor-logging/elog-errors.hpp>
-#include <phosphor-logging/elog.hpp>
-#include <xyz/openbmc_project/Common/error.hpp>
 #endif
 
 namespace phosphor
@@ -26,10 +25,10 @@
 
 using namespace phosphor::logging;
 using sdbusplus::exception::SdBusError;
-
-#ifdef WANT_SIGNATURE_VERIFY
 using InternalFailure =
     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+#ifdef WANT_SIGNATURE_VERIFY
 namespace control = sdbusplus::xyz::openbmc_project::Control::server;
 #endif
 
@@ -64,7 +63,15 @@
 {
     auto method = this->bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
                                             SYSTEMD_INTERFACE, "Unsubscribe");
-    this->bus.call_noreply(method);
+    try
+    {
+        this->bus.call_noreply(method);
+    }
+    catch (const SdBusError& e)
+    {
+        log<level::ERR>("Error in unsubscribing from systemd signals",
+                        entry("ERROR=%s", e.what()));
+    }
 
     return;
 }
@@ -151,6 +158,13 @@
                 // Create active association
                 parent.createActiveAssociation(path);
 
+                if (Activation::checkApplyTimeImmediate() == true)
+                {
+                    log<level::INFO>("Image Active. ApplyTime is immediate, "
+                                     "rebooting BMC.");
+                    Activation::rebootBmc();
+                }
+
                 return softwareServer::Activation::activation(
                     softwareServer::Activation::Activations::Active);
             }
@@ -307,6 +321,63 @@
     bus.call_noreply(method);
 }
 
+bool Activation::checkApplyTimeImmediate()
+{
+    auto service = utils::getService(bus, applyTimeObjPath, applyTimeIntf);
+    if (service.empty())
+    {
+        log<level::INFO>("Error getting the service name for BMC image "
+                         "ApplyTime. The BMC needs to be manually rebooted to "
+                         "complete the image activation if needed "
+                         "immediately.");
+    }
+    else
+    {
+
+        auto method = bus.new_method_call(service.c_str(), applyTimeObjPath,
+                                          dbusPropIntf, "Get");
+        method.append(applyTimeIntf, applyTimeProp);
+
+        try
+        {
+            auto reply = bus.call(method);
+
+            sdbusplus::message::variant<std::string> result;
+            reply.read(result);
+            auto applyTime =
+                sdbusplus::message::variant_ns::get<std::string>(result);
+            if (applyTime == applyTimeImmediate)
+            {
+                return true;
+            }
+        }
+        catch (const SdBusError& e)
+        {
+            log<level::ERR>("Error in getting ApplyTime",
+                            entry("ERROR=%s", e.what()));
+        }
+    }
+    return false;
+}
+
+void Activation::rebootBmc()
+{
+    auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
+                                      SYSTEMD_INTERFACE, "StartUnit");
+    method.append("force-reboot.service", "replace");
+    try
+    {
+        auto reply = bus.call(method);
+    }
+    catch (const SdBusError& e)
+    {
+        log<level::ALERT>("Error in trying to reboot the BMC. "
+                          "The BMC needs to be manually rebooted to complete "
+                          "the image activation.");
+        report<InternalFailure>();
+    }
+}
+
 } // namespace updater
 } // namespace software
 } // namespace phosphor