blob: 4e4639517d67e66ee1d27190f3f3f53e428a55f3 [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>
Chau Ly79629442025-08-20 10:27:20 +00009#include <phosphor-logging/lg2.hpp>
Christopher Meis59ef1e72025-04-16 08:53:25 +020010#include <sdbusplus/bus/match.hpp>
11
12#include <fstream>
Christopher Meis811160e2025-08-08 08:48:37 +020013#include <regex>
14
15const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
Christopher Meis59ef1e72025-04-16 08:53:25 +020016
Alexander Hansen0ab32b32025-06-27 14:50:33 +020017namespace em_utils
18{
19
20constexpr const char* templateChar = "$";
21
Christopher Meis59ef1e72025-04-16 08:53:25 +020022bool fwVersionIsSame()
23{
24 std::ifstream version(versionFile);
25 if (!version.good())
26 {
Alexander Hansenbd85baa2025-08-06 10:46:57 +020027 lg2::error("Can't read {PATH}", "PATH", versionFile);
Christopher Meis59ef1e72025-04-16 08:53:25 +020028 return false;
29 }
30
31 std::string versionData;
32 std::string line;
33 while (std::getline(version, line))
34 {
35 versionData += line;
36 }
37
38 std::string expectedHash =
39 std::to_string(std::hash<std::string>{}(versionData));
40
Alexander Hansenbd85baa2025-08-06 10:46:57 +020041 std::error_code ec;
42 std::filesystem::create_directory(configurationOutDir, ec);
43
44 if (ec)
45 {
46 lg2::error("could not create directory {DIR}", "DIR",
47 configurationOutDir);
48 return false;
49 }
50
Christopher Meis59ef1e72025-04-16 08:53:25 +020051 std::ifstream hashFile(versionHashFile);
52 if (hashFile.good())
53 {
54 std::string hashString;
55 hashFile >> hashString;
56
57 if (expectedHash == hashString)
58 {
59 return true;
60 }
61 hashFile.close();
62 }
63
64 std::ofstream output(versionHashFile);
65 output << expectedHash;
66 return false;
67}
68
Chau Ly79629442025-08-20 10:27:20 +000069void handleLeftOverTemplateVars(nlohmann::json::iterator& keyPair)
70{
71 if (keyPair.value().type() == nlohmann::json::value_t::object ||
72 keyPair.value().type() == nlohmann::json::value_t::array)
73 {
74 for (auto nextLayer = keyPair.value().begin();
75 nextLayer != keyPair.value().end(); nextLayer++)
76 {
77 handleLeftOverTemplateVars(nextLayer);
78 }
79 return;
80 }
81
82 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
83 if (strPtr == nullptr)
84 {
85 return;
86 }
87
88 // Walking through the string to find $<templateVar>
89 while (true)
90 {
Ed Tanousc5a2af92025-08-25 08:58:25 +080091 std::ranges::subrange<std::string::const_iterator> findStart =
92 iFindFirst(*strPtr, std::string_view(templateChar));
93
94 if (!findStart)
Chau Ly79629442025-08-20 10:27:20 +000095 {
96 break;
97 }
98
Ed Tanousc5a2af92025-08-25 08:58:25 +080099 std::ranges::subrange<std::string::iterator> searchRange(
100 strPtr->begin() + (findStart.end() - strPtr->begin()),
101 strPtr->end());
102 std::ranges::subrange<std::string::const_iterator> findSpace =
103 iFindFirst(searchRange, " ");
104
105 std::string::const_iterator templateVarEnd;
106
107 if (!findSpace)
Chau Ly79629442025-08-20 10:27:20 +0000108 {
109 // No space means the template var spans to the end of
110 // of the keyPair value
Ed Tanousc5a2af92025-08-25 08:58:25 +0800111 templateVarEnd = strPtr->end();
Chau Ly79629442025-08-20 10:27:20 +0000112 }
113 else
114 {
115 // A space marks the end of a template var
Ed Tanousc5a2af92025-08-25 08:58:25 +0800116 templateVarEnd = findSpace.begin();
Chau Ly79629442025-08-20 10:27:20 +0000117 }
118
119 lg2::error(
120 "There's still template variable {VAR} un-replaced. Removing it from the string.\n",
Ed Tanousc5a2af92025-08-25 08:58:25 +0800121 "VAR", std::string(findStart.begin(), templateVarEnd));
122 strPtr->erase(findStart.begin(), templateVarEnd);
Chau Ly79629442025-08-20 10:27:20 +0000123 }
124}
125
Christopher Meis59ef1e72025-04-16 08:53:25 +0200126// Replaces the template character like the other version of this function,
127// but checks all properties on all interfaces provided to do the substitution
128// with.
129std::optional<std::string> templateCharReplace(
130 nlohmann::json::iterator& keyPair, const DBusObject& object,
131 const size_t index, const std::optional<std::string>& replaceStr)
132{
133 for (const auto& [_, interface] : object)
134 {
135 auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
136 if (ret)
137 {
Chau Ly79629442025-08-20 10:27:20 +0000138 handleLeftOverTemplateVars(keyPair);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200139 return ret;
140 }
141 }
Chau Ly79629442025-08-20 10:27:20 +0000142 handleLeftOverTemplateVars(keyPair);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200143 return std::nullopt;
144}
145
146// finds the template character (currently set to $) and replaces the value with
147// the field found in a dbus object i.e. $ADDRESS would get populated with the
148// ADDRESS field from a object on dbus
149std::optional<std::string> templateCharReplace(
150 nlohmann::json::iterator& keyPair, const DBusInterface& interface,
151 const size_t index, const std::optional<std::string>& replaceStr)
152{
153 std::optional<std::string> ret = std::nullopt;
154
155 if (keyPair.value().type() == nlohmann::json::value_t::object ||
156 keyPair.value().type() == nlohmann::json::value_t::array)
157 {
158 for (auto nextLayer = keyPair.value().begin();
159 nextLayer != keyPair.value().end(); nextLayer++)
160 {
161 templateCharReplace(nextLayer, interface, index, replaceStr);
162 }
163 return ret;
164 }
165
166 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
167 if (strPtr == nullptr)
168 {
169 return ret;
170 }
171
George Liu5a61ec82025-08-25 11:16:44 +0800172 replaceAll(*strPtr, std::string(templateChar) + "index",
173 std::to_string(index));
Christopher Meis59ef1e72025-04-16 08:53:25 +0200174 if (replaceStr)
175 {
George Liu5a61ec82025-08-25 11:16:44 +0800176 replaceAll(*strPtr, *replaceStr, std::to_string(index));
Christopher Meis59ef1e72025-04-16 08:53:25 +0200177 }
178
179 for (const auto& [propName, propValue] : interface)
180 {
181 std::string templateName = templateChar + propName;
Ed Tanousc5a2af92025-08-25 08:58:25 +0800182 std::ranges::subrange<std::string::const_iterator> find =
183 iFindFirst(*strPtr, templateName);
184 if (!find)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200185 {
186 continue;
187 }
188
Ed Tanousc5a2af92025-08-25 08:58:25 +0800189 size_t start = find.begin() - strPtr->begin();
190
Christopher Meis59ef1e72025-04-16 08:53:25 +0200191 // check for additional operations
Ed Tanousc5a2af92025-08-25 08:58:25 +0800192 if ((start == 0U) && find.end() == strPtr->end())
Christopher Meis59ef1e72025-04-16 08:53:25 +0200193 {
194 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue);
195 return ret;
196 }
197
198 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
199 '/'};
200 size_t nextItemIdx = start + templateName.size() + 1;
201
202 if (nextItemIdx > strPtr->size() ||
203 std::find(mathChars.begin(), mathChars.end(),
204 strPtr->at(nextItemIdx)) == mathChars.end())
205 {
206 std::string val = std::visit(VariantToStringVisitor(), propValue);
George Liu5a61ec82025-08-25 11:16:44 +0800207 iReplaceAll(*strPtr, templateName, val);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200208 continue;
209 }
210
211 // save the prefix
212 std::string prefix = strPtr->substr(0, start);
213
214 // operate on the rest
215 std::string end = strPtr->substr(nextItemIdx);
216
George Liuecf1a312025-08-25 10:43:12 +0800217 std::vector<std::string> splitResult = split(end, ' ');
Christopher Meis59ef1e72025-04-16 08:53:25 +0200218
219 // need at least 1 operation and number
George Liuecf1a312025-08-25 10:43:12 +0800220 if (splitResult.size() < 2)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200221 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200222 lg2::error("Syntax error on template replacement of {STR}", "STR",
223 *strPtr);
George Liuecf1a312025-08-25 10:43:12 +0800224 for (const std::string& data : splitResult)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200225 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200226 lg2::error("{SPLIT} ", "SPLIT", data);
Christopher Meis59ef1e72025-04-16 08:53:25 +0200227 }
Alexander Hansen8feb0452025-09-15 14:29:20 +0200228 lg2::error("");
Christopher Meis59ef1e72025-04-16 08:53:25 +0200229 continue;
230 }
231
232 // we assume that the replacement is a number, because we can
233 // only do math on numbers.. we might concatenate strings in the
234 // future, but thats later
235 int number = std::visit(VariantToIntVisitor(), propValue);
George Liuecf1a312025-08-25 10:43:12 +0800236 auto exprBegin = splitResult.begin();
237 auto exprEnd = splitResult.end();
Christopher Meis59ef1e72025-04-16 08:53:25 +0200238
239 number = expression::evaluate(number, exprBegin, exprEnd);
240
Ed Tanousc5a2af92025-08-25 08:58:25 +0800241 std::string replaced(find.begin(), find.end());
Christopher Meis59ef1e72025-04-16 08:53:25 +0200242 while (exprBegin != exprEnd)
243 {
244 replaced.append(" ").append(*exprBegin++);
245 }
246 ret = replaced;
247
248 std::string result = prefix + std::to_string(number);
George Liuecf1a312025-08-25 10:43:12 +0800249 while (exprEnd != splitResult.end())
Christopher Meis59ef1e72025-04-16 08:53:25 +0200250 {
251 result.append(" ").append(*exprEnd++);
252 }
253 keyPair.value() = result;
254
255 // We probably just invalidated the pointer abovei,
256 // reset and continue to handle multiple templates
257 strPtr = keyPair.value().get_ptr<std::string*>();
258 if (strPtr == nullptr)
259 {
260 break;
261 }
262 }
263
264 strPtr = keyPair.value().get_ptr<std::string*>();
265 if (strPtr == nullptr)
266 {
267 return ret;
268 }
269
270 std::string_view strView = *strPtr;
271 int base = 10;
George Liu164af2f2025-08-21 17:16:31 +0800272 if (strView.starts_with("0x"))
Christopher Meis59ef1e72025-04-16 08:53:25 +0200273 {
274 strView.remove_prefix(2);
275 base = 16;
276 }
277
278 uint64_t temp = 0;
Alexander Hansen5df916f2025-09-26 10:31:36 -0400279 bool fullMatch = false;
Christopher Meis59ef1e72025-04-16 08:53:25 +0200280 const std::from_chars_result res =
Alexander Hansen5df916f2025-09-26 10:31:36 -0400281 fromCharsWrapper(strView, temp, fullMatch, base);
282 if (res.ec == std::errc{} && fullMatch)
Christopher Meis59ef1e72025-04-16 08:53:25 +0200283 {
284 keyPair.value() = temp;
285 }
286
287 return ret;
288}
289
Christopher Meis811160e2025-08-08 08:48:37 +0200290std::string buildInventorySystemPath(std::string& boardName,
291 const std::string& boardType)
292{
293 std::string path = "/xyz/openbmc_project/inventory/system/";
294 std::string boardTypeLower = boost::algorithm::to_lower_copy(boardType);
295
296 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
297 illegalDbusMemberRegex, "_");
298
299 return std::format("{}{}/{}", path, boardTypeLower, boardName);
300}
Christopher Meis59ef1e72025-04-16 08:53:25 +0200301} // namespace em_utils