blob: e00e920107d95b256e63492d52558cade91e1a59 [file] [log] [blame]
Ed Tanous60520632018-06-11 17:46:52 -07001#include <tinyxml2.h>
2
3#include <atomic>
4#include <boost/algorithm/string/predicate.hpp>
5#include <boost/container/flat_map.hpp>
6#include <boost/container/flat_set.hpp>
7#include <chrono>
8#include <iomanip>
9#include <iostream>
10#include <sdbusplus/asio/connection.hpp>
11#include <sdbusplus/asio/object_server.hpp>
12
13constexpr const char* OBJECT_MAPPER_DBUS_NAME =
14 "xyz.openbmc_project.ObjectMapper";
15constexpr const char* ASSOCIATIONS_INTERFACE = "org.openbmc.Associations";
16
17// interface_map_type is the underlying datastructure the mapper uses.
18// The 3 levels of map are
19// object paths
20// connection names
21// interface names
22using interface_map_type = boost::container::flat_map<
23 std::string, boost::container::flat_map<
24 std::string, boost::container::flat_set<std::string>>>;
25
26using Association = std::tuple<std::string, std::string, std::string>;
27
28boost::container::flat_map<std::string,
29 std::shared_ptr<sdbusplus::asio::dbus_interface>>
30 associationInterfaces;
31
32/** Exception thrown when a path is not found in the object list. */
33struct NotFoundException final : public sdbusplus::exception_t
34{
35 const char* name() const noexcept override
36 {
37 return "org.freedesktop.DBus.Error.FileNotFound";
38 };
39 const char* description() const noexcept override
40 {
41 return "path or object not found";
42 };
43 const char* what() const noexcept override
44 {
45 return "org.freedesktop.DBus.Error.FileNotFound: "
46 "The requested object was not found";
47 };
48};
49
50bool get_well_known(
51 boost::container::flat_map<std::string, std::string>& owners,
52 const std::string& request, std::string& well_known)
53{
54 // If it's already a well known name, just return
55 if (!boost::starts_with(request, ":"))
56 {
57 well_known = request;
58 return true;
59 }
60
61 auto it = owners.find(request);
62 if (it == owners.end())
63 {
64 return false;
65 }
66 well_known = it->second;
67 return true;
68}
69
70void update_owners(sdbusplus::asio::connection* conn,
71 boost::container::flat_map<std::string, std::string>& owners,
72 const std::string& new_object)
73{
74 if (boost::starts_with(new_object, ":"))
75 {
76 return;
77 }
78 conn->async_method_call(
79 [&, new_object](const boost::system::error_code ec,
80 const std::string& nameOwner) {
81 if (ec)
82 {
83 std::cerr << "Error getting owner of " << new_object << " : "
84 << ec << "\n";
85 return;
86 }
87 owners[nameOwner] = new_object;
88 },
89 "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetNameOwner",
90 new_object);
91}
92
93void send_introspection_complete_signal(sdbusplus::asio::connection* system_bus,
94 const std::string& process_name)
95{
96 // TODO(ed) This signal doesn't get exposed properly in the
97 // introspect right now. Find out how to register signals in
98 // sdbusplus
99 sdbusplus::message::message m = system_bus->new_signal(
100 "/xyz/openbmc_project/object_mapper",
101 "xyz.openbmc_project.ObjectMapper.Private", "IntrospectionComplete");
102 m.append(process_name);
103 m.signal_send();
104}
105
106struct InProgressIntrospect
107{
108 InProgressIntrospect(
109 sdbusplus::asio::connection* system_bus, boost::asio::io_service& io,
110 const std::string& process_name,
111 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
112 global_start_time) :
113 system_bus(system_bus),
114 io(io), process_name(process_name),
115 global_start_time(global_start_time),
116 process_start_time(std::chrono::steady_clock::now())
117 {
118 }
119 ~InProgressIntrospect()
120 {
121 send_introspection_complete_signal(system_bus, process_name);
122 std::chrono::duration<float> diff =
123 std::chrono::steady_clock::now() - process_start_time;
124 std::cout << std::setw(50) << process_name << " scan took "
125 << diff.count() << " seconds\n";
126
127 // If we're the last outstanding caller globally, calculate the
128 // time it took
129 if (global_start_time != nullptr && global_start_time.use_count() == 1)
130 {
131 diff = std::chrono::steady_clock::now() - *global_start_time;
132 std::cout << "Total scan took " << diff.count()
133 << " seconds to complete\n";
134 }
135 }
136 sdbusplus::asio::connection* system_bus;
137 boost::asio::io_service& io;
138 std::string process_name;
139
140 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
141 global_start_time;
142 std::chrono::time_point<std::chrono::steady_clock> process_start_time;
143};
144
145static const boost::container::flat_set<std::string> ignored_interfaces{
146 "org.freedesktop.DBus.Introspectable", "org.freedesktop.DBus.Peer",
147 "org.freedesktop.DBus.Properties"};
148
149void do_getmanagedobjects(sdbusplus::asio::connection* system_bus,
150 std::shared_ptr<InProgressIntrospect> transaction,
151 interface_map_type& interface_map, std::string path)
152{
153 // note, the variant type doesn't matter, as we don't actually track
154 // property names as of yet. variant<bool> seemed like the most simple.
155 using ManagedObjectType = std::vector<std::pair<
156 sdbusplus::message::object_path,
157 boost::container::flat_map<
158 std::string, boost::container::flat_map<
159 std::string, sdbusplus::message::variant<bool>>>>>;
160
161 system_bus->async_method_call(
162 [&interface_map, system_bus, transaction,
163 path](const boost::system::error_code ec,
164 const ManagedObjectType& objects) {
165 if (ec)
166 {
167 std::cerr << "GetMangedObjects call failed" << ec << "\n";
168 return;
169 }
170
171 interface_map.reserve(interface_map.size() + objects.size());
172 for (const std::pair<
173 sdbusplus::message::object_path,
174 boost::container::flat_map<
175 std::string,
176 boost::container::flat_map<
177 std::string, sdbusplus::message::variant<bool>>>>&
178 object : objects)
179 {
180 const std::string& path_name = object.first.str;
181 auto& this_path_map =
182 interface_map[path_name][transaction->process_name];
183 for (auto& interface_it : object.second)
184 {
185 this_path_map.insert(interface_it.first);
186 }
187 }
188 },
189 transaction->process_name, path, "org.freedesktop.DBus.ObjectManager",
190 "GetManagedObjects");
191}
192
193void addAssociation(sdbusplus::asio::object_server& objectServer,
194 const std::vector<Association>& associations,
195 const std::string& path)
196{
197 boost::container::flat_map<std::string,
198 boost::container::flat_set<std::string>>
199 objects;
200 for (const Association& association : associations)
201 {
202 std::string forward;
203 std::string reverse;
204 std::string endpoint;
205 std::tie(forward, reverse, endpoint) = association;
206
207 if (forward.size())
208 {
209 objects[path + "/" + forward].emplace(endpoint);
210 }
211 if (reverse.size())
212 {
213 if (endpoint.empty())
214 {
215 std::cerr << "Found invalid association on path " << path
216 << "\n";
217 continue;
218 }
219 objects[endpoint + "/" + reverse].emplace(path);
220 }
221 }
222 for (const auto& object : objects)
223 {
224 // the mapper exposes the new association interface but intakes
225 // the old
226
227 auto& iface = associationInterfaces[object.first];
228 iface = objectServer.add_interface(object.first,
229 "xyz.openbmc_project.Association");
230 iface->register_property("endpoints",
231 std::vector<std::string>(object.second.begin(),
232 object.second.end()));
233 iface->initialize();
234 }
235}
236
237void do_associations(sdbusplus::asio::connection* system_bus,
238 sdbusplus::asio::object_server& objectServer,
239 const std::string& processName, const std::string& path)
240{
241 system_bus->async_method_call(
242 [&objectServer,
243 path](const boost::system::error_code ec,
244 const sdbusplus::message::variant<std::vector<Association>>&
245 variantAssociations) {
246 if (ec)
247 {
248 std::cerr << "Error getting associations from " << path << "\n";
249 }
250 std::vector<Association> associations =
251 sdbusplus::message::variant_ns::get<std::vector<Association>>(
252 variantAssociations);
253 addAssociation(objectServer, associations, path);
254 },
255 processName, path, "org.freedesktop.DBus.Properties", "Get",
256 ASSOCIATIONS_INTERFACE, "associations");
257}
258
259void do_introspect(sdbusplus::asio::connection* system_bus,
260 std::shared_ptr<InProgressIntrospect> transaction,
261 interface_map_type& interface_map,
262 sdbusplus::asio::object_server& objectServer,
263 std::string path)
264{
265 system_bus->async_method_call(
266 [&interface_map, &objectServer, transaction, path,
267 system_bus](const boost::system::error_code ec,
268 const std::string& introspect_xml) {
269 if (ec)
270 {
271 std::cerr << "Introspect call failed with error: " << ec << ", "
272 << ec.message()
273 << " on process: " << transaction->process_name
274 << " path: " << path << "\n";
275 return;
276 }
277
278 tinyxml2::XMLDocument doc;
279
280 tinyxml2::XMLError e = doc.Parse(introspect_xml.c_str());
281 if (e != tinyxml2::XMLError::XML_SUCCESS)
282 {
283 std::cerr << "XML parsing failed\n";
284 return;
285 }
286
287 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
288 if (pRoot == nullptr)
289 {
290 std::cerr << "XML document did not contain any data\n";
291 return;
292 }
293 auto& thisPathMap = interface_map[path];
294 bool handling_via_objectmanager = false;
295 tinyxml2::XMLElement* pElement =
296 pRoot->FirstChildElement("interface");
297 while (pElement != nullptr)
298 {
299 const char* iface_name = pElement->Attribute("name");
300 if (iface_name == nullptr)
301 {
302 continue;
303 }
304
305 if (ignored_interfaces.find(std::string(iface_name)) ==
306 ignored_interfaces.end())
307 {
308 thisPathMap[transaction->process_name].emplace(iface_name);
309 }
310 if (std::strcmp(iface_name, ASSOCIATIONS_INTERFACE) == 0)
311 {
312 do_associations(system_bus, objectServer,
313 transaction->process_name, path);
314 }
315 else if (std::strcmp(iface_name,
316 "org.freedesktop.DBus.ObjectManager") == 0)
317 {
318 // TODO(ed) in the current implementation,
319 // introspect is actually faster than
320 // getmanagedObjects, but I suspect it will be
321 // faster when needing to deal with
322 // associations, so leave the code here for now
323
324 // handling_via_objectmanager = true;
325 // do_getmanagedobjects(system_bus, transaction,
326 // interface_map, path);
327 }
328
329 pElement = pElement->NextSiblingElement("interface");
330 }
331
332 if (!handling_via_objectmanager)
333 {
334 pElement = pRoot->FirstChildElement("node");
335 while (pElement != nullptr)
336 {
337 const char* child_path = pElement->Attribute("name");
338 if (child_path != nullptr)
339 {
340 std::string parent_path(path);
341 if (parent_path == "/")
342 {
343 parent_path.clear();
344 }
345
346 do_introspect(system_bus, transaction, interface_map,
347 objectServer,
348 parent_path + "/" + child_path);
349 }
350 pElement = pElement->NextSiblingElement("node");
351 }
352 }
353 },
354 transaction->process_name, path, "org.freedesktop.DBus.Introspectable",
355 "Introspect");
356}
357
358bool need_to_introspect(const std::string& process_name)
359{
360 return boost::starts_with(process_name, "xyz.openbmc_project.") ||
361 boost::starts_with(process_name, "org.openbmc.") ||
362 boost::starts_with(process_name, "com.intel.");
363}
364
365void start_new_introspect(
366 sdbusplus::asio::connection* system_bus, boost::asio::io_service& io,
367 interface_map_type& interface_map, const std::string& process_name,
368 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
369 global_start_time,
370 sdbusplus::asio::object_server& objectServer)
371{
372 if (need_to_introspect(process_name))
373 {
374
375 std::cerr << "starting introspect on " << process_name << "\n";
376 std::shared_ptr<InProgressIntrospect> transaction =
377 std::make_shared<InProgressIntrospect>(system_bus, io, process_name,
378 global_start_time);
379
380 do_introspect(system_bus, transaction, interface_map, objectServer,
381 "/");
382 }
383}
384
385// TODO(ed) replace with std::set_intersection once c++17 is available
386template <class InputIt1, class InputIt2>
387bool intersect(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
388{
389 while (first1 != last1 && first2 != last2)
390 {
391 if (*first1 < *first2)
392 {
393 ++first1;
394 continue;
395 }
396 if (*first2 < *first1)
397 {
398 ++first2;
399 continue;
400 }
401 return true;
402 }
403 return false;
404}
405
406void doListNames(
407 boost::asio::io_service& io, interface_map_type& interface_map,
408 sdbusplus::asio::connection* system_bus,
409 boost::container::flat_map<std::string, std::string>& name_owners,
410 sdbusplus::asio::object_server& objectServer)
411{
412 system_bus->async_method_call(
413 [&io, &interface_map, &name_owners, &objectServer,
414 system_bus](const boost::system::error_code ec,
415 std::vector<std::string> process_names) {
416 if (ec)
417 {
418 std::cerr << "Error getting names: " << ec << "\n";
419 std::exit(EXIT_FAILURE);
420 return;
421 }
422 std::cerr << "ListNames returned " << process_names.size()
423 << " entries\n";
424 // Try to make startup consistent
425 std::sort(process_names.begin(), process_names.end());
426 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
427 global_start_time = std::make_shared<
428 std::chrono::time_point<std::chrono::steady_clock>>(
429 std::chrono::steady_clock::now());
430 for (const std::string& process_name : process_names)
431 {
432 if (need_to_introspect(process_name))
433 {
434 start_new_introspect(system_bus, io, interface_map,
435 process_name, global_start_time,
436 objectServer);
437 update_owners(system_bus, name_owners, process_name);
438 }
439 }
440 },
441 "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
442 "ListNames");
443}
444
445int main(int argc, char** argv)
446{
447 std::cerr << "started\n";
448 boost::asio::io_service io;
449 std::shared_ptr<sdbusplus::asio::connection> system_bus =
450 std::make_shared<sdbusplus::asio::connection>(io);
451
452 system_bus->request_name(OBJECT_MAPPER_DBUS_NAME);
453 sdbusplus::asio::object_server server(system_bus);
454
455 // Construct a signal set registered for process termination.
456 boost::asio::signal_set signals(io, SIGINT, SIGTERM);
457 signals.async_wait([&io](const boost::system::error_code& error,
458 int signal_number) { io.stop(); });
459
460 interface_map_type interface_map;
461 boost::container::flat_map<std::string, std::string> name_owners;
462
463 std::function<void(sdbusplus::message::message & message)>
464 nameChangeHandler = [&interface_map, &io, &name_owners, &server,
465 system_bus](sdbusplus::message::message& message) {
466 std::string name;
467 std::string old_owner;
468 std::string new_owner;
469
470 message.read(name, old_owner, new_owner);
471
472 if (!old_owner.empty())
473 {
474 if (boost::starts_with(old_owner, ":"))
475 {
476 auto it = name_owners.find(old_owner);
477 if (it != name_owners.end())
478 {
479 name_owners.erase(it);
480 }
481 }
482 // Connection removed
483 interface_map_type::iterator path_it = interface_map.begin();
484 while (path_it != interface_map.end())
485 {
486 path_it->second.erase(name);
487 if (path_it->second.empty())
488 {
489 // If the last connection to the object is gone,
490 // delete the top level object
491 path_it = interface_map.erase(path_it);
492 continue;
493 }
494 path_it++;
495 }
496 }
497
498 if (!new_owner.empty())
499 {
500 auto transaction = std::make_shared<
501 std::chrono::time_point<std::chrono::steady_clock>>(
502 std::chrono::steady_clock::now());
503 // New daemon added
504 if (need_to_introspect(name))
505 {
506 name_owners[new_owner] = name;
507 start_new_introspect(system_bus.get(), io, interface_map,
508 name, transaction, server);
509 }
510 }
511 };
512
513 sdbusplus::bus::match::match nameOwnerChanged(
514 static_cast<sdbusplus::bus::bus&>(*system_bus),
515 sdbusplus::bus::match::rules::nameOwnerChanged(), nameChangeHandler);
516
517 std::function<void(sdbusplus::message::message & message)>
518 interfacesAddedHandler = [&interface_map, &name_owners, &server](
519 sdbusplus::message::message& message) {
520 sdbusplus::message::object_path obj_path;
521 std::vector<std::pair<
522 std::string, std::vector<std::pair<
523 std::string, sdbusplus::message::variant<
524 std::vector<Association>>>>>>
525 interfaces_added;
526 message.read(obj_path, interfaces_added);
527 std::string well_known;
528 if (!get_well_known(name_owners, message.get_sender(), well_known))
529 {
530 return; // only introspect well-known
531 }
532 if (need_to_introspect(well_known))
533 {
534 auto& iface_list = interface_map[obj_path.str];
535
536 for (const std::pair<
537 std::string,
538 std::vector<std::pair<std::string,
539 sdbusplus::message::variant<
540 std::vector<Association>>>>>&
541 interface_pair : interfaces_added)
542 {
543 iface_list[well_known].emplace(interface_pair.first);
544
545 if (interface_pair.first == ASSOCIATIONS_INTERFACE)
546 {
547 const sdbusplus::message::variant<
548 std::vector<Association>>* variantAssociations =
549 nullptr;
550 for (const auto& interface : interface_pair.second)
551 {
552 if (interface.first == "associations")
553 {
554 variantAssociations = &(interface.second);
555 }
556 }
557 if (variantAssociations == nullptr)
558 {
559 std::cerr << "Illegal association found on "
560 << well_known << "\n";
561 continue;
562 }
563 std::vector<Association> associations =
564 sdbusplus::message::variant_ns::get<
565 std::vector<Association>>(*variantAssociations);
566 addAssociation(server, associations, obj_path.str);
567 }
568 }
569 }
570 };
571
572 sdbusplus::bus::match::match interfacesAdded(
573 static_cast<sdbusplus::bus::bus&>(*system_bus),
574 sdbusplus::bus::match::rules::interfacesAdded(),
575 interfacesAddedHandler);
576
577 std::function<void(sdbusplus::message::message & message)>
578 interfacesRemovedHandler = [&interface_map, &name_owners, &server](
579 sdbusplus::message::message& message) {
580 sdbusplus::message::object_path obj_path;
581 std::vector<std::string> interfaces_removed;
582 message.read(obj_path, interfaces_removed);
583 auto connection_map = interface_map.find(obj_path.str);
584 if (connection_map == interface_map.end())
585 {
586 return;
587 }
588
589 std::string sender;
590 if (!get_well_known(name_owners, message.get_sender(), sender))
591 {
592 return;
593 }
594 for (const std::string& interface : interfaces_removed)
595 {
596 auto interface_set = connection_map->second.find(sender);
597 if (interface_set == connection_map->second.end())
598 {
599 continue;
600 }
601
602 if (interface == ASSOCIATIONS_INTERFACE)
603 {
604 auto findAssociation =
605 associationInterfaces.find(interface);
606 if (findAssociation != associationInterfaces.end())
607 {
608 server.remove_interface(findAssociation->second);
609 findAssociation->second = nullptr;
610 }
611 }
612
613 interface_set->second.erase(interface);
614 // If this was the last interface on this connection,
615 // erase the connection
616 if (interface_set->second.empty())
617 {
618 connection_map->second.erase(interface_set);
619 }
620 }
621 // If this was the last connection on this object path,
622 // erase the object path
623 if (connection_map->second.empty())
624 {
625 interface_map.erase(connection_map);
626 }
627 };
628
629 sdbusplus::bus::match::match interfacesRemoved(
630 static_cast<sdbusplus::bus::bus&>(*system_bus),
631 sdbusplus::bus::match::rules::interfacesRemoved(),
632 interfacesRemovedHandler);
633
634 std::function<void(sdbusplus::message::message & message)>
635 associationChangedHandler =
636 [&server](sdbusplus::message::message& message) {
637 std::string objectName;
638 boost::container::flat_map<
639 std::string,
640 sdbusplus::message::variant<std::vector<Association>>>
641 values;
642 message.read(objectName, values);
643 auto findAssociations = values.find("associations");
644 if (findAssociations != values.end())
645 {
646 std::vector<Association> associations =
647 sdbusplus::message::variant_ns::get<
648 std::vector<Association>>(findAssociations->second);
649 addAssociation(server, associations, message.get_path());
650 }
651 };
652 sdbusplus::bus::match::match associationChanged(
653 static_cast<sdbusplus::bus::bus&>(*system_bus),
654 sdbusplus::bus::match::rules::interface(
655 "org.freedesktop.DBus.Properties") +
656 sdbusplus::bus::match::rules::member("PropertiesChanged") +
657 sdbusplus::bus::match::rules::argN(0, ASSOCIATIONS_INTERFACE),
658 associationChangedHandler);
659
660 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
661 server.add_interface("/xyz/openbmc_project/object_mapper",
662 "xyz.openbmc_project.ObjectMapper");
663
664 iface->register_method(
665 "GetAncestors", [&interface_map](const std::string& req_path,
666 std::vector<std::string>& interfaces) {
667 // Interfaces need to be sorted for intersect to function
668 std::sort(interfaces.begin(), interfaces.end());
669
670 std::vector<interface_map_type::value_type> ret;
671 for (auto& object_path : interface_map)
672 {
673 auto& this_path = object_path.first;
674 if (boost::starts_with(req_path, this_path))
675 {
676 if (interfaces.empty())
677 {
678 ret.emplace_back(object_path);
679 }
680 else
681 {
682 for (auto& interface_map : object_path.second)
683 {
684
685 if (intersect(interfaces.begin(), interfaces.end(),
686 interface_map.second.begin(),
687 interface_map.second.end()))
688 {
689 ret.emplace_back(object_path);
690 break;
691 }
692 }
693 }
694 }
695 }
696 if (ret.empty())
697 {
698 throw NotFoundException();
699 }
700
701 return ret;
702 });
703
704 iface->register_method(
705 "GetObject", [&interface_map](const std::string& path,
706 std::vector<std::string>& interfaces) {
707 // Interfaces need to be sorted for intersect to function
708 std::sort(interfaces.begin(), interfaces.end());
709 auto path_ref = interface_map.find(path);
710 if (path_ref == interface_map.end())
711 {
712 throw NotFoundException();
713 }
714 if (interfaces.empty())
715 {
716 return path_ref->second;
717 }
718 for (auto& interface_map : path_ref->second)
719 {
720 if (intersect(interfaces.begin(), interfaces.end(),
721 interface_map.second.begin(),
722 interface_map.second.end()))
723 {
724 return path_ref->second;
725 }
726 }
727 // Unable to find intersection, return default constructed
728 // object
729 throw NotFoundException();
730 });
731
732 iface->register_method(
733 "GetSubTree",
734 [&interface_map](const std::string& req_path, int32_t depth,
735 std::vector<std::string>& interfaces) {
736 if (depth <= 0)
737 {
738 depth = std::numeric_limits<int32_t>::max();
739 }
740 // Interfaces need to be sorted for intersect to function
741 std::sort(interfaces.begin(), interfaces.end());
742 std::vector<interface_map_type::value_type> ret;
743
744 for (auto& object_path : interface_map)
745 {
746 auto& this_path = object_path.first;
747 if (boost::starts_with(this_path, req_path))
748 {
749 // count the number of slashes past the search term
750 int32_t this_depth =
751 std::count(this_path.begin() + req_path.size(),
752 this_path.end(), '/');
753 if (this_depth <= depth)
754 {
755 bool add = interfaces.empty();
756 for (auto& interface_map : object_path.second)
757 {
758 if (intersect(interfaces.begin(), interfaces.end(),
759 interface_map.second.begin(),
760 interface_map.second.end()))
761 {
762 add = true;
763 break;
764 }
765 }
766 if (add)
767 {
768 // todo(ed) this is a copy
769 ret.emplace_back(object_path);
770 }
771 }
772 }
773 }
774 if (ret.empty())
775 {
776 throw NotFoundException();
777 }
778 return ret;
779 });
780
781 iface->register_method(
782 "GetSubTreePaths",
783 [&interface_map](const std::string& req_path, int32_t depth,
784 std::vector<std::string>& interfaces) {
785 if (depth <= 0)
786 {
787 depth = std::numeric_limits<int32_t>::max();
788 }
789 // Interfaces need to be sorted for intersect to function
790 std::sort(interfaces.begin(), interfaces.end());
791 std::vector<std::string> ret;
792 for (auto& object_path : interface_map)
793 {
794 auto& this_path = object_path.first;
795 if (boost::starts_with(this_path, req_path))
796 {
797 // count the number of slashes past the search term
798 int this_depth =
799 std::count(this_path.begin() + req_path.size(),
800 this_path.end(), '/');
801 if (this_depth <= depth)
802 {
803 bool add = interfaces.empty();
804 for (auto& interface_map : object_path.second)
805 {
806 if (intersect(interfaces.begin(), interfaces.end(),
807 interface_map.second.begin(),
808 interface_map.second.end()))
809 {
810 add = true;
811 break;
812 }
813 }
814 if (add)
815 {
816 // TODO(ed) this is a copy
817 ret.emplace_back(this_path);
818 }
819 }
820 }
821 }
822 if (ret.empty())
823 {
824 throw NotFoundException();
825 }
826 return ret;
827 });
828
829 iface->initialize();
830
831 io.post([&]() {
832 doListNames(io, interface_map, system_bus.get(), name_owners, server);
833 });
834
835 std::cerr << "starting event loop\n";
836 io.run();
837}