xref: /openbmc/entity-manager/src/entity_manager/utils.cpp (revision fd977ec6188e8837a4bcf0528cf2b456d70d22d9)
1 #include "utils.hpp"
2 
3 #include "../utils.hpp"
4 #include "../variant_visitors.hpp"
5 #include "expression.hpp"
6 #include "phosphor-logging/lg2.hpp"
7 
8 #include <phosphor-logging/lg2.hpp>
9 #include <sdbusplus/bus/match.hpp>
10 
11 #include <fstream>
12 #include <regex>
13 
14 const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
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         lg2::error("Can't read {PATH}", "PATH", versionFile);
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::error_code ec;
41     std::filesystem::create_directory(configurationOutDir, ec);
42 
43     if (ec)
44     {
45         lg2::error("could not create directory {DIR}", "DIR",
46                    configurationOutDir);
47         return false;
48     }
49 
50     std::ifstream hashFile(versionHashFile);
51     if (hashFile.good())
52     {
53         std::string hashString;
54         hashFile >> hashString;
55 
56         if (expectedHash == hashString)
57         {
58             return true;
59         }
60         hashFile.close();
61     }
62 
63     std::ofstream output(versionHashFile);
64     output << expectedHash;
65     return false;
66 }
67 
68 void handleLeftOverTemplateVars(nlohmann::json::iterator& keyPair)
69 {
70     if (keyPair.value().type() == nlohmann::json::value_t::object ||
71         keyPair.value().type() == nlohmann::json::value_t::array)
72     {
73         for (auto nextLayer = keyPair.value().begin();
74              nextLayer != keyPair.value().end(); nextLayer++)
75         {
76             handleLeftOverTemplateVars(nextLayer);
77         }
78         return;
79     }
80 
81     std::string* strPtr = keyPair.value().get_ptr<std::string*>();
82     if (strPtr == nullptr)
83     {
84         return;
85     }
86 
87     // Walking through the string to find $<templateVar>
88     while (true)
89     {
90         std::ranges::subrange<std::string::const_iterator> findStart =
91             iFindFirst(*strPtr, std::string_view(templateChar));
92 
93         if (!findStart)
94         {
95             break;
96         }
97 
98         std::ranges::subrange<std::string::iterator> searchRange(
99             strPtr->begin() + (findStart.end() - strPtr->begin()),
100             strPtr->end());
101         std::ranges::subrange<std::string::const_iterator> findSpace =
102             iFindFirst(searchRange, " ");
103 
104         std::string::const_iterator templateVarEnd;
105 
106         if (!findSpace)
107         {
108             // No space means the template var spans to the end of
109             // of the keyPair value
110             templateVarEnd = strPtr->end();
111         }
112         else
113         {
114             // A space marks the end of a template var
115             templateVarEnd = findSpace.begin();
116         }
117 
118         lg2::error(
119             "There's still template variable {VAR} un-replaced. Removing it from the string.\n",
120             "VAR", std::string(findStart.begin(), templateVarEnd));
121         strPtr->erase(findStart.begin(), templateVarEnd);
122     }
123 }
124 
125 // Replaces the template character like the other version of this function,
126 // but checks all properties on all interfaces provided to do the substitution
127 // with.
128 std::optional<std::string> templateCharReplace(
129     nlohmann::json::iterator& keyPair, const DBusObject& object,
130     const size_t index, const std::optional<std::string>& replaceStr)
131 {
132     for (const auto& [_, interface] : object)
133     {
134         auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
135         if (ret)
136         {
137             handleLeftOverTemplateVars(keyPair);
138             return ret;
139         }
140     }
141     handleLeftOverTemplateVars(keyPair);
142     return std::nullopt;
143 }
144 
145 // finds the template character (currently set to $) and replaces the value with
146 // the field found in a dbus object i.e. $ADDRESS would get populated with the
147 // ADDRESS field from a object on dbus
148 std::optional<std::string> templateCharReplace(
149     nlohmann::json::iterator& keyPair, const DBusInterface& interface,
150     const size_t index, const std::optional<std::string>& replaceStr)
151 {
152     std::optional<std::string> ret = std::nullopt;
153 
154     if (keyPair.value().type() == nlohmann::json::value_t::object ||
155         keyPair.value().type() == nlohmann::json::value_t::array)
156     {
157         for (auto nextLayer = keyPair.value().begin();
158              nextLayer != keyPair.value().end(); nextLayer++)
159         {
160             templateCharReplace(nextLayer, interface, index, replaceStr);
161         }
162         return ret;
163     }
164 
165     std::string* strPtr = keyPair.value().get_ptr<std::string*>();
166     if (strPtr == nullptr)
167     {
168         return ret;
169     }
170 
171     replaceAll(*strPtr, std::string(templateChar) + "index",
172                std::to_string(index));
173     if (replaceStr)
174     {
175         replaceAll(*strPtr, *replaceStr, std::to_string(index));
176     }
177 
178     for (const auto& [propName, propValue] : interface)
179     {
180         std::string templateName = templateChar + propName;
181         std::ranges::subrange<std::string::const_iterator> find =
182             iFindFirst(*strPtr, templateName);
183         if (!find)
184         {
185             continue;
186         }
187 
188         size_t start = find.begin() - strPtr->begin();
189 
190         // check for additional operations
191         if ((start == 0U) && find.end() == strPtr->end())
192         {
193             std::visit([&](auto&& val) { keyPair.value() = val; }, propValue);
194             return ret;
195         }
196 
197         constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
198                                                          '/'};
199         size_t nextItemIdx = start + templateName.size() + 1;
200 
201         if (nextItemIdx > strPtr->size() ||
202             std::find(mathChars.begin(), mathChars.end(),
203                       strPtr->at(nextItemIdx)) == mathChars.end())
204         {
205             std::string val = std::visit(VariantToStringVisitor(), propValue);
206             iReplaceAll(*strPtr, templateName, val);
207             continue;
208         }
209 
210         // save the prefix
211         std::string prefix = strPtr->substr(0, start);
212 
213         // operate on the rest
214         std::string end = strPtr->substr(nextItemIdx);
215 
216         std::vector<std::string> splitResult = split(end, ' ');
217 
218         // need at least 1 operation and number
219         if (splitResult.size() < 2)
220         {
221             lg2::error("Syntax error on template replacement of {STR}", "STR",
222                        *strPtr);
223             for (const std::string& data : splitResult)
224             {
225                 lg2::error("{SPLIT} ", "SPLIT", data);
226             }
227             lg2::error("");
228             continue;
229         }
230 
231         // we assume that the replacement is a number, because we can
232         // only do math on numbers.. we might concatenate strings in the
233         // future, but thats later
234         int number = std::visit(VariantToIntVisitor(), propValue);
235         auto exprBegin = splitResult.begin();
236         auto exprEnd = splitResult.end();
237 
238         number = expression::evaluate(number, exprBegin, exprEnd);
239 
240         std::string replaced(find.begin(), find.end());
241         while (exprBegin != exprEnd)
242         {
243             replaced.append(" ").append(*exprBegin++);
244         }
245         ret = replaced;
246 
247         std::string result = prefix + std::to_string(number);
248         while (exprEnd != splitResult.end())
249         {
250             result.append(" ").append(*exprEnd++);
251         }
252         keyPair.value() = result;
253 
254         // We probably just invalidated the pointer abovei,
255         // reset and continue to handle multiple templates
256         strPtr = keyPair.value().get_ptr<std::string*>();
257         if (strPtr == nullptr)
258         {
259             break;
260         }
261     }
262 
263     strPtr = keyPair.value().get_ptr<std::string*>();
264     if (strPtr == nullptr)
265     {
266         return ret;
267     }
268 
269     std::string_view strView = *strPtr;
270     int base = 10;
271     if (strView.starts_with("0x"))
272     {
273         strView.remove_prefix(2);
274         base = 16;
275     }
276 
277     uint64_t temp = 0;
278     bool fullMatch = false;
279     const std::from_chars_result res =
280         fromCharsWrapper(strView, temp, fullMatch, base);
281     if (res.ec == std::errc{} && fullMatch)
282     {
283         keyPair.value() = temp;
284     }
285 
286     return ret;
287 }
288 
289 std::string buildInventorySystemPath(std::string& boardName,
290                                      const std::string& boardType)
291 {
292     std::string path = "/xyz/openbmc_project/inventory/system/";
293     std::string boardTypeLower = toLowerCopy(boardType);
294     std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(),
295                        illegalDbusMemberRegex, "_");
296 
297     return std::format("{}{}/{}", path, boardTypeLower, boardName);
298 }
299 } // namespace em_utils
300