blob: 902ae6637cc4f9f25a9f7720505040da98524ba1 [file] [log] [blame]
Willy Tuaba14d32023-01-31 14:19:59 -08001#include "handler.hpp"
2
3#include "types.hpp"
4
5#include <xyz/openbmc_project/Common/error.hpp>
6
7#include <algorithm>
Brad Bishop197de182025-07-09 14:41:37 -04008#include <iterator>
Willy Tuaba14d32023-01-31 14:19:59 -08009#include <string>
Willy Tu58881d02022-10-02 20:46:45 +000010#include <unordered_set>
Willy Tuaba14d32023-01-31 14:19:59 -080011#include <utility>
12#include <vector>
13
14void addObjectMapResult(std::vector<InterfaceMapType::value_type>& objectMap,
15 const std::string& objectPath,
16 const ConnectionNames::value_type& interfaceMap)
17{
18 // Adds an object path/service name/interface list entry to
19 // the results of GetSubTree and GetAncestors.
20 // If an entry for the object path already exists, just add the
21 // service name and interfaces to that entry, otherwise create
22 // a new entry.
Patrick Williams47b68cb2023-10-20 11:20:07 -050023 auto entry = std::find_if(
24 objectMap.begin(), objectMap.end(),
25 [&objectPath](const auto& i) { return objectPath == i.first; });
Willy Tuaba14d32023-01-31 14:19:59 -080026
27 if (entry != objectMap.end())
28 {
29 entry->second.emplace(interfaceMap);
30 }
31 else
32 {
33 InterfaceMapType::value_type object;
34 object.first = objectPath;
35 object.second.emplace(interfaceMap);
36 objectMap.push_back(object);
37 }
38}
39
Patrick Williamsd884cdf2025-02-01 08:23:38 -050040std::vector<InterfaceMapType::value_type> getAncestors(
41 const InterfaceMapType& interfaceMap, std::string reqPath,
42 std::vector<std::string>& interfaces)
Willy Tuaba14d32023-01-31 14:19:59 -080043{
44 // Interfaces need to be sorted for intersect to function
45 std::sort(interfaces.begin(), interfaces.end());
46
47 if (reqPath.ends_with("/"))
48 {
49 reqPath.pop_back();
50 }
51 if (!reqPath.empty() && interfaceMap.find(reqPath) == interfaceMap.end())
52 {
53 throw sdbusplus::xyz::openbmc_project::Common::Error::
54 ResourceNotFound();
55 }
56
57 std::vector<InterfaceMapType::value_type> ret;
58 for (const auto& objectPath : interfaceMap)
59 {
60 const auto& thisPath = objectPath.first;
61
62 if (reqPath == thisPath)
63 {
64 continue;
65 }
66
67 if (reqPath.starts_with(thisPath))
68 {
69 if (interfaces.empty())
70 {
71 ret.emplace_back(objectPath);
72 }
73 else
74 {
Brad Bishop11c0cc32025-07-08 16:04:25 -040075 for (const auto& connectionInterfaces : objectPath.second)
Willy Tuaba14d32023-01-31 14:19:59 -080076 {
77 std::vector<std::string> output(std::min(
Brad Bishop11c0cc32025-07-08 16:04:25 -040078 interfaces.size(), connectionInterfaces.second.size()));
Willy Tuaba14d32023-01-31 14:19:59 -080079 // Return iterator points at the first output elemtn,
80 // meaning that there are no intersections.
Patrick Williams9052ebd2024-08-16 15:22:16 -040081 if (std::set_intersection(
82 interfaces.begin(), interfaces.end(),
Brad Bishop11c0cc32025-07-08 16:04:25 -040083 connectionInterfaces.second.begin(),
84 connectionInterfaces.second.end(),
85 output.begin()) != output.begin())
Willy Tuaba14d32023-01-31 14:19:59 -080086 {
Brad Bishop11c0cc32025-07-08 16:04:25 -040087 addObjectMapResult(ret, thisPath, connectionInterfaces);
Willy Tuaba14d32023-01-31 14:19:59 -080088 }
89 }
90 }
91 }
92 }
93
94 return ret;
95}
96
97ConnectionNames getObject(const InterfaceMapType& interfaceMap,
98 const std::string& path,
99 std::vector<std::string>& interfaces)
100{
101 ConnectionNames results;
102
103 // Interfaces need to be sorted for intersect to function
104 std::sort(interfaces.begin(), interfaces.end());
105 auto pathRef = interfaceMap.find(path);
106 if (pathRef == interfaceMap.end())
107 {
108 throw sdbusplus::xyz::openbmc_project::Common::Error::
109 ResourceNotFound();
110 }
111 if (interfaces.empty())
112 {
113 return pathRef->second;
114 }
Brad Bishop11c0cc32025-07-08 16:04:25 -0400115 for (const auto& connectionInterfaces : pathRef->second)
Willy Tuaba14d32023-01-31 14:19:59 -0800116 {
117 std::vector<std::string> output(
Brad Bishop11c0cc32025-07-08 16:04:25 -0400118 std::min(interfaces.size(), connectionInterfaces.second.size()));
Willy Tuaba14d32023-01-31 14:19:59 -0800119 // Return iterator points at the first output elemtn,
120 // meaning that there are no intersections.
121 if (std::set_intersection(interfaces.begin(), interfaces.end(),
Brad Bishop11c0cc32025-07-08 16:04:25 -0400122 connectionInterfaces.second.begin(),
123 connectionInterfaces.second.end(),
124 output.begin()) != output.begin())
Willy Tuaba14d32023-01-31 14:19:59 -0800125 {
Brad Bishop11c0cc32025-07-08 16:04:25 -0400126 results.emplace(connectionInterfaces.first,
127 connectionInterfaces.second);
Willy Tuaba14d32023-01-31 14:19:59 -0800128 }
129 }
130
131 if (results.empty())
132 {
133 throw sdbusplus::xyz::openbmc_project::Common::Error::
134 ResourceNotFound();
135 }
136
137 return results;
138}
139
Patrick Williamsd884cdf2025-02-01 08:23:38 -0500140std::vector<InterfaceMapType::value_type> getSubTree(
141 const InterfaceMapType& interfaceMap, std::string reqPath, int32_t depth,
142 std::vector<std::string>& interfaces)
Willy Tuaba14d32023-01-31 14:19:59 -0800143{
144 if (depth <= 0)
145 {
146 depth = std::numeric_limits<int32_t>::max();
147 }
148 // Interfaces need to be sorted for intersect to function
149 std::sort(interfaces.begin(), interfaces.end());
150
151 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped
152 // will be guaranteed not to have a trailing "/"
153 if (!reqPath.ends_with("/"))
154 {
155 reqPath += "/";
156 }
157 std::string_view reqPathStripped =
158 std::string_view(reqPath).substr(0, reqPath.size() - 1);
159
160 if (!reqPathStripped.empty() &&
161 interfaceMap.find(reqPathStripped) == interfaceMap.end())
162 {
163 throw sdbusplus::xyz::openbmc_project::Common::Error::
164 ResourceNotFound();
165 }
166
167 std::vector<InterfaceMapType::value_type> ret;
168 for (const auto& objectPath : interfaceMap)
169 {
170 const auto& thisPath = objectPath.first;
171
172 // Skip exact match on stripped search term
173 if (thisPath == reqPathStripped)
174 {
175 continue;
176 }
177
178 if (thisPath.starts_with(reqPath))
179 {
180 // count the number of slashes past the stripped search term
Brad Bishop197de182025-07-09 14:41:37 -0400181 auto thisDepth = std::count(
182 thisPath.begin() + std::distance(reqPathStripped.begin(),
183 reqPathStripped.end()),
184 thisPath.end(), '/');
Willy Tuaba14d32023-01-31 14:19:59 -0800185 if (thisDepth <= depth)
186 {
Brad Bishop11c0cc32025-07-08 16:04:25 -0400187 for (const auto& connectionInterfaces : objectPath.second)
Willy Tuaba14d32023-01-31 14:19:59 -0800188 {
189 std::vector<std::string> output(std::min(
Brad Bishop11c0cc32025-07-08 16:04:25 -0400190 interfaces.size(), connectionInterfaces.second.size()));
Willy Tuaba14d32023-01-31 14:19:59 -0800191 // Return iterator points at the first output elemtn,
192 // meaning that there are no intersections.
193 if (std::set_intersection(
194 interfaces.begin(), interfaces.end(),
Brad Bishop11c0cc32025-07-08 16:04:25 -0400195 connectionInterfaces.second.begin(),
196 connectionInterfaces.second.end(),
Willy Tuaba14d32023-01-31 14:19:59 -0800197 output.begin()) != output.begin() ||
198 interfaces.empty())
199 {
Brad Bishop11c0cc32025-07-08 16:04:25 -0400200 addObjectMapResult(ret, thisPath, connectionInterfaces);
Willy Tuaba14d32023-01-31 14:19:59 -0800201 }
202 }
203 }
204 }
205 }
206
207 return ret;
208}
209
Patrick Williamsd884cdf2025-02-01 08:23:38 -0500210std::vector<std::string> getSubTreePaths(const InterfaceMapType& interfaceMap,
211 std::string reqPath, int32_t depth,
212 std::vector<std::string>& interfaces)
Willy Tuaba14d32023-01-31 14:19:59 -0800213{
214 if (depth <= 0)
215 {
216 depth = std::numeric_limits<int32_t>::max();
217 }
218 // Interfaces need to be sorted for intersect to function
219 std::sort(interfaces.begin(), interfaces.end());
220
221 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped
222 // will be guaranteed not to have a trailing "/"
223 if (!reqPath.ends_with("/"))
224 {
225 reqPath += "/";
226 }
227 std::string_view reqPathStripped =
228 std::string_view(reqPath).substr(0, reqPath.size() - 1);
229
230 if (!reqPathStripped.empty() &&
231 interfaceMap.find(reqPathStripped) == interfaceMap.end())
232 {
233 throw sdbusplus::xyz::openbmc_project::Common::Error::
234 ResourceNotFound();
235 }
236
237 std::vector<std::string> ret;
238 for (const auto& objectPath : interfaceMap)
239 {
240 const auto& thisPath = objectPath.first;
241
242 // Skip exact match on stripped search term
243 if (thisPath == reqPathStripped)
244 {
245 continue;
246 }
247
248 if (thisPath.starts_with(reqPath))
249 {
250 // count the number of slashes past the stripped search term
Brad Bishop197de182025-07-09 14:41:37 -0400251 auto thisDepth = std::count(
252 thisPath.begin() + std::distance(reqPathStripped.begin(),
253 reqPathStripped.end()),
254 thisPath.end(), '/');
Willy Tuaba14d32023-01-31 14:19:59 -0800255 if (thisDepth <= depth)
256 {
257 bool add = interfaces.empty();
Brad Bishop11c0cc32025-07-08 16:04:25 -0400258 for (const auto& connectionInterfaces : objectPath.second)
Willy Tuaba14d32023-01-31 14:19:59 -0800259 {
260 std::vector<std::string> output(std::min(
Brad Bishop11c0cc32025-07-08 16:04:25 -0400261 interfaces.size(), connectionInterfaces.second.size()));
Willy Tuaba14d32023-01-31 14:19:59 -0800262 // Return iterator points at the first output elemtn,
263 // meaning that there are no intersections.
Patrick Williams9052ebd2024-08-16 15:22:16 -0400264 if (std::set_intersection(
265 interfaces.begin(), interfaces.end(),
Brad Bishop11c0cc32025-07-08 16:04:25 -0400266 connectionInterfaces.second.begin(),
267 connectionInterfaces.second.end(),
268 output.begin()) != output.begin())
Willy Tuaba14d32023-01-31 14:19:59 -0800269 {
270 add = true;
271 break;
272 }
273 }
274 if (add)
275 {
276 // TODO(ed) this is a copy
277 ret.emplace_back(thisPath);
278 }
279 }
280 }
281 }
282
283 return ret;
284}
Willy Tu58881d02022-10-02 20:46:45 +0000285
Patrick Williams9052ebd2024-08-16 15:22:16 -0400286std::vector<InterfaceMapType::value_type> getAssociatedSubTree(
287 const InterfaceMapType& interfaceMap,
288 const AssociationMaps& associationMaps,
289 const sdbusplus::message::object_path& associationPath,
290 const sdbusplus::message::object_path& reqPath, int32_t depth,
291 std::vector<std::string>& interfaces)
Willy Tu58881d02022-10-02 20:46:45 +0000292{
293 auto findEndpoint = associationMaps.ifaces.find(associationPath.str);
294 if (findEndpoint == associationMaps.ifaces.end())
295 {
296 return {};
297 }
298 const std::vector<std::string>& association =
299 std::get<endpointsPos>(findEndpoint->second);
300 std::unordered_set<std::string> associationSet(association.begin(),
301 association.end());
Patrick Williams8c250062024-08-23 12:28:07 -0400302 const std::vector<InterfaceMapType::value_type> interfacePairs =
Willy Tu58881d02022-10-02 20:46:45 +0000303 getSubTree(interfaceMap, reqPath, depth, interfaces);
304
305 std::vector<InterfaceMapType::value_type> output;
306 for (const InterfaceMapType::value_type& interfacePair : interfacePairs)
307 {
308 if (associationSet.contains(interfacePair.first))
309 {
310 output.emplace_back(interfacePair);
311 }
312 }
313 return output;
314}
315
316std::vector<std::string> getAssociatedSubTreePaths(
317 const InterfaceMapType& interfaceMap,
318 const AssociationMaps& associationMaps,
319 const sdbusplus::message::object_path& associationPath,
320 const sdbusplus::message::object_path& reqPath, int32_t depth,
321 std::vector<std::string>& interfaces)
322{
323 auto findEndpoint = associationMaps.ifaces.find(associationPath.str);
324 if (findEndpoint == associationMaps.ifaces.end())
325 {
326 return {};
327 }
328 const std::vector<std::string>& association =
329 std::get<endpointsPos>(findEndpoint->second);
330 std::unordered_set<std::string> associationSet(association.begin(),
331 association.end());
Patrick Williams8c250062024-08-23 12:28:07 -0400332 const std::vector<std::string> paths =
Willy Tu58881d02022-10-02 20:46:45 +0000333 getSubTreePaths(interfaceMap, reqPath, depth, interfaces);
334
335 std::vector<std::string> output;
336 for (const auto& path : paths)
337 {
338 if (associationSet.contains(path))
339 {
340 output.emplace_back(path);
341 }
342 }
343 return output;
Patrick Williams1a19e6a2023-05-10 07:51:32 -0500344}
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500345
346// This function works like getSubTreePaths() but only matching id with
347// the leaf-name instead of full path.
348std::vector<std::string> getSubTreePathsById(
349 const InterfaceMapType& interfaceMap, const std::string& id,
350 const std::string& objectPath, std::vector<std::string>& interfaces)
351{
352 std::sort(interfaces.begin(), interfaces.end());
353
354 std::string localObjectPath = objectPath;
355
356 if (!localObjectPath.ends_with("/"))
357 {
358 localObjectPath += "/";
359 }
360 std::string_view objectPathStripped =
361 std::string_view(localObjectPath).substr(0, localObjectPath.size() - 1);
362
363 if (!objectPathStripped.empty() &&
364 interfaceMap.find(objectPathStripped) == interfaceMap.end())
365 {
366 throw sdbusplus::xyz::openbmc_project::Common::Error::
367 ResourceNotFound();
368 }
369
Myung Bae1e3f7812025-03-25 08:33:22 -0700370 bool validId = false;
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500371 std::vector<std::string> output;
372 for (const auto& path : interfaceMap)
373 {
374 const auto& thisPath = path.first;
375
Myung Bae1e3f7812025-03-25 08:33:22 -0700376 // Skip the path does not end with the id
377 if (!thisPath.ends_with("/" + id))
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500378 {
379 continue;
380 }
381
Myung Bae1e3f7812025-03-25 08:33:22 -0700382 // Valid if id is matching
383 validId = true;
384
385 // Skip exact match on stripped search term
386 if (thisPath == objectPathStripped)
387 {
388 continue;
389 }
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500390 if (thisPath.starts_with(objectPath))
391 {
Brad Bishop11c0cc32025-07-08 16:04:25 -0400392 for (const auto& connectionInterfaces : path.second)
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500393 {
Brad Bishop11c0cc32025-07-08 16:04:25 -0400394 std::vector<std::string> tempoutput(std::min(
395 interfaces.size(), connectionInterfaces.second.size()));
396 if (std::set_intersection(interfaces.begin(), interfaces.end(),
397 connectionInterfaces.second.begin(),
398 connectionInterfaces.second.end(),
399 tempoutput.begin()) !=
400 tempoutput.begin())
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500401 {
402 output.emplace_back(thisPath);
403 break;
404 }
405 }
406 }
407 }
Myung Bae1e3f7812025-03-25 08:33:22 -0700408 if (!validId)
409 {
410 throw sdbusplus::xyz::openbmc_project::Common::Error::
411 ResourceNotFound();
412 }
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500413 return output;
414}
415
416std::vector<InterfaceMapType::value_type> getAssociatedSubTreeById(
417 const InterfaceMapType& interfaceMap,
418 const AssociationMaps& associationMaps, const std::string& id,
419 const std::string& objectPath, std::vector<std::string>& subtreeInterfaces,
420 const std::string& association,
421 std::vector<std::string>& endpointInterfaces)
422{
423 std::vector<std::string> subtreePaths =
424 getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces);
425
426 std::vector<InterfaceMapType::value_type> output;
427 for (const auto& subtreePath : subtreePaths)
428 {
429 // Form the association path
430 std::string associationPathStr = subtreePath + "/" + association;
431 sdbusplus::message::object_path associationPath(associationPathStr);
432
433 auto associatedSubTree =
434 getAssociatedSubTree(interfaceMap, associationMaps, associationPath,
435 objectPath, 0, endpointInterfaces);
436
437 output.insert(output.end(), associatedSubTree.begin(),
438 associatedSubTree.end());
439 }
440 return output;
441}
442
443std::vector<std::string> getAssociatedSubTreePathsById(
444 const InterfaceMapType& interfaceMap,
445 const AssociationMaps& associationMaps, const std::string& id,
446 const std::string& objectPath, std::vector<std::string>& subtreeInterfaces,
447 const std::string& association,
448 std::vector<std::string>& endpointInterfaces)
449{
450 std::vector<std::string> subtreePaths =
451 getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces);
452 std::vector<std::string> output;
453 for (const auto& subtreePath : subtreePaths)
454 {
455 // Form the association path
456 std::string associationPathStr = subtreePath + "/" + association;
457 sdbusplus::message::object_path associationPath(associationPathStr);
458
459 auto associatedSubTree = getAssociatedSubTreePaths(
460 interfaceMap, associationMaps, associationPath, objectPath, 0,
461 endpointInterfaces);
462
463 output.insert(output.end(), associatedSubTree.begin(),
464 associatedSubTree.end());
465 }
466
467 return output;
468}