1 #include "utils.hpp"
2
3 #include "../utils.hpp"
4 #include "../variant_visitors.hpp"
5 #include "expression.hpp"
6
7 #include <phosphor-logging/lg2.hpp>
8 #include <sdbusplus/bus/match.hpp>
9
10 #include <fstream>
11 #include <regex>
12
13 const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
14
15 namespace em_utils
16 {
17
18 constexpr const char* templateChar = "$";
19
fwVersionIsSame()20 bool fwVersionIsSame()
21 {
22 std::ifstream version(versionFile);
23 if (!version.good())
24 {
25 lg2::error("Can't read {PATH}", "PATH", versionFile);
26 return false;
27 }
28
29 std::string versionData;
30 std::string line;
31 while (std::getline(version, line))
32 {
33 versionData += line;
34 }
35
36 std::string expectedHash =
37 std::to_string(std::hash<std::string>{}(versionData));
38
39 std::error_code ec;
40 std::filesystem::create_directory(configurationOutDir, ec);
41
42 if (ec)
43 {
44 lg2::error("could not create directory {DIR}", "DIR",
45 configurationOutDir);
46 return false;
47 }
48
49 std::ifstream hashFile(versionHashFile);
50 if (hashFile.good())
51 {
52 std::string hashString;
53 hashFile >> hashString;
54
55 if (expectedHash == hashString)
56 {
57 return true;
58 }
59 hashFile.close();
60 }
61
62 std::ofstream output(versionHashFile);
63 output << expectedHash;
64 return false;
65 }
66
handleLeftOverTemplateVars(nlohmann::json::iterator & keyPair)67 void handleLeftOverTemplateVars(nlohmann::json::iterator& keyPair)
68 {
69 if (keyPair.value().type() == nlohmann::json::value_t::object ||
70 keyPair.value().type() == nlohmann::json::value_t::array)
71 {
72 for (auto nextLayer = keyPair.value().begin();
73 nextLayer != keyPair.value().end(); nextLayer++)
74 {
75 handleLeftOverTemplateVars(nextLayer);
76 }
77 return;
78 }
79
80 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
81 if (strPtr == nullptr)
82 {
83 return;
84 }
85
86 // Walking through the string to find $<templateVar>
87 while (true)
88 {
89 std::ranges::subrange<std::string::const_iterator> findStart =
90 iFindFirst(*strPtr, std::string_view(templateChar));
91
92 if (!findStart)
93 {
94 break;
95 }
96
97 std::ranges::subrange<std::string::iterator> searchRange(
98 strPtr->begin() + (findStart.end() - strPtr->begin()),
99 strPtr->end());
100 std::ranges::subrange<std::string::const_iterator> findSpace =
101 iFindFirst(searchRange, " ");
102
103 std::string::const_iterator templateVarEnd;
104
105 if (!findSpace)
106 {
107 // No space means the template var spans to the end of
108 // of the keyPair value
109 templateVarEnd = strPtr->end();
110 }
111 else
112 {
113 // A space marks the end of a template var
114 templateVarEnd = findSpace.begin();
115 }
116
117 lg2::error(
118 "There's still template variable {VAR} un-replaced. Removing it from the string.\n",
119 "VAR", std::string(findStart.begin(), templateVarEnd));
120 strPtr->erase(findStart.begin(), templateVarEnd);
121 }
122 }
123
124 // Replaces the template character like the other version of this function,
125 // but checks all properties on all interfaces provided to do the substitution
126 // with.
templateCharReplace(nlohmann::json::iterator & keyPair,const DBusObject & object,const size_t index,const std::optional<std::string> & replaceStr,bool handleLeftOver)127 std::optional<std::string> templateCharReplace(
128 nlohmann::json::iterator& keyPair, const DBusObject& object,
129 const size_t index, const std::optional<std::string>& replaceStr,
130 bool handleLeftOver)
131 {
132 for (const auto& [_, interface] : object)
133 {
134 auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
135 if (ret)
136 {
137 if (handleLeftOver)
138 {
139 handleLeftOverTemplateVars(keyPair);
140 }
141 return ret;
142 }
143 }
144 if (handleLeftOver)
145 {
146 handleLeftOverTemplateVars(keyPair);
147 }
148 return std::nullopt;
149 }
150
151 // finds the template character (currently set to $) and replaces the value with
152 // the field found in a dbus object i.e. $ADDRESS would get populated with the
153 // ADDRESS field from a object on dbus
templateCharReplace(nlohmann::json::iterator & keyPair,const DBusInterface & interface,const size_t index,const std::optional<std::string> & replaceStr)154 std::optional<std::string> templateCharReplace(
155 nlohmann::json::iterator& keyPair, const DBusInterface& interface,
156 const size_t index, const std::optional<std::string>& replaceStr)
157 {
158 std::optional<std::string> ret = std::nullopt;
159
160 if (keyPair.value().type() == nlohmann::json::value_t::object ||
161 keyPair.value().type() == nlohmann::json::value_t::array)
162 {
163 for (auto nextLayer = keyPair.value().begin();
164 nextLayer != keyPair.value().end(); nextLayer++)
165 {
166 templateCharReplace(nextLayer, interface, index, replaceStr);
167 }
168 return ret;
169 }
170
171 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
172 if (strPtr == nullptr)
173 {
174 return ret;
175 }
176
177 replaceAll(*strPtr, std::string(templateChar) + "index",
178 std::to_string(index));
179 if (replaceStr)
180 {
181 replaceAll(*strPtr, *replaceStr, std::to_string(index));
182 }
183
184 for (const auto& [propName, propValue] : interface)
185 {
186 std::string templateName = templateChar + propName;
187 std::ranges::subrange<std::string::const_iterator> find =
188 iFindFirst(*strPtr, templateName);
189 if (!find)
190 {
191 continue;
192 }
193
194 size_t start = find.begin() - strPtr->begin();
195
196 // check for additional operations
197 if ((start == 0U) && find.end() == strPtr->end())
198 {
199 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue);
200 return ret;
201 }
202
203 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
204 '/'};
205 size_t nextItemIdx = start + templateName.size() + 1;
206
207 if (nextItemIdx > strPtr->size() ||
208 std::find(mathChars.begin(), mathChars.end(),
209 strPtr->at(nextItemIdx)) == mathChars.end())
210 {
211 std::string val = std::visit(VariantToStringVisitor(), propValue);
212 iReplaceAll(*strPtr, templateName, val);
213 continue;
214 }
215
216 // save the prefix
217 std::string prefix = strPtr->substr(0, start);
218
219 // operate on the rest
220 std::string end = strPtr->substr(nextItemIdx);
221
222 std::vector<std::string> splitResult = split(end, ' ');
223
224 // need at least 1 operation and number
225 if (splitResult.size() < 2)
226 {
227 lg2::error("Syntax error on template replacement of {STR}", "STR",
228 *strPtr);
229 for (const std::string& data : splitResult)
230 {
231 lg2::error("{SPLIT} ", "SPLIT", data);
232 }
233 lg2::error("");
234 continue;
235 }
236
237 // we assume that the replacement is a number, because we can
238 // only do math on numbers.. we might concatenate strings in the
239 // future, but thats later
240 int number = std::visit(VariantToIntVisitor(), propValue);
241 auto exprBegin = splitResult.begin();
242 auto exprEnd = splitResult.end();
243
244 number = expression::evaluate(number, exprBegin, exprEnd);
245
246 std::string replaced(find.begin(), find.end());
247 while (exprBegin != exprEnd)
248 {
249 replaced.append(" ").append(*exprBegin++);
250 }
251 ret = replaced;
252
253 std::string result = prefix + std::to_string(number);
254 while (exprEnd != splitResult.end())
255 {
256 result.append(" ").append(*exprEnd++);
257 }
258 keyPair.value() = result;
259
260 // We probably just invalidated the pointer abovei,
261 // reset and continue to handle multiple templates
262 strPtr = keyPair.value().get_ptr<std::string*>();
263 if (strPtr == nullptr)
264 {
265 break;
266 }
267 }
268
269 strPtr = keyPair.value().get_ptr<std::string*>();
270 if (strPtr == nullptr)
271 {
272 return ret;
273 }
274
275 std::string_view strView = *strPtr;
276 int base = 10;
277 if (strView.starts_with("0x"))
278 {
279 strView.remove_prefix(2);
280 base = 16;
281 }
282
283 uint64_t temp = 0;
284 bool fullMatch = false;
285 const std::from_chars_result res =
286 fromCharsWrapper(strView, temp, fullMatch, base);
287 if (res.ec == std::errc{} && fullMatch)
288 {
289 keyPair.value() = temp;
290 }
291
292 return ret;
293 }
294
buildInventorySystemPath(std::string & boardName,const std::string & boardType)295 std::string buildInventorySystemPath(std::string& boardName,
296 const std::string& boardType)
297 {
298 std::string path = "/xyz/openbmc_project/inventory/system/";
299 std::string boardTypeLower = toLowerCopy(boardType);
300 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
301 illegalDbusMemberRegex, "_");
302
303 return std::format("{}{}/{}", path, boardTypeLower, boardName);
304 }
305 } // namespace em_utils
306