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