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