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