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