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