xref: /openbmc/phosphor-objmgr/src/main.cpp (revision ff5ce924)
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 // Based on the latest values of the org.openbmc.Associations.associations
134 // property, passed in via the newAssociations param, check if any of the
135 // paths in the xyz.openbmc_project.Association.endpoints D-Bus property
136 // for that association need to be removed.  If the last path is removed
137 // from the endpoints property, remove that whole association object from
138 // D-Bus.
139 void checkAssociationEndpointRemoves(
140     const std::string& sourcePath, const std::string& owner,
141     const AssociationPaths& newAssociations,
142     sdbusplus::asio::object_server& objectServer)
143 {
144     // Find the services that have associations on this path.
145     auto originalOwners = associationOwners.find(sourcePath);
146     if (originalOwners == associationOwners.end())
147     {
148         return;
149     }
150 
151     // Find the associations for this service
152     auto originalAssociations = originalOwners->second.find(owner);
153     if (originalAssociations == originalOwners->second.end())
154     {
155         return;
156     }
157 
158     // Compare the new endpoints versus the original endpoints, and
159     // remove any of the original ones that aren't in the new list.
160     for (const auto& [originalAssocPath, originalEndpoints] :
161          originalAssociations->second)
162     {
163         // Check if this source even still has each association that
164         // was there previously, and if not, remove all of its endpoints
165         // from the D-Bus endpoints property which will cause the whole
166         // association path to be removed if no endpoints remain.
167         auto newEndpoints = newAssociations.find(originalAssocPath);
168         if (newEndpoints == newAssociations.end())
169         {
170             removeAssociationEndpoints(objectServer, originalAssocPath,
171                                        originalEndpoints,
172                                        associationInterfaces);
173         }
174         else
175         {
176             // The association is still there.  Check if the endpoints
177             // changed.
178             boost::container::flat_set<std::string> toRemove;
179 
180             for (auto& originalEndpoint : originalEndpoints)
181             {
182                 if (std::find(newEndpoints->second.begin(),
183                               newEndpoints->second.end(),
184                               originalEndpoint) == newEndpoints->second.end())
185                 {
186                     toRemove.emplace(originalEndpoint);
187                 }
188             }
189             if (!toRemove.empty())
190             {
191                 removeAssociationEndpoints(objectServer, originalAssocPath,
192                                            toRemove, associationInterfaces);
193             }
194         }
195     }
196 }
197 
198 // Called when either a new org.openbmc.Associations interface was
199 // created, or the associations property on that interface changed.
200 void associationChanged(sdbusplus::asio::object_server& objectServer,
201                         const std::vector<Association>& associations,
202                         const std::string& path, const std::string& owner)
203 {
204     AssociationPaths objects;
205 
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         auto& i = std::get<ifacePos>(iface);
235         auto& endpoints = std::get<endpointsPos>(iface);
236 
237         // Only add new endpoints
238         for (auto& e : object.second)
239         {
240             if (std::find(endpoints.begin(), endpoints.end(), e) ==
241                 endpoints.end())
242             {
243                 endpoints.push_back(e);
244             }
245         }
246 
247         // If the interface already exists, only need to update
248         // the property value, otherwise create it
249         if (i)
250         {
251             i->set_property("endpoints", endpoints);
252         }
253         else
254         {
255             i = objectServer.add_interface(object.first,
256                                            XYZ_ASSOCIATION_INTERFACE);
257             i->register_property("endpoints", endpoints);
258             i->initialize();
259         }
260     }
261 
262     // Check for endpoints being removed instead of added
263     checkAssociationEndpointRemoves(path, owner, objects, objectServer);
264 
265     // Update associationOwners with the latest info
266     auto a = associationOwners.find(path);
267     if (a != associationOwners.end())
268     {
269         auto o = a->second.find(owner);
270         if (o != a->second.end())
271         {
272             o->second = std::move(objects);
273         }
274         else
275         {
276             a->second.emplace(owner, std::move(objects));
277         }
278     }
279     else
280     {
281         boost::container::flat_map<std::string, AssociationPaths> owners;
282         owners.emplace(owner, std::move(objects));
283         associationOwners.emplace(path, owners);
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, path, processName](
293             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             associationChanged(objectServer, associations, path, processName);
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                 thisPathMap[transaction->process_name].emplace(iface_name);
357 
358                 if (std::strcmp(iface_name, ASSOCIATIONS_INTERFACE) == 0)
359                 {
360                     do_associations(system_bus, objectServer,
361                                     transaction->process_name, path);
362                 }
363 
364                 pElement = pElement->NextSiblingElement("interface");
365             }
366 
367             pElement = pRoot->FirstChildElement("node");
368             while (pElement != nullptr)
369             {
370                 const char* child_path = pElement->Attribute("name");
371                 if (child_path != nullptr)
372                 {
373                     std::string parent_path(path);
374                     if (parent_path == "/")
375                     {
376                         parent_path.clear();
377                     }
378 
379                     do_introspect(system_bus, transaction, interface_map,
380                                   objectServer, parent_path + "/" + child_path);
381                 }
382                 pElement = pElement->NextSiblingElement("node");
383             }
384         },
385         transaction->process_name, path, "org.freedesktop.DBus.Introspectable",
386         "Introspect");
387 }
388 
389 void start_new_introspect(
390     sdbusplus::asio::connection* system_bus, boost::asio::io_service& io,
391     interface_map_type& interface_map, const std::string& process_name,
392 #ifdef DEBUG
393     std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
394         global_start_time,
395 #endif
396     sdbusplus::asio::object_server& objectServer)
397 {
398     if (needToIntrospect(process_name, service_whitelist, service_blacklist))
399     {
400         std::shared_ptr<InProgressIntrospect> transaction =
401             std::make_shared<InProgressIntrospect>(system_bus, io, process_name
402 #ifdef DEBUG
403                                                    ,
404                                                    global_start_time
405 #endif
406             );
407 
408         do_introspect(system_bus, transaction, interface_map, objectServer,
409                       "/");
410     }
411 }
412 
413 // TODO(ed) replace with std::set_intersection once c++17 is available
414 template <class InputIt1, class InputIt2>
415 bool intersect(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
416 {
417     while (first1 != last1 && first2 != last2)
418     {
419         if (*first1 < *first2)
420         {
421             ++first1;
422             continue;
423         }
424         if (*first2 < *first1)
425         {
426             ++first2;
427             continue;
428         }
429         return true;
430     }
431     return false;
432 }
433 
434 void doListNames(
435     boost::asio::io_service& io, interface_map_type& interface_map,
436     sdbusplus::asio::connection* system_bus,
437     boost::container::flat_map<std::string, std::string>& name_owners,
438     sdbusplus::asio::object_server& objectServer)
439 {
440     system_bus->async_method_call(
441         [&io, &interface_map, &name_owners, &objectServer,
442          system_bus](const boost::system::error_code ec,
443                      std::vector<std::string> process_names) {
444             if (ec)
445             {
446                 std::cerr << "Error getting names: " << ec << "\n";
447                 std::exit(EXIT_FAILURE);
448                 return;
449             }
450             // Try to make startup consistent
451             std::sort(process_names.begin(), process_names.end());
452 #ifdef DEBUG
453             std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
454                 global_start_time = std::make_shared<
455                     std::chrono::time_point<std::chrono::steady_clock>>(
456                     std::chrono::steady_clock::now());
457 #endif
458             for (const std::string& process_name : process_names)
459             {
460                 if (needToIntrospect(process_name, service_whitelist,
461                                      service_blacklist))
462                 {
463                     start_new_introspect(system_bus, io, interface_map,
464                                          process_name,
465 #ifdef DEBUG
466                                          global_start_time,
467 #endif
468                                          objectServer);
469                     update_owners(system_bus, name_owners, process_name);
470                 }
471             }
472         },
473         "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
474         "ListNames");
475 }
476 
477 void splitArgs(const std::string& stringArgs,
478                boost::container::flat_set<std::string>& listArgs)
479 {
480     std::istringstream args;
481     std::string arg;
482 
483     args.str(stringArgs);
484 
485     while (!args.eof())
486     {
487         args >> arg;
488         if (!arg.empty())
489         {
490             listArgs.insert(arg);
491         }
492     }
493 }
494 
495 void addObjectMapResult(
496     std::vector<interface_map_type::value_type>& objectMap,
497     const std::string& objectPath,
498     const std::pair<std::string, boost::container::flat_set<std::string>>&
499         interfaceMap)
500 {
501     // Adds an object path/service name/interface list entry to
502     // the results of GetSubTree and GetAncestors.
503     // If an entry for the object path already exists, just add the
504     // service name and interfaces to that entry, otherwise create
505     // a new entry.
506     auto entry = std::find_if(
507         objectMap.begin(), objectMap.end(),
508         [&objectPath](const auto& i) { return objectPath == i.first; });
509 
510     if (entry != objectMap.end())
511     {
512         entry->second.emplace(interfaceMap);
513     }
514     else
515     {
516         interface_map_type::value_type object;
517         object.first = objectPath;
518         object.second.emplace(interfaceMap);
519         objectMap.push_back(object);
520     }
521 }
522 
523 // Remove parents of the passed in path that:
524 // 1) Only have the 3 default interfaces on them
525 //    - Means D-Bus created these, not application code,
526 //      with the Properties, Introspectable, and Peer ifaces
527 // 2) Have no other child for this owner
528 void removeUnneededParents(const std::string& objectPath,
529                            const std::string& owner,
530                            interface_map_type& interface_map)
531 {
532     auto parent = objectPath;
533 
534     while (true)
535     {
536         auto pos = parent.find_last_of('/');
537         if ((pos == std::string::npos) || (pos == 0))
538         {
539             break;
540         }
541         parent = parent.substr(0, pos);
542 
543         auto parent_it = interface_map.find(parent);
544         if (parent_it == interface_map.end())
545         {
546             break;
547         }
548 
549         auto ifaces_it = parent_it->second.find(owner);
550         if (ifaces_it == parent_it->second.end())
551         {
552             break;
553         }
554 
555         if (ifaces_it->second.size() != 3)
556         {
557             break;
558         }
559 
560         auto child_path = parent + '/';
561 
562         // Remove this parent if there isn't a remaining child on this owner
563         auto child = std::find_if(
564             interface_map.begin(), interface_map.end(),
565             [&owner, &child_path](const auto& entry) {
566                 return boost::starts_with(entry.first, child_path) &&
567                        (entry.second.find(owner) != entry.second.end());
568             });
569 
570         if (child == interface_map.end())
571         {
572             parent_it->second.erase(ifaces_it);
573             if (parent_it->second.empty())
574             {
575                 interface_map.erase(parent_it);
576             }
577         }
578         else
579         {
580             break;
581         }
582     }
583 }
584 
585 int main(int argc, char** argv)
586 {
587     auto options = ArgumentParser(argc, argv);
588     boost::asio::io_service io;
589     std::shared_ptr<sdbusplus::asio::connection> system_bus =
590         std::make_shared<sdbusplus::asio::connection>(io);
591 
592     splitArgs(options["service-namespaces"], service_whitelist);
593     splitArgs(options["service-blacklists"], service_blacklist);
594 
595     // TODO(Ed) Remove this once all service files are updated to not use this.
596     // For now, simply squash the input, and ignore it.
597     boost::container::flat_set<std::string> iface_whitelist;
598     splitArgs(options["interface-namespaces"], iface_whitelist);
599 
600     system_bus->request_name(OBJECT_MAPPER_DBUS_NAME);
601     sdbusplus::asio::object_server server(system_bus);
602 
603     // Construct a signal set registered for process termination.
604     boost::asio::signal_set signals(io, SIGINT, SIGTERM);
605     signals.async_wait([&io](const boost::system::error_code& error,
606                              int signal_number) { io.stop(); });
607 
608     interface_map_type interface_map;
609     boost::container::flat_map<std::string, std::string> name_owners;
610 
611     std::function<void(sdbusplus::message::message & message)>
612         nameChangeHandler = [&interface_map, &io, &name_owners, &server,
613                              system_bus](sdbusplus::message::message& message) {
614             std::string name;      // well-known
615             std::string old_owner; // unique-name
616             std::string new_owner; // unique-name
617 
618             message.read(name, old_owner, new_owner);
619 
620             if (!old_owner.empty())
621             {
622                 processNameChangeDelete(name_owners, name, old_owner,
623                                         interface_map, associationOwners,
624                                         associationInterfaces, server);
625             }
626 
627             if (!new_owner.empty())
628             {
629 #ifdef DEBUG
630                 auto transaction = std::make_shared<
631                     std::chrono::time_point<std::chrono::steady_clock>>(
632                     std::chrono::steady_clock::now());
633 #endif
634                 // New daemon added
635                 if (needToIntrospect(name, service_whitelist,
636                                      service_blacklist))
637                 {
638                     name_owners[new_owner] = name;
639                     start_new_introspect(system_bus.get(), io, interface_map,
640                                          name,
641 #ifdef DEBUG
642                                          transaction,
643 #endif
644                                          server);
645                 }
646             }
647         };
648 
649     sdbusplus::bus::match::match nameOwnerChanged(
650         static_cast<sdbusplus::bus::bus&>(*system_bus),
651         sdbusplus::bus::match::rules::nameOwnerChanged(), nameChangeHandler);
652 
653     std::function<void(sdbusplus::message::message & message)>
654         interfacesAddedHandler = [&interface_map, &name_owners, &server](
655                                      sdbusplus::message::message& message) {
656             sdbusplus::message::object_path obj_path;
657             std::vector<std::pair<
658                 std::string, std::vector<std::pair<
659                                  std::string, sdbusplus::message::variant<
660                                                   std::vector<Association>>>>>>
661                 interfaces_added;
662             message.read(obj_path, interfaces_added);
663             std::string well_known;
664             if (!getWellKnown(name_owners, message.get_sender(), well_known))
665             {
666                 return; // only introspect well-known
667             }
668             if (needToIntrospect(well_known, service_whitelist,
669                                  service_blacklist))
670             {
671                 auto& iface_list = interface_map[obj_path.str];
672 
673                 for (const auto& interface_pair : interfaces_added)
674                 {
675                     iface_list[well_known].emplace(interface_pair.first);
676 
677                     if (interface_pair.first == ASSOCIATIONS_INTERFACE)
678                     {
679                         const sdbusplus::message::variant<
680                             std::vector<Association>>* variantAssociations =
681                             nullptr;
682                         for (const auto& interface : interface_pair.second)
683                         {
684                             if (interface.first == "associations")
685                             {
686                                 variantAssociations = &(interface.second);
687                             }
688                         }
689                         if (variantAssociations == nullptr)
690                         {
691                             std::cerr << "Illegal association found on "
692                                       << well_known << "\n";
693                             continue;
694                         }
695                         std::vector<Association> associations =
696                             sdbusplus::message::variant_ns::get<
697                                 std::vector<Association>>(*variantAssociations);
698                         associationChanged(server, associations, obj_path.str,
699                                            well_known);
700                     }
701                 }
702 
703                 // To handle the case where an object path is being created
704                 // with 2 or more new path segments, check if the parent paths
705                 // of this path are already in the interface map, and add them
706                 // if they aren't with just the default freedesktop interfaces.
707                 // This would be done via introspection if they would have
708                 // already existed at startup.  While we could also introspect
709                 // them now to do the work, we know there aren't any other
710                 // interfaces or we would have gotten signals for them as well,
711                 // so take a shortcut to speed things up.
712                 //
713                 // This is all needed so that mapper operations can be done
714                 // on the new parent paths.
715                 using iface_map_iterator = interface_map_type::iterator;
716                 using iface_map_value_type = boost::container::flat_map<
717                     std::string, boost::container::flat_set<std::string>>;
718                 using name_map_iterator = iface_map_value_type::iterator;
719 
720                 static const boost::container::flat_set<std::string>
721                     default_ifaces{"org.freedesktop.DBus.Introspectable",
722                                    "org.freedesktop.DBus.Peer",
723                                    "org.freedesktop.DBus.Properties"};
724 
725                 std::string parent = obj_path.str;
726                 auto pos = parent.find_last_of('/');
727 
728                 while (pos != std::string::npos)
729                 {
730                     parent = parent.substr(0, pos);
731 
732                     std::pair<iface_map_iterator, bool> parentEntry =
733                         interface_map.insert(
734                             std::make_pair(parent, iface_map_value_type{}));
735 
736                     std::pair<name_map_iterator, bool> ifaceEntry =
737                         parentEntry.first->second.insert(
738                             std::make_pair(well_known, default_ifaces));
739 
740                     if (!ifaceEntry.second)
741                     {
742                         // Entry was already there for this name so done.
743                         break;
744                     }
745 
746                     pos = parent.find_last_of('/');
747                 }
748             }
749         };
750 
751     sdbusplus::bus::match::match interfacesAdded(
752         static_cast<sdbusplus::bus::bus&>(*system_bus),
753         sdbusplus::bus::match::rules::interfacesAdded(),
754         interfacesAddedHandler);
755 
756     std::function<void(sdbusplus::message::message & message)>
757         interfacesRemovedHandler = [&interface_map, &name_owners, &server](
758                                        sdbusplus::message::message& message) {
759             sdbusplus::message::object_path obj_path;
760             std::vector<std::string> interfaces_removed;
761             message.read(obj_path, interfaces_removed);
762             auto connection_map = interface_map.find(obj_path.str);
763             if (connection_map == interface_map.end())
764             {
765                 return;
766             }
767 
768             std::string sender;
769             if (!getWellKnown(name_owners, message.get_sender(), sender))
770             {
771                 return;
772             }
773             for (const std::string& interface : interfaces_removed)
774             {
775                 auto interface_set = connection_map->second.find(sender);
776                 if (interface_set == connection_map->second.end())
777                 {
778                     continue;
779                 }
780 
781                 if (interface == ASSOCIATIONS_INTERFACE)
782                 {
783                     removeAssociation(obj_path.str, sender, server,
784                                       associationOwners, associationInterfaces);
785                 }
786 
787                 interface_set->second.erase(interface);
788                 // If this was the last interface on this connection,
789                 // erase the connection
790                 if (interface_set->second.empty())
791                 {
792                     connection_map->second.erase(interface_set);
793                 }
794             }
795             // If this was the last connection on this object path,
796             // erase the object path
797             if (connection_map->second.empty())
798             {
799                 interface_map.erase(connection_map);
800             }
801 
802             removeUnneededParents(obj_path.str, sender, interface_map);
803         };
804 
805     sdbusplus::bus::match::match interfacesRemoved(
806         static_cast<sdbusplus::bus::bus&>(*system_bus),
807         sdbusplus::bus::match::rules::interfacesRemoved(),
808         interfacesRemovedHandler);
809 
810     std::function<void(sdbusplus::message::message & message)>
811         associationChangedHandler =
812             [&server, &name_owners](sdbusplus::message::message& message) {
813                 std::string objectName;
814                 boost::container::flat_map<
815                     std::string,
816                     sdbusplus::message::variant<std::vector<Association>>>
817                     values;
818                 message.read(objectName, values);
819                 auto findAssociations = values.find("associations");
820                 if (findAssociations != values.end())
821                 {
822                     std::vector<Association> associations =
823                         sdbusplus::message::variant_ns::get<
824                             std::vector<Association>>(findAssociations->second);
825 
826                     std::string well_known;
827                     if (!getWellKnown(name_owners, message.get_sender(),
828                                       well_known))
829                     {
830                         return;
831                     }
832                     associationChanged(server, associations, message.get_path(),
833                                        well_known);
834                 }
835             };
836     sdbusplus::bus::match::match associationChanged(
837         static_cast<sdbusplus::bus::bus&>(*system_bus),
838         sdbusplus::bus::match::rules::interface(
839             "org.freedesktop.DBus.Properties") +
840             sdbusplus::bus::match::rules::member("PropertiesChanged") +
841             sdbusplus::bus::match::rules::argN(0, ASSOCIATIONS_INTERFACE),
842         associationChangedHandler);
843 
844     std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
845         server.add_interface("/xyz/openbmc_project/object_mapper",
846                              "xyz.openbmc_project.ObjectMapper");
847 
848     iface->register_method(
849         "GetAncestors", [&interface_map](std::string& req_path,
850                                          std::vector<std::string>& interfaces) {
851             // Interfaces need to be sorted for intersect to function
852             std::sort(interfaces.begin(), interfaces.end());
853 
854             if (boost::ends_with(req_path, "/"))
855             {
856                 req_path.pop_back();
857             }
858             if (req_path.size() &&
859                 interface_map.find(req_path) == interface_map.end())
860             {
861                 throw NotFoundException();
862             }
863 
864             std::vector<interface_map_type::value_type> ret;
865             for (auto& object_path : interface_map)
866             {
867                 auto& this_path = object_path.first;
868                 if (boost::starts_with(req_path, this_path) &&
869                     (req_path != this_path))
870                 {
871                     if (interfaces.empty())
872                     {
873                         ret.emplace_back(object_path);
874                     }
875                     else
876                     {
877                         for (auto& interface_map : object_path.second)
878                         {
879 
880                             if (intersect(interfaces.begin(), interfaces.end(),
881                                           interface_map.second.begin(),
882                                           interface_map.second.end()))
883                             {
884                                 addObjectMapResult(ret, this_path,
885                                                    interface_map);
886                             }
887                         }
888                     }
889                 }
890             }
891 
892             return ret;
893         });
894 
895     iface->register_method(
896         "GetObject", [&interface_map](const std::string& path,
897                                       std::vector<std::string>& interfaces) {
898             boost::container::flat_map<std::string,
899                                        boost::container::flat_set<std::string>>
900                 results;
901 
902             // Interfaces need to be sorted for intersect to function
903             std::sort(interfaces.begin(), interfaces.end());
904             auto path_ref = interface_map.find(path);
905             if (path_ref == interface_map.end())
906             {
907                 throw NotFoundException();
908             }
909             if (interfaces.empty())
910             {
911                 return path_ref->second;
912             }
913             for (auto& interface_map : path_ref->second)
914             {
915                 if (intersect(interfaces.begin(), interfaces.end(),
916                               interface_map.second.begin(),
917                               interface_map.second.end()))
918                 {
919                     results.emplace(interface_map.first, interface_map.second);
920                 }
921             }
922 
923             if (results.empty())
924             {
925                 throw NotFoundException();
926             }
927 
928             return results;
929         });
930 
931     iface->register_method(
932         "GetSubTree", [&interface_map](std::string& req_path, int32_t depth,
933                                        std::vector<std::string>& interfaces) {
934             if (depth <= 0)
935             {
936                 depth = std::numeric_limits<int32_t>::max();
937             }
938             // Interfaces need to be sorted for intersect to function
939             std::sort(interfaces.begin(), interfaces.end());
940             std::vector<interface_map_type::value_type> ret;
941 
942             if (boost::ends_with(req_path, "/"))
943             {
944                 req_path.pop_back();
945             }
946             if (req_path.size() &&
947                 interface_map.find(req_path) == interface_map.end())
948             {
949                 throw NotFoundException();
950             }
951 
952             for (auto& object_path : interface_map)
953             {
954                 auto& this_path = object_path.first;
955 
956                 if (this_path == req_path)
957                 {
958                     continue;
959                 }
960 
961                 if (boost::starts_with(this_path, req_path))
962                 {
963                     // count the number of slashes past the search term
964                     int32_t this_depth =
965                         std::count(this_path.begin() + req_path.size(),
966                                    this_path.end(), '/');
967                     if (this_depth <= depth)
968                     {
969                         for (auto& interface_map : object_path.second)
970                         {
971                             if (intersect(interfaces.begin(), interfaces.end(),
972                                           interface_map.second.begin(),
973                                           interface_map.second.end()) ||
974                                 interfaces.empty())
975                             {
976                                 addObjectMapResult(ret, this_path,
977                                                    interface_map);
978                             }
979                         }
980                     }
981                 }
982             }
983 
984             return ret;
985         });
986 
987     iface->register_method(
988         "GetSubTreePaths",
989         [&interface_map](std::string& req_path, int32_t depth,
990                          std::vector<std::string>& interfaces) {
991             if (depth <= 0)
992             {
993                 depth = std::numeric_limits<int32_t>::max();
994             }
995             // Interfaces need to be sorted for intersect to function
996             std::sort(interfaces.begin(), interfaces.end());
997             std::vector<std::string> ret;
998 
999             if (boost::ends_with(req_path, "/"))
1000             {
1001                 req_path.pop_back();
1002             }
1003             if (req_path.size() &&
1004                 interface_map.find(req_path) == interface_map.end())
1005             {
1006                 throw NotFoundException();
1007             }
1008 
1009             for (auto& object_path : interface_map)
1010             {
1011                 auto& this_path = object_path.first;
1012 
1013                 if (this_path == req_path)
1014                 {
1015                     continue;
1016                 }
1017 
1018                 if (boost::starts_with(this_path, req_path))
1019                 {
1020                     // count the number of slashes past the search term
1021                     int this_depth =
1022                         std::count(this_path.begin() + req_path.size(),
1023                                    this_path.end(), '/');
1024                     if (this_depth <= depth)
1025                     {
1026                         bool add = interfaces.empty();
1027                         for (auto& interface_map : object_path.second)
1028                         {
1029                             if (intersect(interfaces.begin(), interfaces.end(),
1030                                           interface_map.second.begin(),
1031                                           interface_map.second.end()))
1032                             {
1033                                 add = true;
1034                                 break;
1035                             }
1036                         }
1037                         if (add)
1038                         {
1039                             // TODO(ed) this is a copy
1040                             ret.emplace_back(this_path);
1041                         }
1042                     }
1043                 }
1044             }
1045 
1046             return ret;
1047         });
1048 
1049     iface->initialize();
1050 
1051     io.post([&]() {
1052         doListNames(io, interface_map, system_bus.get(), name_owners, server);
1053     });
1054 
1055     io.run();
1056 }
1057