blob: a899f639a2d0ff5dbdebb7669d6160fa3305e11c [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
Ed Tanous77192692025-06-24 11:32:12 -070067void handleLeftOverTemplateVars(nlohmann::json& value)
Chau Ly79629442025-08-20 10:27:20 +000068{
Ed Tanous77192692025-06-24 11:32:12 -070069 nlohmann::json::object_t* objPtr =
70 value.get_ptr<nlohmann::json::object_t*>();
71 if (objPtr != nullptr)
Chau Ly79629442025-08-20 10:27:20 +000072 {
Ed Tanous77192692025-06-24 11:32:12 -070073 for (auto& nextLayer : *objPtr)
74 {
75 handleLeftOverTemplateVars(nextLayer.second);
76 }
77 return;
78 }
79
80 nlohmann::json::array_t* arrPtr = value.get_ptr<nlohmann::json::array_t*>();
81 if (arrPtr != nullptr)
82 {
83 for (auto& nextLayer : *arrPtr)
Chau Ly79629442025-08-20 10:27:20 +000084 {
85 handleLeftOverTemplateVars(nextLayer);
86 }
87 return;
88 }
89
Ed Tanous77192692025-06-24 11:32:12 -070090 std::string* strPtr = value.get_ptr<std::string*>();
Chau Ly79629442025-08-20 10:27:20 +000091 if (strPtr == nullptr)
92 {
93 return;
94 }
95
96 // Walking through the string to find $<templateVar>
97 while (true)
98 {
Ed Tanousc5a2af92025-08-25 08:58:25 +080099 std::ranges::subrange<std::string::const_iterator> findStart =
100 iFindFirst(*strPtr, std::string_view(templateChar));
101
102 if (!findStart)
Chau Ly79629442025-08-20 10:27:20 +0000103 {
104 break;
105 }
106
Ed Tanousc5a2af92025-08-25 08:58:25 +0800107 std::ranges::subrange<std::string::iterator> searchRange(
108 strPtr->begin() + (findStart.end() - strPtr->begin()),
109 strPtr->end());
110 std::ranges::subrange<std::string::const_iterator> findSpace =
111 iFindFirst(searchRange, " ");
112
113 std::string::const_iterator templateVarEnd;
114
115 if (!findSpace)
Chau Ly79629442025-08-20 10:27:20 +0000116 {
117 // No space means the template var spans to the end of
118 // of the keyPair value
Ed Tanousc5a2af92025-08-25 08:58:25 +0800119 templateVarEnd = strPtr->end();
Chau Ly79629442025-08-20 10:27:20 +0000120 }
121 else
122 {
123 // A space marks the end of a template var
Ed Tanousc5a2af92025-08-25 08:58:25 +0800124 templateVarEnd = findSpace.begin();
Chau Ly79629442025-08-20 10:27:20 +0000125 }
126
127 lg2::error(
128 "There's still template variable {VAR} un-replaced. Removing it from the string.\n",
Ed Tanousc5a2af92025-08-25 08:58:25 +0800129 "VAR", std::string(findStart.begin(), templateVarEnd));
130 strPtr->erase(findStart.begin(), templateVarEnd);
Chau Ly79629442025-08-20 10:27:20 +0000131 }
132}
133
Christopher Meis59ef1e72025-04-16 08:53:25 +0200134// Replaces the template character like the other version of this function,
135// but checks all properties on all interfaces provided to do the substitution
136// with.
137std::optional<std::string> templateCharReplace(
Ed Tanous77192692025-06-24 11:32:12 -0700138 nlohmann::json& value, const DBusObject& object, const size_t index,
139 const std::optional<std::string>& replaceStr, bool handleLeftOver)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200140{
141 for (const auto& [_, interface] : object)
142 {
Ed Tanous77192692025-06-24 11:32:12 -0700143 auto ret = templateCharReplace(value, interface, index, replaceStr);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200144 if (ret)
145 {
Chau Ly8ee43692025-10-09 07:22:08 +0000146 if (handleLeftOver)
147 {
Ed Tanous77192692025-06-24 11:32:12 -0700148 handleLeftOverTemplateVars(value);
Chau Ly8ee43692025-10-09 07:22:08 +0000149 }
Christopher Meis59ef1e72025-04-16 08:53:25 +0200150 return ret;
151 }
152 }
Chau Ly8ee43692025-10-09 07:22:08 +0000153 if (handleLeftOver)
154 {
Ed Tanous77192692025-06-24 11:32:12 -0700155 handleLeftOverTemplateVars(value);
Chau Ly8ee43692025-10-09 07:22:08 +0000156 }
Christopher Meis59ef1e72025-04-16 08:53:25 +0200157 return std::nullopt;
158}
159
160// finds the template character (currently set to $) and replaces the value with
161// the field found in a dbus object i.e. $ADDRESS would get populated with the
162// ADDRESS field from a object on dbus
163std::optional<std::string> templateCharReplace(
Ed Tanous77192692025-06-24 11:32:12 -0700164 nlohmann::json& value, const DBusInterface& interface, const size_t index,
165 const std::optional<std::string>& replaceStr)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200166{
167 std::optional<std::string> ret = std::nullopt;
168
Ed Tanous77192692025-06-24 11:32:12 -0700169 nlohmann::json::object_t* objPtr =
170 value.get_ptr<nlohmann::json::object_t*>();
171 if (objPtr != nullptr)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200172 {
Ed Tanous77192692025-06-24 11:32:12 -0700173 for (auto& [key, value] : *objPtr)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200174 {
Ed Tanous77192692025-06-24 11:32:12 -0700175 templateCharReplace(value, interface, index, replaceStr);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200176 }
177 return ret;
178 }
179
Ed Tanous77192692025-06-24 11:32:12 -0700180 nlohmann::json::array_t* arrPtr = value.get_ptr<nlohmann::json::array_t*>();
181 if (arrPtr != nullptr)
182 {
183 for (auto& value : *arrPtr)
184 {
185 templateCharReplace(value, interface, index, replaceStr);
186 }
187 return ret;
188 }
189
190 std::string* strPtr = value.get_ptr<std::string*>();
Christopher Meis59ef1e72025-04-16 08:53:25 +0200191 if (strPtr == nullptr)
192 {
193 return ret;
194 }
195
George Liu5a61ec82025-08-25 11:16:44 +0800196 replaceAll(*strPtr, std::string(templateChar) + "index",
197 std::to_string(index));
Christopher Meis59ef1e72025-04-16 08:53:25 +0200198 if (replaceStr)
199 {
George Liu5a61ec82025-08-25 11:16:44 +0800200 replaceAll(*strPtr, *replaceStr, std::to_string(index));
Christopher Meis59ef1e72025-04-16 08:53:25 +0200201 }
202
203 for (const auto& [propName, propValue] : interface)
204 {
205 std::string templateName = templateChar + propName;
Ed Tanousc5a2af92025-08-25 08:58:25 +0800206 std::ranges::subrange<std::string::const_iterator> find =
207 iFindFirst(*strPtr, templateName);
208 if (!find)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200209 {
210 continue;
211 }
212
Ed Tanousc5a2af92025-08-25 08:58:25 +0800213 size_t start = find.begin() - strPtr->begin();
214
Christopher Meis59ef1e72025-04-16 08:53:25 +0200215 // check for additional operations
Ed Tanousc5a2af92025-08-25 08:58:25 +0800216 if ((start == 0U) && find.end() == strPtr->end())
Christopher Meis59ef1e72025-04-16 08:53:25 +0200217 {
Ed Tanous77192692025-06-24 11:32:12 -0700218 std::visit([&](auto&& val) { value = val; }, propValue);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200219 return ret;
220 }
221
222 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
223 '/'};
224 size_t nextItemIdx = start + templateName.size() + 1;
225
226 if (nextItemIdx > strPtr->size() ||
227 std::find(mathChars.begin(), mathChars.end(),
228 strPtr->at(nextItemIdx)) == mathChars.end())
229 {
230 std::string val = std::visit(VariantToStringVisitor(), propValue);
George Liu5a61ec82025-08-25 11:16:44 +0800231 iReplaceAll(*strPtr, templateName, val);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200232 continue;
233 }
234
235 // save the prefix
236 std::string prefix = strPtr->substr(0, start);
237
238 // operate on the rest
239 std::string end = strPtr->substr(nextItemIdx);
240
George Liuecf1a312025-08-25 10:43:12 +0800241 std::vector<std::string> splitResult = split(end, ' ');
Christopher Meis59ef1e72025-04-16 08:53:25 +0200242
243 // need at least 1 operation and number
George Liuecf1a312025-08-25 10:43:12 +0800244 if (splitResult.size() < 2)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200245 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200246 lg2::error("Syntax error on template replacement of {STR}", "STR",
247 *strPtr);
George Liuecf1a312025-08-25 10:43:12 +0800248 for (const std::string& data : splitResult)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200249 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200250 lg2::error("{SPLIT} ", "SPLIT", data);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200251 }
Alexander Hansen8feb0452025-09-15 14:29:20 +0200252 lg2::error("");
Christopher Meis59ef1e72025-04-16 08:53:25 +0200253 continue;
254 }
255
256 // we assume that the replacement is a number, because we can
257 // only do math on numbers.. we might concatenate strings in the
258 // future, but thats later
259 int number = std::visit(VariantToIntVisitor(), propValue);
George Liuecf1a312025-08-25 10:43:12 +0800260 auto exprBegin = splitResult.begin();
261 auto exprEnd = splitResult.end();
Christopher Meis59ef1e72025-04-16 08:53:25 +0200262
263 number = expression::evaluate(number, exprBegin, exprEnd);
264
Ed Tanousc5a2af92025-08-25 08:58:25 +0800265 std::string replaced(find.begin(), find.end());
Christopher Meis59ef1e72025-04-16 08:53:25 +0200266 while (exprBegin != exprEnd)
267 {
268 replaced.append(" ").append(*exprBegin++);
269 }
270 ret = replaced;
271
272 std::string result = prefix + std::to_string(number);
George Liuecf1a312025-08-25 10:43:12 +0800273 while (exprEnd != splitResult.end())
Christopher Meis59ef1e72025-04-16 08:53:25 +0200274 {
275 result.append(" ").append(*exprEnd++);
276 }
Ed Tanous77192692025-06-24 11:32:12 -0700277 value = result;
Christopher Meis59ef1e72025-04-16 08:53:25 +0200278
279 // We probably just invalidated the pointer abovei,
280 // reset and continue to handle multiple templates
Ed Tanous77192692025-06-24 11:32:12 -0700281 strPtr = value.get_ptr<std::string*>();
Christopher Meis59ef1e72025-04-16 08:53:25 +0200282 if (strPtr == nullptr)
283 {
284 break;
285 }
286 }
287
Ed Tanous77192692025-06-24 11:32:12 -0700288 strPtr = value.get_ptr<std::string*>();
Christopher Meis59ef1e72025-04-16 08:53:25 +0200289 if (strPtr == nullptr)
290 {
291 return ret;
292 }
293
294 std::string_view strView = *strPtr;
295 int base = 10;
George Liu164af2f2025-08-21 17:16:31 +0800296 if (strView.starts_with("0x"))
Christopher Meis59ef1e72025-04-16 08:53:25 +0200297 {
298 strView.remove_prefix(2);
299 base = 16;
300 }
301
302 uint64_t temp = 0;
Alexander Hansen5df916f2025-09-26 10:31:36 -0400303 bool fullMatch = false;
Christopher Meis59ef1e72025-04-16 08:53:25 +0200304 const std::from_chars_result res =
Alexander Hansen5df916f2025-09-26 10:31:36 -0400305 fromCharsWrapper(strView, temp, fullMatch, base);
306 if (res.ec == std::errc{} && fullMatch)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200307 {
Ed Tanous77192692025-06-24 11:32:12 -0700308 value = temp;
Christopher Meis59ef1e72025-04-16 08:53:25 +0200309 }
310
311 return ret;
312}
313
Christopher Meis811160e2025-08-08 08:48:37 +0200314std::string buildInventorySystemPath(std::string& boardName,
315 const std::string& boardType)
316{
317 std::string path = "/xyz/openbmc_project/inventory/system/";
George Liufd977ec2025-08-25 11:36:44 +0800318 std::string boardTypeLower = toLowerCopy(boardType);
Christopher Meis811160e2025-08-08 08:48:37 +0200319 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
320 illegalDbusMemberRegex, "_");
321
322 return std::format("{}{}/{}", path, boardTypeLower, boardName);
323}
Christopher Meis59ef1e72025-04-16 08:53:25 +0200324} // namespace em_utils