1 /* 2 // Copyright (c) 2017 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 "Utils.hpp" 18 19 #include "dbus-sensor_config.h" 20 21 #include "DeviceMgmt.hpp" 22 23 #include <boost/container/flat_map.hpp> 24 #include <sdbusplus/asio/connection.hpp> 25 #include <sdbusplus/asio/object_server.hpp> 26 #include <sdbusplus/bus/match.hpp> 27 28 #include <charconv> 29 #include <filesystem> 30 #include <fstream> 31 #include <memory> 32 #include <regex> 33 #include <stdexcept> 34 #include <string> 35 #include <utility> 36 #include <variant> 37 #include <vector> 38 39 namespace fs = std::filesystem; 40 41 static bool powerStatusOn = false; 42 static bool biosHasPost = false; 43 static bool manufacturingMode = false; 44 static bool chassisStatusOn = false; 45 46 static std::unique_ptr<sdbusplus::bus::match_t> powerMatch = nullptr; 47 static std::unique_ptr<sdbusplus::bus::match_t> postMatch = nullptr; 48 static std::unique_ptr<sdbusplus::bus::match_t> chassisMatch = nullptr; 49 50 /** 51 * return the contents of a file 52 * @param[in] hwmonFile - the path to the file to read 53 * @return the contents of the file as a string or nullopt if the file could not 54 * be opened. 55 */ 56 57 std::optional<std::string> openAndRead(const std::string& hwmonFile) 58 { 59 std::string fileVal; 60 std::ifstream fileStream(hwmonFile); 61 if (!fileStream.is_open()) 62 { 63 return std::nullopt; 64 } 65 std::getline(fileStream, fileVal); 66 return fileVal; 67 } 68 69 /** 70 * given a hwmon temperature base name if valid return the full path else 71 * nullopt 72 * @param[in] directory - the hwmon sysfs directory 73 * @param[in] permitSet - a set of labels or hwmon basenames to permit. If this 74 * is empty then *everything* is permitted. 75 * @return a string to the full path of the file to create a temp sensor with or 76 * nullopt to indicate that no sensor should be created for this basename. 77 */ 78 std::optional<std::string> 79 getFullHwmonFilePath(const std::string& directory, 80 const std::string& hwmonBaseName, 81 const std::set<std::string>& permitSet) 82 { 83 std::optional<std::string> result; 84 std::string filename; 85 if (permitSet.empty()) 86 { 87 result = directory + "/" + hwmonBaseName + "_input"; 88 return result; 89 } 90 filename = directory + "/" + hwmonBaseName + "_label"; 91 auto searchVal = openAndRead(filename); 92 if (!searchVal) 93 { 94 /* if the hwmon temp doesn't have a corresponding label file 95 * then use the hwmon temperature base name 96 */ 97 searchVal = hwmonBaseName; 98 } 99 if (permitSet.find(*searchVal) != permitSet.end()) 100 { 101 result = directory + "/" + hwmonBaseName + "_input"; 102 } 103 return result; 104 } 105 106 /** 107 * retrieve a set of basenames and labels to allow sensor creation for. 108 * @param[in] config - a map representing the configuration for a specific 109 * device 110 * @return a set of basenames and labels to allow sensor creation for. An empty 111 * set indicates that everything is permitted. 112 */ 113 std::set<std::string> getPermitSet(const SensorBaseConfigMap& config) 114 { 115 auto permitAttribute = config.find("Labels"); 116 std::set<std::string> permitSet; 117 if (permitAttribute != config.end()) 118 { 119 try 120 { 121 auto val = 122 std::get<std::vector<std::string>>(permitAttribute->second); 123 124 permitSet.insert(std::make_move_iterator(val.begin()), 125 std::make_move_iterator(val.end())); 126 } 127 catch (const std::bad_variant_access& err) 128 { 129 std::cerr << err.what() 130 << ":PermitList does not contain a list, wrong " 131 "variant type.\n"; 132 } 133 } 134 return permitSet; 135 } 136 137 bool getSensorConfiguration( 138 const std::string& type, 139 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, 140 ManagedObjectType& resp) 141 { 142 return getSensorConfiguration(type, dbusConnection, resp, false); 143 } 144 145 bool getSensorConfiguration( 146 const std::string& type, 147 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, 148 ManagedObjectType& resp, bool useCache) 149 { 150 static ManagedObjectType managedObj; 151 std::string typeIntf = configInterfaceName(type); 152 153 if (!useCache) 154 { 155 managedObj.clear(); 156 sdbusplus::message_t getManagedObjects = 157 dbusConnection->new_method_call( 158 entityManagerName, "/xyz/openbmc_project/inventory", 159 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 160 try 161 { 162 sdbusplus::message_t reply = 163 dbusConnection->call(getManagedObjects); 164 reply.read(managedObj); 165 } 166 catch (const sdbusplus::exception_t& e) 167 { 168 std::cerr << "While calling GetManagedObjects on service:" 169 << entityManagerName << " exception name:" << e.name() 170 << "and description:" << e.description() 171 << " was thrown\n"; 172 return false; 173 } 174 } 175 for (const auto& pathPair : managedObj) 176 { 177 for (const auto& [intf, cfg] : pathPair.second) 178 { 179 if (intf.starts_with(typeIntf)) 180 { 181 resp.emplace(pathPair); 182 break; 183 } 184 } 185 } 186 return true; 187 } 188 189 bool findFiles(const fs::path& dirPath, std::string_view matchString, 190 std::vector<fs::path>& foundPaths, int symlinkDepth) 191 { 192 std::error_code ec; 193 if (!fs::exists(dirPath, ec)) 194 { 195 return false; 196 } 197 198 std::vector<std::regex> matchPieces; 199 200 size_t pos = 0; 201 std::string token; 202 // Generate the regex expressions list from the match we were given 203 while ((pos = matchString.find('/')) != std::string::npos) 204 { 205 token = matchString.substr(0, pos); 206 matchPieces.emplace_back(token); 207 matchString.remove_prefix(pos + 1); 208 } 209 matchPieces.emplace_back(std::string{matchString}); 210 211 // Check if the match string contains directories, and skip the match of 212 // subdirectory if not 213 if (matchPieces.size() <= 1) 214 { 215 std::regex search(std::string{matchString}); 216 std::smatch match; 217 for (auto p = fs::recursive_directory_iterator( 218 dirPath, fs::directory_options::follow_directory_symlink); 219 p != fs::recursive_directory_iterator(); ++p) 220 { 221 std::string path = p->path().string(); 222 if (!is_directory(*p)) 223 { 224 if (std::regex_search(path, match, search)) 225 { 226 foundPaths.emplace_back(p->path()); 227 } 228 } 229 if (p.depth() >= symlinkDepth) 230 { 231 p.disable_recursion_pending(); 232 } 233 } 234 return true; 235 } 236 237 // The match string contains directories, verify each level of sub 238 // directories 239 for (auto p = fs::recursive_directory_iterator( 240 dirPath, fs::directory_options::follow_directory_symlink); 241 p != fs::recursive_directory_iterator(); ++p) 242 { 243 std::vector<std::regex>::iterator matchPiece = matchPieces.begin(); 244 fs::path::iterator pathIt = p->path().begin(); 245 for (const fs::path& dir : dirPath) 246 { 247 if (dir.empty()) 248 { 249 // When the path ends with '/', it gets am empty path 250 // skip such case. 251 break; 252 } 253 pathIt++; 254 } 255 256 while (pathIt != p->path().end()) 257 { 258 // Found a path deeper than match. 259 if (matchPiece == matchPieces.end()) 260 { 261 p.disable_recursion_pending(); 262 break; 263 } 264 std::smatch match; 265 std::string component = pathIt->string(); 266 std::regex regexPiece(*matchPiece); 267 if (!std::regex_match(component, match, regexPiece)) 268 { 269 // path prefix doesn't match, no need to iterate further 270 p.disable_recursion_pending(); 271 break; 272 } 273 matchPiece++; 274 pathIt++; 275 } 276 277 if (!is_directory(*p)) 278 { 279 if (matchPiece == matchPieces.end()) 280 { 281 foundPaths.emplace_back(p->path()); 282 } 283 } 284 285 if (p.depth() >= symlinkDepth) 286 { 287 p.disable_recursion_pending(); 288 } 289 } 290 return true; 291 } 292 293 bool isPowerOn(void) 294 { 295 if (!powerMatch) 296 { 297 throw std::runtime_error("Power Match Not Created"); 298 } 299 return powerStatusOn; 300 } 301 302 bool hasBiosPost(void) 303 { 304 if (!postMatch) 305 { 306 throw std::runtime_error("Post Match Not Created"); 307 } 308 return biosHasPost; 309 } 310 311 bool isChassisOn(void) 312 { 313 if (!chassisMatch) 314 { 315 throw std::runtime_error("Chassis On Match Not Created"); 316 } 317 return chassisStatusOn; 318 } 319 320 bool readingStateGood(const PowerState& powerState) 321 { 322 if (powerState == PowerState::on && !isPowerOn()) 323 { 324 return false; 325 } 326 if (powerState == PowerState::biosPost && (!hasBiosPost() || !isPowerOn())) 327 { 328 return false; 329 } 330 if (powerState == PowerState::chassisOn && !isChassisOn()) 331 { 332 return false; 333 } 334 335 return true; 336 } 337 338 static void 339 getPowerStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn, 340 size_t retries = 2) 341 { 342 conn->async_method_call( 343 [conn, retries](boost::system::error_code ec, 344 const std::variant<std::string>& state) { 345 if (ec) 346 { 347 if (retries != 0U) 348 { 349 auto timer = std::make_shared<boost::asio::steady_timer>( 350 conn->get_io_context()); 351 timer->expires_after(std::chrono::seconds(15)); 352 timer->async_wait( 353 [timer, conn, retries](boost::system::error_code) { 354 getPowerStatus(conn, retries - 1); 355 }); 356 return; 357 } 358 359 // we commonly come up before power control, we'll capture the 360 // property change later 361 std::cerr << "error getting power status " << ec.message() << "\n"; 362 return; 363 } 364 powerStatusOn = std::get<std::string>(state).ends_with(".Running"); 365 }, 366 power::busname, power::path, properties::interface, properties::get, 367 power::interface, power::property); 368 } 369 370 static void 371 getPostStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn, 372 size_t retries = 2) 373 { 374 conn->async_method_call( 375 [conn, retries](boost::system::error_code ec, 376 const std::variant<std::string>& state) { 377 if (ec) 378 { 379 if (retries != 0U) 380 { 381 auto timer = std::make_shared<boost::asio::steady_timer>( 382 conn->get_io_context()); 383 timer->expires_after(std::chrono::seconds(15)); 384 timer->async_wait( 385 [timer, conn, retries](boost::system::error_code) { 386 getPostStatus(conn, retries - 1); 387 }); 388 return; 389 } 390 // we commonly come up before power control, we'll capture the 391 // property change later 392 std::cerr << "error getting post status " << ec.message() << "\n"; 393 return; 394 } 395 const auto& value = std::get<std::string>(state); 396 biosHasPost = (value != "Inactive") && 397 (value != "xyz.openbmc_project.State.OperatingSystem." 398 "Status.OSStatus.Inactive"); 399 }, 400 post::busname, post::path, properties::interface, properties::get, 401 post::interface, post::property); 402 } 403 404 static void 405 getChassisStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn, 406 size_t retries = 2) 407 { 408 conn->async_method_call( 409 [conn, retries](boost::system::error_code ec, 410 const std::variant<std::string>& state) { 411 if (ec) 412 { 413 if (retries != 0U) 414 { 415 auto timer = std::make_shared<boost::asio::steady_timer>( 416 conn->get_io_context()); 417 timer->expires_after(std::chrono::seconds(15)); 418 timer->async_wait( 419 [timer, conn, retries](boost::system::error_code) { 420 getChassisStatus(conn, retries - 1); 421 }); 422 return; 423 } 424 425 // we commonly come up before power control, we'll capture the 426 // property change later 427 std::cerr << "error getting chassis power status " << ec.message() 428 << "\n"; 429 return; 430 } 431 chassisStatusOn = std::get<std::string>(state).ends_with(chassis::sOn); 432 }, 433 chassis::busname, chassis::path, properties::interface, properties::get, 434 chassis::interface, chassis::property); 435 } 436 437 void setupPowerMatchCallback( 438 const std::shared_ptr<sdbusplus::asio::connection>& conn, 439 std::function<void(PowerState type, bool state)>&& hostStatusCallback) 440 { 441 static boost::asio::steady_timer timer(conn->get_io_context()); 442 static boost::asio::steady_timer timerChassisOn(conn->get_io_context()); 443 // create a match for powergood changes, first time do a method call to 444 // cache the correct value 445 if (powerMatch) 446 { 447 return; 448 } 449 450 powerMatch = std::make_unique<sdbusplus::bus::match_t>( 451 static_cast<sdbusplus::bus_t&>(*conn), 452 "type='signal',interface='" + std::string(properties::interface) + 453 "',path='" + std::string(power::path) + "',arg0='" + 454 std::string(power::interface) + "'", 455 [hostStatusCallback](sdbusplus::message_t& message) { 456 std::string objectName; 457 boost::container::flat_map<std::string, std::variant<std::string>> 458 values; 459 message.read(objectName, values); 460 auto findState = values.find(power::property); 461 if (findState != values.end()) 462 { 463 bool on = 464 std::get<std::string>(findState->second).ends_with(".Running"); 465 if (!on) 466 { 467 timer.cancel(); 468 powerStatusOn = false; 469 hostStatusCallback(PowerState::on, powerStatusOn); 470 return; 471 } 472 // on comes too quickly 473 timer.expires_after(std::chrono::seconds(10)); 474 timer.async_wait( 475 [hostStatusCallback](boost::system::error_code ec) { 476 if (ec == boost::asio::error::operation_aborted) 477 { 478 return; 479 } 480 if (ec) 481 { 482 std::cerr << "Timer error " << ec.message() << "\n"; 483 return; 484 } 485 powerStatusOn = true; 486 hostStatusCallback(PowerState::on, powerStatusOn); 487 }); 488 } 489 }); 490 491 postMatch = std::make_unique<sdbusplus::bus::match_t>( 492 static_cast<sdbusplus::bus_t&>(*conn), 493 "type='signal',interface='" + std::string(properties::interface) + 494 "',path='" + std::string(post::path) + "',arg0='" + 495 std::string(post::interface) + "'", 496 [hostStatusCallback](sdbusplus::message_t& message) { 497 std::string objectName; 498 boost::container::flat_map<std::string, std::variant<std::string>> 499 values; 500 message.read(objectName, values); 501 auto findState = values.find(post::property); 502 if (findState != values.end()) 503 { 504 auto& value = std::get<std::string>(findState->second); 505 biosHasPost = (value != "Inactive") && 506 (value != "xyz.openbmc_project.State.OperatingSystem." 507 "Status.OSStatus.Inactive"); 508 hostStatusCallback(PowerState::biosPost, biosHasPost); 509 } 510 }); 511 512 chassisMatch = std::make_unique<sdbusplus::bus::match_t>( 513 static_cast<sdbusplus::bus_t&>(*conn), 514 "type='signal',interface='" + std::string(properties::interface) + 515 "',path='" + std::string(chassis::path) + "',arg0='" + 516 std::string(chassis::interface) + "'", 517 [hostStatusCallback](sdbusplus::message_t& message) { 518 std::string objectName; 519 boost::container::flat_map<std::string, std::variant<std::string>> 520 values; 521 message.read(objectName, values); 522 auto findState = values.find(chassis::property); 523 if (findState != values.end()) 524 { 525 bool on = std::get<std::string>(findState->second) 526 .ends_with(chassis::sOn); 527 if (!on) 528 { 529 timerChassisOn.cancel(); 530 chassisStatusOn = false; 531 hostStatusCallback(PowerState::chassisOn, chassisStatusOn); 532 return; 533 } 534 // on comes too quickly 535 timerChassisOn.expires_after(std::chrono::seconds(10)); 536 timerChassisOn.async_wait( 537 [hostStatusCallback](boost::system::error_code ec) { 538 if (ec == boost::asio::error::operation_aborted) 539 { 540 return; 541 } 542 if (ec) 543 { 544 std::cerr << "Timer error " << ec.message() << "\n"; 545 return; 546 } 547 chassisStatusOn = true; 548 hostStatusCallback(PowerState::chassisOn, chassisStatusOn); 549 }); 550 } 551 }); 552 getPowerStatus(conn); 553 getPostStatus(conn); 554 getChassisStatus(conn); 555 } 556 557 void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn) 558 { 559 setupPowerMatchCallback(conn, [](PowerState, bool) {}); 560 } 561 562 // replaces limits if MinReading and MaxReading are found. 563 void findLimits(std::pair<double, double>& limits, 564 const SensorBaseConfiguration* data) 565 { 566 if (data == nullptr) 567 { 568 return; 569 } 570 auto maxFind = data->second.find("MaxReading"); 571 auto minFind = data->second.find("MinReading"); 572 573 if (minFind != data->second.end()) 574 { 575 limits.first = std::visit(VariantToDoubleVisitor(), minFind->second); 576 } 577 if (maxFind != data->second.end()) 578 { 579 limits.second = std::visit(VariantToDoubleVisitor(), maxFind->second); 580 } 581 } 582 583 void createAssociation( 584 std::shared_ptr<sdbusplus::asio::dbus_interface>& association, 585 const std::string& path) 586 { 587 if (association) 588 { 589 fs::path p(path); 590 591 std::vector<Association> associations; 592 associations.emplace_back("chassis", "all_sensors", 593 p.parent_path().string()); 594 association->register_property("Associations", associations); 595 association->initialize(); 596 } 597 } 598 599 void setInventoryAssociation( 600 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association, 601 const std::string& inventoryPath, const std::string& chassisPath) 602 { 603 if (association) 604 { 605 std::vector<Association> associations; 606 associations.emplace_back("inventory", "sensors", inventoryPath); 607 associations.emplace_back("chassis", "all_sensors", chassisPath); 608 609 association->register_property("Associations", associations); 610 association->initialize(); 611 } 612 } 613 614 std::optional<std::string> findContainingChassis(std::string_view configParent, 615 const GetSubTreeType& subtree) 616 { 617 // A parent that is a chassis takes precedence 618 for (const auto& [obj, services] : subtree) 619 { 620 if (obj == configParent) 621 { 622 return obj; 623 } 624 } 625 626 // If the parent is not a chassis, the system chassis is used. This does not 627 // work if there is more than one System, but we assume there is only one 628 // today. 629 for (const auto& [obj, services] : subtree) 630 { 631 for (const auto& [service, interfaces] : services) 632 { 633 if (std::find(interfaces.begin(), interfaces.end(), 634 "xyz.openbmc_project.Inventory.Item.System") != 635 interfaces.end()) 636 { 637 return obj; 638 } 639 } 640 } 641 return std::nullopt; 642 } 643 644 void createInventoryAssoc( 645 const std::shared_ptr<sdbusplus::asio::connection>& conn, 646 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association, 647 const std::string& path) 648 { 649 if (!association) 650 { 651 return; 652 } 653 654 constexpr auto allInterfaces = std::to_array({ 655 "xyz.openbmc_project.Inventory.Item.Board", 656 "xyz.openbmc_project.Inventory.Item.Chassis", 657 }); 658 659 conn->async_method_call( 660 [association, path](const boost::system::error_code ec, 661 const GetSubTreeType& subtree) { 662 // The parent of the config is always the inventory object, and may be 663 // the associated chassis. If the parent is not itself a chassis or 664 // board, the sensor is associated with the system chassis. 665 std::string parent = fs::path(path).parent_path().string(); 666 if (ec) 667 { 668 // In case of error, set the default associations and 669 // initialize the association Interface. 670 setInventoryAssociation(association, parent, parent); 671 return; 672 } 673 setInventoryAssociation( 674 association, parent, 675 findContainingChassis(parent, subtree).value_or(parent)); 676 }, 677 mapper::busName, mapper::path, mapper::interface, "GetSubTree", 678 "/xyz/openbmc_project/inventory/system", 2, allInterfaces); 679 } 680 681 std::optional<double> readFile(const std::string& thresholdFile, 682 const double& scaleFactor) 683 { 684 std::string line; 685 std::ifstream labelFile(thresholdFile); 686 if (labelFile.good()) 687 { 688 std::getline(labelFile, line); 689 labelFile.close(); 690 691 try 692 { 693 return std::stod(line) / scaleFactor; 694 } 695 catch (const std::invalid_argument&) 696 { 697 return std::nullopt; 698 } 699 } 700 return std::nullopt; 701 } 702 703 std::optional<std::tuple<std::string, std::string, std::string>> 704 splitFileName(const fs::path& filePath) 705 { 706 if (filePath.has_filename()) 707 { 708 const auto fileName = filePath.filename().string(); 709 710 size_t numberPos = std::strcspn(fileName.c_str(), "1234567890"); 711 size_t itemPos = std::strcspn(fileName.c_str(), "_"); 712 713 if (numberPos > 0 && itemPos > numberPos && fileName.size() > itemPos) 714 { 715 return std::make_optional( 716 std::make_tuple(fileName.substr(0, numberPos), 717 fileName.substr(numberPos, itemPos - numberPos), 718 fileName.substr(itemPos + 1, fileName.size()))); 719 } 720 } 721 return std::nullopt; 722 } 723 724 static void handleSpecialModeChange(const std::string& manufacturingModeStatus) 725 { 726 manufacturingMode = false; 727 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security." 728 "SpecialMode.Modes.Manufacturing") 729 { 730 manufacturingMode = true; 731 } 732 if (validateUnsecureFeature == 1) 733 { 734 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security." 735 "SpecialMode.Modes.ValidationUnsecure") 736 { 737 manufacturingMode = true; 738 } 739 } 740 } 741 742 void setupManufacturingModeMatch(sdbusplus::asio::connection& conn) 743 { 744 namespace rules = sdbusplus::bus::match::rules; 745 static constexpr const char* specialModeInterface = 746 "xyz.openbmc_project.Security.SpecialMode"; 747 748 const std::string filterSpecialModeIntfAdd = 749 rules::interfacesAdded() + 750 rules::argNpath(0, "/xyz/openbmc_project/security/special_mode"); 751 static std::unique_ptr<sdbusplus::bus::match_t> specialModeIntfMatch = 752 std::make_unique<sdbusplus::bus::match_t>( 753 conn, filterSpecialModeIntfAdd, [](sdbusplus::message_t& m) { 754 sdbusplus::message::object_path path; 755 using PropertyMap = 756 boost::container::flat_map<std::string, std::variant<std::string>>; 757 boost::container::flat_map<std::string, PropertyMap> interfaceAdded; 758 m.read(path, interfaceAdded); 759 auto intfItr = interfaceAdded.find(specialModeInterface); 760 if (intfItr == interfaceAdded.end()) 761 { 762 return; 763 } 764 PropertyMap& propertyList = intfItr->second; 765 auto itr = propertyList.find("SpecialMode"); 766 if (itr == propertyList.end()) 767 { 768 std::cerr << "error getting SpecialMode property " 769 << "\n"; 770 return; 771 } 772 auto* manufacturingModeStatus = std::get_if<std::string>(&itr->second); 773 handleSpecialModeChange(*manufacturingModeStatus); 774 }); 775 776 const std::string filterSpecialModeChange = 777 rules::type::signal() + rules::member("PropertiesChanged") + 778 rules::interface("org.freedesktop.DBus.Properties") + 779 rules::argN(0, specialModeInterface); 780 static std::unique_ptr<sdbusplus::bus::match_t> specialModeChangeMatch = 781 std::make_unique<sdbusplus::bus::match_t>(conn, filterSpecialModeChange, 782 [](sdbusplus::message_t& m) { 783 std::string interfaceName; 784 boost::container::flat_map<std::string, std::variant<std::string>> 785 propertiesChanged; 786 787 m.read(interfaceName, propertiesChanged); 788 auto itr = propertiesChanged.find("SpecialMode"); 789 if (itr == propertiesChanged.end()) 790 { 791 return; 792 } 793 auto* manufacturingModeStatus = std::get_if<std::string>(&itr->second); 794 handleSpecialModeChange(*manufacturingModeStatus); 795 }); 796 797 conn.async_method_call( 798 [](const boost::system::error_code ec, 799 const std::variant<std::string>& getManufactMode) { 800 if (ec) 801 { 802 std::cerr << "error getting SpecialMode status " << ec.message() 803 << "\n"; 804 return; 805 } 806 const auto* manufacturingModeStatus = 807 std::get_if<std::string>(&getManufactMode); 808 handleSpecialModeChange(*manufacturingModeStatus); 809 }, 810 "xyz.openbmc_project.SpecialMode", 811 "/xyz/openbmc_project/security/special_mode", 812 "org.freedesktop.DBus.Properties", "Get", specialModeInterface, 813 "SpecialMode"); 814 } 815 816 bool getManufacturingMode() 817 { 818 return manufacturingMode; 819 } 820 821 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> 822 setupPropertiesChangedMatches( 823 sdbusplus::asio::connection& bus, std::span<const char* const> types, 824 const std::function<void(sdbusplus::message_t&)>& handler) 825 { 826 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches; 827 for (const char* type : types) 828 { 829 auto match = std::make_unique<sdbusplus::bus::match_t>( 830 static_cast<sdbusplus::bus_t&>(bus), 831 "type='signal',member='PropertiesChanged',path_namespace='" + 832 std::string(inventoryPath) + "',arg0namespace='" + 833 configInterfaceName(type) + "'", 834 handler); 835 matches.emplace_back(std::move(match)); 836 } 837 return matches; 838 } 839 840 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> 841 setupPropertiesChangedMatches( 842 sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap, 843 const std::function<void(sdbusplus::message_t&)>& handler) 844 { 845 std::vector<const char*> types; 846 types.reserve(typeMap.size()); 847 for (const auto& [type, dt] : typeMap) 848 { 849 types.push_back(type.data()); 850 } 851 return setupPropertiesChangedMatches(bus, {types}, handler); 852 } 853