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