xref: /openbmc/phosphor-objmgr/src/main.cpp (revision dd945861)
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 int main(int argc, char** argv)
486 {
487     std::cerr << "started\n";
488     auto options = ArgumentParser(argc, argv);
489     boost::asio::io_service io;
490     std::shared_ptr<sdbusplus::asio::connection> system_bus =
491         std::make_shared<sdbusplus::asio::connection>(io);
492 
493     splitArgs(options["service-namespaces"], service_whitelist);
494     splitArgs(options["interface-namespaces"], iface_whitelist);
495     splitArgs(options["service-blacklists"], service_blacklist);
496 
497     system_bus->request_name(OBJECT_MAPPER_DBUS_NAME);
498     sdbusplus::asio::object_server server(system_bus);
499 
500     // Construct a signal set registered for process termination.
501     boost::asio::signal_set signals(io, SIGINT, SIGTERM);
502     signals.async_wait([&io](const boost::system::error_code& error,
503                              int signal_number) { io.stop(); });
504 
505     interface_map_type interface_map;
506     boost::container::flat_map<std::string, std::string> name_owners;
507 
508     std::function<void(sdbusplus::message::message & message)>
509         nameChangeHandler = [&interface_map, &io, &name_owners, &server,
510                              system_bus](sdbusplus::message::message& message) {
511             std::string name;
512             std::string old_owner;
513             std::string new_owner;
514 
515             message.read(name, old_owner, new_owner);
516 
517             if (!old_owner.empty())
518             {
519                 if (boost::starts_with(old_owner, ":"))
520                 {
521                     auto it = name_owners.find(old_owner);
522                     if (it != name_owners.end())
523                     {
524                         name_owners.erase(it);
525                     }
526                 }
527                 // Connection removed
528                 interface_map_type::iterator path_it = interface_map.begin();
529                 while (path_it != interface_map.end())
530                 {
531                     path_it->second.erase(name);
532                     if (path_it->second.empty())
533                     {
534                         // If the last connection to the object is gone,
535                         // delete the top level object
536                         path_it = interface_map.erase(path_it);
537                         continue;
538                     }
539                     path_it++;
540                 }
541             }
542 
543             if (!new_owner.empty())
544             {
545                 auto transaction = std::make_shared<
546                     std::chrono::time_point<std::chrono::steady_clock>>(
547                     std::chrono::steady_clock::now());
548                 // New daemon added
549                 if (need_to_introspect(name))
550                 {
551                     name_owners[new_owner] = name;
552                     start_new_introspect(system_bus.get(), io, interface_map,
553                                          name, transaction, server);
554                 }
555             }
556         };
557 
558     sdbusplus::bus::match::match nameOwnerChanged(
559         static_cast<sdbusplus::bus::bus&>(*system_bus),
560         sdbusplus::bus::match::rules::nameOwnerChanged(), nameChangeHandler);
561 
562     std::function<void(sdbusplus::message::message & message)>
563         interfacesAddedHandler = [&interface_map, &name_owners, &server](
564                                      sdbusplus::message::message& message) {
565             sdbusplus::message::object_path obj_path;
566             std::vector<std::pair<
567                 std::string, std::vector<std::pair<
568                                  std::string, sdbusplus::message::variant<
569                                                   std::vector<Association>>>>>>
570                 interfaces_added;
571             message.read(obj_path, interfaces_added);
572             std::string well_known;
573             if (!get_well_known(name_owners, message.get_sender(), well_known))
574             {
575                 return; // only introspect well-known
576             }
577             if (need_to_introspect(well_known))
578             {
579                 auto& iface_list = interface_map[obj_path.str];
580 
581                 for (const std::pair<
582                          std::string,
583                          std::vector<std::pair<std::string,
584                                                sdbusplus::message::variant<
585                                                    std::vector<Association>>>>>&
586                          interface_pair : interfaces_added)
587                 {
588                     iface_list[well_known].emplace(interface_pair.first);
589 
590                     if (interface_pair.first == ASSOCIATIONS_INTERFACE)
591                     {
592                         const sdbusplus::message::variant<
593                             std::vector<Association>>* variantAssociations =
594                             nullptr;
595                         for (const auto& interface : interface_pair.second)
596                         {
597                             if (interface.first == "associations")
598                             {
599                                 variantAssociations = &(interface.second);
600                             }
601                         }
602                         if (variantAssociations == nullptr)
603                         {
604                             std::cerr << "Illegal association found on "
605                                       << well_known << "\n";
606                             continue;
607                         }
608                         std::vector<Association> associations =
609                             sdbusplus::message::variant_ns::get<
610                                 std::vector<Association>>(*variantAssociations);
611                         addAssociation(server, associations, obj_path.str);
612                     }
613                 }
614             }
615         };
616 
617     sdbusplus::bus::match::match interfacesAdded(
618         static_cast<sdbusplus::bus::bus&>(*system_bus),
619         sdbusplus::bus::match::rules::interfacesAdded(),
620         interfacesAddedHandler);
621 
622     std::function<void(sdbusplus::message::message & message)>
623         interfacesRemovedHandler = [&interface_map, &name_owners, &server](
624                                        sdbusplus::message::message& message) {
625             sdbusplus::message::object_path obj_path;
626             std::vector<std::string> interfaces_removed;
627             message.read(obj_path, interfaces_removed);
628             auto connection_map = interface_map.find(obj_path.str);
629             if (connection_map == interface_map.end())
630             {
631                 return;
632             }
633 
634             std::string sender;
635             if (!get_well_known(name_owners, message.get_sender(), sender))
636             {
637                 return;
638             }
639             for (const std::string& interface : interfaces_removed)
640             {
641                 auto interface_set = connection_map->second.find(sender);
642                 if (interface_set == connection_map->second.end())
643                 {
644                     continue;
645                 }
646 
647                 if (interface == ASSOCIATIONS_INTERFACE)
648                 {
649                     auto findAssociation =
650                         associationInterfaces.find(interface);
651                     if (findAssociation != associationInterfaces.end())
652                     {
653                         server.remove_interface(findAssociation->second);
654                         findAssociation->second = nullptr;
655                     }
656                 }
657 
658                 interface_set->second.erase(interface);
659                 // If this was the last interface on this connection,
660                 // erase the connection
661                 if (interface_set->second.empty())
662                 {
663                     connection_map->second.erase(interface_set);
664                 }
665             }
666             // If this was the last connection on this object path,
667             // erase the object path
668             if (connection_map->second.empty())
669             {
670                 interface_map.erase(connection_map);
671             }
672         };
673 
674     sdbusplus::bus::match::match interfacesRemoved(
675         static_cast<sdbusplus::bus::bus&>(*system_bus),
676         sdbusplus::bus::match::rules::interfacesRemoved(),
677         interfacesRemovedHandler);
678 
679     std::function<void(sdbusplus::message::message & message)>
680         associationChangedHandler =
681             [&server](sdbusplus::message::message& message) {
682                 std::string objectName;
683                 boost::container::flat_map<
684                     std::string,
685                     sdbusplus::message::variant<std::vector<Association>>>
686                     values;
687                 message.read(objectName, values);
688                 auto findAssociations = values.find("associations");
689                 if (findAssociations != values.end())
690                 {
691                     std::vector<Association> associations =
692                         sdbusplus::message::variant_ns::get<
693                             std::vector<Association>>(findAssociations->second);
694                     addAssociation(server, associations, message.get_path());
695                 }
696             };
697     sdbusplus::bus::match::match associationChanged(
698         static_cast<sdbusplus::bus::bus&>(*system_bus),
699         sdbusplus::bus::match::rules::interface(
700             "org.freedesktop.DBus.Properties") +
701             sdbusplus::bus::match::rules::member("PropertiesChanged") +
702             sdbusplus::bus::match::rules::argN(0, ASSOCIATIONS_INTERFACE),
703         associationChangedHandler);
704 
705     std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
706         server.add_interface("/xyz/openbmc_project/object_mapper",
707                              "xyz.openbmc_project.ObjectMapper");
708 
709     iface->register_method(
710         "GetAncestors", [&interface_map](const std::string& req_path,
711                                          std::vector<std::string>& interfaces) {
712             // Interfaces need to be sorted for intersect to function
713             std::sort(interfaces.begin(), interfaces.end());
714 
715             std::vector<interface_map_type::value_type> ret;
716             for (auto& object_path : interface_map)
717             {
718                 auto& this_path = object_path.first;
719                 if (boost::starts_with(req_path, this_path))
720                 {
721                     if (interfaces.empty())
722                     {
723                         ret.emplace_back(object_path);
724                     }
725                     else
726                     {
727                         for (auto& interface_map : object_path.second)
728                         {
729 
730                             if (intersect(interfaces.begin(), interfaces.end(),
731                                           interface_map.second.begin(),
732                                           interface_map.second.end()))
733                             {
734                                 ret.emplace_back(object_path);
735                                 break;
736                             }
737                         }
738                     }
739                 }
740             }
741             if (ret.empty())
742             {
743                 throw NotFoundException();
744             }
745 
746             return ret;
747         });
748 
749     iface->register_method(
750         "GetObject", [&interface_map](const std::string& path,
751                                       std::vector<std::string>& interfaces) {
752             // Interfaces need to be sorted for intersect to function
753             std::sort(interfaces.begin(), interfaces.end());
754             auto path_ref = interface_map.find(path);
755             if (path_ref == interface_map.end())
756             {
757                 throw NotFoundException();
758             }
759             if (interfaces.empty())
760             {
761                 return path_ref->second;
762             }
763             for (auto& interface_map : path_ref->second)
764             {
765                 if (intersect(interfaces.begin(), interfaces.end(),
766                               interface_map.second.begin(),
767                               interface_map.second.end()))
768                 {
769                     return path_ref->second;
770                 }
771             }
772             // Unable to find intersection, return default constructed
773             // object
774             throw NotFoundException();
775         });
776 
777     iface->register_method(
778         "GetSubTree",
779         [&interface_map](const std::string& req_path, int32_t depth,
780                          std::vector<std::string>& interfaces) {
781             if (depth <= 0)
782             {
783                 depth = std::numeric_limits<int32_t>::max();
784             }
785             // Interfaces need to be sorted for intersect to function
786             std::sort(interfaces.begin(), interfaces.end());
787             std::vector<interface_map_type::value_type> ret;
788 
789             for (auto& object_path : interface_map)
790             {
791                 auto& this_path = object_path.first;
792                 if (boost::starts_with(this_path, req_path))
793                 {
794                     // count the number of slashes past the search term
795                     int32_t this_depth =
796                         std::count(this_path.begin() + req_path.size(),
797                                    this_path.end(), '/');
798                     if (this_depth <= depth)
799                     {
800                         bool add = interfaces.empty();
801                         for (auto& interface_map : object_path.second)
802                         {
803                             if (intersect(interfaces.begin(), interfaces.end(),
804                                           interface_map.second.begin(),
805                                           interface_map.second.end()))
806                             {
807                                 add = true;
808                                 break;
809                             }
810                         }
811                         if (add)
812                         {
813                             // todo(ed) this is a copy
814                             ret.emplace_back(object_path);
815                         }
816                     }
817                 }
818             }
819             if (ret.empty())
820             {
821                 throw NotFoundException();
822             }
823             return ret;
824         });
825 
826     iface->register_method(
827         "GetSubTreePaths",
828         [&interface_map](const std::string& req_path, int32_t depth,
829                          std::vector<std::string>& interfaces) {
830             if (depth <= 0)
831             {
832                 depth = std::numeric_limits<int32_t>::max();
833             }
834             // Interfaces need to be sorted for intersect to function
835             std::sort(interfaces.begin(), interfaces.end());
836             std::vector<std::string> ret;
837             for (auto& object_path : interface_map)
838             {
839                 auto& this_path = object_path.first;
840                 if (boost::starts_with(this_path, req_path))
841                 {
842                     // count the number of slashes past the search term
843                     int this_depth =
844                         std::count(this_path.begin() + req_path.size(),
845                                    this_path.end(), '/');
846                     if (this_depth <= depth)
847                     {
848                         bool add = interfaces.empty();
849                         for (auto& interface_map : object_path.second)
850                         {
851                             if (intersect(interfaces.begin(), interfaces.end(),
852                                           interface_map.second.begin(),
853                                           interface_map.second.end()))
854                             {
855                                 add = true;
856                                 break;
857                             }
858                         }
859                         if (add)
860                         {
861                             // TODO(ed) this is a copy
862                             ret.emplace_back(this_path);
863                         }
864                     }
865                 }
866             }
867             if (ret.empty())
868             {
869                 throw NotFoundException();
870             }
871             return ret;
872         });
873 
874     iface->initialize();
875 
876     io.post([&]() {
877         doListNames(io, interface_map, system_bus.get(), name_owners, server);
878     });
879 
880     std::cerr << "starting event loop\n";
881     io.run();
882 }
883