blob: af8ef90a715ff1b602552df0f387537acf1c9cae [file] [log] [blame]
Willy Tuaba14d32023-01-31 14:19:59 -08001#include "handler.hpp"
2
Brad Bishop78b04862025-07-08 16:00:37 -04003#include "path.hpp"
Willy Tuaba14d32023-01-31 14:19:59 -08004#include "types.hpp"
5
6#include <xyz/openbmc_project/Common/error.hpp>
7
8#include <algorithm>
Brad Bishop197de182025-07-09 14:41:37 -04009#include <iterator>
Willy Tuaba14d32023-01-31 14:19:59 -080010#include <string>
Willy Tu58881d02022-10-02 20:46:45 +000011#include <unordered_set>
Willy Tuaba14d32023-01-31 14:19:59 -080012#include <utility>
13#include <vector>
14
15void addObjectMapResult(std::vector<InterfaceMapType::value_type>& objectMap,
16 const std::string& objectPath,
17 const ConnectionNames::value_type& interfaceMap)
18{
19 // Adds an object path/service name/interface list entry to
20 // the results of GetSubTree and GetAncestors.
21 // If an entry for the object path already exists, just add the
22 // service name and interfaces to that entry, otherwise create
23 // a new entry.
Patrick Williams47b68cb2023-10-20 11:20:07 -050024 auto entry = std::find_if(
25 objectMap.begin(), objectMap.end(),
26 [&objectPath](const auto& i) { return objectPath == i.first; });
Willy Tuaba14d32023-01-31 14:19:59 -080027
28 if (entry != objectMap.end())
29 {
30 entry->second.emplace(interfaceMap);
31 }
32 else
33 {
34 InterfaceMapType::value_type object;
35 object.first = objectPath;
36 object.second.emplace(interfaceMap);
37 objectMap.push_back(object);
38 }
39}
40
Patrick Williamsd884cdf2025-02-01 08:23:38 -050041std::vector<InterfaceMapType::value_type> getAncestors(
42 const InterfaceMapType& interfaceMap, std::string reqPath,
43 std::vector<std::string>& interfaces)
Willy Tuaba14d32023-01-31 14:19:59 -080044{
45 // Interfaces need to be sorted for intersect to function
46 std::sort(interfaces.begin(), interfaces.end());
47
48 if (reqPath.ends_with("/"))
49 {
50 reqPath.pop_back();
51 }
Brad Bishopa7257062025-07-29 14:12:35 -040052 if (!reqPath.empty() && !interfaceMap.contains(reqPath))
Willy Tuaba14d32023-01-31 14:19:59 -080053 {
54 throw sdbusplus::xyz::openbmc_project::Common::Error::
55 ResourceNotFound();
56 }
57
58 std::vector<InterfaceMapType::value_type> ret;
59 for (const auto& objectPath : interfaceMap)
60 {
61 const auto& thisPath = objectPath.first;
62
63 if (reqPath == thisPath)
64 {
65 continue;
66 }
67
68 if (reqPath.starts_with(thisPath))
69 {
70 if (interfaces.empty())
71 {
72 ret.emplace_back(objectPath);
73 }
74 else
75 {
Brad Bishop11c0cc32025-07-08 16:04:25 -040076 for (const auto& connectionInterfaces : objectPath.second)
Willy Tuaba14d32023-01-31 14:19:59 -080077 {
78 std::vector<std::string> output(std::min(
Brad Bishop11c0cc32025-07-08 16:04:25 -040079 interfaces.size(), connectionInterfaces.second.size()));
Willy Tuaba14d32023-01-31 14:19:59 -080080 // Return iterator points at the first output elemtn,
81 // meaning that there are no intersections.
Patrick Williams9052ebd2024-08-16 15:22:16 -040082 if (std::set_intersection(
83 interfaces.begin(), interfaces.end(),
Brad Bishop11c0cc32025-07-08 16:04:25 -040084 connectionInterfaces.second.begin(),
85 connectionInterfaces.second.end(),
86 output.begin()) != output.begin())
Willy Tuaba14d32023-01-31 14:19:59 -080087 {
Brad Bishop11c0cc32025-07-08 16:04:25 -040088 addObjectMapResult(ret, thisPath, connectionInterfaces);
Willy Tuaba14d32023-01-31 14:19:59 -080089 }
90 }
91 }
92 }
93 }
94
95 return ret;
96}
97
98ConnectionNames getObject(const InterfaceMapType& interfaceMap,
99 const std::string& path,
100 std::vector<std::string>& interfaces)
101{
102 ConnectionNames results;
103
104 // Interfaces need to be sorted for intersect to function
105 std::sort(interfaces.begin(), interfaces.end());
106 auto pathRef = interfaceMap.find(path);
107 if (pathRef == interfaceMap.end())
108 {
109 throw sdbusplus::xyz::openbmc_project::Common::Error::
110 ResourceNotFound();
111 }
112 if (interfaces.empty())
113 {
114 return pathRef->second;
115 }
Brad Bishop11c0cc32025-07-08 16:04:25 -0400116 for (const auto& connectionInterfaces : pathRef->second)
Willy Tuaba14d32023-01-31 14:19:59 -0800117 {
118 std::vector<std::string> output(
Brad Bishop11c0cc32025-07-08 16:04:25 -0400119 std::min(interfaces.size(), connectionInterfaces.second.size()));
Willy Tuaba14d32023-01-31 14:19:59 -0800120 // Return iterator points at the first output elemtn,
121 // meaning that there are no intersections.
122 if (std::set_intersection(interfaces.begin(), interfaces.end(),
Brad Bishop11c0cc32025-07-08 16:04:25 -0400123 connectionInterfaces.second.begin(),
124 connectionInterfaces.second.end(),
125 output.begin()) != output.begin())
Willy Tuaba14d32023-01-31 14:19:59 -0800126 {
Brad Bishop11c0cc32025-07-08 16:04:25 -0400127 results.emplace(connectionInterfaces.first,
128 connectionInterfaces.second);
Willy Tuaba14d32023-01-31 14:19:59 -0800129 }
130 }
131
132 if (results.empty())
133 {
134 throw sdbusplus::xyz::openbmc_project::Common::Error::
135 ResourceNotFound();
136 }
137
138 return results;
139}
140
Patrick Williamsd884cdf2025-02-01 08:23:38 -0500141std::vector<InterfaceMapType::value_type> getSubTree(
142 const InterfaceMapType& interfaceMap, std::string reqPath, int32_t depth,
143 std::vector<std::string>& interfaces)
Willy Tuaba14d32023-01-31 14:19:59 -0800144{
145 if (depth <= 0)
146 {
147 depth = std::numeric_limits<int32_t>::max();
148 }
149 // Interfaces need to be sorted for intersect to function
150 std::sort(interfaces.begin(), interfaces.end());
151
152 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped
153 // will be guaranteed not to have a trailing "/"
154 if (!reqPath.ends_with("/"))
155 {
156 reqPath += "/";
157 }
158 std::string_view reqPathStripped =
159 std::string_view(reqPath).substr(0, reqPath.size() - 1);
160
161 if (!reqPathStripped.empty() &&
162 interfaceMap.find(reqPathStripped) == interfaceMap.end())
163 {
164 throw sdbusplus::xyz::openbmc_project::Common::Error::
165 ResourceNotFound();
166 }
167
168 std::vector<InterfaceMapType::value_type> ret;
169 for (const auto& objectPath : interfaceMap)
170 {
171 const auto& thisPath = objectPath.first;
172
173 // Skip exact match on stripped search term
174 if (thisPath == reqPathStripped)
175 {
176 continue;
177 }
178
179 if (thisPath.starts_with(reqPath))
180 {
181 // count the number of slashes past the stripped search term
Brad Bishop197de182025-07-09 14:41:37 -0400182 auto thisDepth = std::count(
183 thisPath.begin() + std::distance(reqPathStripped.begin(),
184 reqPathStripped.end()),
185 thisPath.end(), '/');
Willy Tuaba14d32023-01-31 14:19:59 -0800186 if (thisDepth <= depth)
187 {
Brad Bishop11c0cc32025-07-08 16:04:25 -0400188 for (const auto& connectionInterfaces : objectPath.second)
Willy Tuaba14d32023-01-31 14:19:59 -0800189 {
190 std::vector<std::string> output(std::min(
Brad Bishop11c0cc32025-07-08 16:04:25 -0400191 interfaces.size(), connectionInterfaces.second.size()));
Willy Tuaba14d32023-01-31 14:19:59 -0800192 // Return iterator points at the first output elemtn,
193 // meaning that there are no intersections.
194 if (std::set_intersection(
195 interfaces.begin(), interfaces.end(),
Brad Bishop11c0cc32025-07-08 16:04:25 -0400196 connectionInterfaces.second.begin(),
197 connectionInterfaces.second.end(),
Willy Tuaba14d32023-01-31 14:19:59 -0800198 output.begin()) != output.begin() ||
199 interfaces.empty())
200 {
Brad Bishop11c0cc32025-07-08 16:04:25 -0400201 addObjectMapResult(ret, thisPath, connectionInterfaces);
Willy Tuaba14d32023-01-31 14:19:59 -0800202 }
203 }
204 }
205 }
206 }
207
208 return ret;
209}
210
Patrick Williamsd884cdf2025-02-01 08:23:38 -0500211std::vector<std::string> getSubTreePaths(const InterfaceMapType& interfaceMap,
212 std::string reqPath, int32_t depth,
213 std::vector<std::string>& interfaces)
Willy Tuaba14d32023-01-31 14:19:59 -0800214{
215 if (depth <= 0)
216 {
217 depth = std::numeric_limits<int32_t>::max();
218 }
219 // Interfaces need to be sorted for intersect to function
220 std::sort(interfaces.begin(), interfaces.end());
221
222 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped
223 // will be guaranteed not to have a trailing "/"
224 if (!reqPath.ends_with("/"))
225 {
226 reqPath += "/";
227 }
228 std::string_view reqPathStripped =
229 std::string_view(reqPath).substr(0, reqPath.size() - 1);
230
231 if (!reqPathStripped.empty() &&
232 interfaceMap.find(reqPathStripped) == interfaceMap.end())
233 {
234 throw sdbusplus::xyz::openbmc_project::Common::Error::
235 ResourceNotFound();
236 }
237
238 std::vector<std::string> ret;
239 for (const auto& objectPath : interfaceMap)
240 {
241 const auto& thisPath = objectPath.first;
242
243 // Skip exact match on stripped search term
244 if (thisPath == reqPathStripped)
245 {
246 continue;
247 }
248
249 if (thisPath.starts_with(reqPath))
250 {
251 // count the number of slashes past the stripped search term
Brad Bishop197de182025-07-09 14:41:37 -0400252 auto thisDepth = std::count(
253 thisPath.begin() + std::distance(reqPathStripped.begin(),
254 reqPathStripped.end()),
255 thisPath.end(), '/');
Willy Tuaba14d32023-01-31 14:19:59 -0800256 if (thisDepth <= depth)
257 {
258 bool add = interfaces.empty();
Brad Bishop11c0cc32025-07-08 16:04:25 -0400259 for (const auto& connectionInterfaces : objectPath.second)
Willy Tuaba14d32023-01-31 14:19:59 -0800260 {
261 std::vector<std::string> output(std::min(
Brad Bishop11c0cc32025-07-08 16:04:25 -0400262 interfaces.size(), connectionInterfaces.second.size()));
Willy Tuaba14d32023-01-31 14:19:59 -0800263 // Return iterator points at the first output elemtn,
264 // meaning that there are no intersections.
Patrick Williams9052ebd2024-08-16 15:22:16 -0400265 if (std::set_intersection(
266 interfaces.begin(), interfaces.end(),
Brad Bishop11c0cc32025-07-08 16:04:25 -0400267 connectionInterfaces.second.begin(),
268 connectionInterfaces.second.end(),
269 output.begin()) != output.begin())
Willy Tuaba14d32023-01-31 14:19:59 -0800270 {
271 add = true;
272 break;
273 }
274 }
275 if (add)
276 {
277 // TODO(ed) this is a copy
278 ret.emplace_back(thisPath);
279 }
280 }
281 }
282 }
283
284 return ret;
285}
Willy Tu58881d02022-10-02 20:46:45 +0000286
Patrick Williams9052ebd2024-08-16 15:22:16 -0400287std::vector<InterfaceMapType::value_type> getAssociatedSubTree(
288 const InterfaceMapType& interfaceMap,
289 const AssociationMaps& associationMaps,
290 const sdbusplus::message::object_path& associationPath,
291 const sdbusplus::message::object_path& reqPath, int32_t depth,
292 std::vector<std::string>& interfaces)
Willy Tu58881d02022-10-02 20:46:45 +0000293{
294 auto findEndpoint = associationMaps.ifaces.find(associationPath.str);
295 if (findEndpoint == associationMaps.ifaces.end())
296 {
297 return {};
298 }
299 const std::vector<std::string>& association =
300 std::get<endpointsPos>(findEndpoint->second);
301 std::unordered_set<std::string> associationSet(association.begin(),
302 association.end());
Patrick Williams8c250062024-08-23 12:28:07 -0400303 const std::vector<InterfaceMapType::value_type> interfacePairs =
Willy Tu58881d02022-10-02 20:46:45 +0000304 getSubTree(interfaceMap, reqPath, depth, interfaces);
305
306 std::vector<InterfaceMapType::value_type> output;
307 for (const InterfaceMapType::value_type& interfacePair : interfacePairs)
308 {
309 if (associationSet.contains(interfacePair.first))
310 {
311 output.emplace_back(interfacePair);
312 }
313 }
314 return output;
315}
316
317std::vector<std::string> getAssociatedSubTreePaths(
318 const InterfaceMapType& interfaceMap,
319 const AssociationMaps& associationMaps,
320 const sdbusplus::message::object_path& associationPath,
321 const sdbusplus::message::object_path& reqPath, int32_t depth,
322 std::vector<std::string>& interfaces)
323{
324 auto findEndpoint = associationMaps.ifaces.find(associationPath.str);
325 if (findEndpoint == associationMaps.ifaces.end())
326 {
327 return {};
328 }
329 const std::vector<std::string>& association =
330 std::get<endpointsPos>(findEndpoint->second);
331 std::unordered_set<std::string> associationSet(association.begin(),
332 association.end());
Patrick Williams8c250062024-08-23 12:28:07 -0400333 const std::vector<std::string> paths =
Willy Tu58881d02022-10-02 20:46:45 +0000334 getSubTreePaths(interfaceMap, reqPath, depth, interfaces);
335
336 std::vector<std::string> output;
337 for (const auto& path : paths)
338 {
339 if (associationSet.contains(path))
340 {
341 output.emplace_back(path);
342 }
343 }
344 return output;
Patrick Williams1a19e6a2023-05-10 07:51:32 -0500345}
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500346
347// This function works like getSubTreePaths() but only matching id with
348// the leaf-name instead of full path.
Brad Bishop7dd20292025-07-29 16:53:45 -0400349static std::vector<std::string> getSubTreePathsById(
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500350 const InterfaceMapType& interfaceMap, const std::string& id,
351 const std::string& objectPath, std::vector<std::string>& interfaces)
352{
353 std::sort(interfaces.begin(), interfaces.end());
354
355 std::string localObjectPath = objectPath;
356
357 if (!localObjectPath.ends_with("/"))
358 {
359 localObjectPath += "/";
360 }
361 std::string_view objectPathStripped =
362 std::string_view(localObjectPath).substr(0, localObjectPath.size() - 1);
363
364 if (!objectPathStripped.empty() &&
365 interfaceMap.find(objectPathStripped) == interfaceMap.end())
366 {
367 throw sdbusplus::xyz::openbmc_project::Common::Error::
368 ResourceNotFound();
369 }
370
Myung Bae1e3f7812025-03-25 08:33:22 -0700371 bool validId = false;
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500372 std::vector<std::string> output;
373 for (const auto& path : interfaceMap)
374 {
375 const auto& thisPath = path.first;
376
Myung Bae1e3f7812025-03-25 08:33:22 -0700377 // Skip the path does not end with the id
378 if (!thisPath.ends_with("/" + id))
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500379 {
380 continue;
381 }
382
Myung Bae1e3f7812025-03-25 08:33:22 -0700383 // Valid if id is matching
384 validId = true;
385
386 // Skip exact match on stripped search term
387 if (thisPath == objectPathStripped)
388 {
389 continue;
390 }
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500391 if (thisPath.starts_with(objectPath))
392 {
Brad Bishop11c0cc32025-07-08 16:04:25 -0400393 for (const auto& connectionInterfaces : path.second)
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500394 {
Brad Bishop11c0cc32025-07-08 16:04:25 -0400395 std::vector<std::string> tempoutput(std::min(
396 interfaces.size(), connectionInterfaces.second.size()));
397 if (std::set_intersection(interfaces.begin(), interfaces.end(),
398 connectionInterfaces.second.begin(),
399 connectionInterfaces.second.end(),
400 tempoutput.begin()) !=
401 tempoutput.begin())
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500402 {
403 output.emplace_back(thisPath);
404 break;
405 }
406 }
407 }
408 }
Myung Bae1e3f7812025-03-25 08:33:22 -0700409 if (!validId)
410 {
411 throw sdbusplus::xyz::openbmc_project::Common::Error::
412 ResourceNotFound();
413 }
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500414 return output;
415}
416
417std::vector<InterfaceMapType::value_type> getAssociatedSubTreeById(
418 const InterfaceMapType& interfaceMap,
419 const AssociationMaps& associationMaps, const std::string& id,
420 const std::string& objectPath, std::vector<std::string>& subtreeInterfaces,
421 const std::string& association,
422 std::vector<std::string>& endpointInterfaces)
423{
424 std::vector<std::string> subtreePaths =
425 getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces);
426
427 std::vector<InterfaceMapType::value_type> output;
428 for (const auto& subtreePath : subtreePaths)
429 {
430 // Form the association path
Brad Bishop78b04862025-07-08 16:00:37 -0400431 std::string associationPathStr =
432 appendPathSegment(subtreePath, association);
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500433 sdbusplus::message::object_path associationPath(associationPathStr);
434
435 auto associatedSubTree =
436 getAssociatedSubTree(interfaceMap, associationMaps, associationPath,
437 objectPath, 0, endpointInterfaces);
438
439 output.insert(output.end(), associatedSubTree.begin(),
440 associatedSubTree.end());
441 }
442 return output;
443}
444
445std::vector<std::string> getAssociatedSubTreePathsById(
446 const InterfaceMapType& interfaceMap,
447 const AssociationMaps& associationMaps, const std::string& id,
448 const std::string& objectPath, std::vector<std::string>& subtreeInterfaces,
449 const std::string& association,
450 std::vector<std::string>& endpointInterfaces)
451{
452 std::vector<std::string> subtreePaths =
453 getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces);
454 std::vector<std::string> output;
455 for (const auto& subtreePath : subtreePaths)
456 {
457 // Form the association path
Brad Bishop78b04862025-07-08 16:00:37 -0400458 std::string associationPathStr =
459 appendPathSegment(subtreePath, association);
Lakshmi Yadlapatic3633232024-04-09 10:47:29 -0500460 sdbusplus::message::object_path associationPath(associationPathStr);
461
462 auto associatedSubTree = getAssociatedSubTreePaths(
463 interfaceMap, associationMaps, associationPath, objectPath, 0,
464 endpointInterfaces);
465
466 output.insert(output.end(), associatedSubTree.begin(),
467 associatedSubTree.end());
468 }
469
470 return output;
471}