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