xref: /openbmc/openpower-vpd-parser/vpd-manager/include/utility/common_utility.hpp (revision 1f720c96e54cb4e19b80408df61c761dcce15834)
1 #pragma once
2 
3 #include "constants.hpp"
4 #include "error_codes.hpp"
5 #include "logger.hpp"
6 
7 #include <algorithm>
8 #include <chrono>
9 #include <cstdio>
10 #include <cstdlib>
11 #include <vector>
12 
13 /**
14  * @brief Namespace to host common utility methods.
15  *
16  * A method qualifies as a common utility function if,
17  * A)It is being used by the utility namespace at the same level as well as
18  * other files directly.
19  * B) The utility should be a leaf node and should not be dependent on any other
20  * utility.
21  *                  *******************
22  *                  | Commmon Utility | - - - - - - -
23  *                  *******************              |
24  *                          /\                       |
25  *                         /  \                      |
26  *         ****************    ****************      |
27  *         | json utility |    | dbus utility |      |
28  *         ****************    ****************      |
29  *                 \                 /               |
30  *                  \               /                |
31  *               ************************            |
32  *               | Vpd specific Utility | - - - - - - -
33  *               ************************
34  */
35 
36 namespace vpd
37 {
38 
39 namespace commonUtility
40 {
41 /**
42  * @brief API to get error code message.
43  *
44  * @param[in] i_errCode - error code.
45  *
46  * @return Error message set for that error code. Otherwise empty
47  * string.
48  */
getErrCodeMsg(const uint16_t & i_errCode)49 inline std::string getErrCodeMsg(const uint16_t& i_errCode)
50 {
51     if (errorCodeMap.find(i_errCode) != errorCodeMap.end())
52     {
53         return errorCodeMap.at(i_errCode);
54     }
55 
56     return std::string{};
57 }
58 
59 /** @brief Return the hex representation of the incoming byte.
60  *
61  * @param [in] i_aByte - The input byte.
62  * @returns Null character if input byte is out of bound else returns hex
63  * representation of the byte as a character.
64  */
toHex(const size_t & i_aByte)65 constexpr auto toHex(const size_t& i_aByte) noexcept
66 {
67     constexpr auto l_map = "0123456789abcdef";
68 
69     return (i_aByte < std::strlen(l_map)) ? l_map[i_aByte] : '\0';
70 }
71 
72 /**
73  * @brief API to return null at the end of variadic template args.
74  *
75  * @return empty string.
76  */
getCommand()77 inline std::string getCommand()
78 {
79     return "";
80 }
81 
82 /**
83  * @brief API to arrange create command.
84  *
85  * @param[in] arguments to create the command
86  * @return Command string
87  */
88 template <typename T, typename... Types>
getCommand(T i_arg1,Types...i_args)89 inline std::string getCommand(T i_arg1, Types... i_args)
90 {
91     std::string l_cmd = " " + i_arg1 + getCommand(i_args...);
92 
93     return l_cmd;
94 }
95 
96 /**
97  * @brief API to create shell command and execute.
98  *
99  * @throw std::runtime_error.
100  *
101  * @param[in] arguments for command
102  * @param[out] o_errCode - To set error code in case of error.
103  * @returns output of that command
104  */
105 template <typename T, typename... Types>
executeCmd(T && i_path,uint16_t & o_errCode,Types...i_args)106 inline std::vector<std::string> executeCmd(T&& i_path, uint16_t& o_errCode,
107                                            Types... i_args)
108 {
109     o_errCode = 0;
110     std::vector<std::string> l_cmdOutput;
111 
112     try
113     {
114         std::array<char, constants::CMD_BUFFER_LENGTH> l_buffer;
115 
116         std::string l_cmd = i_path + getCommand(i_args...);
117 
118         std::unique_ptr<FILE, decltype(&pclose)> l_cmdPipe(
119             popen(l_cmd.c_str(), "r"), pclose);
120 
121         if (!l_cmdPipe)
122         {
123             o_errCode = error_code::POPEN_FAILED;
124             Logger::getLoggerInstance()->logMessage(
125                 "popen failed with error " + std::string(strerror(errno)));
126             return l_cmdOutput;
127         }
128 
129         while (fgets(l_buffer.data(), l_buffer.size(), l_cmdPipe.get()) !=
130                nullptr)
131         {
132             l_cmdOutput.emplace_back(l_buffer.data());
133         }
134     }
135     catch (const std::exception& l_ex)
136     {
137         o_errCode = error_code::STANDARD_EXCEPTION;
138         Logger::getLoggerInstance()->logMessage(
139             "Error while trying to execute command [" + std::string(i_path) +
140             "], error : " + std::string(l_ex.what()));
141     }
142 
143     return l_cmdOutput;
144 }
145 
146 /** @brief Converts string to lower case.
147  *
148  * @param [in] i_string - Input string.
149  */
toLower(std::string & i_string)150 inline void toLower(std::string& i_string)
151 {
152     std::transform(i_string.begin(), i_string.end(), i_string.begin(),
153                    [](unsigned char l_char) { return std::tolower(l_char); });
154 }
155 
156 /**
157  * @brief An API to get hex representation of the incoming bytes.
158  *
159  * The API returns the hex represented value of the given input in string format
160  * with 0x prefix.
161  *
162  * @param[in] i_keywordValue - Vector of input byte.
163  *
164  * @return - Returns the converted string value.
165  */
convertByteVectorToHex(const types::BinaryVector & i_keywordValue)166 inline std::string convertByteVectorToHex(
167     const types::BinaryVector& i_keywordValue)
168 {
169     std::ostringstream l_oss;
170     l_oss << "0x";
171     for (const auto& l_byte : i_keywordValue)
172     {
173         l_oss << std::setfill('0') << std::setw(2) << std::hex
174               << static_cast<int>(l_byte);
175     }
176 
177     return l_oss.str();
178 }
179 
180 /**
181  * @brief An API to convert binary value into ascii/hex representation.
182  *
183  * If given data contains printable characters, ASCII formated string value of
184  * the input data will be returned. Otherwise if the data has any non-printable
185  * value, returns the hex represented value of the given data in string format.
186  *
187  * @param[in] i_keywordValue - Data in binary format.
188  * @param[out] o_errCode - To set error code in case of error.
189  *
190  * @return - Returns the converted string value.
191  */
getPrintableValue(const types::BinaryVector & i_keywordValue,uint16_t & o_errCode)192 inline std::string getPrintableValue(const types::BinaryVector& i_keywordValue,
193                                      uint16_t& o_errCode)
194 {
195     o_errCode = 0;
196     std::ostringstream l_oss;
197     try
198     {
199         bool l_allPrintable = std::all_of(
200             i_keywordValue.begin(), i_keywordValue.end(),
201             [](const auto& l_byte) { return std::isprint(l_byte); });
202 
203         if (l_allPrintable)
204         {
205             l_oss << std::string(i_keywordValue.begin(), i_keywordValue.end());
206         }
207         else
208         {
209             l_oss << "0x";
210             for (const auto& l_byte : i_keywordValue)
211             {
212                 l_oss << std::setfill('0') << std::setw(2) << std::hex
213                       << static_cast<int>(l_byte);
214             }
215         }
216     }
217     catch (const std::exception& l_ex)
218     {
219         o_errCode = error_code::STANDARD_EXCEPTION;
220     }
221 
222     return l_oss.str();
223 }
224 
225 /**
226  * @brief API to get data in binary format.
227  *
228  * This API converts given string value present in hexadecimal or decimal format
229  * into array of binary data.
230  *
231  * @param[in] i_value - Input data.
232  * @param[out] o_errCode - To set error code in case of error.
233  *
234  * @return - Array of binary data on success, empty vector in case of error.
235  */
convertToBinary(const std::string & i_value,uint16_t & o_errCode)236 inline types::BinaryVector convertToBinary(const std::string& i_value,
237                                            uint16_t& o_errCode)
238 {
239     o_errCode = 0;
240     types::BinaryVector l_binaryValue{};
241 
242     if (i_value.empty())
243     {
244         o_errCode = error_code::INVALID_INPUT_PARAMETER;
245         return l_binaryValue;
246     }
247 
248     try
249     {
250         if (i_value.substr(0, 2).compare("0x") == constants::STR_CMP_SUCCESS)
251         {
252             if (i_value.length() % 2 != 0)
253             {
254                 o_errCode = error_code::INVALID_HEXADECIMAL_VALUE_LENGTH;
255                 return l_binaryValue;
256             }
257 
258             auto l_value = i_value.substr(2);
259 
260             if (l_value.empty())
261             {
262                 o_errCode = error_code::INVALID_HEXADECIMAL_VALUE;
263                 return l_binaryValue;
264             }
265 
266             if (l_value.find_first_not_of("0123456789abcdefABCDEF") !=
267                 std::string::npos)
268             {
269                 o_errCode = error_code::INVALID_HEXADECIMAL_VALUE;
270                 return l_binaryValue;
271             }
272 
273             for (size_t l_pos = 0; l_pos < l_value.length(); l_pos += 2)
274             {
275                 uint8_t l_byte = static_cast<uint8_t>(
276                     std::stoi(l_value.substr(l_pos, 2), nullptr, 16));
277                 l_binaryValue.push_back(l_byte);
278             }
279         }
280         else
281         {
282             l_binaryValue.assign(i_value.begin(), i_value.end());
283         }
284     }
285     catch (const std::exception& l_ex)
286     {
287         o_errCode = error_code::STANDARD_EXCEPTION;
288         Logger::getLoggerInstance()->logMessage(
289             "Error while trying to convert value [" + i_value +
290             "] to binary, error : " + getErrCodeMsg(o_errCode));
291     }
292 
293     return l_binaryValue;
294 }
295 
296 /**
297  * @brief API to get current time stamp since Epoch.
298  *
299  * @return time stamp in seconds.
300  */
getCurrentTimeSinceEpoch()301 inline size_t getCurrentTimeSinceEpoch() noexcept
302 {
303     // Use high_resolution_clock for better precision
304     const auto l_now = std::chrono::high_resolution_clock::now();
305     auto l_durationSinceEpoch = l_now.time_since_epoch();
306 
307     auto l_timeStampSeconds =
308         std::chrono::duration_cast<std::chrono::seconds>(l_durationSinceEpoch)
309             .count();
310     return static_cast<size_t>(l_timeStampSeconds);
311 }
312 
313 /**
314  * @brief API to check is field mode enabled.
315  *
316  * @return true, if field mode is enabled. otherwise false.
317  */
isFieldModeEnabled()318 inline bool isFieldModeEnabled() noexcept
319 {
320     try
321     {
322         uint16_t l_errCode = 0;
323         std::vector<std::string> l_cmdOutput =
324             executeCmd("/sbin/fw_printenv fieldmode", l_errCode);
325 
326         if (l_cmdOutput.size() > 0)
327         {
328             toLower(l_cmdOutput[0]);
329 
330             // Remove the new line character from the string.
331             l_cmdOutput[0].erase(l_cmdOutput[0].length() - 1);
332             return l_cmdOutput[0] == "fieldmode=true";
333         }
334         else if (l_errCode)
335         {
336             // ToDo : Remove log and set error code.
337             Logger::getLoggerInstance()->logMessage(
338                 "Failed to execute command, error : " +
339                 getErrCodeMsg(l_errCode));
340         }
341     }
342     catch (const std::exception& l_ex)
343     {}
344 
345     return false;
346 }
347 
348 /**
349  * @brief API to get VPD collection mode
350  *
351  * VPD collection mode can be hardware, mixed mode or file mode. This is
352  * determined by reading a u-boot variable.
353  *
354  * @param[out] o_errCode - To set error code in case of error.
355  *
356  * @return Hardware mode, mixed mode or file mode.
357  */
getVpdCollectionMode(uint16_t & o_errCode)358 inline types::VpdCollectionMode getVpdCollectionMode(
359     uint16_t& o_errCode) noexcept
360 {
361     o_errCode = 0;
362     types::VpdCollectionMode l_result{types::VpdCollectionMode::DEFAULT_MODE};
363     try
364     {
365         std::vector<std::string> l_cmdOutput =
366             commonUtility::executeCmd("/sbin/fw_printenv vpdmode", o_errCode);
367 
368         if (l_cmdOutput.size() > 0)
369         {
370             commonUtility::toLower(l_cmdOutput[0]);
371 
372             // Remove the new line character from the string.
373             l_cmdOutput[0].erase(l_cmdOutput[0].length() - 1);
374 
375             if (l_cmdOutput[0] == "vpdmode=hardware")
376             {
377                 l_result = types::VpdCollectionMode::HARDWARE_MODE;
378             }
379             else if (l_cmdOutput[0] == "vpdmode=mixed")
380             {
381                 l_result = types::VpdCollectionMode::MIXED_MODE;
382             }
383             else if (l_cmdOutput[0] == "vpdmode=file")
384             {
385                 l_result = types::VpdCollectionMode::FILE_MODE;
386             }
387         }
388     }
389     catch (const std::exception& l_ex)
390     {
391         o_errCode = error_code::STANDARD_EXCEPTION;
392     }
393 
394     return l_result;
395 }
396 
397 /**
398  * @brief API to get effective FRU path
399  *
400  * API to get effective VPD path for a FRU based on the VPD collection mode.
401  *
402  * @param[in] i_vpdCollectionMode - VPD collection mode.
403  * @param[in,out] io_fruPath - Path to the EEPROM file.
404  * @param[out] o_errCode - To set error code in case of error.
405  *
406  */
getEffectiveFruPath(const types::VpdCollectionMode & i_vpdCollectionMode,std::string & io_fruPath,uint16_t & o_errCode)407 inline void getEffectiveFruPath(
408     const types::VpdCollectionMode& i_vpdCollectionMode,
409     std::string& io_fruPath, uint16_t& o_errCode) noexcept
410 {
411     try
412     {
413         o_errCode = 0;
414         if (types::VpdCollectionMode::FILE_MODE == i_vpdCollectionMode)
415         {
416             io_fruPath.insert(0, constants::fileModeDirectoryPath);
417         }
418 
419         // For Hardware mode and mixed mode FRU path is considered as EEPROM
420         // path. No change is needed.
421 
422         // ToDo: Need to handle path for mixed mode, when mixed mode is fully
423         // implemented.
424     }
425     catch (std::exception& l_ex)
426     {
427         o_errCode = error_code::STANDARD_EXCEPTION;
428         Logger::getLoggerInstance()->logMessage(
429             "Error while getting effective path, reason: " +
430             std::string(l_ex.what()));
431     }
432 }
433 
434 } // namespace commonUtility
435 } // namespace vpd
436