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> 99 getFullHwmonFilePath(const std::string& directory, 100 const std::string& hwmonBaseName, 101 const std::set<std::string>& permitSet) 102 { 103 std::optional<std::string> result; 104 std::string filename; 105 if (permitSet.empty()) 106 { 107 result = directory + "/" + hwmonBaseName + "_input"; 108 return result; 109 } 110 filename = directory + "/" + hwmonBaseName + "_label"; 111 auto searchVal = openAndRead(filename); 112 if (!searchVal) 113 { 114 /* if the hwmon temp doesn't have a corresponding label file 115 * then use the hwmon temperature base name 116 */ 117 searchVal = hwmonBaseName; 118 } 119 if (permitSet.find(*searchVal) != permitSet.end()) 120 { 121 result = directory + "/" + hwmonBaseName + "_input"; 122 } 123 return result; 124 } 125 126 /** 127 * retrieve a set of basenames and labels to allow sensor creation for. 128 * @param[in] config - a map representing the configuration for a specific 129 * device 130 * @return a set of basenames and labels to allow sensor creation for. An empty 131 * set indicates that everything is permitted. 132 */ 133 std::set<std::string> getPermitSet(const SensorBaseConfigMap& config) 134 { 135 auto permitAttribute = config.find("Labels"); 136 std::set<std::string> permitSet; 137 if (permitAttribute != config.end()) 138 { 139 try 140 { 141 auto val = 142 std::get<std::vector<std::string>>(permitAttribute->second); 143 144 permitSet.insert(std::make_move_iterator(val.begin()), 145 std::make_move_iterator(val.end())); 146 } 147 catch (const std::bad_variant_access& err) 148 { 149 std::cerr << err.what() 150 << ":PermitList does not contain a list, wrong " 151 "variant type.\n"; 152 } 153 } 154 return permitSet; 155 } 156 157 bool getSensorConfiguration( 158 const std::string& type, 159 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, 160 ManagedObjectType& resp, bool useCache) 161 { 162 static ManagedObjectType managedObj; 163 std::string typeIntf = configInterfaceName(type); 164 165 if (!useCache) 166 { 167 managedObj.clear(); 168 sdbusplus::message_t getManagedObjects = 169 dbusConnection->new_method_call( 170 entityManagerName, "/xyz/openbmc_project/inventory", 171 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 172 try 173 { 174 sdbusplus::message_t reply = 175 dbusConnection->call(getManagedObjects); 176 reply.read(managedObj); 177 } 178 catch (const sdbusplus::exception_t& e) 179 { 180 std::cerr << "While calling GetManagedObjects on service:" 181 << entityManagerName << " exception name:" << e.name() 182 << "and description:" << e.description() 183 << " was thrown\n"; 184 return false; 185 } 186 } 187 for (const auto& pathPair : managedObj) 188 { 189 for (const auto& [intf, cfg] : pathPair.second) 190 { 191 if (intf.starts_with(typeIntf)) 192 { 193 resp.emplace(pathPair); 194 break; 195 } 196 } 197 } 198 return true; 199 } 200 201 bool findFiles(const fs::path& dirPath, std::string_view matchString, 202 std::vector<fs::path>& foundPaths, int symlinkDepth) 203 { 204 std::error_code ec; 205 if (!fs::exists(dirPath, ec)) 206 { 207 return false; 208 } 209 210 std::vector<std::regex> matchPieces; 211 212 size_t pos = 0; 213 std::string token; 214 // Generate the regex expressions list from the match we were given 215 while ((pos = matchString.find('/')) != std::string::npos) 216 { 217 token = matchString.substr(0, pos); 218 matchPieces.emplace_back(token); 219 matchString.remove_prefix(pos + 1); 220 } 221 matchPieces.emplace_back(std::string{matchString}); 222 223 // Check if the match string contains directories, and skip the match of 224 // subdirectory if not 225 if (matchPieces.size() <= 1) 226 { 227 std::regex search(std::string{matchString}); 228 std::smatch match; 229 for (auto p = fs::recursive_directory_iterator( 230 dirPath, fs::directory_options::follow_directory_symlink); 231 p != fs::recursive_directory_iterator(); ++p) 232 { 233 std::string path = p->path().string(); 234 if (!is_directory(*p)) 235 { 236 if (std::regex_search(path, match, search)) 237 { 238 foundPaths.emplace_back(p->path()); 239 } 240 } 241 if (p.depth() >= symlinkDepth) 242 { 243 p.disable_recursion_pending(); 244 } 245 } 246 return true; 247 } 248 249 // The match string contains directories, verify each level of sub 250 // directories 251 for (auto p = fs::recursive_directory_iterator( 252 dirPath, fs::directory_options::follow_directory_symlink); 253 p != fs::recursive_directory_iterator(); ++p) 254 { 255 std::vector<std::regex>::iterator matchPiece = matchPieces.begin(); 256 fs::path::iterator pathIt = p->path().begin(); 257 for (const fs::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() << "\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() << "\n"; 405 return; 406 } 407 const auto& value = std::get<std::string>(state); 408 biosHasPost = (value != "Inactive") && 409 (value != "xyz.openbmc_project.State.OperatingSystem." 410 "Status.OSStatus.Inactive"); 411 }, 412 post::busname, post::path, properties::interface, properties::get, 413 post::interface, post::property); 414 } 415 416 static void 417 getChassisStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn, 418 size_t retries = 2) 419 { 420 conn->async_method_call( 421 [conn, retries](boost::system::error_code ec, 422 const std::variant<std::string>& state) { 423 if (ec) 424 { 425 if (retries != 0U) 426 { 427 auto timer = std::make_shared<boost::asio::steady_timer>( 428 conn->get_io_context()); 429 timer->expires_after(std::chrono::seconds(15)); 430 timer->async_wait( 431 [timer, conn, retries](boost::system::error_code) { 432 getChassisStatus(conn, retries - 1); 433 }); 434 return; 435 } 436 437 // we commonly come up before power control, we'll capture the 438 // property change later 439 std::cerr << "error getting chassis power status " << ec.message() 440 << "\n"; 441 return; 442 } 443 chassisStatusOn = std::get<std::string>(state).ends_with(chassis::sOn); 444 }, 445 chassis::busname, chassis::path, properties::interface, properties::get, 446 chassis::interface, chassis::property); 447 } 448 449 void setupPowerMatchCallback( 450 const std::shared_ptr<sdbusplus::asio::connection>& conn, 451 std::function<void(PowerState type, bool state)>&& hostStatusCallback) 452 { 453 static boost::asio::steady_timer timer(conn->get_io_context()); 454 static boost::asio::steady_timer timerChassisOn(conn->get_io_context()); 455 // create a match for powergood changes, first time do a method call to 456 // cache the correct value 457 if (powerMatch) 458 { 459 return; 460 } 461 462 powerMatch = std::make_unique<sdbusplus::bus::match_t>( 463 static_cast<sdbusplus::bus_t&>(*conn), 464 "type='signal',interface='" + std::string(properties::interface) + 465 "',path='" + std::string(power::path) + "',arg0='" + 466 std::string(power::interface) + "'", 467 [hostStatusCallback](sdbusplus::message_t& message) { 468 std::string objectName; 469 boost::container::flat_map<std::string, std::variant<std::string>> 470 values; 471 message.read(objectName, values); 472 auto findState = values.find(power::property); 473 if (findState != values.end()) 474 { 475 bool on = 476 std::get<std::string>(findState->second).ends_with(".Running"); 477 if (!on) 478 { 479 timer.cancel(); 480 powerStatusOn = false; 481 hostStatusCallback(PowerState::on, powerStatusOn); 482 return; 483 } 484 // on comes too quickly 485 timer.expires_after(std::chrono::seconds(10)); 486 timer.async_wait( 487 [hostStatusCallback](boost::system::error_code ec) { 488 if (ec == boost::asio::error::operation_aborted) 489 { 490 return; 491 } 492 if (ec) 493 { 494 std::cerr << "Timer error " << ec.message() << "\n"; 495 return; 496 } 497 powerStatusOn = true; 498 hostStatusCallback(PowerState::on, powerStatusOn); 499 }); 500 } 501 }); 502 503 postMatch = std::make_unique<sdbusplus::bus::match_t>( 504 static_cast<sdbusplus::bus_t&>(*conn), 505 "type='signal',interface='" + std::string(properties::interface) + 506 "',path='" + std::string(post::path) + "',arg0='" + 507 std::string(post::interface) + "'", 508 [hostStatusCallback](sdbusplus::message_t& message) { 509 std::string objectName; 510 boost::container::flat_map<std::string, std::variant<std::string>> 511 values; 512 message.read(objectName, values); 513 auto findState = values.find(post::property); 514 if (findState != values.end()) 515 { 516 auto& value = std::get<std::string>(findState->second); 517 biosHasPost = (value != "Inactive") && 518 (value != "xyz.openbmc_project.State.OperatingSystem." 519 "Status.OSStatus.Inactive"); 520 hostStatusCallback(PowerState::biosPost, biosHasPost); 521 } 522 }); 523 524 chassisMatch = std::make_unique<sdbusplus::bus::match_t>( 525 static_cast<sdbusplus::bus_t&>(*conn), 526 "type='signal',interface='" + std::string(properties::interface) + 527 "',path='" + std::string(chassis::path) + "',arg0='" + 528 std::string(chassis::interface) + "'", 529 [hostStatusCallback = std::move(hostStatusCallback)]( 530 sdbusplus::message_t& message) { 531 std::string objectName; 532 boost::container::flat_map<std::string, std::variant<std::string>> 533 values; 534 message.read(objectName, values); 535 auto findState = values.find(chassis::property); 536 if (findState != values.end()) 537 { 538 bool on = std::get<std::string>(findState->second) 539 .ends_with(chassis::sOn); 540 if (!on) 541 { 542 timerChassisOn.cancel(); 543 chassisStatusOn = false; 544 hostStatusCallback(PowerState::chassisOn, chassisStatusOn); 545 return; 546 } 547 // on comes too quickly 548 timerChassisOn.expires_after(std::chrono::seconds(10)); 549 timerChassisOn.async_wait( 550 [hostStatusCallback](boost::system::error_code ec) { 551 if (ec == boost::asio::error::operation_aborted) 552 { 553 return; 554 } 555 if (ec) 556 { 557 std::cerr << "Timer error " << ec.message() << "\n"; 558 return; 559 } 560 chassisStatusOn = true; 561 hostStatusCallback(PowerState::chassisOn, chassisStatusOn); 562 }); 563 } 564 }); 565 getPowerStatus(conn); 566 getPostStatus(conn); 567 getChassisStatus(conn); 568 } 569 570 void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn) 571 { 572 setupPowerMatchCallback(conn, [](PowerState, bool) {}); 573 } 574 575 // replaces limits if MinReading and MaxReading are found. 576 void findLimits(std::pair<double, double>& limits, 577 const SensorBaseConfiguration* data) 578 { 579 if (data == nullptr) 580 { 581 return; 582 } 583 auto maxFind = data->second.find("MaxReading"); 584 auto minFind = data->second.find("MinReading"); 585 586 if (minFind != data->second.end()) 587 { 588 limits.first = std::visit(VariantToDoubleVisitor(), minFind->second); 589 } 590 if (maxFind != data->second.end()) 591 { 592 limits.second = std::visit(VariantToDoubleVisitor(), maxFind->second); 593 } 594 } 595 596 void createAssociation( 597 std::shared_ptr<sdbusplus::asio::dbus_interface>& association, 598 const std::string& path) 599 { 600 if (association) 601 { 602 fs::path p(path); 603 604 std::vector<Association> associations; 605 associations.emplace_back("chassis", "all_sensors", 606 p.parent_path().string()); 607 association->register_property("Associations", associations); 608 association->initialize(); 609 } 610 } 611 612 void setInventoryAssociation( 613 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association, 614 const std::string& inventoryPath, const std::string& chassisPath) 615 { 616 if (association) 617 { 618 std::vector<Association> associations; 619 associations.emplace_back("inventory", "sensors", inventoryPath); 620 associations.emplace_back("chassis", "all_sensors", chassisPath); 621 622 association->register_property("Associations", associations); 623 association->initialize(); 624 } 625 } 626 627 std::optional<std::string> findContainingChassis(std::string_view configParent, 628 const GetSubTreeType& subtree) 629 { 630 // A parent that is a chassis takes precedence 631 for (const auto& [obj, services] : subtree) 632 { 633 if (obj == configParent) 634 { 635 return obj; 636 } 637 } 638 639 // If the parent is not a chassis, the system chassis is used. This does not 640 // work if there is more than one System, but we assume there is only one 641 // today. 642 for (const auto& [obj, services] : subtree) 643 { 644 for (const auto& [service, interfaces] : services) 645 { 646 if (std::find(interfaces.begin(), interfaces.end(), 647 "xyz.openbmc_project.Inventory.Item.System") != 648 interfaces.end()) 649 { 650 return obj; 651 } 652 } 653 } 654 return std::nullopt; 655 } 656 657 void createInventoryAssoc( 658 const std::shared_ptr<sdbusplus::asio::connection>& conn, 659 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association, 660 const std::string& path) 661 { 662 if (!association) 663 { 664 return; 665 } 666 667 constexpr auto allInterfaces = std::to_array({ 668 "xyz.openbmc_project.Inventory.Item.Board", 669 "xyz.openbmc_project.Inventory.Item.Chassis", 670 }); 671 672 conn->async_method_call( 673 [association, path](const boost::system::error_code ec, 674 const GetSubTreeType& subtree) { 675 // The parent of the config is always the inventory object, and may be 676 // the associated chassis. If the parent is not itself a chassis or 677 // board, the sensor is associated with the system chassis. 678 std::string parent = fs::path(path).parent_path().string(); 679 if (ec) 680 { 681 // In case of error, set the default associations and 682 // initialize the association Interface. 683 setInventoryAssociation(association, parent, parent); 684 return; 685 } 686 setInventoryAssociation( 687 association, parent, 688 findContainingChassis(parent, subtree).value_or(parent)); 689 }, 690 mapper::busName, mapper::path, mapper::interface, "GetSubTree", 691 "/xyz/openbmc_project/inventory/system", 2, allInterfaces); 692 } 693 694 std::optional<double> readFile(const std::string& thresholdFile, 695 const double& scaleFactor) 696 { 697 std::string line; 698 std::ifstream labelFile(thresholdFile); 699 if (labelFile.good()) 700 { 701 std::getline(labelFile, line); 702 labelFile.close(); 703 704 try 705 { 706 return std::stod(line) / scaleFactor; 707 } 708 catch (const std::invalid_argument&) 709 { 710 return std::nullopt; 711 } 712 } 713 return std::nullopt; 714 } 715 716 std::optional<std::tuple<std::string, std::string, std::string>> 717 splitFileName(const fs::path& filePath) 718 { 719 if (filePath.has_filename()) 720 { 721 const auto fileName = filePath.filename().string(); 722 723 size_t numberPos = std::strcspn(fileName.c_str(), "1234567890"); 724 size_t itemPos = std::strcspn(fileName.c_str(), "_"); 725 726 if (numberPos > 0 && itemPos > numberPos && fileName.size() > itemPos) 727 { 728 return std::make_optional( 729 std::make_tuple(fileName.substr(0, numberPos), 730 fileName.substr(numberPos, itemPos - numberPos), 731 fileName.substr(itemPos + 1, fileName.size()))); 732 } 733 } 734 return std::nullopt; 735 } 736 737 static void handleSpecialModeChange(const std::string& manufacturingModeStatus) 738 { 739 manufacturingMode = false; 740 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security." 741 "SpecialMode.Modes.Manufacturing") 742 { 743 manufacturingMode = true; 744 } 745 if (validateUnsecureFeature == 1) 746 { 747 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security." 748 "SpecialMode.Modes.ValidationUnsecure") 749 { 750 manufacturingMode = true; 751 } 752 } 753 } 754 755 void setupManufacturingModeMatch(sdbusplus::asio::connection& conn) 756 { 757 namespace rules = sdbusplus::bus::match::rules; 758 static constexpr const char* specialModeInterface = 759 "xyz.openbmc_project.Security.SpecialMode"; 760 761 const std::string filterSpecialModeIntfAdd = 762 rules::interfacesAdded() + 763 rules::argNpath(0, "/xyz/openbmc_project/security/special_mode"); 764 static std::unique_ptr<sdbusplus::bus::match_t> specialModeIntfMatch = 765 std::make_unique<sdbusplus::bus::match_t>( 766 conn, filterSpecialModeIntfAdd, [](sdbusplus::message_t& m) { 767 sdbusplus::message::object_path path; 768 using PropertyMap = 769 boost::container::flat_map<std::string, std::variant<std::string>>; 770 boost::container::flat_map<std::string, PropertyMap> interfaceAdded; 771 m.read(path, interfaceAdded); 772 auto intfItr = interfaceAdded.find(specialModeInterface); 773 if (intfItr == interfaceAdded.end()) 774 { 775 return; 776 } 777 PropertyMap& propertyList = intfItr->second; 778 auto itr = propertyList.find("SpecialMode"); 779 if (itr == propertyList.end()) 780 { 781 std::cerr << "error getting SpecialMode property " 782 << "\n"; 783 return; 784 } 785 auto* manufacturingModeStatus = std::get_if<std::string>(&itr->second); 786 handleSpecialModeChange(*manufacturingModeStatus); 787 }); 788 789 const std::string filterSpecialModeChange = 790 rules::type::signal() + rules::member("PropertiesChanged") + 791 rules::interface("org.freedesktop.DBus.Properties") + 792 rules::argN(0, specialModeInterface); 793 static std::unique_ptr<sdbusplus::bus::match_t> specialModeChangeMatch = 794 std::make_unique<sdbusplus::bus::match_t>(conn, filterSpecialModeChange, 795 [](sdbusplus::message_t& m) { 796 std::string interfaceName; 797 boost::container::flat_map<std::string, std::variant<std::string>> 798 propertiesChanged; 799 800 m.read(interfaceName, propertiesChanged); 801 auto itr = propertiesChanged.find("SpecialMode"); 802 if (itr == propertiesChanged.end()) 803 { 804 return; 805 } 806 auto* manufacturingModeStatus = std::get_if<std::string>(&itr->second); 807 handleSpecialModeChange(*manufacturingModeStatus); 808 }); 809 810 conn.async_method_call( 811 [](const boost::system::error_code ec, 812 const std::variant<std::string>& getManufactMode) { 813 if (ec) 814 { 815 std::cerr << "error getting SpecialMode status " << ec.message() 816 << "\n"; 817 return; 818 } 819 const auto* manufacturingModeStatus = 820 std::get_if<std::string>(&getManufactMode); 821 handleSpecialModeChange(*manufacturingModeStatus); 822 }, 823 "xyz.openbmc_project.SpecialMode", 824 "/xyz/openbmc_project/security/special_mode", 825 "org.freedesktop.DBus.Properties", "Get", specialModeInterface, 826 "SpecialMode"); 827 } 828 829 bool getManufacturingMode() 830 { 831 return manufacturingMode; 832 } 833 834 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> 835 setupPropertiesChangedMatches( 836 sdbusplus::asio::connection& bus, std::span<const char* const> types, 837 const std::function<void(sdbusplus::message_t&)>& handler) 838 { 839 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches; 840 for (const char* type : types) 841 { 842 auto match = std::make_unique<sdbusplus::bus::match_t>( 843 static_cast<sdbusplus::bus_t&>(bus), 844 "type='signal',member='PropertiesChanged',path_namespace='" + 845 std::string(inventoryPath) + "',arg0namespace='" + 846 configInterfaceName(type) + "'", 847 handler); 848 matches.emplace_back(std::move(match)); 849 } 850 return matches; 851 } 852 853 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> 854 setupPropertiesChangedMatches( 855 sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap, 856 const std::function<void(sdbusplus::message_t&)>& handler) 857 { 858 std::vector<const char*> types; 859 types.reserve(typeMap.size()); 860 for (const auto& [type, dt] : typeMap) 861 { 862 types.push_back(type.data()); 863 } 864 return setupPropertiesChangedMatches(bus, {types}, handler); 865 } 866