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