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