blob: 14f3c79a663dd36df129f1708bef228cec7f58ba [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>
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>
Christopher Meis811160e2025-08-08 08:48:37 +020016#include <regex>
17
18const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
Christopher Meis59ef1e72025-04-16 08:53:25 +020019
Alexander Hansen0ab32b32025-06-27 14:50:33 +020020namespace em_utils
21{
22
23constexpr const char* templateChar = "$";
24
Christopher Meis59ef1e72025-04-16 08:53:25 +020025bool fwVersionIsSame()
26{
27 std::ifstream version(versionFile);
28 if (!version.good())
29 {
Alexander Hansenbd85baa2025-08-06 10:46:57 +020030 lg2::error("Can't read {PATH}", "PATH", versionFile);
Christopher Meis59ef1e72025-04-16 08:53:25 +020031 return false;
32 }
33
34 std::string versionData;
35 std::string line;
36 while (std::getline(version, line))
37 {
38 versionData += line;
39 }
40
41 std::string expectedHash =
42 std::to_string(std::hash<std::string>{}(versionData));
43
Alexander Hansenbd85baa2025-08-06 10:46:57 +020044 std::error_code ec;
45 std::filesystem::create_directory(configurationOutDir, ec);
46
47 if (ec)
48 {
49 lg2::error("could not create directory {DIR}", "DIR",
50 configurationOutDir);
51 return false;
52 }
53
Christopher Meis59ef1e72025-04-16 08:53:25 +020054 std::ifstream hashFile(versionHashFile);
55 if (hashFile.good())
56 {
57 std::string hashString;
58 hashFile >> hashString;
59
60 if (expectedHash == hashString)
61 {
62 return true;
63 }
64 hashFile.close();
65 }
66
67 std::ofstream output(versionHashFile);
68 output << expectedHash;
69 return false;
70}
71
Chau Ly79629442025-08-20 10:27:20 +000072void handleLeftOverTemplateVars(nlohmann::json::iterator& keyPair)
73{
74 if (keyPair.value().type() == nlohmann::json::value_t::object ||
75 keyPair.value().type() == nlohmann::json::value_t::array)
76 {
77 for (auto nextLayer = keyPair.value().begin();
78 nextLayer != keyPair.value().end(); nextLayer++)
79 {
80 handleLeftOverTemplateVars(nextLayer);
81 }
82 return;
83 }
84
85 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
86 if (strPtr == nullptr)
87 {
88 return;
89 }
90
91 // Walking through the string to find $<templateVar>
92 while (true)
93 {
George Liuce8d1d02025-08-25 08:58:25 +080094 auto [firstIndex, lastIndex] = iFindFirst(*strPtr, templateChar);
95 if (firstIndex == std::string_view::npos)
Chau Ly79629442025-08-20 10:27:20 +000096 {
97 break;
98 }
99
George Liuce8d1d02025-08-25 08:58:25 +0800100 size_t templateVarEndIndex = 0;
101 auto [firstSpaceIndex, _] = iFindFirst(strPtr->substr(lastIndex), " ");
102 if (firstSpaceIndex == std::string_view::npos)
Chau Ly79629442025-08-20 10:27:20 +0000103 {
104 // No space means the template var spans to the end of
105 // of the keyPair value
George Liuce8d1d02025-08-25 08:58:25 +0800106 templateVarEndIndex = strPtr->size();
Chau Ly79629442025-08-20 10:27:20 +0000107 }
108 else
109 {
110 // A space marks the end of a template var
George Liuce8d1d02025-08-25 08:58:25 +0800111 templateVarEndIndex = lastIndex + firstSpaceIndex;
Chau Ly79629442025-08-20 10:27:20 +0000112 }
113
114 lg2::error(
115 "There's still template variable {VAR} un-replaced. Removing it from the string.\n",
George Liuce8d1d02025-08-25 08:58:25 +0800116 "VAR",
117 strPtr->substr(firstIndex, templateVarEndIndex - firstIndex));
118 strPtr->erase(firstIndex, templateVarEndIndex - firstIndex);
Chau Ly79629442025-08-20 10:27:20 +0000119 }
120}
121
Christopher Meis59ef1e72025-04-16 08:53:25 +0200122// Replaces the template character like the other version of this function,
123// but checks all properties on all interfaces provided to do the substitution
124// with.
125std::optional<std::string> templateCharReplace(
126 nlohmann::json::iterator& keyPair, const DBusObject& object,
127 const size_t index, const std::optional<std::string>& replaceStr)
128{
129 for (const auto& [_, interface] : object)
130 {
131 auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
132 if (ret)
133 {
Chau Ly79629442025-08-20 10:27:20 +0000134 handleLeftOverTemplateVars(keyPair);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200135 return ret;
136 }
137 }
Chau Ly79629442025-08-20 10:27:20 +0000138 handleLeftOverTemplateVars(keyPair);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200139 return std::nullopt;
140}
141
142// finds the template character (currently set to $) and replaces the value with
143// the field found in a dbus object i.e. $ADDRESS would get populated with the
144// ADDRESS field from a object on dbus
145std::optional<std::string> templateCharReplace(
146 nlohmann::json::iterator& keyPair, const DBusInterface& interface,
147 const size_t index, const std::optional<std::string>& replaceStr)
148{
149 std::optional<std::string> ret = std::nullopt;
150
151 if (keyPair.value().type() == nlohmann::json::value_t::object ||
152 keyPair.value().type() == nlohmann::json::value_t::array)
153 {
154 for (auto nextLayer = keyPair.value().begin();
155 nextLayer != keyPair.value().end(); nextLayer++)
156 {
157 templateCharReplace(nextLayer, interface, index, replaceStr);
158 }
159 return ret;
160 }
161
162 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
163 if (strPtr == nullptr)
164 {
165 return ret;
166 }
167
168 boost::replace_all(*strPtr, std::string(templateChar) + "index",
169 std::to_string(index));
170 if (replaceStr)
171 {
172 boost::replace_all(*strPtr, *replaceStr, std::to_string(index));
173 }
174
175 for (const auto& [propName, propValue] : interface)
176 {
177 std::string templateName = templateChar + propName;
George Liuce8d1d02025-08-25 08:58:25 +0800178 auto [start, endIdx] = iFindFirst(*strPtr, templateName);
179 if (start == std::string::npos)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200180 {
181 continue;
182 }
183
Christopher Meis59ef1e72025-04-16 08:53:25 +0200184 // check for additional operations
George Liuce8d1d02025-08-25 08:58:25 +0800185 if ((start == 0U) && endIdx == strPtr->size())
Christopher Meis59ef1e72025-04-16 08:53:25 +0200186 {
187 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue);
188 return ret;
189 }
190
191 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
192 '/'};
193 size_t nextItemIdx = start + templateName.size() + 1;
194
195 if (nextItemIdx > strPtr->size() ||
196 std::find(mathChars.begin(), mathChars.end(),
197 strPtr->at(nextItemIdx)) == mathChars.end())
198 {
199 std::string val = std::visit(VariantToStringVisitor(), propValue);
200 boost::ireplace_all(*strPtr, templateName, val);
201 continue;
202 }
203
204 // save the prefix
205 std::string prefix = strPtr->substr(0, start);
206
207 // operate on the rest
208 std::string end = strPtr->substr(nextItemIdx);
209
210 std::vector<std::string> split;
211 boost::split(split, end, boost::is_any_of(" "));
212
213 // need at least 1 operation and number
214 if (split.size() < 2)
215 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200216 lg2::error("Syntax error on template replacement of {STR}", "STR",
217 *strPtr);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200218 for (const std::string& data : split)
219 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200220 lg2::error("{SPLIT} ", "SPLIT", data);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200221 }
Alexander Hansen8feb0452025-09-15 14:29:20 +0200222 lg2::error("");
Christopher Meis59ef1e72025-04-16 08:53:25 +0200223 continue;
224 }
225
226 // we assume that the replacement is a number, because we can
227 // only do math on numbers.. we might concatenate strings in the
228 // future, but thats later
229 int number = std::visit(VariantToIntVisitor(), propValue);
230 auto exprBegin = split.begin();
231 auto exprEnd = split.end();
232
233 number = expression::evaluate(number, exprBegin, exprEnd);
234
George Liuce8d1d02025-08-25 08:58:25 +0800235 std::string replaced(strPtr->begin() + start, strPtr->begin() + endIdx);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200236 while (exprBegin != exprEnd)
237 {
238 replaced.append(" ").append(*exprBegin++);
239 }
240 ret = replaced;
241
242 std::string result = prefix + std::to_string(number);
243 while (exprEnd != split.end())
244 {
245 result.append(" ").append(*exprEnd++);
246 }
247 keyPair.value() = result;
248
249 // We probably just invalidated the pointer abovei,
250 // reset and continue to handle multiple templates
251 strPtr = keyPair.value().get_ptr<std::string*>();
252 if (strPtr == nullptr)
253 {
254 break;
255 }
256 }
257
258 strPtr = keyPair.value().get_ptr<std::string*>();
259 if (strPtr == nullptr)
260 {
261 return ret;
262 }
263
264 std::string_view strView = *strPtr;
265 int base = 10;
George Liu164af2f2025-08-21 17:16:31 +0800266 if (strView.starts_with("0x"))
Christopher Meis59ef1e72025-04-16 08:53:25 +0200267 {
268 strView.remove_prefix(2);
269 base = 16;
270 }
271
272 uint64_t temp = 0;
Alexander Hansen5df916f2025-09-26 10:31:36 -0400273 bool fullMatch = false;
Christopher Meis59ef1e72025-04-16 08:53:25 +0200274 const std::from_chars_result res =
Alexander Hansen5df916f2025-09-26 10:31:36 -0400275 fromCharsWrapper(strView, temp, fullMatch, base);
276 if (res.ec == std::errc{} && fullMatch)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200277 {
278 keyPair.value() = temp;
279 }
280
281 return ret;
282}
283
Christopher Meis811160e2025-08-08 08:48:37 +0200284std::string buildInventorySystemPath(std::string& boardName,
285 const std::string& boardType)
286{
287 std::string path = "/xyz/openbmc_project/inventory/system/";
288 std::string boardTypeLower = boost::algorithm::to_lower_copy(boardType);
289
290 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
291 illegalDbusMemberRegex, "_");
292
293 return std::format("{}{}/{}", path, boardTypeLower, boardName);
294}
Christopher Meis59ef1e72025-04-16 08:53:25 +0200295} // namespace em_utils