blob: 75281178015f9d39770814a1d8c8b009c67ee8bb [file] [log] [blame]
Christopher Meis59ef1e72025-04-16 08:53:25 +02001#include "utils.hpp"
2
3#include "../variant_visitors.hpp"
4#include "expression.hpp"
Alexander Hansenbd85baa2025-08-06 10:46:57 +02005#include "phosphor-logging/lg2.hpp"
Christopher Meis59ef1e72025-04-16 08:53:25 +02006
Christopher Meis811160e2025-08-08 08:48:37 +02007#include <boost/algorithm/string/case_conv.hpp>
Christopher Meis59ef1e72025-04-16 08:53:25 +02008#include <boost/algorithm/string/classification.hpp>
9#include <boost/algorithm/string/find.hpp>
Christopher Meis59ef1e72025-04-16 08:53:25 +020010#include <boost/algorithm/string/replace.hpp>
11#include <boost/algorithm/string/split.hpp>
Chau Ly79629442025-08-20 10:27:20 +000012#include <phosphor-logging/lg2.hpp>
Christopher Meis59ef1e72025-04-16 08:53:25 +020013#include <sdbusplus/bus/match.hpp>
14
15#include <fstream>
16#include <iostream>
Christopher Meis811160e2025-08-08 08:48:37 +020017#include <regex>
18
19const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
Christopher Meis59ef1e72025-04-16 08:53:25 +020020
Alexander Hansen0ab32b32025-06-27 14:50:33 +020021namespace em_utils
22{
23
24constexpr const char* templateChar = "$";
25
Christopher Meis59ef1e72025-04-16 08:53:25 +020026bool fwVersionIsSame()
27{
28 std::ifstream version(versionFile);
29 if (!version.good())
30 {
Alexander Hansenbd85baa2025-08-06 10:46:57 +020031 lg2::error("Can't read {PATH}", "PATH", versionFile);
Christopher Meis59ef1e72025-04-16 08:53:25 +020032 return false;
33 }
34
35 std::string versionData;
36 std::string line;
37 while (std::getline(version, line))
38 {
39 versionData += line;
40 }
41
42 std::string expectedHash =
43 std::to_string(std::hash<std::string>{}(versionData));
44
Alexander Hansenbd85baa2025-08-06 10:46:57 +020045 std::error_code ec;
46 std::filesystem::create_directory(configurationOutDir, ec);
47
48 if (ec)
49 {
50 lg2::error("could not create directory {DIR}", "DIR",
51 configurationOutDir);
52 return false;
53 }
54
Christopher Meis59ef1e72025-04-16 08:53:25 +020055 std::ifstream hashFile(versionHashFile);
56 if (hashFile.good())
57 {
58 std::string hashString;
59 hashFile >> hashString;
60
61 if (expectedHash == hashString)
62 {
63 return true;
64 }
65 hashFile.close();
66 }
67
68 std::ofstream output(versionHashFile);
69 output << expectedHash;
70 return false;
71}
72
Chau Ly79629442025-08-20 10:27:20 +000073void handleLeftOverTemplateVars(nlohmann::json::iterator& keyPair)
74{
75 if (keyPair.value().type() == nlohmann::json::value_t::object ||
76 keyPair.value().type() == nlohmann::json::value_t::array)
77 {
78 for (auto nextLayer = keyPair.value().begin();
79 nextLayer != keyPair.value().end(); nextLayer++)
80 {
81 handleLeftOverTemplateVars(nextLayer);
82 }
83 return;
84 }
85
86 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
87 if (strPtr == nullptr)
88 {
89 return;
90 }
91
92 // Walking through the string to find $<templateVar>
93 while (true)
94 {
95 boost::iterator_range<std::string::const_iterator> findStart =
96 boost::ifind_first(*strPtr, std::string_view(templateChar));
97
98 if (!findStart)
99 {
100 break;
101 }
102
103 boost::iterator_range<std::string::iterator> searchRange(
104 strPtr->begin() + (findStart.end() - strPtr->begin()),
105 strPtr->end());
106 boost::iterator_range<std::string::const_iterator> findSpace =
107 boost::ifind_first(searchRange, " ");
108
109 std::string::const_iterator templateVarEnd;
110
111 if (!findSpace)
112 {
113 // No space means the template var spans to the end of
114 // of the keyPair value
115 templateVarEnd = strPtr->end();
116 }
117 else
118 {
119 // A space marks the end of a template var
120 templateVarEnd = findSpace.begin();
121 }
122
123 lg2::error(
124 "There's still template variable {VAR} un-replaced. Removing it from the string.\n",
125 "VAR", std::string(findStart.begin(), templateVarEnd));
126 strPtr->erase(findStart.begin(), templateVarEnd);
127 }
128}
129
Christopher Meis59ef1e72025-04-16 08:53:25 +0200130// Replaces the template character like the other version of this function,
131// but checks all properties on all interfaces provided to do the substitution
132// with.
133std::optional<std::string> templateCharReplace(
134 nlohmann::json::iterator& keyPair, const DBusObject& object,
135 const size_t index, const std::optional<std::string>& replaceStr)
136{
137 for (const auto& [_, interface] : object)
138 {
139 auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
140 if (ret)
141 {
Chau Ly79629442025-08-20 10:27:20 +0000142 handleLeftOverTemplateVars(keyPair);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200143 return ret;
144 }
145 }
Chau Ly79629442025-08-20 10:27:20 +0000146 handleLeftOverTemplateVars(keyPair);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200147 return std::nullopt;
148}
149
150// finds the template character (currently set to $) and replaces the value with
151// the field found in a dbus object i.e. $ADDRESS would get populated with the
152// ADDRESS field from a object on dbus
153std::optional<std::string> templateCharReplace(
154 nlohmann::json::iterator& keyPair, const DBusInterface& interface,
155 const size_t index, const std::optional<std::string>& replaceStr)
156{
157 std::optional<std::string> ret = std::nullopt;
158
159 if (keyPair.value().type() == nlohmann::json::value_t::object ||
160 keyPair.value().type() == nlohmann::json::value_t::array)
161 {
162 for (auto nextLayer = keyPair.value().begin();
163 nextLayer != keyPair.value().end(); nextLayer++)
164 {
165 templateCharReplace(nextLayer, interface, index, replaceStr);
166 }
167 return ret;
168 }
169
170 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
171 if (strPtr == nullptr)
172 {
173 return ret;
174 }
175
176 boost::replace_all(*strPtr, std::string(templateChar) + "index",
177 std::to_string(index));
178 if (replaceStr)
179 {
180 boost::replace_all(*strPtr, *replaceStr, std::to_string(index));
181 }
182
183 for (const auto& [propName, propValue] : interface)
184 {
185 std::string templateName = templateChar + propName;
186 boost::iterator_range<std::string::const_iterator> find =
187 boost::ifind_first(*strPtr, templateName);
188 if (!find)
189 {
190 continue;
191 }
192
193 size_t start = find.begin() - strPtr->begin();
194
195 // check for additional operations
196 if ((start == 0U) && find.end() == strPtr->end())
197 {
198 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue);
199 return ret;
200 }
201
202 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
203 '/'};
204 size_t nextItemIdx = start + templateName.size() + 1;
205
206 if (nextItemIdx > strPtr->size() ||
207 std::find(mathChars.begin(), mathChars.end(),
208 strPtr->at(nextItemIdx)) == mathChars.end())
209 {
210 std::string val = std::visit(VariantToStringVisitor(), propValue);
211 boost::ireplace_all(*strPtr, templateName, val);
212 continue;
213 }
214
215 // save the prefix
216 std::string prefix = strPtr->substr(0, start);
217
218 // operate on the rest
219 std::string end = strPtr->substr(nextItemIdx);
220
221 std::vector<std::string> split;
222 boost::split(split, end, boost::is_any_of(" "));
223
224 // need at least 1 operation and number
225 if (split.size() < 2)
226 {
227 std::cerr << "Syntax error on template replacement of " << *strPtr
228 << "\n";
229 for (const std::string& data : split)
230 {
231 std::cerr << data << " ";
232 }
233 std::cerr << "\n";
234 continue;
235 }
236
237 // we assume that the replacement is a number, because we can
238 // only do math on numbers.. we might concatenate strings in the
239 // future, but thats later
240 int number = std::visit(VariantToIntVisitor(), propValue);
241 auto exprBegin = split.begin();
242 auto exprEnd = split.end();
243
244 number = expression::evaluate(number, exprBegin, exprEnd);
245
246 std::string replaced(find.begin(), find.end());
247 while (exprBegin != exprEnd)
248 {
249 replaced.append(" ").append(*exprBegin++);
250 }
251 ret = replaced;
252
253 std::string result = prefix + std::to_string(number);
254 while (exprEnd != split.end())
255 {
256 result.append(" ").append(*exprEnd++);
257 }
258 keyPair.value() = result;
259
260 // We probably just invalidated the pointer abovei,
261 // reset and continue to handle multiple templates
262 strPtr = keyPair.value().get_ptr<std::string*>();
263 if (strPtr == nullptr)
264 {
265 break;
266 }
267 }
268
269 strPtr = keyPair.value().get_ptr<std::string*>();
270 if (strPtr == nullptr)
271 {
272 return ret;
273 }
274
275 std::string_view strView = *strPtr;
276 int base = 10;
George Liu164af2f2025-08-21 17:16:31 +0800277 if (strView.starts_with("0x"))
Christopher Meis59ef1e72025-04-16 08:53:25 +0200278 {
279 strView.remove_prefix(2);
280 base = 16;
281 }
282
283 uint64_t temp = 0;
284 const char* strDataEndPtr = strView.data() + strView.size();
285 const std::from_chars_result res =
286 std::from_chars(strView.data(), strDataEndPtr, temp, base);
287 if (res.ec == std::errc{} && res.ptr == strDataEndPtr)
288 {
289 keyPair.value() = temp;
290 }
291
292 return ret;
293}
294
Christopher Meis811160e2025-08-08 08:48:37 +0200295std::string buildInventorySystemPath(std::string& boardName,
296 const std::string& boardType)
297{
298 std::string path = "/xyz/openbmc_project/inventory/system/";
299 std::string boardTypeLower = boost::algorithm::to_lower_copy(boardType);
300
301 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
302 illegalDbusMemberRegex, "_");
303
304 return std::format("{}{}/{}", path, boardTypeLower, boardName);
305}
Christopher Meis59ef1e72025-04-16 08:53:25 +0200306} // namespace em_utils