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