blob: b169bceed217795fd75ab8584a00992b24cbec96 [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
6#include <boost/algorithm/string/classification.hpp>
7#include <boost/algorithm/string/find.hpp>
8#include <boost/algorithm/string/predicate.hpp>
9#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>
15
16namespace em_utils
17{
18
19// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
20static bool powerStatusOn = false;
21// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
22static std::unique_ptr<sdbusplus::bus::match_t> powerMatch = nullptr;
23
24constexpr const char* templateChar = "$";
25
26bool isPowerOn()
27{
28 if (!powerMatch)
29 {
30 throw std::runtime_error("Power Match Not Created");
31 }
32 return powerStatusOn;
33}
34
35void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
36{
37 powerMatch = std::make_unique<sdbusplus::bus::match_t>(
38 static_cast<sdbusplus::bus_t&>(*conn),
39 "type='signal',interface='" + std::string(properties::interface) +
40 "',path='" + std::string(power::path) + "',arg0='" +
41 std::string(power::interface) + "'",
42 [](sdbusplus::message_t& message) {
43 std::string objectName;
44 boost::container::flat_map<std::string, std::variant<std::string>>
45 values;
46 message.read(objectName, values);
47 auto findState = values.find(power::property);
48 if (findState != values.end())
49 {
50 powerStatusOn = boost::ends_with(
51 std::get<std::string>(findState->second), "Running");
52 }
53 });
54
55 conn->async_method_call(
56 [](boost::system::error_code ec,
57 const std::variant<std::string>& state) {
58 if (ec)
59 {
60 return;
61 }
62 powerStatusOn =
63 boost::ends_with(std::get<std::string>(state), "Running");
64 },
65 power::busname, power::path, properties::interface, properties::get,
66 power::interface, power::property);
67}
68
69bool fwVersionIsSame()
70{
71 std::ifstream version(versionFile);
72 if (!version.good())
73 {
74 std::cerr << "Can't read " << versionFile << "\n";
75 return false;
76 }
77
78 std::string versionData;
79 std::string line;
80 while (std::getline(version, line))
81 {
82 versionData += line;
83 }
84
85 std::string expectedHash =
86 std::to_string(std::hash<std::string>{}(versionData));
87
88 std::filesystem::create_directory(configurationOutDir);
89 std::ifstream hashFile(versionHashFile);
90 if (hashFile.good())
91 {
92 std::string hashString;
93 hashFile >> hashString;
94
95 if (expectedHash == hashString)
96 {
97 return true;
98 }
99 hashFile.close();
100 }
101
102 std::ofstream output(versionHashFile);
103 output << expectedHash;
104 return false;
105}
106
107// Replaces the template character like the other version of this function,
108// but checks all properties on all interfaces provided to do the substitution
109// with.
110std::optional<std::string> templateCharReplace(
111 nlohmann::json::iterator& keyPair, const DBusObject& object,
112 const size_t index, const std::optional<std::string>& replaceStr)
113{
114 for (const auto& [_, interface] : object)
115 {
116 auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
117 if (ret)
118 {
119 return ret;
120 }
121 }
122 return std::nullopt;
123}
124
125// finds the template character (currently set to $) and replaces the value with
126// the field found in a dbus object i.e. $ADDRESS would get populated with the
127// ADDRESS field from a object on dbus
128std::optional<std::string> templateCharReplace(
129 nlohmann::json::iterator& keyPair, const DBusInterface& interface,
130 const size_t index, const std::optional<std::string>& replaceStr)
131{
132 std::optional<std::string> ret = std::nullopt;
133
134 if (keyPair.value().type() == nlohmann::json::value_t::object ||
135 keyPair.value().type() == nlohmann::json::value_t::array)
136 {
137 for (auto nextLayer = keyPair.value().begin();
138 nextLayer != keyPair.value().end(); nextLayer++)
139 {
140 templateCharReplace(nextLayer, interface, index, replaceStr);
141 }
142 return ret;
143 }
144
145 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
146 if (strPtr == nullptr)
147 {
148 return ret;
149 }
150
151 boost::replace_all(*strPtr, std::string(templateChar) + "index",
152 std::to_string(index));
153 if (replaceStr)
154 {
155 boost::replace_all(*strPtr, *replaceStr, std::to_string(index));
156 }
157
158 for (const auto& [propName, propValue] : interface)
159 {
160 std::string templateName = templateChar + propName;
161 boost::iterator_range<std::string::const_iterator> find =
162 boost::ifind_first(*strPtr, templateName);
163 if (!find)
164 {
165 continue;
166 }
167
168 size_t start = find.begin() - strPtr->begin();
169
170 // check for additional operations
171 if ((start == 0U) && find.end() == strPtr->end())
172 {
173 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue);
174 return ret;
175 }
176
177 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
178 '/'};
179 size_t nextItemIdx = start + templateName.size() + 1;
180
181 if (nextItemIdx > strPtr->size() ||
182 std::find(mathChars.begin(), mathChars.end(),
183 strPtr->at(nextItemIdx)) == mathChars.end())
184 {
185 std::string val = std::visit(VariantToStringVisitor(), propValue);
186 boost::ireplace_all(*strPtr, templateName, val);
187 continue;
188 }
189
190 // save the prefix
191 std::string prefix = strPtr->substr(0, start);
192
193 // operate on the rest
194 std::string end = strPtr->substr(nextItemIdx);
195
196 std::vector<std::string> split;
197 boost::split(split, end, boost::is_any_of(" "));
198
199 // need at least 1 operation and number
200 if (split.size() < 2)
201 {
202 std::cerr << "Syntax error on template replacement of " << *strPtr
203 << "\n";
204 for (const std::string& data : split)
205 {
206 std::cerr << data << " ";
207 }
208 std::cerr << "\n";
209 continue;
210 }
211
212 // we assume that the replacement is a number, because we can
213 // only do math on numbers.. we might concatenate strings in the
214 // future, but thats later
215 int number = std::visit(VariantToIntVisitor(), propValue);
216 auto exprBegin = split.begin();
217 auto exprEnd = split.end();
218
219 number = expression::evaluate(number, exprBegin, exprEnd);
220
221 std::string replaced(find.begin(), find.end());
222 while (exprBegin != exprEnd)
223 {
224 replaced.append(" ").append(*exprBegin++);
225 }
226 ret = replaced;
227
228 std::string result = prefix + std::to_string(number);
229 while (exprEnd != split.end())
230 {
231 result.append(" ").append(*exprEnd++);
232 }
233 keyPair.value() = result;
234
235 // We probably just invalidated the pointer abovei,
236 // reset and continue to handle multiple templates
237 strPtr = keyPair.value().get_ptr<std::string*>();
238 if (strPtr == nullptr)
239 {
240 break;
241 }
242 }
243
244 strPtr = keyPair.value().get_ptr<std::string*>();
245 if (strPtr == nullptr)
246 {
247 return ret;
248 }
249
250 std::string_view strView = *strPtr;
251 int base = 10;
252 if (boost::starts_with(strView, "0x"))
253 {
254 strView.remove_prefix(2);
255 base = 16;
256 }
257
258 uint64_t temp = 0;
259 const char* strDataEndPtr = strView.data() + strView.size();
260 const std::from_chars_result res =
261 std::from_chars(strView.data(), strDataEndPtr, temp, base);
262 if (res.ec == std::errc{} && res.ptr == strDataEndPtr)
263 {
264 keyPair.value() = temp;
265 }
266
267 return ret;
268}
269
270} // namespace em_utils