xref: /openbmc/entity-manager/src/entity_manager/utils.cpp (revision c9d4beeb372c2a81ea3bfceec35f720009adede8)
1 #include "utils.hpp"
2 
3 #include "../utils.hpp"
4 #include "../variant_visitors.hpp"
5 #include "expression.hpp"
6 
7 #include <phosphor-logging/lg2.hpp>
8 #include <sdbusplus/bus/match.hpp>
9 
10 #include <fstream>
11 #include <regex>
12 
13 const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
14 
15 namespace em_utils
16 {
17 
18 constexpr const char* templateChar = "$";
19 
20 bool fwVersionIsSame()
21 {
22     std::ifstream version(versionFile);
23     if (!version.good())
24     {
25         lg2::error("Can't read {PATH}", "PATH", versionFile);
26         return false;
27     }
28 
29     std::string versionData;
30     std::string line;
31     while (std::getline(version, line))
32     {
33         versionData += line;
34     }
35 
36     std::string expectedHash =
37         std::to_string(std::hash<std::string>{}(versionData));
38 
39     std::error_code ec;
40     std::filesystem::create_directory(configurationOutDir, ec);
41 
42     if (ec)
43     {
44         lg2::error("could not create directory {DIR}", "DIR",
45                    configurationOutDir);
46         return false;
47     }
48 
49     std::ifstream hashFile(versionHashFile);
50     if (hashFile.good())
51     {
52         std::string hashString;
53         hashFile >> hashString;
54 
55         if (expectedHash == hashString)
56         {
57             return true;
58         }
59         hashFile.close();
60     }
61 
62     std::ofstream output(versionHashFile);
63     output << expectedHash;
64     return false;
65 }
66 
67 void handleLeftOverTemplateVars(nlohmann::json& value)
68 {
69     nlohmann::json::object_t* objPtr =
70         value.get_ptr<nlohmann::json::object_t*>();
71     if (objPtr != nullptr)
72     {
73         handleLeftOverTemplateVars(*objPtr);
74         return;
75     }
76 
77     nlohmann::json::array_t* arrPtr = value.get_ptr<nlohmann::json::array_t*>();
78     if (arrPtr != nullptr)
79     {
80         handleLeftOverTemplateVars(*arrPtr);
81         return;
82     }
83 
84     std::string* strPtr = value.get_ptr<std::string*>();
85     if (strPtr == nullptr)
86     {
87         return;
88     }
89     handleLeftOverTemplateVars(*strPtr);
90 }
91 
92 void handleLeftOverTemplateVars(nlohmann::json::object_t& value)
93 {
94     for (auto& nextLayer : value)
95     {
96         handleLeftOverTemplateVars(nextLayer.second);
97     }
98 }
99 
100 void handleLeftOverTemplateVars(nlohmann::json::array_t& value)
101 {
102     for (auto& nextLayer : value)
103     {
104         handleLeftOverTemplateVars(nextLayer);
105     }
106 }
107 
108 void handleLeftOverTemplateVars(std::string& value)
109 {
110     std::string* strPtr = &value;
111     // Walking through the string to find $<templateVar>
112     while (true)
113     {
114         std::ranges::subrange<std::string::const_iterator> findStart =
115             iFindFirst(*strPtr, std::string_view(templateChar));
116 
117         if (!findStart)
118         {
119             break;
120         }
121 
122         std::ranges::subrange<std::string::iterator> searchRange(
123             strPtr->begin() + (findStart.end() - strPtr->begin()),
124             strPtr->end());
125         std::ranges::subrange<std::string::const_iterator> findSpace =
126             iFindFirst(searchRange, " ");
127 
128         std::string::const_iterator templateVarEnd;
129 
130         if (!findSpace)
131         {
132             // No space means the template var spans to the end of
133             // of the keyPair value
134             templateVarEnd = strPtr->end();
135         }
136         else
137         {
138             // A space marks the end of a template var
139             templateVarEnd = findSpace.begin();
140         }
141 
142         lg2::error(
143             "There's still template variable {VAR} un-replaced. Removing it from the string.\n",
144             "VAR", std::string(findStart.begin(), templateVarEnd));
145         strPtr->erase(findStart.begin(), templateVarEnd);
146     }
147 }
148 
149 // Replaces the template character like the other version of this function,
150 // but checks all properties on all interfaces provided to do the substitution
151 // with.
152 std::optional<std::string> templateCharReplace(
153     nlohmann::json& value, const DBusObject& object, const size_t index,
154     const std::optional<std::string>& replaceStr, bool handleLeftOver)
155 {
156     for (const auto& [_, interface] : object)
157     {
158         auto ret = templateCharReplace(value, interface, index, replaceStr);
159         if (ret)
160         {
161             if (handleLeftOver)
162             {
163                 handleLeftOverTemplateVars(value);
164             }
165             return ret;
166         }
167     }
168     if (handleLeftOver)
169     {
170         handleLeftOverTemplateVars(value);
171     }
172     return std::nullopt;
173 }
174 
175 static bool templateCharReplaceLoop(
176     std::string*& strPtr, const DBusInterface& interface,
177     std::optional<std::string>& ret, nlohmann::json& value)
178 {
179     for (const auto& [propName, propValue] : interface)
180     {
181         std::string templateName = templateChar + propName;
182         std::ranges::subrange<std::string::const_iterator> find =
183             iFindFirst(*strPtr, templateName);
184         if (!find)
185         {
186             continue;
187         }
188 
189         size_t start = find.begin() - strPtr->begin();
190 
191         // check for additional operations
192         if ((start == 0U) && find.end() == strPtr->end())
193         {
194             std::visit([&](auto&& val) { value = val; }, propValue);
195             return true;
196         }
197 
198         constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
199                                                          '/'};
200         size_t nextItemIdx = start + templateName.size() + 1;
201 
202         if (nextItemIdx > strPtr->size() ||
203             std::find(mathChars.begin(), mathChars.end(),
204                       strPtr->at(nextItemIdx)) == mathChars.end())
205         {
206             std::string val = std::visit(VariantToStringVisitor(), propValue);
207             iReplaceAll(*strPtr, templateName, val);
208             continue;
209         }
210 
211         // save the prefix
212         std::string prefix = strPtr->substr(0, start);
213 
214         // operate on the rest
215         std::string end = strPtr->substr(nextItemIdx);
216 
217         std::vector<std::string> splitResult = split(end, ' ');
218 
219         // need at least 1 operation and number
220         if (splitResult.size() < 2)
221         {
222             lg2::error("Syntax error on template replacement of {STR}", "STR",
223                        *strPtr);
224             for (const std::string& data : splitResult)
225             {
226                 lg2::error("{SPLIT} ", "SPLIT", data);
227             }
228             lg2::error("");
229             continue;
230         }
231 
232         // we assume that the replacement is a number, because we can
233         // only do math on numbers.. we might concatenate strings in the
234         // future, but thats later
235         int number = std::visit(VariantToIntVisitor(), propValue);
236         auto exprBegin = splitResult.begin();
237         auto exprEnd = splitResult.end();
238 
239         number = expression::evaluate(number, exprBegin, exprEnd);
240 
241         std::string replaced(find.begin(), find.end());
242         while (exprBegin != exprEnd)
243         {
244             replaced.append(" ").append(*exprBegin++);
245         }
246         ret = replaced;
247 
248         std::string result = prefix + std::to_string(number);
249         while (exprEnd != splitResult.end())
250         {
251             result.append(" ").append(*exprEnd++);
252         }
253         value = result;
254 
255         // We probably just invalidated the pointer abovei,
256         // reset and continue to handle multiple templates
257         strPtr = value.get_ptr<std::string*>();
258         if (strPtr == nullptr)
259         {
260             break;
261         }
262     }
263 
264     return false;
265 }
266 
267 // finds the template character (currently set to $) and replaces the value with
268 // the field found in a dbus object i.e. $ADDRESS would get populated with the
269 // ADDRESS field from a object on dbus
270 std::optional<std::string> templateCharReplace(
271     nlohmann::json& value, const DBusInterface& interface, const size_t index,
272     const std::optional<std::string>& replaceStr)
273 {
274     std::optional<std::string> ret = std::nullopt;
275 
276     nlohmann::json::object_t* objPtr =
277         value.get_ptr<nlohmann::json::object_t*>();
278     if (objPtr != nullptr)
279     {
280         for (auto& [key, value] : *objPtr)
281         {
282             templateCharReplace(value, interface, index, replaceStr);
283         }
284         return ret;
285     }
286 
287     nlohmann::json::array_t* arrPtr = value.get_ptr<nlohmann::json::array_t*>();
288     if (arrPtr != nullptr)
289     {
290         for (auto& value : *arrPtr)
291         {
292             templateCharReplace(value, interface, index, replaceStr);
293         }
294         return ret;
295     }
296 
297     std::string* strPtr = value.get_ptr<std::string*>();
298     if (strPtr == nullptr)
299     {
300         return ret;
301     }
302 
303     replaceAll(*strPtr, std::string(templateChar) + "index",
304                std::to_string(index));
305     if (replaceStr)
306     {
307         replaceAll(*strPtr, *replaceStr, std::to_string(index));
308     }
309 
310     if (templateCharReplaceLoop(strPtr, interface, ret, value))
311     {
312         return ret;
313     }
314 
315     strPtr = value.get_ptr<std::string*>();
316     if (strPtr == nullptr)
317     {
318         return ret;
319     }
320 
321     std::string_view strView = *strPtr;
322     int base = 10;
323     if (strView.starts_with("0x"))
324     {
325         strView.remove_prefix(2);
326         base = 16;
327     }
328 
329     uint64_t temp = 0;
330     bool fullMatch = false;
331     const std::from_chars_result res =
332         fromCharsWrapper(strView, temp, fullMatch, base);
333     if (res.ec == std::errc{} && fullMatch)
334     {
335         value = temp;
336     }
337 
338     return ret;
339 }
340 
341 std::string buildInventorySystemPath(std::string& boardName,
342                                      const std::string& boardType)
343 {
344     std::string path = "/xyz/openbmc_project/inventory/system/";
345     std::string boardTypeLower = toLowerCopy(boardType);
346     std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
347                        illegalDbusMemberRegex, "_");
348 
349     return std::format("{}{}/{}", path, boardTypeLower, boardName);
350 }
351 } // namespace em_utils
352