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