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