1 #include "associations.hpp" 2 #include "processing.hpp" 3 #include "src/argument.hpp" 4 5 #include <tinyxml2.h> 6 7 #include <atomic> 8 #include <boost/algorithm/string/predicate.hpp> 9 #include <boost/container/flat_map.hpp> 10 #include <chrono> 11 #include <iomanip> 12 #include <iostream> 13 #include <sdbusplus/asio/connection.hpp> 14 #include <sdbusplus/asio/object_server.hpp> 15 16 constexpr const char* OBJECT_MAPPER_DBUS_NAME = 17 "xyz.openbmc_project.ObjectMapper"; 18 constexpr const char* XYZ_ASSOCIATION_INTERFACE = 19 "xyz.openbmc_project.Association"; 20 21 using Association = std::tuple<std::string, std::string, std::string>; 22 23 AssociationInterfaces associationInterfaces; 24 AssociationOwnersType associationOwners; 25 26 static WhiteBlackList service_whitelist; 27 static WhiteBlackList service_blacklist; 28 29 /** Exception thrown when a path is not found in the object list. */ 30 struct NotFoundException final : public sdbusplus::exception_t 31 { 32 const char* name() const noexcept override 33 { 34 return "org.freedesktop.DBus.Error.FileNotFound"; 35 }; 36 const char* description() const noexcept override 37 { 38 return "path or object not found"; 39 }; 40 const char* what() const noexcept override 41 { 42 return "org.freedesktop.DBus.Error.FileNotFound: " 43 "The requested object was not found"; 44 }; 45 }; 46 47 void update_owners(sdbusplus::asio::connection* conn, 48 boost::container::flat_map<std::string, std::string>& owners, 49 const std::string& new_object) 50 { 51 if (boost::starts_with(new_object, ":")) 52 { 53 return; 54 } 55 conn->async_method_call( 56 [&, new_object](const boost::system::error_code ec, 57 const std::string& nameOwner) { 58 if (ec) 59 { 60 std::cerr << "Error getting owner of " << new_object << " : " 61 << ec << "\n"; 62 return; 63 } 64 owners[nameOwner] = new_object; 65 }, 66 "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetNameOwner", 67 new_object); 68 } 69 70 void send_introspection_complete_signal(sdbusplus::asio::connection* system_bus, 71 const std::string& process_name) 72 { 73 // TODO(ed) This signal doesn't get exposed properly in the 74 // introspect right now. Find out how to register signals in 75 // sdbusplus 76 sdbusplus::message::message m = system_bus->new_signal( 77 "/xyz/openbmc_project/object_mapper", 78 "xyz.openbmc_project.ObjectMapper.Private", "IntrospectionComplete"); 79 m.append(process_name); 80 m.signal_send(); 81 } 82 83 struct InProgressIntrospect 84 { 85 InProgressIntrospect( 86 sdbusplus::asio::connection* system_bus, boost::asio::io_service& io, 87 const std::string& process_name 88 #ifdef DEBUG 89 , 90 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>> 91 global_start_time 92 #endif 93 ) : 94 system_bus(system_bus), 95 io(io), process_name(process_name) 96 #ifdef DEBUG 97 , 98 global_start_time(global_start_time), 99 process_start_time(std::chrono::steady_clock::now()) 100 #endif 101 { 102 } 103 ~InProgressIntrospect() 104 { 105 send_introspection_complete_signal(system_bus, process_name); 106 107 #ifdef DEBUG 108 std::chrono::duration<float> diff = 109 std::chrono::steady_clock::now() - process_start_time; 110 std::cout << std::setw(50) << process_name << " scan took " 111 << diff.count() << " seconds\n"; 112 113 // If we're the last outstanding caller globally, calculate the 114 // time it took 115 if (global_start_time != nullptr && global_start_time.use_count() == 1) 116 { 117 diff = std::chrono::steady_clock::now() - *global_start_time; 118 std::cout << "Total scan took " << diff.count() 119 << " seconds to complete\n"; 120 } 121 #endif 122 } 123 sdbusplus::asio::connection* system_bus; 124 boost::asio::io_service& io; 125 std::string process_name; 126 #ifdef DEBUG 127 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>> 128 global_start_time; 129 std::chrono::time_point<std::chrono::steady_clock> process_start_time; 130 #endif 131 }; 132 133 // Based on the latest values of the org.openbmc.Associations.associations 134 // property, passed in via the newAssociations param, check if any of the 135 // paths in the xyz.openbmc_project.Association.endpoints D-Bus property 136 // for that association need to be removed. If the last path is removed 137 // from the endpoints property, remove that whole association object from 138 // D-Bus. 139 void checkAssociationEndpointRemoves( 140 const std::string& sourcePath, const std::string& owner, 141 const AssociationPaths& newAssociations, 142 sdbusplus::asio::object_server& objectServer) 143 { 144 // Find the services that have associations on this path. 145 auto originalOwners = associationOwners.find(sourcePath); 146 if (originalOwners == associationOwners.end()) 147 { 148 return; 149 } 150 151 // Find the associations for this service 152 auto originalAssociations = originalOwners->second.find(owner); 153 if (originalAssociations == originalOwners->second.end()) 154 { 155 return; 156 } 157 158 // Compare the new endpoints versus the original endpoints, and 159 // remove any of the original ones that aren't in the new list. 160 for (const auto& [originalAssocPath, originalEndpoints] : 161 originalAssociations->second) 162 { 163 // Check if this source even still has each association that 164 // was there previously, and if not, remove all of its endpoints 165 // from the D-Bus endpoints property which will cause the whole 166 // association path to be removed if no endpoints remain. 167 auto newEndpoints = newAssociations.find(originalAssocPath); 168 if (newEndpoints == newAssociations.end()) 169 { 170 removeAssociationEndpoints(objectServer, originalAssocPath, 171 originalEndpoints, 172 associationInterfaces); 173 } 174 else 175 { 176 // The association is still there. Check if the endpoints 177 // changed. 178 boost::container::flat_set<std::string> toRemove; 179 180 for (auto& originalEndpoint : originalEndpoints) 181 { 182 if (std::find(newEndpoints->second.begin(), 183 newEndpoints->second.end(), 184 originalEndpoint) == newEndpoints->second.end()) 185 { 186 toRemove.emplace(originalEndpoint); 187 } 188 } 189 if (!toRemove.empty()) 190 { 191 removeAssociationEndpoints(objectServer, originalAssocPath, 192 toRemove, associationInterfaces); 193 } 194 } 195 } 196 } 197 198 // Called when either a new org.openbmc.Associations interface was 199 // created, or the associations property on that interface changed. 200 void associationChanged(sdbusplus::asio::object_server& objectServer, 201 const std::vector<Association>& associations, 202 const std::string& path, const std::string& owner) 203 { 204 AssociationPaths objects; 205 206 for (const Association& association : associations) 207 { 208 std::string forward; 209 std::string reverse; 210 std::string endpoint; 211 std::tie(forward, reverse, endpoint) = association; 212 213 if (forward.size()) 214 { 215 objects[path + "/" + forward].emplace(endpoint); 216 } 217 if (reverse.size()) 218 { 219 if (endpoint.empty()) 220 { 221 std::cerr << "Found invalid association on path " << path 222 << "\n"; 223 continue; 224 } 225 objects[endpoint + "/" + reverse].emplace(path); 226 } 227 } 228 for (const auto& object : objects) 229 { 230 // the mapper exposes the new association interface but intakes 231 // the old 232 233 auto& iface = associationInterfaces[object.first]; 234 auto& i = std::get<ifacePos>(iface); 235 auto& endpoints = std::get<endpointsPos>(iface); 236 237 // Only add new endpoints 238 for (auto& e : object.second) 239 { 240 if (std::find(endpoints.begin(), endpoints.end(), e) == 241 endpoints.end()) 242 { 243 endpoints.push_back(e); 244 } 245 } 246 247 // If the interface already exists, only need to update 248 // the property value, otherwise create it 249 if (i) 250 { 251 i->set_property("endpoints", endpoints); 252 } 253 else 254 { 255 i = objectServer.add_interface(object.first, 256 XYZ_ASSOCIATION_INTERFACE); 257 i->register_property("endpoints", endpoints); 258 i->initialize(); 259 } 260 } 261 262 // Check for endpoints being removed instead of added 263 checkAssociationEndpointRemoves(path, owner, objects, objectServer); 264 265 // Update associationOwners with the latest info 266 auto a = associationOwners.find(path); 267 if (a != associationOwners.end()) 268 { 269 auto o = a->second.find(owner); 270 if (o != a->second.end()) 271 { 272 o->second = std::move(objects); 273 } 274 else 275 { 276 a->second.emplace(owner, std::move(objects)); 277 } 278 } 279 else 280 { 281 boost::container::flat_map<std::string, AssociationPaths> owners; 282 owners.emplace(owner, std::move(objects)); 283 associationOwners.emplace(path, owners); 284 } 285 } 286 287 void do_associations(sdbusplus::asio::connection* system_bus, 288 sdbusplus::asio::object_server& objectServer, 289 const std::string& processName, const std::string& path) 290 { 291 system_bus->async_method_call( 292 [&objectServer, path, processName]( 293 const boost::system::error_code ec, 294 const sdbusplus::message::variant<std::vector<Association>>& 295 variantAssociations) { 296 if (ec) 297 { 298 std::cerr << "Error getting associations from " << path << "\n"; 299 } 300 std::vector<Association> associations = 301 sdbusplus::message::variant_ns::get<std::vector<Association>>( 302 variantAssociations); 303 associationChanged(objectServer, associations, path, processName); 304 }, 305 processName, path, "org.freedesktop.DBus.Properties", "Get", 306 ASSOCIATIONS_INTERFACE, "associations"); 307 } 308 309 void do_introspect(sdbusplus::asio::connection* system_bus, 310 std::shared_ptr<InProgressIntrospect> transaction, 311 interface_map_type& interface_map, 312 sdbusplus::asio::object_server& objectServer, 313 std::string path) 314 { 315 system_bus->async_method_call( 316 [&interface_map, &objectServer, transaction, path, 317 system_bus](const boost::system::error_code ec, 318 const std::string& introspect_xml) { 319 if (ec) 320 { 321 std::cerr << "Introspect call failed with error: " << ec << ", " 322 << ec.message() 323 << " on process: " << transaction->process_name 324 << " path: " << path << "\n"; 325 return; 326 } 327 328 tinyxml2::XMLDocument doc; 329 330 tinyxml2::XMLError e = doc.Parse(introspect_xml.c_str()); 331 if (e != tinyxml2::XMLError::XML_SUCCESS) 332 { 333 std::cerr << "XML parsing failed\n"; 334 return; 335 } 336 337 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 338 if (pRoot == nullptr) 339 { 340 std::cerr << "XML document did not contain any data\n"; 341 return; 342 } 343 auto& thisPathMap = interface_map[path]; 344 tinyxml2::XMLElement* pElement = 345 pRoot->FirstChildElement("interface"); 346 while (pElement != nullptr) 347 { 348 const char* iface_name = pElement->Attribute("name"); 349 if (iface_name == nullptr) 350 { 351 continue; 352 } 353 354 std::string iface{iface_name}; 355 356 thisPathMap[transaction->process_name].emplace(iface_name); 357 358 if (std::strcmp(iface_name, ASSOCIATIONS_INTERFACE) == 0) 359 { 360 do_associations(system_bus, objectServer, 361 transaction->process_name, path); 362 } 363 364 pElement = pElement->NextSiblingElement("interface"); 365 } 366 367 pElement = pRoot->FirstChildElement("node"); 368 while (pElement != nullptr) 369 { 370 const char* child_path = pElement->Attribute("name"); 371 if (child_path != nullptr) 372 { 373 std::string parent_path(path); 374 if (parent_path == "/") 375 { 376 parent_path.clear(); 377 } 378 379 do_introspect(system_bus, transaction, interface_map, 380 objectServer, parent_path + "/" + child_path); 381 } 382 pElement = pElement->NextSiblingElement("node"); 383 } 384 }, 385 transaction->process_name, path, "org.freedesktop.DBus.Introspectable", 386 "Introspect"); 387 } 388 389 void start_new_introspect( 390 sdbusplus::asio::connection* system_bus, boost::asio::io_service& io, 391 interface_map_type& interface_map, const std::string& process_name, 392 #ifdef DEBUG 393 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>> 394 global_start_time, 395 #endif 396 sdbusplus::asio::object_server& objectServer) 397 { 398 if (needToIntrospect(process_name, service_whitelist, service_blacklist)) 399 { 400 std::shared_ptr<InProgressIntrospect> transaction = 401 std::make_shared<InProgressIntrospect>(system_bus, io, process_name 402 #ifdef DEBUG 403 , 404 global_start_time 405 #endif 406 ); 407 408 do_introspect(system_bus, transaction, interface_map, objectServer, 409 "/"); 410 } 411 } 412 413 // TODO(ed) replace with std::set_intersection once c++17 is available 414 template <class InputIt1, class InputIt2> 415 bool intersect(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2) 416 { 417 while (first1 != last1 && first2 != last2) 418 { 419 if (*first1 < *first2) 420 { 421 ++first1; 422 continue; 423 } 424 if (*first2 < *first1) 425 { 426 ++first2; 427 continue; 428 } 429 return true; 430 } 431 return false; 432 } 433 434 void doListNames( 435 boost::asio::io_service& io, interface_map_type& interface_map, 436 sdbusplus::asio::connection* system_bus, 437 boost::container::flat_map<std::string, std::string>& name_owners, 438 sdbusplus::asio::object_server& objectServer) 439 { 440 system_bus->async_method_call( 441 [&io, &interface_map, &name_owners, &objectServer, 442 system_bus](const boost::system::error_code ec, 443 std::vector<std::string> process_names) { 444 if (ec) 445 { 446 std::cerr << "Error getting names: " << ec << "\n"; 447 std::exit(EXIT_FAILURE); 448 return; 449 } 450 // Try to make startup consistent 451 std::sort(process_names.begin(), process_names.end()); 452 #ifdef DEBUG 453 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>> 454 global_start_time = std::make_shared< 455 std::chrono::time_point<std::chrono::steady_clock>>( 456 std::chrono::steady_clock::now()); 457 #endif 458 for (const std::string& process_name : process_names) 459 { 460 if (needToIntrospect(process_name, service_whitelist, 461 service_blacklist)) 462 { 463 start_new_introspect(system_bus, io, interface_map, 464 process_name, 465 #ifdef DEBUG 466 global_start_time, 467 #endif 468 objectServer); 469 update_owners(system_bus, name_owners, process_name); 470 } 471 } 472 }, 473 "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", 474 "ListNames"); 475 } 476 477 void splitArgs(const std::string& stringArgs, 478 boost::container::flat_set<std::string>& listArgs) 479 { 480 std::istringstream args; 481 std::string arg; 482 483 args.str(stringArgs); 484 485 while (!args.eof()) 486 { 487 args >> arg; 488 if (!arg.empty()) 489 { 490 listArgs.insert(arg); 491 } 492 } 493 } 494 495 void addObjectMapResult( 496 std::vector<interface_map_type::value_type>& objectMap, 497 const std::string& objectPath, 498 const std::pair<std::string, boost::container::flat_set<std::string>>& 499 interfaceMap) 500 { 501 // Adds an object path/service name/interface list entry to 502 // the results of GetSubTree and GetAncestors. 503 // If an entry for the object path already exists, just add the 504 // service name and interfaces to that entry, otherwise create 505 // a new entry. 506 auto entry = std::find_if( 507 objectMap.begin(), objectMap.end(), 508 [&objectPath](const auto& i) { return objectPath == i.first; }); 509 510 if (entry != objectMap.end()) 511 { 512 entry->second.emplace(interfaceMap); 513 } 514 else 515 { 516 interface_map_type::value_type object; 517 object.first = objectPath; 518 object.second.emplace(interfaceMap); 519 objectMap.push_back(object); 520 } 521 } 522 523 // Remove parents of the passed in path that: 524 // 1) Only have the 3 default interfaces on them 525 // - Means D-Bus created these, not application code, 526 // with the Properties, Introspectable, and Peer ifaces 527 // 2) Have no other child for this owner 528 void removeUnneededParents(const std::string& objectPath, 529 const std::string& owner, 530 interface_map_type& interface_map) 531 { 532 auto parent = objectPath; 533 534 while (true) 535 { 536 auto pos = parent.find_last_of('/'); 537 if ((pos == std::string::npos) || (pos == 0)) 538 { 539 break; 540 } 541 parent = parent.substr(0, pos); 542 543 auto parent_it = interface_map.find(parent); 544 if (parent_it == interface_map.end()) 545 { 546 break; 547 } 548 549 auto ifaces_it = parent_it->second.find(owner); 550 if (ifaces_it == parent_it->second.end()) 551 { 552 break; 553 } 554 555 if (ifaces_it->second.size() != 3) 556 { 557 break; 558 } 559 560 auto child_path = parent + '/'; 561 562 // Remove this parent if there isn't a remaining child on this owner 563 auto child = std::find_if( 564 interface_map.begin(), interface_map.end(), 565 [&owner, &child_path](const auto& entry) { 566 return boost::starts_with(entry.first, child_path) && 567 (entry.second.find(owner) != entry.second.end()); 568 }); 569 570 if (child == interface_map.end()) 571 { 572 parent_it->second.erase(ifaces_it); 573 if (parent_it->second.empty()) 574 { 575 interface_map.erase(parent_it); 576 } 577 } 578 else 579 { 580 break; 581 } 582 } 583 } 584 585 int main(int argc, char** argv) 586 { 587 auto options = ArgumentParser(argc, argv); 588 boost::asio::io_service io; 589 std::shared_ptr<sdbusplus::asio::connection> system_bus = 590 std::make_shared<sdbusplus::asio::connection>(io); 591 592 splitArgs(options["service-namespaces"], service_whitelist); 593 splitArgs(options["service-blacklists"], service_blacklist); 594 595 // TODO(Ed) Remove this once all service files are updated to not use this. 596 // For now, simply squash the input, and ignore it. 597 boost::container::flat_set<std::string> iface_whitelist; 598 splitArgs(options["interface-namespaces"], iface_whitelist); 599 600 system_bus->request_name(OBJECT_MAPPER_DBUS_NAME); 601 sdbusplus::asio::object_server server(system_bus); 602 603 // Construct a signal set registered for process termination. 604 boost::asio::signal_set signals(io, SIGINT, SIGTERM); 605 signals.async_wait([&io](const boost::system::error_code& error, 606 int signal_number) { io.stop(); }); 607 608 interface_map_type interface_map; 609 boost::container::flat_map<std::string, std::string> name_owners; 610 611 std::function<void(sdbusplus::message::message & message)> 612 nameChangeHandler = [&interface_map, &io, &name_owners, &server, 613 system_bus](sdbusplus::message::message& message) { 614 std::string name; // well-known 615 std::string old_owner; // unique-name 616 std::string new_owner; // unique-name 617 618 message.read(name, old_owner, new_owner); 619 620 if (!old_owner.empty()) 621 { 622 processNameChangeDelete(name_owners, name, old_owner, 623 interface_map, associationOwners, 624 associationInterfaces, server); 625 } 626 627 if (!new_owner.empty()) 628 { 629 #ifdef DEBUG 630 auto transaction = std::make_shared< 631 std::chrono::time_point<std::chrono::steady_clock>>( 632 std::chrono::steady_clock::now()); 633 #endif 634 // New daemon added 635 if (needToIntrospect(name, service_whitelist, 636 service_blacklist)) 637 { 638 name_owners[new_owner] = name; 639 start_new_introspect(system_bus.get(), io, interface_map, 640 name, 641 #ifdef DEBUG 642 transaction, 643 #endif 644 server); 645 } 646 } 647 }; 648 649 sdbusplus::bus::match::match nameOwnerChanged( 650 static_cast<sdbusplus::bus::bus&>(*system_bus), 651 sdbusplus::bus::match::rules::nameOwnerChanged(), nameChangeHandler); 652 653 std::function<void(sdbusplus::message::message & message)> 654 interfacesAddedHandler = [&interface_map, &name_owners, &server]( 655 sdbusplus::message::message& message) { 656 sdbusplus::message::object_path obj_path; 657 std::vector<std::pair< 658 std::string, std::vector<std::pair< 659 std::string, sdbusplus::message::variant< 660 std::vector<Association>>>>>> 661 interfaces_added; 662 message.read(obj_path, interfaces_added); 663 std::string well_known; 664 if (!getWellKnown(name_owners, message.get_sender(), well_known)) 665 { 666 return; // only introspect well-known 667 } 668 if (needToIntrospect(well_known, service_whitelist, 669 service_blacklist)) 670 { 671 auto& iface_list = interface_map[obj_path.str]; 672 673 for (const auto& interface_pair : interfaces_added) 674 { 675 iface_list[well_known].emplace(interface_pair.first); 676 677 if (interface_pair.first == ASSOCIATIONS_INTERFACE) 678 { 679 const sdbusplus::message::variant< 680 std::vector<Association>>* variantAssociations = 681 nullptr; 682 for (const auto& interface : interface_pair.second) 683 { 684 if (interface.first == "associations") 685 { 686 variantAssociations = &(interface.second); 687 } 688 } 689 if (variantAssociations == nullptr) 690 { 691 std::cerr << "Illegal association found on " 692 << well_known << "\n"; 693 continue; 694 } 695 std::vector<Association> associations = 696 sdbusplus::message::variant_ns::get< 697 std::vector<Association>>(*variantAssociations); 698 associationChanged(server, associations, obj_path.str, 699 well_known); 700 } 701 } 702 703 // To handle the case where an object path is being created 704 // with 2 or more new path segments, check if the parent paths 705 // of this path are already in the interface map, and add them 706 // if they aren't with just the default freedesktop interfaces. 707 // This would be done via introspection if they would have 708 // already existed at startup. While we could also introspect 709 // them now to do the work, we know there aren't any other 710 // interfaces or we would have gotten signals for them as well, 711 // so take a shortcut to speed things up. 712 // 713 // This is all needed so that mapper operations can be done 714 // on the new parent paths. 715 using iface_map_iterator = interface_map_type::iterator; 716 using iface_map_value_type = boost::container::flat_map< 717 std::string, boost::container::flat_set<std::string>>; 718 using name_map_iterator = iface_map_value_type::iterator; 719 720 static const boost::container::flat_set<std::string> 721 default_ifaces{"org.freedesktop.DBus.Introspectable", 722 "org.freedesktop.DBus.Peer", 723 "org.freedesktop.DBus.Properties"}; 724 725 std::string parent = obj_path.str; 726 auto pos = parent.find_last_of('/'); 727 728 while (pos != std::string::npos) 729 { 730 parent = parent.substr(0, pos); 731 732 std::pair<iface_map_iterator, bool> parentEntry = 733 interface_map.insert( 734 std::make_pair(parent, iface_map_value_type{})); 735 736 std::pair<name_map_iterator, bool> ifaceEntry = 737 parentEntry.first->second.insert( 738 std::make_pair(well_known, default_ifaces)); 739 740 if (!ifaceEntry.second) 741 { 742 // Entry was already there for this name so done. 743 break; 744 } 745 746 pos = parent.find_last_of('/'); 747 } 748 } 749 }; 750 751 sdbusplus::bus::match::match interfacesAdded( 752 static_cast<sdbusplus::bus::bus&>(*system_bus), 753 sdbusplus::bus::match::rules::interfacesAdded(), 754 interfacesAddedHandler); 755 756 std::function<void(sdbusplus::message::message & message)> 757 interfacesRemovedHandler = [&interface_map, &name_owners, &server]( 758 sdbusplus::message::message& message) { 759 sdbusplus::message::object_path obj_path; 760 std::vector<std::string> interfaces_removed; 761 message.read(obj_path, interfaces_removed); 762 auto connection_map = interface_map.find(obj_path.str); 763 if (connection_map == interface_map.end()) 764 { 765 return; 766 } 767 768 std::string sender; 769 if (!getWellKnown(name_owners, message.get_sender(), sender)) 770 { 771 return; 772 } 773 for (const std::string& interface : interfaces_removed) 774 { 775 auto interface_set = connection_map->second.find(sender); 776 if (interface_set == connection_map->second.end()) 777 { 778 continue; 779 } 780 781 if (interface == ASSOCIATIONS_INTERFACE) 782 { 783 removeAssociation(obj_path.str, sender, server, 784 associationOwners, associationInterfaces); 785 } 786 787 interface_set->second.erase(interface); 788 // If this was the last interface on this connection, 789 // erase the connection 790 if (interface_set->second.empty()) 791 { 792 connection_map->second.erase(interface_set); 793 } 794 } 795 // If this was the last connection on this object path, 796 // erase the object path 797 if (connection_map->second.empty()) 798 { 799 interface_map.erase(connection_map); 800 } 801 802 removeUnneededParents(obj_path.str, sender, interface_map); 803 }; 804 805 sdbusplus::bus::match::match interfacesRemoved( 806 static_cast<sdbusplus::bus::bus&>(*system_bus), 807 sdbusplus::bus::match::rules::interfacesRemoved(), 808 interfacesRemovedHandler); 809 810 std::function<void(sdbusplus::message::message & message)> 811 associationChangedHandler = 812 [&server, &name_owners](sdbusplus::message::message& message) { 813 std::string objectName; 814 boost::container::flat_map< 815 std::string, 816 sdbusplus::message::variant<std::vector<Association>>> 817 values; 818 message.read(objectName, values); 819 auto findAssociations = values.find("associations"); 820 if (findAssociations != values.end()) 821 { 822 std::vector<Association> associations = 823 sdbusplus::message::variant_ns::get< 824 std::vector<Association>>(findAssociations->second); 825 826 std::string well_known; 827 if (!getWellKnown(name_owners, message.get_sender(), 828 well_known)) 829 { 830 return; 831 } 832 associationChanged(server, associations, message.get_path(), 833 well_known); 834 } 835 }; 836 sdbusplus::bus::match::match associationChanged( 837 static_cast<sdbusplus::bus::bus&>(*system_bus), 838 sdbusplus::bus::match::rules::interface( 839 "org.freedesktop.DBus.Properties") + 840 sdbusplus::bus::match::rules::member("PropertiesChanged") + 841 sdbusplus::bus::match::rules::argN(0, ASSOCIATIONS_INTERFACE), 842 associationChangedHandler); 843 844 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = 845 server.add_interface("/xyz/openbmc_project/object_mapper", 846 "xyz.openbmc_project.ObjectMapper"); 847 848 iface->register_method( 849 "GetAncestors", [&interface_map](std::string& req_path, 850 std::vector<std::string>& interfaces) { 851 // Interfaces need to be sorted for intersect to function 852 std::sort(interfaces.begin(), interfaces.end()); 853 854 if (boost::ends_with(req_path, "/")) 855 { 856 req_path.pop_back(); 857 } 858 if (req_path.size() && 859 interface_map.find(req_path) == interface_map.end()) 860 { 861 throw NotFoundException(); 862 } 863 864 std::vector<interface_map_type::value_type> ret; 865 for (auto& object_path : interface_map) 866 { 867 auto& this_path = object_path.first; 868 if (boost::starts_with(req_path, this_path) && 869 (req_path != this_path)) 870 { 871 if (interfaces.empty()) 872 { 873 ret.emplace_back(object_path); 874 } 875 else 876 { 877 for (auto& interface_map : object_path.second) 878 { 879 880 if (intersect(interfaces.begin(), interfaces.end(), 881 interface_map.second.begin(), 882 interface_map.second.end())) 883 { 884 addObjectMapResult(ret, this_path, 885 interface_map); 886 } 887 } 888 } 889 } 890 } 891 892 return ret; 893 }); 894 895 iface->register_method( 896 "GetObject", [&interface_map](const std::string& path, 897 std::vector<std::string>& interfaces) { 898 boost::container::flat_map<std::string, 899 boost::container::flat_set<std::string>> 900 results; 901 902 // Interfaces need to be sorted for intersect to function 903 std::sort(interfaces.begin(), interfaces.end()); 904 auto path_ref = interface_map.find(path); 905 if (path_ref == interface_map.end()) 906 { 907 throw NotFoundException(); 908 } 909 if (interfaces.empty()) 910 { 911 return path_ref->second; 912 } 913 for (auto& interface_map : path_ref->second) 914 { 915 if (intersect(interfaces.begin(), interfaces.end(), 916 interface_map.second.begin(), 917 interface_map.second.end())) 918 { 919 results.emplace(interface_map.first, interface_map.second); 920 } 921 } 922 923 if (results.empty()) 924 { 925 throw NotFoundException(); 926 } 927 928 return results; 929 }); 930 931 iface->register_method( 932 "GetSubTree", [&interface_map](std::string& req_path, int32_t depth, 933 std::vector<std::string>& interfaces) { 934 if (depth <= 0) 935 { 936 depth = std::numeric_limits<int32_t>::max(); 937 } 938 // Interfaces need to be sorted for intersect to function 939 std::sort(interfaces.begin(), interfaces.end()); 940 std::vector<interface_map_type::value_type> ret; 941 942 if (boost::ends_with(req_path, "/")) 943 { 944 req_path.pop_back(); 945 } 946 if (req_path.size() && 947 interface_map.find(req_path) == interface_map.end()) 948 { 949 throw NotFoundException(); 950 } 951 952 for (auto& object_path : interface_map) 953 { 954 auto& this_path = object_path.first; 955 956 if (this_path == req_path) 957 { 958 continue; 959 } 960 961 if (boost::starts_with(this_path, req_path)) 962 { 963 // count the number of slashes past the search term 964 int32_t this_depth = 965 std::count(this_path.begin() + req_path.size(), 966 this_path.end(), '/'); 967 if (this_depth <= depth) 968 { 969 for (auto& interface_map : object_path.second) 970 { 971 if (intersect(interfaces.begin(), interfaces.end(), 972 interface_map.second.begin(), 973 interface_map.second.end()) || 974 interfaces.empty()) 975 { 976 addObjectMapResult(ret, this_path, 977 interface_map); 978 } 979 } 980 } 981 } 982 } 983 984 return ret; 985 }); 986 987 iface->register_method( 988 "GetSubTreePaths", 989 [&interface_map](std::string& req_path, int32_t depth, 990 std::vector<std::string>& interfaces) { 991 if (depth <= 0) 992 { 993 depth = std::numeric_limits<int32_t>::max(); 994 } 995 // Interfaces need to be sorted for intersect to function 996 std::sort(interfaces.begin(), interfaces.end()); 997 std::vector<std::string> ret; 998 999 if (boost::ends_with(req_path, "/")) 1000 { 1001 req_path.pop_back(); 1002 } 1003 if (req_path.size() && 1004 interface_map.find(req_path) == interface_map.end()) 1005 { 1006 throw NotFoundException(); 1007 } 1008 1009 for (auto& object_path : interface_map) 1010 { 1011 auto& this_path = object_path.first; 1012 1013 if (this_path == req_path) 1014 { 1015 continue; 1016 } 1017 1018 if (boost::starts_with(this_path, req_path)) 1019 { 1020 // count the number of slashes past the search term 1021 int this_depth = 1022 std::count(this_path.begin() + req_path.size(), 1023 this_path.end(), '/'); 1024 if (this_depth <= depth) 1025 { 1026 bool add = interfaces.empty(); 1027 for (auto& interface_map : object_path.second) 1028 { 1029 if (intersect(interfaces.begin(), interfaces.end(), 1030 interface_map.second.begin(), 1031 interface_map.second.end())) 1032 { 1033 add = true; 1034 break; 1035 } 1036 } 1037 if (add) 1038 { 1039 // TODO(ed) this is a copy 1040 ret.emplace_back(this_path); 1041 } 1042 } 1043 } 1044 } 1045 1046 return ret; 1047 }); 1048 1049 iface->initialize(); 1050 1051 io.post([&]() { 1052 doListNames(io, interface_map, system_bus.get(), name_owners, server); 1053 }); 1054 1055 io.run(); 1056 } 1057