1 #include "associations.hpp" 2 #include "processing.hpp" 3 #include "src/argument.hpp" 4 #include "types.hpp" 5 6 #include <tinyxml2.h> 7 8 #include <boost/asio/io_context.hpp> 9 #include <boost/asio/signal_set.hpp> 10 #include <boost/container/flat_map.hpp> 11 #include <sdbusplus/asio/connection.hpp> 12 #include <sdbusplus/asio/object_server.hpp> 13 #include <xyz/openbmc_project/Common/error.hpp> 14 15 #include <atomic> 16 #include <chrono> 17 #include <exception> 18 #include <iomanip> 19 #include <iostream> 20 #include <string> 21 #include <string_view> 22 #include <utility> 23 24 AssociationMaps associationMaps; 25 26 static AllowDenyList serviceAllowList; 27 28 void updateOwners(sdbusplus::asio::connection* conn, 29 boost::container::flat_map<std::string, std::string>& owners, 30 const std::string& newObject) 31 { 32 if (newObject.starts_with(":")) 33 { 34 return; 35 } 36 conn->async_method_call( 37 [&, newObject](const boost::system::error_code ec, 38 const std::string& nameOwner) { 39 if (ec) 40 { 41 std::cerr << "Error getting owner of " << newObject << " : " 42 << ec << "\n"; 43 return; 44 } 45 owners[nameOwner] = newObject; 46 }, 47 "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetNameOwner", 48 newObject); 49 } 50 51 void sendIntrospectionCompleteSignal(sdbusplus::asio::connection* systemBus, 52 const std::string& processName) 53 { 54 // TODO(ed) This signal doesn't get exposed properly in the 55 // introspect right now. Find out how to register signals in 56 // sdbusplus 57 sdbusplus::message_t m = systemBus->new_signal( 58 "/xyz/openbmc_project/object_mapper", 59 "xyz.openbmc_project.ObjectMapper.Private", "IntrospectionComplete"); 60 m.append(processName); 61 m.signal_send(); 62 } 63 64 struct InProgressIntrospect 65 { 66 InProgressIntrospect() = delete; 67 InProgressIntrospect(const InProgressIntrospect&) = delete; 68 InProgressIntrospect(InProgressIntrospect&&) = delete; 69 InProgressIntrospect& operator=(const InProgressIntrospect&) = delete; 70 InProgressIntrospect& operator=(InProgressIntrospect&&) = delete; 71 InProgressIntrospect( 72 sdbusplus::asio::connection* systemBus, boost::asio::io_context& io, 73 const std::string& processName, AssociationMaps& am 74 #ifdef MAPPER_ENABLE_DEBUG 75 , 76 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>> 77 globalStartTime 78 #endif 79 ) : 80 systemBus(systemBus), 81 io(io), processName(processName), assocMaps(am) 82 #ifdef MAPPER_ENABLE_DEBUG 83 , 84 globalStartTime(std::move(globalStartTime)), 85 processStartTime(std::chrono::steady_clock::now()) 86 #endif 87 {} 88 ~InProgressIntrospect() 89 { 90 try 91 { 92 sendIntrospectionCompleteSignal(systemBus, processName); 93 #ifdef MAPPER_ENABLE_DEBUG 94 std::chrono::duration<float> diff = 95 std::chrono::steady_clock::now() - processStartTime; 96 std::cout << std::setw(50) << processName << " scan took " 97 << diff.count() << " seconds\n"; 98 99 // If we're the last outstanding caller globally, calculate the 100 // time it took 101 if (globalStartTime != nullptr && globalStartTime.use_count() == 1) 102 { 103 diff = std::chrono::steady_clock::now() - *globalStartTime; 104 std::cout << "Total scan took " << diff.count() 105 << " seconds to complete\n"; 106 } 107 #endif 108 } 109 catch (const std::exception& e) 110 { 111 std::cerr 112 << "Terminating, unhandled exception while introspecting: " 113 << e.what() << "\n"; 114 std::terminate(); 115 } 116 catch (...) 117 { 118 std::cerr 119 << "Terminating, unhandled exception while introspecting\n"; 120 std::terminate(); 121 } 122 } 123 sdbusplus::asio::connection* systemBus; 124 boost::asio::io_context& io; 125 std::string processName; 126 AssociationMaps& assocMaps; 127 #ifdef MAPPER_ENABLE_DEBUG 128 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>> 129 globalStartTime; 130 std::chrono::time_point<std::chrono::steady_clock> processStartTime; 131 #endif 132 }; 133 134 void doAssociations(sdbusplus::asio::connection* systemBus, 135 InterfaceMapType& interfaceMap, 136 sdbusplus::asio::object_server& objectServer, 137 const std::string& processName, const std::string& path, 138 int timeoutRetries = 0) 139 { 140 constexpr int maxTimeoutRetries = 3; 141 systemBus->async_method_call( 142 [&objectServer, path, processName, &interfaceMap, systemBus, 143 timeoutRetries]( 144 const boost::system::error_code ec, 145 const std::variant<std::vector<Association>>& variantAssociations) { 146 if (ec) 147 { 148 if (ec.value() == boost::system::errc::timed_out && 149 timeoutRetries < maxTimeoutRetries) 150 { 151 doAssociations(systemBus, interfaceMap, objectServer, 152 processName, path, timeoutRetries + 1); 153 return; 154 } 155 std::cerr << "Error getting associations from " << path << "\n"; 156 } 157 std::vector<Association> associations = 158 std::get<std::vector<Association>>(variantAssociations); 159 associationChanged(objectServer, associations, path, processName, 160 interfaceMap, associationMaps); 161 }, 162 processName, path, "org.freedesktop.DBus.Properties", "Get", 163 assocDefsInterface, assocDefsProperty); 164 } 165 166 void doIntrospect(sdbusplus::asio::connection* systemBus, 167 const std::shared_ptr<InProgressIntrospect>& transaction, 168 InterfaceMapType& interfaceMap, 169 sdbusplus::asio::object_server& objectServer, 170 const std::string& path, int timeoutRetries = 0) 171 { 172 constexpr int maxTimeoutRetries = 3; 173 systemBus->async_method_call( 174 [&interfaceMap, &objectServer, transaction, path, systemBus, 175 timeoutRetries](const boost::system::error_code ec, 176 const std::string& introspectXml) { 177 if (ec) 178 { 179 if (ec.value() == boost::system::errc::timed_out && 180 timeoutRetries < maxTimeoutRetries) 181 { 182 doIntrospect(systemBus, transaction, interfaceMap, 183 objectServer, path, timeoutRetries + 1); 184 return; 185 } 186 std::cerr << "Introspect call failed with error: " << ec << ", " 187 << ec.message() 188 << " on process: " << transaction->processName 189 << " path: " << path << "\n"; 190 return; 191 } 192 193 tinyxml2::XMLDocument doc; 194 195 tinyxml2::XMLError e = doc.Parse(introspectXml.c_str()); 196 if (e != tinyxml2::XMLError::XML_SUCCESS) 197 { 198 std::cerr << "XML parsing failed\n"; 199 return; 200 } 201 202 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 203 if (pRoot == nullptr) 204 { 205 std::cerr << "XML document did not contain any data\n"; 206 return; 207 } 208 auto& thisPathMap = interfaceMap[path]; 209 tinyxml2::XMLElement* pElement = 210 pRoot->FirstChildElement("interface"); 211 while (pElement != nullptr) 212 { 213 const char* ifaceName = pElement->Attribute("name"); 214 if (ifaceName == nullptr) 215 { 216 continue; 217 } 218 219 thisPathMap[transaction->processName].emplace(ifaceName); 220 221 if (std::strcmp(ifaceName, assocDefsInterface) == 0) 222 { 223 doAssociations(systemBus, interfaceMap, objectServer, 224 transaction->processName, path); 225 } 226 227 pElement = pElement->NextSiblingElement("interface"); 228 } 229 230 // Check if this new path has a pending association that can 231 // now be completed. 232 checkIfPendingAssociation(path, interfaceMap, 233 transaction->assocMaps, objectServer); 234 235 pElement = pRoot->FirstChildElement("node"); 236 while (pElement != nullptr) 237 { 238 const char* childPath = pElement->Attribute("name"); 239 if (childPath != nullptr) 240 { 241 std::string parentPath(path); 242 if (parentPath == "/") 243 { 244 parentPath.clear(); 245 } 246 247 doIntrospect(systemBus, transaction, interfaceMap, 248 objectServer, parentPath + "/" + childPath); 249 } 250 pElement = pElement->NextSiblingElement("node"); 251 } 252 }, 253 transaction->processName, path, "org.freedesktop.DBus.Introspectable", 254 "Introspect"); 255 } 256 257 void startNewIntrospect( 258 sdbusplus::asio::connection* systemBus, boost::asio::io_context& io, 259 InterfaceMapType& interfaceMap, const std::string& processName, 260 AssociationMaps& assocMaps, 261 #ifdef MAPPER_ENABLE_DEBUG 262 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>> 263 globalStartTime, 264 #endif 265 sdbusplus::asio::object_server& objectServer) 266 { 267 if (needToIntrospect(processName, serviceAllowList)) 268 { 269 std::shared_ptr<InProgressIntrospect> transaction = 270 std::make_shared<InProgressIntrospect>(systemBus, io, processName, 271 assocMaps 272 #ifdef MAPPER_ENABLE_DEBUG 273 , 274 globalStartTime 275 #endif 276 ); 277 278 doIntrospect(systemBus, transaction, interfaceMap, objectServer, "/"); 279 } 280 } 281 282 // TODO(ed) replace with std::set_intersection once c++17 is available 283 template <class InputIt1, class InputIt2> 284 bool intersect(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2) 285 { 286 while (first1 != last1 && first2 != last2) 287 { 288 if (*first1 < *first2) 289 { 290 ++first1; 291 continue; 292 } 293 if (*first2 < *first1) 294 { 295 ++first2; 296 continue; 297 } 298 return true; 299 } 300 return false; 301 } 302 303 void doListNames( 304 boost::asio::io_context& io, InterfaceMapType& interfaceMap, 305 sdbusplus::asio::connection* systemBus, 306 boost::container::flat_map<std::string, std::string>& nameOwners, 307 AssociationMaps& assocMaps, sdbusplus::asio::object_server& objectServer) 308 { 309 systemBus->async_method_call( 310 [&io, &interfaceMap, &nameOwners, &objectServer, systemBus, 311 &assocMaps](const boost::system::error_code ec, 312 std::vector<std::string> processNames) { 313 if (ec) 314 { 315 std::cerr << "Error getting names: " << ec << "\n"; 316 std::exit(EXIT_FAILURE); 317 return; 318 } 319 // Try to make startup consistent 320 std::sort(processNames.begin(), processNames.end()); 321 #ifdef MAPPER_ENABLE_DEBUG 322 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>> 323 globalStartTime = std::make_shared< 324 std::chrono::time_point<std::chrono::steady_clock>>( 325 std::chrono::steady_clock::now()); 326 #endif 327 for (const std::string& processName : processNames) 328 { 329 if (needToIntrospect(processName, serviceAllowList)) 330 { 331 startNewIntrospect(systemBus, io, interfaceMap, processName, 332 assocMaps, 333 #ifdef MAPPER_ENABLE_DEBUG 334 globalStartTime, 335 #endif 336 objectServer); 337 updateOwners(systemBus, nameOwners, processName); 338 } 339 } 340 }, 341 "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", 342 "ListNames"); 343 } 344 345 void splitArgs(const std::string& stringArgs, 346 boost::container::flat_set<std::string>& listArgs) 347 { 348 std::istringstream args; 349 std::string arg; 350 351 args.str(stringArgs); 352 353 while (!args.eof()) 354 { 355 args >> arg; 356 if (!arg.empty()) 357 { 358 listArgs.insert(arg); 359 } 360 } 361 } 362 363 void addObjectMapResult(std::vector<InterfaceMapType::value_type>& objectMap, 364 const std::string& objectPath, 365 const ConnectionNames::value_type& interfaceMap) 366 { 367 // Adds an object path/service name/interface list entry to 368 // the results of GetSubTree and GetAncestors. 369 // If an entry for the object path already exists, just add the 370 // service name and interfaces to that entry, otherwise create 371 // a new entry. 372 auto entry = std::find_if( 373 objectMap.begin(), objectMap.end(), 374 [&objectPath](const auto& i) { return objectPath == i.first; }); 375 376 if (entry != objectMap.end()) 377 { 378 entry->second.emplace(interfaceMap); 379 } 380 else 381 { 382 InterfaceMapType::value_type object; 383 object.first = objectPath; 384 object.second.emplace(interfaceMap); 385 objectMap.push_back(object); 386 } 387 } 388 389 // Remove parents of the passed in path that: 390 // 1) Only have the 3 default interfaces on them 391 // - Means D-Bus created these, not application code, 392 // with the Properties, Introspectable, and Peer ifaces 393 // 2) Have no other child for this owner 394 void removeUnneededParents(const std::string& objectPath, 395 const std::string& owner, 396 InterfaceMapType& interfaceMap) 397 { 398 auto parent = objectPath; 399 400 while (true) 401 { 402 auto pos = parent.find_last_of('/'); 403 if ((pos == std::string::npos) || (pos == 0)) 404 { 405 break; 406 } 407 parent = parent.substr(0, pos); 408 409 auto parentIt = interfaceMap.find(parent); 410 if (parentIt == interfaceMap.end()) 411 { 412 break; 413 } 414 415 auto ifacesIt = parentIt->second.find(owner); 416 if (ifacesIt == parentIt->second.end()) 417 { 418 break; 419 } 420 421 if (ifacesIt->second.size() != 3) 422 { 423 break; 424 } 425 426 auto childPath = parent + '/'; 427 428 // Remove this parent if there isn't a remaining child on this owner 429 auto child = std::find_if( 430 interfaceMap.begin(), interfaceMap.end(), 431 [&owner, &childPath](const auto& entry) { 432 return entry.first.starts_with(childPath) && 433 (entry.second.find(owner) != entry.second.end()); 434 }); 435 436 if (child == interfaceMap.end()) 437 { 438 parentIt->second.erase(ifacesIt); 439 if (parentIt->second.empty()) 440 { 441 interfaceMap.erase(parentIt); 442 } 443 } 444 else 445 { 446 break; 447 } 448 } 449 } 450 451 std::vector<InterfaceMapType::value_type> 452 getAncestors(const InterfaceMapType& interfaceMap, std::string reqPath, 453 std::vector<std::string>& interfaces) 454 { 455 // Interfaces need to be sorted for intersect to function 456 std::sort(interfaces.begin(), interfaces.end()); 457 458 if (reqPath.ends_with("/")) 459 { 460 reqPath.pop_back(); 461 } 462 if (!reqPath.empty() && interfaceMap.find(reqPath) == interfaceMap.end()) 463 { 464 throw sdbusplus::xyz::openbmc_project::Common::Error:: 465 ResourceNotFound(); 466 } 467 468 std::vector<InterfaceMapType::value_type> ret; 469 for (const auto& objectPath : interfaceMap) 470 { 471 const auto& thisPath = objectPath.first; 472 473 if (reqPath == thisPath) 474 { 475 continue; 476 } 477 478 if (reqPath.starts_with(thisPath)) 479 { 480 if (interfaces.empty()) 481 { 482 ret.emplace_back(objectPath); 483 } 484 else 485 { 486 for (const auto& interfaceMap : objectPath.second) 487 { 488 if (intersect(interfaces.begin(), interfaces.end(), 489 interfaceMap.second.begin(), 490 interfaceMap.second.end())) 491 { 492 addObjectMapResult(ret, thisPath, interfaceMap); 493 } 494 } 495 } 496 } 497 } 498 499 return ret; 500 } 501 502 ConnectionNames getObject(const InterfaceMapType& interfaceMap, 503 const std::string& path, 504 std::vector<std::string>& interfaces) 505 { 506 ConnectionNames results; 507 508 // Interfaces need to be sorted for intersect to function 509 std::sort(interfaces.begin(), interfaces.end()); 510 auto pathRef = interfaceMap.find(path); 511 if (pathRef == interfaceMap.end()) 512 { 513 throw sdbusplus::xyz::openbmc_project::Common::Error:: 514 ResourceNotFound(); 515 } 516 if (interfaces.empty()) 517 { 518 return pathRef->second; 519 } 520 for (const auto& interfaceMap : pathRef->second) 521 { 522 if (intersect(interfaces.begin(), interfaces.end(), 523 interfaceMap.second.begin(), interfaceMap.second.end())) 524 { 525 results.emplace(interfaceMap.first, interfaceMap.second); 526 } 527 } 528 529 if (results.empty()) 530 { 531 throw sdbusplus::xyz::openbmc_project::Common::Error:: 532 ResourceNotFound(); 533 } 534 535 return results; 536 } 537 538 std::vector<InterfaceMapType::value_type> 539 getSubTree(const InterfaceMapType& interfaceMap, std::string reqPath, 540 int32_t depth, std::vector<std::string>& interfaces) 541 { 542 if (depth <= 0) 543 { 544 depth = std::numeric_limits<int32_t>::max(); 545 } 546 // Interfaces need to be sorted for intersect to function 547 std::sort(interfaces.begin(), interfaces.end()); 548 549 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped 550 // will be guaranteed not to have a trailing "/" 551 if (!reqPath.ends_with("/")) 552 { 553 reqPath += "/"; 554 } 555 std::string_view reqPathStripped = 556 std::string_view(reqPath).substr(0, reqPath.size() - 1); 557 558 if (!reqPathStripped.empty() && 559 interfaceMap.find(reqPathStripped) == interfaceMap.end()) 560 { 561 throw sdbusplus::xyz::openbmc_project::Common::Error:: 562 ResourceNotFound(); 563 } 564 565 std::vector<InterfaceMapType::value_type> ret; 566 for (const auto& objectPath : interfaceMap) 567 { 568 const auto& thisPath = objectPath.first; 569 570 // Skip exact match on stripped search term 571 if (thisPath == reqPathStripped) 572 { 573 continue; 574 } 575 576 if (thisPath.starts_with(reqPath)) 577 { 578 // count the number of slashes past the stripped search term 579 int32_t thisDepth = std::count( 580 thisPath.begin() + reqPathStripped.size(), thisPath.end(), '/'); 581 if (thisDepth <= depth) 582 { 583 for (const auto& interfaceMap : objectPath.second) 584 { 585 if (intersect(interfaces.begin(), interfaces.end(), 586 interfaceMap.second.begin(), 587 interfaceMap.second.end()) || 588 interfaces.empty()) 589 { 590 addObjectMapResult(ret, thisPath, interfaceMap); 591 } 592 } 593 } 594 } 595 } 596 597 return ret; 598 } 599 600 std::vector<std::string> getSubTreePaths(const InterfaceMapType& interfaceMap, 601 std::string reqPath, int32_t depth, 602 std::vector<std::string>& interfaces) 603 { 604 if (depth <= 0) 605 { 606 depth = std::numeric_limits<int32_t>::max(); 607 } 608 // Interfaces need to be sorted for intersect to function 609 std::sort(interfaces.begin(), interfaces.end()); 610 611 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped 612 // will be guaranteed not to have a trailing "/" 613 if (!reqPath.ends_with("/")) 614 { 615 reqPath += "/"; 616 } 617 std::string_view reqPathStripped = 618 std::string_view(reqPath).substr(0, reqPath.size() - 1); 619 620 if (!reqPathStripped.empty() && 621 interfaceMap.find(reqPathStripped) == interfaceMap.end()) 622 { 623 throw sdbusplus::xyz::openbmc_project::Common::Error:: 624 ResourceNotFound(); 625 } 626 627 std::vector<std::string> ret; 628 for (const auto& objectPath : interfaceMap) 629 { 630 const auto& thisPath = objectPath.first; 631 632 // Skip exact match on stripped search term 633 if (thisPath == reqPathStripped) 634 { 635 continue; 636 } 637 638 if (thisPath.starts_with(reqPath)) 639 { 640 // count the number of slashes past the stripped search term 641 int thisDepth = std::count( 642 thisPath.begin() + reqPathStripped.size(), thisPath.end(), '/'); 643 if (thisDepth <= depth) 644 { 645 bool add = interfaces.empty(); 646 for (const auto& interfaceMap : objectPath.second) 647 { 648 if (intersect(interfaces.begin(), interfaces.end(), 649 interfaceMap.second.begin(), 650 interfaceMap.second.end())) 651 { 652 add = true; 653 break; 654 } 655 } 656 if (add) 657 { 658 // TODO(ed) this is a copy 659 ret.emplace_back(thisPath); 660 } 661 } 662 } 663 } 664 665 return ret; 666 } 667 668 int main(int argc, char** argv) 669 { 670 auto options = ArgumentParser(argc, argv); 671 boost::asio::io_context io; 672 std::shared_ptr<sdbusplus::asio::connection> systemBus = 673 std::make_shared<sdbusplus::asio::connection>(io); 674 675 splitArgs(options["service-namespaces"], serviceAllowList); 676 677 sdbusplus::asio::object_server server(systemBus); 678 679 // Construct a signal set registered for process termination. 680 boost::asio::signal_set signals(io, SIGINT, SIGTERM); 681 signals.async_wait( 682 [&io](const boost::system::error_code&, int) { io.stop(); }); 683 684 InterfaceMapType interfaceMap; 685 boost::container::flat_map<std::string, std::string> nameOwners; 686 687 std::function<void(sdbusplus::message_t & message)> nameChangeHandler = 688 [&interfaceMap, &io, &nameOwners, &server, 689 systemBus](sdbusplus::message_t& message) { 690 std::string name; // well-known 691 std::string oldOwner; // unique-name 692 std::string newOwner; // unique-name 693 694 message.read(name, oldOwner, newOwner); 695 696 if (!oldOwner.empty()) 697 { 698 processNameChangeDelete(nameOwners, name, oldOwner, 699 interfaceMap, associationMaps, server); 700 } 701 702 if (!newOwner.empty()) 703 { 704 #ifdef MAPPER_ENABLE_DEBUG 705 auto transaction = std::make_shared< 706 std::chrono::time_point<std::chrono::steady_clock>>( 707 std::chrono::steady_clock::now()); 708 #endif 709 // New daemon added 710 if (needToIntrospect(name, serviceAllowList)) 711 { 712 nameOwners[newOwner] = name; 713 startNewIntrospect(systemBus.get(), io, interfaceMap, name, 714 associationMaps, 715 #ifdef MAPPER_ENABLE_DEBUG 716 transaction, 717 #endif 718 server); 719 } 720 } 721 }; 722 723 sdbusplus::bus::match_t nameOwnerChanged( 724 static_cast<sdbusplus::bus_t&>(*systemBus), 725 sdbusplus::bus::match::rules::nameOwnerChanged(), nameChangeHandler); 726 727 std::function<void(sdbusplus::message_t & message)> interfacesAddedHandler = 728 [&interfaceMap, &nameOwners, &server](sdbusplus::message_t& message) { 729 sdbusplus::message::object_path objPath; 730 InterfacesAdded interfacesAdded; 731 message.read(objPath, interfacesAdded); 732 std::string wellKnown; 733 if (!getWellKnown(nameOwners, message.get_sender(), wellKnown)) 734 { 735 return; // only introspect well-known 736 } 737 if (needToIntrospect(wellKnown, serviceAllowList)) 738 { 739 processInterfaceAdded(interfaceMap, objPath, interfacesAdded, 740 wellKnown, associationMaps, server); 741 } 742 }; 743 744 sdbusplus::bus::match_t interfacesAdded( 745 static_cast<sdbusplus::bus_t&>(*systemBus), 746 sdbusplus::bus::match::rules::interfacesAdded(), 747 interfacesAddedHandler); 748 749 std::function<void(sdbusplus::message_t & message)> 750 interfacesRemovedHandler = [&interfaceMap, &nameOwners, 751 &server](sdbusplus::message_t& message) { 752 sdbusplus::message::object_path objPath; 753 std::vector<std::string> interfacesRemoved; 754 message.read(objPath, interfacesRemoved); 755 auto connectionMap = interfaceMap.find(objPath.str); 756 if (connectionMap == interfaceMap.end()) 757 { 758 return; 759 } 760 761 std::string sender; 762 if (!getWellKnown(nameOwners, message.get_sender(), sender)) 763 { 764 return; 765 } 766 for (const std::string& interface : interfacesRemoved) 767 { 768 auto interfaceSet = connectionMap->second.find(sender); 769 if (interfaceSet == connectionMap->second.end()) 770 { 771 continue; 772 } 773 774 if (interface == assocDefsInterface) 775 { 776 removeAssociation(objPath.str, sender, server, 777 associationMaps); 778 } 779 780 interfaceSet->second.erase(interface); 781 782 if (interfaceSet->second.empty()) 783 { 784 // If this was the last interface on this connection, 785 // erase the connection 786 connectionMap->second.erase(interfaceSet); 787 788 // Instead of checking if every single path is the endpoint 789 // of an association that needs to be moved to pending, 790 // only check when the only remaining owner of this path is 791 // ourself, which would be because we still own the 792 // association path. 793 if ((connectionMap->second.size() == 1) && 794 (connectionMap->second.begin()->first == 795 "xyz.openbmc_project.ObjectMapper")) 796 { 797 // Remove the 2 association D-Bus paths and move the 798 // association to pending. 799 moveAssociationToPending(objPath.str, associationMaps, 800 server); 801 } 802 } 803 } 804 // If this was the last connection on this object path, 805 // erase the object path 806 if (connectionMap->second.empty()) 807 { 808 interfaceMap.erase(connectionMap); 809 } 810 811 removeUnneededParents(objPath.str, sender, interfaceMap); 812 }; 813 814 sdbusplus::bus::match_t interfacesRemoved( 815 static_cast<sdbusplus::bus_t&>(*systemBus), 816 sdbusplus::bus::match::rules::interfacesRemoved(), 817 interfacesRemovedHandler); 818 819 std::function<void(sdbusplus::message_t & message)> 820 associationChangedHandler = [&server, &nameOwners, &interfaceMap]( 821 sdbusplus::message_t& message) { 822 std::string objectName; 823 boost::container::flat_map<std::string, 824 std::variant<std::vector<Association>>> 825 values; 826 message.read(objectName, values); 827 auto prop = values.find(assocDefsProperty); 828 if (prop != values.end()) 829 { 830 std::vector<Association> associations = 831 std::get<std::vector<Association>>(prop->second); 832 833 std::string wellKnown; 834 if (!getWellKnown(nameOwners, message.get_sender(), wellKnown)) 835 { 836 return; 837 } 838 associationChanged(server, associations, message.get_path(), 839 wellKnown, interfaceMap, associationMaps); 840 } 841 }; 842 sdbusplus::bus::match_t assocChangedMatch( 843 static_cast<sdbusplus::bus_t&>(*systemBus), 844 sdbusplus::bus::match::rules::interface( 845 "org.freedesktop.DBus.Properties") + 846 sdbusplus::bus::match::rules::member("PropertiesChanged") + 847 sdbusplus::bus::match::rules::argN(0, assocDefsInterface), 848 associationChangedHandler); 849 850 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = 851 server.add_interface("/xyz/openbmc_project/object_mapper", 852 "xyz.openbmc_project.ObjectMapper"); 853 854 iface->register_method( 855 "GetAncestors", [&interfaceMap](std::string& reqPath, 856 std::vector<std::string>& interfaces) { 857 return getAncestors(interfaceMap, reqPath, interfaces); 858 }); 859 860 iface->register_method( 861 "GetObject", [&interfaceMap](const std::string& path, 862 std::vector<std::string>& interfaces) { 863 return getObject(interfaceMap, path, interfaces); 864 }); 865 866 iface->register_method( 867 "GetSubTree", [&interfaceMap](std::string& reqPath, int32_t depth, 868 std::vector<std::string>& interfaces) { 869 return getSubTree(interfaceMap, reqPath, depth, interfaces); 870 }); 871 872 iface->register_method( 873 "GetSubTreePaths", 874 [&interfaceMap](std::string& reqPath, int32_t depth, 875 std::vector<std::string>& interfaces) { 876 return getSubTreePaths(interfaceMap, reqPath, depth, interfaces); 877 }); 878 879 iface->initialize(); 880 881 io.post([&]() { 882 doListNames(io, interfaceMap, systemBus.get(), nameOwners, 883 associationMaps, server); 884 }); 885 886 systemBus->request_name("xyz.openbmc_project.ObjectMapper"); 887 888 io.run(); 889 } 890