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