1 #include "utils.hpp"
2
3 #include "../utils.hpp"
4 #include "../variant_visitors.hpp"
5 #include "expression.hpp"
6 #include "phosphor-logging/lg2.hpp"
7
8 #include <boost/algorithm/string/case_conv.hpp>
9 #include <boost/algorithm/string/classification.hpp>
10 #include <boost/algorithm/string/find.hpp>
11 #include <boost/algorithm/string/replace.hpp>
12 #include <boost/algorithm/string/split.hpp>
13 #include <phosphor-logging/lg2.hpp>
14 #include <sdbusplus/bus/match.hpp>
15
16 #include <fstream>
17 #include <iostream>
18 #include <regex>
19
20 const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
21
22 namespace em_utils
23 {
24
25 constexpr const char* templateChar = "$";
26
fwVersionIsSame()27 bool fwVersionIsSame()
28 {
29 std::ifstream version(versionFile);
30 if (!version.good())
31 {
32 lg2::error("Can't read {PATH}", "PATH", versionFile);
33 return false;
34 }
35
36 std::string versionData;
37 std::string line;
38 while (std::getline(version, line))
39 {
40 versionData += line;
41 }
42
43 std::string expectedHash =
44 std::to_string(std::hash<std::string>{}(versionData));
45
46 std::error_code ec;
47 std::filesystem::create_directory(configurationOutDir, ec);
48
49 if (ec)
50 {
51 lg2::error("could not create directory {DIR}", "DIR",
52 configurationOutDir);
53 return false;
54 }
55
56 std::ifstream hashFile(versionHashFile);
57 if (hashFile.good())
58 {
59 std::string hashString;
60 hashFile >> hashString;
61
62 if (expectedHash == hashString)
63 {
64 return true;
65 }
66 hashFile.close();
67 }
68
69 std::ofstream output(versionHashFile);
70 output << expectedHash;
71 return false;
72 }
73
handleLeftOverTemplateVars(nlohmann::json::iterator & keyPair)74 void handleLeftOverTemplateVars(nlohmann::json::iterator& keyPair)
75 {
76 if (keyPair.value().type() == nlohmann::json::value_t::object ||
77 keyPair.value().type() == nlohmann::json::value_t::array)
78 {
79 for (auto nextLayer = keyPair.value().begin();
80 nextLayer != keyPair.value().end(); nextLayer++)
81 {
82 handleLeftOverTemplateVars(nextLayer);
83 }
84 return;
85 }
86
87 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
88 if (strPtr == nullptr)
89 {
90 return;
91 }
92
93 // Walking through the string to find $<templateVar>
94 while (true)
95 {
96 boost::iterator_range<std::string::const_iterator> findStart =
97 boost::ifind_first(*strPtr, std::string_view(templateChar));
98
99 if (!findStart)
100 {
101 break;
102 }
103
104 boost::iterator_range<std::string::iterator> searchRange(
105 strPtr->begin() + (findStart.end() - strPtr->begin()),
106 strPtr->end());
107 boost::iterator_range<std::string::const_iterator> findSpace =
108 boost::ifind_first(searchRange, " ");
109
110 std::string::const_iterator templateVarEnd;
111
112 if (!findSpace)
113 {
114 // No space means the template var spans to the end of
115 // of the keyPair value
116 templateVarEnd = strPtr->end();
117 }
118 else
119 {
120 // A space marks the end of a template var
121 templateVarEnd = findSpace.begin();
122 }
123
124 lg2::error(
125 "There's still template variable {VAR} un-replaced. Removing it from the string.\n",
126 "VAR", std::string(findStart.begin(), templateVarEnd));
127 strPtr->erase(findStart.begin(), templateVarEnd);
128 }
129 }
130
131 // Replaces the template character like the other version of this function,
132 // but checks all properties on all interfaces provided to do the substitution
133 // with.
templateCharReplace(nlohmann::json::iterator & keyPair,const DBusObject & object,const size_t index,const std::optional<std::string> & replaceStr)134 std::optional<std::string> templateCharReplace(
135 nlohmann::json::iterator& keyPair, const DBusObject& object,
136 const size_t index, const std::optional<std::string>& replaceStr)
137 {
138 for (const auto& [_, interface] : object)
139 {
140 auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
141 if (ret)
142 {
143 handleLeftOverTemplateVars(keyPair);
144 return ret;
145 }
146 }
147 handleLeftOverTemplateVars(keyPair);
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 boost::replace_all(*strPtr, std::string(templateChar) + "index",
178 std::to_string(index));
179 if (replaceStr)
180 {
181 boost::replace_all(*strPtr, *replaceStr, std::to_string(index));
182 }
183
184 for (const auto& [propName, propValue] : interface)
185 {
186 std::string templateName = templateChar + propName;
187 boost::iterator_range<std::string::const_iterator> find =
188 boost::ifind_first(*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 boost::ireplace_all(*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> split;
223 boost::split(split, end, boost::is_any_of(" "));
224
225 // need at least 1 operation and number
226 if (split.size() < 2)
227 {
228 std::cerr << "Syntax error on template replacement of " << *strPtr
229 << "\n";
230 for (const std::string& data : split)
231 {
232 std::cerr << data << " ";
233 }
234 std::cerr << "\n";
235 continue;
236 }
237
238 // we assume that the replacement is a number, because we can
239 // only do math on numbers.. we might concatenate strings in the
240 // future, but thats later
241 int number = std::visit(VariantToIntVisitor(), propValue);
242 auto exprBegin = split.begin();
243 auto exprEnd = split.end();
244
245 number = expression::evaluate(number, exprBegin, exprEnd);
246
247 std::string replaced(find.begin(), find.end());
248 while (exprBegin != exprEnd)
249 {
250 replaced.append(" ").append(*exprBegin++);
251 }
252 ret = replaced;
253
254 std::string result = prefix + std::to_string(number);
255 while (exprEnd != split.end())
256 {
257 result.append(" ").append(*exprEnd++);
258 }
259 keyPair.value() = result;
260
261 // We probably just invalidated the pointer abovei,
262 // reset and continue to handle multiple templates
263 strPtr = keyPair.value().get_ptr<std::string*>();
264 if (strPtr == nullptr)
265 {
266 break;
267 }
268 }
269
270 strPtr = keyPair.value().get_ptr<std::string*>();
271 if (strPtr == nullptr)
272 {
273 return ret;
274 }
275
276 std::string_view strView = *strPtr;
277 int base = 10;
278 if (strView.starts_with("0x"))
279 {
280 strView.remove_prefix(2);
281 base = 16;
282 }
283
284 uint64_t temp = 0;
285 bool fullMatch = false;
286 const std::from_chars_result res =
287 fromCharsWrapper(strView, temp, fullMatch, base);
288 if (res.ec == std::errc{} && fullMatch)
289 {
290 keyPair.value() = temp;
291 }
292
293 return ret;
294 }
295
buildInventorySystemPath(std::string & boardName,const std::string & boardType)296 std::string buildInventorySystemPath(std::string& boardName,
297 const std::string& boardType)
298 {
299 std::string path = "/xyz/openbmc_project/inventory/system/";
300 std::string boardTypeLower = boost::algorithm::to_lower_copy(boardType);
301
302 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
303 illegalDbusMemberRegex, "_");
304
305 return std::format("{}{}/{}", path, boardTypeLower, boardName);
306 }
307 } // namespace em_utils
308