blob: 9084d0dc48c96306920beee64af0056c186c0a08 [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
178void 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
222void 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];
Matt Spinler1036b4d2018-09-18 16:45:29 -0500257 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
287void 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 }
Ed Tanous60520632018-06-11 17:46:52 -0700328 }
329}
330
331void 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
353void 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
Matt Spinlerdd945862018-09-07 12:41:05 -0500399 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"))
Ed Tanous60520632018-06-11 17:46:52 -0700409 {
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
460bool need_to_introspect(const std::string& process_name)
461{
Matt Spinlerdd945862018-09-07 12:41:05 -0500462 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;
Ed Tanous60520632018-06-11 17:46:52 -0700473}
474
475void 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,
Matt Spinleraecabe82018-09-19 13:25:42 -0500478#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700479 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
480 global_start_time,
Matt Spinleraecabe82018-09-19 13:25:42 -0500481#endif
Ed Tanous60520632018-06-11 17:46:52 -0700482 sdbusplus::asio::object_server& objectServer)
483{
484 if (need_to_introspect(process_name))
485 {
Ed Tanous60520632018-06-11 17:46:52 -0700486 std::shared_ptr<InProgressIntrospect> transaction =
Matt Spinleraecabe82018-09-19 13:25:42 -0500487 std::make_shared<InProgressIntrospect>(system_bus, io, process_name
488#ifdef DEBUG
489 ,
490 global_start_time
491#endif
492 );
Ed Tanous60520632018-06-11 17:46:52 -0700493
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
500template <class InputIt1, class InputIt2>
501bool 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
520void 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 }
Ed Tanous60520632018-06-11 17:46:52 -0700536 // Try to make startup consistent
537 std::sort(process_names.begin(), process_names.end());
Matt Spinleraecabe82018-09-19 13:25:42 -0500538#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700539 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());
Matt Spinleraecabe82018-09-19 13:25:42 -0500543#endif
Ed Tanous60520632018-06-11 17:46:52 -0700544 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,
Matt Spinleraecabe82018-09-19 13:25:42 -0500549 process_name,
550#ifdef DEBUG
551 global_start_time,
552#endif
Ed Tanous60520632018-06-11 17:46:52 -0700553 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
Matt Spinlerdd945862018-09-07 12:41:05 -0500562void 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
Matt Spinler9f0958e2018-09-11 08:26:10 -0500580void 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
Ed Tanous60520632018-06-11 17:46:52 -0700608int main(int argc, char** argv)
609{
Matt Spinlerdd945862018-09-07 12:41:05 -0500610 auto options = ArgumentParser(argc, argv);
Ed Tanous60520632018-06-11 17:46:52 -0700611 boost::asio::io_service io;
612 std::shared_ptr<sdbusplus::asio::connection> system_bus =
613 std::make_shared<sdbusplus::asio::connection>(io);
614
Matt Spinlerdd945862018-09-07 12:41:05 -0500615 splitArgs(options["service-namespaces"], service_whitelist);
616 splitArgs(options["interface-namespaces"], iface_whitelist);
617 splitArgs(options["service-blacklists"], service_blacklist);
618
Ed Tanous60520632018-06-11 17:46:52 -0700619 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 {
Matt Spinler1036b4d2018-09-18 16:45:29 -0500653 // 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
Ed Tanous60520632018-06-11 17:46:52 -0700669 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 {
Matt Spinleraecabe82018-09-19 13:25:42 -0500683#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700684 auto transaction = std::make_shared<
685 std::chrono::time_point<std::chrono::steady_clock>>(
686 std::chrono::steady_clock::now());
Matt Spinleraecabe82018-09-19 13:25:42 -0500687#endif
Ed Tanous60520632018-06-11 17:46:52 -0700688 // 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,
Matt Spinleraecabe82018-09-19 13:25:42 -0500693 name,
694#ifdef DEBUG
695 transaction,
696#endif
697 server);
Ed Tanous60520632018-06-11 17:46:52 -0700698 }
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
Matt Spinlercc74e4b2018-09-18 16:21:54 -0500725 for (const auto& interface_pair : interfaces_added)
Ed Tanous60520632018-06-11 17:46:52 -0700726 {
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 {
Matt Spinler1036b4d2018-09-18 16:45:29 -0500788 removeAssociation(obj_path.str, server);
Ed Tanous60520632018-06-11 17:46:52 -0700789 }
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;
Matt Spinlerea6516c2018-09-25 16:23:13 -0500852 if (boost::starts_with(req_path, this_path) &&
853 (req_path != this_path))
Ed Tanous60520632018-06-11 17:46:52 -0700854 {
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) {
Matt Spinler7a38d412018-09-18 16:13:25 -0500886 boost::container::flat_map<std::string,
887 boost::container::flat_set<std::string>>
888 results;
889
Ed Tanous60520632018-06-11 17:46:52 -0700890 // 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 {
Matt Spinler7a38d412018-09-18 16:13:25 -0500907 results.emplace(interface_map.first, interface_map.second);
Ed Tanous60520632018-06-11 17:46:52 -0700908 }
909 }
Matt Spinler7a38d412018-09-18 16:13:25 -0500910
911 if (results.empty())
912 {
913 throw NotFoundException();
914 }
915
916 return results;
Ed Tanous60520632018-06-11 17:46:52 -0700917 });
918
919 iface->register_method(
Matt Spinler153494e2018-11-07 16:35:40 -0600920 "GetSubTree", [&interface_map](std::string& req_path, int32_t depth,
921 std::vector<std::string>& interfaces) {
Ed Tanous60520632018-06-11 17:46:52 -0700922 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
Matt Spinler153494e2018-11-07 16:35:40 -0600930 if (req_path.size() > 1 && req_path.back() == '/')
931 {
932 req_path.pop_back();
933 }
934
Ed Tanous60520632018-06-11 17:46:52 -0700935 for (auto& object_path : interface_map)
936 {
937 auto& this_path = object_path.first;
Matt Spinler153494e2018-11-07 16:35:40 -0600938
939 if (this_path == req_path)
940 {
941 continue;
942 }
943
Ed Tanous60520632018-06-11 17:46:52 -0700944 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 {
Ed Tanous60520632018-06-11 17:46:52 -0700952 for (auto& interface_map : object_path.second)
953 {
954 if (intersect(interfaces.begin(), interfaces.end(),
955 interface_map.second.begin(),
Matt Spinler9f0958e2018-09-11 08:26:10 -0500956 interface_map.second.end()) ||
957 interfaces.empty())
Ed Tanous60520632018-06-11 17:46:52 -0700958 {
Matt Spinler9f0958e2018-09-11 08:26:10 -0500959 addSubtreeResult(ret, this_path, interface_map);
Ed Tanous60520632018-06-11 17:46:52 -0700960 }
961 }
Ed Tanous60520632018-06-11 17:46:52 -0700962 }
963 }
964 }
965 if (ret.empty())
966 {
967 throw NotFoundException();
968 }
969 return ret;
970 });
971
972 iface->register_method(
973 "GetSubTreePaths",
Matt Spinler153494e2018-11-07 16:35:40 -0600974 [&interface_map](std::string& req_path, int32_t depth,
Ed Tanous60520632018-06-11 17:46:52 -0700975 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;
Matt Spinler153494e2018-11-07 16:35:40 -0600983
984 if (req_path.size() > 1 && req_path.back() == '/')
985 {
986 req_path.pop_back();
987 }
988
Ed Tanous60520632018-06-11 17:46:52 -0700989 for (auto& object_path : interface_map)
990 {
991 auto& this_path = object_path.first;
Matt Spinler153494e2018-11-07 16:35:40 -0600992
993 if (this_path == req_path)
994 {
995 continue;
996 }
997
Ed Tanous60520632018-06-11 17:46:52 -0700998 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
Ed Tanous60520632018-06-11 17:46:52 -07001038 io.run();
1039}