blob: eb97caaec0eb5f384ed9cbcb27b25c6ac1554858 [file] [log] [blame]
Matt Spinlerdd945862018-09-07 12:41:05 -05001#include "src/argument.hpp"
2
Ed Tanous60520632018-06-11 17:46:52 -07003#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
15constexpr const char* OBJECT_MAPPER_DBUS_NAME =
16 "xyz.openbmc_project.ObjectMapper";
17constexpr const char* ASSOCIATIONS_INTERFACE = "org.openbmc.Associations";
Matt Spinler1036b4d2018-09-18 16:45:29 -050018constexpr const char* XYZ_ASSOCIATION_INTERFACE =
19 "xyz.openbmc_project.Association";
Ed Tanous60520632018-06-11 17:46:52 -070020
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
26using interface_map_type = boost::container::flat_map<
27 std::string, boost::container::flat_map<
28 std::string, boost::container::flat_set<std::string>>>;
29
30using Association = std::tuple<std::string, std::string, std::string>;
31
Matt Spinler1036b4d2018-09-18 16:45:29 -050032// 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
37static constexpr auto ifacePos = 0;
38static constexpr auto endpointsPos = 1;
39static constexpr auto sourcePos = 2;
40using Endpoints = std::vector<std::string>;
41boost::container::flat_map<
42 std::string, std::tuple<std::shared_ptr<sdbusplus::asio::dbus_interface>,
43 Endpoints, std::string>>
Ed Tanous60520632018-06-11 17:46:52 -070044 associationInterfaces;
45
Matt Spinlerdd945862018-09-07 12:41:05 -050046static boost::container::flat_set<std::string> service_whitelist;
47static boost::container::flat_set<std::string> service_blacklist;
48static boost::container::flat_set<std::string> iface_whitelist;
49
Ed Tanous60520632018-06-11 17:46:52 -070050/** Exception thrown when a path is not found in the object list. */
51struct 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
68bool 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
88void 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
111void 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
124struct InProgressIntrospect
125{
126 InProgressIntrospect(
127 sdbusplus::asio::connection* system_bus, boost::asio::io_service& io,
Matt Spinleraecabe82018-09-19 13:25:42 -0500128 const std::string& process_name
129#ifdef DEBUG
130 ,
Ed Tanous60520632018-06-11 17:46:52 -0700131 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
Matt Spinleraecabe82018-09-19 13:25:42 -0500132 global_start_time
133#endif
134 ) :
Ed Tanous60520632018-06-11 17:46:52 -0700135 system_bus(system_bus),
Matt Spinleraecabe82018-09-19 13:25:42 -0500136 io(io), process_name(process_name)
137#ifdef DEBUG
138 ,
Ed Tanous60520632018-06-11 17:46:52 -0700139 global_start_time(global_start_time),
140 process_start_time(std::chrono::steady_clock::now())
Matt Spinleraecabe82018-09-19 13:25:42 -0500141#endif
Ed Tanous60520632018-06-11 17:46:52 -0700142 {
143 }
144 ~InProgressIntrospect()
145 {
146 send_introspection_complete_signal(system_bus, process_name);
Matt Spinleraecabe82018-09-19 13:25:42 -0500147
148#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700149 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 }
Matt Spinleraecabe82018-09-19 13:25:42 -0500162#endif
Ed Tanous60520632018-06-11 17:46:52 -0700163 }
164 sdbusplus::asio::connection* system_bus;
165 boost::asio::io_service& io;
166 std::string process_name;
Matt Spinleraecabe82018-09-19 13:25:42 -0500167#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700168 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;
Matt Spinleraecabe82018-09-19 13:25:42 -0500171#endif
Ed Tanous60520632018-06-11 17:46:52 -0700172};
173
174static const boost::container::flat_set<std::string> ignored_interfaces{
175 "org.freedesktop.DBus.Introspectable", "org.freedesktop.DBus.Peer",
176 "org.freedesktop.DBus.Properties"};
177
Ed Tanous60520632018-06-11 17:46:52 -0700178void addAssociation(sdbusplus::asio::object_server& objectServer,
179 const std::vector<Association>& associations,
180 const std::string& path)
181{
182 boost::container::flat_map<std::string,
183 boost::container::flat_set<std::string>>
184 objects;
185 for (const Association& association : associations)
186 {
187 std::string forward;
188 std::string reverse;
189 std::string endpoint;
190 std::tie(forward, reverse, endpoint) = association;
191
192 if (forward.size())
193 {
194 objects[path + "/" + forward].emplace(endpoint);
195 }
196 if (reverse.size())
197 {
198 if (endpoint.empty())
199 {
200 std::cerr << "Found invalid association on path " << path
201 << "\n";
202 continue;
203 }
204 objects[endpoint + "/" + reverse].emplace(path);
205 }
206 }
207 for (const auto& object : objects)
208 {
209 // the mapper exposes the new association interface but intakes
210 // the old
211
212 auto& iface = associationInterfaces[object.first];
Matt Spinler1036b4d2018-09-18 16:45:29 -0500213 auto& i = std::get<ifacePos>(iface);
214 auto& endpoints = std::get<endpointsPos>(iface);
215 std::get<sourcePos>(iface) = path;
216
217 // Only add new endpoints
218 for (auto& e : object.second)
219 {
220 if (std::find(endpoints.begin(), endpoints.end(), e) ==
221 endpoints.end())
222 {
223 endpoints.push_back(e);
224 }
225 }
226
227 // If the interface already exists, only need to update
228 // the property value, otherwise create it
229 if (i)
230 {
231 i->set_property("endpoints", endpoints);
232 }
233 else
234 {
235 i = objectServer.add_interface(object.first,
236 XYZ_ASSOCIATION_INTERFACE);
237 i->register_property("endpoints", endpoints);
238 i->initialize();
239 }
240 }
241}
242
243void removeAssociation(const std::string& sourcePath,
244 sdbusplus::asio::object_server& server)
245{
246 // The sourcePath passed in can be:
247 // a) the source of the association object, in which case
248 // that whole object needs to be deleted, and/or
249 // b) just an entry in the endpoints property under some
250 // other path, in which case it just needs to be removed
251 // from the property.
252 for (auto& assoc : associationInterfaces)
253 {
254 if (sourcePath == std::get<sourcePos>(assoc.second))
255 {
256 server.remove_interface(std::get<ifacePos>(assoc.second));
257 std::get<ifacePos>(assoc.second) = nullptr;
258 std::get<endpointsPos>(assoc.second).clear();
259 std::get<sourcePos>(assoc.second).clear();
260 }
261 else
262 {
263 auto& endpoints = std::get<endpointsPos>(assoc.second);
264 auto toRemove =
265 std::find(endpoints.begin(), endpoints.end(), sourcePath);
266 if (toRemove != endpoints.end())
267 {
268 endpoints.erase(toRemove);
269
270 if (endpoints.empty())
271 {
272 // Remove the interface object too if no longer needed
273 server.remove_interface(std::get<ifacePos>(assoc.second));
274 std::get<ifacePos>(assoc.second) = nullptr;
275 std::get<sourcePos>(assoc.second).clear();
276 }
277 else
278 {
279 std::get<ifacePos>(assoc.second)
280 ->set_property("endpoints", endpoints);
281 }
282 }
283 }
Ed Tanous60520632018-06-11 17:46:52 -0700284 }
285}
286
287void do_associations(sdbusplus::asio::connection* system_bus,
288 sdbusplus::asio::object_server& objectServer,
289 const std::string& processName, const std::string& path)
290{
291 system_bus->async_method_call(
292 [&objectServer,
293 path](const boost::system::error_code ec,
294 const sdbusplus::message::variant<std::vector<Association>>&
295 variantAssociations) {
296 if (ec)
297 {
298 std::cerr << "Error getting associations from " << path << "\n";
299 }
300 std::vector<Association> associations =
301 sdbusplus::message::variant_ns::get<std::vector<Association>>(
302 variantAssociations);
303 addAssociation(objectServer, associations, path);
304 },
305 processName, path, "org.freedesktop.DBus.Properties", "Get",
306 ASSOCIATIONS_INTERFACE, "associations");
307}
308
309void 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];
Ed Tanous60520632018-06-11 17:46:52 -0700344 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
Matt Spinlerdd945862018-09-07 12:41:05 -0500354 std::string iface{iface_name};
355
356 if (((ignored_interfaces.find(iface) ==
357 ignored_interfaces.end()) &&
358 (std::find_if(iface_whitelist.begin(),
359 iface_whitelist.end(),
360 [iface](const auto& prefix) {
361 return boost::starts_with(iface, prefix);
362 }) != iface_whitelist.end())) ||
363 (iface == "org.freedesktop.DBus.ObjectManager"))
Ed Tanous60520632018-06-11 17:46:52 -0700364 {
365 thisPathMap[transaction->process_name].emplace(iface_name);
366 }
367 if (std::strcmp(iface_name, ASSOCIATIONS_INTERFACE) == 0)
368 {
369 do_associations(system_bus, objectServer,
370 transaction->process_name, path);
371 }
Ed Tanous60520632018-06-11 17:46:52 -0700372
373 pElement = pElement->NextSiblingElement("interface");
374 }
375
Ed Tanous50232cd2018-11-12 11:34:43 -0800376 pElement = pRoot->FirstChildElement("node");
377 while (pElement != nullptr)
Ed Tanous60520632018-06-11 17:46:52 -0700378 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800379 const char* child_path = pElement->Attribute("name");
380 if (child_path != nullptr)
Ed Tanous60520632018-06-11 17:46:52 -0700381 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800382 std::string parent_path(path);
383 if (parent_path == "/")
Ed Tanous60520632018-06-11 17:46:52 -0700384 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800385 parent_path.clear();
Ed Tanous60520632018-06-11 17:46:52 -0700386 }
Ed Tanous50232cd2018-11-12 11:34:43 -0800387
388 do_introspect(system_bus, transaction, interface_map,
389 objectServer, parent_path + "/" + child_path);
Ed Tanous60520632018-06-11 17:46:52 -0700390 }
Ed Tanous50232cd2018-11-12 11:34:43 -0800391 pElement = pElement->NextSiblingElement("node");
Ed Tanous60520632018-06-11 17:46:52 -0700392 }
393 },
394 transaction->process_name, path, "org.freedesktop.DBus.Introspectable",
395 "Introspect");
396}
397
398bool need_to_introspect(const std::string& process_name)
399{
Matt Spinlerdd945862018-09-07 12:41:05 -0500400 auto inWhitelist =
401 std::find_if(service_whitelist.begin(), service_whitelist.end(),
402 [&process_name](const auto& prefix) {
403 return boost::starts_with(process_name, prefix);
404 }) != service_whitelist.end();
405
406 // This holds full service names, not prefixes
407 auto inBlacklist =
408 service_blacklist.find(process_name) != service_blacklist.end();
409
410 return inWhitelist && !inBlacklist;
Ed Tanous60520632018-06-11 17:46:52 -0700411}
412
413void start_new_introspect(
414 sdbusplus::asio::connection* system_bus, boost::asio::io_service& io,
415 interface_map_type& interface_map, const std::string& process_name,
Matt Spinleraecabe82018-09-19 13:25:42 -0500416#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700417 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
418 global_start_time,
Matt Spinleraecabe82018-09-19 13:25:42 -0500419#endif
Ed Tanous60520632018-06-11 17:46:52 -0700420 sdbusplus::asio::object_server& objectServer)
421{
422 if (need_to_introspect(process_name))
423 {
Ed Tanous60520632018-06-11 17:46:52 -0700424 std::shared_ptr<InProgressIntrospect> transaction =
Matt Spinleraecabe82018-09-19 13:25:42 -0500425 std::make_shared<InProgressIntrospect>(system_bus, io, process_name
426#ifdef DEBUG
427 ,
428 global_start_time
429#endif
430 );
Ed Tanous60520632018-06-11 17:46:52 -0700431
432 do_introspect(system_bus, transaction, interface_map, objectServer,
433 "/");
434 }
435}
436
437// TODO(ed) replace with std::set_intersection once c++17 is available
438template <class InputIt1, class InputIt2>
439bool intersect(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
440{
441 while (first1 != last1 && first2 != last2)
442 {
443 if (*first1 < *first2)
444 {
445 ++first1;
446 continue;
447 }
448 if (*first2 < *first1)
449 {
450 ++first2;
451 continue;
452 }
453 return true;
454 }
455 return false;
456}
457
458void doListNames(
459 boost::asio::io_service& io, interface_map_type& interface_map,
460 sdbusplus::asio::connection* system_bus,
461 boost::container::flat_map<std::string, std::string>& name_owners,
462 sdbusplus::asio::object_server& objectServer)
463{
464 system_bus->async_method_call(
465 [&io, &interface_map, &name_owners, &objectServer,
466 system_bus](const boost::system::error_code ec,
467 std::vector<std::string> process_names) {
468 if (ec)
469 {
470 std::cerr << "Error getting names: " << ec << "\n";
471 std::exit(EXIT_FAILURE);
472 return;
473 }
Ed Tanous60520632018-06-11 17:46:52 -0700474 // Try to make startup consistent
475 std::sort(process_names.begin(), process_names.end());
Matt Spinleraecabe82018-09-19 13:25:42 -0500476#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700477 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
478 global_start_time = std::make_shared<
479 std::chrono::time_point<std::chrono::steady_clock>>(
480 std::chrono::steady_clock::now());
Matt Spinleraecabe82018-09-19 13:25:42 -0500481#endif
Ed Tanous60520632018-06-11 17:46:52 -0700482 for (const std::string& process_name : process_names)
483 {
484 if (need_to_introspect(process_name))
485 {
486 start_new_introspect(system_bus, io, interface_map,
Matt Spinleraecabe82018-09-19 13:25:42 -0500487 process_name,
488#ifdef DEBUG
489 global_start_time,
490#endif
Ed Tanous60520632018-06-11 17:46:52 -0700491 objectServer);
492 update_owners(system_bus, name_owners, process_name);
493 }
494 }
495 },
496 "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
497 "ListNames");
498}
499
Matt Spinlerdd945862018-09-07 12:41:05 -0500500void splitArgs(const std::string& stringArgs,
501 boost::container::flat_set<std::string>& listArgs)
502{
503 std::istringstream args;
504 std::string arg;
505
506 args.str(stringArgs);
507
508 while (!args.eof())
509 {
510 args >> arg;
511 if (!arg.empty())
512 {
513 listArgs.insert(arg);
514 }
515 }
516}
517
Matt Spinler9f0958e2018-09-11 08:26:10 -0500518void addSubtreeResult(
519 std::vector<interface_map_type::value_type>& subtree,
520 const std::string& objectPath,
521 const std::pair<std::string, boost::container::flat_set<std::string>>&
522 interfaceMap)
523{
524 // Adds an object path/service name/interface list entry to
525 // the results of GetSubTree.
526 // If an entry for the object path already exists, just add the
527 // service name and interfaces to that entry, otherwise create
528 // a new entry.
529 auto entry = std::find_if(
530 subtree.begin(), subtree.end(),
531 [&objectPath](const auto& i) { return objectPath == i.first; });
532
533 if (entry != subtree.end())
534 {
535 entry->second.emplace(interfaceMap);
536 }
537 else
538 {
539 interface_map_type::value_type object;
540 object.first = objectPath;
541 object.second.emplace(interfaceMap);
542 subtree.push_back(object);
543 }
544}
545
Ed Tanous60520632018-06-11 17:46:52 -0700546int main(int argc, char** argv)
547{
Matt Spinlerdd945862018-09-07 12:41:05 -0500548 auto options = ArgumentParser(argc, argv);
Ed Tanous60520632018-06-11 17:46:52 -0700549 boost::asio::io_service io;
550 std::shared_ptr<sdbusplus::asio::connection> system_bus =
551 std::make_shared<sdbusplus::asio::connection>(io);
552
Matt Spinlerdd945862018-09-07 12:41:05 -0500553 splitArgs(options["service-namespaces"], service_whitelist);
554 splitArgs(options["interface-namespaces"], iface_whitelist);
555 splitArgs(options["service-blacklists"], service_blacklist);
556
Ed Tanous60520632018-06-11 17:46:52 -0700557 system_bus->request_name(OBJECT_MAPPER_DBUS_NAME);
558 sdbusplus::asio::object_server server(system_bus);
559
560 // Construct a signal set registered for process termination.
561 boost::asio::signal_set signals(io, SIGINT, SIGTERM);
562 signals.async_wait([&io](const boost::system::error_code& error,
563 int signal_number) { io.stop(); });
564
565 interface_map_type interface_map;
566 boost::container::flat_map<std::string, std::string> name_owners;
567
568 std::function<void(sdbusplus::message::message & message)>
569 nameChangeHandler = [&interface_map, &io, &name_owners, &server,
570 system_bus](sdbusplus::message::message& message) {
571 std::string name;
572 std::string old_owner;
573 std::string new_owner;
574
575 message.read(name, old_owner, new_owner);
576
577 if (!old_owner.empty())
578 {
579 if (boost::starts_with(old_owner, ":"))
580 {
581 auto it = name_owners.find(old_owner);
582 if (it != name_owners.end())
583 {
584 name_owners.erase(it);
585 }
586 }
587 // Connection removed
588 interface_map_type::iterator path_it = interface_map.begin();
589 while (path_it != interface_map.end())
590 {
Matt Spinler1036b4d2018-09-18 16:45:29 -0500591 // If an associations interface is being removed,
592 // also need to remove the corresponding associations
593 // objects and properties.
594 auto ifaces = path_it->second.find(name);
595 if (ifaces != path_it->second.end())
596 {
597 auto assoc = std::find(ifaces->second.begin(),
598 ifaces->second.end(),
599 ASSOCIATIONS_INTERFACE);
600
601 if (assoc != ifaces->second.end())
602 {
603 removeAssociation(path_it->first, server);
604 }
605 }
606
Ed Tanous60520632018-06-11 17:46:52 -0700607 path_it->second.erase(name);
608 if (path_it->second.empty())
609 {
610 // If the last connection to the object is gone,
611 // delete the top level object
612 path_it = interface_map.erase(path_it);
613 continue;
614 }
615 path_it++;
616 }
617 }
618
619 if (!new_owner.empty())
620 {
Matt Spinleraecabe82018-09-19 13:25:42 -0500621#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700622 auto transaction = std::make_shared<
623 std::chrono::time_point<std::chrono::steady_clock>>(
624 std::chrono::steady_clock::now());
Matt Spinleraecabe82018-09-19 13:25:42 -0500625#endif
Ed Tanous60520632018-06-11 17:46:52 -0700626 // New daemon added
627 if (need_to_introspect(name))
628 {
629 name_owners[new_owner] = name;
630 start_new_introspect(system_bus.get(), io, interface_map,
Matt Spinleraecabe82018-09-19 13:25:42 -0500631 name,
632#ifdef DEBUG
633 transaction,
634#endif
635 server);
Ed Tanous60520632018-06-11 17:46:52 -0700636 }
637 }
638 };
639
640 sdbusplus::bus::match::match nameOwnerChanged(
641 static_cast<sdbusplus::bus::bus&>(*system_bus),
642 sdbusplus::bus::match::rules::nameOwnerChanged(), nameChangeHandler);
643
644 std::function<void(sdbusplus::message::message & message)>
645 interfacesAddedHandler = [&interface_map, &name_owners, &server](
646 sdbusplus::message::message& message) {
647 sdbusplus::message::object_path obj_path;
648 std::vector<std::pair<
649 std::string, std::vector<std::pair<
650 std::string, sdbusplus::message::variant<
651 std::vector<Association>>>>>>
652 interfaces_added;
653 message.read(obj_path, interfaces_added);
654 std::string well_known;
655 if (!get_well_known(name_owners, message.get_sender(), well_known))
656 {
657 return; // only introspect well-known
658 }
659 if (need_to_introspect(well_known))
660 {
661 auto& iface_list = interface_map[obj_path.str];
662
Matt Spinlercc74e4b2018-09-18 16:21:54 -0500663 for (const auto& interface_pair : interfaces_added)
Ed Tanous60520632018-06-11 17:46:52 -0700664 {
665 iface_list[well_known].emplace(interface_pair.first);
666
667 if (interface_pair.first == ASSOCIATIONS_INTERFACE)
668 {
669 const sdbusplus::message::variant<
670 std::vector<Association>>* variantAssociations =
671 nullptr;
672 for (const auto& interface : interface_pair.second)
673 {
674 if (interface.first == "associations")
675 {
676 variantAssociations = &(interface.second);
677 }
678 }
679 if (variantAssociations == nullptr)
680 {
681 std::cerr << "Illegal association found on "
682 << well_known << "\n";
683 continue;
684 }
685 std::vector<Association> associations =
686 sdbusplus::message::variant_ns::get<
687 std::vector<Association>>(*variantAssociations);
688 addAssociation(server, associations, obj_path.str);
689 }
690 }
691 }
692 };
693
694 sdbusplus::bus::match::match interfacesAdded(
695 static_cast<sdbusplus::bus::bus&>(*system_bus),
696 sdbusplus::bus::match::rules::interfacesAdded(),
697 interfacesAddedHandler);
698
699 std::function<void(sdbusplus::message::message & message)>
700 interfacesRemovedHandler = [&interface_map, &name_owners, &server](
701 sdbusplus::message::message& message) {
702 sdbusplus::message::object_path obj_path;
703 std::vector<std::string> interfaces_removed;
704 message.read(obj_path, interfaces_removed);
705 auto connection_map = interface_map.find(obj_path.str);
706 if (connection_map == interface_map.end())
707 {
708 return;
709 }
710
711 std::string sender;
712 if (!get_well_known(name_owners, message.get_sender(), sender))
713 {
714 return;
715 }
716 for (const std::string& interface : interfaces_removed)
717 {
718 auto interface_set = connection_map->second.find(sender);
719 if (interface_set == connection_map->second.end())
720 {
721 continue;
722 }
723
724 if (interface == ASSOCIATIONS_INTERFACE)
725 {
Matt Spinler1036b4d2018-09-18 16:45:29 -0500726 removeAssociation(obj_path.str, server);
Ed Tanous60520632018-06-11 17:46:52 -0700727 }
728
729 interface_set->second.erase(interface);
730 // If this was the last interface on this connection,
731 // erase the connection
732 if (interface_set->second.empty())
733 {
734 connection_map->second.erase(interface_set);
735 }
736 }
737 // If this was the last connection on this object path,
738 // erase the object path
739 if (connection_map->second.empty())
740 {
741 interface_map.erase(connection_map);
742 }
743 };
744
745 sdbusplus::bus::match::match interfacesRemoved(
746 static_cast<sdbusplus::bus::bus&>(*system_bus),
747 sdbusplus::bus::match::rules::interfacesRemoved(),
748 interfacesRemovedHandler);
749
750 std::function<void(sdbusplus::message::message & message)>
751 associationChangedHandler =
752 [&server](sdbusplus::message::message& message) {
753 std::string objectName;
754 boost::container::flat_map<
755 std::string,
756 sdbusplus::message::variant<std::vector<Association>>>
757 values;
758 message.read(objectName, values);
759 auto findAssociations = values.find("associations");
760 if (findAssociations != values.end())
761 {
762 std::vector<Association> associations =
763 sdbusplus::message::variant_ns::get<
764 std::vector<Association>>(findAssociations->second);
765 addAssociation(server, associations, message.get_path());
766 }
767 };
768 sdbusplus::bus::match::match associationChanged(
769 static_cast<sdbusplus::bus::bus&>(*system_bus),
770 sdbusplus::bus::match::rules::interface(
771 "org.freedesktop.DBus.Properties") +
772 sdbusplus::bus::match::rules::member("PropertiesChanged") +
773 sdbusplus::bus::match::rules::argN(0, ASSOCIATIONS_INTERFACE),
774 associationChangedHandler);
775
776 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
777 server.add_interface("/xyz/openbmc_project/object_mapper",
778 "xyz.openbmc_project.ObjectMapper");
779
780 iface->register_method(
781 "GetAncestors", [&interface_map](const std::string& req_path,
782 std::vector<std::string>& interfaces) {
783 // Interfaces need to be sorted for intersect to function
784 std::sort(interfaces.begin(), interfaces.end());
785
786 std::vector<interface_map_type::value_type> ret;
787 for (auto& object_path : interface_map)
788 {
789 auto& this_path = object_path.first;
Matt Spinlerea6516c2018-09-25 16:23:13 -0500790 if (boost::starts_with(req_path, this_path) &&
791 (req_path != this_path))
Ed Tanous60520632018-06-11 17:46:52 -0700792 {
793 if (interfaces.empty())
794 {
795 ret.emplace_back(object_path);
796 }
797 else
798 {
799 for (auto& interface_map : object_path.second)
800 {
801
802 if (intersect(interfaces.begin(), interfaces.end(),
803 interface_map.second.begin(),
804 interface_map.second.end()))
805 {
806 ret.emplace_back(object_path);
807 break;
808 }
809 }
810 }
811 }
812 }
813 if (ret.empty())
814 {
815 throw NotFoundException();
816 }
817
818 return ret;
819 });
820
821 iface->register_method(
822 "GetObject", [&interface_map](const std::string& path,
823 std::vector<std::string>& interfaces) {
Matt Spinler7a38d412018-09-18 16:13:25 -0500824 boost::container::flat_map<std::string,
825 boost::container::flat_set<std::string>>
826 results;
827
Ed Tanous60520632018-06-11 17:46:52 -0700828 // Interfaces need to be sorted for intersect to function
829 std::sort(interfaces.begin(), interfaces.end());
830 auto path_ref = interface_map.find(path);
831 if (path_ref == interface_map.end())
832 {
833 throw NotFoundException();
834 }
835 if (interfaces.empty())
836 {
837 return path_ref->second;
838 }
839 for (auto& interface_map : path_ref->second)
840 {
841 if (intersect(interfaces.begin(), interfaces.end(),
842 interface_map.second.begin(),
843 interface_map.second.end()))
844 {
Matt Spinler7a38d412018-09-18 16:13:25 -0500845 results.emplace(interface_map.first, interface_map.second);
Ed Tanous60520632018-06-11 17:46:52 -0700846 }
847 }
Matt Spinler7a38d412018-09-18 16:13:25 -0500848
849 if (results.empty())
850 {
851 throw NotFoundException();
852 }
853
854 return results;
Ed Tanous60520632018-06-11 17:46:52 -0700855 });
856
857 iface->register_method(
Matt Spinler153494e2018-11-07 16:35:40 -0600858 "GetSubTree", [&interface_map](std::string& req_path, int32_t depth,
859 std::vector<std::string>& interfaces) {
Ed Tanous60520632018-06-11 17:46:52 -0700860 if (depth <= 0)
861 {
862 depth = std::numeric_limits<int32_t>::max();
863 }
864 // Interfaces need to be sorted for intersect to function
865 std::sort(interfaces.begin(), interfaces.end());
866 std::vector<interface_map_type::value_type> ret;
867
Matt Spinler153494e2018-11-07 16:35:40 -0600868 if (req_path.size() > 1 && req_path.back() == '/')
869 {
870 req_path.pop_back();
871 }
872
Ed Tanous60520632018-06-11 17:46:52 -0700873 for (auto& object_path : interface_map)
874 {
875 auto& this_path = object_path.first;
Matt Spinler153494e2018-11-07 16:35:40 -0600876
877 if (this_path == req_path)
878 {
879 continue;
880 }
881
Ed Tanous60520632018-06-11 17:46:52 -0700882 if (boost::starts_with(this_path, req_path))
883 {
884 // count the number of slashes past the search term
885 int32_t this_depth =
886 std::count(this_path.begin() + req_path.size(),
887 this_path.end(), '/');
888 if (this_depth <= depth)
889 {
Ed Tanous60520632018-06-11 17:46:52 -0700890 for (auto& interface_map : object_path.second)
891 {
892 if (intersect(interfaces.begin(), interfaces.end(),
893 interface_map.second.begin(),
Matt Spinler9f0958e2018-09-11 08:26:10 -0500894 interface_map.second.end()) ||
895 interfaces.empty())
Ed Tanous60520632018-06-11 17:46:52 -0700896 {
Matt Spinler9f0958e2018-09-11 08:26:10 -0500897 addSubtreeResult(ret, this_path, interface_map);
Ed Tanous60520632018-06-11 17:46:52 -0700898 }
899 }
Ed Tanous60520632018-06-11 17:46:52 -0700900 }
901 }
902 }
903 if (ret.empty())
904 {
905 throw NotFoundException();
906 }
907 return ret;
908 });
909
910 iface->register_method(
911 "GetSubTreePaths",
Matt Spinler153494e2018-11-07 16:35:40 -0600912 [&interface_map](std::string& req_path, int32_t depth,
Ed Tanous60520632018-06-11 17:46:52 -0700913 std::vector<std::string>& interfaces) {
914 if (depth <= 0)
915 {
916 depth = std::numeric_limits<int32_t>::max();
917 }
918 // Interfaces need to be sorted for intersect to function
919 std::sort(interfaces.begin(), interfaces.end());
920 std::vector<std::string> ret;
Matt Spinler153494e2018-11-07 16:35:40 -0600921
922 if (req_path.size() > 1 && req_path.back() == '/')
923 {
924 req_path.pop_back();
925 }
926
Ed Tanous60520632018-06-11 17:46:52 -0700927 for (auto& object_path : interface_map)
928 {
929 auto& this_path = object_path.first;
Matt Spinler153494e2018-11-07 16:35:40 -0600930
931 if (this_path == req_path)
932 {
933 continue;
934 }
935
Ed Tanous60520632018-06-11 17:46:52 -0700936 if (boost::starts_with(this_path, req_path))
937 {
938 // count the number of slashes past the search term
939 int this_depth =
940 std::count(this_path.begin() + req_path.size(),
941 this_path.end(), '/');
942 if (this_depth <= depth)
943 {
944 bool add = interfaces.empty();
945 for (auto& interface_map : object_path.second)
946 {
947 if (intersect(interfaces.begin(), interfaces.end(),
948 interface_map.second.begin(),
949 interface_map.second.end()))
950 {
951 add = true;
952 break;
953 }
954 }
955 if (add)
956 {
957 // TODO(ed) this is a copy
958 ret.emplace_back(this_path);
959 }
960 }
961 }
962 }
963 if (ret.empty())
964 {
965 throw NotFoundException();
966 }
967 return ret;
968 });
969
970 iface->initialize();
971
972 io.post([&]() {
973 doListNames(io, interface_map, system_bus.get(), name_owners, server);
974 });
975
Ed Tanous60520632018-06-11 17:46:52 -0700976 io.run();
977}