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