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
templateCharReplaceLoop(std::string * & strPtr,const DBusInterface & interface,std::optional<std::string> & ret,nlohmann::json & value)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
templateCharReplace(nlohmann::json & value,const DBusInterface & interface,const size_t index,const std::optional<std::string> & replaceStr)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
buildInventorySystemPath(std::string & boardName,const std::string & boardType)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