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