Host updater: Remove the Object.Delete interface from functional version

This commit enhances the host updater by dynamically removing the
Object.Delete interface from a host activation that is currently
running. Once the host isn't running anymore, the interface is re-added
so that the activation may be deleted.

Additionally, isVersionFunctional() from  the parent updater is exposed,
since this function is needed to determine whether a given activation
is currently running on the host.

Add the Delete interface to all interfaces when the item updater starts
up, because the chassis state would be off initially, and in the case
where it automatically powers on because the BMC rebooted while the host
was on, the chassis property signal would cause the Delete interface to
be removed.

Change-Id: I4afcc1ebe2e8a3ce212b426749295e79b68cac62
Signed-off-by: Michael Tritz <mtritz@us.ibm.com>
diff --git a/activation.cpp b/activation.cpp
index f7cfd1a..cd973b5 100755
--- a/activation.cpp
+++ b/activation.cpp
@@ -208,12 +208,44 @@
     return;
 }
 
-void Activation::delete_()
+void Activation::updateDeleteInterface(sdbusplus::message::message& msg)
+{
+    std::string interface, chassisState;
+    std::map<std::string, sdbusplus::message::variant<std::string>> properties;
+
+    msg.read(interface, properties);
+
+    for (const auto& p : properties)
+    {
+        if (p.first == "CurrentPowerState")
+        {
+            chassisState = p.second.get<std::string>();
+        }
+    }
+
+    if ((parent.isVersionFunctional(this->versionId)) &&
+        (chassisState != CHASSIS_STATE_OFF))
+    {
+        if (deleteObject)
+        {
+            deleteObject.reset(nullptr);
+        }
+    }
+    else
+    {
+        if (!deleteObject)
+        {
+            deleteObject = std::make_unique<Delete>(bus, path, *this);
+        }
+    }
+}
+
+void Delete::delete_()
 {
     // Remove active association
-    parent.removeActiveAssociation(path);
+    parent.parent.removeActiveAssociation(parent.path);
 
-    parent.erase(versionId);
+    parent.parent.erase(parent.versionId);
 }
 
 } // namespace updater
diff --git a/activation.hpp b/activation.hpp
index 3fcb622..286c85d 100755
--- a/activation.hpp
+++ b/activation.hpp
@@ -8,6 +8,7 @@
 #include "xyz/openbmc_project/Software/ActivationProgress/server.hpp"
 #include "xyz/openbmc_project/Object/Delete/server.hpp"
 #include "org/openbmc/Associations/server.hpp"
+#include "config.h"
 
 namespace openpower
 {
@@ -19,7 +20,6 @@
 using AssociationList =
         std::vector<std::tuple<std::string, std::string, std::string>>;
 using ActivationInherit = sdbusplus::server::object::object<
-    sdbusplus::xyz::openbmc_project::Object::server::Delete,
     sdbusplus::xyz::openbmc_project::Software::server::ExtendedVersion,
     sdbusplus::xyz::openbmc_project::Software::server::Activation,
     sdbusplus::org::openbmc::server::Associations>;
@@ -29,6 +29,8 @@
     sdbusplus::xyz::openbmc_project::Software::server::RedundancyPriority>;
 using ActivationProgressInherit = sdbusplus::server::object::object<
     sdbusplus::xyz::openbmc_project::Software::server::ActivationProgress>;
+using DeleteInherit = sdbusplus::server::object::object<
+        sdbusplus::xyz::openbmc_project::Object::server::Delete>;
 
 namespace sdbusRule = sdbusplus::bus::match::rules;
 
@@ -168,6 +170,53 @@
         std::string path;
 };
 
