Add InterfacesRemoved signal handling

When an InterfacesRemoved signal is received for a subscribed object
path, each interface returned is checked against the interface which was
defined for each object on the event. When these are equal, the
interface (and all associated properties) are removed from the shared
cache of event properties.

Tested:
    Manually added an InterfacesRemoved signal
    Verified interface was removed from object path in cache

Change-Id: I348d82f14e0cfba2b18a81a9f54c6cb06b586797
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/functor.hpp b/control/functor.hpp
index 8e85fcb..0a2b955 100644
--- a/control/functor.hpp
+++ b/control/functor.hpp
@@ -255,6 +255,86 @@
 }
 
 /**
+ * @struct Interface Removed
+ * @brief A match filter functor for Dbus interface removed signals
+ *
+ * @tparam U - The type of the handler
+ */
+template <typename U>
+struct InterfaceRemoved
+{
+    InterfaceRemoved() = delete;
+    ~InterfaceRemoved() = default;
+    InterfaceRemoved(const InterfaceRemoved&) = default;
+    InterfaceRemoved& operator=(const InterfaceRemoved&) = default;
+    InterfaceRemoved(InterfaceRemoved&&) = default;
+    InterfaceRemoved& operator=(InterfaceRemoved&&) = default;
+    InterfaceRemoved(const char* path,
+                     const char* iface,
+                     U&& handler) :
+        _path(path),
+        _iface(iface),
+        _handler(std::forward<U>(handler)) { }
+
+    /** @brief Run signal handler function
+     *
+     * Extract the property from the InterfacesRemoved
+     * message and run the handler function.
+     */
+    void operator()(sdbusplus::bus::bus&,
+                    sdbusplus::message::message& msg,
+                    Zone& zone) const
+    {
+        if (msg)
+        {
+            std::vector<std::string> intfs;
+            sdbusplus::message::object_path op;
+
+            msg.read(op);
+            if (static_cast<const std::string&>(op) != _path)
+            {
+                // Object path does not match this handler's path
+                return;
+            }
+
+            msg.read(intfs);
+            auto itIntf = std::find(intfs.begin(), intfs.end(), _iface);
+            if (itIntf == intfs.cend())
+            {
+                // Interface not found on this handler's path
+                return;
+            }
+
+            _handler(zone);
+        }
+    }
+
+private:
+    const char* _path;
+    const char* _iface;
+    U _handler;
+};
+
+/**
+ * @brief Used to process a Dbus interface removed signal event
+ *
+ * @param[in] path - Object path
+ * @param[in] iface - Object interface
+ * @param[in] handler - Handler function to perform
+ *
+ * @tparam U - The type of the handler
+ */
+template <typename U>
+auto objectSignal(const char* path,
+                  const char* iface,
+                  U&& handler)
+{
+    return InterfaceRemoved<U>(path,
+                               iface,
+                               std::forward<U>(handler));
+}
+
+/**
  * @struct Name Owner Changed
  * @brief A match filter functor for Dbus name owner changed signals
  *
diff --git a/control/handlers.hpp b/control/handlers.hpp
index 96cfa2f..ef5d5d8 100644
--- a/control/handlers.hpp
+++ b/control/handlers.hpp
@@ -49,6 +49,25 @@
     };
 }
 
+/**
+ * @brief A handler function to remove an interface from an object path
+ * @details Removes an interface from an object's path which includes removing
+ * all properties that would be under that interface
+ *
+ * @param[in] path - Object's path name
+ * @param[in] interface - Object's interface name
+ *
+ * @return Lambda function
+ *     A lambda function to remove the interface
+ */
+auto removeInterface(const char* path, const char* interface)
+{
+    return[=](auto& zone)
+    {
+        zone.removeObjIntf(path, interface);
+    };
+}
+
 } // namespace handler
 } // namespace control
 } // namespace fan
diff --git a/control/matches.hpp b/control/matches.hpp
index 855a749..ccacbc4 100644
--- a/control/matches.hpp
+++ b/control/matches.hpp
@@ -47,6 +47,20 @@
 }
 
 /**
+ * @brief A match function that constructs an InterfacesRemoved match string
+ * @details Constructs an InterfacesRemoved match string with a given object
+ * path
+ *
+ * @param[in] obj - Object's path name
+ *
+ * @return - An InterfacesRemoved match string
+ */
+inline auto interfacesRemoved(const std::string& obj)
+{
+    return rules::interfacesRemoved(obj);
+}
+
+/**
  * @brief A match function that constructs a NameOwnerChanged match string
  * @details Constructs a NameOwnerChanged match string with a given object
  * path and interface
diff --git a/control/zone.hpp b/control/zone.hpp
index c047b62..481f12c 100644
--- a/control/zone.hpp
+++ b/control/zone.hpp
@@ -151,6 +151,22 @@
         };
 
         /**
+         * @brief Remove an object's interface
+         *
+         * @param[in] object - Name of the object with the interface
+         * @param[in] interface - Interface name to remove
+         */
+        inline void removeObjIntf(const char* object,
+                                  const char* interface)
+        {
+            auto it = _properties.find(object);
+            if (it != std::end(_properties))
+            {
+                _properties[object].erase(interface);
+            }
+        }
+
+        /**
          * @brief Remove a service associated to a group
          *
          * @param[in] group - Group associated with service