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