#include "ExternalSensor.hpp" #include "Utils.hpp" #include "VariantVisitors.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Copied from HwmonTempSensor and inspired by // https://gerrit.openbmc-project.xyz/c/openbmc/dbus-sensors/+/35476 // The ExternalSensor is a sensor whose value is intended to be writable // by something external to the BMC, so that the host (or something else) // can write to it, perhaps by using an IPMI connection. // Unlike most other sensors, an external sensor does not correspond // to a hwmon file or other kernel/hardware interface, // so, after initialization, this module does not have much to do, // but it handles reinitialization and thresholds, similar to the others. // As there is no corresponding driver or hardware to support, // all configuration of this sensor comes from the JSON parameters: // MinValue, MaxValue, PowerState, Measure, Name // The purpose of "Measure" is to specify the physical characteristic // the external sensor is measuring, because with an external sensor // there is no other way to tell, and it will be used for the object path // here: /xyz/openbmc_project/sensors// static constexpr bool DEBUG = false; static const char* sensorType = "xyz.openbmc_project.Configuration.ExternalSensor"; void createSensors( boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer, boost::container::flat_map>& sensors, std::shared_ptr& dbusConnection, const std::shared_ptr>& sensorsChanged) { auto getter = std::make_shared( dbusConnection, [&io, &objectServer, &sensors, &dbusConnection, sensorsChanged](const ManagedObjectType& sensorConfigurations) { bool firstScan = (sensorsChanged == nullptr); for (const std::pair& sensor : sensorConfigurations) { const std::string& interfacePath = sensor.first.str; const SensorData& sensorData = sensor.second; auto sensorBase = sensorData.find(sensorType); if (sensorBase == sensorData.end()) { std::cerr << "Base configuration not found for " << interfacePath << "\n"; continue; } const SensorBaseConfiguration& baseConfiguration = *sensorBase; const SensorBaseConfigMap& baseConfigMap = baseConfiguration.second; double minValue; double maxValue; // MinValue and MinValue are mandatory numeric parameters auto minFound = baseConfigMap.find("MinValue"); if (minFound == baseConfigMap.end()) { std::cerr << "MinValue parameter not found for " << interfacePath << "\n"; continue; } minValue = std::visit(VariantToDoubleVisitor(), minFound->second); if (!std::isfinite(minValue)) { std::cerr << "MinValue parameter not parsed for " << interfacePath << "\n"; continue; } auto maxFound = baseConfigMap.find("MaxValue"); if (maxFound == baseConfigMap.end()) { std::cerr << "MaxValue parameter not found for " << interfacePath << "\n"; continue; } maxValue = std::visit(VariantToDoubleVisitor(), maxFound->second); if (!std::isfinite(maxValue)) { std::cerr << "MaxValue parameter not parsed for " << interfacePath << "\n"; continue; } std::string sensorName; std::string sensorMeasure; // Name and Measure are mandatory string parameters auto nameFound = baseConfigMap.find("Name"); if (nameFound == baseConfigMap.end()) { std::cerr << "Name parameter not found for " << interfacePath << "\n"; continue; } sensorName = std::visit(VariantToStringVisitor(), nameFound->second); if (sensorName.empty()) { std::cerr << "Name parameter not parsed for " << interfacePath << "\n"; continue; } auto measureFound = baseConfigMap.find("Units"); if (measureFound == baseConfigMap.end()) { std::cerr << "Units parameter not found for " << interfacePath << "\n"; continue; } sensorMeasure = std::visit(VariantToStringVisitor(), measureFound->second); if (sensorMeasure.empty()) { std::cerr << "Measure parameter not parsed for " << interfacePath << "\n"; continue; } // on rescans, only update sensors we were signaled by auto findSensor = sensors.find(sensorName); if (!firstScan && (findSensor != sensors.end())) { std::string suffixName = "/"; suffixName += findSensor->second->name; bool found = false; for (auto it = sensorsChanged->begin(); it != sensorsChanged->end(); it++) { std::string suffixIt = "/"; suffixIt += *it; if (boost::ends_with(suffixIt, suffixName)) { sensorsChanged->erase(it); findSensor->second = nullptr; found = true; break; } } if (!found) { continue; } } std::vector sensorThresholds; if (!parseThresholdsFromConfig(sensorData, sensorThresholds)) { std::cerr << "error populating thresholds for " << sensorName << "\n"; } auto findPowerOn = baseConfiguration.second.find("PowerState"); PowerState readState = PowerState::always; if (findPowerOn != baseConfiguration.second.end()) { std::string powerState = std::visit( VariantToStringVisitor(), findPowerOn->second); setReadState(powerState, readState); } auto& sensorEntry = sensors[sensorName]; sensorEntry = nullptr; sensorEntry = std::make_shared( sensorType, objectServer, dbusConnection, sensorName, sensorMeasure, std::move(sensorThresholds), interfacePath, maxValue, minValue, readState); } }); getter->getConfiguration(std::vector{sensorType}); } int main() { boost::asio::io_service io; auto systemBus = std::make_shared(io); systemBus->request_name("xyz.openbmc_project.ExternalSensor"); sdbusplus::asio::object_server objectServer(systemBus); boost::container::flat_map> sensors; std::vector> matches; auto sensorsChanged = std::make_shared>(); io.post([&io, &objectServer, &sensors, &systemBus]() { createSensors(io, objectServer, sensors, systemBus, nullptr); }); boost::asio::deadline_timer filterTimer(io); std::function eventHandler = [&io, &objectServer, &sensors, &systemBus, &sensorsChanged, &filterTimer](sdbusplus::message::message& message) { if (message.is_method_error()) { std::cerr << "callback method error\n"; return; } sensorsChanged->insert(message.get_path()); // this implicitly cancels the timer filterTimer.expires_from_now(boost::posix_time::seconds(1)); filterTimer.async_wait([&io, &objectServer, &sensors, &systemBus, &sensorsChanged]( const boost::system::error_code& ec) { if (ec) { if (ec != boost::asio::error::operation_aborted) { std::cerr << "callback error: " << ec.message() << "\n"; } return; } createSensors(io, objectServer, sensors, systemBus, sensorsChanged); }); }; auto match = std::make_unique( static_cast(*systemBus), "type='signal',member='PropertiesChanged',path_namespace='" + std::string(inventoryPath) + "',arg0namespace='" + sensorType + "'", eventHandler); matches.emplace_back(std::move(match)); io.run(); }