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