blob: 801d910cf0e0513967d6eba4e36f41623833e699 [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>
Ed Tanous60520632018-06-11 17:46:52 -07009#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
Matt Spinler937a2322019-01-23 13:54:22 -060035// * endpointsPos - holds the endpoints array that shadows the property
Matt Spinler1036b4d2018-09-18 16:45:29 -050036static constexpr auto ifacePos = 0;
37static constexpr auto endpointsPos = 1;
Matt Spinler1036b4d2018-09-18 16:45:29 -050038using Endpoints = std::vector<std::string>;
Andrew Geissler67e5a422019-02-04 13:00:59 -060039using AssociationInterfaces = boost::container::flat_map<
Matt Spinler937a2322019-01-23 13:54:22 -060040 std::string,
Andrew Geissler67e5a422019-02-04 13:00:59 -060041 std::tuple<std::shared_ptr<sdbusplus::asio::dbus_interface>, Endpoints>>;
42
43AssociationInterfaces associationInterfaces;
Ed Tanous60520632018-06-11 17:46:52 -070044
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
Andrew Geissler82815da2019-02-04 12:19:41 -060068static WhiteBlackList service_whitelist;
69static WhiteBlackList 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,
Andrew Geissler67e5a422019-02-04 13:00:59 -0600369 sdbusplus::asio::object_server& server,
370 AssociationOwnersType& assocOwners,
371 AssociationInterfaces& assocInterfaces)
Matt Spinler937a2322019-01-23 13:54:22 -0600372{
373 // Use associationOwners to find the association paths and endpoints
374 // that the passed in object path and service own. Remove all of
375 // these endpoints from the actual association D-Bus objects, and if
376 // the endpoints property is then empty, the whole association object
377 // can be removed. Note there can be multiple services that own an
378 // association, and also that sourcePath is the path of the object
379 // that contains the org.openbmc.Associations interface and not the
380 // association path itself.
381
382 // Find the services that have associations for this object path
Andrew Geissler67e5a422019-02-04 13:00:59 -0600383 auto owners = assocOwners.find(sourcePath);
384 if (owners == assocOwners.end())
Matt Spinler937a2322019-01-23 13:54:22 -0600385 {
386 return;
387 }
388
389 // Find the association paths and endpoints owned by this object
390 // path for this service.
391 auto assocs = owners->second.find(owner);
392 if (assocs == owners->second.end())
393 {
394 return;
395 }
396
397 for (const auto& [assocPath, endpointsToRemove] : assocs->second)
398 {
399 // Get the association D-Bus object for this assocPath
Andrew Geissler67e5a422019-02-04 13:00:59 -0600400 auto target = assocInterfaces.find(assocPath);
401 if (target == assocInterfaces.end())
Matt Spinler937a2322019-01-23 13:54:22 -0600402 {
403 continue;
404 }
405
406 // Remove the entries in the endpoints D-Bus property for this
407 // path/owner/association-path.
408 auto& existingEndpoints = std::get<endpointsPos>(target->second);
409 for (const auto& endpointToRemove : endpointsToRemove)
410 {
411 auto e = std::find(existingEndpoints.begin(),
412 existingEndpoints.end(), endpointToRemove);
413
414 if (e != existingEndpoints.end())
415 {
416 existingEndpoints.erase(e);
Matt Spinler1036b4d2018-09-18 16:45:29 -0500417 }
418 }
Matt Spinler937a2322019-01-23 13:54:22 -0600419
420 // Remove the association from D-Bus if there are no more endpoints,
421 // otherwise just update the endpoints property.
422 if (existingEndpoints.empty())
423 {
424 server.remove_interface(std::get<ifacePos>(target->second));
425 std::get<ifacePos>(target->second) = nullptr;
426 std::get<endpointsPos>(target->second).clear();
427 }
428 else
429 {
430 std::get<ifacePos>(target->second)
431 ->set_property("endpoints", existingEndpoints);
432 }
433 }
434
435 // Remove the associationOwners entries for this owning path/service.
436 owners->second.erase(assocs);
437 if (owners->second.empty())
438 {
439 associationOwners.erase(owners);
Ed Tanous60520632018-06-11 17:46:52 -0700440 }
441}
442
443void do_associations(sdbusplus::asio::connection* system_bus,
444 sdbusplus::asio::object_server& objectServer,
445 const std::string& processName, const std::string& path)
446{
447 system_bus->async_method_call(
Matt Spinler937a2322019-01-23 13:54:22 -0600448 [&objectServer, path, processName](
449 const boost::system::error_code ec,
450 const sdbusplus::message::variant<std::vector<Association>>&
451 variantAssociations) {
Ed Tanous60520632018-06-11 17:46:52 -0700452 if (ec)
453 {
454 std::cerr << "Error getting associations from " << path << "\n";
455 }
456 std::vector<Association> associations =
457 sdbusplus::message::variant_ns::get<std::vector<Association>>(
458 variantAssociations);
Matt Spinler09be5762019-01-23 14:28:17 -0600459 associationChanged(objectServer, associations, path, processName);
Ed Tanous60520632018-06-11 17:46:52 -0700460 },
461 processName, path, "org.freedesktop.DBus.Properties", "Get",
462 ASSOCIATIONS_INTERFACE, "associations");
463}
464
465void do_introspect(sdbusplus::asio::connection* system_bus,
466 std::shared_ptr<InProgressIntrospect> transaction,
467 interface_map_type& interface_map,
468 sdbusplus::asio::object_server& objectServer,
469 std::string path)
470{
471 system_bus->async_method_call(
472 [&interface_map, &objectServer, transaction, path,
473 system_bus](const boost::system::error_code ec,
474 const std::string& introspect_xml) {
475 if (ec)
476 {
477 std::cerr << "Introspect call failed with error: " << ec << ", "
478 << ec.message()
479 << " on process: " << transaction->process_name
480 << " path: " << path << "\n";
481 return;
482 }
483
484 tinyxml2::XMLDocument doc;
485
486 tinyxml2::XMLError e = doc.Parse(introspect_xml.c_str());
487 if (e != tinyxml2::XMLError::XML_SUCCESS)
488 {
489 std::cerr << "XML parsing failed\n";
490 return;
491 }
492
493 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
494 if (pRoot == nullptr)
495 {
496 std::cerr << "XML document did not contain any data\n";
497 return;
498 }
499 auto& thisPathMap = interface_map[path];
Ed Tanous60520632018-06-11 17:46:52 -0700500 tinyxml2::XMLElement* pElement =
501 pRoot->FirstChildElement("interface");
502 while (pElement != nullptr)
503 {
504 const char* iface_name = pElement->Attribute("name");
505 if (iface_name == nullptr)
506 {
507 continue;
508 }
509
Matt Spinlerdd945862018-09-07 12:41:05 -0500510 std::string iface{iface_name};
511
Ed Tanousd4dd96a2018-11-12 11:37:44 -0800512 thisPathMap[transaction->process_name].emplace(iface_name);
513
Ed Tanous60520632018-06-11 17:46:52 -0700514 if (std::strcmp(iface_name, ASSOCIATIONS_INTERFACE) == 0)
515 {
516 do_associations(system_bus, objectServer,
517 transaction->process_name, path);
518 }
Ed Tanous60520632018-06-11 17:46:52 -0700519
520 pElement = pElement->NextSiblingElement("interface");
521 }
522
Ed Tanous50232cd2018-11-12 11:34:43 -0800523 pElement = pRoot->FirstChildElement("node");
524 while (pElement != nullptr)
Ed Tanous60520632018-06-11 17:46:52 -0700525 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800526 const char* child_path = pElement->Attribute("name");
527 if (child_path != nullptr)
Ed Tanous60520632018-06-11 17:46:52 -0700528 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800529 std::string parent_path(path);
530 if (parent_path == "/")
Ed Tanous60520632018-06-11 17:46:52 -0700531 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800532 parent_path.clear();
Ed Tanous60520632018-06-11 17:46:52 -0700533 }
Ed Tanous50232cd2018-11-12 11:34:43 -0800534
535 do_introspect(system_bus, transaction, interface_map,
536 objectServer, parent_path + "/" + child_path);
Ed Tanous60520632018-06-11 17:46:52 -0700537 }
Ed Tanous50232cd2018-11-12 11:34:43 -0800538 pElement = pElement->NextSiblingElement("node");
Ed Tanous60520632018-06-11 17:46:52 -0700539 }
540 },
541 transaction->process_name, path, "org.freedesktop.DBus.Introspectable",
542 "Introspect");
543}
544
Ed Tanous60520632018-06-11 17:46:52 -0700545void start_new_introspect(
546 sdbusplus::asio::connection* system_bus, boost::asio::io_service& io,
547 interface_map_type& interface_map, const std::string& process_name,
Matt Spinleraecabe82018-09-19 13:25:42 -0500548#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700549 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
550 global_start_time,
Matt Spinleraecabe82018-09-19 13:25:42 -0500551#endif
Ed Tanous60520632018-06-11 17:46:52 -0700552 sdbusplus::asio::object_server& objectServer)
553{
Andrew Geissler82815da2019-02-04 12:19:41 -0600554 if (needToIntrospect(process_name, service_whitelist, service_blacklist))
Ed Tanous60520632018-06-11 17:46:52 -0700555 {
Ed Tanous60520632018-06-11 17:46:52 -0700556 std::shared_ptr<InProgressIntrospect> transaction =
Matt Spinleraecabe82018-09-19 13:25:42 -0500557 std::make_shared<InProgressIntrospect>(system_bus, io, process_name
558#ifdef DEBUG
559 ,
560 global_start_time
561#endif
562 );
Ed Tanous60520632018-06-11 17:46:52 -0700563
564 do_introspect(system_bus, transaction, interface_map, objectServer,
565 "/");
566 }
567}
568
569// TODO(ed) replace with std::set_intersection once c++17 is available
570template <class InputIt1, class InputIt2>
571bool intersect(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
572{
573 while (first1 != last1 && first2 != last2)
574 {
575 if (*first1 < *first2)
576 {
577 ++first1;
578 continue;
579 }
580 if (*first2 < *first1)
581 {
582 ++first2;
583 continue;
584 }
585 return true;
586 }
587 return false;
588}
589
590void doListNames(
591 boost::asio::io_service& io, interface_map_type& interface_map,
592 sdbusplus::asio::connection* system_bus,
593 boost::container::flat_map<std::string, std::string>& name_owners,
594 sdbusplus::asio::object_server& objectServer)
595{
596 system_bus->async_method_call(
597 [&io, &interface_map, &name_owners, &objectServer,
598 system_bus](const boost::system::error_code ec,
599 std::vector<std::string> process_names) {
600 if (ec)
601 {
602 std::cerr << "Error getting names: " << ec << "\n";
603 std::exit(EXIT_FAILURE);
604 return;
605 }
Ed Tanous60520632018-06-11 17:46:52 -0700606 // Try to make startup consistent
607 std::sort(process_names.begin(), process_names.end());
Matt Spinleraecabe82018-09-19 13:25:42 -0500608#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700609 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
610 global_start_time = std::make_shared<
611 std::chrono::time_point<std::chrono::steady_clock>>(
612 std::chrono::steady_clock::now());
Matt Spinleraecabe82018-09-19 13:25:42 -0500613#endif
Ed Tanous60520632018-06-11 17:46:52 -0700614 for (const std::string& process_name : process_names)
615 {
Andrew Geissler82815da2019-02-04 12:19:41 -0600616 if (needToIntrospect(process_name, service_whitelist,
617 service_blacklist))
Ed Tanous60520632018-06-11 17:46:52 -0700618 {
619 start_new_introspect(system_bus, io, interface_map,
Matt Spinleraecabe82018-09-19 13:25:42 -0500620 process_name,
621#ifdef DEBUG
622 global_start_time,
623#endif
Ed Tanous60520632018-06-11 17:46:52 -0700624 objectServer);
625 update_owners(system_bus, name_owners, process_name);
626 }
627 }
628 },
629 "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
630 "ListNames");
631}
632
Matt Spinlerdd945862018-09-07 12:41:05 -0500633void splitArgs(const std::string& stringArgs,
634 boost::container::flat_set<std::string>& listArgs)
635{
636 std::istringstream args;
637 std::string arg;
638
639 args.str(stringArgs);
640
641 while (!args.eof())
642 {
643 args >> arg;
644 if (!arg.empty())
645 {
646 listArgs.insert(arg);
647 }
648 }
649}
650
Matt Spinler47c09752018-11-29 14:54:13 -0600651void addObjectMapResult(
652 std::vector<interface_map_type::value_type>& objectMap,
Matt Spinler9f0958e2018-09-11 08:26:10 -0500653 const std::string& objectPath,
654 const std::pair<std::string, boost::container::flat_set<std::string>>&
655 interfaceMap)
656{
657 // Adds an object path/service name/interface list entry to
Matt Spinler47c09752018-11-29 14:54:13 -0600658 // the results of GetSubTree and GetAncestors.
Matt Spinler9f0958e2018-09-11 08:26:10 -0500659 // If an entry for the object path already exists, just add the
660 // service name and interfaces to that entry, otherwise create
661 // a new entry.
662 auto entry = std::find_if(
Matt Spinler47c09752018-11-29 14:54:13 -0600663 objectMap.begin(), objectMap.end(),
Matt Spinler9f0958e2018-09-11 08:26:10 -0500664 [&objectPath](const auto& i) { return objectPath == i.first; });
665
Matt Spinler47c09752018-11-29 14:54:13 -0600666 if (entry != objectMap.end())
Matt Spinler9f0958e2018-09-11 08:26:10 -0500667 {
668 entry->second.emplace(interfaceMap);
669 }
670 else
671 {
672 interface_map_type::value_type object;
673 object.first = objectPath;
674 object.second.emplace(interfaceMap);
Matt Spinler47c09752018-11-29 14:54:13 -0600675 objectMap.push_back(object);
Matt Spinler9f0958e2018-09-11 08:26:10 -0500676 }
677}
678
Matt Spinlera82779f2019-01-09 12:39:42 -0600679// Remove parents of the passed in path that:
680// 1) Only have the 3 default interfaces on them
681// - Means D-Bus created these, not application code,
682// with the Properties, Introspectable, and Peer ifaces
683// 2) Have no other child for this owner
684void removeUnneededParents(const std::string& objectPath,
685 const std::string& owner,
686 interface_map_type& interface_map)
687{
688 auto parent = objectPath;
689
690 while (true)
691 {
692 auto pos = parent.find_last_of('/');
693 if ((pos == std::string::npos) || (pos == 0))
694 {
695 break;
696 }
697 parent = parent.substr(0, pos);
698
699 auto parent_it = interface_map.find(parent);
700 if (parent_it == interface_map.end())
701 {
702 break;
703 }
704
705 auto ifaces_it = parent_it->second.find(owner);
706 if (ifaces_it == parent_it->second.end())
707 {
708 break;
709 }
710
711 if (ifaces_it->second.size() != 3)
712 {
713 break;
714 }
715
716 auto child_path = parent + '/';
717
718 // Remove this parent if there isn't a remaining child on this owner
719 auto child = std::find_if(
720 interface_map.begin(), interface_map.end(),
721 [&owner, &child_path](const auto& entry) {
722 return boost::starts_with(entry.first, child_path) &&
723 (entry.second.find(owner) != entry.second.end());
724 });
725
726 if (child == interface_map.end())
727 {
728 parent_it->second.erase(ifaces_it);
729 if (parent_it->second.empty())
730 {
731 interface_map.erase(parent_it);
732 }
733 }
734 else
735 {
736 break;
737 }
738 }
739}
740
Ed Tanous60520632018-06-11 17:46:52 -0700741int main(int argc, char** argv)
742{
Matt Spinlerdd945862018-09-07 12:41:05 -0500743 auto options = ArgumentParser(argc, argv);
Ed Tanous60520632018-06-11 17:46:52 -0700744 boost::asio::io_service io;
745 std::shared_ptr<sdbusplus::asio::connection> system_bus =
746 std::make_shared<sdbusplus::asio::connection>(io);
747
Matt Spinlerdd945862018-09-07 12:41:05 -0500748 splitArgs(options["service-namespaces"], service_whitelist);
Matt Spinlerdd945862018-09-07 12:41:05 -0500749 splitArgs(options["service-blacklists"], service_blacklist);
750
Ed Tanousd4dd96a2018-11-12 11:37:44 -0800751 // TODO(Ed) Remove this once all service files are updated to not use this.
752 // For now, simply squash the input, and ignore it.
753 boost::container::flat_set<std::string> iface_whitelist;
754 splitArgs(options["interface-namespaces"], iface_whitelist);
755
Ed Tanous60520632018-06-11 17:46:52 -0700756 system_bus->request_name(OBJECT_MAPPER_DBUS_NAME);
757 sdbusplus::asio::object_server server(system_bus);
758
759 // Construct a signal set registered for process termination.
760 boost::asio::signal_set signals(io, SIGINT, SIGTERM);
761 signals.async_wait([&io](const boost::system::error_code& error,
762 int signal_number) { io.stop(); });
763
764 interface_map_type interface_map;
765 boost::container::flat_map<std::string, std::string> name_owners;
766
767 std::function<void(sdbusplus::message::message & message)>
768 nameChangeHandler = [&interface_map, &io, &name_owners, &server,
769 system_bus](sdbusplus::message::message& message) {
770 std::string name;
771 std::string old_owner;
772 std::string new_owner;
773
774 message.read(name, old_owner, new_owner);
775
776 if (!old_owner.empty())
777 {
778 if (boost::starts_with(old_owner, ":"))
779 {
780 auto it = name_owners.find(old_owner);
781 if (it != name_owners.end())
782 {
783 name_owners.erase(it);
784 }
785 }
786 // Connection removed
787 interface_map_type::iterator path_it = interface_map.begin();
788 while (path_it != interface_map.end())
789 {
Matt Spinler1036b4d2018-09-18 16:45:29 -0500790 // If an associations interface is being removed,
791 // also need to remove the corresponding associations
792 // objects and properties.
793 auto ifaces = path_it->second.find(name);
794 if (ifaces != path_it->second.end())
795 {
796 auto assoc = std::find(ifaces->second.begin(),
797 ifaces->second.end(),
798 ASSOCIATIONS_INTERFACE);
799
800 if (assoc != ifaces->second.end())
801 {
Andrew Geissler67e5a422019-02-04 13:00:59 -0600802 removeAssociation(path_it->first, name, server,
803 associationOwners,
804 associationInterfaces);
Matt Spinler1036b4d2018-09-18 16:45:29 -0500805 }
806 }
807
Ed Tanous60520632018-06-11 17:46:52 -0700808 path_it->second.erase(name);
809 if (path_it->second.empty())
810 {
811 // If the last connection to the object is gone,
812 // delete the top level object
813 path_it = interface_map.erase(path_it);
814 continue;
815 }
816 path_it++;
817 }
818 }
819
820 if (!new_owner.empty())
821 {
Matt Spinleraecabe82018-09-19 13:25:42 -0500822#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700823 auto transaction = std::make_shared<
824 std::chrono::time_point<std::chrono::steady_clock>>(
825 std::chrono::steady_clock::now());
Matt Spinleraecabe82018-09-19 13:25:42 -0500826#endif
Ed Tanous60520632018-06-11 17:46:52 -0700827 // New daemon added
Andrew Geissler82815da2019-02-04 12:19:41 -0600828 if (needToIntrospect(name, service_whitelist,
829 service_blacklist))
Ed Tanous60520632018-06-11 17:46:52 -0700830 {
831 name_owners[new_owner] = name;
832 start_new_introspect(system_bus.get(), io, interface_map,
Matt Spinleraecabe82018-09-19 13:25:42 -0500833 name,
834#ifdef DEBUG
835 transaction,
836#endif
837 server);
Ed Tanous60520632018-06-11 17:46:52 -0700838 }
839 }
840 };
841
842 sdbusplus::bus::match::match nameOwnerChanged(
843 static_cast<sdbusplus::bus::bus&>(*system_bus),
844 sdbusplus::bus::match::rules::nameOwnerChanged(), nameChangeHandler);
845
846 std::function<void(sdbusplus::message::message & message)>
847 interfacesAddedHandler = [&interface_map, &name_owners, &server](
848 sdbusplus::message::message& message) {
849 sdbusplus::message::object_path obj_path;
850 std::vector<std::pair<
851 std::string, std::vector<std::pair<
852 std::string, sdbusplus::message::variant<
853 std::vector<Association>>>>>>
854 interfaces_added;
855 message.read(obj_path, interfaces_added);
856 std::string well_known;
Andrew Geissler3b025e62019-02-01 10:33:54 -0600857 if (!getWellKnown(name_owners, message.get_sender(), well_known))
Ed Tanous60520632018-06-11 17:46:52 -0700858 {
859 return; // only introspect well-known
860 }
Andrew Geissler82815da2019-02-04 12:19:41 -0600861 if (needToIntrospect(well_known, service_whitelist,
862 service_blacklist))
Ed Tanous60520632018-06-11 17:46:52 -0700863 {
864 auto& iface_list = interface_map[obj_path.str];
865
Matt Spinlercc74e4b2018-09-18 16:21:54 -0500866 for (const auto& interface_pair : interfaces_added)
Ed Tanous60520632018-06-11 17:46:52 -0700867 {
868 iface_list[well_known].emplace(interface_pair.first);
869
870 if (interface_pair.first == ASSOCIATIONS_INTERFACE)
871 {
872 const sdbusplus::message::variant<
873 std::vector<Association>>* variantAssociations =
874 nullptr;
875 for (const auto& interface : interface_pair.second)
876 {
877 if (interface.first == "associations")
878 {
879 variantAssociations = &(interface.second);
880 }
881 }
882 if (variantAssociations == nullptr)
883 {
884 std::cerr << "Illegal association found on "
885 << well_known << "\n";
886 continue;
887 }
888 std::vector<Association> associations =
889 sdbusplus::message::variant_ns::get<
890 std::vector<Association>>(*variantAssociations);
Matt Spinler09be5762019-01-23 14:28:17 -0600891 associationChanged(server, associations, obj_path.str,
892 well_known);
Ed Tanous60520632018-06-11 17:46:52 -0700893 }
894 }
Matt Spinler1b4a5022019-01-03 14:48:33 -0600895
896 // To handle the case where an object path is being created
897 // with 2 or more new path segments, check if the parent paths
898 // of this path are already in the interface map, and add them
899 // if they aren't with just the default freedesktop interfaces.
900 // This would be done via introspection if they would have
901 // already existed at startup. While we could also introspect
902 // them now to do the work, we know there aren't any other
903 // interfaces or we would have gotten signals for them as well,
904 // so take a shortcut to speed things up.
905 //
906 // This is all needed so that mapper operations can be done
907 // on the new parent paths.
908 using iface_map_iterator = interface_map_type::iterator;
909 using iface_map_value_type = boost::container::flat_map<
910 std::string, boost::container::flat_set<std::string>>;
911 using name_map_iterator = iface_map_value_type::iterator;
912
913 static const boost::container::flat_set<std::string>
914 default_ifaces{"org.freedesktop.DBus.Introspectable",
915 "org.freedesktop.DBus.Peer",
916 "org.freedesktop.DBus.Properties"};
917
918 std::string parent = obj_path.str;
919 auto pos = parent.find_last_of('/');
920
921 while (pos != std::string::npos)
922 {
923 parent = parent.substr(0, pos);
924
925 std::pair<iface_map_iterator, bool> parentEntry =
926 interface_map.insert(
927 std::make_pair(parent, iface_map_value_type{}));
928
929 std::pair<name_map_iterator, bool> ifaceEntry =
930 parentEntry.first->second.insert(
931 std::make_pair(well_known, default_ifaces));
932
933 if (!ifaceEntry.second)
934 {
935 // Entry was already there for this name so done.
936 break;
937 }
938
939 pos = parent.find_last_of('/');
940 }
Ed Tanous60520632018-06-11 17:46:52 -0700941 }
942 };
943
944 sdbusplus::bus::match::match interfacesAdded(
945 static_cast<sdbusplus::bus::bus&>(*system_bus),
946 sdbusplus::bus::match::rules::interfacesAdded(),
947 interfacesAddedHandler);
948
949 std::function<void(sdbusplus::message::message & message)>
950 interfacesRemovedHandler = [&interface_map, &name_owners, &server](
951 sdbusplus::message::message& message) {
952 sdbusplus::message::object_path obj_path;
953 std::vector<std::string> interfaces_removed;
954 message.read(obj_path, interfaces_removed);
955 auto connection_map = interface_map.find(obj_path.str);
956 if (connection_map == interface_map.end())
957 {
958 return;
959 }
960
961 std::string sender;
Andrew Geissler3b025e62019-02-01 10:33:54 -0600962 if (!getWellKnown(name_owners, message.get_sender(), sender))
Ed Tanous60520632018-06-11 17:46:52 -0700963 {
964 return;
965 }
966 for (const std::string& interface : interfaces_removed)
967 {
968 auto interface_set = connection_map->second.find(sender);
969 if (interface_set == connection_map->second.end())
970 {
971 continue;
972 }
973
974 if (interface == ASSOCIATIONS_INTERFACE)
975 {
Andrew Geissler67e5a422019-02-04 13:00:59 -0600976 removeAssociation(obj_path.str, sender, server,
977 associationOwners, associationInterfaces);
Ed Tanous60520632018-06-11 17:46:52 -0700978 }
979
980 interface_set->second.erase(interface);
981 // If this was the last interface on this connection,
982 // erase the connection
983 if (interface_set->second.empty())
984 {
985 connection_map->second.erase(interface_set);
986 }
987 }
988 // If this was the last connection on this object path,
989 // erase the object path
990 if (connection_map->second.empty())
991 {
992 interface_map.erase(connection_map);
993 }
Matt Spinlera82779f2019-01-09 12:39:42 -0600994
995 removeUnneededParents(obj_path.str, sender, interface_map);
Ed Tanous60520632018-06-11 17:46:52 -0700996 };
997
998 sdbusplus::bus::match::match interfacesRemoved(
999 static_cast<sdbusplus::bus::bus&>(*system_bus),
1000 sdbusplus::bus::match::rules::interfacesRemoved(),
1001 interfacesRemovedHandler);
1002
1003 std::function<void(sdbusplus::message::message & message)>
1004 associationChangedHandler =
Matt Spinler937a2322019-01-23 13:54:22 -06001005 [&server, &name_owners](sdbusplus::message::message& message) {
Ed Tanous60520632018-06-11 17:46:52 -07001006 std::string objectName;
1007 boost::container::flat_map<
1008 std::string,
1009 sdbusplus::message::variant<std::vector<Association>>>
1010 values;
1011 message.read(objectName, values);
1012 auto findAssociations = values.find("associations");
1013 if (findAssociations != values.end())
1014 {
1015 std::vector<Association> associations =
1016 sdbusplus::message::variant_ns::get<
1017 std::vector<Association>>(findAssociations->second);
Matt Spinler937a2322019-01-23 13:54:22 -06001018
1019 std::string well_known;
Andrew Geissler3b025e62019-02-01 10:33:54 -06001020 if (!getWellKnown(name_owners, message.get_sender(),
1021 well_known))
Matt Spinler937a2322019-01-23 13:54:22 -06001022 {
1023 return;
1024 }
Matt Spinler09be5762019-01-23 14:28:17 -06001025 associationChanged(server, associations, message.get_path(),
1026 well_known);
Ed Tanous60520632018-06-11 17:46:52 -07001027 }
1028 };
1029 sdbusplus::bus::match::match associationChanged(
1030 static_cast<sdbusplus::bus::bus&>(*system_bus),
1031 sdbusplus::bus::match::rules::interface(
1032 "org.freedesktop.DBus.Properties") +
1033 sdbusplus::bus::match::rules::member("PropertiesChanged") +
1034 sdbusplus::bus::match::rules::argN(0, ASSOCIATIONS_INTERFACE),
1035 associationChangedHandler);
1036
1037 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
1038 server.add_interface("/xyz/openbmc_project/object_mapper",
1039 "xyz.openbmc_project.ObjectMapper");
1040
1041 iface->register_method(
Matt Spinler6a39e8c2018-11-13 11:11:36 -06001042 "GetAncestors", [&interface_map](std::string& req_path,
Ed Tanous60520632018-06-11 17:46:52 -07001043 std::vector<std::string>& interfaces) {
1044 // Interfaces need to be sorted for intersect to function
1045 std::sort(interfaces.begin(), interfaces.end());
1046
James Feistd7322872019-01-28 11:25:00 -08001047 if (boost::ends_with(req_path, "/"))
Matt Spinler6a39e8c2018-11-13 11:11:36 -06001048 {
James Feistd7322872019-01-28 11:25:00 -08001049 req_path.pop_back();
1050 }
1051 if (req_path.size() &&
1052 interface_map.find(req_path) == interface_map.end())
1053 {
1054 throw NotFoundException();
Matt Spinler6a39e8c2018-11-13 11:11:36 -06001055 }
1056
Ed Tanous60520632018-06-11 17:46:52 -07001057 std::vector<interface_map_type::value_type> ret;
1058 for (auto& object_path : interface_map)
1059 {
1060 auto& this_path = object_path.first;
Matt Spinlerea6516c2018-09-25 16:23:13 -05001061 if (boost::starts_with(req_path, this_path) &&
1062 (req_path != this_path))
Ed Tanous60520632018-06-11 17:46:52 -07001063 {
1064 if (interfaces.empty())
1065 {
1066 ret.emplace_back(object_path);
1067 }
1068 else
1069 {
1070 for (auto& interface_map : object_path.second)
1071 {
1072
1073 if (intersect(interfaces.begin(), interfaces.end(),
1074 interface_map.second.begin(),
1075 interface_map.second.end()))
1076 {
Matt Spinler47c09752018-11-29 14:54:13 -06001077 addObjectMapResult(ret, this_path,
1078 interface_map);
Ed Tanous60520632018-06-11 17:46:52 -07001079 }
1080 }
1081 }
1082 }
1083 }
Ed Tanous60520632018-06-11 17:46:52 -07001084
1085 return ret;
1086 });
1087
1088 iface->register_method(
1089 "GetObject", [&interface_map](const std::string& path,
1090 std::vector<std::string>& interfaces) {
Matt Spinler7a38d412018-09-18 16:13:25 -05001091 boost::container::flat_map<std::string,
1092 boost::container::flat_set<std::string>>
1093 results;
1094
Ed Tanous60520632018-06-11 17:46:52 -07001095 // Interfaces need to be sorted for intersect to function
1096 std::sort(interfaces.begin(), interfaces.end());
1097 auto path_ref = interface_map.find(path);
1098 if (path_ref == interface_map.end())
1099 {
1100 throw NotFoundException();
1101 }
1102 if (interfaces.empty())
1103 {
1104 return path_ref->second;
1105 }
1106 for (auto& interface_map : path_ref->second)
1107 {
1108 if (intersect(interfaces.begin(), interfaces.end(),
1109 interface_map.second.begin(),
1110 interface_map.second.end()))
1111 {
Matt Spinler7a38d412018-09-18 16:13:25 -05001112 results.emplace(interface_map.first, interface_map.second);
Ed Tanous60520632018-06-11 17:46:52 -07001113 }
1114 }
Matt Spinler7a38d412018-09-18 16:13:25 -05001115
1116 if (results.empty())
1117 {
1118 throw NotFoundException();
1119 }
1120
1121 return results;
Ed Tanous60520632018-06-11 17:46:52 -07001122 });
1123
1124 iface->register_method(
Matt Spinler153494e2018-11-07 16:35:40 -06001125 "GetSubTree", [&interface_map](std::string& req_path, int32_t depth,
1126 std::vector<std::string>& interfaces) {
Ed Tanous60520632018-06-11 17:46:52 -07001127 if (depth <= 0)
1128 {
1129 depth = std::numeric_limits<int32_t>::max();
1130 }
1131 // Interfaces need to be sorted for intersect to function
1132 std::sort(interfaces.begin(), interfaces.end());
1133 std::vector<interface_map_type::value_type> ret;
1134
James Feistd7322872019-01-28 11:25:00 -08001135 if (boost::ends_with(req_path, "/"))
Matt Spinler153494e2018-11-07 16:35:40 -06001136 {
James Feistd7322872019-01-28 11:25:00 -08001137 req_path.pop_back();
1138 }
1139 if (req_path.size() &&
1140 interface_map.find(req_path) == interface_map.end())
1141 {
1142 throw NotFoundException();
Matt Spinler153494e2018-11-07 16:35:40 -06001143 }
1144
Ed Tanous60520632018-06-11 17:46:52 -07001145 for (auto& object_path : interface_map)
1146 {
1147 auto& this_path = object_path.first;
Matt Spinler153494e2018-11-07 16:35:40 -06001148
1149 if (this_path == req_path)
1150 {
1151 continue;
1152 }
1153
Ed Tanous60520632018-06-11 17:46:52 -07001154 if (boost::starts_with(this_path, req_path))
1155 {
1156 // count the number of slashes past the search term
1157 int32_t this_depth =
1158 std::count(this_path.begin() + req_path.size(),
1159 this_path.end(), '/');
1160 if (this_depth <= depth)
1161 {
Ed Tanous60520632018-06-11 17:46:52 -07001162 for (auto& interface_map : object_path.second)
1163 {
1164 if (intersect(interfaces.begin(), interfaces.end(),
1165 interface_map.second.begin(),
Matt Spinler9f0958e2018-09-11 08:26:10 -05001166 interface_map.second.end()) ||
1167 interfaces.empty())
Ed Tanous60520632018-06-11 17:46:52 -07001168 {
Matt Spinler47c09752018-11-29 14:54:13 -06001169 addObjectMapResult(ret, this_path,
1170 interface_map);
Ed Tanous60520632018-06-11 17:46:52 -07001171 }
1172 }
Ed Tanous60520632018-06-11 17:46:52 -07001173 }
1174 }
1175 }
Matt Spinler6a39e8c2018-11-13 11:11:36 -06001176
Ed Tanous60520632018-06-11 17:46:52 -07001177 return ret;
1178 });
1179
1180 iface->register_method(
1181 "GetSubTreePaths",
Matt Spinler153494e2018-11-07 16:35:40 -06001182 [&interface_map](std::string& req_path, int32_t depth,
Ed Tanous60520632018-06-11 17:46:52 -07001183 std::vector<std::string>& interfaces) {
1184 if (depth <= 0)
1185 {
1186 depth = std::numeric_limits<int32_t>::max();
1187 }
1188 // Interfaces need to be sorted for intersect to function
1189 std::sort(interfaces.begin(), interfaces.end());
1190 std::vector<std::string> ret;
Matt Spinler153494e2018-11-07 16:35:40 -06001191
James Feistd7322872019-01-28 11:25:00 -08001192 if (boost::ends_with(req_path, "/"))
Matt Spinler153494e2018-11-07 16:35:40 -06001193 {
James Feistd7322872019-01-28 11:25:00 -08001194 req_path.pop_back();
1195 }
1196 if (req_path.size() &&
1197 interface_map.find(req_path) == interface_map.end())
1198 {
1199 throw NotFoundException();
Matt Spinler153494e2018-11-07 16:35:40 -06001200 }
1201
Ed Tanous60520632018-06-11 17:46:52 -07001202 for (auto& object_path : interface_map)
1203 {
1204 auto& this_path = object_path.first;
Matt Spinler153494e2018-11-07 16:35:40 -06001205
1206 if (this_path == req_path)
1207 {
1208 continue;
1209 }
1210
Ed Tanous60520632018-06-11 17:46:52 -07001211 if (boost::starts_with(this_path, req_path))
1212 {
1213 // count the number of slashes past the search term
1214 int this_depth =
1215 std::count(this_path.begin() + req_path.size(),
1216 this_path.end(), '/');
1217 if (this_depth <= depth)
1218 {
1219 bool add = interfaces.empty();
1220 for (auto& interface_map : object_path.second)
1221 {
1222 if (intersect(interfaces.begin(), interfaces.end(),
1223 interface_map.second.begin(),
1224 interface_map.second.end()))
1225 {
1226 add = true;
1227 break;
1228 }
1229 }
1230 if (add)
1231 {
1232 // TODO(ed) this is a copy
1233 ret.emplace_back(this_path);
1234 }
1235 }
1236 }
1237 }
Matt Spinler6a39e8c2018-11-13 11:11:36 -06001238
Ed Tanous60520632018-06-11 17:46:52 -07001239 return ret;
1240 });
1241
1242 iface->initialize();
1243
1244 io.post([&]() {
1245 doListNames(io, interface_map, system_bus.get(), name_owners, server);
1246 });
1247
Ed Tanous60520632018-06-11 17:46:52 -07001248 io.run();
1249}