Monitor: Support hwmon service offline during startup

It is possible for fan-monitor to startup before the Hwmonitor service,
causing unhandled exceptions that block system initialization. This fix
catches the exception until a proper hwmon presence detector is
deployed.

If the exception is caught, this code change forces a re-subscription
during the poweron event to ensure tach sensors will receive published
updates upon resumption of the hwmon service.

Signed-off-by: Mike Capps <mikepcapps@gmail.com>
Change-Id: I8e696e747c432d7a6f696c5ccd9dab73abf7708f
diff --git a/monitor/system.cpp b/monitor/system.cpp
index 9b27b7e..aa80bd4 100644
--- a/monitor/system.cpp
+++ b/monitor/system.cpp
@@ -77,22 +77,16 @@
                       });
     }
 
-    auto sensorMap = buildNameOwnerChangedMap();
-
-    namespace match = sdbusplus::bus::match;
-
-    // for each service, register a callback handler for nameOwnerChanged
-    for (const auto& service_itr : sensorMap)
+    if (_sensorMatch.empty())
     {
-        _sensorMatch.push_back(std::make_unique<match::match>(
-            _bus, match::rules::nameOwnerChanged(service_itr.first),
-            std::bind(&System::tachSignalOffline, this, std::placeholders::_1,
-                      sensorMap)));
+        subscribeSensorsToServices();
     }
 }
 
-SensorMapType System::buildNameOwnerChangedMap() const
+void System::subscribeSensorsToServices()
 {
+    namespace match = sdbusplus::bus::match;
+
     SensorMapType sensorMap;
 
     // build a list of all interfaces, always including the value interface
@@ -110,36 +104,52 @@
     std::vector<std::string> interfaces(unique_interfaces.begin(),
                                         unique_interfaces.end());
 
-    // get service information for all service names that are
-    // hosting these interfaces
-    auto serviceObjects =
-        util::SDBusPlus::getSubTreeRaw(_bus, FAN_SENSOR_PATH, interfaces, 0);
-
-    for (const auto& fan : _fans)
+    try
     {
-        // For every sensor in each fan
-        for (const auto& sensor : fan->sensors())
+        // get service information for all service names that are
+        // hosting these interfaces
+        auto serviceObjects = util::SDBusPlus::getSubTreeRaw(
+            _bus, FAN_SENSOR_PATH, interfaces, 0);
+
+        for (const auto& fan : _fans)
         {
-            const auto itServ = serviceObjects.find(sensor->name());
-
-            if (serviceObjects.end() == itServ || itServ->second.empty())
+            // For every sensor in each fan
+            for (const auto& sensor : fan->sensors())
             {
-                getLogger().log(
-                    fmt::format("Fan sensor entry {} not found in D-Bus",
-                                sensor->name()),
-                    Logger::error);
-                continue;
-            }
+                const auto itServ = serviceObjects.find(sensor->name());
 
-            for (const auto& [serviceName, unused] : itServ->second)
-            {
-                // map its service name to the sensor
-                sensorMap[serviceName].insert(sensor);
+                if (serviceObjects.end() == itServ || itServ->second.empty())
+                {
+                    getLogger().log(
+                        fmt::format("Fan sensor entry {} not found in D-Bus",
+                                    sensor->name()),
+                        Logger::error);
+                    continue;
+                }
+
+                for (const auto& [serviceName, unused] : itServ->second)
+                {
+                    // associate service name with sensor
+                    sensorMap[serviceName].insert(sensor);
+                }
             }
         }
-    }
 
-    return sensorMap;
+        // only create 1 match per service
+        for (const auto& [serviceName, unused] : sensorMap)
+        {
+            // map its service name to the sensor
+            _sensorMatch.emplace_back(std::make_unique<match::match>(
+                _bus, match::rules::nameOwnerChanged(serviceName),
+                std::bind(&System::tachSignalOffline, this,
+                          std::placeholders::_1, sensorMap)));
+        }
+    }
+    catch (const util::DBusError&)
+    {
+        // catch exception from getSubTreeRaw() when fan sensor paths don't
+        // exist yet
+    }
 }
 
 void System::sighupHandler(sdeventplus::source::Signal&,
@@ -169,6 +179,9 @@
                               rule->check(PowerRuleState::runtime, _fanHealth);
                           });
         }
+
+        _sensorMatch.clear();
+        subscribeSensorsToServices();
     }
     catch (std::runtime_error& re)
     {
@@ -320,6 +333,11 @@
             return;
         }
 
+        if (_sensorMatch.empty())
+        {
+            subscribeSensorsToServices();
+        }
+
         std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
                       [this](auto& rule) {
                           rule->check(PowerRuleState::atPgood, _fanHealth);
diff --git a/monitor/system.hpp b/monitor/system.hpp
index a690567..328c958 100644
--- a/monitor/system.hpp
+++ b/monitor/system.hpp
@@ -186,12 +186,11 @@
     json captureSensorData();
 
     /**
-     * @brief Builds a mapping for sensors to be identified
-     *        for a given service name.
+     * @brief creates a subscription (service->sensor) to take sensors
+     *        on/offline when D-Bus starts/stops updating values
      *
-     * @return a map of service_name->[sensor1,sensor2...]
      */
-    SensorMapType buildNameOwnerChangedMap() const;
+    void subscribeSensorsToServices();
 
     /**
      * @brief Retrieve the configured trust groups