+/** @class ActivationDelete
+ *  @brief OpenBMC Delete implementation.
+ *  @details A concrete implementation for xyz.openbmc_project.Object.Delete
+ *  D-Bus API.
+ */
+class Delete : public DeleteInherit
+{
+    public:
+        /** @brief Constructs Delete.
+          *
+          * @param[in] bus    - The D-Bus bus object
+          * @param[in] path   - The D-Bus object path
+          * @param[in] parent - Parent object.
+          */
+        Delete(sdbusplus::bus::bus& bus,
+               const std::string& path,
+               Activation& parent) :
+                DeleteInherit(bus, path.c_str(), true),
+                parent(parent),
+                bus(bus),
+                path(path)
+        {
+            std::vector<std::string> interfaces({interface});
+            bus.emit_interfaces_added(path.c_str(), interfaces);
+        }
+
+        ~Delete()
+        {
+            std::vector<std::string> interfaces({interface});
+            bus.emit_interfaces_removed(path.c_str(), interfaces);
+        }
+
+        /**
+         * @brief delete the D-Bus object.
+         */
+        void delete_() override;
+
+        /** @brief Parent Object. */
+        Activation& parent;
+
+    private:
+        static constexpr auto interface =
+                "xyz.openbmc_project.Object.Delete";
+        sdbusplus::bus::bus& bus;
+        std::string path;
+};
+
 /** @class Activation
  *  @brief OpenBMC activation software management implementation.
  *  @details A concrete implementation for
@@ -206,7 +255,17 @@
                            sdbusRule::interface(
                                    "org.freedesktop.systemd1.Manager"),
                            std::bind(std::mem_fn(&Activation::unitStateChange),
-                                  this, std::placeholders::_1))
+                                  this, std::placeholders::_1)),
+                  chassisStateSignals(
+                          bus,
+                          sdbusRule::type::signal() +
+                          sdbusRule::member("PropertiesChanged") +
+                          sdbusRule::path(CHASSIS_STATE_PATH) +
+                          sdbusRule::argN(0, CHASSIS_STATE_OBJ) +
+                          sdbusRule::interface(SYSTEMD_PROPERTY_INTERFACE),
+                          std::bind(std::mem_fn(
+                                  &Activation::updateDeleteInterface), this,
+                                  std::placeholders::_1))
         {
             // Enable systemd signals
             subscribeToSystemdSignals();
@@ -252,6 +311,17 @@
          */
         void unitStateChange(sdbusplus::message::message& msg);
 
+        /** @brief Update the Object.Delete interface for this activation
+         *
+         * Update the delete interface based on whether or not this activation
+         * is currently functional. A functional activation will have no
+         * Object.Delete, while a non-functional activation will have one.
+         *
+         * @param[in]  msg       - Data associated with subscribed signal
+         *
+         */
+        void updateDeleteInterface(sdbusplus::message::message& msg);
+
         /**
          * @brief subscribe to the systemd signals
          *
@@ -291,9 +361,15 @@
         /** @brief Persistent RedundancyPriority dbus object */
         std::unique_ptr<RedundancyPriority> redundancyPriority;
 
+        /** @brief Persistent Delete dbus object */
+        std::unique_ptr<Delete> deleteObject;
+
         /** @brief Used to subscribe to dbus systemd signals **/
         sdbusplus::bus::match_t systemdSignals;
 
+        /** @brief Used to subscribe to chassis power state changes **/
+        sdbusplus::bus::match_t chassisStateSignals;
+
         /** @brief Tracks whether the read-only & read-write volumes have been
          *created as part of the activation process. **/
         bool ubiVolumesCreated = false;
@@ -304,12 +380,6 @@
          */
         using ActivationInherit::activation;
 
-        /** @brief Deletes the d-bus object.
-         *
-         *
-         * */
-        void delete_() override;
-
     private:
         /** @brief Member function for clarity & brevity at activation start */
         void startActivation();
diff --git a/configure.ac b/configure.ac
index 4996623..96cde53 100755
--- a/configure.ac
+++ b/configure.ac
@@ -76,6 +76,13 @@
 AS_IF([test "x$MANIFEST_FILE" == "x"], [MANIFEST_FILE="MANIFEST"])
 AC_DEFINE_UNQUOTED([MANIFEST_FILE], ["$MANIFEST_FILE"], [The path to the MANIFEST file])
 
