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