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