#pragma once #include "VariantVisitors.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const constexpr char* jsonStore = "/var/configuration/flattened.json"; const constexpr char* inventoryPath = "/xyz/openbmc_project/inventory"; const constexpr char* entityManagerName = "xyz.openbmc_project.EntityManager"; constexpr const char* cpuInventoryPath = "/xyz/openbmc_project/inventory/system/chassis/motherboard"; const std::regex illegalDbusRegex("[^A-Za-z0-9_]"); using BasicVariantType = std::variant, std::string, int64_t, uint64_t, double, int32_t, uint32_t, int16_t, uint16_t, uint8_t, bool>; using SensorBaseConfigMap = boost::container::flat_map; using SensorBaseConfiguration = std::pair; using SensorData = boost::container::flat_map; using ManagedObjectType = boost::container::flat_map; using GetSubTreeType = std::vector< std::pair>>>>; using Association = std::tuple; inline std::string escapeName(const std::string& sensorName) { return boost::replace_all_copy(sensorName, " ", "_"); } enum class PowerState { on, biosPost, always, chassisOn }; std::optional openAndRead(const std::string& hwmonFile); std::optional getFullHwmonFilePath( const std::string& directory, const std::string& hwmonBaseName, const std::set& permitSet); std::set getPermitSet(const SensorBaseConfigMap& config); bool findFiles(const std::filesystem::path& dirPath, std::string_view matchString, std::vector& foundPaths, int symlinkDepth = 1); bool isPowerOn(); bool hasBiosPost(); bool isChassisOn(); void setupPowerMatchCallback( const std::shared_ptr& conn, std::function&& callback); void setupPowerMatch(const std::shared_ptr& conn); bool getSensorConfiguration( const std::string& type, const std::shared_ptr& dbusConnection, ManagedObjectType& resp, bool useCache); void createAssociation( std::shared_ptr& association, const std::string& path); // replaces limits if MinReading and MaxReading are found. void findLimits(std::pair& limits, const SensorBaseConfiguration* data); bool readingStateGood(const PowerState& powerState); constexpr const char* configInterfacePrefix = "xyz.openbmc_project.Configuration."; inline std::string configInterfaceName(const std::string& type) { return std::string(configInterfacePrefix) + type; } namespace mapper { constexpr const char* busName = "xyz.openbmc_project.ObjectMapper"; constexpr const char* path = "/xyz/openbmc_project/object_mapper"; constexpr const char* interface = "xyz.openbmc_project.ObjectMapper"; constexpr const char* subtree = "GetSubTree"; } // namespace mapper namespace properties { constexpr const char* interface = "org.freedesktop.DBus.Properties"; constexpr const char* get = "Get"; constexpr const char* set = "Set"; } // namespace properties namespace power { const static constexpr char* busname = "xyz.openbmc_project.State.Host0"; const static constexpr char* interface = "xyz.openbmc_project.State.Host"; const static constexpr char* path = "/xyz/openbmc_project/state/host0"; const static constexpr char* property = "CurrentHostState"; } // namespace power namespace chassis { const static constexpr char* busname = "xyz.openbmc_project.State.Chassis0"; const static constexpr char* interface = "xyz.openbmc_project.State.Chassis"; const static constexpr char* path = "/xyz/openbmc_project/state/chassis0"; const static constexpr char* property = "CurrentPowerState"; const static constexpr char* sOn = ".On"; } // namespace chassis namespace post { const static constexpr char* busname = "xyz.openbmc_project.State.Host0"; const static constexpr char* interface = "xyz.openbmc_project.State.OperatingSystem.Status"; const static constexpr char* path = "/xyz/openbmc_project/state/host0"; const static constexpr char* property = "OperatingSystemState"; } // namespace post namespace association { const static constexpr char* interface = "xyz.openbmc_project.Association.Definitions"; } // namespace association template inline T loadVariant(const SensorBaseConfigMap& data, const std::string& key) { auto it = data.find(key); if (it == data.end()) { std::cerr << "Configuration missing " << key << "\n"; throw std::invalid_argument("Key Missing"); } if constexpr (std::is_same_v) { return std::visit(VariantToDoubleVisitor(), it->second); } else if constexpr (std::is_unsigned_v) { return std::visit(VariantToUnsignedIntVisitor(), it->second); } else if constexpr (std::is_same_v) { return std::visit(VariantToStringVisitor(), it->second); } else { static_assert(!std::is_same_v, "Type Not Implemented"); } } inline void setReadState(const std::string& str, PowerState& val) { if (str == "On") { val = PowerState::on; } else if (str == "BiosPost") { val = PowerState::biosPost; } else if (str == "Always") { val = PowerState::always; } else if (str == "ChassisOn") { val = PowerState::chassisOn; } } inline PowerState getPowerState(const SensorBaseConfigMap& cfg) { PowerState state = PowerState::always; auto findPowerState = cfg.find("PowerState"); if (findPowerState != cfg.end()) { std::string powerState = std::visit(VariantToStringVisitor(), findPowerState->second); setReadState(powerState, state); } return state; } inline float getPollRate(const SensorBaseConfigMap& cfg, float dflt) { float pollRate = dflt; auto findPollRate = cfg.find("PollRate"); if (findPollRate != cfg.end()) { pollRate = std::visit(VariantToFloatVisitor(), findPollRate->second); if (!std::isfinite(pollRate) || pollRate <= 0.0F) { pollRate = dflt; // poll time invalid, fall back to default } } return pollRate; } inline void setLed(const std::shared_ptr& conn, const std::string& name, bool on) { conn->async_method_call( [name](const boost::system::error_code ec) { if (ec) { std::cerr << "Failed to set LED " << name << "\n"; } }, "xyz.openbmc_project.LED.GroupManager", "/xyz/openbmc_project/led/groups/" + name, properties::interface, properties::set, "xyz.openbmc_project.Led.Group", "Asserted", std::variant(on)); } void createInventoryAssoc( const std::shared_ptr& conn, const std::shared_ptr& association, const std::string& path); struct GetSensorConfiguration : std::enable_shared_from_this { GetSensorConfiguration( std::shared_ptr connection, std::function&& callbackFunc) : dbusConnection(std::move(connection)), callback(std::move(callbackFunc)) {} void getPath(const std::string& path, const std::string& interface, const std::string& owner, size_t retries = 5) { if (retries > 5) { retries = 5; } std::shared_ptr self = shared_from_this(); self->dbusConnection->async_method_call( [self, path, interface, owner, retries]( const boost::system::error_code ec, SensorBaseConfigMap& data) { if (ec) { std::cerr << "Error getting " << path << ": retries left" << retries - 1 << "\n"; if (retries == 0U) { return; } auto timer = std::make_shared( self->dbusConnection->get_io_context()); timer->expires_after(std::chrono::seconds(10)); timer->async_wait([self, timer, path, interface, owner, retries](boost::system::error_code ec) { if (ec) { std::cerr << "Timer error!\n"; return; } self->getPath(path, interface, owner, retries - 1); }); return; } self->respData[path][interface] = std::move(data); }, owner, path, "org.freedesktop.DBus.Properties", "GetAll", interface); } void getConfiguration(const std::vector& types, size_t retries = 0) { if (retries > 5) { retries = 5; } std::vector interfaces(types.size()); for (const auto& type : types) { interfaces.push_back(configInterfaceName(type)); } std::shared_ptr self = shared_from_this(); dbusConnection->async_method_call( [self, interfaces, retries](const boost::system::error_code ec, const GetSubTreeType& ret) { if (ec) { std::cerr << "Error calling mapper\n"; if (retries == 0U) { return; } auto timer = std::make_shared( self->dbusConnection->get_io_context()); timer->expires_after(std::chrono::seconds(10)); timer->async_wait([self, timer, interfaces, retries](boost::system::error_code ec) { if (ec) { std::cerr << "Timer error!\n"; return; } self->getConfiguration(interfaces, retries - 1); }); return; } for (const auto& [path, objDict] : ret) { if (objDict.empty()) { return; } const std::string& owner = objDict.begin()->first; for (const std::string& interface : objDict.begin()->second) { // anything that starts with a requested configuration // is good if (std::find_if( interfaces.begin(), interfaces.end(), [interface](const std::string& possible) { return interface.starts_with(possible); }) == interfaces.end()) { continue; } self->getPath(path, interface, owner); } } }, mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/", 0, interfaces); } ~GetSensorConfiguration() { callback(respData); } std::shared_ptr dbusConnection; std::function callback; ManagedObjectType respData; }; // The common scheme for sysfs files naming is: _. // This function returns optionally these 3 elements as a tuple. std::optional> splitFileName(const std::filesystem::path& filePath); std::optional readFile(const std::string& thresholdFile, const double& scaleFactor); void setupManufacturingModeMatch(sdbusplus::asio::connection& conn); bool getManufacturingMode(); std::vector> setupPropertiesChangedMatches( sdbusplus::asio::connection& bus, std::span types, const std::function& handler); template bool getDeviceBusAddr(const std::string& deviceName, T& bus, T& addr) { auto findHyphen = deviceName.find('-'); if (findHyphen == std::string::npos) { std::cerr << "found bad device " << deviceName << "\n"; return false; } std::string busStr = deviceName.substr(0, findHyphen); std::string addrStr = deviceName.substr(findHyphen + 1); std::from_chars_result res{}; res = std::from_chars(&*busStr.begin(), &*busStr.end(), bus); if (res.ec != std::errc{} || res.ptr != &*busStr.end()) { std::cerr << "Error finding bus for " << deviceName << "\n"; return false; } res = std::from_chars(&*addrStr.begin(), &*addrStr.end(), addr, 16); if (res.ec != std::errc{} || res.ptr != &*addrStr.end()) { std::cerr << "Error finding addr for " << deviceName << "\n"; return false; } return true; }