xref: /openbmc/entity-manager/src/entity_manager/utils.cpp (revision 83c8e49400667e88453e25636128a8a61b878b81)
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 
fwVersionIsSame()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 
handleLeftOverTemplateVars(nlohmann::json & value)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 
handleLeftOverTemplateVars(nlohmann::json::object_t & value)92 void handleLeftOverTemplateVars(nlohmann::json::object_t& value)
93 {
94     for (auto& nextLayer : value)
95     {
96         handleLeftOverTemplateVars(nextLayer.second);
97     }
98 }
99 
handleLeftOverTemplateVars(nlohmann::json::array_t & value)100 void handleLeftOverTemplateVars(nlohmann::json::array_t& value)
101 {
102     for (auto& nextLayer : value)
103     {
104         handleLeftOverTemplateVars(nextLayer);
105     }
106 }
107 
handleLeftOverTemplateVars(std::string & value)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.
templateCharReplace(nlohmann::json & value,const DBusObject & object,const size_t index,const std::optional<std::string> & replaceStr,bool handleLeftOver)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 // finds the template character (currently set to $) and replaces the value with
176 // the field found in a dbus object i.e. $ADDRESS would get populated with the
177 // ADDRESS field from a object on dbus
templateCharReplace(nlohmann::json & value,const DBusInterface & interface,const size_t index,const std::optional<std::string> & replaceStr)178 std::optional<std::string> templateCharReplace(
179     nlohmann::json& value, const DBusInterface& interface, const size_t index,
180     const std::optional<std::string>& replaceStr)
181 {
182     std::optional<std::string> ret = std::nullopt;
183 
184     nlohmann::json::object_t* objPtr =
185         value.get_ptr<nlohmann::json::object_t*>();
186     if (objPtr != nullptr)
187     {
188         for (auto& [key, value] : *objPtr)
189         {
190             templateCharReplace(value, interface, index, replaceStr);
191         }
192         return ret;
193     }
194 
195     nlohmann::json::array_t* arrPtr = value.get_ptr<nlohmann::json::array_t*>();
196     if (arrPtr != nullptr)
197     {
198         for (auto& value : *arrPtr)
199         {
200             templateCharReplace(value, interface, index, replaceStr);
201         }
202         return ret;
203     }
204 
205     std::string* strPtr = value.get_ptr<std::string*>();
206     if (strPtr == nullptr)
207     {
208         return ret;
209     }
210 
211     replaceAll(*strPtr, std::string(templateChar) + "index",
212                std::to_string(index));
213     if (replaceStr)
214     {
215         replaceAll(*strPtr, *replaceStr, std::to_string(index));
216     }
217 
218     for (const auto& [propName, propValue] : interface)
219     {
220         std::string templateName = templateChar + propName;
221         std::ranges::subrange<std::string::const_iterator> find =
222             iFindFirst(*strPtr, templateName);
223         if (!find)
224         {
225             continue;
226         }
227 
228         size_t start = find.begin() - strPtr->begin();
229 
230         // check for additional operations
231         if ((start == 0U) && find.end() == strPtr->end())
232         {
233             std::visit([&](auto&& val) { value = val; }, propValue);
234             return ret;
235         }
236 
237         constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
238                                                          '/'};
239         size_t nextItemIdx = start + templateName.size() + 1;
240 
241         if (nextItemIdx > strPtr->size() ||
242             std::find(mathChars.begin(), mathChars.end(),
243                       strPtr->at(nextItemIdx)) == mathChars.end())
244         {
245             std::string val = std::visit(VariantToStringVisitor(), propValue);
246             iReplaceAll(*strPtr, templateName, val);
247             continue;
248         }
249 
250         // save the prefix
251         std::string prefix = strPtr->substr(0, start);
252 
253         // operate on the rest
254         std::string end = strPtr->substr(nextItemIdx);
255 
256         std::vector<std::string> splitResult = split(end, ' ');
257 
258         // need at least 1 operation and number
259         if (splitResult.size() < 2)
260         {
261             lg2::error("Syntax error on template replacement of {STR}", "STR",
262                        *strPtr);
263             for (const std::string& data : splitResult)
264             {
265                 lg2::error("{SPLIT} ", "SPLIT", data);
266             }
267             lg2::error("");
268             continue;
269         }
270 
271         // we assume that the replacement is a number, because we can
272         // only do math on numbers.. we might concatenate strings in the
273         // future, but thats later
274         int number = std::visit(VariantToIntVisitor(), propValue);
275         auto exprBegin = splitResult.begin();
276         auto exprEnd = splitResult.end();
277 
278         number = expression::evaluate(number, exprBegin, exprEnd);
279 
280         std::string replaced(find.begin(), find.end());
281         while (exprBegin != exprEnd)
282         {
283             replaced.append(" ").append(*exprBegin++);
284         }
285         ret = replaced;
286 
287         std::string result = prefix + std::to_string(number);
288         while (exprEnd != splitResult.end())
289         {
290             result.append(" ").append(*exprEnd++);
291         }
292         value = result;
293 
294         // We probably just invalidated the pointer abovei,
295         // reset and continue to handle multiple templates
296         strPtr = value.get_ptr<std::string*>();
297         if (strPtr == nullptr)
298         {
299             break;
300         }
301     }
302 
303     strPtr = value.get_ptr<std::string*>();
304     if (strPtr == nullptr)
305     {
306         return ret;
307     }
308 
309     std::string_view strView = *strPtr;
310     int base = 10;
311     if (strView.starts_with("0x"))
312     {
313         strView.remove_prefix(2);
314         base = 16;
315     }
316 
317     uint64_t temp = 0;
318     bool fullMatch = false;
319     const std::from_chars_result res =
320         fromCharsWrapper(strView, temp, fullMatch, base);
321     if (res.ec == std::errc{} && fullMatch)
322     {
323         value = temp;
324     }
325 
326     return ret;
327 }
328 
buildInventorySystemPath(std::string & boardName,const std::string & boardType)329 std::string buildInventorySystemPath(std::string& boardName,
330                                      const std::string& boardType)
331 {
332     std::string path = "/xyz/openbmc_project/inventory/system/";
333     std::string boardTypeLower = toLowerCopy(boardType);
334     std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
335                        illegalDbusMemberRegex, "_");
336 
337     return std::format("{}{}/{}", path, boardTypeLower, boardName);
338 }
339 } // namespace em_utils
340