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