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