| /* |
| // Copyright (c) 2018 Intel Corporation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| */ |
| |
| #include "IntelCPUSensor.hpp" |
| #include "Thresholds.hpp" |
| #include "Utils.hpp" |
| #include "VariantVisitors.hpp" |
| |
| #include <peci.h> |
| |
| #include <boost/algorithm/string/replace.hpp> |
| #include <boost/asio/error.hpp> |
| #include <boost/asio/io_context.hpp> |
| #include <boost/asio/steady_timer.hpp> |
| #include <boost/container/flat_map.hpp> |
| #include <boost/container/flat_set.hpp> |
| #include <sdbusplus/asio/connection.hpp> |
| #include <sdbusplus/asio/object_server.hpp> |
| #include <sdbusplus/bus/match.hpp> |
| #include <sdbusplus/message.hpp> |
| |
| #include <algorithm> |
| #include <array> |
| #include <cctype> |
| #include <cerrno> |
| #include <chrono> |
| #include <cstddef> |
| #include <cstdint> |
| #include <cstring> |
| #include <filesystem> |
| #include <fstream> |
| #include <functional> |
| #include <ios> |
| #include <iostream> |
| #include <iterator> |
| #include <memory> |
| #include <optional> |
| #include <regex> |
| #include <sstream> |
| #include <string> |
| #include <utility> |
| #include <variant> |
| #include <vector> |
| |
| // clang-format off |
| // this needs to be included last or we'll have build issues |
| #include <linux/peci-ioctl.h> |
| #if !defined(PECI_MBX_INDEX_DDR_DIMM_TEMP) |
| #define PECI_MBX_INDEX_DDR_DIMM_TEMP MBX_INDEX_DDR_DIMM_TEMP |
| #endif |
| // clang-format on |
| |
| static constexpr bool debug = false; |
| |
| boost::container::flat_map<std::string, std::shared_ptr<IntelCPUSensor>> |
| gCpuSensors; |
| boost::container::flat_map<std::string, |
| std::shared_ptr<sdbusplus::asio::dbus_interface>> |
| inventoryIfaces; |
| |
| enum State |
| { |
| OFF, // host powered down |
| ON, // host powered on |
| READY // host powered on and mem test passed - fully ready |
| }; |
| |
| struct CPUConfig |
| { |
| CPUConfig(const uint64_t& bus, const uint64_t& addr, |
| const std::string& name, const State& state) : |
| bus(bus), addr(addr), name(name), state(state) |
| {} |
| int bus; |
| int addr; |
| std::string name; |
| State state; |
| |
| bool operator<(const CPUConfig& rhs) const |
| { |
| // NOLINTNEXTLINE |
| return (name < rhs.name); |
| } |
| }; |
| |
| static constexpr const char* peciDev = "/dev/peci-"; |
| static constexpr const char* peciDevPath = "/sys/bus/peci/devices/"; |
| static constexpr const char* rescanPath = "/sys/bus/peci/rescan"; |
| static constexpr const unsigned int rankNumMax = 8; |
| |
| namespace fs = std::filesystem; |
| |
| static constexpr auto sensorTypes{std::to_array<const char*>({"XeonCPU"})}; |
| static constexpr auto hiddenProps{std::to_array<const char*>( |
| {IntelCPUSensor::labelTcontrol, "Tthrottle", "Tjmax"})}; |
| |
| void detectCpuAsync( |
| boost::asio::steady_timer& pingTimer, |
| boost::asio::steady_timer& creationTimer, boost::asio::io_context& io, |
| sdbusplus::asio::object_server& objectServer, |
| std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, |
| boost::container::flat_set<CPUConfig>& cpuConfigs, |
| ManagedObjectType& sensorConfigs); |
| |
| std::string createSensorName(const std::string& label, const std::string& item, |
| const int& cpuId) |
| { |
| std::string sensorName = label; |
| if (item != "input") |
| { |
| sensorName += " " + item; |
| } |
| |
| std::string cpuStr = "CPU" + std::to_string(cpuId); |
| constexpr const char* subLabel = "DIMM"; |
| std::size_t found = label.find(subLabel); |
| if (found != std::string::npos) |
| { |
| sensorName = cpuStr + " " + sensorName; |
| } |
| else |
| { |
| sensorName += " " + cpuStr; |
| } |
| // converting to Upper Camel case whole name |
| bool isWordEnd = true; |
| std::transform(sensorName.begin(), sensorName.end(), sensorName.begin(), |
| [&isWordEnd](int c) { |
| if (std::isspace(c) != 0) |
| { |
| isWordEnd = true; |
| } |
| else |
| { |
| if (isWordEnd) |
| { |
| isWordEnd = false; |
| return std::toupper(c); |
| } |
| } |
| return c; |
| }); |
| return sensorName; |
| } |
| |
| bool createSensors(boost::asio::io_context& io, |
| sdbusplus::asio::object_server& objectServer, |
| std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, |
| boost::container::flat_set<CPUConfig>& cpuConfigs, |
| ManagedObjectType& sensorConfigs) |
| { |
| bool available = false; |
| for (const CPUConfig& cpu : cpuConfigs) |
| { |
| if (cpu.state != State::OFF) |
| { |
| available = true; |
| std::shared_ptr<sdbusplus::asio::dbus_interface>& iface = |
| inventoryIfaces[cpu.name]; |
| if (iface != nullptr) |
| { |
| continue; |
| } |
| iface = objectServer.add_interface( |
| cpuInventoryPath + std::string("/") + cpu.name, |
| "xyz.openbmc_project.Inventory.Item"); |
| iface->register_property("PrettyName", cpu.name); |
| iface->register_property("Present", true); |
| iface->initialize(); |
| } |
| } |
| if (!available) |
| { |
| return false; |
| } |
| |
| if (sensorConfigs.empty()) |
| { |
| return false; |
| } |
| |
| std::vector<fs::path> hwmonNamePaths; |
| findFiles(fs::path(peciDevPath), |
| R"(peci-\d+/\d+-.+/peci[-_].+/hwmon/hwmon\d+/name$)", |
| hwmonNamePaths, 6); |
| if (hwmonNamePaths.empty()) |
| { |
| std::cerr << "No CPU sensors in system\n"; |
| return false; |
| } |
| |
| boost::container::flat_set<std::string> scannedDirectories; |
| boost::container::flat_set<std::string> createdSensors; |
| |
| for (const fs::path& hwmonNamePath : hwmonNamePaths) |
| { |
| auto hwmonDirectory = hwmonNamePath.parent_path(); |
| |
| auto ret = scannedDirectories.insert(hwmonDirectory.string()); |
| if (!ret.second) |
| { |
| continue; // already searched this path |
| } |
| |
| fs::path::iterator it = hwmonNamePath.begin(); |
| std::advance(it, 6); // pick the 6th part for a PECI client device name |
| std::string deviceName = *it; |
| |
| size_t bus = 0; |
| size_t addr = 0; |
| if (!getDeviceBusAddr(deviceName, bus, addr)) |
| { |
| continue; |
| } |
| |
| std::ifstream nameFile(hwmonNamePath); |
| if (!nameFile.good()) |
| { |
| std::cerr << "Failure reading " << hwmonNamePath << "\n"; |
| continue; |
| } |
| std::string hwmonName; |
| std::getline(nameFile, hwmonName); |
| nameFile.close(); |
| if (hwmonName.empty()) |
| { |
| // shouldn't have an empty name file |
| continue; |
| } |
| if (debug) |
| { |
| std::cout << "Checking: " << hwmonNamePath << ": " << hwmonName |
| << "\n"; |
| } |
| |
| std::string sensorType; |
| const SensorData* sensorData = nullptr; |
| const std::string* interfacePath = nullptr; |
| const SensorBaseConfiguration* baseConfiguration = nullptr; |
| |
| for (const auto& [path, cfgData] : sensorConfigs) |
| { |
| sensorData = &cfgData; |
| for (const char* type : sensorTypes) |
| { |
| sensorType = type; |
| auto sensorBase = |
| sensorData->find(configInterfaceName(sensorType)); |
| if (sensorBase != sensorData->end()) |
| { |
| baseConfiguration = &(*sensorBase); |
| break; |
| } |
| } |
| if (baseConfiguration == nullptr) |
| { |
| std::cerr << "error finding base configuration for" << hwmonName |
| << "\n"; |
| continue; |
| } |
| auto configurationBus = baseConfiguration->second.find("Bus"); |
| auto configurationAddress = |
| baseConfiguration->second.find("Address"); |
| |
| if (configurationBus == baseConfiguration->second.end() || |
| configurationAddress == baseConfiguration->second.end()) |
| { |
| std::cerr << "error finding bus or address in configuration"; |
| continue; |
| } |
| |
| if (std::get<uint64_t>(configurationBus->second) != bus || |
| std::get<uint64_t>(configurationAddress->second) != addr) |
| { |
| continue; |
| } |
| |
| interfacePath = &path.str; |
| break; |
| } |
| if (interfacePath == nullptr) |
| { |
| std::cerr << "failed to find match for " << hwmonName << "\n"; |
| continue; |
| } |
| |
| auto findCpuId = baseConfiguration->second.find("CpuID"); |
| if (findCpuId == baseConfiguration->second.end()) |
| { |
| std::cerr << "could not determine CPU ID for " << hwmonName << "\n"; |
| continue; |
| } |
| int cpuId = |
| std::visit(VariantToUnsignedIntVisitor(), findCpuId->second); |
| |
| auto directory = hwmonNamePath.parent_path(); |
| std::vector<fs::path> inputPaths; |
| if (!findFiles(directory, R"((temp|power)\d+_(input|average|cap)$)", |
| inputPaths, 0)) |
| { |
| std::cerr << "No temperature sensors in system\n"; |
| continue; |
| } |
| |
| // iterate through all found temp sensors |
| for (const auto& inputPath : inputPaths) |
| { |
| auto fileParts = splitFileName(inputPath); |
| if (!fileParts) |
| { |
| continue; |
| } |
| auto& [type, nr, item] = *fileParts; |
| auto inputPathStr = inputPath.string(); |
| auto labelPath = |
| boost::replace_all_copy(inputPathStr, item, "label"); |
| std::ifstream labelFile(labelPath); |
| if (!labelFile.good()) |
| { |
| std::cerr << "Failure reading " << labelPath << "\n"; |
| continue; |
| } |
| std::string label; |
| std::getline(labelFile, label); |
| labelFile.close(); |
| |
| std::string sensorName = createSensorName(label, item, cpuId); |
| |
| auto findSensor = gCpuSensors.find(sensorName); |
| if (findSensor != gCpuSensors.end()) |
| { |
| if (debug) |
| { |
| std::cout << "Skipped: " << inputPath << ": " << sensorName |
| << " is already created\n"; |
| } |
| continue; |
| } |
| |
| // check hidden properties |
| bool show = true; |
| for (const char* prop : hiddenProps) |
| { |
| if (label == prop) |
| { |
| show = false; |
| break; |
| } |
| } |
| |
| /* |
| * Find if there is DtsCritOffset is configured in config file |
| * set it if configured or else set it to 0 |
| */ |
| double dtsOffset = 0; |
| if (label == "DTS") |
| { |
| auto findThrOffset = |
| baseConfiguration->second.find("DtsCritOffset"); |
| if (findThrOffset != baseConfiguration->second.end()) |
| { |
| dtsOffset = std::visit(VariantToDoubleVisitor(), |
| findThrOffset->second); |
| } |
| } |
| |
| std::vector<thresholds::Threshold> sensorThresholds; |
| std::string labelHead = label.substr(0, label.find(' ')); |
| parseThresholdsFromConfig(*sensorData, sensorThresholds, |
| &labelHead); |
| if (sensorThresholds.empty()) |
| { |
| if (!parseThresholdsFromAttr(sensorThresholds, inputPathStr, |
| IntelCPUSensor::sensorScaleFactor, |
| dtsOffset, 0)) |
| { |
| std::cerr << "error populating thresholds for " |
| << sensorName << "\n"; |
| } |
| } |
| auto& sensorPtr = gCpuSensors[sensorName]; |
| // make sure destructor fires before creating a new one |
| sensorPtr = nullptr; |
| sensorPtr = std::make_shared<IntelCPUSensor>( |
| inputPathStr, sensorType, objectServer, dbusConnection, io, |
| sensorName, std::move(sensorThresholds), *interfacePath, cpuId, |
| show, dtsOffset); |
| sensorPtr->setupRead(); |
| createdSensors.insert(sensorName); |
| if (debug) |
| { |
| std::cout << "Mapped: " << inputPath << " to " << sensorName |
| << "\n"; |
| } |
| } |
| } |
| |
| if (static_cast<unsigned int>(!createdSensors.empty()) != 0U) |
| { |
| std::cout << "Sensor" << (createdSensors.size() == 1 ? " is" : "s are") |
| << " created\n"; |
| } |
| |
| return true; |
| } |
| |
| bool exportDevice(const CPUConfig& config) |
| { |
| std::ostringstream hex; |
| hex << std::hex << config.addr; |
| const std::string& addrHexStr = hex.str(); |
| std::string busStr = std::to_string(config.bus); |
| |
| std::string parameters = "peci-client 0x" + addrHexStr; |
| std::string devPath = peciDevPath; |
| std::string delDevice = devPath + "peci-" + busStr + "/delete_device"; |
| std::string newDevice = devPath + "peci-" + busStr + "/new_device"; |
| std::string newClient = devPath + busStr + "-" + addrHexStr + "/driver"; |
| |
| std::filesystem::path devicePath(newDevice); |
| const std::string& dir = devicePath.parent_path().string(); |
| for (const auto& path : std::filesystem::directory_iterator(dir)) |
| { |
| if (!std::filesystem::is_directory(path)) |
| { |
| continue; |
| } |
| |
| const std::string& directoryName = path.path().filename(); |
| if (directoryName.starts_with(busStr) && |
| directoryName.ends_with(addrHexStr)) |
| { |
| if (debug) |
| { |
| std::cout << parameters << " on bus " << busStr |
| << " is already exported\n"; |
| } |
| |
| std::ofstream delDeviceFile(delDevice); |
| if (!delDeviceFile.good()) |
| { |
| std::cerr << "Error opening " << delDevice << "\n"; |
| return false; |
| } |
| delDeviceFile << parameters; |
| delDeviceFile.close(); |
| |
| break; |
| } |
| } |
| |
| std::ofstream deviceFile(newDevice); |
| if (!deviceFile.good()) |
| { |
| std::cerr << "Error opening " << newDevice << "\n"; |
| return false; |
| } |
| deviceFile << parameters; |
| deviceFile.close(); |
| |
| if (!std::filesystem::exists(newClient)) |
| { |
| std::cerr << "Error creating " << newClient << "\n"; |
| return false; |
| } |
| |
| std::cout << parameters << " on bus " << busStr << " is exported\n"; |
| |
| return true; |
| } |
| |
| void detectCpu(boost::asio::steady_timer& pingTimer, |
| boost::asio::steady_timer& creationTimer, |
| boost::asio::io_context& io, |
| sdbusplus::asio::object_server& objectServer, |
| std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, |
| boost::container::flat_set<CPUConfig>& cpuConfigs, |
| ManagedObjectType& sensorConfigs) |
| { |
| size_t rescanDelaySeconds = 0; |
| static bool keepPinging = false; |
| int peciFd = -1; |
| |
| for (CPUConfig& config : cpuConfigs) |
| { |
| if (config.state == State::READY) |
| { |
| continue; |
| } |
| |
| std::fstream rescan{rescanPath, std::ios::out}; |
| if (rescan.is_open()) |
| { |
| std::vector<fs::path> peciPaths; |
| std::ostringstream searchPath; |
| searchPath << std::hex << "peci-" << config.bus << "/" << config.bus |
| << "-" << config.addr; |
| findFiles(fs::path(peciDevPath + searchPath.str()), |
| R"(peci_cpu.dimmtemp.+/hwmon/hwmon\d+/name$)", peciPaths, |
| 3); |
| if (!peciPaths.empty()) |
| { |
| config.state = State::READY; |
| rescanDelaySeconds = 1; |
| } |
| else |
| { |
| findFiles(fs::path(peciDevPath + searchPath.str()), |
| R"(peci_cpu.cputemp.+/hwmon/hwmon\d+/name$)", |
| peciPaths, 3); |
| if (!peciPaths.empty()) |
| { |
| config.state = State::ON; |
| rescanDelaySeconds = 3; |
| } |
| else |
| { |
| // https://www.kernel.org/doc/html/latest/admin-guide/abi-testing.html#abi-sys-bus-peci-rescan |
| rescan << "1"; |
| } |
| } |
| if (config.state != State::READY) |
| { |
| keepPinging = true; |
| } |
| |
| continue; |
| } |
| |
| std::string peciDevPath = peciDev + std::to_string(config.bus); |
| |
| peci_SetDevName(peciDevPath.data()); |
| |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) |
| if ((peci_Lock(&peciFd, PECI_NO_WAIT) != PECI_CC_SUCCESS) || |
| (peciFd < 0)) |
| { |
| std::cerr << "unable to open " << peciDevPath << " " |
| << std::strerror(errno) << "\n"; |
| detectCpuAsync(pingTimer, creationTimer, io, objectServer, |
| dbusConnection, cpuConfigs, sensorConfigs); |
| return; |
| } |
| |
| State newState = State::OFF; |
| |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) |
| if (peci_Ping(config.addr) == PECI_CC_SUCCESS) |
| { |
| bool dimmReady = false; |
| for (unsigned int rank = 0; rank < rankNumMax; rank++) |
| { |
| std::array<uint8_t, 8> pkgConfig{}; |
| uint8_t cc = 0; |
| |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) |
| if (peci_RdPkgConfig(config.addr, PECI_MBX_INDEX_DDR_DIMM_TEMP, |
| rank, 4, pkgConfig.data(), &cc) == |
| PECI_CC_SUCCESS) |
| { |
| // Depending on CPU generation, both 0 and 0xFF can be used |
| // to indicate no DIMM presence |
| if (((pkgConfig[0] != 0xFF) && (pkgConfig[0] != 0U)) || |
| ((pkgConfig[1] != 0xFF) && (pkgConfig[1] != 0U))) |
| { |
| dimmReady = true; |
| break; |
| } |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| if (dimmReady) |
| { |
| newState = State::READY; |
| } |
| else |
| { |
| newState = State::ON; |
| } |
| } |
| |
| if (config.state != newState) |
| { |
| if (newState != State::OFF) |
| { |
| if (config.state == State::OFF) |
| { |
| std::array<uint8_t, 8> pkgConfig{}; |
| uint8_t cc = 0; |
| |
| if (peci_RdPkgConfig(config.addr, PECI_MBX_INDEX_CPU_ID, 0, |
| 4, pkgConfig.data(), &cc) == |
| PECI_CC_SUCCESS) |
| { |
| std::cout << config.name << " is detected\n"; |
| if (!exportDevice(config)) |
| { |
| newState = State::OFF; |
| } |
| } |
| else |
| { |
| newState = State::OFF; |
| } |
| } |
| |
| if (newState == State::ON) |
| { |
| rescanDelaySeconds = 3; |
| } |
| else if (newState == State::READY) |
| { |
| rescanDelaySeconds = 5; |
| std::cout |
| << "DIMM(s) on " << config.name << " is/are detected\n"; |
| } |
| } |
| |
| config.state = newState; |
| } |
| |
| if (config.state != State::READY) |
| { |
| keepPinging = true; |
| } |
| |
| if (debug) |
| { |
| std::cout << config.name << ", state: " << config.state << "\n"; |
| } |
| peci_Unlock(peciFd); |
| } |
| |
| if (rescanDelaySeconds != 0U) |
| { |
| creationTimer.expires_after(std::chrono::seconds(rescanDelaySeconds)); |
| creationTimer.async_wait([&](const boost::system::error_code& ec) { |
| if (ec == boost::asio::error::operation_aborted) |
| { |
| return; // we're being canceled |
| } |
| |
| if (!createSensors(io, objectServer, dbusConnection, cpuConfigs, |
| sensorConfigs) || |
| keepPinging) |
| { |
| detectCpuAsync(pingTimer, creationTimer, io, objectServer, |
| dbusConnection, cpuConfigs, sensorConfigs); |
| } |
| }); |
| } |
| else if (keepPinging) |
| { |
| detectCpuAsync(pingTimer, creationTimer, io, objectServer, |
| dbusConnection, cpuConfigs, sensorConfigs); |
| } |
| } |
| |
| void detectCpuAsync( |
| boost::asio::steady_timer& pingTimer, |
| boost::asio::steady_timer& creationTimer, boost::asio::io_context& io, |
| sdbusplus::asio::object_server& objectServer, |
| std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, |
| boost::container::flat_set<CPUConfig>& cpuConfigs, |
| ManagedObjectType& sensorConfigs) |
| { |
| pingTimer.expires_after(std::chrono::seconds(1)); |
| pingTimer.async_wait([&](const boost::system::error_code& ec) { |
| if (ec == boost::asio::error::operation_aborted) |
| { |
| return; // we're being canceled |
| } |
| |
| detectCpu(pingTimer, creationTimer, io, objectServer, dbusConnection, |
| cpuConfigs, sensorConfigs); |
| }); |
| } |
| |
| bool getCpuConfig(const std::shared_ptr<sdbusplus::asio::connection>& systemBus, |
| boost::container::flat_set<CPUConfig>& cpuConfigs, |
| ManagedObjectType& sensorConfigs, |
| sdbusplus::asio::object_server& objectServer) |
| { |
| bool useCache = false; |
| sensorConfigs.clear(); |
| // use new data the first time, then refresh |
| for (const char* type : sensorTypes) |
| { |
| if (!getSensorConfiguration(type, systemBus, sensorConfigs, useCache)) |
| { |
| return false; |
| } |
| useCache = true; |
| } |
| |
| // check PECI client addresses and names from CPU configuration |
| // before starting ping operation |
| for (const char* type : sensorTypes) |
| { |
| for (const auto& [path, cfgData] : sensorConfigs) |
| { |
| for (const auto& [intf, cfg] : cfgData) |
| { |
| if (intf != configInterfaceName(type)) |
| { |
| continue; |
| } |
| |
| auto findName = cfg.find("Name"); |
| if (findName == cfg.end()) |
| { |
| continue; |
| } |
| std::string nameRaw = |
| std::visit(VariantToStringVisitor(), findName->second); |
| std::string name = |
| std::regex_replace(nameRaw, illegalDbusRegex, "_"); |
| |
| auto present = std::optional<bool>(); |
| // if we can't detect it via gpio, we set presence later |
| for (const auto& [suppIntf, suppCfg] : cfgData) |
| { |
| if (suppIntf.find("PresenceGpio") != std::string::npos) |
| { |
| present = cpuIsPresent(suppCfg); |
| break; |
| } |
| } |
| |
| if (inventoryIfaces.find(name) == inventoryIfaces.end() && |
| present) |
| { |
| auto iface = objectServer.add_interface( |
| cpuInventoryPath + std::string("/") + name, |
| "xyz.openbmc_project.Inventory.Item"); |
| iface->register_property("PrettyName", name); |
| iface->register_property("Present", *present); |
| iface->initialize(); |
| inventoryIfaces[name] = std::move(iface); |
| } |
| |
| auto findBus = cfg.find("Bus"); |
| if (findBus == cfg.end()) |
| { |
| std::cerr << "Can't find 'Bus' setting in " << name << "\n"; |
| continue; |
| } |
| uint64_t bus = |
| std::visit(VariantToUnsignedIntVisitor(), findBus->second); |
| |
| auto findAddress = cfg.find("Address"); |
| if (findAddress == cfg.end()) |
| { |
| std::cerr |
| << "Can't find 'Address' setting in " << name << "\n"; |
| continue; |
| } |
| uint64_t addr = std::visit(VariantToUnsignedIntVisitor(), |
| findAddress->second); |
| |
| if (debug) |
| { |
| std::cout << "bus: " << bus << "\n"; |
| std::cout << "addr: " << addr << "\n"; |
| std::cout << "name: " << name << "\n"; |
| std::cout << "type: " << type << "\n"; |
| } |
| |
| cpuConfigs.emplace(bus, addr, name, State::OFF); |
| } |
| } |
| } |
| |
| if (static_cast<unsigned int>(!cpuConfigs.empty()) != 0U) |
| { |
| std::cout << "CPU config" << (cpuConfigs.size() == 1 ? " is" : "s are") |
| << " parsed\n"; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| int main() |
| { |
| boost::asio::io_context io; |
| auto systemBus = std::make_shared<sdbusplus::asio::connection>(io); |
| boost::container::flat_set<CPUConfig> cpuConfigs; |
| |
| sdbusplus::asio::object_server objectServer(systemBus, true); |
| objectServer.add_manager("/xyz/openbmc_project/sensors"); |
| boost::asio::steady_timer pingTimer(io); |
| boost::asio::steady_timer creationTimer(io); |
| boost::asio::steady_timer filterTimer(io); |
| ManagedObjectType sensorConfigs; |
| |
| filterTimer.expires_after(std::chrono::seconds(1)); |
| filterTimer.async_wait([&](const boost::system::error_code& ec) { |
| if (ec == boost::asio::error::operation_aborted) |
| { |
| return; // we're being canceled |
| } |
| |
| if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs, objectServer)) |
| { |
| detectCpuAsync(pingTimer, creationTimer, io, objectServer, |
| systemBus, cpuConfigs, sensorConfigs); |
| } |
| }); |
| |
| std::function<void(sdbusplus::message_t&)> eventHandler = |
| [&](sdbusplus::message_t& message) { |
| if (message.is_method_error()) |
| { |
| std::cerr << "callback method error\n"; |
| return; |
| } |
| |
| if (debug) |
| { |
| std::cout << message.get_path() << " is changed\n"; |
| } |
| |
| // this implicitly cancels the timer |
| filterTimer.expires_after(std::chrono::seconds(1)); |
| filterTimer.async_wait([&](const boost::system::error_code& ec) { |
| if (ec == boost::asio::error::operation_aborted) |
| { |
| return; // we're being canceled |
| } |
| |
| if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs, |
| objectServer)) |
| { |
| detectCpuAsync(pingTimer, creationTimer, io, objectServer, |
| systemBus, cpuConfigs, sensorConfigs); |
| } |
| }); |
| }; |
| |
| std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches = |
| setupPropertiesChangedMatches(*systemBus, sensorTypes, eventHandler); |
| |
| systemBus->request_name("xyz.openbmc_project.IntelCPUSensor"); |
| |
| setupManufacturingModeMatch(*systemBus); |
| io.run(); |
| return 0; |
| } |