xref: /openbmc/phosphor-objmgr/src/handler.cpp (revision 670edd12)
1 #include "handler.hpp"
2 
3 #include "types.hpp"
4 
5 #include <xyz/openbmc_project/Common/error.hpp>
6 
7 #include <algorithm>
8 #include <string>
9 #include <unordered_set>
10 #include <utility>
11 #include <vector>
12 
13 void 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(objectMap.begin(), objectMap.end(),
23                               [&objectPath](const auto& i) {
24         return objectPath == i.first;
25     });
26 
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 
40 std::vector<InterfaceMapType::value_type>
41     getAncestors(const InterfaceMapType& interfaceMap, std::string reqPath,
42                  std::vector<std::string>& interfaces)
43 {
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.
81                     if (std::set_intersection(interfaces.begin(),
82                                               interfaces.end(),
83                                               interfaceMap.second.begin(),
84                                               interfaceMap.second.end(),
85                                               output.begin()) != output.begin())
86                     {
87                         addObjectMapResult(ret, thisPath, interfaceMap);
88                     }
89                 }
90             }
91         }
92     }
93 
94     return ret;
95 }
96 
97 ConnectionNames 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(),
123                                   interfaceMap.second.end(),
124                                   output.begin()) != output.begin())
125         {
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 
139 std::vector<InterfaceMapType::value_type>
140     getSubTree(const InterfaceMapType& interfaceMap, std::string reqPath,
141                int32_t depth, std::vector<std::string>& interfaces)
142 {
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
180             int32_t thisDepth = std::count(
181                 thisPath.begin() + reqPathStripped.size(), thisPath.end(), '/');
182             if (thisDepth <= depth)
183             {
184                 for (const auto& interfaceMap : objectPath.second)
185                 {
186                     std::vector<std::string> output(std::min(
187                         interfaces.size(), interfaceMap.second.size()));
188                     // Return iterator points at the first output elemtn,
189                     // meaning that there are no intersections.
190                     if (std::set_intersection(
191                             interfaces.begin(), interfaces.end(),
192                             interfaceMap.second.begin(),
193                             interfaceMap.second.end(),
194                             output.begin()) != output.begin() ||
195                         interfaces.empty())
196                     {
197                         addObjectMapResult(ret, thisPath, interfaceMap);
198                     }
199                 }
200             }
201         }
202     }
203 
204     return ret;
205 }
206 
207 std::vector<std::string> getSubTreePaths(const InterfaceMapType& interfaceMap,
208                                          std::string reqPath, int32_t depth,
209                                          std::vector<std::string>& interfaces)
210 {
211     if (depth <= 0)
212     {
213         depth = std::numeric_limits<int32_t>::max();
214     }
215     // Interfaces need to be sorted for intersect to function
216     std::sort(interfaces.begin(), interfaces.end());
217 
218     // reqPath is now guaranteed to have a trailing "/" while reqPathStripped
219     // will be guaranteed not to have a trailing "/"
220     if (!reqPath.ends_with("/"))
221     {
222         reqPath += "/";
223     }
224     std::string_view reqPathStripped =
225         std::string_view(reqPath).substr(0, reqPath.size() - 1);
226 
227     if (!reqPathStripped.empty() &&
228         interfaceMap.find(reqPathStripped) == interfaceMap.end())
229     {
230         throw sdbusplus::xyz::openbmc_project::Common::Error::
231             ResourceNotFound();
232     }
233 
234     std::vector<std::string> ret;
235     for (const auto& objectPath : interfaceMap)
236     {
237         const auto& thisPath = objectPath.first;
238 
239         // Skip exact match on stripped search term
240         if (thisPath == reqPathStripped)
241         {
242             continue;
243         }
244 
245         if (thisPath.starts_with(reqPath))
246         {
247             // count the number of slashes past the stripped search term
248             int thisDepth = std::count(
249                 thisPath.begin() + reqPathStripped.size(), thisPath.end(), '/');
250             if (thisDepth <= depth)
251             {
252                 bool add = interfaces.empty();
253                 for (const auto& interfaceMap : objectPath.second)
254                 {
255                     std::vector<std::string> output(std::min(
256                         interfaces.size(), interfaceMap.second.size()));
257                     // Return iterator points at the first output elemtn,
258                     // meaning that there are no intersections.
259                     if (std::set_intersection(interfaces.begin(),
260                                               interfaces.end(),
261                                               interfaceMap.second.begin(),
262                                               interfaceMap.second.end(),
263                                               output.begin()) != output.begin())
264                     {
265                         add = true;
266                         break;
267                     }
268                 }
269                 if (add)
270                 {
271                     // TODO(ed) this is a copy
272                     ret.emplace_back(thisPath);
273                 }
274             }
275         }
276     }
277 
278     return ret;
279 }
280 
281 std::vector<InterfaceMapType::value_type>
282     getAssociatedSubTree(const InterfaceMapType& interfaceMap,
283                          const AssociationMaps& associationMaps,
284                          const sdbusplus::message::object_path& associationPath,
285                          const sdbusplus::message::object_path& reqPath,
286                          int32_t depth, std::vector<std::string>& interfaces)
287 {
288     auto findEndpoint = associationMaps.ifaces.find(associationPath.str);
289     if (findEndpoint == associationMaps.ifaces.end())
290     {
291         return {};
292     }
293     const std::vector<std::string>& association =
294         std::get<endpointsPos>(findEndpoint->second);
295     std::unordered_set<std::string> associationSet(association.begin(),
296                                                    association.end());
297     const std::vector<InterfaceMapType::value_type>& interfacePairs =
298         getSubTree(interfaceMap, reqPath, depth, interfaces);
299 
300     std::vector<InterfaceMapType::value_type> output;
301     for (const InterfaceMapType::value_type& interfacePair : interfacePairs)
302     {
303         if (associationSet.contains(interfacePair.first))
304         {
305             output.emplace_back(interfacePair);
306         }
307     }
308     return output;
309 }
310 
311 std::vector<std::string> getAssociatedSubTreePaths(
312     const InterfaceMapType& interfaceMap,
313     const AssociationMaps& associationMaps,
314     const sdbusplus::message::object_path& associationPath,
315     const sdbusplus::message::object_path& reqPath, int32_t depth,
316     std::vector<std::string>& interfaces)
317 {
318     auto findEndpoint = associationMaps.ifaces.find(associationPath.str);
319     if (findEndpoint == associationMaps.ifaces.end())
320     {
321         return {};
322     }
323     const std::vector<std::string>& association =
324         std::get<endpointsPos>(findEndpoint->second);
325     std::unordered_set<std::string> associationSet(association.begin(),
326                                                    association.end());
327     const std::vector<std::string>& paths =
328         getSubTreePaths(interfaceMap, reqPath, depth, interfaces);
329 
330     std::vector<std::string> output;
331     for (const auto& path : paths)
332     {
333         if (associationSet.contains(path))
334         {
335             output.emplace_back(path);
336         }
337     }
338     return output;
339 }