blob: a1efb8441a8b9e319cfe6246a05b6f0ea343a4d7 [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>
8#include <string>
Willy Tu58881d02022-10-02 20:46:45 +00009#include <unordered_set>
Willy Tuaba14d32023-01-31 14:19:59 -080010#include <utility>
11#include <vector>
12
13void addObjectMapResult(std::vector<InterfaceMapType::value_type>& objectMap,
14 const std::string& objectPath,
15 const ConnectionNames::value_type& interfaceMap)
16{
17 // Adds an object path/service name/interface list entry to
18 // the results of GetSubTree and GetAncestors.
19 // If an entry for the object path already exists, just add the
20 // service name and interfaces to that entry, otherwise create
21 // a new entry.
22 auto entry = std::find_if(
23 objectMap.begin(), objectMap.end(),
24 [&objectPath](const auto& i) { return objectPath == i.first; });
25
26 if (entry != objectMap.end())
27 {
28 entry->second.emplace(interfaceMap);
29 }
30 else
31 {
32 InterfaceMapType::value_type object;
33 object.first = objectPath;
34 object.second.emplace(interfaceMap);
35 objectMap.push_back(object);
36 }
37}
38
39std::vector<InterfaceMapType::value_type>
40 getAncestors(const InterfaceMapType& interfaceMap, std::string reqPath,
41 std::vector<std::string>& interfaces)
42{
43 // Interfaces need to be sorted for intersect to function
44 std::sort(interfaces.begin(), interfaces.end());
45
46 if (reqPath.ends_with("/"))
47 {
48 reqPath.pop_back();
49 }
50 if (!reqPath.empty() && interfaceMap.find(reqPath) == interfaceMap.end())
51 {
52 throw sdbusplus::xyz::openbmc_project::Common::Error::
53 ResourceNotFound();
54 }
55
56 std::vector<InterfaceMapType::value_type> ret;
57 for (const auto& objectPath : interfaceMap)
58 {
59 const auto& thisPath = objectPath.first;
60
61 if (reqPath == thisPath)
62 {
63 continue;
64 }
65
66 if (reqPath.starts_with(thisPath))
67 {
68 if (interfaces.empty())
69 {
70 ret.emplace_back(objectPath);
71 }
72 else
73 {
74 for (const auto& interfaceMap : objectPath.second)
75 {
76 std::vector<std::string> output(std::min(
77 interfaces.size(), interfaceMap.second.size()));
78 // Return iterator points at the first output elemtn,
79 // meaning that there are no intersections.
80 if (std::set_intersection(interfaces.begin(),
81 interfaces.end(),
82 interfaceMap.second.begin(),
83 interfaceMap.second.end(),
84 output.begin()) != output.begin())
85 {
86 addObjectMapResult(ret, thisPath, interfaceMap);
87 }
88 }
89 }
90 }
91 }
92
93 return ret;
94}
95
96ConnectionNames getObject(const InterfaceMapType& interfaceMap,
97 const std::string& path,
98 std::vector<std::string>& interfaces)
99{
100 ConnectionNames results;
101
102 // Interfaces need to be sorted for intersect to function
103 std::sort(interfaces.begin(), interfaces.end());
104 auto pathRef = interfaceMap.find(path);
105 if (pathRef == interfaceMap.end())
106 {
107 throw sdbusplus::xyz::openbmc_project::Common::Error::
108 ResourceNotFound();
109 }
110 if (interfaces.empty())
111 {
112 return pathRef->second;
113 }
114 for (const auto& interfaceMap : pathRef->second)
115 {
116 std::vector<std::string> output(
117 std::min(interfaces.size(), interfaceMap.second.size()));
118 // Return iterator points at the first output elemtn,
119 // meaning that there are no intersections.
120 if (std::set_intersection(interfaces.begin(), interfaces.end(),
121 interfaceMap.second.begin(),
122 interfaceMap.second.end(),
123 output.begin()) != output.begin())
124 {
125 results.emplace(interfaceMap.first, interfaceMap.second);
126 }
127 }
128
129 if (results.empty())
130 {
131 throw sdbusplus::xyz::openbmc_project::Common::Error::
132 ResourceNotFound();
133 }
134
135 return results;
136}
137
138std::vector<InterfaceMapType::value_type>
139 getSubTree(const InterfaceMapType& interfaceMap, std::string reqPath,
140 int32_t depth, std::vector<std::string>& interfaces)
141{
142 if (depth <= 0)
143 {
144 depth = std::numeric_limits<int32_t>::max();
145 }
146 // Interfaces need to be sorted for intersect to function
147 std::sort(interfaces.begin(), interfaces.end());
148
149 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped
150 // will be guaranteed not to have a trailing "/"
151 if (!reqPath.ends_with("/"))
152 {
153 reqPath += "/";
154 }
155 std::string_view reqPathStripped =
156 std::string_view(reqPath).substr(0, reqPath.size() - 1);
157
158 if (!reqPathStripped.empty() &&
159 interfaceMap.find(reqPathStripped) == interfaceMap.end())
160 {
161 throw sdbusplus::xyz::openbmc_project::Common::Error::
162 ResourceNotFound();
163 }
164
165 std::vector<InterfaceMapType::value_type> ret;
166 for (const auto& objectPath : interfaceMap)
167 {
168 const auto& thisPath = objectPath.first;
169
170 // Skip exact match on stripped search term
171 if (thisPath == reqPathStripped)
172 {
173 continue;
174 }
175
176 if (thisPath.starts_with(reqPath))
177 {
178 // count the number of slashes past the stripped search term
179 int32_t thisDepth = std::count(
180 thisPath.begin() + reqPathStripped.size(), thisPath.end(), '/');
181 if (thisDepth <= depth)
182 {
183 for (const auto& interfaceMap : objectPath.second)
184 {
185 std::vector<std::string> output(std::min(
186 interfaces.size(), interfaceMap.second.size()));
187 // Return iterator points at the first output elemtn,
188 // meaning that there are no intersections.
189 if (std::set_intersection(
190 interfaces.begin(), interfaces.end(),
191 interfaceMap.second.begin(),
192 interfaceMap.second.end(),
193 output.begin()) != output.begin() ||
194 interfaces.empty())
195 {
196 addObjectMapResult(ret, thisPath, interfaceMap);
197 }
198 }
199 }
200 }
201 }
202
203 return ret;
204}
205
206std::vector<std::string> getSubTreePaths(const InterfaceMapType& interfaceMap,
207 std::string reqPath, int32_t depth,
208 std::vector<std::string>& interfaces)
209{
210 if (depth <= 0)
211 {
212 depth = std::numeric_limits<int32_t>::max();
213 }
214 // Interfaces need to be sorted for intersect to function
215 std::sort(interfaces.begin(), interfaces.end());
216
217 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped
218 // will be guaranteed not to have a trailing "/"
219 if (!reqPath.ends_with("/"))
220 {
221 reqPath += "/";
222 }
223 std::string_view reqPathStripped =
224 std::string_view(reqPath).substr(0, reqPath.size() - 1);
225
226 if (!reqPathStripped.empty() &&
227 interfaceMap.find(reqPathStripped) == interfaceMap.end())
228 {
229 throw sdbusplus::xyz::openbmc_project::Common::Error::
230 ResourceNotFound();
231 }
232
233 std::vector<std::string> ret;
234 for (const auto& objectPath : interfaceMap)
235 {
236 const auto& thisPath = objectPath.first;
237
238 // Skip exact match on stripped search term
239 if (thisPath == reqPathStripped)
240 {
241 continue;
242 }
243
244 if (thisPath.starts_with(reqPath))
245 {
246 // count the number of slashes past the stripped search term
247 int thisDepth = std::count(
248 thisPath.begin() + reqPathStripped.size(), thisPath.end(), '/');
249 if (thisDepth <= depth)
250 {
251 bool add = interfaces.empty();
252 for (const auto& interfaceMap : objectPath.second)
253 {
254 std::vector<std::string> output(std::min(
255 interfaces.size(), interfaceMap.second.size()));
256 // Return iterator points at the first output elemtn,
257 // meaning that there are no intersections.
258 if (std::set_intersection(interfaces.begin(),
259 interfaces.end(),
260 interfaceMap.second.begin(),
261 interfaceMap.second.end(),
262 output.begin()) != output.begin())
263 {
264 add = true;
265 break;
266 }
267 }
268 if (add)
269 {
270 // TODO(ed) this is a copy
271 ret.emplace_back(thisPath);
272 }
273 }
274 }
275 }
276
277 return ret;
278}
Willy Tu58881d02022-10-02 20:46:45 +0000279
280std::vector<InterfaceMapType::value_type>
281 getAssociatedSubTree(const InterfaceMapType& interfaceMap,
282 const AssociationMaps& associationMaps,
283 const sdbusplus::message::object_path& associationPath,
284 const sdbusplus::message::object_path& reqPath,
285 int32_t depth, std::vector<std::string>& interfaces)
286{
287 auto findEndpoint = associationMaps.ifaces.find(associationPath.str);
288 if (findEndpoint == associationMaps.ifaces.end())
289 {
290 return {};
291 }
292 const std::vector<std::string>& association =
293 std::get<endpointsPos>(findEndpoint->second);
294 std::unordered_set<std::string> associationSet(association.begin(),
295 association.end());
296 const std::vector<InterfaceMapType::value_type>& interfacePairs =
297 getSubTree(interfaceMap, reqPath, depth, interfaces);
298
299 std::vector<InterfaceMapType::value_type> output;
300 for (const InterfaceMapType::value_type& interfacePair : interfacePairs)
301 {
302 if (associationSet.contains(interfacePair.first))
303 {
304 output.emplace_back(interfacePair);
305 }
306 }
307 return output;
308}
309
310std::vector<std::string> getAssociatedSubTreePaths(
311 const InterfaceMapType& interfaceMap,
312 const AssociationMaps& associationMaps,
313 const sdbusplus::message::object_path& associationPath,
314 const sdbusplus::message::object_path& reqPath, int32_t depth,
315 std::vector<std::string>& interfaces)
316{
317 auto findEndpoint = associationMaps.ifaces.find(associationPath.str);
318 if (findEndpoint == associationMaps.ifaces.end())
319 {
320 return {};
321 }
322 const std::vector<std::string>& association =
323 std::get<endpointsPos>(findEndpoint->second);
324 std::unordered_set<std::string> associationSet(association.begin(),
325 association.end());
326 const std::vector<std::string>& paths =
327 getSubTreePaths(interfaceMap, reqPath, depth, interfaces);
328
329 std::vector<std::string> output;
330 for (const auto& path : paths)
331 {
332 if (associationSet.contains(path))
333 {
334 output.emplace_back(path);
335 }
336 }
337 return output;
338}