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