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