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