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