blob: cba23049bece75ef29c0afef38cce72415d89ab7 [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,
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
163static const boost::container::flat_set<std::string> ignored_interfaces{
164 "org.freedesktop.DBus.Introspectable", "org.freedesktop.DBus.Peer",
165 "org.freedesktop.DBus.Properties"};
166
167void 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
211void 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];
Matt Spinler1036b4d2018-09-18 16:45:29 -0500246 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
276void 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 }
Ed Tanous60520632018-06-11 17:46:52 -0700317 }
318}
319
320void 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
342void 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
Matt Spinlerdd945862018-09-07 12:41:05 -0500388 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"))
Ed Tanous60520632018-06-11 17:46:52 -0700398 {
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
449bool need_to_introspect(const std::string& process_name)
450{
Matt Spinlerdd945862018-09-07 12:41:05 -0500451 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;
Ed Tanous60520632018-06-11 17:46:52 -0700462}
463
464void 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
485template <class InputIt1, class InputIt2>
486bool 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
505void 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
Matt Spinlerdd945862018-09-07 12:41:05 -0500544void 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
Matt Spinler9f0958e2018-09-11 08:26:10 -0500562void 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
Ed Tanous60520632018-06-11 17:46:52 -0700590int main(int argc, char** argv)
591{
592 std::cerr << "started\n";
Matt Spinlerdd945862018-09-07 12:41:05 -0500593 auto options = ArgumentParser(argc, argv);
Ed Tanous60520632018-06-11 17:46:52 -0700594 boost::asio::io_service io;
595 std::shared_ptr<sdbusplus::asio::connection> system_bus =
596 std::make_shared<sdbusplus::asio::connection>(io);
597
Matt Spinlerdd945862018-09-07 12:41:05 -0500598 splitArgs(options["service-namespaces"], service_whitelist);
599 splitArgs(options["interface-namespaces"], iface_whitelist);
600 splitArgs(options["service-blacklists"], service_blacklist);
601
Ed Tanous60520632018-06-11 17:46:52 -0700602 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 {
Matt Spinler1036b4d2018-09-18 16:45:29 -0500636 // 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
Ed Tanous60520632018-06-11 17:46:52 -0700652 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
Matt Spinlercc74e4b2018-09-18 16:21:54 -0500702 for (const auto& interface_pair : interfaces_added)
Ed Tanous60520632018-06-11 17:46:52 -0700703 {
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 {
Matt Spinler1036b4d2018-09-18 16:45:29 -0500765 removeAssociation(obj_path.str, server);
Ed Tanous60520632018-06-11 17:46:52 -0700766 }
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) {
Matt Spinler7a38d412018-09-18 16:13:25 -0500862 boost::container::flat_map<std::string,
863 boost::container::flat_set<std::string>>
864 results;
865
Ed Tanous60520632018-06-11 17:46:52 -0700866 // 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 {
Matt Spinler7a38d412018-09-18 16:13:25 -0500883 results.emplace(interface_map.first, interface_map.second);
Ed Tanous60520632018-06-11 17:46:52 -0700884 }
885 }
Matt Spinler7a38d412018-09-18 16:13:25 -0500886
887 if (results.empty())
888 {
889 throw NotFoundException();
890 }
891
892 return results;
Ed Tanous60520632018-06-11 17:46:52 -0700893 });
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 {
Ed Tanous60520632018-06-11 17:46:52 -0700918 for (auto& interface_map : object_path.second)
919 {
920 if (intersect(interfaces.begin(), interfaces.end(),
921 interface_map.second.begin(),
Matt Spinler9f0958e2018-09-11 08:26:10 -0500922 interface_map.second.end()) ||
923 interfaces.empty())
Ed Tanous60520632018-06-11 17:46:52 -0700924 {
Matt Spinler9f0958e2018-09-11 08:26:10 -0500925 addSubtreeResult(ret, this_path, interface_map);
926
927 // if not just adding every interface, then done
928 if (!interfaces.empty())
929 {
930 break;
931 }
Ed Tanous60520632018-06-11 17:46:52 -0700932 }
933 }
Ed Tanous60520632018-06-11 17:46:52 -0700934 }
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}