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