+AC_DEFINE(CHASSIS_STATE_PATH, "/xyz/openbmc_project/state/chassis0",
+    [The chassis state path.])
+AC_DEFINE(CHASSIS_STATE_OBJ, "xyz.openbmc_project.State.Chassis",
+    [The chassis state interface.])
+AC_DEFINE(CHASSIS_STATE_OFF, "xyz.openbmc_project.State.Chassis.PowerState.Off",
+    [The chassis state off property value.])
+
 AC_DEFINE(MAPPER_BUSNAME, "xyz.openbmc_project.ObjectMapper",
     [The object mapper busname.])
 AC_DEFINE(MAPPER_PATH, "/xyz/openbmc_project/object_mapper",
@@ -89,6 +96,8 @@
     [systemd path.])
 AC_DEFINE(SYSTEMD_INTERFACE, "org.freedesktop.systemd1.Manager",
     [systemd interface.])
+AC_DEFINE(SYSTEMD_PROPERTY_INTERFACE, "org.freedesktop.DBus.Properties",
+    [systemd properties interface.])
 
 AC_DEFINE(PNOR_TOC_FILE, "pnor.toc",
     [The name of the PNOR table of contents file])
diff --git a/item_updater.cpp b/item_updater.cpp
index 6b9ec1c..60ea7ce 100644
--- a/item_updater.cpp
+++ b/item_updater.cpp
@@ -26,11 +26,6 @@
 using namespace phosphor::logging;
 
 constexpr auto squashFSImage = "pnor.xz.squashfs";
-constexpr auto CHASSIS_STATE_PATH = "/xyz/openbmc_project/state/chassis0";
-constexpr auto CHASSIS_STATE_OBJ = "xyz.openbmc_project.State.Chassis";
-constexpr auto CHASSIS_STATE_OFF =
-                        "xyz.openbmc_project.State.Chassis.PowerState.Off";
-constexpr auto SYSTEMD_PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
 
 // TODO: Change paths once openbmc/openbmc#1663 is completed.
 constexpr auto MBOXD_INTERFACE = "org.openbmc.mboxd";
@@ -135,6 +130,12 @@
                         extendedVersion,
                         activationState,
                         associations)));
+
+        activations.find(versionId)->second->deleteObject =
+                std::make_unique<Delete>(bus,
+                                         path,
+                                         *activations.find(versionId)->second);
+
         versions.insert(std::make_pair(
                             versionId,
                             std::make_unique<Version>(
@@ -221,6 +222,11 @@
                                        activationState,
                                        associations)));
 
+            activations.find(id)->second->deleteObject =
+                    std::make_unique<Delete>(bus,
+                                             path,
+                                             *activations.find(id)->second);
+
             // If Active, create RedundancyPriority instance for this version.
             if (activationState == server::Activation::Activations::Active)
             {
@@ -361,7 +367,7 @@
     return;
 }
 
-bool ItemUpdater::isVersionFunctional(std::string versionId)
+bool ItemUpdater::isVersionFunctional(const std::string& versionId)
 {
     if (!fs::exists(PNOR_RO_ACTIVE_PATH))
     {
diff --git a/item_updater.hpp b/item_updater.hpp
index 893e8b9..e0c4450 100755
--- a/item_updater.hpp
+++ b/item_updater.hpp
@@ -172,6 +172,14 @@
         /** @brief Persistent GardReset dbus object */
         std::unique_ptr<GardReset> gardReset;
 
+        /** @brief Check whether the provided image id is the functional one
+         *
+         * @param[in] - versionId - The id of the image to check.
+         *
+         * @return - Returns true if this version is currently functional.
+         */
+        static bool isVersionFunctional(const std::string& versionId);
+
     private:
         /** @brief Callback function for Software.Version match.
          *  @details Creates an Activation D-Bus object.
@@ -227,14 +235,6 @@
           * Activation D-Bus object */
         void reset() override;
 
-        /** @brief Check whether the provided image id is the functional one
-         *
-         * @param[in] - versionId - The id of the image to check.
-         *
-         * @return - Returns true if this version is currently functional.
-         */
-        static bool isVersionFunctional(std::string versionId);
-
         /** @brief Check whether the host is running
          *
          * @return - Returns true if the Chassis is powered on.