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