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