blob: 86024c5c0b3ca2863166c93445553a56a13b0c50 [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
Chau Ly79629442025-08-20 10:27:20 +00008#include <phosphor-logging/lg2.hpp>
Christopher Meis59ef1e72025-04-16 08:53:25 +02009#include <sdbusplus/bus/match.hpp>
10
11#include <fstream>
Christopher Meis811160e2025-08-08 08:48:37 +020012#include <regex>
13
14const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
Christopher Meis59ef1e72025-04-16 08:53:25 +020015
Alexander Hansen0ab32b32025-06-27 14:50:33 +020016namespace em_utils
17{
18
19constexpr const char* templateChar = "$";
20
Christopher Meis59ef1e72025-04-16 08:53:25 +020021bool fwVersionIsSame()
22{
23 std::ifstream version(versionFile);
24 if (!version.good())
25 {
Alexander Hansenbd85baa2025-08-06 10:46:57 +020026 lg2::error("Can't read {PATH}", "PATH", versionFile);
Christopher Meis59ef1e72025-04-16 08:53:25 +020027 return false;
28 }
29
30 std::string versionData;
31 std::string line;
32 while (std::getline(version, line))
33 {
34 versionData += line;
35 }
36
37 std::string expectedHash =
38 std::to_string(std::hash<std::string>{}(versionData));
39
Alexander Hansenbd85baa2025-08-06 10:46:57 +020040 std::error_code ec;
41 std::filesystem::create_directory(configurationOutDir, ec);
42
43 if (ec)
44 {
45 lg2::error("could not create directory {DIR}", "DIR",
46 configurationOutDir);
47 return false;
48 }
49
Christopher Meis59ef1e72025-04-16 08:53:25 +020050 std::ifstream hashFile(versionHashFile);
51 if (hashFile.good())
52 {
53 std::string hashString;
54 hashFile >> hashString;
55
56 if (expectedHash == hashString)
57 {
58 return true;
59 }
60 hashFile.close();
61 }
62
63 std::ofstream output(versionHashFile);
64 output << expectedHash;
65 return false;
66}
67
Chau Ly79629442025-08-20 10:27:20 +000068void handleLeftOverTemplateVars(nlohmann::json::iterator& keyPair)
69{
70 if (keyPair.value().type() == nlohmann::json::value_t::object ||
71 keyPair.value().type() == nlohmann::json::value_t::array)
72 {
73 for (auto nextLayer = keyPair.value().begin();
74 nextLayer != keyPair.value().end(); nextLayer++)
75 {
76 handleLeftOverTemplateVars(nextLayer);
77 }
78 return;
79 }
80
81 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
82 if (strPtr == nullptr)
83 {
84 return;
85 }
86
87 // Walking through the string to find $<templateVar>
88 while (true)
89 {
Ed Tanousc5a2af92025-08-25 08:58:25 +080090 std::ranges::subrange<std::string::const_iterator> findStart =
91 iFindFirst(*strPtr, std::string_view(templateChar));
92
93 if (!findStart)
Chau Ly79629442025-08-20 10:27:20 +000094 {
95 break;
96 }
97
Ed Tanousc5a2af92025-08-25 08:58:25 +080098 std::ranges::subrange<std::string::iterator> searchRange(
99 strPtr->begin() + (findStart.end() - strPtr->begin()),
100 strPtr->end());
101 std::ranges::subrange<std::string::const_iterator> findSpace =
102 iFindFirst(searchRange, " ");
103
104 std::string::const_iterator templateVarEnd;
105
106 if (!findSpace)
Chau Ly79629442025-08-20 10:27:20 +0000107 {
108 // No space means the template var spans to the end of
109 // of the keyPair value
Ed Tanousc5a2af92025-08-25 08:58:25 +0800110 templateVarEnd = strPtr->end();
Chau Ly79629442025-08-20 10:27:20 +0000111 }
112 else
113 {
114 // A space marks the end of a template var
Ed Tanousc5a2af92025-08-25 08:58:25 +0800115 templateVarEnd = findSpace.begin();
Chau Ly79629442025-08-20 10:27:20 +0000116 }
117
118 lg2::error(
119 "There's still template variable {VAR} un-replaced. Removing it from the string.\n",
Ed Tanousc5a2af92025-08-25 08:58:25 +0800120 "VAR", std::string(findStart.begin(), templateVarEnd));
121 strPtr->erase(findStart.begin(), templateVarEnd);
Chau Ly79629442025-08-20 10:27:20 +0000122 }
123}
124
Christopher Meis59ef1e72025-04-16 08:53:25 +0200125// Replaces the template character like the other version of this function,
126// but checks all properties on all interfaces provided to do the substitution
127// with.
128std::optional<std::string> templateCharReplace(
129 nlohmann::json::iterator& keyPair, const DBusObject& object,
130 const size_t index, const std::optional<std::string>& replaceStr)
131{
132 for (const auto& [_, interface] : object)
133 {
134 auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
135 if (ret)
136 {
Chau Ly79629442025-08-20 10:27:20 +0000137 handleLeftOverTemplateVars(keyPair);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200138 return ret;
139 }
140 }
Chau Ly79629442025-08-20 10:27:20 +0000141 handleLeftOverTemplateVars(keyPair);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200142 return std::nullopt;
143}
144
145// finds the template character (currently set to $) and replaces the value with
146// the field found in a dbus object i.e. $ADDRESS would get populated with the
147// ADDRESS field from a object on dbus
148std::optional<std::string> templateCharReplace(
149 nlohmann::json::iterator& keyPair, const DBusInterface& interface,
150 const size_t index, const std::optional<std::string>& replaceStr)
151{
152 std::optional<std::string> ret = std::nullopt;
153
154 if (keyPair.value().type() == nlohmann::json::value_t::object ||
155 keyPair.value().type() == nlohmann::json::value_t::array)
156 {
157 for (auto nextLayer = keyPair.value().begin();
158 nextLayer != keyPair.value().end(); nextLayer++)
159 {
160 templateCharReplace(nextLayer, interface, index, replaceStr);
161 }
162 return ret;
163 }
164
165 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
166 if (strPtr == nullptr)
167 {
168 return ret;
169 }
170
George Liu5a61ec82025-08-25 11:16:44 +0800171 replaceAll(*strPtr, std::string(templateChar) + "index",
172 std::to_string(index));
Christopher Meis59ef1e72025-04-16 08:53:25 +0200173 if (replaceStr)
174 {
George Liu5a61ec82025-08-25 11:16:44 +0800175 replaceAll(*strPtr, *replaceStr, std::to_string(index));
Christopher Meis59ef1e72025-04-16 08:53:25 +0200176 }
177
178 for (const auto& [propName, propValue] : interface)
179 {
180 std::string templateName = templateChar + propName;
Ed Tanousc5a2af92025-08-25 08:58:25 +0800181 std::ranges::subrange<std::string::const_iterator> find =
182 iFindFirst(*strPtr, templateName);
183 if (!find)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200184 {
185 continue;
186 }
187
Ed Tanousc5a2af92025-08-25 08:58:25 +0800188 size_t start = find.begin() - strPtr->begin();
189
Christopher Meis59ef1e72025-04-16 08:53:25 +0200190 // check for additional operations
Ed Tanousc5a2af92025-08-25 08:58:25 +0800191 if ((start == 0U) && find.end() == strPtr->end())
Christopher Meis59ef1e72025-04-16 08:53:25 +0200192 {
193 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue);
194 return ret;
195 }
196
197 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
198 '/'};
199 size_t nextItemIdx = start + templateName.size() + 1;
200
201 if (nextItemIdx > strPtr->size() ||
202 std::find(mathChars.begin(), mathChars.end(),
203 strPtr->at(nextItemIdx)) == mathChars.end())
204 {
205 std::string val = std::visit(VariantToStringVisitor(), propValue);
George Liu5a61ec82025-08-25 11:16:44 +0800206 iReplaceAll(*strPtr, templateName, val);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200207 continue;
208 }
209
210 // save the prefix
211 std::string prefix = strPtr->substr(0, start);
212
213 // operate on the rest
214 std::string end = strPtr->substr(nextItemIdx);
215
George Liuecf1a312025-08-25 10:43:12 +0800216 std::vector<std::string> splitResult = split(end, ' ');
Christopher Meis59ef1e72025-04-16 08:53:25 +0200217
218 // need at least 1 operation and number
George Liuecf1a312025-08-25 10:43:12 +0800219 if (splitResult.size() < 2)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200220 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200221 lg2::error("Syntax error on template replacement of {STR}", "STR",
222 *strPtr);
George Liuecf1a312025-08-25 10:43:12 +0800223 for (const std::string& data : splitResult)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200224 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200225 lg2::error("{SPLIT} ", "SPLIT", data);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200226 }
Alexander Hansen8feb0452025-09-15 14:29:20 +0200227 lg2::error("");
Christopher Meis59ef1e72025-04-16 08:53:25 +0200228 continue;
229 }
230
231 // we assume that the replacement is a number, because we can
232 // only do math on numbers.. we might concatenate strings in the
233 // future, but thats later
234 int number = std::visit(VariantToIntVisitor(), propValue);
George Liuecf1a312025-08-25 10:43:12 +0800235 auto exprBegin = splitResult.begin();
236 auto exprEnd = splitResult.end();
Christopher Meis59ef1e72025-04-16 08:53:25 +0200237
238 number = expression::evaluate(number, exprBegin, exprEnd);
239
Ed Tanousc5a2af92025-08-25 08:58:25 +0800240 std::string replaced(find.begin(), find.end());
Christopher Meis59ef1e72025-04-16 08:53:25 +0200241 while (exprBegin != exprEnd)
242 {
243 replaced.append(" ").append(*exprBegin++);
244 }
245 ret = replaced;
246
247 std::string result = prefix + std::to_string(number);
George Liuecf1a312025-08-25 10:43:12 +0800248 while (exprEnd != splitResult.end())
Christopher Meis59ef1e72025-04-16 08:53:25 +0200249 {
250 result.append(" ").append(*exprEnd++);
251 }
252 keyPair.value() = result;
253
254 // We probably just invalidated the pointer abovei,
255 // reset and continue to handle multiple templates
256 strPtr = keyPair.value().get_ptr<std::string*>();
257 if (strPtr == nullptr)
258 {
259 break;
260 }
261 }
262
263 strPtr = keyPair.value().get_ptr<std::string*>();
264 if (strPtr == nullptr)
265 {
266 return ret;
267 }
268
269 std::string_view strView = *strPtr;
270 int base = 10;
George Liu164af2f2025-08-21 17:16:31 +0800271 if (strView.starts_with("0x"))
Christopher Meis59ef1e72025-04-16 08:53:25 +0200272 {
273 strView.remove_prefix(2);
274 base = 16;
275 }
276
277 uint64_t temp = 0;
Alexander Hansen5df916f2025-09-26 10:31:36 -0400278 bool fullMatch = false;
Christopher Meis59ef1e72025-04-16 08:53:25 +0200279 const std::from_chars_result res =
Alexander Hansen5df916f2025-09-26 10:31:36 -0400280 fromCharsWrapper(strView, temp, fullMatch, base);
281 if (res.ec == std::errc{} && fullMatch)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200282 {
283 keyPair.value() = temp;
284 }
285
286 return ret;
287}
288
Christopher Meis811160e2025-08-08 08:48:37 +0200289std::string buildInventorySystemPath(std::string& boardName,
290 const std::string& boardType)
291{
292 std::string path = "/xyz/openbmc_project/inventory/system/";
George Liufd977ec2025-08-25 11:36:44 +0800293 std::string boardTypeLower = toLowerCopy(boardType);
Christopher Meis811160e2025-08-08 08:48:37 +0200294 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
295 illegalDbusMemberRegex, "_");
296
297 return std::format("{}{}/{}", path, boardTypeLower, boardName);
298}
Christopher Meis59ef1e72025-04-16 08:53:25 +0200299} // namespace em_utils