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