blob: 5870aeac87d41b5204978717795ae714b68a919e [file] [log] [blame]
Andrew Geissler3b025e62019-02-01 10:33:54 -06001#include "processing.hpp"
Matt Spinlerdd945862018-09-07 12:41:05 -05002#include "src/argument.hpp"
3
Ed Tanous60520632018-06-11 17:46:52 -07004#include <tinyxml2.h>
5
6#include <atomic>
7#include <boost/algorithm/string/predicate.hpp>
8#include <boost/container/flat_map.hpp>
9#include <boost/container/flat_set.hpp>
10#include <chrono>
11#include <iomanip>
12#include <iostream>
13#include <sdbusplus/asio/connection.hpp>
14#include <sdbusplus/asio/object_server.hpp>
15
16constexpr const char* OBJECT_MAPPER_DBUS_NAME =
17 "xyz.openbmc_project.ObjectMapper";
18constexpr const char* ASSOCIATIONS_INTERFACE = "org.openbmc.Associations";
Matt Spinler1036b4d2018-09-18 16:45:29 -050019constexpr const char* XYZ_ASSOCIATION_INTERFACE =
20 "xyz.openbmc_project.Association";
Ed Tanous60520632018-06-11 17:46:52 -070021
22// interface_map_type is the underlying datastructure the mapper uses.
23// The 3 levels of map are
24// object paths
25// connection names
26// interface names
27using interface_map_type = boost::container::flat_map<
28 std::string, boost::container::flat_map<
29 std::string, boost::container::flat_set<std::string>>>;
30
31using Association = std::tuple<std::string, std::string, std::string>;
32
Matt Spinler1036b4d2018-09-18 16:45:29 -050033// Associations and some metadata are stored in associationInterfaces.
34// The fields are:
35// * ifacePos - holds the D-Bus interface object
Matt Spinler937a2322019-01-23 13:54:22 -060036// * endpointsPos - holds the endpoints array that shadows the property
Matt Spinler1036b4d2018-09-18 16:45:29 -050037static constexpr auto ifacePos = 0;
38static constexpr auto endpointsPos = 1;
Matt Spinler1036b4d2018-09-18 16:45:29 -050039using Endpoints = std::vector<std::string>;
40boost::container::flat_map<
Matt Spinler937a2322019-01-23 13:54:22 -060041 std::string,
42 std::tuple<std::shared_ptr<sdbusplus::asio::dbus_interface>, Endpoints>>
Ed Tanous60520632018-06-11 17:46:52 -070043 associationInterfaces;
44
Matt Spinler937a2322019-01-23 13:54:22 -060045// The associationOwners map contains information about creators of
46// associations, so that when a org.openbmc.Association interface is
47// removed or its 'associations' property is changed, the mapper owned
48// association objects can be correctly handled. It is a map of the
49// object path of the org.openbmc.Association owner to a map of the
50// service the path is owned by, to a map of the association objects to
51// their endpoint paths:
52// map[ownerPath : map[service : map[assocPath : [endpoint paths]]]
53// For example:
54// [/logging/entry/1 :
55// [xyz.openbmc_project.Logging :
56// [/logging/entry/1/callout : [/system/cpu0],
57// /system/cpu0/fault : [/logging/entry/1]]]]
58
59using AssociationPaths =
60 boost::container::flat_map<std::string,
61 boost::container::flat_set<std::string>>;
62
63using AssociationOwnersType = boost::container::flat_map<
64 std::string, boost::container::flat_map<std::string, AssociationPaths>>;
65
66AssociationOwnersType associationOwners;
67
Matt Spinlerdd945862018-09-07 12:41:05 -050068static boost::container::flat_set<std::string> service_whitelist;
69static boost::container::flat_set<std::string> service_blacklist;
Matt Spinlerdd945862018-09-07 12:41:05 -050070
Ed Tanous60520632018-06-11 17:46:52 -070071/** Exception thrown when a path is not found in the object list. */
72struct NotFoundException final : public sdbusplus::exception_t
73{
74 const char* name() const noexcept override
75 {
76 return "org.freedesktop.DBus.Error.FileNotFound";
77 };
78 const char* description() const noexcept override
79 {
80 return "path or object not found";
81 };
82 const char* what() const noexcept override
83 {
84 return "org.freedesktop.DBus.Error.FileNotFound: "
85 "The requested object was not found";
86 };
87};
88
Ed Tanous60520632018-06-11 17:46:52 -070089void update_owners(sdbusplus::asio::connection* conn,
90 boost::container::flat_map<std::string, std::string>& owners,
91 const std::string& new_object)
92{
93 if (boost::starts_with(new_object, ":"))
94 {
95 return;
96 }
97 conn->async_method_call(
98 [&, new_object](const boost::system::error_code ec,
99 const std::string& nameOwner) {
100 if (ec)
101 {
102 std::cerr << "Error getting owner of " << new_object << " : "
103 << ec << "\n";
104 return;
105 }
106 owners[nameOwner] = new_object;
107 },
108 "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetNameOwner",
109 new_object);
110}
111
112void send_introspection_complete_signal(sdbusplus::asio::connection* system_bus,
113 const std::string& process_name)
114{
115 // TODO(ed) This signal doesn't get exposed properly in the
116 // introspect right now. Find out how to register signals in
117 // sdbusplus
118 sdbusplus::message::message m = system_bus->new_signal(
119 "/xyz/openbmc_project/object_mapper",
120 "xyz.openbmc_project.ObjectMapper.Private", "IntrospectionComplete");
121 m.append(process_name);
122 m.signal_send();
123}
124
125struct InProgressIntrospect
126{
127 InProgressIntrospect(
128 sdbusplus::asio::connection* system_bus, boost::asio::io_service& io,
Matt Spinleraecabe82018-09-19 13:25:42 -0500129 const std::string& process_name
130#ifdef DEBUG
131 ,
Ed Tanous60520632018-06-11 17:46:52 -0700132 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
Matt Spinleraecabe82018-09-19 13:25:42 -0500133 global_start_time
134#endif
135 ) :
Ed Tanous60520632018-06-11 17:46:52 -0700136 system_bus(system_bus),
Matt Spinleraecabe82018-09-19 13:25:42 -0500137 io(io), process_name(process_name)
138#ifdef DEBUG
139 ,
Ed Tanous60520632018-06-11 17:46:52 -0700140 global_start_time(global_start_time),
141 process_start_time(std::chrono::steady_clock::now())
Matt Spinleraecabe82018-09-19 13:25:42 -0500142#endif
Ed Tanous60520632018-06-11 17:46:52 -0700143 {
144 }
145 ~InProgressIntrospect()
146 {
147 send_introspection_complete_signal(system_bus, process_name);
Matt Spinleraecabe82018-09-19 13:25:42 -0500148
149#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700150 std::chrono::duration<float> diff =
151 std::chrono::steady_clock::now() - process_start_time;
152 std::cout << std::setw(50) << process_name << " scan took "
153 << diff.count() << " seconds\n";
154
155 // If we're the last outstanding caller globally, calculate the
156 // time it took
157 if (global_start_time != nullptr && global_start_time.use_count() == 1)
158 {
159 diff = std::chrono::steady_clock::now() - *global_start_time;
160 std::cout << "Total scan took " << diff.count()
161 << " seconds to complete\n";
162 }
Matt Spinleraecabe82018-09-19 13:25:42 -0500163#endif
Ed Tanous60520632018-06-11 17:46:52 -0700164 }
165 sdbusplus::asio::connection* system_bus;
166 boost::asio::io_service& io;
167 std::string process_name;
Matt Spinleraecabe82018-09-19 13:25:42 -0500168#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700169 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
170 global_start_time;
171 std::chrono::time_point<std::chrono::steady_clock> process_start_time;
Matt Spinleraecabe82018-09-19 13:25:42 -0500172#endif
Ed Tanous60520632018-06-11 17:46:52 -0700173};
174
Matt Spinler09be5762019-01-23 14:28:17 -0600175// Remove paths from the endpoints property of an association.
176// If the last endpoint was removed, then remove the whole
177// association object, otherwise just set the property.
178void removeAssociationEndpoints(
179 sdbusplus::asio::object_server& objectServer, const std::string& assocPath,
180 const std::string& owner,
181 const boost::container::flat_set<std::string>& endpointsToRemove)
182{
183 auto assoc = associationInterfaces.find(assocPath);
184 if (assoc == associationInterfaces.end())
185 {
186 return;
187 }
188
189 auto& endpointsInDBus = std::get<endpointsPos>(assoc->second);
190
191 for (const auto& endpointToRemove : endpointsToRemove)
192 {
193 auto e = std::find(endpointsInDBus.begin(), endpointsInDBus.end(),
194 endpointToRemove);
195
196 if (e != endpointsInDBus.end())
197 {
198 endpointsInDBus.erase(e);
199 }
200 }
201
202 if (endpointsInDBus.empty())
203 {
204 objectServer.remove_interface(std::get<ifacePos>(assoc->second));
205 std::get<ifacePos>(assoc->second) = nullptr;
206 std::get<endpointsPos>(assoc->second).clear();
207 }
208 else
209 {
210 std::get<ifacePos>(assoc->second)
211 ->set_property("endpoints", endpointsInDBus);
212 }
213}
214
215// Based on the latest values of the org.openbmc.Associations.associations
216// property, passed in via the newAssociations param, check if any of the
217// paths in the xyz.openbmc_project.Association.endpoints D-Bus property
218// for that association need to be removed. If the last path is removed
219// from the endpoints property, remove that whole association object from
220// D-Bus.
221void checkAssociationEndpointRemoves(
222 const std::string& sourcePath, const std::string& owner,
223 const AssociationPaths& newAssociations,
224 sdbusplus::asio::object_server& objectServer)
225{
226 // Find the services that have associations on this path.
227 auto originalOwners = associationOwners.find(sourcePath);
228 if (originalOwners == associationOwners.end())
229 {
230 return;
231 }
232
233 // Find the associations for this service
234 auto originalAssociations = originalOwners->second.find(owner);
235 if (originalAssociations == originalOwners->second.end())
236 {
237 return;
238 }
239
240 // Compare the new endpoints versus the original endpoints, and
241 // remove any of the original ones that aren't in the new list.
242 for (const auto& [originalAssocPath, originalEndpoints] :
243 originalAssociations->second)
244 {
245 // Check if this source even still has each association that
246 // was there previously, and if not, remove all of its endpoints
247 // from the D-Bus endpoints property which will cause the whole
248 // association path to be removed if no endpoints remain.
249 auto newEndpoints = newAssociations.find(originalAssocPath);
250 if (newEndpoints == newAssociations.end())
251 {
252 removeAssociationEndpoints(objectServer, originalAssocPath, owner,
253 originalEndpoints);
254 }
255 else
256 {
257 // The association is still there. Check if the endpoints
258 // changed.
259 boost::container::flat_set<std::string> toRemove;
260
261 for (auto& originalEndpoint : originalEndpoints)
262 {
263 if (std::find(newEndpoints->second.begin(),
264 newEndpoints->second.end(),
265 originalEndpoint) == newEndpoints->second.end())
266 {
267 toRemove.emplace(originalEndpoint);
268 }
269 }
270 if (!toRemove.empty())
271 {
272 removeAssociationEndpoints(objectServer, originalAssocPath,
273 owner, toRemove);
274 }
275 }
276 }
277}
278
Matt Spinler937a2322019-01-23 13:54:22 -0600279// Called when either a new org.openbmc.Associations interface was
280// created, or the associations property on that interface changed.
Matt Spinler09be5762019-01-23 14:28:17 -0600281void associationChanged(sdbusplus::asio::object_server& objectServer,
282 const std::vector<Association>& associations,
283 const std::string& path, const std::string& owner)
Ed Tanous60520632018-06-11 17:46:52 -0700284{
Matt Spinler937a2322019-01-23 13:54:22 -0600285 AssociationPaths objects;
286
Ed Tanous60520632018-06-11 17:46:52 -0700287 for (const Association& association : associations)
288 {
289 std::string forward;
290 std::string reverse;
291 std::string endpoint;
292 std::tie(forward, reverse, endpoint) = association;
293
294 if (forward.size())
295 {
296 objects[path + "/" + forward].emplace(endpoint);
297 }
298 if (reverse.size())
299 {
300 if (endpoint.empty())
301 {
302 std::cerr << "Found invalid association on path " << path
303 << "\n";
304 continue;
305 }
306 objects[endpoint + "/" + reverse].emplace(path);
307 }
308 }
309 for (const auto& object : objects)
310 {
311 // the mapper exposes the new association interface but intakes
312 // the old
313
314 auto& iface = associationInterfaces[object.first];
Matt Spinler1036b4d2018-09-18 16:45:29 -0500315 auto& i = std::get<ifacePos>(iface);
316 auto& endpoints = std::get<endpointsPos>(iface);
Matt Spinler1036b4d2018-09-18 16:45:29 -0500317
318 // Only add new endpoints
319 for (auto& e : object.second)
320 {
321 if (std::find(endpoints.begin(), endpoints.end(), e) ==
322 endpoints.end())
323 {
324 endpoints.push_back(e);
325 }
326 }
327
328 // If the interface already exists, only need to update
329 // the property value, otherwise create it
330 if (i)
331 {
332 i->set_property("endpoints", endpoints);
333 }
334 else
335 {
336 i = objectServer.add_interface(object.first,
337 XYZ_ASSOCIATION_INTERFACE);
338 i->register_property("endpoints", endpoints);
339 i->initialize();
340 }
341 }
Matt Spinler1036b4d2018-09-18 16:45:29 -0500342
Matt Spinler09be5762019-01-23 14:28:17 -0600343 // Check for endpoints being removed instead of added
344 checkAssociationEndpointRemoves(path, owner, objects, objectServer);
345
Matt Spinler937a2322019-01-23 13:54:22 -0600346 // Update associationOwners with the latest info
347 auto a = associationOwners.find(path);
348 if (a != associationOwners.end())
Matt Spinler1036b4d2018-09-18 16:45:29 -0500349 {
Matt Spinler937a2322019-01-23 13:54:22 -0600350 auto o = a->second.find(owner);
351 if (o != a->second.end())
Matt Spinler1036b4d2018-09-18 16:45:29 -0500352 {
Matt Spinler937a2322019-01-23 13:54:22 -0600353 o->second = std::move(objects);
Matt Spinler1036b4d2018-09-18 16:45:29 -0500354 }
355 else
356 {
Matt Spinler937a2322019-01-23 13:54:22 -0600357 a->second.emplace(owner, std::move(objects));
358 }
359 }
360 else
361 {
362 boost::container::flat_map<std::string, AssociationPaths> owners;
363 owners.emplace(owner, std::move(objects));
364 associationOwners.emplace(path, owners);
365 }
366}
Matt Spinler1036b4d2018-09-18 16:45:29 -0500367
Matt Spinler937a2322019-01-23 13:54:22 -0600368void removeAssociation(const std::string& sourcePath, const std::string& owner,
369 sdbusplus::asio::object_server& server)
370{
371 // Use associationOwners to find the association paths and endpoints
372 // that the passed in object path and service own. Remove all of
373 // these endpoints from the actual association D-Bus objects, and if
374 // the endpoints property is then empty, the whole association object
375 // can be removed. Note there can be multiple services that own an
376 // association, and also that sourcePath is the path of the object
377 // that contains the org.openbmc.Associations interface and not the
378 // association path itself.
379
380 // Find the services that have associations for this object path
381 auto owners = associationOwners.find(sourcePath);
382 if (owners == associationOwners.end())
383 {
384 return;
385 }
386
387 // Find the association paths and endpoints owned by this object
388 // path for this service.
389 auto assocs = owners->second.find(owner);
390 if (assocs == owners->second.end())
391 {
392 return;
393 }
394
395 for (const auto& [assocPath, endpointsToRemove] : assocs->second)
396 {
397 // Get the association D-Bus object for this assocPath
398 auto target = associationInterfaces.find(assocPath);
399 if (target == associationInterfaces.end())
400 {
401 continue;
402 }
403
404 // Remove the entries in the endpoints D-Bus property for this
405 // path/owner/association-path.
406 auto& existingEndpoints = std::get<endpointsPos>(target->second);
407 for (const auto& endpointToRemove : endpointsToRemove)
408 {
409 auto e = std::find(existingEndpoints.begin(),
410 existingEndpoints.end(), endpointToRemove);
411
412 if (e != existingEndpoints.end())
413 {
414 existingEndpoints.erase(e);
Matt Spinler1036b4d2018-09-18 16:45:29 -0500415 }
416 }
Matt Spinler937a2322019-01-23 13:54:22 -0600417
418 // Remove the association from D-Bus if there are no more endpoints,
419 // otherwise just update the endpoints property.
420 if (existingEndpoints.empty())
421 {
422 server.remove_interface(std::get<ifacePos>(target->second));
423 std::get<ifacePos>(target->second) = nullptr;
424 std::get<endpointsPos>(target->second).clear();
425 }
426 else
427 {
428 std::get<ifacePos>(target->second)
429 ->set_property("endpoints", existingEndpoints);
430 }
431 }
432
433 // Remove the associationOwners entries for this owning path/service.
434 owners->second.erase(assocs);
435 if (owners->second.empty())
436 {
437 associationOwners.erase(owners);
Ed Tanous60520632018-06-11 17:46:52 -0700438 }
439}
440
441void do_associations(sdbusplus::asio::connection* system_bus,
442 sdbusplus::asio::object_server& objectServer,
443 const std::string& processName, const std::string& path)
444{
445 system_bus->async_method_call(
Matt Spinler937a2322019-01-23 13:54:22 -0600446 [&objectServer, path, processName](
447 const boost::system::error_code ec,
448 const sdbusplus::message::variant<std::vector<Association>>&
449 variantAssociations) {
Ed Tanous60520632018-06-11 17:46:52 -0700450 if (ec)
451 {
452 std::cerr << "Error getting associations from " << path << "\n";
453 }
454 std::vector<Association> associations =
455 sdbusplus::message::variant_ns::get<std::vector<Association>>(
456 variantAssociations);
Matt Spinler09be5762019-01-23 14:28:17 -0600457 associationChanged(objectServer, associations, path, processName);
Ed Tanous60520632018-06-11 17:46:52 -0700458 },
459 processName, path, "org.freedesktop.DBus.Properties", "Get",
460 ASSOCIATIONS_INTERFACE, "associations");
461}
462
463void do_introspect(sdbusplus::asio::connection* system_bus,
464 std::shared_ptr<InProgressIntrospect> transaction,
465 interface_map_type& interface_map,
466 sdbusplus::asio::object_server& objectServer,
467 std::string path)
468{
469 system_bus->async_method_call(
470 [&interface_map, &objectServer, transaction, path,
471 system_bus](const boost::system::error_code ec,
472 const std::string& introspect_xml) {
473 if (ec)
474 {
475 std::cerr << "Introspect call failed with error: " << ec << ", "
476 << ec.message()
477 << " on process: " << transaction->process_name
478 << " path: " << path << "\n";
479 return;
480 }
481
482 tinyxml2::XMLDocument doc;
483
484 tinyxml2::XMLError e = doc.Parse(introspect_xml.c_str());
485 if (e != tinyxml2::XMLError::XML_SUCCESS)
486 {
487 std::cerr << "XML parsing failed\n";
488 return;
489 }
490
491 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
492 if (pRoot == nullptr)
493 {
494 std::cerr << "XML document did not contain any data\n";
495 return;
496 }
497 auto& thisPathMap = interface_map[path];
Ed Tanous60520632018-06-11 17:46:52 -0700498 tinyxml2::XMLElement* pElement =
499 pRoot->FirstChildElement("interface");
500 while (pElement != nullptr)
501 {
502 const char* iface_name = pElement->Attribute("name");
503 if (iface_name == nullptr)
504 {
505 continue;
506 }
507
Matt Spinlerdd945862018-09-07 12:41:05 -0500508 std::string iface{iface_name};
509
Ed Tanousd4dd96a2018-11-12 11:37:44 -0800510 thisPathMap[transaction->process_name].emplace(iface_name);
511
Ed Tanous60520632018-06-11 17:46:52 -0700512 if (std::strcmp(iface_name, ASSOCIATIONS_INTERFACE) == 0)
513 {
514 do_associations(system_bus, objectServer,
515 transaction->process_name, path);
516 }
Ed Tanous60520632018-06-11 17:46:52 -0700517
518 pElement = pElement->NextSiblingElement("interface");
519 }
520
Ed Tanous50232cd2018-11-12 11:34:43 -0800521 pElement = pRoot->FirstChildElement("node");
522 while (pElement != nullptr)
Ed Tanous60520632018-06-11 17:46:52 -0700523 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800524 const char* child_path = pElement->Attribute("name");
525 if (child_path != nullptr)
Ed Tanous60520632018-06-11 17:46:52 -0700526 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800527 std::string parent_path(path);
528 if (parent_path == "/")
Ed Tanous60520632018-06-11 17:46:52 -0700529 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800530 parent_path.clear();
Ed Tanous60520632018-06-11 17:46:52 -0700531 }
Ed Tanous50232cd2018-11-12 11:34:43 -0800532
533 do_introspect(system_bus, transaction, interface_map,
534 objectServer, parent_path + "/" + child_path);
Ed Tanous60520632018-06-11 17:46:52 -0700535 }
Ed Tanous50232cd2018-11-12 11:34:43 -0800536 pElement = pElement->NextSiblingElement("node");
Ed Tanous60520632018-06-11 17:46:52 -0700537 }
538 },
539 transaction->process_name, path, "org.freedesktop.DBus.Introspectable",
540 "Introspect");
541}
542
543bool need_to_introspect(const std::string& process_name)
544{
Matt Spinlerdd945862018-09-07 12:41:05 -0500545 auto inWhitelist =
546 std::find_if(service_whitelist.begin(), service_whitelist.end(),
547 [&process_name](const auto& prefix) {
548 return boost::starts_with(process_name, prefix);
549 }) != service_whitelist.end();
550
551 // This holds full service names, not prefixes
552 auto inBlacklist =
553 service_blacklist.find(process_name) != service_blacklist.end();
554
555 return inWhitelist && !inBlacklist;
Ed Tanous60520632018-06-11 17:46:52 -0700556}
557
558void start_new_introspect(
559 sdbusplus::asio::connection* system_bus, boost::asio::io_service& io,
560 interface_map_type& interface_map, const std::string& process_name,
Matt Spinleraecabe82018-09-19 13:25:42 -0500561#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700562 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
563 global_start_time,
Matt Spinleraecabe82018-09-19 13:25:42 -0500564#endif
Ed Tanous60520632018-06-11 17:46:52 -0700565 sdbusplus::asio::object_server& objectServer)
566{
567 if (need_to_introspect(process_name))
568 {
Ed Tanous60520632018-06-11 17:46:52 -0700569 std::shared_ptr<InProgressIntrospect> transaction =
Matt Spinleraecabe82018-09-19 13:25:42 -0500570 std::make_shared<InProgressIntrospect>(system_bus, io, process_name
571#ifdef DEBUG
572 ,
573 global_start_time
574#endif
575 );
Ed Tanous60520632018-06-11 17:46:52 -0700576
577 do_introspect(system_bus, transaction, interface_map, objectServer,
578 "/");
579 }
580}
581
582// TODO(ed) replace with std::set_intersection once c++17 is available
583template <class InputIt1, class InputIt2>
584bool intersect(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
585{
586 while (first1 != last1 && first2 != last2)
587 {
588 if (*first1 < *first2)
589 {
590 ++first1;
591 continue;
592 }
593 if (*first2 < *first1)
594 {
595 ++first2;
596 continue;
597 }
598 return true;
599 }
600 return false;
601}
602
603void doListNames(
604 boost::asio::io_service& io, interface_map_type& interface_map,
605 sdbusplus::asio::connection* system_bus,
606 boost::container::flat_map<std::string, std::string>& name_owners,
607 sdbusplus::asio::object_server& objectServer)
608{
609 system_bus->async_method_call(
610 [&io, &interface_map, &name_owners, &objectServer,
611 system_bus](const boost::system::error_code ec,
612 std::vector<std::string> process_names) {
613 if (ec)
614 {
615 std::cerr << "Error getting names: " << ec << "\n";
616 std::exit(EXIT_FAILURE);
617 return;
618 }
Ed Tanous60520632018-06-11 17:46:52 -0700619 // Try to make startup consistent
620 std::sort(process_names.begin(), process_names.end());
Matt Spinleraecabe82018-09-19 13:25:42 -0500621#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700622 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
623 global_start_time = std::make_shared<
624 std::chrono::time_point<std::chrono::steady_clock>>(
625 std::chrono::steady_clock::now());
Matt Spinleraecabe82018-09-19 13:25:42 -0500626#endif
Ed Tanous60520632018-06-11 17:46:52 -0700627 for (const std::string& process_name : process_names)
628 {
629 if (need_to_introspect(process_name))
630 {
631 start_new_introspect(system_bus, io, interface_map,
Matt Spinleraecabe82018-09-19 13:25:42 -0500632 process_name,
633#ifdef DEBUG
634 global_start_time,
635#endif
Ed Tanous60520632018-06-11 17:46:52 -0700636 objectServer);
637 update_owners(system_bus, name_owners, process_name);
638 }
639 }
640 },
641 "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
642 "ListNames");
643}
644
Matt Spinlerdd945862018-09-07 12:41:05 -0500645void splitArgs(const std::string& stringArgs,
646 boost::container::flat_set<std::string>& listArgs)
647{
648 std::istringstream args;
649 std::string arg;
650
651 args.str(stringArgs);
652
653 while (!args.eof())
654 {
655 args >> arg;
656 if (!arg.empty())
657 {
658 listArgs.insert(arg);
659 }
660 }
661}
662
Matt Spinler47c09752018-11-29 14:54:13 -0600663void addObjectMapResult(
664 std::vector<interface_map_type::value_type>& objectMap,
Matt Spinler9f0958e2018-09-11 08:26:10 -0500665 const std::string& objectPath,
666 const std::pair<std::string, boost::container::flat_set<std::string>>&
667 interfaceMap)
668{
669 // Adds an object path/service name/interface list entry to
Matt Spinler47c09752018-11-29 14:54:13 -0600670 // the results of GetSubTree and GetAncestors.
Matt Spinler9f0958e2018-09-11 08:26:10 -0500671 // If an entry for the object path already exists, just add the
672 // service name and interfaces to that entry, otherwise create
673 // a new entry.
674 auto entry = std::find_if(
Matt Spinler47c09752018-11-29 14:54:13 -0600675 objectMap.begin(), objectMap.end(),
Matt Spinler9f0958e2018-09-11 08:26:10 -0500676 [&objectPath](const auto& i) { return objectPath == i.first; });
677
Matt Spinler47c09752018-11-29 14:54:13 -0600678 if (entry != objectMap.end())
Matt Spinler9f0958e2018-09-11 08:26:10 -0500679 {
680 entry->second.emplace(interfaceMap);
681 }
682 else
683 {
684 interface_map_type::value_type object;
685 object.first = objectPath;
686 object.second.emplace(interfaceMap);
Matt Spinler47c09752018-11-29 14:54:13 -0600687 objectMap.push_back(object);
Matt Spinler9f0958e2018-09-11 08:26:10 -0500688 }
689}
690
Matt Spinlera82779f2019-01-09 12:39:42 -0600691// Remove parents of the passed in path that:
692// 1) Only have the 3 default interfaces on them
693// - Means D-Bus created these, not application code,
694// with the Properties, Introspectable, and Peer ifaces
695// 2) Have no other child for this owner
696void removeUnneededParents(const std::string& objectPath,
697 const std::string& owner,
698 interface_map_type& interface_map)
699{
700 auto parent = objectPath;
701
702 while (true)
703 {
704 auto pos = parent.find_last_of('/');
705 if ((pos == std::string::npos) || (pos == 0))
706 {
707 break;
708 }
709 parent = parent.substr(0, pos);
710
711 auto parent_it = interface_map.find(parent);
712 if (parent_it == interface_map.end())
713 {
714 break;
715 }
716
717 auto ifaces_it = parent_it->second.find(owner);
718 if (ifaces_it == parent_it->second.end())
719 {
720 break;
721 }
722
723 if (ifaces_it->second.size() != 3)
724 {
725 break;
726 }
727
728 auto child_path = parent + '/';
729
730 // Remove this parent if there isn't a remaining child on this owner
731 auto child = std::find_if(
732 interface_map.begin(), interface_map.end(),
733 [&owner, &child_path](const auto& entry) {
734 return boost::starts_with(entry.first, child_path) &&
735 (entry.second.find(owner) != entry.second.end());
736 });
737
738 if (child == interface_map.end())
739 {
740 parent_it->second.erase(ifaces_it);
741 if (parent_it->second.empty())
742 {
743 interface_map.erase(parent_it);
744 }
745 }
746 else
747 {
748 break;
749 }
750 }
751}
752
Ed Tanous60520632018-06-11 17:46:52 -0700753int main(int argc, char** argv)
754{
Matt Spinlerdd945862018-09-07 12:41:05 -0500755 auto options = ArgumentParser(argc, argv);
Ed Tanous60520632018-06-11 17:46:52 -0700756 boost::asio::io_service io;
757 std::shared_ptr<sdbusplus::asio::connection> system_bus =
758 std::make_shared<sdbusplus::asio::connection>(io);
759
Matt Spinlerdd945862018-09-07 12:41:05 -0500760 splitArgs(options["service-namespaces"], service_whitelist);
Matt Spinlerdd945862018-09-07 12:41:05 -0500761 splitArgs(options["service-blacklists"], service_blacklist);
762
Ed Tanousd4dd96a2018-11-12 11:37:44 -0800763 // TODO(Ed) Remove this once all service files are updated to not use this.
764 // For now, simply squash the input, and ignore it.
765 boost::container::flat_set<std::string> iface_whitelist;
766 splitArgs(options["interface-namespaces"], iface_whitelist);
767
Ed Tanous60520632018-06-11 17:46:52 -0700768 system_bus->request_name(OBJECT_MAPPER_DBUS_NAME);
769 sdbusplus::asio::object_server server(system_bus);
770
771 // Construct a signal set registered for process termination.
772 boost::asio::signal_set signals(io, SIGINT, SIGTERM);
773 signals.async_wait([&io](const boost::system::error_code& error,
774 int signal_number) { io.stop(); });
775
776 interface_map_type interface_map;
777 boost::container::flat_map<std::string, std::string> name_owners;
778
779 std::function<void(sdbusplus::message::message & message)>
780 nameChangeHandler = [&interface_map, &io, &name_owners, &server,
781 system_bus](sdbusplus::message::message& message) {
782 std::string name;
783 std::string old_owner;
784 std::string new_owner;
785
786 message.read(name, old_owner, new_owner);
787
788 if (!old_owner.empty())
789 {
790 if (boost::starts_with(old_owner, ":"))
791 {
792 auto it = name_owners.find(old_owner);
793 if (it != name_owners.end())
794 {
795 name_owners.erase(it);
796 }
797 }
798 // Connection removed
799 interface_map_type::iterator path_it = interface_map.begin();
800 while (path_it != interface_map.end())
801 {
Matt Spinler1036b4d2018-09-18 16:45:29 -0500802 // If an associations interface is being removed,
803 // also need to remove the corresponding associations
804 // objects and properties.
805 auto ifaces = path_it->second.find(name);
806 if (ifaces != path_it->second.end())
807 {
808 auto assoc = std::find(ifaces->second.begin(),
809 ifaces->second.end(),
810 ASSOCIATIONS_INTERFACE);
811
812 if (assoc != ifaces->second.end())
813 {
Matt Spinler937a2322019-01-23 13:54:22 -0600814 removeAssociation(path_it->first, name, server);
Matt Spinler1036b4d2018-09-18 16:45:29 -0500815 }
816 }
817
Ed Tanous60520632018-06-11 17:46:52 -0700818 path_it->second.erase(name);
819 if (path_it->second.empty())
820 {
821 // If the last connection to the object is gone,
822 // delete the top level object
823 path_it = interface_map.erase(path_it);
824 continue;
825 }
826 path_it++;
827 }
828 }
829
830 if (!new_owner.empty())
831 {
Matt Spinleraecabe82018-09-19 13:25:42 -0500832#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700833 auto transaction = std::make_shared<
834 std::chrono::time_point<std::chrono::steady_clock>>(
835 std::chrono::steady_clock::now());
Matt Spinleraecabe82018-09-19 13:25:42 -0500836#endif
Ed Tanous60520632018-06-11 17:46:52 -0700837 // New daemon added
838 if (need_to_introspect(name))
839 {
840 name_owners[new_owner] = name;
841 start_new_introspect(system_bus.get(), io, interface_map,
Matt Spinleraecabe82018-09-19 13:25:42 -0500842 name,
843#ifdef DEBUG
844 transaction,
845#endif
846 server);
Ed Tanous60520632018-06-11 17:46:52 -0700847 }
848 }
849 };
850
851 sdbusplus::bus::match::match nameOwnerChanged(
852 static_cast<sdbusplus::bus::bus&>(*system_bus),
853 sdbusplus::bus::match::rules::nameOwnerChanged(), nameChangeHandler);
854
855 std::function<void(sdbusplus::message::message & message)>
856 interfacesAddedHandler = [&interface_map, &name_owners, &server](
857 sdbusplus::message::message& message) {
858 sdbusplus::message::object_path obj_path;
859 std::vector<std::pair<
860 std::string, std::vector<std::pair<
861 std::string, sdbusplus::message::variant<
862 std::vector<Association>>>>>>
863 interfaces_added;
864 message.read(obj_path, interfaces_added);
865 std::string well_known;
Andrew Geissler3b025e62019-02-01 10:33:54 -0600866 if (!getWellKnown(name_owners, message.get_sender(), well_known))
Ed Tanous60520632018-06-11 17:46:52 -0700867 {
868 return; // only introspect well-known
869 }
870 if (need_to_introspect(well_known))
871 {
872 auto& iface_list = interface_map[obj_path.str];
873
Matt Spinlercc74e4b2018-09-18 16:21:54 -0500874 for (const auto& interface_pair : interfaces_added)
Ed Tanous60520632018-06-11 17:46:52 -0700875 {
876 iface_list[well_known].emplace(interface_pair.first);
877
878 if (interface_pair.first == ASSOCIATIONS_INTERFACE)
879 {
880 const sdbusplus::message::variant<
881 std::vector<Association>>* variantAssociations =
882 nullptr;
883 for (const auto& interface : interface_pair.second)
884 {
885 if (interface.first == "associations")
886 {
887 variantAssociations = &(interface.second);
888 }
889 }
890 if (variantAssociations == nullptr)
891 {
892 std::cerr << "Illegal association found on "
893 << well_known << "\n";
894 continue;
895 }
896 std::vector<Association> associations =
897 sdbusplus::message::variant_ns::get<
898 std::vector<Association>>(*variantAssociations);
Matt Spinler09be5762019-01-23 14:28:17 -0600899 associationChanged(server, associations, obj_path.str,
900 well_known);
Ed Tanous60520632018-06-11 17:46:52 -0700901 }
902 }
Matt Spinler1b4a5022019-01-03 14:48:33 -0600903
904 // To handle the case where an object path is being created
905 // with 2 or more new path segments, check if the parent paths
906 // of this path are already in the interface map, and add them
907 // if they aren't with just the default freedesktop interfaces.
908 // This would be done via introspection if they would have
909 // already existed at startup. While we could also introspect
910 // them now to do the work, we know there aren't any other
911 // interfaces or we would have gotten signals for them as well,
912 // so take a shortcut to speed things up.
913 //
914 // This is all needed so that mapper operations can be done
915 // on the new parent paths.
916 using iface_map_iterator = interface_map_type::iterator;
917 using iface_map_value_type = boost::container::flat_map<
918 std::string, boost::container::flat_set<std::string>>;
919 using name_map_iterator = iface_map_value_type::iterator;
920
921 static const boost::container::flat_set<std::string>
922 default_ifaces{"org.freedesktop.DBus.Introspectable",
923 "org.freedesktop.DBus.Peer",
924 "org.freedesktop.DBus.Properties"};
925
926 std::string parent = obj_path.str;
927 auto pos = parent.find_last_of('/');
928
929 while (pos != std::string::npos)
930 {
931 parent = parent.substr(0, pos);
932
933 std::pair<iface_map_iterator, bool> parentEntry =
934 interface_map.insert(
935 std::make_pair(parent, iface_map_value_type{}));
936
937 std::pair<name_map_iterator, bool> ifaceEntry =
938 parentEntry.first->second.insert(
939 std::make_pair(well_known, default_ifaces));
940
941 if (!ifaceEntry.second)
942 {
943 // Entry was already there for this name so done.
944 break;
945 }
946
947 pos = parent.find_last_of('/');
948 }
Ed Tanous60520632018-06-11 17:46:52 -0700949 }
950 };
951
952 sdbusplus::bus::match::match interfacesAdded(
953 static_cast<sdbusplus::bus::bus&>(*system_bus),
954 sdbusplus::bus::match::rules::interfacesAdded(),
955 interfacesAddedHandler);
956
957 std::function<void(sdbusplus::message::message & message)>
958 interfacesRemovedHandler = [&interface_map, &name_owners, &server](
959 sdbusplus::message::message& message) {
960 sdbusplus::message::object_path obj_path;
961 std::vector<std::string> interfaces_removed;
962 message.read(obj_path, interfaces_removed);
963 auto connection_map = interface_map.find(obj_path.str);
964 if (connection_map == interface_map.end())
965 {
966 return;
967 }
968
969 std::string sender;
Andrew Geissler3b025e62019-02-01 10:33:54 -0600970 if (!getWellKnown(name_owners, message.get_sender(), sender))
Ed Tanous60520632018-06-11 17:46:52 -0700971 {
972 return;
973 }
974 for (const std::string& interface : interfaces_removed)
975 {
976 auto interface_set = connection_map->second.find(sender);
977 if (interface_set == connection_map->second.end())
978 {
979 continue;
980 }
981
982 if (interface == ASSOCIATIONS_INTERFACE)
983 {
Matt Spinler937a2322019-01-23 13:54:22 -0600984 removeAssociation(obj_path.str, sender, server);
Ed Tanous60520632018-06-11 17:46:52 -0700985 }
986
987 interface_set->second.erase(interface);
988 // If this was the last interface on this connection,
989 // erase the connection
990 if (interface_set->second.empty())
991 {
992 connection_map->second.erase(interface_set);
993 }
994 }
995 // If this was the last connection on this object path,
996 // erase the object path
997 if (connection_map->second.empty())
998 {
999 interface_map.erase(connection_map);
1000 }
Matt Spinlera82779f2019-01-09 12:39:42 -06001001
1002 removeUnneededParents(obj_path.str, sender, interface_map);
Ed Tanous60520632018-06-11 17:46:52 -07001003 };
1004
1005 sdbusplus::bus::match::match interfacesRemoved(
1006 static_cast<sdbusplus::bus::bus&>(*system_bus),
1007 sdbusplus::bus::match::rules::interfacesRemoved(),
1008 interfacesRemovedHandler);
1009
1010 std::function<void(sdbusplus::message::message & message)>
1011 associationChangedHandler =
Matt Spinler937a2322019-01-23 13:54:22 -06001012 [&server, &name_owners](sdbusplus::message::message& message) {
Ed Tanous60520632018-06-11 17:46:52 -07001013 std::string objectName;
1014 boost::container::flat_map<
1015 std::string,
1016 sdbusplus::message::variant<std::vector<Association>>>
1017 values;
1018 message.read(objectName, values);
1019 auto findAssociations = values.find("associations");
1020 if (findAssociations != values.end())
1021 {
1022 std::vector<Association> associations =
1023 sdbusplus::message::variant_ns::get<
1024 std::vector<Association>>(findAssociations->second);
Matt Spinler937a2322019-01-23 13:54:22 -06001025
1026 std::string well_known;
Andrew Geissler3b025e62019-02-01 10:33:54 -06001027 if (!getWellKnown(name_owners, message.get_sender(),
1028 well_known))
Matt Spinler937a2322019-01-23 13:54:22 -06001029 {
1030 return;
1031 }
Matt Spinler09be5762019-01-23 14:28:17 -06001032 associationChanged(server, associations, message.get_path(),
1033 well_known);
Ed Tanous60520632018-06-11 17:46:52 -07001034 }
1035 };
1036 sdbusplus::bus::match::match associationChanged(
1037 static_cast<sdbusplus::bus::bus&>(*system_bus),
1038 sdbusplus::bus::match::rules::interface(
1039 "org.freedesktop.DBus.Properties") +
1040 sdbusplus::bus::match::rules::member("PropertiesChanged") +
1041 sdbusplus::bus::match::rules::argN(0, ASSOCIATIONS_INTERFACE),
1042 associationChangedHandler);
1043
1044 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
1045 server.add_interface("/xyz/openbmc_project/object_mapper",
1046 "xyz.openbmc_project.ObjectMapper");
1047
1048 iface->register_method(
Matt Spinler6a39e8c2018-11-13 11:11:36 -06001049 "GetAncestors", [&interface_map](std::string& req_path,
Ed Tanous60520632018-06-11 17:46:52 -07001050 std::vector<std::string>& interfaces) {
1051 // Interfaces need to be sorted for intersect to function
1052 std::sort(interfaces.begin(), interfaces.end());
1053
James Feistd7322872019-01-28 11:25:00 -08001054 if (boost::ends_with(req_path, "/"))
Matt Spinler6a39e8c2018-11-13 11:11:36 -06001055 {
James Feistd7322872019-01-28 11:25:00 -08001056 req_path.pop_back();
1057 }
1058 if (req_path.size() &&
1059 interface_map.find(req_path) == interface_map.end())
1060 {
1061 throw NotFoundException();
Matt Spinler6a39e8c2018-11-13 11:11:36 -06001062 }
1063
Ed Tanous60520632018-06-11 17:46:52 -07001064 std::vector<interface_map_type::value_type> ret;
1065 for (auto& object_path : interface_map)
1066 {
1067 auto& this_path = object_path.first;
Matt Spinlerea6516c2018-09-25 16:23:13 -05001068 if (boost::starts_with(req_path, this_path) &&
1069 (req_path != this_path))
Ed Tanous60520632018-06-11 17:46:52 -07001070 {
1071 if (interfaces.empty())
1072 {
1073 ret.emplace_back(object_path);
1074 }
1075 else
1076 {
1077 for (auto& interface_map : object_path.second)
1078 {
1079
1080 if (intersect(interfaces.begin(), interfaces.end(),
1081 interface_map.second.begin(),
1082 interface_map.second.end()))
1083 {
Matt Spinler47c09752018-11-29 14:54:13 -06001084 addObjectMapResult(ret, this_path,
1085 interface_map);
Ed Tanous60520632018-06-11 17:46:52 -07001086 }
1087 }
1088 }
1089 }
1090 }
Ed Tanous60520632018-06-11 17:46:52 -07001091
1092 return ret;
1093 });
1094
1095 iface->register_method(
1096 "GetObject", [&interface_map](const std::string& path,
1097 std::vector<std::string>& interfaces) {
Matt Spinler7a38d412018-09-18 16:13:25 -05001098 boost::container::flat_map<std::string,
1099 boost::container::flat_set<std::string>>
1100 results;
1101
Ed Tanous60520632018-06-11 17:46:52 -07001102 // Interfaces need to be sorted for intersect to function
1103 std::sort(interfaces.begin(), interfaces.end());
1104 auto path_ref = interface_map.find(path);
1105 if (path_ref == interface_map.end())
1106 {
1107 throw NotFoundException();
1108 }
1109 if (interfaces.empty())
1110 {
1111 return path_ref->second;
1112 }
1113 for (auto& interface_map : path_ref->second)
1114 {
1115 if (intersect(interfaces.begin(), interfaces.end(),
1116 interface_map.second.begin(),
1117 interface_map.second.end()))
1118 {
Matt Spinler7a38d412018-09-18 16:13:25 -05001119 results.emplace(interface_map.first, interface_map.second);
Ed Tanous60520632018-06-11 17:46:52 -07001120 }
1121 }
Matt Spinler7a38d412018-09-18 16:13:25 -05001122
1123 if (results.empty())
1124 {
1125 throw NotFoundException();
1126 }
1127
1128 return results;
Ed Tanous60520632018-06-11 17:46:52 -07001129 });
1130
1131 iface->register_method(
Matt Spinler153494e2018-11-07 16:35:40 -06001132 "GetSubTree", [&interface_map](std::string& req_path, int32_t depth,
1133 std::vector<std::string>& interfaces) {
Ed Tanous60520632018-06-11 17:46:52 -07001134 if (depth <= 0)
1135 {
1136 depth = std::numeric_limits<int32_t>::max();
1137 }
1138 // Interfaces need to be sorted for intersect to function
1139 std::sort(interfaces.begin(), interfaces.end());
1140 std::vector<interface_map_type::value_type> ret;
1141
James Feistd7322872019-01-28 11:25:00 -08001142 if (boost::ends_with(req_path, "/"))
Matt Spinler153494e2018-11-07 16:35:40 -06001143 {
James Feistd7322872019-01-28 11:25:00 -08001144 req_path.pop_back();
1145 }
1146 if (req_path.size() &&
1147 interface_map.find(req_path) == interface_map.end())
1148 {
1149 throw NotFoundException();
Matt Spinler153494e2018-11-07 16:35:40 -06001150 }
1151
Ed Tanous60520632018-06-11 17:46:52 -07001152 for (auto& object_path : interface_map)
1153 {
1154 auto& this_path = object_path.first;
Matt Spinler153494e2018-11-07 16:35:40 -06001155
1156 if (this_path == req_path)
1157 {
1158 continue;
1159 }
1160
Ed Tanous60520632018-06-11 17:46:52 -07001161 if (boost::starts_with(this_path, req_path))
1162 {
1163 // count the number of slashes past the search term
1164 int32_t this_depth =
1165 std::count(this_path.begin() + req_path.size(),
1166 this_path.end(), '/');
1167 if (this_depth <= depth)
1168 {
Ed Tanous60520632018-06-11 17:46:52 -07001169 for (auto& interface_map : object_path.second)
1170 {
1171 if (intersect(interfaces.begin(), interfaces.end(),
1172 interface_map.second.begin(),
Matt Spinler9f0958e2018-09-11 08:26:10 -05001173 interface_map.second.end()) ||
1174 interfaces.empty())
Ed Tanous60520632018-06-11 17:46:52 -07001175 {
Matt Spinler47c09752018-11-29 14:54:13 -06001176 addObjectMapResult(ret, this_path,
1177 interface_map);
Ed Tanous60520632018-06-11 17:46:52 -07001178 }
1179 }
Ed Tanous60520632018-06-11 17:46:52 -07001180 }
1181 }
1182 }
Matt Spinler6a39e8c2018-11-13 11:11:36 -06001183
Ed Tanous60520632018-06-11 17:46:52 -07001184 return ret;
1185 });
1186
1187 iface->register_method(
1188 "GetSubTreePaths",
Matt Spinler153494e2018-11-07 16:35:40 -06001189 [&interface_map](std::string& req_path, int32_t depth,
Ed Tanous60520632018-06-11 17:46:52 -07001190 std::vector<std::string>& interfaces) {
1191 if (depth <= 0)
1192 {
1193 depth = std::numeric_limits<int32_t>::max();
1194 }
1195 // Interfaces need to be sorted for intersect to function
1196 std::sort(interfaces.begin(), interfaces.end());
1197 std::vector<std::string> ret;
Matt Spinler153494e2018-11-07 16:35:40 -06001198
James Feistd7322872019-01-28 11:25:00 -08001199 if (boost::ends_with(req_path, "/"))
Matt Spinler153494e2018-11-07 16:35:40 -06001200 {
James Feistd7322872019-01-28 11:25:00 -08001201 req_path.pop_back();
1202 }
1203 if (req_path.size() &&
1204 interface_map.find(req_path) == interface_map.end())
1205 {
1206 throw NotFoundException();
Matt Spinler153494e2018-11-07 16:35:40 -06001207 }
1208
Ed Tanous60520632018-06-11 17:46:52 -07001209 for (auto& object_path : interface_map)
1210 {
1211 auto& this_path = object_path.first;
Matt Spinler153494e2018-11-07 16:35:40 -06001212
1213 if (this_path == req_path)
1214 {
1215 continue;
1216 }
1217
Ed Tanous60520632018-06-11 17:46:52 -07001218 if (boost::starts_with(this_path, req_path))
1219 {
1220 // count the number of slashes past the search term
1221 int this_depth =
1222 std::count(this_path.begin() + req_path.size(),
1223 this_path.end(), '/');
1224 if (this_depth <= depth)
1225 {
1226 bool add = interfaces.empty();
1227 for (auto& interface_map : object_path.second)
1228 {
1229 if (intersect(interfaces.begin(), interfaces.end(),
1230 interface_map.second.begin(),
1231 interface_map.second.end()))
1232 {
1233 add = true;
1234 break;
1235 }
1236 }
1237 if (add)
1238 {
1239 // TODO(ed) this is a copy
1240 ret.emplace_back(this_path);
1241 }
1242 }
1243 }
1244 }
Matt Spinler6a39e8c2018-11-13 11:11:36 -06001245
Ed Tanous60520632018-06-11 17:46:52 -07001246 return ret;
1247 });
1248
1249 iface->initialize();
1250
1251 io.post([&]() {
1252 doListNames(io, interface_map, system_bus.get(), name_owners, server);
1253 });
1254
Ed Tanous60520632018-06-11 17:46:52 -07001255 io.run();
1256}