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