blob: e96ffe376e226157a0a9b7a8b981084515ebfdaa [file] [log] [blame]
Andrew Geisslera80a3af2019-02-04 14:01:49 -06001#include "associations.hpp"
Andrew Geissler3b025e62019-02-01 10:33:54 -06002#include "processing.hpp"
Matt Spinlerdd945862018-09-07 12:41:05 -05003#include "src/argument.hpp"
4
Ed Tanous60520632018-06-11 17:46:52 -07005#include <tinyxml2.h>
6
7#include <atomic>
8#include <boost/algorithm/string/predicate.hpp>
9#include <boost/container/flat_map.hpp>
Ed Tanous60520632018-06-11 17:46:52 -070010#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
Andrew Geissler67e5a422019-02-04 13:00:59 -060033AssociationInterfaces associationInterfaces;
Matt Spinler937a2322019-01-23 13:54:22 -060034AssociationOwnersType associationOwners;
35
Andrew Geissler82815da2019-02-04 12:19:41 -060036static WhiteBlackList service_whitelist;
37static WhiteBlackList service_blacklist;
Matt Spinlerdd945862018-09-07 12:41:05 -050038
Ed Tanous60520632018-06-11 17:46:52 -070039/** Exception thrown when a path is not found in the object list. */
40struct NotFoundException final : public sdbusplus::exception_t
41{
42 const char* name() const noexcept override
43 {
44 return "org.freedesktop.DBus.Error.FileNotFound";
45 };
46 const char* description() const noexcept override
47 {
48 return "path or object not found";
49 };
50 const char* what() const noexcept override
51 {
52 return "org.freedesktop.DBus.Error.FileNotFound: "
53 "The requested object was not found";
54 };
55};
56
Ed Tanous60520632018-06-11 17:46:52 -070057void update_owners(sdbusplus::asio::connection* conn,
58 boost::container::flat_map<std::string, std::string>& owners,
59 const std::string& new_object)
60{
61 if (boost::starts_with(new_object, ":"))
62 {
63 return;
64 }
65 conn->async_method_call(
66 [&, new_object](const boost::system::error_code ec,
67 const std::string& nameOwner) {
68 if (ec)
69 {
70 std::cerr << "Error getting owner of " << new_object << " : "
71 << ec << "\n";
72 return;
73 }
74 owners[nameOwner] = new_object;
75 },
76 "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetNameOwner",
77 new_object);
78}
79
80void send_introspection_complete_signal(sdbusplus::asio::connection* system_bus,
81 const std::string& process_name)
82{
83 // TODO(ed) This signal doesn't get exposed properly in the
84 // introspect right now. Find out how to register signals in
85 // sdbusplus
86 sdbusplus::message::message m = system_bus->new_signal(
87 "/xyz/openbmc_project/object_mapper",
88 "xyz.openbmc_project.ObjectMapper.Private", "IntrospectionComplete");
89 m.append(process_name);
90 m.signal_send();
91}
92
93struct InProgressIntrospect
94{
95 InProgressIntrospect(
96 sdbusplus::asio::connection* system_bus, boost::asio::io_service& io,
Matt Spinleraecabe82018-09-19 13:25:42 -050097 const std::string& process_name
98#ifdef DEBUG
99 ,
Ed Tanous60520632018-06-11 17:46:52 -0700100 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
Matt Spinleraecabe82018-09-19 13:25:42 -0500101 global_start_time
102#endif
103 ) :
Ed Tanous60520632018-06-11 17:46:52 -0700104 system_bus(system_bus),
Matt Spinleraecabe82018-09-19 13:25:42 -0500105 io(io), process_name(process_name)
106#ifdef DEBUG
107 ,
Ed Tanous60520632018-06-11 17:46:52 -0700108 global_start_time(global_start_time),
109 process_start_time(std::chrono::steady_clock::now())
Matt Spinleraecabe82018-09-19 13:25:42 -0500110#endif
Ed Tanous60520632018-06-11 17:46:52 -0700111 {
112 }
113 ~InProgressIntrospect()
114 {
115 send_introspection_complete_signal(system_bus, process_name);
Matt Spinleraecabe82018-09-19 13:25:42 -0500116
117#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700118 std::chrono::duration<float> diff =
119 std::chrono::steady_clock::now() - process_start_time;
120 std::cout << std::setw(50) << process_name << " scan took "
121 << diff.count() << " seconds\n";
122
123 // If we're the last outstanding caller globally, calculate the
124 // time it took
125 if (global_start_time != nullptr && global_start_time.use_count() == 1)
126 {
127 diff = std::chrono::steady_clock::now() - *global_start_time;
128 std::cout << "Total scan took " << diff.count()
129 << " seconds to complete\n";
130 }
Matt Spinleraecabe82018-09-19 13:25:42 -0500131#endif
Ed Tanous60520632018-06-11 17:46:52 -0700132 }
133 sdbusplus::asio::connection* system_bus;
134 boost::asio::io_service& io;
135 std::string process_name;
Matt Spinleraecabe82018-09-19 13:25:42 -0500136#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700137 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
138 global_start_time;
139 std::chrono::time_point<std::chrono::steady_clock> process_start_time;
Matt Spinleraecabe82018-09-19 13:25:42 -0500140#endif
Ed Tanous60520632018-06-11 17:46:52 -0700141};
142
Matt Spinler09be5762019-01-23 14:28:17 -0600143// Remove paths from the endpoints property of an association.
144// If the last endpoint was removed, then remove the whole
145// association object, otherwise just set the property.
146void removeAssociationEndpoints(
147 sdbusplus::asio::object_server& objectServer, const std::string& assocPath,
148 const std::string& owner,
149 const boost::container::flat_set<std::string>& endpointsToRemove)
150{
151 auto assoc = associationInterfaces.find(assocPath);
152 if (assoc == associationInterfaces.end())
153 {
154 return;
155 }
156
157 auto& endpointsInDBus = std::get<endpointsPos>(assoc->second);
158
159 for (const auto& endpointToRemove : endpointsToRemove)
160 {
161 auto e = std::find(endpointsInDBus.begin(), endpointsInDBus.end(),
162 endpointToRemove);
163
164 if (e != endpointsInDBus.end())
165 {
166 endpointsInDBus.erase(e);
167 }
168 }
169
170 if (endpointsInDBus.empty())
171 {
172 objectServer.remove_interface(std::get<ifacePos>(assoc->second));
173 std::get<ifacePos>(assoc->second) = nullptr;
174 std::get<endpointsPos>(assoc->second).clear();
175 }
176 else
177 {
178 std::get<ifacePos>(assoc->second)
179 ->set_property("endpoints", endpointsInDBus);
180 }
181}
182
183// Based on the latest values of the org.openbmc.Associations.associations
184// property, passed in via the newAssociations param, check if any of the
185// paths in the xyz.openbmc_project.Association.endpoints D-Bus property
186// for that association need to be removed. If the last path is removed
187// from the endpoints property, remove that whole association object from
188// D-Bus.
189void checkAssociationEndpointRemoves(
190 const std::string& sourcePath, const std::string& owner,
191 const AssociationPaths& newAssociations,
192 sdbusplus::asio::object_server& objectServer)
193{
194 // Find the services that have associations on this path.
195 auto originalOwners = associationOwners.find(sourcePath);
196 if (originalOwners == associationOwners.end())
197 {
198 return;
199 }
200
201 // Find the associations for this service
202 auto originalAssociations = originalOwners->second.find(owner);
203 if (originalAssociations == originalOwners->second.end())
204 {
205 return;
206 }
207
208 // Compare the new endpoints versus the original endpoints, and
209 // remove any of the original ones that aren't in the new list.
210 for (const auto& [originalAssocPath, originalEndpoints] :
211 originalAssociations->second)
212 {
213 // Check if this source even still has each association that
214 // was there previously, and if not, remove all of its endpoints
215 // from the D-Bus endpoints property which will cause the whole
216 // association path to be removed if no endpoints remain.
217 auto newEndpoints = newAssociations.find(originalAssocPath);
218 if (newEndpoints == newAssociations.end())
219 {
220 removeAssociationEndpoints(objectServer, originalAssocPath, owner,
221 originalEndpoints);
222 }
223 else
224 {
225 // The association is still there. Check if the endpoints
226 // changed.
227 boost::container::flat_set<std::string> toRemove;
228
229 for (auto& originalEndpoint : originalEndpoints)
230 {
231 if (std::find(newEndpoints->second.begin(),
232 newEndpoints->second.end(),
233 originalEndpoint) == newEndpoints->second.end())
234 {
235 toRemove.emplace(originalEndpoint);
236 }
237 }
238 if (!toRemove.empty())
239 {
240 removeAssociationEndpoints(objectServer, originalAssocPath,
241 owner, toRemove);
242 }
243 }
244 }
245}
246
Matt Spinler937a2322019-01-23 13:54:22 -0600247// Called when either a new org.openbmc.Associations interface was
248// created, or the associations property on that interface changed.
Matt Spinler09be5762019-01-23 14:28:17 -0600249void associationChanged(sdbusplus::asio::object_server& objectServer,
250 const std::vector<Association>& associations,
251 const std::string& path, const std::string& owner)
Ed Tanous60520632018-06-11 17:46:52 -0700252{
Matt Spinler937a2322019-01-23 13:54:22 -0600253 AssociationPaths objects;
254
Ed Tanous60520632018-06-11 17:46:52 -0700255 for (const Association& association : associations)
256 {
257 std::string forward;
258 std::string reverse;
259 std::string endpoint;
260 std::tie(forward, reverse, endpoint) = association;
261
262 if (forward.size())
263 {
264 objects[path + "/" + forward].emplace(endpoint);
265 }
266 if (reverse.size())
267 {
268 if (endpoint.empty())
269 {
270 std::cerr << "Found invalid association on path " << path
271 << "\n";
272 continue;
273 }
274 objects[endpoint + "/" + reverse].emplace(path);
275 }
276 }
277 for (const auto& object : objects)
278 {
279 // the mapper exposes the new association interface but intakes
280 // the old
281
282 auto& iface = associationInterfaces[object.first];
Matt Spinler1036b4d2018-09-18 16:45:29 -0500283 auto& i = std::get<ifacePos>(iface);
284 auto& endpoints = std::get<endpointsPos>(iface);
Matt Spinler1036b4d2018-09-18 16:45:29 -0500285
286 // Only add new endpoints
287 for (auto& e : object.second)
288 {
289 if (std::find(endpoints.begin(), endpoints.end(), e) ==
290 endpoints.end())
291 {
292 endpoints.push_back(e);
293 }
294 }
295
296 // If the interface already exists, only need to update
297 // the property value, otherwise create it
298 if (i)
299 {
300 i->set_property("endpoints", endpoints);
301 }
302 else
303 {
304 i = objectServer.add_interface(object.first,
305 XYZ_ASSOCIATION_INTERFACE);
306 i->register_property("endpoints", endpoints);
307 i->initialize();
308 }
309 }
Matt Spinler1036b4d2018-09-18 16:45:29 -0500310
Matt Spinler09be5762019-01-23 14:28:17 -0600311 // Check for endpoints being removed instead of added
312 checkAssociationEndpointRemoves(path, owner, objects, objectServer);
313
Matt Spinler937a2322019-01-23 13:54:22 -0600314 // Update associationOwners with the latest info
315 auto a = associationOwners.find(path);
316 if (a != associationOwners.end())
Matt Spinler1036b4d2018-09-18 16:45:29 -0500317 {
Matt Spinler937a2322019-01-23 13:54:22 -0600318 auto o = a->second.find(owner);
319 if (o != a->second.end())
Matt Spinler1036b4d2018-09-18 16:45:29 -0500320 {
Matt Spinler937a2322019-01-23 13:54:22 -0600321 o->second = std::move(objects);
Matt Spinler1036b4d2018-09-18 16:45:29 -0500322 }
323 else
324 {
Matt Spinler937a2322019-01-23 13:54:22 -0600325 a->second.emplace(owner, std::move(objects));
326 }
327 }
328 else
329 {
330 boost::container::flat_map<std::string, AssociationPaths> owners;
331 owners.emplace(owner, std::move(objects));
332 associationOwners.emplace(path, owners);
333 }
334}
Matt Spinler1036b4d2018-09-18 16:45:29 -0500335
Ed Tanous60520632018-06-11 17:46:52 -0700336void do_associations(sdbusplus::asio::connection* system_bus,
337 sdbusplus::asio::object_server& objectServer,
338 const std::string& processName, const std::string& path)
339{
340 system_bus->async_method_call(
Matt Spinler937a2322019-01-23 13:54:22 -0600341 [&objectServer, path, processName](
342 const boost::system::error_code ec,
343 const sdbusplus::message::variant<std::vector<Association>>&
344 variantAssociations) {
Ed Tanous60520632018-06-11 17:46:52 -0700345 if (ec)
346 {
347 std::cerr << "Error getting associations from " << path << "\n";
348 }
349 std::vector<Association> associations =
350 sdbusplus::message::variant_ns::get<std::vector<Association>>(
351 variantAssociations);
Matt Spinler09be5762019-01-23 14:28:17 -0600352 associationChanged(objectServer, associations, path, processName);
Ed Tanous60520632018-06-11 17:46:52 -0700353 },
354 processName, path, "org.freedesktop.DBus.Properties", "Get",
355 ASSOCIATIONS_INTERFACE, "associations");
356}
357
358void do_introspect(sdbusplus::asio::connection* system_bus,
359 std::shared_ptr<InProgressIntrospect> transaction,
360 interface_map_type& interface_map,
361 sdbusplus::asio::object_server& objectServer,
362 std::string path)
363{
364 system_bus->async_method_call(
365 [&interface_map, &objectServer, transaction, path,
366 system_bus](const boost::system::error_code ec,
367 const std::string& introspect_xml) {
368 if (ec)
369 {
370 std::cerr << "Introspect call failed with error: " << ec << ", "
371 << ec.message()
372 << " on process: " << transaction->process_name
373 << " path: " << path << "\n";
374 return;
375 }
376
377 tinyxml2::XMLDocument doc;
378
379 tinyxml2::XMLError e = doc.Parse(introspect_xml.c_str());
380 if (e != tinyxml2::XMLError::XML_SUCCESS)
381 {
382 std::cerr << "XML parsing failed\n";
383 return;
384 }
385
386 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
387 if (pRoot == nullptr)
388 {
389 std::cerr << "XML document did not contain any data\n";
390 return;
391 }
392 auto& thisPathMap = interface_map[path];
Ed Tanous60520632018-06-11 17:46:52 -0700393 tinyxml2::XMLElement* pElement =
394 pRoot->FirstChildElement("interface");
395 while (pElement != nullptr)
396 {
397 const char* iface_name = pElement->Attribute("name");
398 if (iface_name == nullptr)
399 {
400 continue;
401 }
402
Matt Spinlerdd945862018-09-07 12:41:05 -0500403 std::string iface{iface_name};
404
Ed Tanousd4dd96a2018-11-12 11:37:44 -0800405 thisPathMap[transaction->process_name].emplace(iface_name);
406
Ed Tanous60520632018-06-11 17:46:52 -0700407 if (std::strcmp(iface_name, ASSOCIATIONS_INTERFACE) == 0)
408 {
409 do_associations(system_bus, objectServer,
410 transaction->process_name, path);
411 }
Ed Tanous60520632018-06-11 17:46:52 -0700412
413 pElement = pElement->NextSiblingElement("interface");
414 }
415
Ed Tanous50232cd2018-11-12 11:34:43 -0800416 pElement = pRoot->FirstChildElement("node");
417 while (pElement != nullptr)
Ed Tanous60520632018-06-11 17:46:52 -0700418 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800419 const char* child_path = pElement->Attribute("name");
420 if (child_path != nullptr)
Ed Tanous60520632018-06-11 17:46:52 -0700421 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800422 std::string parent_path(path);
423 if (parent_path == "/")
Ed Tanous60520632018-06-11 17:46:52 -0700424 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800425 parent_path.clear();
Ed Tanous60520632018-06-11 17:46:52 -0700426 }
Ed Tanous50232cd2018-11-12 11:34:43 -0800427
428 do_introspect(system_bus, transaction, interface_map,
429 objectServer, parent_path + "/" + child_path);
Ed Tanous60520632018-06-11 17:46:52 -0700430 }
Ed Tanous50232cd2018-11-12 11:34:43 -0800431 pElement = pElement->NextSiblingElement("node");
Ed Tanous60520632018-06-11 17:46:52 -0700432 }
433 },
434 transaction->process_name, path, "org.freedesktop.DBus.Introspectable",
435 "Introspect");
436}
437
Ed Tanous60520632018-06-11 17:46:52 -0700438void start_new_introspect(
439 sdbusplus::asio::connection* system_bus, boost::asio::io_service& io,
440 interface_map_type& interface_map, const std::string& process_name,
Matt Spinleraecabe82018-09-19 13:25:42 -0500441#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700442 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
443 global_start_time,
Matt Spinleraecabe82018-09-19 13:25:42 -0500444#endif
Ed Tanous60520632018-06-11 17:46:52 -0700445 sdbusplus::asio::object_server& objectServer)
446{
Andrew Geissler82815da2019-02-04 12:19:41 -0600447 if (needToIntrospect(process_name, service_whitelist, service_blacklist))
Ed Tanous60520632018-06-11 17:46:52 -0700448 {
Ed Tanous60520632018-06-11 17:46:52 -0700449 std::shared_ptr<InProgressIntrospect> transaction =
Matt Spinleraecabe82018-09-19 13:25:42 -0500450 std::make_shared<InProgressIntrospect>(system_bus, io, process_name
451#ifdef DEBUG
452 ,
453 global_start_time
454#endif
455 );
Ed Tanous60520632018-06-11 17:46:52 -0700456
457 do_introspect(system_bus, transaction, interface_map, objectServer,
458 "/");
459 }
460}
461
462// TODO(ed) replace with std::set_intersection once c++17 is available
463template <class InputIt1, class InputIt2>
464bool intersect(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
465{
466 while (first1 != last1 && first2 != last2)
467 {
468 if (*first1 < *first2)
469 {
470 ++first1;
471 continue;
472 }
473 if (*first2 < *first1)
474 {
475 ++first2;
476 continue;
477 }
478 return true;
479 }
480 return false;
481}
482
483void doListNames(
484 boost::asio::io_service& io, interface_map_type& interface_map,
485 sdbusplus::asio::connection* system_bus,
486 boost::container::flat_map<std::string, std::string>& name_owners,
487 sdbusplus::asio::object_server& objectServer)
488{
489 system_bus->async_method_call(
490 [&io, &interface_map, &name_owners, &objectServer,
491 system_bus](const boost::system::error_code ec,
492 std::vector<std::string> process_names) {
493 if (ec)
494 {
495 std::cerr << "Error getting names: " << ec << "\n";
496 std::exit(EXIT_FAILURE);
497 return;
498 }
Ed Tanous60520632018-06-11 17:46:52 -0700499 // Try to make startup consistent
500 std::sort(process_names.begin(), process_names.end());
Matt Spinleraecabe82018-09-19 13:25:42 -0500501#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700502 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
503 global_start_time = std::make_shared<
504 std::chrono::time_point<std::chrono::steady_clock>>(
505 std::chrono::steady_clock::now());
Matt Spinleraecabe82018-09-19 13:25:42 -0500506#endif
Ed Tanous60520632018-06-11 17:46:52 -0700507 for (const std::string& process_name : process_names)
508 {
Andrew Geissler82815da2019-02-04 12:19:41 -0600509 if (needToIntrospect(process_name, service_whitelist,
510 service_blacklist))
Ed Tanous60520632018-06-11 17:46:52 -0700511 {
512 start_new_introspect(system_bus, io, interface_map,
Matt Spinleraecabe82018-09-19 13:25:42 -0500513 process_name,
514#ifdef DEBUG
515 global_start_time,
516#endif
Ed Tanous60520632018-06-11 17:46:52 -0700517 objectServer);
518 update_owners(system_bus, name_owners, process_name);
519 }
520 }
521 },
522 "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
523 "ListNames");
524}
525
Matt Spinlerdd945862018-09-07 12:41:05 -0500526void splitArgs(const std::string& stringArgs,
527 boost::container::flat_set<std::string>& listArgs)
528{
529 std::istringstream args;
530 std::string arg;
531
532 args.str(stringArgs);
533
534 while (!args.eof())
535 {
536 args >> arg;
537 if (!arg.empty())
538 {
539 listArgs.insert(arg);
540 }
541 }
542}
543
Matt Spinler47c09752018-11-29 14:54:13 -0600544void addObjectMapResult(
545 std::vector<interface_map_type::value_type>& objectMap,
Matt Spinler9f0958e2018-09-11 08:26:10 -0500546 const std::string& objectPath,
547 const std::pair<std::string, boost::container::flat_set<std::string>>&
548 interfaceMap)
549{
550 // Adds an object path/service name/interface list entry to
Matt Spinler47c09752018-11-29 14:54:13 -0600551 // the results of GetSubTree and GetAncestors.
Matt Spinler9f0958e2018-09-11 08:26:10 -0500552 // If an entry for the object path already exists, just add the
553 // service name and interfaces to that entry, otherwise create
554 // a new entry.
555 auto entry = std::find_if(
Matt Spinler47c09752018-11-29 14:54:13 -0600556 objectMap.begin(), objectMap.end(),
Matt Spinler9f0958e2018-09-11 08:26:10 -0500557 [&objectPath](const auto& i) { return objectPath == i.first; });
558
Matt Spinler47c09752018-11-29 14:54:13 -0600559 if (entry != objectMap.end())
Matt Spinler9f0958e2018-09-11 08:26:10 -0500560 {
561 entry->second.emplace(interfaceMap);
562 }
563 else
564 {
565 interface_map_type::value_type object;
566 object.first = objectPath;
567 object.second.emplace(interfaceMap);
Matt Spinler47c09752018-11-29 14:54:13 -0600568 objectMap.push_back(object);
Matt Spinler9f0958e2018-09-11 08:26:10 -0500569 }
570}
571
Matt Spinlera82779f2019-01-09 12:39:42 -0600572// Remove parents of the passed in path that:
573// 1) Only have the 3 default interfaces on them
574// - Means D-Bus created these, not application code,
575// with the Properties, Introspectable, and Peer ifaces
576// 2) Have no other child for this owner
577void removeUnneededParents(const std::string& objectPath,
578 const std::string& owner,
579 interface_map_type& interface_map)
580{
581 auto parent = objectPath;
582
583 while (true)
584 {
585 auto pos = parent.find_last_of('/');
586 if ((pos == std::string::npos) || (pos == 0))
587 {
588 break;
589 }
590 parent = parent.substr(0, pos);
591
592 auto parent_it = interface_map.find(parent);
593 if (parent_it == interface_map.end())
594 {
595 break;
596 }
597
598 auto ifaces_it = parent_it->second.find(owner);
599 if (ifaces_it == parent_it->second.end())
600 {
601 break;
602 }
603
604 if (ifaces_it->second.size() != 3)
605 {
606 break;
607 }
608
609 auto child_path = parent + '/';
610
611 // Remove this parent if there isn't a remaining child on this owner
612 auto child = std::find_if(
613 interface_map.begin(), interface_map.end(),
614 [&owner, &child_path](const auto& entry) {
615 return boost::starts_with(entry.first, child_path) &&
616 (entry.second.find(owner) != entry.second.end());
617 });
618
619 if (child == interface_map.end())
620 {
621 parent_it->second.erase(ifaces_it);
622 if (parent_it->second.empty())
623 {
624 interface_map.erase(parent_it);
625 }
626 }
627 else
628 {
629 break;
630 }
631 }
632}
633
Ed Tanous60520632018-06-11 17:46:52 -0700634int main(int argc, char** argv)
635{
Matt Spinlerdd945862018-09-07 12:41:05 -0500636 auto options = ArgumentParser(argc, argv);
Ed Tanous60520632018-06-11 17:46:52 -0700637 boost::asio::io_service io;
638 std::shared_ptr<sdbusplus::asio::connection> system_bus =
639 std::make_shared<sdbusplus::asio::connection>(io);
640
Matt Spinlerdd945862018-09-07 12:41:05 -0500641 splitArgs(options["service-namespaces"], service_whitelist);
Matt Spinlerdd945862018-09-07 12:41:05 -0500642 splitArgs(options["service-blacklists"], service_blacklist);
643
Ed Tanousd4dd96a2018-11-12 11:37:44 -0800644 // TODO(Ed) Remove this once all service files are updated to not use this.
645 // For now, simply squash the input, and ignore it.
646 boost::container::flat_set<std::string> iface_whitelist;
647 splitArgs(options["interface-namespaces"], iface_whitelist);
648
Ed Tanous60520632018-06-11 17:46:52 -0700649 system_bus->request_name(OBJECT_MAPPER_DBUS_NAME);
650 sdbusplus::asio::object_server server(system_bus);
651
652 // Construct a signal set registered for process termination.
653 boost::asio::signal_set signals(io, SIGINT, SIGTERM);
654 signals.async_wait([&io](const boost::system::error_code& error,
655 int signal_number) { io.stop(); });
656
657 interface_map_type interface_map;
658 boost::container::flat_map<std::string, std::string> name_owners;
659
660 std::function<void(sdbusplus::message::message & message)>
661 nameChangeHandler = [&interface_map, &io, &name_owners, &server,
662 system_bus](sdbusplus::message::message& message) {
663 std::string name;
664 std::string old_owner;
665 std::string new_owner;
666
667 message.read(name, old_owner, new_owner);
668
669 if (!old_owner.empty())
670 {
671 if (boost::starts_with(old_owner, ":"))
672 {
673 auto it = name_owners.find(old_owner);
674 if (it != name_owners.end())
675 {
676 name_owners.erase(it);
677 }
678 }
679 // Connection removed
680 interface_map_type::iterator path_it = interface_map.begin();
681 while (path_it != interface_map.end())
682 {
Matt Spinler1036b4d2018-09-18 16:45:29 -0500683 // If an associations interface is being removed,
684 // also need to remove the corresponding associations
685 // objects and properties.
686 auto ifaces = path_it->second.find(name);
687 if (ifaces != path_it->second.end())
688 {
689 auto assoc = std::find(ifaces->second.begin(),
690 ifaces->second.end(),
691 ASSOCIATIONS_INTERFACE);
692
693 if (assoc != ifaces->second.end())
694 {
Andrew Geissler67e5a422019-02-04 13:00:59 -0600695 removeAssociation(path_it->first, name, server,
696 associationOwners,
697 associationInterfaces);
Matt Spinler1036b4d2018-09-18 16:45:29 -0500698 }
699 }
700
Ed Tanous60520632018-06-11 17:46:52 -0700701 path_it->second.erase(name);
702 if (path_it->second.empty())
703 {
704 // If the last connection to the object is gone,
705 // delete the top level object
706 path_it = interface_map.erase(path_it);
707 continue;
708 }
709 path_it++;
710 }
711 }
712
713 if (!new_owner.empty())
714 {
Matt Spinleraecabe82018-09-19 13:25:42 -0500715#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700716 auto transaction = std::make_shared<
717 std::chrono::time_point<std::chrono::steady_clock>>(
718 std::chrono::steady_clock::now());
Matt Spinleraecabe82018-09-19 13:25:42 -0500719#endif
Ed Tanous60520632018-06-11 17:46:52 -0700720 // New daemon added
Andrew Geissler82815da2019-02-04 12:19:41 -0600721 if (needToIntrospect(name, service_whitelist,
722 service_blacklist))
Ed Tanous60520632018-06-11 17:46:52 -0700723 {
724 name_owners[new_owner] = name;
725 start_new_introspect(system_bus.get(), io, interface_map,
Matt Spinleraecabe82018-09-19 13:25:42 -0500726 name,
727#ifdef DEBUG
728 transaction,
729#endif
730 server);
Ed Tanous60520632018-06-11 17:46:52 -0700731 }
732 }
733 };
734
735 sdbusplus::bus::match::match nameOwnerChanged(
736 static_cast<sdbusplus::bus::bus&>(*system_bus),
737 sdbusplus::bus::match::rules::nameOwnerChanged(), nameChangeHandler);
738
739 std::function<void(sdbusplus::message::message & message)>
740 interfacesAddedHandler = [&interface_map, &name_owners, &server](
741 sdbusplus::message::message& message) {
742 sdbusplus::message::object_path obj_path;
743 std::vector<std::pair<
744 std::string, std::vector<std::pair<
745 std::string, sdbusplus::message::variant<
746 std::vector<Association>>>>>>
747 interfaces_added;
748 message.read(obj_path, interfaces_added);
749 std::string well_known;
Andrew Geissler3b025e62019-02-01 10:33:54 -0600750 if (!getWellKnown(name_owners, message.get_sender(), well_known))
Ed Tanous60520632018-06-11 17:46:52 -0700751 {
752 return; // only introspect well-known
753 }
Andrew Geissler82815da2019-02-04 12:19:41 -0600754 if (needToIntrospect(well_known, service_whitelist,
755 service_blacklist))
Ed Tanous60520632018-06-11 17:46:52 -0700756 {
757 auto& iface_list = interface_map[obj_path.str];
758
Matt Spinlercc74e4b2018-09-18 16:21:54 -0500759 for (const auto& interface_pair : interfaces_added)
Ed Tanous60520632018-06-11 17:46:52 -0700760 {
761 iface_list[well_known].emplace(interface_pair.first);
762
763 if (interface_pair.first == ASSOCIATIONS_INTERFACE)
764 {
765 const sdbusplus::message::variant<
766 std::vector<Association>>* variantAssociations =
767 nullptr;
768 for (const auto& interface : interface_pair.second)
769 {
770 if (interface.first == "associations")
771 {
772 variantAssociations = &(interface.second);
773 }
774 }
775 if (variantAssociations == nullptr)
776 {
777 std::cerr << "Illegal association found on "
778 << well_known << "\n";
779 continue;
780 }
781 std::vector<Association> associations =
782 sdbusplus::message::variant_ns::get<
783 std::vector<Association>>(*variantAssociations);
Matt Spinler09be5762019-01-23 14:28:17 -0600784 associationChanged(server, associations, obj_path.str,
785 well_known);
Ed Tanous60520632018-06-11 17:46:52 -0700786 }
787 }
Matt Spinler1b4a5022019-01-03 14:48:33 -0600788
789 // To handle the case where an object path is being created
790 // with 2 or more new path segments, check if the parent paths
791 // of this path are already in the interface map, and add them
792 // if they aren't with just the default freedesktop interfaces.
793 // This would be done via introspection if they would have
794 // already existed at startup. While we could also introspect
795 // them now to do the work, we know there aren't any other
796 // interfaces or we would have gotten signals for them as well,
797 // so take a shortcut to speed things up.
798 //
799 // This is all needed so that mapper operations can be done
800 // on the new parent paths.
801 using iface_map_iterator = interface_map_type::iterator;
802 using iface_map_value_type = boost::container::flat_map<
803 std::string, boost::container::flat_set<std::string>>;
804 using name_map_iterator = iface_map_value_type::iterator;
805
806 static const boost::container::flat_set<std::string>
807 default_ifaces{"org.freedesktop.DBus.Introspectable",
808 "org.freedesktop.DBus.Peer",
809 "org.freedesktop.DBus.Properties"};
810
811 std::string parent = obj_path.str;
812 auto pos = parent.find_last_of('/');
813
814 while (pos != std::string::npos)
815 {
816 parent = parent.substr(0, pos);
817
818 std::pair<iface_map_iterator, bool> parentEntry =
819 interface_map.insert(
820 std::make_pair(parent, iface_map_value_type{}));
821
822 std::pair<name_map_iterator, bool> ifaceEntry =
823 parentEntry.first->second.insert(
824 std::make_pair(well_known, default_ifaces));
825
826 if (!ifaceEntry.second)
827 {
828 // Entry was already there for this name so done.
829 break;
830 }
831
832 pos = parent.find_last_of('/');
833 }
Ed Tanous60520632018-06-11 17:46:52 -0700834 }
835 };
836
837 sdbusplus::bus::match::match interfacesAdded(
838 static_cast<sdbusplus::bus::bus&>(*system_bus),
839 sdbusplus::bus::match::rules::interfacesAdded(),
840 interfacesAddedHandler);
841
842 std::function<void(sdbusplus::message::message & message)>
843 interfacesRemovedHandler = [&interface_map, &name_owners, &server](
844 sdbusplus::message::message& message) {
845 sdbusplus::message::object_path obj_path;
846 std::vector<std::string> interfaces_removed;
847 message.read(obj_path, interfaces_removed);
848 auto connection_map = interface_map.find(obj_path.str);
849 if (connection_map == interface_map.end())
850 {
851 return;
852 }
853
854 std::string sender;
Andrew Geissler3b025e62019-02-01 10:33:54 -0600855 if (!getWellKnown(name_owners, message.get_sender(), sender))
Ed Tanous60520632018-06-11 17:46:52 -0700856 {
857 return;
858 }
859 for (const std::string& interface : interfaces_removed)
860 {
861 auto interface_set = connection_map->second.find(sender);
862 if (interface_set == connection_map->second.end())
863 {
864 continue;
865 }
866
867 if (interface == ASSOCIATIONS_INTERFACE)
868 {
Andrew Geissler67e5a422019-02-04 13:00:59 -0600869 removeAssociation(obj_path.str, sender, server,
870 associationOwners, associationInterfaces);
Ed Tanous60520632018-06-11 17:46:52 -0700871 }
872
873 interface_set->second.erase(interface);
874 // If this was the last interface on this connection,
875 // erase the connection
876 if (interface_set->second.empty())
877 {
878 connection_map->second.erase(interface_set);
879 }
880 }
881 // If this was the last connection on this object path,
882 // erase the object path
883 if (connection_map->second.empty())
884 {
885 interface_map.erase(connection_map);
886 }
Matt Spinlera82779f2019-01-09 12:39:42 -0600887
888 removeUnneededParents(obj_path.str, sender, interface_map);
Ed Tanous60520632018-06-11 17:46:52 -0700889 };
890
891 sdbusplus::bus::match::match interfacesRemoved(
892 static_cast<sdbusplus::bus::bus&>(*system_bus),
893 sdbusplus::bus::match::rules::interfacesRemoved(),
894 interfacesRemovedHandler);
895
896 std::function<void(sdbusplus::message::message & message)>
897 associationChangedHandler =
Matt Spinler937a2322019-01-23 13:54:22 -0600898 [&server, &name_owners](sdbusplus::message::message& message) {
Ed Tanous60520632018-06-11 17:46:52 -0700899 std::string objectName;
900 boost::container::flat_map<
901 std::string,
902 sdbusplus::message::variant<std::vector<Association>>>
903 values;
904 message.read(objectName, values);
905 auto findAssociations = values.find("associations");
906 if (findAssociations != values.end())
907 {
908 std::vector<Association> associations =
909 sdbusplus::message::variant_ns::get<
910 std::vector<Association>>(findAssociations->second);
Matt Spinler937a2322019-01-23 13:54:22 -0600911
912 std::string well_known;
Andrew Geissler3b025e62019-02-01 10:33:54 -0600913 if (!getWellKnown(name_owners, message.get_sender(),
914 well_known))
Matt Spinler937a2322019-01-23 13:54:22 -0600915 {
916 return;
917 }
Matt Spinler09be5762019-01-23 14:28:17 -0600918 associationChanged(server, associations, message.get_path(),
919 well_known);
Ed Tanous60520632018-06-11 17:46:52 -0700920 }
921 };
922 sdbusplus::bus::match::match associationChanged(
923 static_cast<sdbusplus::bus::bus&>(*system_bus),
924 sdbusplus::bus::match::rules::interface(
925 "org.freedesktop.DBus.Properties") +
926 sdbusplus::bus::match::rules::member("PropertiesChanged") +
927 sdbusplus::bus::match::rules::argN(0, ASSOCIATIONS_INTERFACE),
928 associationChangedHandler);
929
930 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
931 server.add_interface("/xyz/openbmc_project/object_mapper",
932 "xyz.openbmc_project.ObjectMapper");
933
934 iface->register_method(
Matt Spinler6a39e8c2018-11-13 11:11:36 -0600935 "GetAncestors", [&interface_map](std::string& req_path,
Ed Tanous60520632018-06-11 17:46:52 -0700936 std::vector<std::string>& interfaces) {
937 // Interfaces need to be sorted for intersect to function
938 std::sort(interfaces.begin(), interfaces.end());
939
James Feistd7322872019-01-28 11:25:00 -0800940 if (boost::ends_with(req_path, "/"))
Matt Spinler6a39e8c2018-11-13 11:11:36 -0600941 {
James Feistd7322872019-01-28 11:25:00 -0800942 req_path.pop_back();
943 }
944 if (req_path.size() &&
945 interface_map.find(req_path) == interface_map.end())
946 {
947 throw NotFoundException();
Matt Spinler6a39e8c2018-11-13 11:11:36 -0600948 }
949
Ed Tanous60520632018-06-11 17:46:52 -0700950 std::vector<interface_map_type::value_type> ret;
951 for (auto& object_path : interface_map)
952 {
953 auto& this_path = object_path.first;
Matt Spinlerea6516c2018-09-25 16:23:13 -0500954 if (boost::starts_with(req_path, this_path) &&
955 (req_path != this_path))
Ed Tanous60520632018-06-11 17:46:52 -0700956 {
957 if (interfaces.empty())
958 {
959 ret.emplace_back(object_path);
960 }
961 else
962 {
963 for (auto& interface_map : object_path.second)
964 {
965
966 if (intersect(interfaces.begin(), interfaces.end(),
967 interface_map.second.begin(),
968 interface_map.second.end()))
969 {
Matt Spinler47c09752018-11-29 14:54:13 -0600970 addObjectMapResult(ret, this_path,
971 interface_map);
Ed Tanous60520632018-06-11 17:46:52 -0700972 }
973 }
974 }
975 }
976 }
Ed Tanous60520632018-06-11 17:46:52 -0700977
978 return ret;
979 });
980
981 iface->register_method(
982 "GetObject", [&interface_map](const std::string& path,
983 std::vector<std::string>& interfaces) {
Matt Spinler7a38d412018-09-18 16:13:25 -0500984 boost::container::flat_map<std::string,
985 boost::container::flat_set<std::string>>
986 results;
987
Ed Tanous60520632018-06-11 17:46:52 -0700988 // Interfaces need to be sorted for intersect to function
989 std::sort(interfaces.begin(), interfaces.end());
990 auto path_ref = interface_map.find(path);
991 if (path_ref == interface_map.end())
992 {
993 throw NotFoundException();
994 }
995 if (interfaces.empty())
996 {
997 return path_ref->second;
998 }
999 for (auto& interface_map : path_ref->second)
1000 {
1001 if (intersect(interfaces.begin(), interfaces.end(),
1002 interface_map.second.begin(),
1003 interface_map.second.end()))
1004 {
Matt Spinler7a38d412018-09-18 16:13:25 -05001005 results.emplace(interface_map.first, interface_map.second);
Ed Tanous60520632018-06-11 17:46:52 -07001006 }
1007 }
Matt Spinler7a38d412018-09-18 16:13:25 -05001008
1009 if (results.empty())
1010 {
1011 throw NotFoundException();
1012 }
1013
1014 return results;
Ed Tanous60520632018-06-11 17:46:52 -07001015 });
1016
1017 iface->register_method(
Matt Spinler153494e2018-11-07 16:35:40 -06001018 "GetSubTree", [&interface_map](std::string& req_path, int32_t depth,
1019 std::vector<std::string>& interfaces) {
Ed Tanous60520632018-06-11 17:46:52 -07001020 if (depth <= 0)
1021 {
1022 depth = std::numeric_limits<int32_t>::max();
1023 }
1024 // Interfaces need to be sorted for intersect to function
1025 std::sort(interfaces.begin(), interfaces.end());
1026 std::vector<interface_map_type::value_type> ret;
1027
James Feistd7322872019-01-28 11:25:00 -08001028 if (boost::ends_with(req_path, "/"))
Matt Spinler153494e2018-11-07 16:35:40 -06001029 {
James Feistd7322872019-01-28 11:25:00 -08001030 req_path.pop_back();
1031 }
1032 if (req_path.size() &&
1033 interface_map.find(req_path) == interface_map.end())
1034 {
1035 throw NotFoundException();
Matt Spinler153494e2018-11-07 16:35:40 -06001036 }
1037
Ed Tanous60520632018-06-11 17:46:52 -07001038 for (auto& object_path : interface_map)
1039 {
1040 auto& this_path = object_path.first;
Matt Spinler153494e2018-11-07 16:35:40 -06001041
1042 if (this_path == req_path)
1043 {
1044 continue;
1045 }
1046
Ed Tanous60520632018-06-11 17:46:52 -07001047 if (boost::starts_with(this_path, req_path))
1048 {
1049 // count the number of slashes past the search term
1050 int32_t this_depth =
1051 std::count(this_path.begin() + req_path.size(),
1052 this_path.end(), '/');
1053 if (this_depth <= depth)
1054 {
Ed Tanous60520632018-06-11 17:46:52 -07001055 for (auto& interface_map : object_path.second)
1056 {
1057 if (intersect(interfaces.begin(), interfaces.end(),
1058 interface_map.second.begin(),
Matt Spinler9f0958e2018-09-11 08:26:10 -05001059 interface_map.second.end()) ||
1060 interfaces.empty())
Ed Tanous60520632018-06-11 17:46:52 -07001061 {
Matt Spinler47c09752018-11-29 14:54:13 -06001062 addObjectMapResult(ret, this_path,
1063 interface_map);
Ed Tanous60520632018-06-11 17:46:52 -07001064 }
1065 }
Ed Tanous60520632018-06-11 17:46:52 -07001066 }
1067 }
1068 }
Matt Spinler6a39e8c2018-11-13 11:11:36 -06001069
Ed Tanous60520632018-06-11 17:46:52 -07001070 return ret;
1071 });
1072
1073 iface->register_method(
1074 "GetSubTreePaths",
Matt Spinler153494e2018-11-07 16:35:40 -06001075 [&interface_map](std::string& req_path, int32_t depth,
Ed Tanous60520632018-06-11 17:46:52 -07001076 std::vector<std::string>& interfaces) {
1077 if (depth <= 0)
1078 {
1079 depth = std::numeric_limits<int32_t>::max();
1080 }
1081 // Interfaces need to be sorted for intersect to function
1082 std::sort(interfaces.begin(), interfaces.end());
1083 std::vector<std::string> ret;
Matt Spinler153494e2018-11-07 16:35:40 -06001084
James Feistd7322872019-01-28 11:25:00 -08001085 if (boost::ends_with(req_path, "/"))
Matt Spinler153494e2018-11-07 16:35:40 -06001086 {
James Feistd7322872019-01-28 11:25:00 -08001087 req_path.pop_back();
1088 }
1089 if (req_path.size() &&
1090 interface_map.find(req_path) == interface_map.end())
1091 {
1092 throw NotFoundException();
Matt Spinler153494e2018-11-07 16:35:40 -06001093 }
1094
Ed Tanous60520632018-06-11 17:46:52 -07001095 for (auto& object_path : interface_map)
1096 {
1097 auto& this_path = object_path.first;
Matt Spinler153494e2018-11-07 16:35:40 -06001098
1099 if (this_path == req_path)
1100 {
1101 continue;
1102 }
1103
Ed Tanous60520632018-06-11 17:46:52 -07001104 if (boost::starts_with(this_path, req_path))
1105 {
1106 // count the number of slashes past the search term
1107 int this_depth =
1108 std::count(this_path.begin() + req_path.size(),
1109 this_path.end(), '/');
1110 if (this_depth <= depth)
1111 {
1112 bool add = interfaces.empty();
1113 for (auto& interface_map : object_path.second)
1114 {
1115 if (intersect(interfaces.begin(), interfaces.end(),
1116 interface_map.second.begin(),
1117 interface_map.second.end()))
1118 {
1119 add = true;
1120 break;
1121 }
1122 }
1123 if (add)
1124 {
1125 // TODO(ed) this is a copy
1126 ret.emplace_back(this_path);
1127 }
1128 }
1129 }
1130 }
Matt Spinler6a39e8c2018-11-13 11:11:36 -06001131
Ed Tanous60520632018-06-11 17:46:52 -07001132 return ret;
1133 });
1134
1135 iface->initialize();
1136
1137 io.post([&]() {
1138 doListNames(io, interface_map, system_bus.get(), name_owners, server);
1139 });
1140
Ed Tanous60520632018-06-11 17:46:52 -07001141 io.run();
1142}