blob: 3e4a805279bf5f5b334b571b6a901d892e73ee12 [file] [log] [blame]
Christopher Meis59ef1e72025-04-16 08:53:25 +02001#include "utils.hpp"
2
3#include "../variant_visitors.hpp"
4#include "expression.hpp"
5
Christopher Meis811160e2025-08-08 08:48:37 +02006#include <boost/algorithm/string/case_conv.hpp>
Christopher Meis59ef1e72025-04-16 08:53:25 +02007#include <boost/algorithm/string/classification.hpp>
8#include <boost/algorithm/string/find.hpp>
Christopher Meis59ef1e72025-04-16 08:53:25 +02009#include <boost/algorithm/string/replace.hpp>
10#include <boost/algorithm/string/split.hpp>
11#include <sdbusplus/bus/match.hpp>
12
13#include <fstream>
14#include <iostream>
Christopher Meis811160e2025-08-08 08:48:37 +020015#include <regex>
16
17const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
Christopher Meis59ef1e72025-04-16 08:53:25 +020018
Alexander Hansen0ab32b32025-06-27 14:50:33 +020019namespace em_utils
20{
21
22constexpr const char* templateChar = "$";
23
Christopher Meis59ef1e72025-04-16 08:53:25 +020024bool fwVersionIsSame()
25{
26 std::ifstream version(versionFile);
27 if (!version.good())
28 {
29 std::cerr << "Can't read " << versionFile << "\n";
30 return false;
31 }
32
33 std::string versionData;
34 std::string line;
35 while (std::getline(version, line))
36 {
37 versionData += line;
38 }
39
40 std::string expectedHash =
41 std::to_string(std::hash<std::string>{}(versionData));
42
43 std::filesystem::create_directory(configurationOutDir);
44 std::ifstream hashFile(versionHashFile);
45 if (hashFile.good())
46 {
47 std::string hashString;
48 hashFile >> hashString;
49
50 if (expectedHash == hashString)
51 {
52 return true;
53 }
54 hashFile.close();
55 }
56
57 std::ofstream output(versionHashFile);
58 output << expectedHash;
59 return false;
60}
61
62// Replaces the template character like the other version of this function,
63// but checks all properties on all interfaces provided to do the substitution
64// with.
65std::optional<std::string> templateCharReplace(
66 nlohmann::json::iterator& keyPair, const DBusObject& object,
67 const size_t index, const std::optional<std::string>& replaceStr)
68{
69 for (const auto& [_, interface] : object)
70 {
71 auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
72 if (ret)
73 {
74 return ret;
75 }
76 }
77 return std::nullopt;
78}
79
80// finds the template character (currently set to $) and replaces the value with
81// the field found in a dbus object i.e. $ADDRESS would get populated with the
82// ADDRESS field from a object on dbus
83std::optional<std::string> templateCharReplace(
84 nlohmann::json::iterator& keyPair, const DBusInterface& interface,
85 const size_t index, const std::optional<std::string>& replaceStr)
86{
87 std::optional<std::string> ret = std::nullopt;
88
89 if (keyPair.value().type() == nlohmann::json::value_t::object ||
90 keyPair.value().type() == nlohmann::json::value_t::array)
91 {
92 for (auto nextLayer = keyPair.value().begin();
93 nextLayer != keyPair.value().end(); nextLayer++)
94 {
95 templateCharReplace(nextLayer, interface, index, replaceStr);
96 }
97 return ret;
98 }
99
100 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
101 if (strPtr == nullptr)
102 {
103 return ret;
104 }
105
106 boost::replace_all(*strPtr, std::string(templateChar) + "index",
107 std::to_string(index));
108 if (replaceStr)
109 {
110 boost::replace_all(*strPtr, *replaceStr, std::to_string(index));
111 }
112
113 for (const auto& [propName, propValue] : interface)
114 {
115 std::string templateName = templateChar + propName;
116 boost::iterator_range<std::string::const_iterator> find =
117 boost::ifind_first(*strPtr, templateName);
118 if (!find)
119 {
120 continue;
121 }
122
123 size_t start = find.begin() - strPtr->begin();
124
125 // check for additional operations
126 if ((start == 0U) && find.end() == strPtr->end())
127 {
128 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue);
129 return ret;
130 }
131
132 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
133 '/'};
134 size_t nextItemIdx = start + templateName.size() + 1;
135
136 if (nextItemIdx > strPtr->size() ||
137 std::find(mathChars.begin(), mathChars.end(),
138 strPtr->at(nextItemIdx)) == mathChars.end())
139 {
140 std::string val = std::visit(VariantToStringVisitor(), propValue);
141 boost::ireplace_all(*strPtr, templateName, val);
142 continue;
143 }
144
145 // save the prefix
146 std::string prefix = strPtr->substr(0, start);
147
148 // operate on the rest
149 std::string end = strPtr->substr(nextItemIdx);
150
151 std::vector<std::string> split;
152 boost::split(split, end, boost::is_any_of(" "));
153
154 // need at least 1 operation and number
155 if (split.size() < 2)
156 {
157 std::cerr << "Syntax error on template replacement of " << *strPtr
158 << "\n";
159 for (const std::string& data : split)
160 {
161 std::cerr << data << " ";
162 }
163 std::cerr << "\n";
164 continue;
165 }
166
167 // we assume that the replacement is a number, because we can
168 // only do math on numbers.. we might concatenate strings in the
169 // future, but thats later
170 int number = std::visit(VariantToIntVisitor(), propValue);
171 auto exprBegin = split.begin();
172 auto exprEnd = split.end();
173
174 number = expression::evaluate(number, exprBegin, exprEnd);
175
176 std::string replaced(find.begin(), find.end());
177 while (exprBegin != exprEnd)
178 {
179 replaced.append(" ").append(*exprBegin++);
180 }
181 ret = replaced;
182
183 std::string result = prefix + std::to_string(number);
184 while (exprEnd != split.end())
185 {
186 result.append(" ").append(*exprEnd++);
187 }
188 keyPair.value() = result;
189
190 // We probably just invalidated the pointer abovei,
191 // reset and continue to handle multiple templates
192 strPtr = keyPair.value().get_ptr<std::string*>();
193 if (strPtr == nullptr)
194 {
195 break;
196 }
197 }
198
199 strPtr = keyPair.value().get_ptr<std::string*>();
200 if (strPtr == nullptr)
201 {
202 return ret;
203 }
204
205 std::string_view strView = *strPtr;
206 int base = 10;
George Liu164af2f2025-08-21 17:16:31 +0800207 if (strView.starts_with("0x"))
Christopher Meis59ef1e72025-04-16 08:53:25 +0200208 {
209 strView.remove_prefix(2);
210 base = 16;
211 }
212
213 uint64_t temp = 0;
214 const char* strDataEndPtr = strView.data() + strView.size();
215 const std::from_chars_result res =
216 std::from_chars(strView.data(), strDataEndPtr, temp, base);
217 if (res.ec == std::errc{} && res.ptr == strDataEndPtr)
218 {
219 keyPair.value() = temp;
220 }
221
222 return ret;
223}
224
Christopher Meis811160e2025-08-08 08:48:37 +0200225std::string buildInventorySystemPath(std::string& boardName,
226 const std::string& boardType)
227{
228 std::string path = "/xyz/openbmc_project/inventory/system/";
229 std::string boardTypeLower = boost::algorithm::to_lower_copy(boardType);
230
231 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
232 illegalDbusMemberRegex, "_");
233
234 return std::format("{}{}/{}", path, boardTypeLower, boardName);
235}
Christopher Meis59ef1e72025-04-16 08:53:25 +0200236} // namespace em_utils