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