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