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