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