xref: /openbmc/entity-manager/src/entity_manager/utils.cpp (revision 7719269f0d269b1bf206545fcc645e86c9c42e90)
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         for (auto& nextLayer : *objPtr)
74         {
75             handleLeftOverTemplateVars(nextLayer.second);
76         }
77         return;
78     }
79 
80     nlohmann::json::array_t* arrPtr = value.get_ptr<nlohmann::json::array_t*>();
81     if (arrPtr != nullptr)
82     {
83         for (auto& nextLayer : *arrPtr)
84         {
85             handleLeftOverTemplateVars(nextLayer);
86         }
87         return;
88     }
89 
90     std::string* strPtr = value.get_ptr<std::string*>();
91     if (strPtr == nullptr)
92     {
93         return;
94     }
95 
96     // Walking through the string to find $<templateVar>
97     while (true)
98     {
99         std::ranges::subrange<std::string::const_iterator> findStart =
100             iFindFirst(*strPtr, std::string_view(templateChar));
101 
102         if (!findStart)
103         {
104             break;
105         }
106 
107         std::ranges::subrange<std::string::iterator> searchRange(
108             strPtr->begin() + (findStart.end() - strPtr->begin()),
109             strPtr->end());
110         std::ranges::subrange<std::string::const_iterator> findSpace =
111             iFindFirst(searchRange, " ");
112 
113         std::string::const_iterator templateVarEnd;
114 
115         if (!findSpace)
116         {
117             // No space means the template var spans to the end of
118             // of the keyPair value
119             templateVarEnd = strPtr->end();
120         }
121         else
122         {
123             // A space marks the end of a template var
124             templateVarEnd = findSpace.begin();
125         }
126 
127         lg2::error(
128             "There's still template variable {VAR} un-replaced. Removing it from the string.\n",
129             "VAR", std::string(findStart.begin(), templateVarEnd));
130         strPtr->erase(findStart.begin(), templateVarEnd);
131     }
132 }
133 
134 // Replaces the template character like the other version of this function,
135 // but checks all properties on all interfaces provided to do the substitution
136 // with.
templateCharReplace(nlohmann::json & value,const DBusObject & object,const size_t index,const std::optional<std::string> & replaceStr,bool handleLeftOver)137 std::optional<std::string> templateCharReplace(
138     nlohmann::json& value, const DBusObject& object, const size_t index,
139     const std::optional<std::string>& replaceStr, bool handleLeftOver)
140 {
141     for (const auto& [_, interface] : object)
142     {
143         auto ret = templateCharReplace(value, interface, index, replaceStr);
144         if (ret)
145         {
146             if (handleLeftOver)
147             {
148                 handleLeftOverTemplateVars(value);
149             }
150             return ret;
151         }
152     }
153     if (handleLeftOver)
154     {
155         handleLeftOverTemplateVars(value);
156     }
157     return std::nullopt;
158 }
159 
160 // finds the template character (currently set to $) and replaces the value with
161 // the field found in a dbus object i.e. $ADDRESS would get populated with the
162 // ADDRESS field from a object on dbus
templateCharReplace(nlohmann::json & value,const DBusInterface & interface,const size_t index,const std::optional<std::string> & replaceStr)163 std::optional<std::string> templateCharReplace(
164     nlohmann::json& value, const DBusInterface& interface, const size_t index,
165     const std::optional<std::string>& replaceStr)
166 {
167     std::optional<std::string> ret = std::nullopt;
168 
169     nlohmann::json::object_t* objPtr =
170         value.get_ptr<nlohmann::json::object_t*>();
171     if (objPtr != nullptr)
172     {
173         for (auto& [key, value] : *objPtr)
174         {
175             templateCharReplace(value, interface, index, replaceStr);
176         }
177         return ret;
178     }
179 
180     nlohmann::json::array_t* arrPtr = value.get_ptr<nlohmann::json::array_t*>();
181     if (arrPtr != nullptr)
182     {
183         for (auto& value : *arrPtr)
184         {
185             templateCharReplace(value, interface, index, replaceStr);
186         }
187         return ret;
188     }
189 
190     std::string* strPtr = value.get_ptr<std::string*>();
191     if (strPtr == nullptr)
192     {
193         return ret;
194     }
195 
196     replaceAll(*strPtr, std::string(templateChar) + "index",
197                std::to_string(index));
198     if (replaceStr)
199     {
200         replaceAll(*strPtr, *replaceStr, std::to_string(index));
201     }
202 
203     for (const auto& [propName, propValue] : interface)
204     {
205         std::string templateName = templateChar + propName;
206         std::ranges::subrange<std::string::const_iterator> find =
207             iFindFirst(*strPtr, templateName);
208         if (!find)
209         {
210             continue;
211         }
212 
213         size_t start = find.begin() - strPtr->begin();
214 
215         // check for additional operations
216         if ((start == 0U) && find.end() == strPtr->end())
217         {
218             std::visit([&](auto&& val) { value = val; }, propValue);
219             return ret;
220         }
221 
222         constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
223                                                          '/'};
224         size_t nextItemIdx = start + templateName.size() + 1;
225 
226         if (nextItemIdx > strPtr->size() ||
227             std::find(mathChars.begin(), mathChars.end(),
228                       strPtr->at(nextItemIdx)) == mathChars.end())
229         {
230             std::string val = std::visit(VariantToStringVisitor(), propValue);
231             iReplaceAll(*strPtr, templateName, val);
232             continue;
233         }
234 
235         // save the prefix
236         std::string prefix = strPtr->substr(0, start);
237 
238         // operate on the rest
239         std::string end = strPtr->substr(nextItemIdx);
240 
241         std::vector<std::string> splitResult = split(end, ' ');
242 
243         // need at least 1 operation and number
244         if (splitResult.size() < 2)
245         {
246             lg2::error("Syntax error on template replacement of {STR}", "STR",
247                        *strPtr);
248             for (const std::string& data : splitResult)
249             {
250                 lg2::error("{SPLIT} ", "SPLIT", data);
251             }
252             lg2::error("");
253             continue;
254         }
255 
256         // we assume that the replacement is a number, because we can
257         // only do math on numbers.. we might concatenate strings in the
258         // future, but thats later
259         int number = std::visit(VariantToIntVisitor(), propValue);
260         auto exprBegin = splitResult.begin();
261         auto exprEnd = splitResult.end();
262 
263         number = expression::evaluate(number, exprBegin, exprEnd);
264 
265         std::string replaced(find.begin(), find.end());
266         while (exprBegin != exprEnd)
267         {
268             replaced.append(" ").append(*exprBegin++);
269         }
270         ret = replaced;
271 
272         std::string result = prefix + std::to_string(number);
273         while (exprEnd != splitResult.end())
274         {
275             result.append(" ").append(*exprEnd++);
276         }
277         value = result;
278 
279         // We probably just invalidated the pointer abovei,
280         // reset and continue to handle multiple templates
281         strPtr = value.get_ptr<std::string*>();
282         if (strPtr == nullptr)
283         {
284             break;
285         }
286     }
287 
288     strPtr = value.get_ptr<std::string*>();
289     if (strPtr == nullptr)
290     {
291         return ret;
292     }
293 
294     std::string_view strView = *strPtr;
295     int base = 10;
296     if (strView.starts_with("0x"))
297     {
298         strView.remove_prefix(2);
299         base = 16;
300     }
301 
302     uint64_t temp = 0;
303     bool fullMatch = false;
304     const std::from_chars_result res =
305         fromCharsWrapper(strView, temp, fullMatch, base);
306     if (res.ec == std::errc{} && fullMatch)
307     {
308         value = temp;
309     }
310 
311     return ret;
312 }
313 
buildInventorySystemPath(std::string & boardName,const std::string & boardType)314 std::string buildInventorySystemPath(std::string& boardName,
315                                      const std::string& boardType)
316 {
317     std::string path = "/xyz/openbmc_project/inventory/system/";
318     std::string boardTypeLower = toLowerCopy(boardType);
319     std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
320                        illegalDbusMemberRegex, "_");
321 
322     return std::format("{}{}/{}", path, boardTypeLower, boardName);
323 }
324 } // namespace em_utils
325