1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 17 #include "IntelCPUSensor.hpp" 18 #include "Thresholds.hpp" 19 #include "Utils.hpp" 20 #include "VariantVisitors.hpp" 21 22 #include <peci.h> 23 24 #include <boost/algorithm/string/replace.hpp> 25 #include <boost/asio/error.hpp> 26 #include <boost/asio/io_context.hpp> 27 #include <boost/asio/steady_timer.hpp> 28 #include <boost/container/flat_map.hpp> 29 #include <boost/container/flat_set.hpp> 30 #include <sdbusplus/asio/connection.hpp> 31 #include <sdbusplus/asio/object_server.hpp> 32 #include <sdbusplus/bus/match.hpp> 33 #include <sdbusplus/message.hpp> 34 35 #include <algorithm> 36 #include <array> 37 #include <cctype> 38 #include <cerrno> 39 #include <chrono> 40 #include <cstddef> 41 #include <cstdint> 42 #include <cstring> 43 #include <filesystem> 44 #include <fstream> 45 #include <functional> 46 #include <ios> 47 #include <iostream> 48 #include <iterator> 49 #include <memory> 50 #include <optional> 51 #include <regex> 52 #include <sstream> 53 #include <string> 54 #include <utility> 55 #include <variant> 56 #include <vector> 57 58 // clang-format off 59 // this needs to be included last or we'll have build issues 60 #include <linux/peci-ioctl.h> 61 #if !defined(PECI_MBX_INDEX_DDR_DIMM_TEMP) 62 #define PECI_MBX_INDEX_DDR_DIMM_TEMP MBX_INDEX_DDR_DIMM_TEMP 63 #endif 64 // clang-format on 65 66 static constexpr bool debug = false; 67 68 boost::container::flat_map<std::string, std::shared_ptr<IntelCPUSensor>> 69 gCpuSensors; 70 boost::container::flat_map<std::string, 71 std::shared_ptr<sdbusplus::asio::dbus_interface>> 72 inventoryIfaces; 73 74 enum State 75 { 76 OFF, // host powered down 77 ON, // host powered on 78 READY // host powered on and mem test passed - fully ready 79 }; 80 81 struct CPUConfig 82 { 83 CPUConfig(const uint64_t& bus, const uint64_t& addr, 84 const std::string& name, const State& state) : 85 bus(bus), 86 addr(addr), name(name), state(state) 87 {} 88 int bus; 89 int addr; 90 std::string name; 91 State state; 92 93 bool operator<(const CPUConfig& rhs) const 94 { 95 // NOLINTNEXTLINE 96 return (name < rhs.name); 97 } 98 }; 99 100 static constexpr const char* peciDev = "/dev/peci-"; 101 static constexpr const char* peciDevPath = "/sys/bus/peci/devices/"; 102 static constexpr const char* rescanPath = "/sys/bus/peci/rescan"; 103 static constexpr const unsigned int rankNumMax = 8; 104 105 namespace fs = std::filesystem; 106 107 static constexpr auto sensorTypes{std::to_array<const char*>({"XeonCPU"})}; 108 static constexpr auto hiddenProps{std::to_array<const char*>( 109 {IntelCPUSensor::labelTcontrol, "Tthrottle", "Tjmax"})}; 110 111 void detectCpuAsync( 112 boost::asio::steady_timer& pingTimer, 113 boost::asio::steady_timer& creationTimer, boost::asio::io_context& io, 114 sdbusplus::asio::object_server& objectServer, 115 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, 116 boost::container::flat_set<CPUConfig>& cpuConfigs, 117 ManagedObjectType& sensorConfigs); 118 119 std::string createSensorName(const std::string& label, const std::string& item, 120 const int& cpuId) 121 { 122 std::string sensorName = label; 123 if (item != "input") 124 { 125 sensorName += " " + item; 126 } 127 sensorName += " CPU" + std::to_string(cpuId); 128 // converting to Upper Camel case whole name 129 bool isWordEnd = true; 130 std::transform(sensorName.begin(), sensorName.end(), sensorName.begin(), 131 [&isWordEnd](int c) { 132 if (std::isspace(c) != 0) 133 { 134 isWordEnd = true; 135 } 136 else 137 { 138 if (isWordEnd) 139 { 140 isWordEnd = false; 141 return std::toupper(c); 142 } 143 } 144 return c; 145 }); 146 return sensorName; 147 } 148 149 bool createSensors(boost::asio::io_context& io, 150 sdbusplus::asio::object_server& objectServer, 151 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, 152 boost::container::flat_set<CPUConfig>& cpuConfigs, 153 ManagedObjectType& sensorConfigs) 154 { 155 bool available = false; 156 for (const CPUConfig& cpu : cpuConfigs) 157 { 158 if (cpu.state != State::OFF) 159 { 160 available = true; 161 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface = 162 inventoryIfaces[cpu.name]; 163 if (iface != nullptr) 164 { 165 continue; 166 } 167 iface = objectServer.add_interface( 168 cpuInventoryPath + std::string("/") + cpu.name, 169 "xyz.openbmc_project.Inventory.Item"); 170 iface->register_property("PrettyName", cpu.name); 171 iface->register_property("Present", true); 172 iface->initialize(); 173 } 174 } 175 if (!available) 176 { 177 return false; 178 } 179 180 if (sensorConfigs.empty()) 181 { 182 return false; 183 } 184 185 std::vector<fs::path> hwmonNamePaths; 186 findFiles(fs::path(peciDevPath), 187 R"(peci-\d+/\d+-.+/peci[-_].+/hwmon/hwmon\d+/name$)", 188 hwmonNamePaths, 6); 189 if (hwmonNamePaths.empty()) 190 { 191 std::cerr << "No CPU sensors in system\n"; 192 return false; 193 } 194 195 boost::container::flat_set<std::string> scannedDirectories; 196 boost::container::flat_set<std::string> createdSensors; 197 198 for (const fs::path& hwmonNamePath : hwmonNamePaths) 199 { 200 auto hwmonDirectory = hwmonNamePath.parent_path(); 201 202 auto ret = scannedDirectories.insert(hwmonDirectory.string()); 203 if (!ret.second) 204 { 205 continue; // already searched this path 206 } 207 208 fs::path::iterator it = hwmonNamePath.begin(); 209 std::advance(it, 6); // pick the 6th part for a PECI client device name 210 std::string deviceName = *it; 211 212 size_t bus = 0; 213 size_t addr = 0; 214 if (!getDeviceBusAddr(deviceName, bus, addr)) 215 { 216 continue; 217 } 218 219 std::ifstream nameFile(hwmonNamePath); 220 if (!nameFile.good()) 221 { 222 std::cerr << "Failure reading " << hwmonNamePath << "\n"; 223 continue; 224 } 225 std::string hwmonName; 226 std::getline(nameFile, hwmonName); 227 nameFile.close(); 228 if (hwmonName.empty()) 229 { 230 // shouldn't have an empty name file 231 continue; 232 } 233 if (debug) 234 { 235 std::cout << "Checking: " << hwmonNamePath << ": " << hwmonName 236 << "\n"; 237 } 238 239 std::string sensorType; 240 const SensorData* sensorData = nullptr; 241 const std::string* interfacePath = nullptr; 242 const SensorBaseConfiguration* baseConfiguration = nullptr; 243 244 for (const auto& [path, cfgData] : sensorConfigs) 245 { 246 sensorData = &cfgData; 247 for (const char* type : sensorTypes) 248 { 249 sensorType = type; 250 auto sensorBase = 251 sensorData->find(configInterfaceName(sensorType)); 252 if (sensorBase != sensorData->end()) 253 { 254 baseConfiguration = &(*sensorBase); 255 break; 256 } 257 } 258 if (baseConfiguration == nullptr) 259 { 260 std::cerr << "error finding base configuration for" << hwmonName 261 << "\n"; 262 continue; 263 } 264 auto configurationBus = baseConfiguration->second.find("Bus"); 265 auto configurationAddress = 266 baseConfiguration->second.find("Address"); 267 268 if (configurationBus == baseConfiguration->second.end() || 269 configurationAddress == baseConfiguration->second.end()) 270 { 271 std::cerr << "error finding bus or address in configuration"; 272 continue; 273 } 274 275 if (std::get<uint64_t>(configurationBus->second) != bus || 276 std::get<uint64_t>(configurationAddress->second) != addr) 277 { 278 continue; 279 } 280 281 interfacePath = &path.str; 282 break; 283 } 284 if (interfacePath == nullptr) 285 { 286 std::cerr << "failed to find match for " << hwmonName << "\n"; 287 continue; 288 } 289 290 auto findCpuId = baseConfiguration->second.find("CpuID"); 291 if (findCpuId == baseConfiguration->second.end()) 292 { 293 std::cerr << "could not determine CPU ID for " << hwmonName << "\n"; 294 continue; 295 } 296 int cpuId = std::visit(VariantToUnsignedIntVisitor(), 297 findCpuId->second); 298 299 auto directory = hwmonNamePath.parent_path(); 300 std::vector<fs::path> inputPaths; 301 if (!findFiles(directory, R"((temp|power)\d+_(input|average|cap)$)", 302 inputPaths, 0)) 303 { 304 std::cerr << "No temperature sensors in system\n"; 305 continue; 306 } 307 308 // iterate through all found temp sensors 309 for (const auto& inputPath : inputPaths) 310 { 311 auto fileParts = splitFileName(inputPath); 312 if (!fileParts) 313 { 314 continue; 315 } 316 auto& [type, nr, item] = *fileParts; 317 auto inputPathStr = inputPath.string(); 318 auto labelPath = boost::replace_all_copy(inputPathStr, item, 319 "label"); 320 std::ifstream labelFile(labelPath); 321 if (!labelFile.good()) 322 { 323 std::cerr << "Failure reading " << labelPath << "\n"; 324 continue; 325 } 326 std::string label; 327 std::getline(labelFile, label); 328 labelFile.close(); 329 330 std::string sensorName = createSensorName(label, item, cpuId); 331 332 auto findSensor = gCpuSensors.find(sensorName); 333 if (findSensor != gCpuSensors.end()) 334 { 335 if (debug) 336 { 337 std::cout << "Skipped: " << inputPath << ": " << sensorName 338 << " is already created\n"; 339 } 340 continue; 341 } 342 343 // check hidden properties 344 bool show = true; 345 for (const char* prop : hiddenProps) 346 { 347 if (label == prop) 348 { 349 show = false; 350 break; 351 } 352 } 353 354 /* 355 * Find if there is DtsCritOffset is configured in config file 356 * set it if configured or else set it to 0 357 */ 358 double dtsOffset = 0; 359 if (label == "DTS") 360 { 361 auto findThrOffset = 362 baseConfiguration->second.find("DtsCritOffset"); 363 if (findThrOffset != baseConfiguration->second.end()) 364 { 365 dtsOffset = std::visit(VariantToDoubleVisitor(), 366 findThrOffset->second); 367 } 368 } 369 370 std::vector<thresholds::Threshold> sensorThresholds; 371 std::string labelHead = label.substr(0, label.find(' ')); 372 parseThresholdsFromConfig(*sensorData, sensorThresholds, 373 &labelHead); 374 if (sensorThresholds.empty()) 375 { 376 if (!parseThresholdsFromAttr(sensorThresholds, inputPathStr, 377 IntelCPUSensor::sensorScaleFactor, 378 dtsOffset, 0)) 379 { 380 std::cerr << "error populating thresholds for " 381 << sensorName << "\n"; 382 } 383 } 384 auto& sensorPtr = gCpuSensors[sensorName]; 385 // make sure destructor fires before creating a new one 386 sensorPtr = nullptr; 387 sensorPtr = std::make_shared<IntelCPUSensor>( 388 inputPathStr, sensorType, objectServer, dbusConnection, io, 389 sensorName, std::move(sensorThresholds), *interfacePath, cpuId, 390 show, dtsOffset); 391 sensorPtr->setupRead(); 392 createdSensors.insert(sensorName); 393 if (debug) 394 { 395 std::cout << "Mapped: " << inputPath << " to " << sensorName 396 << "\n"; 397 } 398 } 399 } 400 401 if (static_cast<unsigned int>(!createdSensors.empty()) != 0U) 402 { 403 std::cout << "Sensor" << (createdSensors.size() == 1 ? " is" : "s are") 404 << " created\n"; 405 } 406 407 return true; 408 } 409 410 bool exportDevice(const CPUConfig& config) 411 { 412 std::ostringstream hex; 413 hex << std::hex << config.addr; 414 const std::string& addrHexStr = hex.str(); 415 std::string busStr = std::to_string(config.bus); 416 417 std::string parameters = "peci-client 0x" + addrHexStr; 418 std::string devPath = peciDevPath; 419 std::string delDevice = devPath + "peci-" + busStr + "/delete_device"; 420 std::string newDevice = devPath + "peci-" + busStr + "/new_device"; 421 std::string newClient = devPath + busStr + "-" + addrHexStr + "/driver"; 422 423 std::filesystem::path devicePath(newDevice); 424 const std::string& dir = devicePath.parent_path().string(); 425 for (const auto& path : std::filesystem::directory_iterator(dir)) 426 { 427 if (!std::filesystem::is_directory(path)) 428 { 429 continue; 430 } 431 432 const std::string& directoryName = path.path().filename(); 433 if (directoryName.starts_with(busStr) && 434 directoryName.ends_with(addrHexStr)) 435 { 436 if (debug) 437 { 438 std::cout << parameters << " on bus " << busStr 439 << " is already exported\n"; 440 } 441 442 std::ofstream delDeviceFile(delDevice); 443 if (!delDeviceFile.good()) 444 { 445 std::cerr << "Error opening " << delDevice << "\n"; 446 return false; 447 } 448 delDeviceFile << parameters; 449 delDeviceFile.close(); 450 451 break; 452 } 453 } 454 455 std::ofstream deviceFile(newDevice); 456 if (!deviceFile.good()) 457 { 458 std::cerr << "Error opening " << newDevice << "\n"; 459 return false; 460 } 461 deviceFile << parameters; 462 deviceFile.close(); 463 464 if (!std::filesystem::exists(newClient)) 465 { 466 std::cerr << "Error creating " << newClient << "\n"; 467 return false; 468 } 469 470 std::cout << parameters << " on bus " << busStr << " is exported\n"; 471 472 return true; 473 } 474 475 void detectCpu(boost::asio::steady_timer& pingTimer, 476 boost::asio::steady_timer& creationTimer, 477 boost::asio::io_context& io, 478 sdbusplus::asio::object_server& objectServer, 479 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, 480 boost::container::flat_set<CPUConfig>& cpuConfigs, 481 ManagedObjectType& sensorConfigs) 482 { 483 size_t rescanDelaySeconds = 0; 484 static bool keepPinging = false; 485 int peciFd = -1; 486 487 for (CPUConfig& config : cpuConfigs) 488 { 489 if (config.state == State::READY) 490 { 491 continue; 492 } 493 494 std::fstream rescan{rescanPath, std::ios::out}; 495 if (rescan.is_open()) 496 { 497 std::vector<fs::path> peciPaths; 498 std::ostringstream searchPath; 499 searchPath << std::hex << "peci-" << config.bus << "/" << config.bus 500 << "-" << config.addr; 501 findFiles(fs::path(peciDevPath + searchPath.str()), 502 R"(peci_cpu.dimmtemp.+/hwmon/hwmon\d+/name$)", peciPaths, 503 3); 504 if (!peciPaths.empty()) 505 { 506 config.state = State::READY; 507 rescanDelaySeconds = 1; 508 } 509 else 510 { 511 findFiles(fs::path(peciDevPath + searchPath.str()), 512 R"(peci_cpu.cputemp.+/hwmon/hwmon\d+/name$)", 513 peciPaths, 3); 514 if (!peciPaths.empty()) 515 { 516 config.state = State::ON; 517 rescanDelaySeconds = 3; 518 } 519 else 520 { 521 // https://www.kernel.org/doc/html/latest/admin-guide/abi-testing.html#abi-sys-bus-peci-rescan 522 rescan << "1"; 523 } 524 } 525 if (config.state != State::READY) 526 { 527 keepPinging = true; 528 } 529 530 continue; 531 } 532 533 std::string peciDevPath = peciDev + std::to_string(config.bus); 534 535 peci_SetDevName(peciDevPath.data()); 536 537 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) 538 if ((peci_Lock(&peciFd, PECI_NO_WAIT) != PECI_CC_SUCCESS) || 539 (peciFd < 0)) 540 { 541 std::cerr << "unable to open " << peciDevPath << " " 542 << std::strerror(errno) << "\n"; 543 detectCpuAsync(pingTimer, creationTimer, io, objectServer, 544 dbusConnection, cpuConfigs, sensorConfigs); 545 return; 546 } 547 548 State newState = State::OFF; 549 550 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) 551 if (peci_Ping(config.addr) == PECI_CC_SUCCESS) 552 { 553 bool dimmReady = false; 554 for (unsigned int rank = 0; rank < rankNumMax; rank++) 555 { 556 std::array<uint8_t, 8> pkgConfig{}; 557 uint8_t cc = 0; 558 559 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) 560 if (peci_RdPkgConfig(config.addr, PECI_MBX_INDEX_DDR_DIMM_TEMP, 561 rank, 4, pkgConfig.data(), 562 &cc) == PECI_CC_SUCCESS) 563 { 564 // Depending on CPU generation, both 0 and 0xFF can be used 565 // to indicate no DIMM presence 566 if (((pkgConfig[0] != 0xFF) && (pkgConfig[0] != 0U)) || 567 ((pkgConfig[1] != 0xFF) && (pkgConfig[1] != 0U))) 568 { 569 dimmReady = true; 570 break; 571 } 572 } 573 else 574 { 575 break; 576 } 577 } 578 579 if (dimmReady) 580 { 581 newState = State::READY; 582 } 583 else 584 { 585 newState = State::ON; 586 } 587 } 588 589 if (config.state != newState) 590 { 591 if (newState != State::OFF) 592 { 593 if (config.state == State::OFF) 594 { 595 std::array<uint8_t, 8> pkgConfig{}; 596 uint8_t cc = 0; 597 598 if (peci_RdPkgConfig(config.addr, PECI_MBX_INDEX_CPU_ID, 0, 599 4, pkgConfig.data(), 600 &cc) == PECI_CC_SUCCESS) 601 { 602 std::cout << config.name << " is detected\n"; 603 if (!exportDevice(config)) 604 { 605 newState = State::OFF; 606 } 607 } 608 else 609 { 610 newState = State::OFF; 611 } 612 } 613 614 if (newState == State::ON) 615 { 616 rescanDelaySeconds = 3; 617 } 618 else if (newState == State::READY) 619 { 620 rescanDelaySeconds = 5; 621 std::cout << "DIMM(s) on " << config.name 622 << " is/are detected\n"; 623 } 624 } 625 626 config.state = newState; 627 } 628 629 if (config.state != State::READY) 630 { 631 keepPinging = true; 632 } 633 634 if (debug) 635 { 636 std::cout << config.name << ", state: " << config.state << "\n"; 637 } 638 peci_Unlock(peciFd); 639 } 640 641 if (rescanDelaySeconds != 0U) 642 { 643 creationTimer.expires_after(std::chrono::seconds(rescanDelaySeconds)); 644 creationTimer.async_wait([&](const boost::system::error_code& ec) { 645 if (ec == boost::asio::error::operation_aborted) 646 { 647 return; // we're being canceled 648 } 649 650 if (!createSensors(io, objectServer, dbusConnection, cpuConfigs, 651 sensorConfigs) || 652 keepPinging) 653 { 654 detectCpuAsync(pingTimer, creationTimer, io, objectServer, 655 dbusConnection, cpuConfigs, sensorConfigs); 656 } 657 }); 658 } 659 else if (keepPinging) 660 { 661 detectCpuAsync(pingTimer, creationTimer, io, objectServer, 662 dbusConnection, cpuConfigs, sensorConfigs); 663 } 664 } 665 666 void detectCpuAsync( 667 boost::asio::steady_timer& pingTimer, 668 boost::asio::steady_timer& creationTimer, boost::asio::io_context& io, 669 sdbusplus::asio::object_server& objectServer, 670 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, 671 boost::container::flat_set<CPUConfig>& cpuConfigs, 672 ManagedObjectType& sensorConfigs) 673 { 674 pingTimer.expires_after(std::chrono::seconds(1)); 675 pingTimer.async_wait([&](const boost::system::error_code& ec) { 676 if (ec == boost::asio::error::operation_aborted) 677 { 678 return; // we're being canceled 679 } 680 681 detectCpu(pingTimer, creationTimer, io, objectServer, dbusConnection, 682 cpuConfigs, sensorConfigs); 683 }); 684 } 685 686 bool getCpuConfig(const std::shared_ptr<sdbusplus::asio::connection>& systemBus, 687 boost::container::flat_set<CPUConfig>& cpuConfigs, 688 ManagedObjectType& sensorConfigs, 689 sdbusplus::asio::object_server& objectServer) 690 { 691 bool useCache = false; 692 sensorConfigs.clear(); 693 // use new data the first time, then refresh 694 for (const char* type : sensorTypes) 695 { 696 if (!getSensorConfiguration(type, systemBus, sensorConfigs, useCache)) 697 { 698 return false; 699 } 700 useCache = true; 701 } 702 703 // check PECI client addresses and names from CPU configuration 704 // before starting ping operation 705 for (const char* type : sensorTypes) 706 { 707 for (const auto& [path, cfgData] : sensorConfigs) 708 { 709 for (const auto& [intf, cfg] : cfgData) 710 { 711 if (intf != configInterfaceName(type)) 712 { 713 continue; 714 } 715 716 auto findName = cfg.find("Name"); 717 if (findName == cfg.end()) 718 { 719 continue; 720 } 721 std::string nameRaw = std::visit(VariantToStringVisitor(), 722 findName->second); 723 std::string name = std::regex_replace(nameRaw, illegalDbusRegex, 724 "_"); 725 726 auto present = std::optional<bool>(); 727 // if we can't detect it via gpio, we set presence later 728 for (const auto& [suppIntf, suppCfg] : cfgData) 729 { 730 if (suppIntf.find("PresenceGpio") != std::string::npos) 731 { 732 present = cpuIsPresent(suppCfg); 733 break; 734 } 735 } 736 737 if (inventoryIfaces.find(name) == inventoryIfaces.end() && 738 present) 739 { 740 auto iface = objectServer.add_interface( 741 cpuInventoryPath + std::string("/") + name, 742 "xyz.openbmc_project.Inventory.Item"); 743 iface->register_property("PrettyName", name); 744 iface->register_property("Present", *present); 745 iface->initialize(); 746 inventoryIfaces[name] = std::move(iface); 747 } 748 749 auto findBus = cfg.find("Bus"); 750 if (findBus == cfg.end()) 751 { 752 std::cerr << "Can't find 'Bus' setting in " << name << "\n"; 753 continue; 754 } 755 uint64_t bus = std::visit(VariantToUnsignedIntVisitor(), 756 findBus->second); 757 758 auto findAddress = cfg.find("Address"); 759 if (findAddress == cfg.end()) 760 { 761 std::cerr << "Can't find 'Address' setting in " << name 762 << "\n"; 763 continue; 764 } 765 uint64_t addr = std::visit(VariantToUnsignedIntVisitor(), 766 findAddress->second); 767 768 if (debug) 769 { 770 std::cout << "bus: " << bus << "\n"; 771 std::cout << "addr: " << addr << "\n"; 772 std::cout << "name: " << name << "\n"; 773 std::cout << "type: " << type << "\n"; 774 } 775 776 cpuConfigs.emplace(bus, addr, name, State::OFF); 777 } 778 } 779 } 780 781 if (static_cast<unsigned int>(!cpuConfigs.empty()) != 0U) 782 { 783 std::cout << "CPU config" << (cpuConfigs.size() == 1 ? " is" : "s are") 784 << " parsed\n"; 785 return true; 786 } 787 788 return false; 789 } 790 791 int main() 792 { 793 boost::asio::io_context io; 794 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io); 795 boost::container::flat_set<CPUConfig> cpuConfigs; 796 797 sdbusplus::asio::object_server objectServer(systemBus, true); 798 objectServer.add_manager("/xyz/openbmc_project/sensors"); 799 boost::asio::steady_timer pingTimer(io); 800 boost::asio::steady_timer creationTimer(io); 801 boost::asio::steady_timer filterTimer(io); 802 ManagedObjectType sensorConfigs; 803 804 filterTimer.expires_after(std::chrono::seconds(1)); 805 filterTimer.async_wait([&](const boost::system::error_code& ec) { 806 if (ec == boost::asio::error::operation_aborted) 807 { 808 return; // we're being canceled 809 } 810 811 if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs, objectServer)) 812 { 813 detectCpuAsync(pingTimer, creationTimer, io, objectServer, 814 systemBus, cpuConfigs, sensorConfigs); 815 } 816 }); 817 818 std::function<void(sdbusplus::message_t&)> eventHandler = 819 [&](sdbusplus::message_t& message) { 820 if (message.is_method_error()) 821 { 822 std::cerr << "callback method error\n"; 823 return; 824 } 825 826 if (debug) 827 { 828 std::cout << message.get_path() << " is changed\n"; 829 } 830 831 // this implicitly cancels the timer 832 filterTimer.expires_after(std::chrono::seconds(1)); 833 filterTimer.async_wait([&](const boost::system::error_code& ec) { 834 if (ec == boost::asio::error::operation_aborted) 835 { 836 return; // we're being canceled 837 } 838 839 if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs, 840 objectServer)) 841 { 842 detectCpuAsync(pingTimer, creationTimer, io, objectServer, 843 systemBus, cpuConfigs, sensorConfigs); 844 } 845 }); 846 }; 847 848 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches = 849 setupPropertiesChangedMatches(*systemBus, sensorTypes, eventHandler); 850 851 systemBus->request_name("xyz.openbmc_project.IntelCPUSensor"); 852 853 setupManufacturingModeMatch(*systemBus); 854 io.run(); 855 return 0; 856 } 857