blob: 5fb93b2415593495de17bf156cd54d58c1c290ff [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"
Matt Spinler35396c12019-04-05 11:46:57 -05004#include "types.hpp"
Matt Spinlerdd945862018-09-07 12:41:05 -05005
Ed Tanous60520632018-06-11 17:46:52 -07006#include <tinyxml2.h>
7
Ed Tanous60520632018-06-11 17:46:52 -07008#include <boost/algorithm/string/predicate.hpp>
Ed Tanous21c60592020-08-17 23:43:46 -07009#include <boost/asio/io_context.hpp>
10#include <boost/asio/signal_set.hpp>
Ed Tanous60520632018-06-11 17:46:52 -070011#include <boost/container/flat_map.hpp>
Ed Tanous60520632018-06-11 17:46:52 -070012#include <sdbusplus/asio/connection.hpp>
13#include <sdbusplus/asio/object_server.hpp>
Konstantin Aladyshevb15df6b2022-01-11 14:50:55 +030014#include <xyz/openbmc_project/Common/error.hpp>
Ed Tanous60520632018-06-11 17:46:52 -070015
Brad Bishop23520882022-05-26 21:39:53 -040016#include <atomic>
17#include <chrono>
18#include <iomanip>
19#include <iostream>
20
Matt Spinlere2359fb2019-04-05 14:11:33 -050021AssociationMaps associationMaps;
Matt Spinler937a2322019-01-23 13:54:22 -060022
Andrew Geissler82815da2019-02-04 12:19:41 -060023static WhiteBlackList service_whitelist;
24static WhiteBlackList service_blacklist;
Matt Spinlerdd945862018-09-07 12:41:05 -050025
Ed Tanous60520632018-06-11 17:46:52 -070026void update_owners(sdbusplus::asio::connection* conn,
27 boost::container::flat_map<std::string, std::string>& owners,
28 const std::string& new_object)
29{
30 if (boost::starts_with(new_object, ":"))
31 {
32 return;
33 }
34 conn->async_method_call(
35 [&, new_object](const boost::system::error_code ec,
36 const std::string& nameOwner) {
37 if (ec)
38 {
39 std::cerr << "Error getting owner of " << new_object << " : "
40 << ec << "\n";
41 return;
42 }
43 owners[nameOwner] = new_object;
44 },
45 "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetNameOwner",
46 new_object);
47}
48
49void send_introspection_complete_signal(sdbusplus::asio::connection* system_bus,
50 const std::string& process_name)
51{
52 // TODO(ed) This signal doesn't get exposed properly in the
53 // introspect right now. Find out how to register signals in
54 // sdbusplus
55 sdbusplus::message::message m = system_bus->new_signal(
Brad Bishopa02cd542021-10-12 19:12:42 -040056 "/xyz/openbmc_project/object_mapper",
57 "xyz.openbmc_project.ObjectMapper.Private", "IntrospectionComplete");
Ed Tanous60520632018-06-11 17:46:52 -070058 m.append(process_name);
59 m.signal_send();
60}
61
62struct InProgressIntrospect
63{
64 InProgressIntrospect(
Ed Tanous21c60592020-08-17 23:43:46 -070065 sdbusplus::asio::connection* system_bus, boost::asio::io_context& io,
Matt Spinler11401e22019-04-08 13:13:25 -050066 const std::string& process_name, AssociationMaps& am
Matt Spinleraecabe82018-09-19 13:25:42 -050067#ifdef DEBUG
68 ,
Ed Tanous60520632018-06-11 17:46:52 -070069 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
Matt Spinleraecabe82018-09-19 13:25:42 -050070 global_start_time
71#endif
72 ) :
Ed Tanous60520632018-06-11 17:46:52 -070073 system_bus(system_bus),
Matt Spinler11401e22019-04-08 13:13:25 -050074 io(io), process_name(process_name), assocMaps(am)
Matt Spinleraecabe82018-09-19 13:25:42 -050075#ifdef DEBUG
76 ,
Ed Tanous60520632018-06-11 17:46:52 -070077 global_start_time(global_start_time),
78 process_start_time(std::chrono::steady_clock::now())
Matt Spinleraecabe82018-09-19 13:25:42 -050079#endif
Brad Bishop23520882022-05-26 21:39:53 -040080 {}
Ed Tanous60520632018-06-11 17:46:52 -070081 ~InProgressIntrospect()
82 {
83 send_introspection_complete_signal(system_bus, process_name);
Matt Spinleraecabe82018-09-19 13:25:42 -050084
85#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -070086 std::chrono::duration<float> diff =
87 std::chrono::steady_clock::now() - process_start_time;
88 std::cout << std::setw(50) << process_name << " scan took "
89 << diff.count() << " seconds\n";
90
91 // If we're the last outstanding caller globally, calculate the
92 // time it took
93 if (global_start_time != nullptr && global_start_time.use_count() == 1)
94 {
95 diff = std::chrono::steady_clock::now() - *global_start_time;
96 std::cout << "Total scan took " << diff.count()
97 << " seconds to complete\n";
98 }
Matt Spinleraecabe82018-09-19 13:25:42 -050099#endif
Ed Tanous60520632018-06-11 17:46:52 -0700100 }
101 sdbusplus::asio::connection* system_bus;
Ed Tanous21c60592020-08-17 23:43:46 -0700102 boost::asio::io_context& io;
Ed Tanous60520632018-06-11 17:46:52 -0700103 std::string process_name;
Matt Spinler11401e22019-04-08 13:13:25 -0500104 AssociationMaps& assocMaps;
Matt Spinleraecabe82018-09-19 13:25:42 -0500105#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700106 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
107 global_start_time;
108 std::chrono::time_point<std::chrono::steady_clock> process_start_time;
Matt Spinleraecabe82018-09-19 13:25:42 -0500109#endif
Ed Tanous60520632018-06-11 17:46:52 -0700110};
111
Ed Tanous60520632018-06-11 17:46:52 -0700112void do_associations(sdbusplus::asio::connection* system_bus,
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500113 interface_map_type& interfaceMap,
Ed Tanous60520632018-06-11 17:46:52 -0700114 sdbusplus::asio::object_server& objectServer,
Adrian Ambrożewiczbb40bd32021-02-12 13:36:26 +0100115 const std::string& processName, const std::string& path,
116 int timeoutRetries = 0)
Ed Tanous60520632018-06-11 17:46:52 -0700117{
Adrian Ambrożewiczbb40bd32021-02-12 13:36:26 +0100118 constexpr int maxTimeoutRetries = 3;
Ed Tanous60520632018-06-11 17:46:52 -0700119 system_bus->async_method_call(
Adrian Ambrożewiczbb40bd32021-02-12 13:36:26 +0100120 [&objectServer, path, processName, &interfaceMap, system_bus,
121 timeoutRetries](
Matt Spinler937a2322019-01-23 13:54:22 -0600122 const boost::system::error_code ec,
Patrick Williams2bb2d6b2020-05-13 17:59:02 -0500123 const std::variant<std::vector<Association>>& variantAssociations) {
Ed Tanous60520632018-06-11 17:46:52 -0700124 if (ec)
125 {
Adrian Ambrożewiczbb40bd32021-02-12 13:36:26 +0100126 if (ec.value() == boost::system::errc::timed_out &&
127 timeoutRetries < maxTimeoutRetries)
128 {
129 do_associations(system_bus, interfaceMap, objectServer,
130 processName, path, timeoutRetries + 1);
131 return;
132 }
Ed Tanous60520632018-06-11 17:46:52 -0700133 std::cerr << "Error getting associations from " << path << "\n";
134 }
135 std::vector<Association> associations =
Patrick Williamsb05bc122020-05-13 12:21:00 -0500136 std::get<std::vector<Association>>(variantAssociations);
Andrew Geissler4511b332019-02-21 15:40:40 -0600137 associationChanged(objectServer, associations, path, processName,
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500138 interfaceMap, associationMaps);
Ed Tanous60520632018-06-11 17:46:52 -0700139 },
140 processName, path, "org.freedesktop.DBus.Properties", "Get",
John Wangd0cf9422019-09-17 16:01:34 +0800141 assocDefsInterface, assocDefsProperty);
Ed Tanous60520632018-06-11 17:46:52 -0700142}
143
144void do_introspect(sdbusplus::asio::connection* system_bus,
145 std::shared_ptr<InProgressIntrospect> transaction,
146 interface_map_type& interface_map,
147 sdbusplus::asio::object_server& objectServer,
Vernon Maueryc52be0d2020-01-14 11:14:25 -0800148 std::string path, int timeoutRetries = 0)
Ed Tanous60520632018-06-11 17:46:52 -0700149{
Vernon Maueryc52be0d2020-01-14 11:14:25 -0800150 constexpr int maxTimeoutRetries = 3;
Ed Tanous60520632018-06-11 17:46:52 -0700151 system_bus->async_method_call(
Vernon Maueryc52be0d2020-01-14 11:14:25 -0800152 [&interface_map, &objectServer, transaction, path, system_bus,
153 timeoutRetries](const boost::system::error_code ec,
154 const std::string& introspect_xml) {
Ed Tanous60520632018-06-11 17:46:52 -0700155 if (ec)
156 {
Vernon Maueryc52be0d2020-01-14 11:14:25 -0800157 if (ec.value() == boost::system::errc::timed_out &&
158 timeoutRetries < maxTimeoutRetries)
159 {
160 do_introspect(system_bus, transaction, interface_map,
161 objectServer, path, timeoutRetries + 1);
162 return;
163 }
Ed Tanous60520632018-06-11 17:46:52 -0700164 std::cerr << "Introspect call failed with error: " << ec << ", "
165 << ec.message()
166 << " on process: " << transaction->process_name
167 << " path: " << path << "\n";
168 return;
169 }
170
171 tinyxml2::XMLDocument doc;
172
173 tinyxml2::XMLError e = doc.Parse(introspect_xml.c_str());
174 if (e != tinyxml2::XMLError::XML_SUCCESS)
175 {
176 std::cerr << "XML parsing failed\n";
177 return;
178 }
179
180 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
181 if (pRoot == nullptr)
182 {
183 std::cerr << "XML document did not contain any data\n";
184 return;
185 }
186 auto& thisPathMap = interface_map[path];
Ed Tanous60520632018-06-11 17:46:52 -0700187 tinyxml2::XMLElement* pElement =
188 pRoot->FirstChildElement("interface");
189 while (pElement != nullptr)
190 {
191 const char* iface_name = pElement->Attribute("name");
192 if (iface_name == nullptr)
193 {
194 continue;
195 }
196
Ed Tanousd4dd96a2018-11-12 11:37:44 -0800197 thisPathMap[transaction->process_name].emplace(iface_name);
198
John Wangd0cf9422019-09-17 16:01:34 +0800199 if (std::strcmp(iface_name, assocDefsInterface) == 0)
Ed Tanous60520632018-06-11 17:46:52 -0700200 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500201 do_associations(system_bus, interface_map, objectServer,
John Wangd0cf9422019-09-17 16:01:34 +0800202 transaction->process_name, path);
Ed Tanous60520632018-06-11 17:46:52 -0700203 }
Ed Tanous60520632018-06-11 17:46:52 -0700204
205 pElement = pElement->NextSiblingElement("interface");
206 }
207
Matt Spinler11401e22019-04-08 13:13:25 -0500208 // Check if this new path has a pending association that can
209 // now be completed.
210 checkIfPendingAssociation(path, interface_map,
211 transaction->assocMaps, objectServer);
212
Ed Tanous50232cd2018-11-12 11:34:43 -0800213 pElement = pRoot->FirstChildElement("node");
214 while (pElement != nullptr)
Ed Tanous60520632018-06-11 17:46:52 -0700215 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800216 const char* child_path = pElement->Attribute("name");
217 if (child_path != nullptr)
Ed Tanous60520632018-06-11 17:46:52 -0700218 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800219 std::string parent_path(path);
220 if (parent_path == "/")
Ed Tanous60520632018-06-11 17:46:52 -0700221 {
Ed Tanous50232cd2018-11-12 11:34:43 -0800222 parent_path.clear();
Ed Tanous60520632018-06-11 17:46:52 -0700223 }
Ed Tanous50232cd2018-11-12 11:34:43 -0800224
225 do_introspect(system_bus, transaction, interface_map,
226 objectServer, parent_path + "/" + child_path);
Ed Tanous60520632018-06-11 17:46:52 -0700227 }
Ed Tanous50232cd2018-11-12 11:34:43 -0800228 pElement = pElement->NextSiblingElement("node");
Ed Tanous60520632018-06-11 17:46:52 -0700229 }
230 },
231 transaction->process_name, path, "org.freedesktop.DBus.Introspectable",
232 "Introspect");
233}
234
Ed Tanous60520632018-06-11 17:46:52 -0700235void start_new_introspect(
Ed Tanous21c60592020-08-17 23:43:46 -0700236 sdbusplus::asio::connection* system_bus, boost::asio::io_context& io,
Ed Tanous60520632018-06-11 17:46:52 -0700237 interface_map_type& interface_map, const std::string& process_name,
Matt Spinler11401e22019-04-08 13:13:25 -0500238 AssociationMaps& assocMaps,
Matt Spinleraecabe82018-09-19 13:25:42 -0500239#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700240 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
241 global_start_time,
Matt Spinleraecabe82018-09-19 13:25:42 -0500242#endif
Ed Tanous60520632018-06-11 17:46:52 -0700243 sdbusplus::asio::object_server& objectServer)
244{
Andrew Geissler82815da2019-02-04 12:19:41 -0600245 if (needToIntrospect(process_name, service_whitelist, service_blacklist))
Ed Tanous60520632018-06-11 17:46:52 -0700246 {
Ed Tanous60520632018-06-11 17:46:52 -0700247 std::shared_ptr<InProgressIntrospect> transaction =
Matt Spinler11401e22019-04-08 13:13:25 -0500248 std::make_shared<InProgressIntrospect>(system_bus, io, process_name,
249 assocMaps
Matt Spinleraecabe82018-09-19 13:25:42 -0500250#ifdef DEBUG
251 ,
252 global_start_time
253#endif
254 );
Ed Tanous60520632018-06-11 17:46:52 -0700255
256 do_introspect(system_bus, transaction, interface_map, objectServer,
257 "/");
258 }
259}
260
261// TODO(ed) replace with std::set_intersection once c++17 is available
262template <class InputIt1, class InputIt2>
263bool intersect(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
264{
265 while (first1 != last1 && first2 != last2)
266 {
267 if (*first1 < *first2)
268 {
269 ++first1;
270 continue;
271 }
272 if (*first2 < *first1)
273 {
274 ++first2;
275 continue;
276 }
277 return true;
278 }
279 return false;
280}
281
282void doListNames(
Ed Tanous21c60592020-08-17 23:43:46 -0700283 boost::asio::io_context& io, interface_map_type& interface_map,
Ed Tanous60520632018-06-11 17:46:52 -0700284 sdbusplus::asio::connection* system_bus,
285 boost::container::flat_map<std::string, std::string>& name_owners,
Matt Spinler11401e22019-04-08 13:13:25 -0500286 AssociationMaps& assocMaps, sdbusplus::asio::object_server& objectServer)
Ed Tanous60520632018-06-11 17:46:52 -0700287{
288 system_bus->async_method_call(
Matt Spinler11401e22019-04-08 13:13:25 -0500289 [&io, &interface_map, &name_owners, &objectServer, system_bus,
290 &assocMaps](const boost::system::error_code ec,
Ed Tanous60520632018-06-11 17:46:52 -0700291 std::vector<std::string> process_names) {
292 if (ec)
293 {
294 std::cerr << "Error getting names: " << ec << "\n";
295 std::exit(EXIT_FAILURE);
296 return;
297 }
Ed Tanous60520632018-06-11 17:46:52 -0700298 // Try to make startup consistent
299 std::sort(process_names.begin(), process_names.end());
Matt Spinleraecabe82018-09-19 13:25:42 -0500300#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700301 std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
302 global_start_time = std::make_shared<
303 std::chrono::time_point<std::chrono::steady_clock>>(
304 std::chrono::steady_clock::now());
Matt Spinleraecabe82018-09-19 13:25:42 -0500305#endif
Ed Tanous60520632018-06-11 17:46:52 -0700306 for (const std::string& process_name : process_names)
307 {
Andrew Geissler82815da2019-02-04 12:19:41 -0600308 if (needToIntrospect(process_name, service_whitelist,
309 service_blacklist))
Ed Tanous60520632018-06-11 17:46:52 -0700310 {
311 start_new_introspect(system_bus, io, interface_map,
Matt Spinler11401e22019-04-08 13:13:25 -0500312 process_name, assocMaps,
Matt Spinleraecabe82018-09-19 13:25:42 -0500313#ifdef DEBUG
314 global_start_time,
315#endif
Ed Tanous60520632018-06-11 17:46:52 -0700316 objectServer);
317 update_owners(system_bus, name_owners, process_name);
318 }
319 }
320 },
321 "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
322 "ListNames");
323}
324
Matt Spinlerdd945862018-09-07 12:41:05 -0500325void splitArgs(const std::string& stringArgs,
326 boost::container::flat_set<std::string>& listArgs)
327{
328 std::istringstream args;
329 std::string arg;
330
331 args.str(stringArgs);
332
333 while (!args.eof())
334 {
335 args >> arg;
336 if (!arg.empty())
337 {
338 listArgs.insert(arg);
339 }
340 }
341}
342
Matt Spinler47c09752018-11-29 14:54:13 -0600343void addObjectMapResult(
344 std::vector<interface_map_type::value_type>& objectMap,
Matt Spinler9f0958e2018-09-11 08:26:10 -0500345 const std::string& objectPath,
346 const std::pair<std::string, boost::container::flat_set<std::string>>&
347 interfaceMap)
348{
349 // Adds an object path/service name/interface list entry to
Matt Spinler47c09752018-11-29 14:54:13 -0600350 // the results of GetSubTree and GetAncestors.
Matt Spinler9f0958e2018-09-11 08:26:10 -0500351 // If an entry for the object path already exists, just add the
352 // service name and interfaces to that entry, otherwise create
353 // a new entry.
354 auto entry = std::find_if(
Matt Spinler47c09752018-11-29 14:54:13 -0600355 objectMap.begin(), objectMap.end(),
Matt Spinler9f0958e2018-09-11 08:26:10 -0500356 [&objectPath](const auto& i) { return objectPath == i.first; });
357
Matt Spinler47c09752018-11-29 14:54:13 -0600358 if (entry != objectMap.end())
Matt Spinler9f0958e2018-09-11 08:26:10 -0500359 {
360 entry->second.emplace(interfaceMap);
361 }
362 else
363 {
364 interface_map_type::value_type object;
365 object.first = objectPath;
366 object.second.emplace(interfaceMap);
Matt Spinler47c09752018-11-29 14:54:13 -0600367 objectMap.push_back(object);
Matt Spinler9f0958e2018-09-11 08:26:10 -0500368 }
369}
370
Matt Spinlera82779f2019-01-09 12:39:42 -0600371// Remove parents of the passed in path that:
372// 1) Only have the 3 default interfaces on them
373// - Means D-Bus created these, not application code,
374// with the Properties, Introspectable, and Peer ifaces
375// 2) Have no other child for this owner
376void removeUnneededParents(const std::string& objectPath,
377 const std::string& owner,
378 interface_map_type& interface_map)
379{
380 auto parent = objectPath;
381
382 while (true)
383 {
384 auto pos = parent.find_last_of('/');
385 if ((pos == std::string::npos) || (pos == 0))
386 {
387 break;
388 }
389 parent = parent.substr(0, pos);
390
391 auto parent_it = interface_map.find(parent);
392 if (parent_it == interface_map.end())
393 {
394 break;
395 }
396
397 auto ifaces_it = parent_it->second.find(owner);
398 if (ifaces_it == parent_it->second.end())
399 {
400 break;
401 }
402
403 if (ifaces_it->second.size() != 3)
404 {
405 break;
406 }
407
408 auto child_path = parent + '/';
409
410 // Remove this parent if there isn't a remaining child on this owner
411 auto child = std::find_if(
412 interface_map.begin(), interface_map.end(),
413 [&owner, &child_path](const auto& entry) {
414 return boost::starts_with(entry.first, child_path) &&
415 (entry.second.find(owner) != entry.second.end());
416 });
417
418 if (child == interface_map.end())
419 {
420 parent_it->second.erase(ifaces_it);
421 if (parent_it->second.empty())
422 {
423 interface_map.erase(parent_it);
424 }
425 }
426 else
427 {
428 break;
429 }
430 }
431}
432
Ed Tanous0a13c762021-09-28 13:29:25 -0700433std::vector<interface_map_type::value_type>
434 getAncestors(const interface_map_type& interface_map, std::string req_path,
435 std::vector<std::string>& interfaces)
436{
437 // Interfaces need to be sorted for intersect to function
438 std::sort(interfaces.begin(), interfaces.end());
439
440 if (boost::ends_with(req_path, "/"))
441 {
442 req_path.pop_back();
443 }
444 if (req_path.size() && interface_map.find(req_path) == interface_map.end())
445 {
Konstantin Aladyshevb15df6b2022-01-11 14:50:55 +0300446 throw sdbusplus::xyz::openbmc_project::Common::Error::
447 ResourceNotFound();
Ed Tanous0a13c762021-09-28 13:29:25 -0700448 }
449
450 std::vector<interface_map_type::value_type> ret;
451 for (auto& object_path : interface_map)
452 {
453 auto& this_path = object_path.first;
454 if (boost::starts_with(req_path, this_path) && (req_path != this_path))
455 {
456 if (interfaces.empty())
457 {
458 ret.emplace_back(object_path);
459 }
460 else
461 {
462 for (auto& interface_map : object_path.second)
463 {
Ed Tanous0a13c762021-09-28 13:29:25 -0700464 if (intersect(interfaces.begin(), interfaces.end(),
465 interface_map.second.begin(),
466 interface_map.second.end()))
467 {
468 addObjectMapResult(ret, this_path, interface_map);
469 }
470 }
471 }
472 }
473 }
474
475 return ret;
476}
477
478boost::container::flat_map<std::string, boost::container::flat_set<std::string>>
479 getObject(const interface_map_type& interface_map, const std::string& path,
480 std::vector<std::string>& interfaces)
481{
482 boost::container::flat_map<std::string,
483 boost::container::flat_set<std::string>>
484 results;
485
486 // Interfaces need to be sorted for intersect to function
487 std::sort(interfaces.begin(), interfaces.end());
488 auto path_ref = interface_map.find(path);
489 if (path_ref == interface_map.end())
490 {
Konstantin Aladyshevb15df6b2022-01-11 14:50:55 +0300491 throw sdbusplus::xyz::openbmc_project::Common::Error::
492 ResourceNotFound();
Ed Tanous0a13c762021-09-28 13:29:25 -0700493 }
494 if (interfaces.empty())
495 {
496 return path_ref->second;
497 }
498 for (auto& interface_map : path_ref->second)
499 {
500 if (intersect(interfaces.begin(), interfaces.end(),
501 interface_map.second.begin(), interface_map.second.end()))
502 {
503 results.emplace(interface_map.first, interface_map.second);
504 }
505 }
506
507 if (results.empty())
508 {
Konstantin Aladyshevb15df6b2022-01-11 14:50:55 +0300509 throw sdbusplus::xyz::openbmc_project::Common::Error::
510 ResourceNotFound();
Ed Tanous0a13c762021-09-28 13:29:25 -0700511 }
512
513 return results;
514}
515
516std::vector<interface_map_type::value_type>
517 getSubTree(const interface_map_type& interface_map, std::string req_path,
518 int32_t depth, std::vector<std::string>& interfaces)
519{
520 if (depth <= 0)
521 {
522 depth = std::numeric_limits<int32_t>::max();
523 }
524 // Interfaces need to be sorted for intersect to function
525 std::sort(interfaces.begin(), interfaces.end());
526 std::vector<interface_map_type::value_type> ret;
527
528 if (boost::ends_with(req_path, "/"))
529 {
530 req_path.pop_back();
531 }
532 if (req_path.size() && interface_map.find(req_path) == interface_map.end())
533 {
Konstantin Aladyshevb15df6b2022-01-11 14:50:55 +0300534 throw sdbusplus::xyz::openbmc_project::Common::Error::
535 ResourceNotFound();
Ed Tanous0a13c762021-09-28 13:29:25 -0700536 }
537
538 for (auto& object_path : interface_map)
539 {
540 auto& this_path = object_path.first;
541
542 if (this_path == req_path)
543 {
544 continue;
545 }
546
547 if (boost::starts_with(this_path, req_path))
548 {
549 // count the number of slashes past the search term
550 int32_t this_depth = std::count(this_path.begin() + req_path.size(),
551 this_path.end(), '/');
552 if (this_depth <= depth)
553 {
554 for (auto& interface_map : object_path.second)
555 {
556 if (intersect(interfaces.begin(), interfaces.end(),
557 interface_map.second.begin(),
558 interface_map.second.end()) ||
559 interfaces.empty())
560 {
561 addObjectMapResult(ret, this_path, interface_map);
562 }
563 }
564 }
565 }
566 }
567
568 return ret;
569}
570
571std::vector<std::string>
572 getSubTreePaths(const interface_map_type& interface_map,
573 std::string req_path, int32_t depth,
574 std::vector<std::string>& interfaces)
575{
576 if (depth <= 0)
577 {
578 depth = std::numeric_limits<int32_t>::max();
579 }
580 // Interfaces need to be sorted for intersect to function
581 std::sort(interfaces.begin(), interfaces.end());
582 std::vector<std::string> ret;
583
584 if (boost::ends_with(req_path, "/"))
585 {
586 req_path.pop_back();
587 }
588 if (req_path.size() && interface_map.find(req_path) == interface_map.end())
589 {
Konstantin Aladyshevb15df6b2022-01-11 14:50:55 +0300590 throw sdbusplus::xyz::openbmc_project::Common::Error::
591 ResourceNotFound();
Ed Tanous0a13c762021-09-28 13:29:25 -0700592 }
593
594 for (auto& object_path : interface_map)
595 {
596 auto& this_path = object_path.first;
597
598 if (this_path == req_path)
599 {
600 continue;
601 }
602
603 if (boost::starts_with(this_path, req_path))
604 {
605 // count the number of slashes past the search term
606 int this_depth = std::count(this_path.begin() + req_path.size(),
607 this_path.end(), '/');
608 if (this_depth <= depth)
609 {
610 bool add = interfaces.empty();
611 for (auto& interface_map : object_path.second)
612 {
613 if (intersect(interfaces.begin(), interfaces.end(),
614 interface_map.second.begin(),
615 interface_map.second.end()))
616 {
617 add = true;
618 break;
619 }
620 }
621 if (add)
622 {
623 // TODO(ed) this is a copy
624 ret.emplace_back(this_path);
625 }
626 }
627 }
628 }
629
630 return ret;
631}
632
Ed Tanous60520632018-06-11 17:46:52 -0700633int main(int argc, char** argv)
634{
Matt Spinlerdd945862018-09-07 12:41:05 -0500635 auto options = ArgumentParser(argc, argv);
Ed Tanous21c60592020-08-17 23:43:46 -0700636 boost::asio::io_context io;
Ed Tanous60520632018-06-11 17:46:52 -0700637 std::shared_ptr<sdbusplus::asio::connection> system_bus =
638 std::make_shared<sdbusplus::asio::connection>(io);
639
Matt Spinlerdd945862018-09-07 12:41:05 -0500640 splitArgs(options["service-namespaces"], service_whitelist);
Matt Spinlerdd945862018-09-07 12:41:05 -0500641 splitArgs(options["service-blacklists"], service_blacklist);
642
Ed Tanousd4dd96a2018-11-12 11:37:44 -0800643 // TODO(Ed) Remove this once all service files are updated to not use this.
644 // For now, simply squash the input, and ignore it.
645 boost::container::flat_set<std::string> iface_whitelist;
646 splitArgs(options["interface-namespaces"], iface_whitelist);
647
Ed Tanous60520632018-06-11 17:46:52 -0700648 sdbusplus::asio::object_server server(system_bus);
649
650 // Construct a signal set registered for process termination.
651 boost::asio::signal_set signals(io, SIGINT, SIGTERM);
Brad Bishop2d41d6a2021-08-03 08:14:45 -0400652 signals.async_wait(
653 [&io](const boost::system::error_code&, int) { io.stop(); });
Ed Tanous60520632018-06-11 17:46:52 -0700654
655 interface_map_type interface_map;
656 boost::container::flat_map<std::string, std::string> name_owners;
657
658 std::function<void(sdbusplus::message::message & message)>
659 nameChangeHandler = [&interface_map, &io, &name_owners, &server,
660 system_bus](sdbusplus::message::message& message) {
Andrew Geissler20679262019-02-11 20:20:40 -0600661 std::string name; // well-known
662 std::string old_owner; // unique-name
663 std::string new_owner; // unique-name
Ed Tanous60520632018-06-11 17:46:52 -0700664
665 message.read(name, old_owner, new_owner);
666
667 if (!old_owner.empty())
668 {
Andrew Geissler20679262019-02-11 20:20:40 -0600669 processNameChangeDelete(name_owners, name, old_owner,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500670 interface_map, associationMaps, server);
Ed Tanous60520632018-06-11 17:46:52 -0700671 }
672
673 if (!new_owner.empty())
674 {
Matt Spinleraecabe82018-09-19 13:25:42 -0500675#ifdef DEBUG
Ed Tanous60520632018-06-11 17:46:52 -0700676 auto transaction = std::make_shared<
677 std::chrono::time_point<std::chrono::steady_clock>>(
678 std::chrono::steady_clock::now());
Matt Spinleraecabe82018-09-19 13:25:42 -0500679#endif
Ed Tanous60520632018-06-11 17:46:52 -0700680 // New daemon added
Andrew Geissler82815da2019-02-04 12:19:41 -0600681 if (needToIntrospect(name, service_whitelist,
682 service_blacklist))
Ed Tanous60520632018-06-11 17:46:52 -0700683 {
684 name_owners[new_owner] = name;
685 start_new_introspect(system_bus.get(), io, interface_map,
Matt Spinler11401e22019-04-08 13:13:25 -0500686 name, associationMaps,
Matt Spinleraecabe82018-09-19 13:25:42 -0500687#ifdef DEBUG
688 transaction,
689#endif
690 server);
Ed Tanous60520632018-06-11 17:46:52 -0700691 }
692 }
693 };
694
695 sdbusplus::bus::match::match nameOwnerChanged(
696 static_cast<sdbusplus::bus::bus&>(*system_bus),
697 sdbusplus::bus::match::rules::nameOwnerChanged(), nameChangeHandler);
698
699 std::function<void(sdbusplus::message::message & message)>
700 interfacesAddedHandler = [&interface_map, &name_owners, &server](
701 sdbusplus::message::message& message) {
702 sdbusplus::message::object_path obj_path;
Andrew Geissler70461892019-02-27 09:57:37 -0600703 InterfacesAdded interfaces_added;
Ed Tanous60520632018-06-11 17:46:52 -0700704 message.read(obj_path, interfaces_added);
705 std::string well_known;
Andrew Geissler3b025e62019-02-01 10:33:54 -0600706 if (!getWellKnown(name_owners, message.get_sender(), well_known))
Ed Tanous60520632018-06-11 17:46:52 -0700707 {
708 return; // only introspect well-known
709 }
Andrew Geissler82815da2019-02-04 12:19:41 -0600710 if (needToIntrospect(well_known, service_whitelist,
711 service_blacklist))
Ed Tanous60520632018-06-11 17:46:52 -0700712 {
Andrew Geissler70461892019-02-27 09:57:37 -0600713 processInterfaceAdded(interface_map, obj_path, interfaces_added,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500714 well_known, associationMaps, server);
Ed Tanous60520632018-06-11 17:46:52 -0700715 }
716 };
717
718 sdbusplus::bus::match::match interfacesAdded(
719 static_cast<sdbusplus::bus::bus&>(*system_bus),
720 sdbusplus::bus::match::rules::interfacesAdded(),
721 interfacesAddedHandler);
722
723 std::function<void(sdbusplus::message::message & message)>
724 interfacesRemovedHandler = [&interface_map, &name_owners, &server](
725 sdbusplus::message::message& message) {
726 sdbusplus::message::object_path obj_path;
727 std::vector<std::string> interfaces_removed;
728 message.read(obj_path, interfaces_removed);
729 auto connection_map = interface_map.find(obj_path.str);
730 if (connection_map == interface_map.end())
731 {
732 return;
733 }
734
735 std::string sender;
Andrew Geissler3b025e62019-02-01 10:33:54 -0600736 if (!getWellKnown(name_owners, message.get_sender(), sender))
Ed Tanous60520632018-06-11 17:46:52 -0700737 {
738 return;
739 }
740 for (const std::string& interface : interfaces_removed)
741 {
742 auto interface_set = connection_map->second.find(sender);
743 if (interface_set == connection_map->second.end())
744 {
745 continue;
746 }
747
John Wangd0cf9422019-09-17 16:01:34 +0800748 if (interface == assocDefsInterface)
Ed Tanous60520632018-06-11 17:46:52 -0700749 {
Andrew Geissler67e5a422019-02-04 13:00:59 -0600750 removeAssociation(obj_path.str, sender, server,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500751 associationMaps);
Ed Tanous60520632018-06-11 17:46:52 -0700752 }
753
754 interface_set->second.erase(interface);
Matt Spinler9c3d2852019-04-08 15:57:19 -0500755
Ed Tanous60520632018-06-11 17:46:52 -0700756 if (interface_set->second.empty())
757 {
Matt Spinler9c3d2852019-04-08 15:57:19 -0500758 // If this was the last interface on this connection,
759 // erase the connection
Ed Tanous60520632018-06-11 17:46:52 -0700760 connection_map->second.erase(interface_set);
Matt Spinler9c3d2852019-04-08 15:57:19 -0500761
762 // Instead of checking if every single path is the endpoint
763 // of an association that needs to be moved to pending,
764 // only check when the only remaining owner of this path is
765 // ourself, which would be because we still own the
766 // association path.
767 if ((connection_map->second.size() == 1) &&
768 (connection_map->second.begin()->first ==
Brad Bishopa02cd542021-10-12 19:12:42 -0400769 "xyz.openbmc_project.ObjectMapper"))
Matt Spinler9c3d2852019-04-08 15:57:19 -0500770 {
771 // Remove the 2 association D-Bus paths and move the
772 // association to pending.
773 moveAssociationToPending(obj_path.str, associationMaps,
774 server);
775 }
Ed Tanous60520632018-06-11 17:46:52 -0700776 }
777 }
778 // If this was the last connection on this object path,
779 // erase the object path
780 if (connection_map->second.empty())
781 {
782 interface_map.erase(connection_map);
783 }
Matt Spinlera82779f2019-01-09 12:39:42 -0600784
785 removeUnneededParents(obj_path.str, sender, interface_map);
Ed Tanous60520632018-06-11 17:46:52 -0700786 };
787
788 sdbusplus::bus::match::match interfacesRemoved(
789 static_cast<sdbusplus::bus::bus&>(*system_bus),
790 sdbusplus::bus::match::rules::interfacesRemoved(),
791 interfacesRemovedHandler);
792
793 std::function<void(sdbusplus::message::message & message)>
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500794 associationChangedHandler = [&server, &name_owners, &interface_map](
Matt Spinler8f876a52019-04-15 13:22:50 -0500795 sdbusplus::message::message& message) {
796 std::string objectName;
Patrick Williams2bb2d6b2020-05-13 17:59:02 -0500797 boost::container::flat_map<std::string,
798 std::variant<std::vector<Association>>>
Matt Spinler8f876a52019-04-15 13:22:50 -0500799 values;
800 message.read(objectName, values);
John Wangd0cf9422019-09-17 16:01:34 +0800801 auto prop = values.find(assocDefsProperty);
Matt Spinler8f876a52019-04-15 13:22:50 -0500802 if (prop != values.end())
803 {
804 std::vector<Association> associations =
Patrick Williamsb05bc122020-05-13 12:21:00 -0500805 std::get<std::vector<Association>>(prop->second);
Matt Spinler8f876a52019-04-15 13:22:50 -0500806
807 std::string well_known;
808 if (!getWellKnown(name_owners, message.get_sender(),
809 well_known))
810 {
811 return;
Ed Tanous60520632018-06-11 17:46:52 -0700812 }
Matt Spinler8f876a52019-04-15 13:22:50 -0500813 associationChanged(server, associations, message.get_path(),
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500814 well_known, interface_map, associationMaps);
Matt Spinler8f876a52019-04-15 13:22:50 -0500815 }
816 };
817 sdbusplus::bus::match::match assocChangedMatch(
Ed Tanous60520632018-06-11 17:46:52 -0700818 static_cast<sdbusplus::bus::bus&>(*system_bus),
819 sdbusplus::bus::match::rules::interface(
820 "org.freedesktop.DBus.Properties") +
821 sdbusplus::bus::match::rules::member("PropertiesChanged") +
Matt Spinler8f876a52019-04-15 13:22:50 -0500822 sdbusplus::bus::match::rules::argN(0, assocDefsInterface),
823 associationChangedHandler);
824
Ed Tanous60520632018-06-11 17:46:52 -0700825 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
Brad Bishopa02cd542021-10-12 19:12:42 -0400826 server.add_interface("/xyz/openbmc_project/object_mapper",
827 "xyz.openbmc_project.ObjectMapper");
Ed Tanous60520632018-06-11 17:46:52 -0700828
829 iface->register_method(
Matt Spinler6a39e8c2018-11-13 11:11:36 -0600830 "GetAncestors", [&interface_map](std::string& req_path,
Ed Tanous60520632018-06-11 17:46:52 -0700831 std::vector<std::string>& interfaces) {
Ed Tanous0a13c762021-09-28 13:29:25 -0700832 return getAncestors(interface_map, req_path, interfaces);
Ed Tanous60520632018-06-11 17:46:52 -0700833 });
834
835 iface->register_method(
836 "GetObject", [&interface_map](const std::string& path,
837 std::vector<std::string>& interfaces) {
Ed Tanous0a13c762021-09-28 13:29:25 -0700838 return getObject(interface_map, path, interfaces);
Ed Tanous60520632018-06-11 17:46:52 -0700839 });
840
841 iface->register_method(
Matt Spinler153494e2018-11-07 16:35:40 -0600842 "GetSubTree", [&interface_map](std::string& req_path, int32_t depth,
843 std::vector<std::string>& interfaces) {
Ed Tanous0a13c762021-09-28 13:29:25 -0700844 return getSubTree(interface_map, req_path, depth, interfaces);
Ed Tanous60520632018-06-11 17:46:52 -0700845 });
846
847 iface->register_method(
848 "GetSubTreePaths",
Matt Spinler153494e2018-11-07 16:35:40 -0600849 [&interface_map](std::string& req_path, int32_t depth,
Ed Tanous60520632018-06-11 17:46:52 -0700850 std::vector<std::string>& interfaces) {
Ed Tanous0a13c762021-09-28 13:29:25 -0700851 return getSubTreePaths(interface_map, req_path, depth, interfaces);
Ed Tanous60520632018-06-11 17:46:52 -0700852 });
853
854 iface->initialize();
855
856 io.post([&]() {
Matt Spinler11401e22019-04-08 13:13:25 -0500857 doListNames(io, interface_map, system_bus.get(), name_owners,
858 associationMaps, server);
Ed Tanous60520632018-06-11 17:46:52 -0700859 });
860
Brad Bishopa02cd542021-10-12 19:12:42 -0400861 system_bus->request_name("xyz.openbmc_project.ObjectMapper");
Vishwanatha Subbanna64354ef2020-08-21 03:35:26 -0500862
Ed Tanous60520632018-06-11 17:46:52 -0700863 io.run();
864}