blob: 6c28f5b7f98d39a31026b7b752887d574d4e1dfe [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#pragma once
2
3#include "constants.hpp"
Rekha Aparnac6159a22025-10-09 12:20:20 +05304#include "error_codes.hpp"
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05005#include "logger.hpp"
6
7#include <algorithm>
Sunny Srivastava995e1c22025-08-28 03:13:00 -05008#include <chrono>
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05009#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
36namespace vpd
37{
38
39namespace commonUtility
40{
Rekha Aparnac6159a22025-10-09 12:20:20 +053041/**
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 */
49inline 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
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050059/** @brief Return the hex representation of the incoming byte.
60 *
61 * @param [in] i_aByte - The input byte.
Rekha Aparnabffecd92025-04-08 13:01:41 -050062 * @returns Null character if input byte is out of bound else returns hex
63 * representation of the byte as a character.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050064 */
Rekha Aparnabffecd92025-04-08 13:01:41 -050065constexpr auto toHex(const size_t& i_aByte) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050066{
67 constexpr auto l_map = "0123456789abcdef";
Rekha Aparnabffecd92025-04-08 13:01:41 -050068
69 return (i_aByte < std::strlen(l_map)) ? l_map[i_aByte] : '\0';
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050070}
71
72/**
73 * @brief API to return null at the end of variadic template args.
74 *
75 * @return empty string.
76 */
77inline 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 */
88template <typename T, typename... Types>
89inline 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
Rekha Aparna719093d2025-11-13 03:13:12 -0600102 * @param[out] o_errCode - To set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500103 * @returns output of that command
104 */
105template <typename T, typename... Types>
Rekha Aparna719093d2025-11-13 03:13:12 -0600106inline std::vector<std::string> executeCmd(T&& i_path, uint16_t& o_errCode,
107 Types... i_args)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500108{
Rekha Aparna719093d2025-11-13 03:13:12 -0600109 o_errCode = 0;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500110 std::vector<std::string> l_cmdOutput;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500111
Rekha Aparna719093d2025-11-13 03:13:12 -0600112 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500113 {
Rekha Aparna719093d2025-11-13 03:13:12 -0600114 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 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500134 }
Rekha Aparna719093d2025-11-13 03:13:12 -0600135 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500136 {
Rekha Aparna719093d2025-11-13 03:13:12 -0600137 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()));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500141 }
142
143 return l_cmdOutput;
144}
145
146/** @brief Converts string to lower case.
147 *
148 * @param [in] i_string - Input string.
149 */
150inline 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}
Anupama B R6f142832025-04-11 04:17:49 -0500155
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 */
166inline 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}
Souvik Royaf528982025-07-16 13:50:18 -0500179
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 *
189 * @throw - Throws std::bad_alloc or std::terminate in case of error.
190 *
191 * @return - Returns the converted string value.
192 */
193inline std::string getPrintableValue(const types::BinaryVector& i_keywordValue)
194{
195 bool l_allPrintable =
196 std::all_of(i_keywordValue.begin(), i_keywordValue.end(),
197 [](const auto& l_byte) { return std::isprint(l_byte); });
198
199 std::ostringstream l_oss;
200 if (l_allPrintable)
201 {
202 l_oss << std::string(i_keywordValue.begin(), i_keywordValue.end());
203 }
204 else
205 {
206 l_oss << "0x";
207 for (const auto& l_byte : i_keywordValue)
208 {
209 l_oss << std::setfill('0') << std::setw(2) << std::hex
210 << static_cast<int>(l_byte);
211 }
212 }
213
214 return l_oss.str();
215}
216
217/**
218 * @brief API to get data in binary format.
219 *
220 * This API converts given string value present in hexadecimal or decimal format
221 * into array of binary data.
222 *
223 * @param[in] i_value - Input data.
224 *
225 * @return - Array of binary data on success, throws as exception in case
226 * of any error.
227 *
228 * @throw std::runtime_error, std::out_of_range, std::bad_alloc,
229 * std::invalid_argument
230 */
231inline types::BinaryVector convertToBinary(const std::string& i_value)
232{
233 if (i_value.empty())
234 {
235 throw std::runtime_error("Empty input provided");
236 }
237
238 types::BinaryVector l_binaryValue{};
239
240 if (i_value.substr(0, 2).compare("0x") == constants::STR_CMP_SUCCESS)
241 {
242 if (i_value.length() % 2 != 0)
243 {
244 throw std::runtime_error(
245 "Write option accepts 2 digit hex numbers. (Ex. 0x1 "
246 "should be given as 0x01).");
247 }
248
249 auto l_value = i_value.substr(2);
250
251 if (l_value.empty())
252 {
253 throw std::runtime_error(
254 "Provide a valid hexadecimal input. (Ex. 0x30313233)");
255 }
256
257 if (l_value.find_first_not_of("0123456789abcdefABCDEF") !=
258 std::string::npos)
259 {
260 throw std::runtime_error("Provide a valid hexadecimal input.");
261 }
262
263 for (size_t l_pos = 0; l_pos < l_value.length(); l_pos += 2)
264 {
265 uint8_t l_byte = static_cast<uint8_t>(
266 std::stoi(l_value.substr(l_pos, 2), nullptr, 16));
267 l_binaryValue.push_back(l_byte);
268 }
269 }
270 else
271 {
272 l_binaryValue.assign(i_value.begin(), i_value.end());
273 }
274 return l_binaryValue;
275}
276
Sunny Srivastava995e1c22025-08-28 03:13:00 -0500277/**
278 * @brief API to get current time stamp since Epoch.
279 *
280 * @return time stamp in seconds.
281 */
282inline size_t getCurrentTimeSinceEpoch() noexcept
283{
284 // Use high_resolution_clock for better precision
285 const auto l_now = std::chrono::high_resolution_clock::now();
286 auto l_durationSinceEpoch = l_now.time_since_epoch();
287
288 auto l_timeStampSeconds =
289 std::chrono::duration_cast<std::chrono::seconds>(l_durationSinceEpoch)
290 .count();
291 return static_cast<size_t>(l_timeStampSeconds);
292}
293
Souvik Royc4fa6182025-10-16 08:56:35 +0000294/**
295 * @brief API to check is field mode enabled.
296 *
297 * @return true, if field mode is enabled. otherwise false.
298 */
299inline bool isFieldModeEnabled() noexcept
300{
301 try
302 {
Rekha Aparna719093d2025-11-13 03:13:12 -0600303 uint16_t l_errCode = 0;
Souvik Royc4fa6182025-10-16 08:56:35 +0000304 std::vector<std::string> l_cmdOutput =
Rekha Aparna719093d2025-11-13 03:13:12 -0600305 executeCmd("/sbin/fw_printenv fieldmode", l_errCode);
Souvik Royc4fa6182025-10-16 08:56:35 +0000306
307 if (l_cmdOutput.size() > 0)
308 {
309 toLower(l_cmdOutput[0]);
310
311 // Remove the new line character from the string.
312 l_cmdOutput[0].erase(l_cmdOutput[0].length() - 1);
Souvik Royc4fa6182025-10-16 08:56:35 +0000313 return l_cmdOutput[0] == "fieldmode=true";
314 }
Rekha Aparna719093d2025-11-13 03:13:12 -0600315 else if (l_errCode)
316 {
317 // ToDo : Remove log and set error code.
318 Logger::getLoggerInstance()->logMessage(
319 "Failed to execute command, error : " +
320 getErrCodeMsg(l_errCode));
321 }
Souvik Royc4fa6182025-10-16 08:56:35 +0000322 }
323 catch (const std::exception& l_ex)
324 {}
325
326 return false;
327}
328
Souvik Royc1171732025-10-16 09:27:38 +0000329/**
330 * @brief API to get VPD collection mode
331 *
332 * VPD collection mode can be hardware, mixed mode or file mode. This is
333 * determined by reading a u-boot variable.
334 *
335 * @param[out] o_errCode - To set error code in case of error.
336 *
337 * @return Hardware mode, mixed mode or file mode.
338 */
339inline types::VpdCollectionMode getVpdCollectionMode(
340 uint16_t& o_errCode) noexcept
341{
Rekha Aparna719093d2025-11-13 03:13:12 -0600342 o_errCode = 0;
Souvik Royc1171732025-10-16 09:27:38 +0000343 types::VpdCollectionMode l_result{types::VpdCollectionMode::DEFAULT_MODE};
344 try
345 {
346 std::vector<std::string> l_cmdOutput =
Rekha Aparna719093d2025-11-13 03:13:12 -0600347 commonUtility::executeCmd("/sbin/fw_printenv vpdmode", o_errCode);
Souvik Royc1171732025-10-16 09:27:38 +0000348
349 if (l_cmdOutput.size() > 0)
350 {
351 commonUtility::toLower(l_cmdOutput[0]);
352
353 // Remove the new line character from the string.
354 l_cmdOutput[0].erase(l_cmdOutput[0].length() - 1);
355
356 if (l_cmdOutput[0] == "vpdmode=hardware")
357 {
358 l_result = types::VpdCollectionMode::HARDWARE_MODE;
359 }
360 else if (l_cmdOutput[0] == "vpdmode=mixed")
361 {
362 l_result = types::VpdCollectionMode::MIXED_MODE;
363 }
364 else if (l_cmdOutput[0] == "vpdmode=file")
365 {
366 l_result = types::VpdCollectionMode::FILE_MODE;
367 }
368 }
369 }
370 catch (const std::exception& l_ex)
371 {
372 o_errCode = error_code::STANDARD_EXCEPTION;
373 }
374
375 return l_result;
376}
377
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500378} // namespace commonUtility
379} // namespace vpd