blob: d3ff6b9888831f7d6598ae51d4b53bba7d96cfcd [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"
6
Chau Ly79629442025-08-20 10:27:20 +00007#include <phosphor-logging/lg2.hpp>
Christopher Meis59ef1e72025-04-16 08:53:25 +02008#include <sdbusplus/bus/match.hpp>
9
10#include <fstream>
Christopher Meis811160e2025-08-08 08:48:37 +020011#include <regex>
12
13const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
Christopher Meis59ef1e72025-04-16 08:53:25 +020014
Alexander Hansen0ab32b32025-06-27 14:50:33 +020015namespace em_utils
16{
17
18constexpr const char* templateChar = "$";
19
Christopher Meis59ef1e72025-04-16 08:53:25 +020020bool fwVersionIsSame()
21{
22 std::ifstream version(versionFile);
23 if (!version.good())
24 {
Alexander Hansenbd85baa2025-08-06 10:46:57 +020025 lg2::error("Can't read {PATH}", "PATH", versionFile);
Christopher Meis59ef1e72025-04-16 08:53:25 +020026 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
Alexander Hansenbd85baa2025-08-06 10:46:57 +020039 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
Christopher Meis59ef1e72025-04-16 08:53:25 +020049 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
Chau Ly79629442025-08-20 10:27:20 +000067void handleLeftOverTemplateVars(nlohmann::json::iterator& keyPair)
68{
69 if (keyPair.value().type() == nlohmann::json::value_t::object ||
70 keyPair.value().type() == nlohmann::json::value_t::array)
71 {
72 for (auto nextLayer = keyPair.value().begin();
73 nextLayer != keyPair.value().end(); nextLayer++)
74 {
75 handleLeftOverTemplateVars(nextLayer);
76 }
77 return;
78 }
79
80 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
81 if (strPtr == nullptr)
82 {
83 return;
84 }
85
86 // Walking through the string to find $<templateVar>
87 while (true)
88 {
Ed Tanousc5a2af92025-08-25 08:58:25 +080089 std::ranges::subrange<std::string::const_iterator> findStart =
90 iFindFirst(*strPtr, std::string_view(templateChar));
91
92 if (!findStart)
Chau Ly79629442025-08-20 10:27:20 +000093 {
94 break;
95 }
96
Ed Tanousc5a2af92025-08-25 08:58:25 +080097 std::ranges::subrange<std::string::iterator> searchRange(
98 strPtr->begin() + (findStart.end() - strPtr->begin()),
99 strPtr->end());
100 std::ranges::subrange<std::string::const_iterator> findSpace =
101 iFindFirst(searchRange, " ");
102
103 std::string::const_iterator templateVarEnd;
104
105 if (!findSpace)
Chau Ly79629442025-08-20 10:27:20 +0000106 {
107 // No space means the template var spans to the end of
108 // of the keyPair value
Ed Tanousc5a2af92025-08-25 08:58:25 +0800109 templateVarEnd = strPtr->end();
Chau Ly79629442025-08-20 10:27:20 +0000110 }
111 else
112 {
113 // A space marks the end of a template var
Ed Tanousc5a2af92025-08-25 08:58:25 +0800114 templateVarEnd = findSpace.begin();
Chau Ly79629442025-08-20 10:27:20 +0000115 }
116
117 lg2::error(
118 "There's still template variable {VAR} un-replaced. Removing it from the string.\n",
Ed Tanousc5a2af92025-08-25 08:58:25 +0800119 "VAR", std::string(findStart.begin(), templateVarEnd));
120 strPtr->erase(findStart.begin(), templateVarEnd);
Chau Ly79629442025-08-20 10:27:20 +0000121 }
122}
123
Christopher Meis59ef1e72025-04-16 08:53:25 +0200124// Replaces the template character like the other version of this function,
125// but checks all properties on all interfaces provided to do the substitution
126// with.
127std::optional<std::string> templateCharReplace(
128 nlohmann::json::iterator& keyPair, const DBusObject& object,
129 const size_t index, const std::optional<std::string>& replaceStr)
130{
131 for (const auto& [_, interface] : object)
132 {
133 auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
134 if (ret)
135 {
Chau Ly79629442025-08-20 10:27:20 +0000136 handleLeftOverTemplateVars(keyPair);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200137 return ret;
138 }
139 }
Chau Ly79629442025-08-20 10:27:20 +0000140 handleLeftOverTemplateVars(keyPair);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200141 return std::nullopt;
142}
143
144// finds the template character (currently set to $) and replaces the value with
145// the field found in a dbus object i.e. $ADDRESS would get populated with the
146// ADDRESS field from a object on dbus
147std::optional<std::string> templateCharReplace(
148 nlohmann::json::iterator& keyPair, const DBusInterface& interface,
149 const size_t index, const std::optional<std::string>& replaceStr)
150{
151 std::optional<std::string> ret = std::nullopt;
152
153 if (keyPair.value().type() == nlohmann::json::value_t::object ||
154 keyPair.value().type() == nlohmann::json::value_t::array)
155 {
156 for (auto nextLayer = keyPair.value().begin();
157 nextLayer != keyPair.value().end(); nextLayer++)
158 {
159 templateCharReplace(nextLayer, interface, index, replaceStr);
160 }
161 return ret;
162 }
163
164 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
165 if (strPtr == nullptr)
166 {
167 return ret;
168 }
169
George Liu5a61ec82025-08-25 11:16:44 +0800170 replaceAll(*strPtr, std::string(templateChar) + "index",
171 std::to_string(index));
Christopher Meis59ef1e72025-04-16 08:53:25 +0200172 if (replaceStr)
173 {
George Liu5a61ec82025-08-25 11:16:44 +0800174 replaceAll(*strPtr, *replaceStr, std::to_string(index));
Christopher Meis59ef1e72025-04-16 08:53:25 +0200175 }
176
177 for (const auto& [propName, propValue] : interface)
178 {
179 std::string templateName = templateChar + propName;
Ed Tanousc5a2af92025-08-25 08:58:25 +0800180 std::ranges::subrange<std::string::const_iterator> find =
181 iFindFirst(*strPtr, templateName);
182 if (!find)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200183 {
184 continue;
185 }
186
Ed Tanousc5a2af92025-08-25 08:58:25 +0800187 size_t start = find.begin() - strPtr->begin();
188
Christopher Meis59ef1e72025-04-16 08:53:25 +0200189 // check for additional operations
Ed Tanousc5a2af92025-08-25 08:58:25 +0800190 if ((start == 0U) && find.end() == strPtr->end())
Christopher Meis59ef1e72025-04-16 08:53:25 +0200191 {
192 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue);
193 return ret;
194 }
195
196 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
197 '/'};
198 size_t nextItemIdx = start + templateName.size() + 1;
199
200 if (nextItemIdx > strPtr->size() ||
201 std::find(mathChars.begin(), mathChars.end(),
202 strPtr->at(nextItemIdx)) == mathChars.end())
203 {
204 std::string val = std::visit(VariantToStringVisitor(), propValue);
George Liu5a61ec82025-08-25 11:16:44 +0800205 iReplaceAll(*strPtr, templateName, val);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200206 continue;
207 }
208
209 // save the prefix
210 std::string prefix = strPtr->substr(0, start);
211
212 // operate on the rest
213 std::string end = strPtr->substr(nextItemIdx);
214
George Liuecf1a312025-08-25 10:43:12 +0800215 std::vector<std::string> splitResult = split(end, ' ');
Christopher Meis59ef1e72025-04-16 08:53:25 +0200216
217 // need at least 1 operation and number
George Liuecf1a312025-08-25 10:43:12 +0800218 if (splitResult.size() < 2)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200219 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200220 lg2::error("Syntax error on template replacement of {STR}", "STR",
221 *strPtr);
George Liuecf1a312025-08-25 10:43:12 +0800222 for (const std::string& data : splitResult)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200223 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200224 lg2::error("{SPLIT} ", "SPLIT", data);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200225 }
Alexander Hansen8feb0452025-09-15 14:29:20 +0200226 lg2::error("");
Christopher Meis59ef1e72025-04-16 08:53:25 +0200227 continue;
228 }
229
230 // we assume that the replacement is a number, because we can
231 // only do math on numbers.. we might concatenate strings in the
232 // future, but thats later
233 int number = std::visit(VariantToIntVisitor(), propValue);
George Liuecf1a312025-08-25 10:43:12 +0800234 auto exprBegin = splitResult.begin();
235 auto exprEnd = splitResult.end();
Christopher Meis59ef1e72025-04-16 08:53:25 +0200236
237 number = expression::evaluate(number, exprBegin, exprEnd);
238
Ed Tanousc5a2af92025-08-25 08:58:25 +0800239 std::string replaced(find.begin(), find.end());
Christopher Meis59ef1e72025-04-16 08:53:25 +0200240 while (exprBegin != exprEnd)
241 {
242 replaced.append(" ").append(*exprBegin++);
243 }
244 ret = replaced;
245
246 std::string result = prefix + std::to_string(number);
George Liuecf1a312025-08-25 10:43:12 +0800247 while (exprEnd != splitResult.end())
Christopher Meis59ef1e72025-04-16 08:53:25 +0200248 {
249 result.append(" ").append(*exprEnd++);
250 }
251 keyPair.value() = result;
252
253 // We probably just invalidated the pointer abovei,
254 // reset and continue to handle multiple templates
255 strPtr = keyPair.value().get_ptr<std::string*>();
256 if (strPtr == nullptr)
257 {
258 break;
259 }
260 }
261
262 strPtr = keyPair.value().get_ptr<std::string*>();
263 if (strPtr == nullptr)
264 {
265 return ret;
266 }
267
268 std::string_view strView = *strPtr;
269 int base = 10;
George Liu164af2f2025-08-21 17:16:31 +0800270 if (strView.starts_with("0x"))
Christopher Meis59ef1e72025-04-16 08:53:25 +0200271 {
272 strView.remove_prefix(2);
273 base = 16;
274 }
275
276 uint64_t temp = 0;
Alexander Hansen5df916f2025-09-26 10:31:36 -0400277 bool fullMatch = false;
Christopher Meis59ef1e72025-04-16 08:53:25 +0200278 const std::from_chars_result res =
Alexander Hansen5df916f2025-09-26 10:31:36 -0400279 fromCharsWrapper(strView, temp, fullMatch, base);
280 if (res.ec == std::errc{} && fullMatch)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200281 {
282 keyPair.value() = temp;
283 }
284
285 return ret;
286}
287
Christopher Meis811160e2025-08-08 08:48:37 +0200288std::string buildInventorySystemPath(std::string& boardName,
289 const std::string& boardType)
290{
291 std::string path = "/xyz/openbmc_project/inventory/system/";
George Liufd977ec2025-08-25 11:36:44 +0800292 std::string boardTypeLower = toLowerCopy(boardType);
Christopher Meis811160e2025-08-08 08:48:37 +0200293 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
294 illegalDbusMemberRegex, "_");
295
296 return std::format("{}{}/{}", path, boardTypeLower, boardName);
297}
Christopher Meis59ef1e72025-04-16 08:53:25 +0200298} // namespace em_utils