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