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