HwmonTempSensor: Watch for InterfacesRemoved

In the current code, if a temperature sensor is on a removable card and
that card is removed, hwmontempsensor does not know it has been removed
and will keep on trying to read the sensor, resulting in it reporting
the sensor is in error state (Value = NaN and
OperationalStatus.Functional = false).  This isn't ideal because a) The
device isn't in the system so why have a sensor for it and b) This error
reading implies there is something wrong when there isn't.

This commit adds support to remove the sensor from D-Bus when its entity
manager configuration interface has been removed from D-Bus, which
entity-manager will do when a currently passing probe statement no
longer passes.  This could happen, for example, when the EEPROM D-Bus
property it was matching on is removed from D-Bus by the daemon that
keeps EEPROM contents on D-Bus when it detects the EEPROM is no longer
there.

Tested: The D-Bus sensor lifetime matches the entity-manager
configuration object lifetime.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I2946e9adf8995abbfa977128a06a5eca84fb46f5
diff --git a/src/HwmonTempMain.cpp b/src/HwmonTempMain.cpp
index d9fe52d..dbc0d2d 100644
--- a/src/HwmonTempMain.cpp
+++ b/src/HwmonTempMain.cpp
@@ -298,6 +298,40 @@
         std::vector<std::string>(sensorTypes.begin(), sensorTypes.end()));
 }
 
+void interfaceRemoved(
+    sdbusplus::message::message& message,
+    boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>&
+        sensors)
+{
+    if (message.is_method_error())
+    {
+        std::cerr << "interfacesRemoved callback method error\n";
+        return;
+    }
+
+    sdbusplus::message::object_path path;
+    std::vector<std::string> interfaces;
+
+    message.read(path, interfaces);
+
+    // If the xyz.openbmc_project.Confguration.X interface was removed
+    // for one or more sensors, delete those sensor objects.
+    auto sensorIt = sensors.begin();
+    while (sensorIt != sensors.end())
+    {
+        if ((sensorIt->second->configurationPath == path) &&
+            (std::find(interfaces.begin(), interfaces.end(),
+                       sensorIt->second->objectType) != interfaces.end()))
+        {
+            sensorIt = sensors.erase(sensorIt);
+        }
+        else
+        {
+            sensorIt++;
+        }
+    }
+}
+
 int main()
 {
     boost::asio::io_service io;
@@ -353,5 +387,18 @@
     }
 
     setupManufacturingModeMatch(*systemBus);
+
+    // Watch for entity-manager to remove configuration interfaces
+    // so the corresponding sensors can be removed.
+    auto ifaceRemovedMatch = std::make_unique<sdbusplus::bus::match::match>(
+        static_cast<sdbusplus::bus::bus&>(*systemBus),
+        "type='signal',member='InterfacesRemoved',arg0path='" +
+            std::string(inventoryPath) + "/'",
+        [&sensors](sdbusplus::message::message& msg) {
+            interfaceRemoved(msg, sensors);
+        });
+
+    matches.emplace_back(std::move(ifaceRemovedMatch));
+
     io.run();
 }