monitor: Subscribe to tach target and feedback services

Subscribes to nameOwnerChanged signals for the services of the sensor
and target interfaces for each configured fan. If those services go
offline, the fan tach sensors should get marked nonfunctional due to no
longer receiving updated target or feedback values. In this design, we
use the existing method of determining when a fan tach sensor should be
marked nonfunctional to allow a recovery window, wherein a brief
offline/online transition (such as during a restart) will not trigger a
nonfunctional state change.

Change-Id: I0a935ccad5a864dc952d023185356a1ef1226830
Signed-off-by: Mike Capps <mikepcapps@gmail.com>
diff --git a/monitor/system.cpp b/monitor/system.cpp
index 01f1a9b..9b27b7e 100644
--- a/monitor/system.cpp
+++ b/monitor/system.cpp
@@ -1,5 +1,5 @@
 /**
- * Copyright © 2020 IBM Corporation
+ * Copyright © 2021 IBM Corporation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
 #include "tach_sensor.hpp"
 #include "trust_manager.hpp"
 #include "types.hpp"
+#include "utility.hpp"
 #ifdef MONITOR_USE_JSON
 #include "json_parser.hpp"
 #endif
@@ -75,6 +76,70 @@
                           rule->check(PowerRuleState::runtime, _fanHealth);
                       });
     }
+
+    auto sensorMap = buildNameOwnerChangedMap();
+
+    namespace match = sdbusplus::bus::match;
+
+    // for each service, register a callback handler for nameOwnerChanged
+    for (const auto& service_itr : sensorMap)
+    {
+        _sensorMatch.push_back(std::make_unique<match::match>(
+            _bus, match::rules::nameOwnerChanged(service_itr.first),
+            std::bind(&System::tachSignalOffline, this, std::placeholders::_1,
+                      sensorMap)));
+    }
+}
+
+SensorMapType System::buildNameOwnerChangedMap() const
+{
+    SensorMapType sensorMap;
+
+    // build a list of all interfaces, always including the value interface
+    // using set automatically guards against duplicates
+    std::set<std::string> unique_interfaces{util::FAN_SENSOR_VALUE_INTF};
+
+    for (const auto& fan : _fans)
+    {
+        for (const auto& sensor : fan->sensors())
+        {
+            unique_interfaces.insert(sensor->getInterface());
+        }
+    }
+    // convert them to vector to pass into getSubTreeRaw
+    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)
+    {
+        // For every sensor in each fan
+        for (const auto& sensor : fan->sensors())
+        {
+            const auto itServ = serviceObjects.find(sensor->name());
+
+            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)
+            {
+                // map its service name to the sensor
+                sensorMap[serviceName].insert(sensor);
+            }
+        }
+    }
+
+    return sensorMap;
 }
 
 void System::sighupHandler(sdeventplus::source::Signal&,
@@ -157,6 +222,40 @@
     }
 }
 
+// callback indicating a service went [on|off]line.
+// Determine on/offline status, set all sensors for that service
+// to new state
+//
+void System::tachSignalOffline(sdbusplus::message::message& msg,
+                               SensorMapType const& sensorMap)
+{
+    std::string serviceName, oldOwner, newOwner;
+
+    msg.read(serviceName);
+    msg.read(oldOwner);
+    msg.read(newOwner);
+
+    // true if sensor server came back online, false -> went offline
+    bool hasOwner = !newOwner.empty() && oldOwner.empty();
+
+    std::string stateStr(hasOwner ? "online" : "offline");
+    getLogger().log(fmt::format("Changing sensors for service {} to {}",
+                                serviceName, stateStr),
+                    Logger::info);
+
+    auto sensorItr(sensorMap.find(serviceName));
+
+    if (sensorItr != sensorMap.end())
+    {
+        // set all sensors' owner state to not-owned
+        for (auto& sensor : sensorItr->second)
+        {
+            sensor->setOwner(hasOwner);
+            sensor->getFan().process(*sensor);
+        }
+    }
+}
+
 void System::updateFanHealth(const Fan& fan)
 {
     std::vector<bool> sensorStatus;