blob: 6f51ec8196fa53631daa8337b5ed1da798f58cc9 [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/replace.hpp>
Chau Ly79629442025-08-20 10:27:20 +000010#include <phosphor-logging/lg2.hpp>
Christopher Meis59ef1e72025-04-16 08:53:25 +020011#include <sdbusplus/bus/match.hpp>
12
13#include <fstream>
Christopher Meis811160e2025-08-08 08:48:37 +020014#include <regex>
15
16const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
Christopher Meis59ef1e72025-04-16 08:53:25 +020017
Alexander Hansen0ab32b32025-06-27 14:50:33 +020018namespace em_utils
19{
20
21constexpr const char* templateChar = "$";
22
Christopher Meis59ef1e72025-04-16 08:53:25 +020023bool fwVersionIsSame()
24{
25 std::ifstream version(versionFile);
26 if (!version.good())
27 {
Alexander Hansenbd85baa2025-08-06 10:46:57 +020028 lg2::error("Can't read {PATH}", "PATH", versionFile);
Christopher Meis59ef1e72025-04-16 08:53:25 +020029 return false;
30 }
31
32 std::string versionData;
33 std::string line;
34 while (std::getline(version, line))
35 {
36 versionData += line;
37 }
38
39 std::string expectedHash =
40 std::to_string(std::hash<std::string>{}(versionData));
41
Alexander Hansenbd85baa2025-08-06 10:46:57 +020042 std::error_code ec;
43 std::filesystem::create_directory(configurationOutDir, ec);
44
45 if (ec)
46 {
47 lg2::error("could not create directory {DIR}", "DIR",
48 configurationOutDir);
49 return false;
50 }
51
Christopher Meis59ef1e72025-04-16 08:53:25 +020052 std::ifstream hashFile(versionHashFile);
53 if (hashFile.good())
54 {
55 std::string hashString;
56 hashFile >> hashString;
57
58 if (expectedHash == hashString)
59 {
60 return true;
61 }
62 hashFile.close();
63 }
64
65 std::ofstream output(versionHashFile);
66 output << expectedHash;
67 return false;
68}
69
Chau Ly79629442025-08-20 10:27:20 +000070void handleLeftOverTemplateVars(nlohmann::json::iterator& keyPair)
71{
72 if (keyPair.value().type() == nlohmann::json::value_t::object ||
73 keyPair.value().type() == nlohmann::json::value_t::array)
74 {
75 for (auto nextLayer = keyPair.value().begin();
76 nextLayer != keyPair.value().end(); nextLayer++)
77 {
78 handleLeftOverTemplateVars(nextLayer);
79 }
80 return;
81 }
82
83 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
84 if (strPtr == nullptr)
85 {
86 return;
87 }
88
89 // Walking through the string to find $<templateVar>
90 while (true)
91 {
Ed Tanousc5a2af92025-08-25 08:58:25 +080092 std::ranges::subrange<std::string::const_iterator> findStart =
93 iFindFirst(*strPtr, std::string_view(templateChar));
94
95 if (!findStart)
Chau Ly79629442025-08-20 10:27:20 +000096 {
97 break;
98 }
99
Ed Tanousc5a2af92025-08-25 08:58:25 +0800100 std::ranges::subrange<std::string::iterator> searchRange(
101 strPtr->begin() + (findStart.end() - strPtr->begin()),
102 strPtr->end());
103 std::ranges::subrange<std::string::const_iterator> findSpace =
104 iFindFirst(searchRange, " ");
105
106 std::string::const_iterator templateVarEnd;
107
108 if (!findSpace)
Chau Ly79629442025-08-20 10:27:20 +0000109 {
110 // No space means the template var spans to the end of
111 // of the keyPair value
Ed Tanousc5a2af92025-08-25 08:58:25 +0800112 templateVarEnd = strPtr->end();
Chau Ly79629442025-08-20 10:27:20 +0000113 }
114 else
115 {
116 // A space marks the end of a template var
Ed Tanousc5a2af92025-08-25 08:58:25 +0800117 templateVarEnd = findSpace.begin();
Chau Ly79629442025-08-20 10:27:20 +0000118 }
119
120 lg2::error(
121 "There's still template variable {VAR} un-replaced. Removing it from the string.\n",
Ed Tanousc5a2af92025-08-25 08:58:25 +0800122 "VAR", std::string(findStart.begin(), templateVarEnd));
123 strPtr->erase(findStart.begin(), templateVarEnd);
Chau Ly79629442025-08-20 10:27:20 +0000124 }
125}
126
Christopher Meis59ef1e72025-04-16 08:53:25 +0200127// Replaces the template character like the other version of this function,
128// but checks all properties on all interfaces provided to do the substitution
129// with.
130std::optional<std::string> templateCharReplace(
131 nlohmann::json::iterator& keyPair, const DBusObject& object,
132 const size_t index, const std::optional<std::string>& replaceStr)
133{
134 for (const auto& [_, interface] : object)
135 {
136 auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
137 if (ret)
138 {
Chau Ly79629442025-08-20 10:27:20 +0000139 handleLeftOverTemplateVars(keyPair);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200140 return ret;
141 }
142 }
Chau Ly79629442025-08-20 10:27:20 +0000143 handleLeftOverTemplateVars(keyPair);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200144 return std::nullopt;
145}
146
147// finds the template character (currently set to $) and replaces the value with
148// the field found in a dbus object i.e. $ADDRESS would get populated with the
149// ADDRESS field from a object on dbus
150std::optional<std::string> templateCharReplace(
151 nlohmann::json::iterator& keyPair, const DBusInterface& interface,
152 const size_t index, const std::optional<std::string>& replaceStr)
153{
154 std::optional<std::string> ret = std::nullopt;
155
156 if (keyPair.value().type() == nlohmann::json::value_t::object ||
157 keyPair.value().type() == nlohmann::json::value_t::array)
158 {
159 for (auto nextLayer = keyPair.value().begin();
160 nextLayer != keyPair.value().end(); nextLayer++)
161 {
162 templateCharReplace(nextLayer, interface, index, replaceStr);
163 }
164 return ret;
165 }
166
167 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
168 if (strPtr == nullptr)
169 {
170 return ret;
171 }
172
173 boost::replace_all(*strPtr, std::string(templateChar) + "index",
174 std::to_string(index));
175 if (replaceStr)
176 {
177 boost::replace_all(*strPtr, *replaceStr, std::to_string(index));
178 }
179
180 for (const auto& [propName, propValue] : interface)
181 {
182 std::string templateName = templateChar + propName;
Ed Tanousc5a2af92025-08-25 08:58:25 +0800183 std::ranges::subrange<std::string::const_iterator> find =
184 iFindFirst(*strPtr, templateName);
185 if (!find)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200186 {
187 continue;
188 }
189
Ed Tanousc5a2af92025-08-25 08:58:25 +0800190 size_t start = find.begin() - strPtr->begin();
191
Christopher Meis59ef1e72025-04-16 08:53:25 +0200192 // check for additional operations
Ed Tanousc5a2af92025-08-25 08:58:25 +0800193 if ((start == 0U) && find.end() == strPtr->end())
Christopher Meis59ef1e72025-04-16 08:53:25 +0200194 {
195 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue);
196 return ret;
197 }
198
199 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
200 '/'};
201 size_t nextItemIdx = start + templateName.size() + 1;
202
203 if (nextItemIdx > strPtr->size() ||
204 std::find(mathChars.begin(), mathChars.end(),
205 strPtr->at(nextItemIdx)) == mathChars.end())
206 {
207 std::string val = std::visit(VariantToStringVisitor(), propValue);
208 boost::ireplace_all(*strPtr, templateName, val);
209 continue;
210 }
211
212 // save the prefix
213 std::string prefix = strPtr->substr(0, start);
214
215 // operate on the rest
216 std::string end = strPtr->substr(nextItemIdx);
217
George Liuecf1a312025-08-25 10:43:12 +0800218 std::vector<std::string> splitResult = split(end, ' ');
Christopher Meis59ef1e72025-04-16 08:53:25 +0200219
220 // need at least 1 operation and number
George Liuecf1a312025-08-25 10:43:12 +0800221 if (splitResult.size() < 2)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200222 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200223 lg2::error("Syntax error on template replacement of {STR}", "STR",
224 *strPtr);
George Liuecf1a312025-08-25 10:43:12 +0800225 for (const std::string& data : splitResult)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200226 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200227 lg2::error("{SPLIT} ", "SPLIT", data);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200228 }
Alexander Hansen8feb0452025-09-15 14:29:20 +0200229 lg2::error("");
Christopher Meis59ef1e72025-04-16 08:53:25 +0200230 continue;
231 }
232
233 // we assume that the replacement is a number, because we can
234 // only do math on numbers.. we might concatenate strings in the
235 // future, but thats later
236 int number = std::visit(VariantToIntVisitor(), propValue);
George Liuecf1a312025-08-25 10:43:12 +0800237 auto exprBegin = splitResult.begin();
238 auto exprEnd = splitResult.end();
Christopher Meis59ef1e72025-04-16 08:53:25 +0200239
240 number = expression::evaluate(number, exprBegin, exprEnd);
241
Ed Tanousc5a2af92025-08-25 08:58:25 +0800242 std::string replaced(find.begin(), find.end());
Christopher Meis59ef1e72025-04-16 08:53:25 +0200243 while (exprBegin != exprEnd)
244 {
245 replaced.append(" ").append(*exprBegin++);
246 }
247 ret = replaced;
248
249 std::string result = prefix + std::to_string(number);
George Liuecf1a312025-08-25 10:43:12 +0800250 while (exprEnd != splitResult.end())
Christopher Meis59ef1e72025-04-16 08:53:25 +0200251 {
252 result.append(" ").append(*exprEnd++);
253 }
254 keyPair.value() = result;
255
256 // We probably just invalidated the pointer abovei,
257 // reset and continue to handle multiple templates
258 strPtr = keyPair.value().get_ptr<std::string*>();
259 if (strPtr == nullptr)
260 {
261 break;
262 }
263 }
264
265 strPtr = keyPair.value().get_ptr<std::string*>();
266 if (strPtr == nullptr)
267 {
268 return ret;
269 }
270
271 std::string_view strView = *strPtr;
272 int base = 10;
George Liu164af2f2025-08-21 17:16:31 +0800273 if (strView.starts_with("0x"))
Christopher Meis59ef1e72025-04-16 08:53:25 +0200274 {
275 strView.remove_prefix(2);
276 base = 16;
277 }
278
279 uint64_t temp = 0;
Alexander Hansen5df916f2025-09-26 10:31:36 -0400280 bool fullMatch = false;
Christopher Meis59ef1e72025-04-16 08:53:25 +0200281 const std::from_chars_result res =
Alexander Hansen5df916f2025-09-26 10:31:36 -0400282 fromCharsWrapper(strView, temp, fullMatch, base);
283 if (res.ec == std::errc{} && fullMatch)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200284 {
285 keyPair.value() = temp;
286 }
287
288 return ret;
289}
290
Christopher Meis811160e2025-08-08 08:48:37 +0200291std::string buildInventorySystemPath(std::string& boardName,
292 const std::string& boardType)
293{
294 std::string path = "/xyz/openbmc_project/inventory/system/";
295 std::string boardTypeLower = boost::algorithm::to_lower_copy(boardType);
296
297 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
298 illegalDbusMemberRegex, "_");
299
300 return std::format("{}{}/{}", path, boardTypeLower, boardName);
301}
Christopher Meis59ef1e72025-04-16 08:53:25 +0200302} // namespace em_utils