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