xref: /openbmc/phosphor-objmgr/src/handler.cpp (revision 3e2eb6af)
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(
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 
39 std::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 
96 ConnectionNames 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 
138 std::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 
206 std::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 }
279 
280 std::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 
310 std::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 }
339