12a40e939SJosh Lehan #include "ExternalSensor.hpp"
2*eacbfdd1SEd Tanous #include "Thresholds.hpp"
32a40e939SJosh Lehan #include "Utils.hpp"
42a40e939SJosh Lehan #include "VariantVisitors.hpp"
52a40e939SJosh Lehan 
6*eacbfdd1SEd Tanous #include <boost/asio/error.hpp>
7*eacbfdd1SEd Tanous #include <boost/asio/io_context.hpp>
8*eacbfdd1SEd Tanous #include <boost/asio/post.hpp>
9*eacbfdd1SEd Tanous #include <boost/asio/steady_timer.hpp>
102a40e939SJosh Lehan #include <boost/container/flat_map.hpp>
112a40e939SJosh Lehan #include <boost/container/flat_set.hpp>
122a40e939SJosh Lehan #include <sdbusplus/asio/connection.hpp>
132a40e939SJosh Lehan #include <sdbusplus/asio/object_server.hpp>
142a40e939SJosh Lehan #include <sdbusplus/bus/match.hpp>
15*eacbfdd1SEd Tanous #include <sdbusplus/message.hpp>
16*eacbfdd1SEd Tanous #include <sdbusplus/message/native_types.hpp>
172a40e939SJosh Lehan 
18*eacbfdd1SEd Tanous #include <algorithm>
192a40e939SJosh Lehan #include <array>
20*eacbfdd1SEd Tanous #include <chrono>
21*eacbfdd1SEd Tanous #include <cmath>
222a40e939SJosh Lehan #include <functional>
23*eacbfdd1SEd Tanous #include <iostream>
242a40e939SJosh Lehan #include <memory>
252a40e939SJosh Lehan #include <string>
262a40e939SJosh Lehan #include <utility>
272a40e939SJosh Lehan #include <variant>
282a40e939SJosh Lehan #include <vector>
292a40e939SJosh Lehan 
302a40e939SJosh Lehan // Copied from HwmonTempSensor and inspired by
312a40e939SJosh Lehan // https://gerrit.openbmc-project.xyz/c/openbmc/dbus-sensors/+/35476
322a40e939SJosh Lehan 
332a40e939SJosh Lehan // The ExternalSensor is a sensor whose value is intended to be writable
342a40e939SJosh Lehan // by something external to the BMC, so that the host (or something else)
357243217bSJosh Lehan // can write to it, perhaps by using an IPMI or Redfish connection.
362a40e939SJosh Lehan 
372a40e939SJosh Lehan // Unlike most other sensors, an external sensor does not correspond
387243217bSJosh Lehan // to a hwmon file or any other kernel/hardware interface,
392a40e939SJosh Lehan // so, after initialization, this module does not have much to do,
402a40e939SJosh Lehan // but it handles reinitialization and thresholds, similar to the others.
417243217bSJosh Lehan // The main work of this module is to provide backing storage for a
427243217bSJosh Lehan // sensor that exists only virtually, and to provide an optional
437243217bSJosh Lehan // timeout service for detecting loss of timely updates.
442a40e939SJosh Lehan 
452a40e939SJosh Lehan // As there is no corresponding driver or hardware to support,
462a40e939SJosh Lehan // all configuration of this sensor comes from the JSON parameters:
477243217bSJosh Lehan // MinValue, MaxValue, Timeout, PowerState, Units, Name
482a40e939SJosh Lehan 
497243217bSJosh Lehan // The purpose of "Units" is to specify the physical characteristic
502a40e939SJosh Lehan // the external sensor is measuring, because with an external sensor
512a40e939SJosh Lehan // there is no other way to tell, and it will be used for the object path
527243217bSJosh Lehan // here: /xyz/openbmc_project/sensors/<Units>/<Name>
537243217bSJosh Lehan 
547243217bSJosh Lehan // For more information, see external-sensor.md design document:
557243217bSJosh Lehan // https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/41452
567243217bSJosh Lehan // https://github.com/openbmc/docs/tree/master/designs/
572a40e939SJosh Lehan 
588a57ec09SEd Tanous static constexpr bool debug = false;
592a40e939SJosh Lehan 
60054aad8fSZev Weiss static const char* sensorType = "ExternalSensor";
612a40e939SJosh Lehan 
updateReaper(boost::container::flat_map<std::string,std::shared_ptr<ExternalSensor>> & sensors,boost::asio::steady_timer & timer,const std::chrono::steady_clock::time_point & now)627243217bSJosh Lehan void updateReaper(boost::container::flat_map<
637243217bSJosh Lehan                       std::string, std::shared_ptr<ExternalSensor>>& sensors,
647243217bSJosh Lehan                   boost::asio::steady_timer& timer,
657243217bSJosh Lehan                   const std::chrono::steady_clock::time_point& now)
667243217bSJosh Lehan {
677243217bSJosh Lehan     // First pass, reap all stale sensors
6808cb50c5SZev Weiss     for (const auto& [name, sensor] : sensors)
697243217bSJosh Lehan     {
7008cb50c5SZev Weiss         if (!sensor)
717243217bSJosh Lehan         {
727243217bSJosh Lehan             continue;
737243217bSJosh Lehan         }
747243217bSJosh Lehan 
7508cb50c5SZev Weiss         if (!sensor->isAliveAndPerishable())
767243217bSJosh Lehan         {
777243217bSJosh Lehan             continue;
787243217bSJosh Lehan         }
797243217bSJosh Lehan 
8008cb50c5SZev Weiss         if (!sensor->isAliveAndFresh(now))
817243217bSJosh Lehan         {
827243217bSJosh Lehan             // Mark sensor as dead, no longer alive
8308cb50c5SZev Weiss             sensor->writeInvalidate();
847243217bSJosh Lehan         }
857243217bSJosh Lehan     }
867243217bSJosh Lehan 
877243217bSJosh Lehan     std::chrono::steady_clock::duration nextCheck;
887243217bSJosh Lehan     bool needCheck = false;
897243217bSJosh Lehan 
907243217bSJosh Lehan     // Second pass, determine timer interval to next check
9108cb50c5SZev Weiss     for (const auto& [name, sensor] : sensors)
927243217bSJosh Lehan     {
9308cb50c5SZev Weiss         if (!sensor)
947243217bSJosh Lehan         {
957243217bSJosh Lehan             continue;
967243217bSJosh Lehan         }
977243217bSJosh Lehan 
9808cb50c5SZev Weiss         if (!sensor->isAliveAndPerishable())
997243217bSJosh Lehan         {
1007243217bSJosh Lehan             continue;
1017243217bSJosh Lehan         }
1027243217bSJosh Lehan 
10308cb50c5SZev Weiss         auto expiration = sensor->ageRemaining(now);
1047243217bSJosh Lehan 
1057243217bSJosh Lehan         if (needCheck)
1067243217bSJosh Lehan         {
1077243217bSJosh Lehan             nextCheck = std::min(nextCheck, expiration);
1087243217bSJosh Lehan         }
1097243217bSJosh Lehan         else
1107243217bSJosh Lehan         {
1117243217bSJosh Lehan             // Initialization
1127243217bSJosh Lehan             nextCheck = expiration;
1137243217bSJosh Lehan             needCheck = true;
1147243217bSJosh Lehan         }
1157243217bSJosh Lehan     }
1167243217bSJosh Lehan 
1177243217bSJosh Lehan     if (!needCheck)
1187243217bSJosh Lehan     {
1197243217bSJosh Lehan         if constexpr (debug)
1207243217bSJosh Lehan         {
1217243217bSJosh Lehan             std::cerr << "Next ExternalSensor timer idle\n";
1227243217bSJosh Lehan         }
1237243217bSJosh Lehan 
1247243217bSJosh Lehan         return;
1257243217bSJosh Lehan     }
1267243217bSJosh Lehan 
1277243217bSJosh Lehan     timer.expires_at(now + nextCheck);
1287243217bSJosh Lehan 
1297243217bSJosh Lehan     timer.async_wait([&sensors, &timer](const boost::system::error_code& err) {
1307243217bSJosh Lehan         if (err != boost::system::errc::success)
1317243217bSJosh Lehan         {
1327243217bSJosh Lehan             // Cancellation is normal, as timer is dynamically rescheduled
1330362738dSJosh Lehan             if (err != boost::asio::error::operation_aborted)
1347243217bSJosh Lehan             {
1357243217bSJosh Lehan                 std::cerr << "ExternalSensor timer scheduling problem: "
1367243217bSJosh Lehan                           << err.message() << "\n";
1377243217bSJosh Lehan             }
1387243217bSJosh Lehan             return;
1397243217bSJosh Lehan         }
1400362738dSJosh Lehan 
1417243217bSJosh Lehan         updateReaper(sensors, timer, std::chrono::steady_clock::now());
1427243217bSJosh Lehan     });
1437243217bSJosh Lehan 
1447243217bSJosh Lehan     if constexpr (debug)
1457243217bSJosh Lehan     {
1467243217bSJosh Lehan         std::cerr << "Next ExternalSensor timer "
1477243217bSJosh Lehan                   << std::chrono::duration_cast<std::chrono::microseconds>(
1487243217bSJosh Lehan                          nextCheck)
1497243217bSJosh Lehan                          .count()
1507243217bSJosh Lehan                   << " us\n";
1517243217bSJosh Lehan     }
1527243217bSJosh Lehan }
1537243217bSJosh Lehan 
createSensors(sdbusplus::asio::object_server & objectServer,boost::container::flat_map<std::string,std::shared_ptr<ExternalSensor>> & sensors,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection,const std::shared_ptr<boost::container::flat_set<std::string>> & sensorsChanged,boost::asio::steady_timer & reaperTimer)1542a40e939SJosh Lehan void createSensors(
1558a17c303SEd Tanous     sdbusplus::asio::object_server& objectServer,
1562a40e939SJosh Lehan     boost::container::flat_map<std::string, std::shared_ptr<ExternalSensor>>&
1572a40e939SJosh Lehan         sensors,
1582a40e939SJosh Lehan     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
1592a40e939SJosh Lehan     const std::shared_ptr<boost::container::flat_set<std::string>>&
1607243217bSJosh Lehan         sensorsChanged,
1617243217bSJosh Lehan     boost::asio::steady_timer& reaperTimer)
1622a40e939SJosh Lehan {
1630362738dSJosh Lehan     if constexpr (debug)
1640362738dSJosh Lehan     {
1650362738dSJosh Lehan         std::cerr << "ExternalSensor considering creating sensors\n";
1660362738dSJosh Lehan     }
1670362738dSJosh Lehan 
1682a40e939SJosh Lehan     auto getter = std::make_shared<GetSensorConfiguration>(
1692a40e939SJosh Lehan         dbusConnection,
1708a17c303SEd Tanous         [&objectServer, &sensors, &dbusConnection, sensorsChanged,
1717243217bSJosh Lehan          &reaperTimer](const ManagedObjectType& sensorConfigurations) {
1722a40e939SJosh Lehan         bool firstScan = (sensorsChanged == nullptr);
1732a40e939SJosh Lehan 
1742a40e939SJosh Lehan         for (const std::pair<sdbusplus::message::object_path, SensorData>&
1752a40e939SJosh Lehan                  sensor : sensorConfigurations)
1762a40e939SJosh Lehan         {
1772a40e939SJosh Lehan             const std::string& interfacePath = sensor.first.str;
1782a40e939SJosh Lehan             const SensorData& sensorData = sensor.second;
1792a40e939SJosh Lehan 
180054aad8fSZev Weiss             auto sensorBase = sensorData.find(configInterfaceName(sensorType));
1812a40e939SJosh Lehan             if (sensorBase == sensorData.end())
1822a40e939SJosh Lehan             {
1832a40e939SJosh Lehan                 std::cerr << "Base configuration not found for "
1842a40e939SJosh Lehan                           << interfacePath << "\n";
1852a40e939SJosh Lehan                 continue;
1862a40e939SJosh Lehan             }
1872a40e939SJosh Lehan 
1882a40e939SJosh Lehan             const SensorBaseConfiguration& baseConfiguration = *sensorBase;
189bb67932aSEd Tanous             const SensorBaseConfigMap& baseConfigMap = baseConfiguration.second;
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             }
199779c96a2SPatrick Williams             double minValue = std::visit(VariantToDoubleVisitor(),
200779c96a2SPatrick Williams                                          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             }
215779c96a2SPatrick Williams             double maxValue = std::visit(VariantToDoubleVisitor(),
216779c96a2SPatrick Williams                                          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             {
230779c96a2SPatrick Williams                 timeoutSecs = std::visit(VariantToDoubleVisitor(),
231779c96a2SPatrick Williams                                          timeoutFound->second);
2327243217bSJosh Lehan             }
2339da019ccSPatrick Williams             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             {
247bb67932aSEd Tanous                 std::cerr << "Name parameter not found for " << interfacePath
248bb67932aSEd Tanous                           << "\n";
2492a40e939SJosh Lehan                 continue;
2502a40e939SJosh Lehan             }
251779c96a2SPatrick Williams             sensorName = std::visit(VariantToStringVisitor(),
252779c96a2SPatrick Williams                                     nameFound->second);
2532a40e939SJosh Lehan             if (sensorName.empty())
2542a40e939SJosh Lehan             {
255bb67932aSEd Tanous                 std::cerr << "Name parameter not parsed for " << interfacePath
256bb67932aSEd Tanous                           << "\n";
2572a40e939SJosh Lehan                 continue;
2582a40e939SJosh Lehan             }
2592a40e939SJosh Lehan 
2607243217bSJosh Lehan             auto unitsFound = baseConfigMap.find("Units");
2617243217bSJosh Lehan             if (unitsFound == baseConfigMap.end())
2622a40e939SJosh Lehan             {
263bb67932aSEd Tanous                 std::cerr << "Units parameter not found for " << interfacePath
264bb67932aSEd Tanous                           << "\n";
2652a40e939SJosh Lehan                 continue;
2662a40e939SJosh Lehan             }
267779c96a2SPatrick Williams             sensorUnits = std::visit(VariantToStringVisitor(),
268779c96a2SPatrick Williams                                      unitsFound->second);
2697243217bSJosh Lehan             if (sensorUnits.empty())
2702a40e939SJosh Lehan             {
271bb67932aSEd Tanous                 std::cerr << "Units parameter not parsed for " << interfacePath
272bb67932aSEd Tanous                           << "\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;
2886c106d66SZev Weiss                     if (suffixIt.ends_with(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             {
310bb67932aSEd Tanous                 std::cerr << "error populating thresholds for " << sensorName
311bb67932aSEd Tanous                           << "\n";
3122a40e939SJosh Lehan             }
3132a40e939SJosh Lehan 
314a4d2768cSZev Weiss             PowerState readState = getPowerState(baseConfigMap);
3152a40e939SJosh Lehan 
3162a40e939SJosh Lehan             auto& sensorEntry = sensors[sensorName];
3172a40e939SJosh Lehan             sensorEntry = nullptr;
3182a40e939SJosh Lehan 
3192a40e939SJosh Lehan             sensorEntry = std::make_shared<ExternalSensor>(
3202a40e939SJosh Lehan                 sensorType, objectServer, dbusConnection, sensorName,
3217243217bSJosh Lehan                 sensorUnits, std::move(sensorThresholds), interfacePath,
3220362738dSJosh Lehan                 maxValue, minValue, timeoutSecs, readState);
3230362738dSJosh Lehan             sensorEntry->initWriteHook(
3247243217bSJosh Lehan                 [&sensors, &reaperTimer](
3257243217bSJosh Lehan                     const std::chrono::steady_clock::time_point& now) {
3267243217bSJosh Lehan                 updateReaper(sensors, reaperTimer, now);
3277243217bSJosh Lehan             });
3287243217bSJosh Lehan 
3297243217bSJosh Lehan             if constexpr (debug)
3307243217bSJosh Lehan             {
331bb67932aSEd Tanous                 std::cerr << "ExternalSensor " << sensorName << " created\n";
3327243217bSJosh Lehan             }
3332a40e939SJosh Lehan         }
3342a40e939SJosh Lehan     });
3352a40e939SJosh Lehan 
3362a40e939SJosh Lehan     getter->getConfiguration(std::vector<std::string>{sensorType});
3372a40e939SJosh Lehan }
3382a40e939SJosh Lehan 
main()3392a40e939SJosh Lehan int main()
3402a40e939SJosh Lehan {
3417243217bSJosh Lehan     if constexpr (debug)
3427243217bSJosh Lehan     {
3437243217bSJosh Lehan         std::cerr << "ExternalSensor service starting up\n";
3447243217bSJosh Lehan     }
3457243217bSJosh Lehan 
3461f978631SEd Tanous     boost::asio::io_context io;
3472a40e939SJosh Lehan     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
34814ed5e99SEd Tanous     sdbusplus::asio::object_server objectServer(systemBus, true);
34914ed5e99SEd Tanous 
35014ed5e99SEd Tanous     objectServer.add_manager("/xyz/openbmc_project/sensors");
3512a40e939SJosh Lehan     systemBus->request_name("xyz.openbmc_project.ExternalSensor");
35214ed5e99SEd Tanous 
3532a40e939SJosh Lehan     boost::container::flat_map<std::string, std::shared_ptr<ExternalSensor>>
3542a40e939SJosh Lehan         sensors;
3552a40e939SJosh Lehan     auto sensorsChanged =
3562a40e939SJosh Lehan         std::make_shared<boost::container::flat_set<std::string>>();
3577243217bSJosh Lehan     boost::asio::steady_timer reaperTimer(io);
3582a40e939SJosh Lehan 
35983db50caSEd Tanous     boost::asio::post(io,
36083db50caSEd Tanous                       [&objectServer, &sensors, &systemBus, &reaperTimer]() {
3618a17c303SEd Tanous         createSensors(objectServer, sensors, systemBus, nullptr, reaperTimer);
3622a40e939SJosh Lehan     });
3632a40e939SJosh Lehan 
3649b4a20e9SEd Tanous     boost::asio::steady_timer filterTimer(io);
36592f8f515SPatrick Williams     std::function<void(sdbusplus::message_t&)> eventHandler =
3668a17c303SEd Tanous         [&objectServer, &sensors, &systemBus, &sensorsChanged, &filterTimer,
36792f8f515SPatrick Williams          &reaperTimer](sdbusplus::message_t& message) mutable {
3682a40e939SJosh Lehan         if (message.is_method_error())
3692a40e939SJosh Lehan         {
3702a40e939SJosh Lehan             std::cerr << "callback method error\n";
3712a40e939SJosh Lehan             return;
3722a40e939SJosh Lehan         }
3730362738dSJosh Lehan 
3742049bd26SEd Tanous         const auto* messagePath = message.get_path();
3750362738dSJosh Lehan         sensorsChanged->insert(messagePath);
3760362738dSJosh Lehan         if constexpr (debug)
3770362738dSJosh Lehan         {
378bb67932aSEd Tanous             std::cerr << "ExternalSensor change event received: " << messagePath
379bb67932aSEd Tanous                       << "\n";
3800362738dSJosh Lehan         }
3810362738dSJosh Lehan 
3822a40e939SJosh Lehan         // this implicitly cancels the timer
38383db50caSEd Tanous         filterTimer.expires_after(std::chrono::seconds(1));
3842a40e939SJosh Lehan 
3858a17c303SEd Tanous         filterTimer.async_wait(
3868a17c303SEd Tanous             [&objectServer, &sensors, &systemBus, &sensorsChanged,
3878a17c303SEd Tanous              &reaperTimer](const boost::system::error_code& ec) mutable {
3880362738dSJosh Lehan             if (ec != boost::system::errc::success)
3892a40e939SJosh Lehan             {
3902a40e939SJosh Lehan                 if (ec != boost::asio::error::operation_aborted)
3912a40e939SJosh Lehan                 {
392bb67932aSEd Tanous                     std::cerr << "callback error: " << ec.message() << "\n";
3932a40e939SJosh Lehan                 }
3942a40e939SJosh Lehan                 return;
3952a40e939SJosh Lehan             }
3960362738dSJosh Lehan 
397bb67932aSEd Tanous             createSensors(objectServer, sensors, systemBus, sensorsChanged,
398bb67932aSEd Tanous                           reaperTimer);
3992a40e939SJosh Lehan         });
4002a40e939SJosh Lehan     };
4012a40e939SJosh Lehan 
402214d9717SZev Weiss     std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
403214d9717SZev Weiss         setupPropertiesChangedMatches(
404214d9717SZev Weiss             *systemBus, std::to_array<const char*>({sensorType}), eventHandler);
4052a40e939SJosh Lehan 
4067243217bSJosh Lehan     if constexpr (debug)
4077243217bSJosh Lehan     {
4087243217bSJosh Lehan         std::cerr << "ExternalSensor service entering main loop\n";
4097243217bSJosh Lehan     }
4107243217bSJosh Lehan 
4112a40e939SJosh Lehan     io.run();
4122a40e939SJosh Lehan }
413