12a40e939SJosh Lehan #include "ExternalSensor.hpp"
22a40e939SJosh Lehan #include "Utils.hpp"
32a40e939SJosh Lehan #include "VariantVisitors.hpp"
42a40e939SJosh Lehan 
52a40e939SJosh Lehan #include <boost/algorithm/string/predicate.hpp>
62a40e939SJosh Lehan #include <boost/algorithm/string/replace.hpp>
72a40e939SJosh Lehan #include <boost/container/flat_map.hpp>
82a40e939SJosh Lehan #include <boost/container/flat_set.hpp>
92a40e939SJosh Lehan #include <sdbusplus/asio/connection.hpp>
102a40e939SJosh Lehan #include <sdbusplus/asio/object_server.hpp>
112a40e939SJosh Lehan #include <sdbusplus/bus/match.hpp>
122a40e939SJosh Lehan 
132a40e939SJosh Lehan #include <array>
142a40e939SJosh Lehan #include <filesystem>
152a40e939SJosh Lehan #include <fstream>
162a40e939SJosh Lehan #include <functional>
172a40e939SJosh Lehan #include <memory>
182a40e939SJosh Lehan #include <regex>
192a40e939SJosh Lehan #include <stdexcept>
202a40e939SJosh Lehan #include <string>
212a40e939SJosh Lehan #include <utility>
222a40e939SJosh Lehan #include <variant>
232a40e939SJosh Lehan #include <vector>
242a40e939SJosh Lehan 
252a40e939SJosh Lehan // Copied from HwmonTempSensor and inspired by
262a40e939SJosh Lehan // https://gerrit.openbmc-project.xyz/c/openbmc/dbus-sensors/+/35476
272a40e939SJosh Lehan 
282a40e939SJosh Lehan // The ExternalSensor is a sensor whose value is intended to be writable
292a40e939SJosh Lehan // by something external to the BMC, so that the host (or something else)
307243217bSJosh Lehan // can write to it, perhaps by using an IPMI or Redfish connection.
312a40e939SJosh Lehan 
322a40e939SJosh Lehan // Unlike most other sensors, an external sensor does not correspond
337243217bSJosh Lehan // to a hwmon file or any other kernel/hardware interface,
342a40e939SJosh Lehan // so, after initialization, this module does not have much to do,
352a40e939SJosh Lehan // but it handles reinitialization and thresholds, similar to the others.
367243217bSJosh Lehan // The main work of this module is to provide backing storage for a
377243217bSJosh Lehan // sensor that exists only virtually, and to provide an optional
387243217bSJosh Lehan // timeout service for detecting loss of timely updates.
392a40e939SJosh Lehan 
402a40e939SJosh Lehan // As there is no corresponding driver or hardware to support,
412a40e939SJosh Lehan // all configuration of this sensor comes from the JSON parameters:
427243217bSJosh Lehan // MinValue, MaxValue, Timeout, PowerState, Units, Name
432a40e939SJosh Lehan 
447243217bSJosh Lehan // The purpose of "Units" is to specify the physical characteristic
452a40e939SJosh Lehan // the external sensor is measuring, because with an external sensor
462a40e939SJosh Lehan // there is no other way to tell, and it will be used for the object path
477243217bSJosh Lehan // here: /xyz/openbmc_project/sensors/<Units>/<Name>
487243217bSJosh Lehan 
497243217bSJosh Lehan // For more information, see external-sensor.md design document:
507243217bSJosh Lehan // https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/41452
517243217bSJosh Lehan // https://github.com/openbmc/docs/tree/master/designs/
522a40e939SJosh Lehan 
538a57ec09SEd Tanous static constexpr bool debug = false;
542a40e939SJosh Lehan 
552a40e939SJosh Lehan static const char* sensorType =
562a40e939SJosh Lehan     "xyz.openbmc_project.Configuration.ExternalSensor";
572a40e939SJosh Lehan 
587243217bSJosh Lehan void updateReaper(boost::container::flat_map<
597243217bSJosh Lehan                       std::string, std::shared_ptr<ExternalSensor>>& sensors,
607243217bSJosh Lehan                   boost::asio::steady_timer& timer,
617243217bSJosh Lehan                   const std::chrono::steady_clock::time_point& now)
627243217bSJosh Lehan {
637243217bSJosh Lehan     // First pass, reap all stale sensors
647243217bSJosh Lehan     for (auto& sensor : sensors)
657243217bSJosh Lehan     {
667243217bSJosh Lehan         if (!sensor.second)
677243217bSJosh Lehan         {
687243217bSJosh Lehan             continue;
697243217bSJosh Lehan         }
707243217bSJosh Lehan 
717243217bSJosh Lehan         if (!sensor.second->isAliveAndPerishable())
727243217bSJosh Lehan         {
737243217bSJosh Lehan             continue;
747243217bSJosh Lehan         }
757243217bSJosh Lehan 
767243217bSJosh Lehan         if (!sensor.second->isAliveAndFresh(now))
777243217bSJosh Lehan         {
787243217bSJosh Lehan             // Mark sensor as dead, no longer alive
797243217bSJosh Lehan             sensor.second->writeInvalidate();
807243217bSJosh Lehan         }
817243217bSJosh Lehan     }
827243217bSJosh Lehan 
837243217bSJosh Lehan     std::chrono::steady_clock::duration nextCheck;
847243217bSJosh Lehan     bool needCheck = false;
857243217bSJosh Lehan 
867243217bSJosh Lehan     // Second pass, determine timer interval to next check
877243217bSJosh Lehan     for (auto& sensor : sensors)
887243217bSJosh Lehan     {
897243217bSJosh Lehan         if (!sensor.second)
907243217bSJosh Lehan         {
917243217bSJosh Lehan             continue;
927243217bSJosh Lehan         }
937243217bSJosh Lehan 
947243217bSJosh Lehan         if (!sensor.second->isAliveAndPerishable())
957243217bSJosh Lehan         {
967243217bSJosh Lehan             continue;
977243217bSJosh Lehan         }
987243217bSJosh Lehan 
997243217bSJosh Lehan         auto expiration = sensor.second->ageRemaining(now);
1007243217bSJosh Lehan 
1017243217bSJosh Lehan         if (needCheck)
1027243217bSJosh Lehan         {
1037243217bSJosh Lehan             nextCheck = std::min(nextCheck, expiration);
1047243217bSJosh Lehan         }
1057243217bSJosh Lehan         else
1067243217bSJosh Lehan         {
1077243217bSJosh Lehan             // Initialization
1087243217bSJosh Lehan             nextCheck = expiration;
1097243217bSJosh Lehan             needCheck = true;
1107243217bSJosh Lehan         }
1117243217bSJosh Lehan     }
1127243217bSJosh Lehan 
1137243217bSJosh Lehan     if (!needCheck)
1147243217bSJosh Lehan     {
1157243217bSJosh Lehan         if constexpr (debug)
1167243217bSJosh Lehan         {
1177243217bSJosh Lehan             std::cerr << "Next ExternalSensor timer idle\n";
1187243217bSJosh Lehan         }
1197243217bSJosh Lehan 
1207243217bSJosh Lehan         return;
1217243217bSJosh Lehan     }
1227243217bSJosh Lehan 
1237243217bSJosh Lehan     timer.expires_at(now + nextCheck);
1247243217bSJosh Lehan 
1257243217bSJosh Lehan     timer.async_wait([&sensors, &timer](const boost::system::error_code& err) {
1267243217bSJosh Lehan         if (err != boost::system::errc::success)
1277243217bSJosh Lehan         {
1287243217bSJosh Lehan             // Cancellation is normal, as timer is dynamically rescheduled
1290362738dSJosh Lehan             if (err != boost::asio::error::operation_aborted)
1307243217bSJosh Lehan             {
1317243217bSJosh Lehan                 std::cerr << "ExternalSensor timer scheduling problem: "
1327243217bSJosh Lehan                           << err.message() << "\n";
1337243217bSJosh Lehan             }
1347243217bSJosh Lehan             return;
1357243217bSJosh Lehan         }
1360362738dSJosh Lehan 
1377243217bSJosh Lehan         updateReaper(sensors, timer, std::chrono::steady_clock::now());
1387243217bSJosh Lehan     });
1397243217bSJosh Lehan 
1407243217bSJosh Lehan     if constexpr (debug)
1417243217bSJosh Lehan     {
1427243217bSJosh Lehan         std::cerr << "Next ExternalSensor timer "
1437243217bSJosh Lehan                   << std::chrono::duration_cast<std::chrono::microseconds>(
1447243217bSJosh Lehan                          nextCheck)
1457243217bSJosh Lehan                          .count()
1467243217bSJosh Lehan                   << " us\n";
1477243217bSJosh Lehan     }
1487243217bSJosh Lehan }
1497243217bSJosh Lehan 
1502a40e939SJosh Lehan void createSensors(
151*8a17c303SEd Tanous     sdbusplus::asio::object_server& objectServer,
1522a40e939SJosh Lehan     boost::container::flat_map<std::string, std::shared_ptr<ExternalSensor>>&
1532a40e939SJosh Lehan         sensors,
1542a40e939SJosh Lehan     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
1552a40e939SJosh Lehan     const std::shared_ptr<boost::container::flat_set<std::string>>&
1567243217bSJosh Lehan         sensorsChanged,
1577243217bSJosh Lehan     boost::asio::steady_timer& reaperTimer)
1582a40e939SJosh Lehan {
1590362738dSJosh Lehan     if constexpr (debug)
1600362738dSJosh Lehan     {
1610362738dSJosh Lehan         std::cerr << "ExternalSensor considering creating sensors\n";
1620362738dSJosh Lehan     }
1630362738dSJosh Lehan 
1642a40e939SJosh Lehan     auto getter = std::make_shared<GetSensorConfiguration>(
1652a40e939SJosh Lehan         dbusConnection,
166*8a17c303SEd Tanous         [&objectServer, &sensors, &dbusConnection, sensorsChanged,
1677243217bSJosh Lehan          &reaperTimer](const ManagedObjectType& sensorConfigurations) {
1682a40e939SJosh Lehan             bool firstScan = (sensorsChanged == nullptr);
1692a40e939SJosh Lehan 
1702a40e939SJosh Lehan             for (const std::pair<sdbusplus::message::object_path, SensorData>&
1712a40e939SJosh Lehan                      sensor : sensorConfigurations)
1722a40e939SJosh Lehan             {
1732a40e939SJosh Lehan                 const std::string& interfacePath = sensor.first.str;
1742a40e939SJosh Lehan                 const SensorData& sensorData = sensor.second;
1752a40e939SJosh Lehan 
1762a40e939SJosh Lehan                 auto sensorBase = sensorData.find(sensorType);
1772a40e939SJosh Lehan                 if (sensorBase == sensorData.end())
1782a40e939SJosh Lehan                 {
1792a40e939SJosh Lehan                     std::cerr << "Base configuration not found for "
1802a40e939SJosh Lehan                               << interfacePath << "\n";
1812a40e939SJosh Lehan                     continue;
1822a40e939SJosh Lehan                 }
1832a40e939SJosh Lehan 
1842a40e939SJosh Lehan                 const SensorBaseConfiguration& baseConfiguration = *sensorBase;
1852a40e939SJosh Lehan                 const SensorBaseConfigMap& baseConfigMap =
1862a40e939SJosh Lehan                     baseConfiguration.second;
1872a40e939SJosh Lehan 
1882a40e939SJosh Lehan                 double minValue;
1892a40e939SJosh Lehan                 double maxValue;
1902a40e939SJosh Lehan 
1912a40e939SJosh Lehan                 // MinValue and MinValue are mandatory numeric parameters
1922a40e939SJosh Lehan                 auto minFound = baseConfigMap.find("MinValue");
1932a40e939SJosh Lehan                 if (minFound == baseConfigMap.end())
1942a40e939SJosh Lehan                 {
1952a40e939SJosh Lehan                     std::cerr << "MinValue parameter not found for "
1962a40e939SJosh Lehan                               << interfacePath << "\n";
1972a40e939SJosh Lehan                     continue;
1982a40e939SJosh Lehan                 }
1992a40e939SJosh Lehan                 minValue =
2002a40e939SJosh Lehan                     std::visit(VariantToDoubleVisitor(), minFound->second);
2012a40e939SJosh Lehan                 if (!std::isfinite(minValue))
2022a40e939SJosh Lehan                 {
2032a40e939SJosh Lehan                     std::cerr << "MinValue parameter not parsed for "
2042a40e939SJosh Lehan                               << interfacePath << "\n";
2052a40e939SJosh Lehan                     continue;
2062a40e939SJosh Lehan                 }
2072a40e939SJosh Lehan 
2082a40e939SJosh Lehan                 auto maxFound = baseConfigMap.find("MaxValue");
2092a40e939SJosh Lehan                 if (maxFound == baseConfigMap.end())
2102a40e939SJosh Lehan                 {
2112a40e939SJosh Lehan                     std::cerr << "MaxValue parameter not found for "
2122a40e939SJosh Lehan                               << interfacePath << "\n";
2132a40e939SJosh Lehan                     continue;
2142a40e939SJosh Lehan                 }
2152a40e939SJosh Lehan                 maxValue =
2162a40e939SJosh Lehan                     std::visit(VariantToDoubleVisitor(), maxFound->second);
2172a40e939SJosh Lehan                 if (!std::isfinite(maxValue))
2182a40e939SJosh Lehan                 {
2192a40e939SJosh Lehan                     std::cerr << "MaxValue parameter not parsed for "
2202a40e939SJosh Lehan                               << interfacePath << "\n";
2212a40e939SJosh Lehan                     continue;
2222a40e939SJosh Lehan                 }
2232a40e939SJosh Lehan 
2247243217bSJosh Lehan                 double timeoutSecs = 0.0;
2252a40e939SJosh Lehan 
2267243217bSJosh Lehan                 // Timeout is an optional numeric parameter
2277243217bSJosh Lehan                 auto timeoutFound = baseConfigMap.find("Timeout");
2287243217bSJosh Lehan                 if (timeoutFound != baseConfigMap.end())
2297243217bSJosh Lehan                 {
2307243217bSJosh Lehan                     timeoutSecs = std::visit(VariantToDoubleVisitor(),
2317243217bSJosh Lehan                                              timeoutFound->second);
2327243217bSJosh Lehan                 }
2337243217bSJosh Lehan                 if (!(std::isfinite(timeoutSecs) && (timeoutSecs >= 0.0)))
2347243217bSJosh Lehan                 {
2357243217bSJosh Lehan                     std::cerr << "Timeout parameter not parsed for "
2367243217bSJosh Lehan                               << interfacePath << "\n";
2377243217bSJosh Lehan                     continue;
2387243217bSJosh Lehan                 }
2397243217bSJosh Lehan 
2407243217bSJosh Lehan                 std::string sensorName;
2417243217bSJosh Lehan                 std::string sensorUnits;
2427243217bSJosh Lehan 
2437243217bSJosh Lehan                 // Name and Units are mandatory string parameters
2442a40e939SJosh Lehan                 auto nameFound = baseConfigMap.find("Name");
2452a40e939SJosh Lehan                 if (nameFound == baseConfigMap.end())
2462a40e939SJosh Lehan                 {
2472a40e939SJosh Lehan                     std::cerr << "Name parameter not found for "
2482a40e939SJosh Lehan                               << interfacePath << "\n";
2492a40e939SJosh Lehan                     continue;
2502a40e939SJosh Lehan                 }
2512a40e939SJosh Lehan                 sensorName =
2522a40e939SJosh Lehan                     std::visit(VariantToStringVisitor(), nameFound->second);
2532a40e939SJosh Lehan                 if (sensorName.empty())
2542a40e939SJosh Lehan                 {
2552a40e939SJosh Lehan                     std::cerr << "Name parameter not parsed for "
2562a40e939SJosh Lehan                               << interfacePath << "\n";
2572a40e939SJosh Lehan                     continue;
2582a40e939SJosh Lehan                 }
2592a40e939SJosh Lehan 
2607243217bSJosh Lehan                 auto unitsFound = baseConfigMap.find("Units");
2617243217bSJosh Lehan                 if (unitsFound == baseConfigMap.end())
2622a40e939SJosh Lehan                 {
2632a40e939SJosh Lehan                     std::cerr << "Units parameter not found for "
2642a40e939SJosh Lehan                               << interfacePath << "\n";
2652a40e939SJosh Lehan                     continue;
2662a40e939SJosh Lehan                 }
2677243217bSJosh Lehan                 sensorUnits =
2687243217bSJosh Lehan                     std::visit(VariantToStringVisitor(), unitsFound->second);
2697243217bSJosh Lehan                 if (sensorUnits.empty())
2702a40e939SJosh Lehan                 {
2717243217bSJosh Lehan                     std::cerr << "Units parameter not parsed for "
2722a40e939SJosh Lehan                               << interfacePath << "\n";
2732a40e939SJosh Lehan                     continue;
2742a40e939SJosh Lehan                 }
2752a40e939SJosh Lehan 
2762a40e939SJosh Lehan                 // on rescans, only update sensors we were signaled by
2772a40e939SJosh Lehan                 auto findSensor = sensors.find(sensorName);
2782a40e939SJosh Lehan                 if (!firstScan && (findSensor != sensors.end()))
2792a40e939SJosh Lehan                 {
2802a40e939SJosh Lehan                     std::string suffixName = "/";
2812a40e939SJosh Lehan                     suffixName += findSensor->second->name;
2822a40e939SJosh Lehan                     bool found = false;
2832a40e939SJosh Lehan                     for (auto it = sensorsChanged->begin();
2842a40e939SJosh Lehan                          it != sensorsChanged->end(); it++)
2852a40e939SJosh Lehan                     {
2862a40e939SJosh Lehan                         std::string suffixIt = "/";
2872a40e939SJosh Lehan                         suffixIt += *it;
2882a40e939SJosh Lehan                         if (boost::ends_with(suffixIt, suffixName))
2892a40e939SJosh Lehan                         {
2902a40e939SJosh Lehan                             sensorsChanged->erase(it);
2912a40e939SJosh Lehan                             findSensor->second = nullptr;
2922a40e939SJosh Lehan                             found = true;
2930362738dSJosh Lehan                             if constexpr (debug)
2940362738dSJosh Lehan                             {
2950362738dSJosh Lehan                                 std::cerr << "ExternalSensor " << sensorName
2960362738dSJosh Lehan                                           << " change found\n";
2970362738dSJosh Lehan                             }
2982a40e939SJosh Lehan                             break;
2992a40e939SJosh Lehan                         }
3002a40e939SJosh Lehan                     }
3012a40e939SJosh Lehan                     if (!found)
3022a40e939SJosh Lehan                     {
3032a40e939SJosh Lehan                         continue;
3042a40e939SJosh Lehan                     }
3052a40e939SJosh Lehan                 }
3062a40e939SJosh Lehan 
3072a40e939SJosh Lehan                 std::vector<thresholds::Threshold> sensorThresholds;
3082a40e939SJosh Lehan                 if (!parseThresholdsFromConfig(sensorData, sensorThresholds))
3092a40e939SJosh Lehan                 {
3102a40e939SJosh Lehan                     std::cerr << "error populating thresholds for "
3112a40e939SJosh Lehan                               << sensorName << "\n";
3122a40e939SJosh Lehan                 }
3132a40e939SJosh Lehan 
3142a40e939SJosh Lehan                 auto findPowerOn = baseConfiguration.second.find("PowerState");
3152a40e939SJosh Lehan                 PowerState readState = PowerState::always;
3162a40e939SJosh Lehan                 if (findPowerOn != baseConfiguration.second.end())
3172a40e939SJosh Lehan                 {
3182a40e939SJosh Lehan                     std::string powerState = std::visit(
3192a40e939SJosh Lehan                         VariantToStringVisitor(), findPowerOn->second);
3202a40e939SJosh Lehan                     setReadState(powerState, readState);
3212a40e939SJosh Lehan                 }
3222a40e939SJosh Lehan 
3232a40e939SJosh Lehan                 auto& sensorEntry = sensors[sensorName];
3242a40e939SJosh Lehan                 sensorEntry = nullptr;
3252a40e939SJosh Lehan 
3262a40e939SJosh Lehan                 sensorEntry = std::make_shared<ExternalSensor>(
3272a40e939SJosh Lehan                     sensorType, objectServer, dbusConnection, sensorName,
3287243217bSJosh Lehan                     sensorUnits, std::move(sensorThresholds), interfacePath,
3290362738dSJosh Lehan                     maxValue, minValue, timeoutSecs, readState);
3300362738dSJosh Lehan                 sensorEntry->initWriteHook(
3317243217bSJosh Lehan                     [&sensors, &reaperTimer](
3327243217bSJosh Lehan                         const std::chrono::steady_clock::time_point& now) {
3337243217bSJosh Lehan                         updateReaper(sensors, reaperTimer, now);
3347243217bSJosh Lehan                     });
3357243217bSJosh Lehan 
3367243217bSJosh Lehan                 if constexpr (debug)
3377243217bSJosh Lehan                 {
3387243217bSJosh Lehan                     std::cerr << "ExternalSensor " << sensorName
3397243217bSJosh Lehan                               << " created\n";
3407243217bSJosh Lehan                 }
3412a40e939SJosh Lehan             }
3422a40e939SJosh Lehan         });
3432a40e939SJosh Lehan 
3442a40e939SJosh Lehan     getter->getConfiguration(std::vector<std::string>{sensorType});
3452a40e939SJosh Lehan }
3462a40e939SJosh Lehan 
3472a40e939SJosh Lehan int main()
3482a40e939SJosh Lehan {
3497243217bSJosh Lehan     if constexpr (debug)
3507243217bSJosh Lehan     {
3517243217bSJosh Lehan         std::cerr << "ExternalSensor service starting up\n";
3527243217bSJosh Lehan     }
3537243217bSJosh Lehan 
3542a40e939SJosh Lehan     boost::asio::io_service io;
3552a40e939SJosh Lehan     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
3562a40e939SJosh Lehan     systemBus->request_name("xyz.openbmc_project.ExternalSensor");
3572a40e939SJosh Lehan     sdbusplus::asio::object_server objectServer(systemBus);
3582a40e939SJosh Lehan     boost::container::flat_map<std::string, std::shared_ptr<ExternalSensor>>
3592a40e939SJosh Lehan         sensors;
3602a40e939SJosh Lehan     std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
3612a40e939SJosh Lehan     auto sensorsChanged =
3622a40e939SJosh Lehan         std::make_shared<boost::container::flat_set<std::string>>();
3637243217bSJosh Lehan     boost::asio::steady_timer reaperTimer(io);
3642a40e939SJosh Lehan 
365*8a17c303SEd Tanous     io.post([&objectServer, &sensors, &systemBus, &reaperTimer]() {
366*8a17c303SEd Tanous         createSensors(objectServer, sensors, systemBus, nullptr, reaperTimer);
3672a40e939SJosh Lehan     });
3682a40e939SJosh Lehan 
3692a40e939SJosh Lehan     boost::asio::deadline_timer filterTimer(io);
3702a40e939SJosh Lehan     std::function<void(sdbusplus::message::message&)> eventHandler =
371*8a17c303SEd Tanous         [&objectServer, &sensors, &systemBus, &sensorsChanged, &filterTimer,
372*8a17c303SEd Tanous          &reaperTimer](sdbusplus::message::message& message) mutable {
3732a40e939SJosh Lehan             if (message.is_method_error())
3742a40e939SJosh Lehan             {
3752a40e939SJosh Lehan                 std::cerr << "callback method error\n";
3762a40e939SJosh Lehan                 return;
3772a40e939SJosh Lehan             }
3780362738dSJosh Lehan 
3790362738dSJosh Lehan             auto messagePath = message.get_path();
3800362738dSJosh Lehan             sensorsChanged->insert(messagePath);
3810362738dSJosh Lehan             if constexpr (debug)
3820362738dSJosh Lehan             {
3830362738dSJosh Lehan                 std::cerr << "ExternalSensor change event received: "
3840362738dSJosh Lehan                           << messagePath << "\n";
3850362738dSJosh Lehan             }
3860362738dSJosh Lehan 
3872a40e939SJosh Lehan             // this implicitly cancels the timer
3882a40e939SJosh Lehan             filterTimer.expires_from_now(boost::posix_time::seconds(1));
3892a40e939SJosh Lehan 
390*8a17c303SEd Tanous             filterTimer.async_wait(
391*8a17c303SEd Tanous                 [&objectServer, &sensors, &systemBus, &sensorsChanged,
392*8a17c303SEd Tanous                  &reaperTimer](const boost::system::error_code& ec) mutable {
3930362738dSJosh Lehan                     if (ec != boost::system::errc::success)
3942a40e939SJosh Lehan                     {
3952a40e939SJosh Lehan                         if (ec != boost::asio::error::operation_aborted)
3962a40e939SJosh Lehan                         {
397*8a17c303SEd Tanous                             std::cerr << "callback error: " << ec.message()
398*8a17c303SEd Tanous                                       << "\n";
3992a40e939SJosh Lehan                         }
4002a40e939SJosh Lehan                         return;
4012a40e939SJosh Lehan                     }
4020362738dSJosh Lehan 
403*8a17c303SEd Tanous                     createSensors(objectServer, sensors, systemBus,
4047243217bSJosh Lehan                                   sensorsChanged, reaperTimer);
4052a40e939SJosh Lehan                 });
4062a40e939SJosh Lehan         };
4072a40e939SJosh Lehan 
4082a40e939SJosh Lehan     auto match = std::make_unique<sdbusplus::bus::match::match>(
4092a40e939SJosh Lehan         static_cast<sdbusplus::bus::bus&>(*systemBus),
4102a40e939SJosh Lehan         "type='signal',member='PropertiesChanged',path_namespace='" +
4112a40e939SJosh Lehan             std::string(inventoryPath) + "',arg0namespace='" + sensorType + "'",
4122a40e939SJosh Lehan         eventHandler);
4132a40e939SJosh Lehan     matches.emplace_back(std::move(match));
4142a40e939SJosh Lehan 
4157243217bSJosh Lehan     if constexpr (debug)
4167243217bSJosh Lehan     {
4177243217bSJosh Lehan         std::cerr << "ExternalSensor service entering main loop\n";
4187243217bSJosh Lehan     }
4197243217bSJosh Lehan 
4202a40e939SJosh Lehan     io.run();
4212a40e939SJosh Lehan }
422