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