blob: 9a5244e31d1f762631cc4a8dfd42256f15e4b2f7 [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 {
75 for (const auto& interfaceMap : objectPath.second)
76 {
77 std::vector<std::string> output(std::min(
78 interfaces.size(), interfaceMap.second.size()));
79 // 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(),
83 interfaceMap.second.begin(),
84 interfaceMap.second.end(), output.begin()) !=
85 output.begin())
Willy Tuaba14d32023-01-31 14:19:59 -080086 {
87 addObjectMapResult(ret, thisPath, interfaceMap);
88 }
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 }
115 for (const auto& interfaceMap : pathRef->second)
116 {
117 std::vector<std::string> output(
118 std::min(interfaces.size(), interfaceMap.second.size()));
119 // Return iterator points at the first output elemtn,
120 // meaning that there are no intersections.
121 if (std::set_intersection(interfaces.begin(), interfaces.end(),
122 interfaceMap.second.begin(),
Patrick Williams9052ebd2024-08-16 15:22:16 -0400123 interfaceMap.second.end(), output.begin()) !=
124 output.begin())
Willy Tuaba14d32023-01-31 14:19:59 -0800125 {
126 results.emplace(interfaceMap.first, interfaceMap.second);
127 }
128 }
129
130 if (results.empty())
131 {
132 throw sdbusplus::xyz::openbmc_project::Common::Error::
133 ResourceNotFound();
134 }
135
136 return results;
137}
138
Patrick Williamsd884cdf2025-02-01 08:23:38 -0500139std::vector<InterfaceMapType::value_type> getSubTree(
140 const InterfaceMapType& interfaceMap, std::string reqPath, int32_t depth,
141 std::vector<std::string>& interfaces)
Willy Tuaba14d32023-01-31 14:19:59 -0800142{
143 if (depth <= 0)
144 {
145 depth = std::numeric_limits<int32_t>::max();
146 }
147 // Interfaces need to be sorted for intersect to function
148 std::sort(interfaces.begin(), interfaces.end());
149
150 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped
151 // will be guaranteed not to have a trailing "/"
152 if (!reqPath.ends_with("/"))
153 {
154 reqPath += "/";
155 }
156 std::string_view reqPathStripped =
157 std::string_view(reqPath).substr(0, reqPath.size() - 1);
158
159 if (!reqPathStripped.empty() &&
160 interfaceMap.find(reqPathStripped) == interfaceMap.end())
161 {
162 throw sdbusplus::xyz::openbmc_project::Common::Error::
163 ResourceNotFound();
164 }
165
166 std::vector<InterfaceMapType::value_type> ret;
167 for (const auto& objectPath : interfaceMap)
168 {
169 const auto& thisPath = objectPath.first;
170
171 // Skip exact match on stripped search term
172 if (thisPath == reqPathStripped)
173 {
174 continue;
175 }
176
177 if (thisPath.starts_with(reqPath))
178 {
179 // count the number of slashes past the stripped search term
Brad Bishop197de182025-07-09 14:41:37 -0400180 auto thisDepth = std::count(
181 thisPath.begin() + std::distance(reqPathStripped.begin(),
182 reqPathStripped.end()),
183 thisPath.end(), '/');
Willy Tuaba14d32023-01-31 14:19:59 -0800184 if (thisDepth <= depth)
185 {
186 for (const auto& interfaceMap : objectPath.second)
187 {
188 std::vector<std::string> output(std::min(
189 interfaces.size(), interfaceMap.second.size()));
190 // Return iterator points at the first output elemtn,
191 // meaning that there are no intersections.
192 if (std::set_intersection(
193 interfaces.begin(), interfaces.end(),
194 interfaceMap.second.begin(),
195 interfaceMap.second.end(),
196 output.begin()) != output.begin() ||
197 interfaces.empty())
198 {
199 addObjectMapResult(ret, thisPath, interfaceMap);
200 }
201 }
202 }
203 }
204 }
205
206 return ret;
207}
208
Patrick Williamsd884cdf2025-02-01 08:23:38 -0500209std::vector<std::string> getSubTreePaths(const InterfaceMapType& interfaceMap,
210 std::string reqPath, int32_t depth,
211 std::vector<std::string>& interfaces)
Willy Tuaba14d32023-01-31 14:19:59 -0800212{
213 if (depth <= 0)
214 {
215 depth = std::numeric_limits<int32_t>::max();
216 }
217 // Interfaces need to be sorted for intersect to function
218 std::sort(interfaces.begin(), interfaces.end());
219
220 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped
221 // will be guaranteed not to have a trailing "/"
222 if (!reqPath.ends_with("/"))
223 {
224 reqPath += "/";
225 }
226 std::string_view reqPathStripped =
227 std::string_view(reqPath).substr(0, reqPath.size() - 1);
228
229 if (!reqPathStripped.empty() &&
230 interfaceMap.find(reqPathStripped) == interfaceMap.end())
231 {
232 throw sdbusplus::xyz::openbmc_project::Common::Error::
233 ResourceNotFound();
234 }
235
236 std::vector<std::string> ret;
237 for (const auto& objectPath : interfaceMap)
238 {
239 const auto& thisPath = objectPath.first;
240
241 // Skip exact match on stripped search term
242 if (thisPath == reqPathStripped)
243 {
244 continue;
245 }
246
247 if (thisPath.starts_with(reqPath))
248 {
249 // count the number of slashes past the stripped search term
Brad Bishop197de182025-07-09 14:41:37 -0400250 auto thisDepth = std::count(
251 thisPath.begin() + std::distance(reqPathStripped.begin(),
252 reqPathStripped.end()),
253 thisPath.end(), '/');
Willy Tuaba14d32023-01-31 14:19:59 -0800254 if (thisDepth <= depth)
255 {
256 bool add = interfaces.empty();
257 for (const auto& interfaceMap : objectPath.second)
258 {
259 std::vector<std::string> output(std::min(
260 interfaces.size(), interfaceMap.second.size()));
261 // Return iterator points at the first output elemtn,
262 // meaning that there are no intersections.
Patrick Williams9052ebd2024-08-16 15:22:16 -0400263 if (std::set_intersection(
264 interfaces.begin(), interfaces.end(),
265 interfaceMap.second.begin(),
266 interfaceMap.second.end(), output.begin()) !=
267 output.begin())
Willy Tuaba14d32023-01-31 14:19:59 -0800268 {
269 add = true;
270 break;
271 }
272 }
273 if (add)
274 {
275 // TODO(ed) this is a copy
276 ret.emplace_back(thisPath);
277 }
278 }
279 }
280 }
281
282 return ret;
283}
Willy Tu58881d02022-10-02 20:46:45 +0000284
Patrick Williams9052ebd2024-08-16 15:22:16 -0400285std::vector<InterfaceMapType::value_type> getAssociatedSubTree(
286 const InterfaceMapType& interfaceMap,
287 const AssociationMaps& associationMaps,
288 const sdbusplus::message::object_path& associationPath,
289 const sdbusplus::message::object_path& reqPath, int32_t depth,
290 std::vector<std::string>& interfaces)
Willy Tu58881d02022-10-02 20:46:45 +0000291{
292 auto findEndpoint = associationMaps.ifaces.find(associationPath.str);
293 if (findEndpoint == associationMaps.ifaces.end())
294 {
295 return {};
296 }
297 const std::vector<std::string>& association =
298 std::get<endpointsPos>(findEndpoint->second);
299 std::unordered_set<std::string> associationSet(association.begin(),
300 association.end());
Patrick Williams8c250062024-08-23 12:28:07 -0400301 const std::vector<InterfaceMapType::value_type> interfacePairs =
Willy Tu58881d02022-10-02 20:46:45 +0000302 getSubTree(interfaceMap, reqPath, depth, interfaces);
303
304 std::vector<InterfaceMapType::value_type> output;
305 for (const InterfaceMapType::value_type& interfacePair : interfacePairs)
306 {
307 if (associationSet.contains(interfacePair.first))
308 {
309 output.emplace_back(interfacePair);
310 }
311 }
312 return output;
313}
314
315std::vector<std::string> getAssociatedSubTreePaths(
316 const InterfaceMapType& interfaceMap,
317 const AssociationMaps& associationMaps,
318 const sdbusplus::message::object_path& associationPath,
319 const sdbusplus::message::object_path& reqPath, int32_t depth,
320 std::vector<std::string>& interfaces)
321{
322 auto findEndpoint = associationMaps.ifaces.find(associationPath.str);
323 if (findEndpoint == associationMaps.ifaces.end())
324 {
325 return {};
326 }
327 const std::vector<std::string>& association =
328 std::get<endpointsPos>(findEndpoint->second);
329 std::unordered_set<std::string> associationSet(association.begin(),
330 association.end());
Patrick Williams8c250062024-08-23 12:28:07 -0400331 const std::vector<std::string> paths =
Willy Tu58881d02022-10-02 20:46:45 +0000332 getSubTreePaths(interfaceMap, reqPath, depth, interfaces);
333
334 std::vector<std::string> output;
335 for (const auto& path : paths)
336 {
337 if (associationSet.contains(path))
338 {
339 output.emplace_back(path);
340 }
341 }
342 return output;
Patrick Williams1a19e6a2023-05-10 07:51:32 -0500343}
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500344
345// This function works like getSubTreePaths() but only matching id with
346// the leaf-name instead of full path.
347std::vector<std::string> getSubTreePathsById(
348 const InterfaceMapType& interfaceMap, const std::string& id,
349 const std::string& objectPath, std::vector<std::string>& interfaces)
350{
351 std::sort(interfaces.begin(), interfaces.end());
352
353 std::string localObjectPath = objectPath;
354
355 if (!localObjectPath.ends_with("/"))
356 {
357 localObjectPath += "/";
358 }
359 std::string_view objectPathStripped =
360 std::string_view(localObjectPath).substr(0, localObjectPath.size() - 1);
361
362 if (!objectPathStripped.empty() &&
363 interfaceMap.find(objectPathStripped) == interfaceMap.end())
364 {
365 throw sdbusplus::xyz::openbmc_project::Common::Error::
366 ResourceNotFound();
367 }
368
Myung Bae1e3f7812025-03-25 08:33:22 -0700369 bool validId = false;
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500370 std::vector<std::string> output;
371 for (const auto& path : interfaceMap)
372 {
373 const auto& thisPath = path.first;
374
Myung Bae1e3f7812025-03-25 08:33:22 -0700375 // Skip the path does not end with the id
376 if (!thisPath.ends_with("/" + id))
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500377 {
378 continue;
379 }
380
Myung Bae1e3f7812025-03-25 08:33:22 -0700381 // Valid if id is matching
382 validId = true;
383
384 // Skip exact match on stripped search term
385 if (thisPath == objectPathStripped)
386 {
387 continue;
388 }
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500389 if (thisPath.starts_with(objectPath))
390 {
391 for (const auto& interfaceMap : path.second)
392 {
393 std::vector<std::string> tempoutput(
394 std::min(interfaces.size(), interfaceMap.second.size()));
395 if (std::set_intersection(
396 interfaces.begin(), interfaces.end(),
397 interfaceMap.second.begin(), interfaceMap.second.end(),
398 tempoutput.begin()) != tempoutput.begin())
399 {
400 output.emplace_back(thisPath);
401 break;
402 }
403 }
404 }
405 }
Myung Bae1e3f7812025-03-25 08:33:22 -0700406 if (!validId)
407 {
408 throw sdbusplus::xyz::openbmc_project::Common::Error::
409 ResourceNotFound();
410 }
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500411 return output;
412}
413
414std::vector<InterfaceMapType::value_type> getAssociatedSubTreeById(
415 const InterfaceMapType& interfaceMap,
416 const AssociationMaps& associationMaps, const std::string& id,
417 const std::string& objectPath, std::vector<std::string>& subtreeInterfaces,
418 const std::string& association,
419 std::vector<std::string>& endpointInterfaces)
420{
421 std::vector<std::string> subtreePaths =
422 getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces);
423
424 std::vector<InterfaceMapType::value_type> output;
425 for (const auto& subtreePath : subtreePaths)
426 {
427 // Form the association path
428 std::string associationPathStr = subtreePath + "/" + association;
429 sdbusplus::message::object_path associationPath(associationPathStr);
430
431 auto associatedSubTree =
432 getAssociatedSubTree(interfaceMap, associationMaps, associationPath,
433 objectPath, 0, endpointInterfaces);
434
435 output.insert(output.end(), associatedSubTree.begin(),
436 associatedSubTree.end());
437 }
438 return output;
439}
440
441std::vector<std::string> getAssociatedSubTreePathsById(
442 const InterfaceMapType& interfaceMap,
443 const AssociationMaps& associationMaps, const std::string& id,
444 const std::string& objectPath, std::vector<std::string>& subtreeInterfaces,
445 const std::string& association,
446 std::vector<std::string>& endpointInterfaces)
447{
448 std::vector<std::string> subtreePaths =
449 getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces);
450 std::vector<std::string> output;
451 for (const auto& subtreePath : subtreePaths)
452 {
453 // Form the association path
454 std::string associationPathStr = subtreePath + "/" + association;
455 sdbusplus::message::object_path associationPath(associationPathStr);
456
457 auto associatedSubTree = getAssociatedSubTreePaths(
458 interfaceMap, associationMaps, associationPath, objectPath, 0,
459 endpointInterfaces);
460
461 output.insert(output.end(), associatedSubTree.begin(),
462 associatedSubTree.end());
463 }
464
465 return output;
466}