1 #pragma once
2
3 #include "tool_constants.hpp"
4 #include "tool_types.hpp"
5
6 #include <nlohmann/json.hpp>
7 #include <sdbusplus/bus.hpp>
8 #include <sdbusplus/exception.hpp>
9
10 #include <cctype>
11 #include <fstream>
12 #include <iostream>
13
14 namespace vpd
15 {
16 namespace utils
17 {
18 /**
19 * @brief An API to read property from Dbus.
20 *
21 * API reads the property value for the specified interface and object path from
22 * the given Dbus service.
23 *
24 * The caller of the API needs to validate the validity and correctness of the
25 * type and value of data returned. The API will just fetch and return the data
26 * without any data validation.
27 *
28 * Note: It will be caller's responsibility to check for empty value returned
29 * and generate appropriate error if required.
30 *
31 * @param[in] i_serviceName - Name of the Dbus service.
32 * @param[in] i_objectPath - Object path under the service.
33 * @param[in] i_interface - Interface under which property exist.
34 * @param[in] i_property - Property whose value is to be read.
35 *
36 * @return - Value read from Dbus.
37 *
38 * @throw std::runtime_error
39 */
readDbusProperty(const std::string & i_serviceName,const std::string & i_objectPath,const std::string & i_interface,const std::string & i_property)40 inline types::DbusVariantType readDbusProperty(
41 const std::string& i_serviceName, const std::string& i_objectPath,
42 const std::string& i_interface, const std::string& i_property)
43 {
44 types::DbusVariantType l_propertyValue;
45
46 // Mandatory fields to make a dbus call.
47 if (i_serviceName.empty() || i_objectPath.empty() || i_interface.empty() ||
48 i_property.empty())
49 {
50 // TODO: Enable logging when verbose is enabled.
51 /*std::cout << "One of the parameter to make Dbus read call is empty."
52 << std::endl;*/
53 throw std::runtime_error("Empty Parameter");
54 }
55
56 try
57 {
58 auto l_bus = sdbusplus::bus::new_default();
59 auto l_method =
60 l_bus.new_method_call(i_serviceName.c_str(), i_objectPath.c_str(),
61 "org.freedesktop.DBus.Properties", "Get");
62 l_method.append(i_interface, i_property);
63
64 auto result = l_bus.call(l_method);
65 result.read(l_propertyValue);
66 }
67 catch (const sdbusplus::exception::SdBusError& l_ex)
68 {
69 // TODO: Enable logging when verbose is enabled.
70 // std::cout << std::string(l_ex.what()) << std::endl;
71 throw std::runtime_error(std::string(l_ex.what()));
72 }
73 return l_propertyValue;
74 }
75
76 /**
77 * @brief An API to get property map for an interface.
78 *
79 * This API returns a map of property and its value with respect to a particular
80 * interface.
81 *
82 * Note: It will be caller's responsibility to check for empty map returned and
83 * generate appropriate error.
84 *
85 * @param[in] i_service - Service name.
86 * @param[in] i_objectPath - object path.
87 * @param[in] i_interface - Interface, for the properties to be listed.
88 *
89 * @return - A map of property and value of an interface, if success.
90 * if failed, empty map.
91 */
getPropertyMap(const std::string & i_service,const std::string & i_objectPath,const std::string & i_interface)92 inline types::PropertyMap getPropertyMap(
93 const std::string& i_service, const std::string& i_objectPath,
94 const std::string& i_interface) noexcept
95 {
96 types::PropertyMap l_propertyValueMap;
97 if (i_service.empty() || i_objectPath.empty() || i_interface.empty())
98 {
99 // TODO: Enable logging when verbose is enabled.
100 // std::cout << "Invalid parameters to get property map" << std::endl;
101 return l_propertyValueMap;
102 }
103
104 try
105 {
106 auto l_bus = sdbusplus::bus::new_default();
107 auto l_method =
108 l_bus.new_method_call(i_service.c_str(), i_objectPath.c_str(),
109 "org.freedesktop.DBus.Properties", "GetAll");
110 l_method.append(i_interface);
111 auto l_result = l_bus.call(l_method);
112 l_result.read(l_propertyValueMap);
113 }
114 catch (const sdbusplus::exception::SdBusError& l_ex)
115 {
116 // TODO: Enable logging when verbose is enabled.
117 // std::cerr << "Failed to get property map for service: [" << i_service
118 // << "], object path: [" << i_objectPath
119 // << "] Error : " << l_ex.what() << std::endl;
120 }
121
122 return l_propertyValueMap;
123 }
124
125 /**
126 * @brief An API to print json data on stdout.
127 *
128 * @param[in] i_jsonData - JSON object.
129 */
printJson(const nlohmann::json & i_jsonData)130 inline void printJson(const nlohmann::json& i_jsonData)
131 {
132 try
133 {
134 std::cout << i_jsonData.dump(constants::INDENTATION) << std::endl;
135 }
136 catch (const nlohmann::json::type_error& l_ex)
137 {
138 throw std::runtime_error(
139 "Failed to dump JSON data, error: " + std::string(l_ex.what()));
140 }
141 }
142
143 /**
144 * @brief An API to convert binary value into ascii/hex representation.
145 *
146 * If given data contains printable characters, ASCII formated string value of
147 * the input data will be returned. Otherwise if the data has any non-printable
148 * value, returns the hex represented value of the given data in string format.
149 *
150 * @param[in] i_keywordValue - Data in binary format.
151 *
152 * @throw - Throws std::bad_alloc or std::terminate in case of error.
153 *
154 * @return - Returns the converted string value.
155 */
getPrintableValue(const types::BinaryVector & i_keywordValue)156 inline std::string getPrintableValue(const types::BinaryVector& i_keywordValue)
157 {
158 bool l_allPrintable =
159 std::all_of(i_keywordValue.begin(), i_keywordValue.end(),
160 [](const auto& l_byte) { return std::isprint(l_byte); });
161
162 std::ostringstream l_oss;
163 if (l_allPrintable)
164 {
165 l_oss << std::string(i_keywordValue.begin(), i_keywordValue.end());
166 }
167 else
168 {
169 l_oss << "0x";
170 for (const auto& l_byte : i_keywordValue)
171 {
172 l_oss << std::setfill('0') << std::setw(2) << std::hex
173 << static_cast<int>(l_byte);
174 }
175 }
176
177 return l_oss.str();
178 }
179
180 /**
181 * @brief API to read keyword's value from hardware.
182 *
183 * This API reads keyword's value by requesting DBus service(vpd-manager) who
184 * hosts the 'ReadKeyword' method to read keyword's value.
185 *
186 * @param[in] i_eepromPath - EEPROM file path.
187 * @param[in] i_paramsToReadData - Property whose value has to be read.
188 *
189 * @return - Value read from hardware
190 *
191 * @throw std::runtime_error, sdbusplus::exception::SdBusError
192 */
readKeywordFromHardware(const std::string & i_eepromPath,const types::ReadVpdParams i_paramsToReadData)193 inline types::DbusVariantType readKeywordFromHardware(
194 const std::string& i_eepromPath,
195 const types::ReadVpdParams i_paramsToReadData)
196 {
197 if (i_eepromPath.empty())
198 {
199 throw std::runtime_error("Empty EEPROM path");
200 }
201
202 try
203 {
204 types::DbusVariantType l_propertyValue;
205
206 auto l_bus = sdbusplus::bus::new_default();
207
208 auto l_method = l_bus.new_method_call(
209 constants::vpdManagerService, constants::vpdManagerObjectPath,
210 constants::vpdManagerInfName, "ReadKeyword");
211
212 l_method.append(i_eepromPath, i_paramsToReadData);
213 auto l_result = l_bus.call(l_method);
214
215 l_result.read(l_propertyValue);
216
217 return l_propertyValue;
218 }
219 catch (const sdbusplus::exception::SdBusError& l_error)
220 {
221 throw;
222 }
223 }
224
225 /**
226 * @brief API to save keyword's value on file.
227 *
228 * API writes keyword's value on the given file path. If the data is in hex
229 * format, API strips '0x' and saves the value on the given file.
230 *
231 * @param[in] i_filePath - File path.
232 * @param[in] i_keywordValue - Keyword's value.
233 *
234 * @return - true on successfully writing to file, false otherwise.
235 */
saveToFile(const std::string & i_filePath,const std::string & i_keywordValue)236 inline bool saveToFile(const std::string& i_filePath,
237 const std::string& i_keywordValue)
238 {
239 bool l_returnStatus = false;
240
241 if (i_keywordValue.empty())
242 {
243 // ToDo: log only when verbose is enabled
244 std::cerr << "Save to file[ " << i_filePath
245 << "] failed, reason: Empty keyword's value received"
246 << std::endl;
247 return l_returnStatus;
248 }
249
250 std::string l_keywordValue{i_keywordValue};
251 if (i_keywordValue.substr(0, 2).compare("0x") == constants::STR_CMP_SUCCESS)
252 {
253 l_keywordValue = i_keywordValue.substr(2);
254 }
255
256 std::ofstream l_outPutFileStream;
257 l_outPutFileStream.exceptions(
258 std::ifstream::badbit | std::ifstream::failbit);
259 try
260 {
261 l_outPutFileStream.open(i_filePath);
262
263 if (l_outPutFileStream.is_open())
264 {
265 l_outPutFileStream.write(l_keywordValue.c_str(),
266 l_keywordValue.size());
267 l_returnStatus = true;
268 }
269 else
270 {
271 // ToDo: log only when verbose is enabled
272 std::cerr << "Error opening output file " << i_filePath
273 << std::endl;
274 }
275 }
276 catch (const std::ios_base::failure& l_ex)
277 {
278 // ToDo: log only when verbose is enabled
279 std::cerr
280 << "Failed to write to file: " << i_filePath
281 << ", either base folder path doesn't exist or internal error occured, error: "
282 << l_ex.what() << '\n';
283 }
284
285 return l_returnStatus;
286 }
287
288 /**
289 * @brief API to print data in JSON format on console
290 *
291 * @param[in] i_fruPath - FRU path.
292 * @param[in] i_keywordName - Keyword name.
293 * @param[in] i_keywordStrValue - Keyword's value.
294 */
displayOnConsole(const std::string & i_fruPath,const std::string & i_keywordName,const std::string & i_keywordStrValue)295 inline void displayOnConsole(const std::string& i_fruPath,
296 const std::string& i_keywordName,
297 const std::string& i_keywordStrValue)
298 {
299 nlohmann::json l_resultInJson = nlohmann::json::object({});
300 nlohmann::json l_keywordValInJson = nlohmann::json::object({});
301
302 l_keywordValInJson.emplace(i_keywordName, i_keywordStrValue);
303 l_resultInJson.emplace(i_fruPath, l_keywordValInJson);
304
305 printJson(l_resultInJson);
306 }
307
308 /**
309 * @brief API to write keyword's value.
310 *
311 * This API writes keyword's value by requesting DBus service(vpd-manager) who
312 * hosts the 'UpdateKeyword' method to update keyword's value.
313 *
314 * @param[in] i_vpdPath - EEPROM or object path, where keyword is present.
315 * @param[in] i_paramsToWriteData - Data required to update keyword's value.
316 *
317 * @return - Number of bytes written on success, -1 on failure.
318 *
319 * @throw - std::runtime_error, sdbusplus::exception::SdBusError
320 */
writeKeyword(const std::string & i_vpdPath,const types::WriteVpdParams & i_paramsToWriteData)321 inline int writeKeyword(const std::string& i_vpdPath,
322 const types::WriteVpdParams& i_paramsToWriteData)
323 {
324 if (i_vpdPath.empty())
325 {
326 throw std::runtime_error("Empty path");
327 }
328
329 int l_rc = constants::FAILURE;
330 auto l_bus = sdbusplus::bus::new_default();
331
332 auto l_method = l_bus.new_method_call(
333 constants::vpdManagerService, constants::vpdManagerObjectPath,
334 constants::vpdManagerInfName, "UpdateKeyword");
335
336 l_method.append(i_vpdPath, i_paramsToWriteData);
337 auto l_result = l_bus.call(l_method);
338
339 l_result.read(l_rc);
340 return l_rc;
341 }
342
343 /**
344 * @brief API to write keyword's value on hardware.
345 *
346 * This API writes keyword's value by requesting DBus service(vpd-manager) who
347 * hosts the 'WriteKeywordOnHardware' method to update keyword's value.
348 *
349 * Note: This API updates keyword's value only on the given hardware path, any
350 * backup or redundant EEPROM (if exists) paths won't get updated.
351 *
352 * @param[in] i_eepromPath - EEPROM where keyword is present.
353 * @param[in] i_paramsToWriteData - Data required to update keyword's value.
354 *
355 * @return - Number of bytes written on success, -1 on failure.
356 *
357 * @throw - std::runtime_error, sdbusplus::exception::SdBusError
358 */
writeKeywordOnHardware(const std::string & i_eepromPath,const types::WriteVpdParams & i_paramsToWriteData)359 inline int writeKeywordOnHardware(
360 const std::string& i_eepromPath,
361 const types::WriteVpdParams& i_paramsToWriteData)
362 {
363 if (i_eepromPath.empty())
364 {
365 throw std::runtime_error("Empty path");
366 }
367
368 int l_rc = constants::FAILURE;
369 auto l_bus = sdbusplus::bus::new_default();
370
371 auto l_method = l_bus.new_method_call(
372 constants::vpdManagerService, constants::vpdManagerObjectPath,
373 constants::vpdManagerInfName, "WriteKeywordOnHardware");
374
375 l_method.append(i_eepromPath, i_paramsToWriteData);
376 auto l_result = l_bus.call(l_method);
377
378 l_result.read(l_rc);
379
380 return l_rc;
381 }
382
383 /**
384 * @brief API to get data in binary format.
385 *
386 * This API converts given string value into array of binary data.
387 *
388 * @param[in] i_value - Input data.
389 *
390 * @return - Array of binary data on success, throws as exception in case
391 * of any error.
392 *
393 * @throw std::runtime_error, std::out_of_range, std::bad_alloc,
394 * std::invalid_argument
395 */
convertToBinary(const std::string & i_value)396 inline types::BinaryVector convertToBinary(const std::string& i_value)
397 {
398 if (i_value.empty())
399 {
400 throw std::runtime_error(
401 "Provide a valid hexadecimal input. (Ex. 0x30313233)");
402 }
403
404 std::vector<uint8_t> l_binaryValue{};
405
406 if (i_value.substr(0, 2).compare("0x") == constants::STR_CMP_SUCCESS)
407 {
408 if (i_value.length() % 2 != 0)
409 {
410 throw std::runtime_error(
411 "Write option accepts 2 digit hex numbers. (Ex. 0x1 "
412 "should be given as 0x01).");
413 }
414
415 auto l_value = i_value.substr(2);
416
417 if (l_value.empty())
418 {
419 throw std::runtime_error(
420 "Provide a valid hexadecimal input. (Ex. 0x30313233)");
421 }
422
423 if (l_value.find_first_not_of("0123456789abcdefABCDEF") !=
424 std::string::npos)
425 {
426 throw std::runtime_error("Provide a valid hexadecimal input.");
427 }
428
429 for (size_t l_pos = 0; l_pos < l_value.length(); l_pos += 2)
430 {
431 uint8_t l_byte = static_cast<uint8_t>(
432 std::stoi(l_value.substr(l_pos, 2), nullptr, 16));
433 l_binaryValue.push_back(l_byte);
434 }
435 }
436 else
437 {
438 l_binaryValue.assign(i_value.begin(), i_value.end());
439 }
440 return l_binaryValue;
441 }
442
443 /**
444 * @brief API to parse respective JSON.
445 *
446 * @param[in] i_pathToJson - Path to JSON.
447 *
448 * @return Parsed JSON, throws exception in case of error.
449 *
450 * @throw std::runtime_error
451 */
getParsedJson(const std::string & i_pathToJson)452 inline nlohmann::json getParsedJson(const std::string& i_pathToJson)
453 {
454 if (i_pathToJson.empty())
455 {
456 throw std::runtime_error("Path to JSON is missing");
457 }
458
459 std::error_code l_ec;
460 if (!std::filesystem::exists(i_pathToJson, l_ec))
461 {
462 std::string l_message{
463 "file system call failed for file: " + i_pathToJson};
464
465 if (l_ec)
466 {
467 l_message += ", error: " + l_ec.message();
468 }
469 throw std::runtime_error(l_message);
470 }
471
472 if (std::filesystem::is_empty(i_pathToJson, l_ec))
473 {
474 throw std::runtime_error("Empty file: " + i_pathToJson);
475 }
476 else if (l_ec)
477 {
478 throw std::runtime_error("is_empty file system call failed for file: " +
479 i_pathToJson + ", error: " + l_ec.message());
480 }
481
482 std::ifstream l_jsonFile(i_pathToJson);
483 if (!l_jsonFile)
484 {
485 throw std::runtime_error("Failed to access Json path: " + i_pathToJson);
486 }
487
488 try
489 {
490 return nlohmann::json::parse(l_jsonFile);
491 }
492 catch (const nlohmann::json::parse_error& l_ex)
493 {
494 throw std::runtime_error("Failed to parse JSON file: " + i_pathToJson);
495 }
496 }
497
498 /**
499 * @brief API to get list of interfaces under a given object path.
500 *
501 * Given a DBus object path, this API returns a map of service -> implemented
502 * interface(s) under that object path. This API calls DBus method GetObject
503 * hosted by ObjectMapper DBus service.
504 *
505 * @param[in] i_objectPath - DBus object path.
506 * @param[in] i_constrainingInterfaces - An array of result set constraining
507 * interfaces.
508 *
509 * @return On success, returns a map of service -> implemented interface(s),
510 * else returns an empty map. The caller of this
511 * API should check for empty map.
512 */
GetServiceInterfacesForObject(const std::string & i_objectPath,const std::vector<std::string> & i_constrainingInterfaces)513 inline types::MapperGetObject GetServiceInterfacesForObject(
514 const std::string& i_objectPath,
515 const std::vector<std::string>& i_constrainingInterfaces) noexcept
516 {
517 types::MapperGetObject l_serviceInfMap;
518 if (i_objectPath.empty())
519 {
520 // TODO: log only when verbose is enabled
521 std::cerr << "Object path is empty." << std::endl;
522 return l_serviceInfMap;
523 }
524
525 try
526 {
527 auto l_bus = sdbusplus::bus::new_default();
528 auto l_method = l_bus.new_method_call(
529 constants::objectMapperService, constants::objectMapperObjectPath,
530 constants::objectMapperInfName, "GetObject");
531
532 l_method.append(i_objectPath, i_constrainingInterfaces);
533
534 auto l_result = l_bus.call(l_method);
535 l_result.read(l_serviceInfMap);
536 }
537 catch (const sdbusplus::exception::SdBusError& l_ex)
538 {
539 // TODO: log only when verbose is enabled
540 std::cerr << std::string(l_ex.what()) << std::endl;
541 }
542 return l_serviceInfMap;
543 }
544
545 /** @brief API to get list of sub tree paths for a given object path
546 *
547 * Given a DBus object path, this API returns a list of object paths under that
548 * object path in the DBus tree. This API calls DBus method GetSubTreePaths
549 * hosted by ObjectMapper DBus service.
550 *
551 * @param[in] i_objectPath - DBus object path.
552 * @param[in] i_constrainingInterfaces - An array of result set constraining
553 * interfaces.
554 * @param[in] i_depth - The maximum subtree depth for which results should be
555 * fetched. For unconstrained fetches use a depth of zero.
556 *
557 * @return On success, returns a std::vector<std::string> of object paths in
558 * Phosphor Inventory Manager DBus service's tree, else returns an empty vector.
559 * The caller of this API should check for empty vector.
560 */
GetSubTreePaths(const std::string i_objectPath,const int i_depth=0,const std::vector<std::string> & i_constrainingInterfaces={})561 inline std::vector<std::string> GetSubTreePaths(
562 const std::string i_objectPath, const int i_depth = 0,
563 const std::vector<std::string>& i_constrainingInterfaces = {}) noexcept
564 {
565 std::vector<std::string> l_objectPaths;
566
567 try
568 {
569 auto l_bus = sdbusplus::bus::new_default();
570 auto l_method = l_bus.new_method_call(
571 constants::objectMapperService, constants::objectMapperObjectPath,
572 constants::objectMapperInfName, "GetSubTreePaths");
573
574 l_method.append(i_objectPath, i_depth, i_constrainingInterfaces);
575
576 auto l_result = l_bus.call(l_method);
577 l_result.read(l_objectPaths);
578 }
579 catch (const sdbusplus::exception::SdBusError& l_ex)
580 {
581 // TODO: log only when verbose is enabled
582 std::cerr << std::string(l_ex.what()) << std::endl;
583 }
584 return l_objectPaths;
585 }
586
587 /**
588 * @brief A class to print data in tabular format
589 *
590 * This class implements methods to print data in a two dimensional tabular
591 * format. All entries in the table must be in string format.
592 *
593 */
594 class Table
595 {
596 class Column : public types::TableColumnNameSizePair
597 {
598 public:
599 /**
600 * @brief API to get the name of the Column
601 *
602 * @return Name of the Column.
603 */
Name() const604 const std::string& Name() const
605 {
606 return this->first;
607 }
608
609 /**
610 * @brief API to get the width of the Column
611 *
612 * @return Width of the Column.
613 */
Width() const614 std::size_t Width() const
615 {
616 return this->second;
617 }
618 };
619
620 // Current width of the table
621 std::size_t m_currentWidth;
622
623 // Character to be used as fill character between entries
624 char m_fillCharacter;
625
626 // Separator character to be used between columns
627 char m_separator;
628
629 // Array of columns
630 std::vector<Column> m_columns;
631
632 /**
633 * @brief API to Print Header
634 *
635 * Header line prints the names of the Column headers separated by the
636 * specified separator character and spaced accordingly.
637 *
638 * @throw std::out_of_range, std::length_error, std::bad_alloc
639 */
PrintHeader() const640 void PrintHeader() const
641 {
642 for (const auto& l_column : m_columns)
643 {
644 PrintEntry(l_column.Name(), l_column.Width());
645 }
646 std::cout << m_separator << std::endl;
647 }
648
649 /**
650 * @brief API to Print Horizontal Line
651 *
652 * A horizontal line is a sequence of '*'s.
653 *
654 * @throw std::out_of_range, std::length_error, std::bad_alloc
655 */
PrintHorizontalLine() const656 void PrintHorizontalLine() const
657 {
658 std::cout << std::string(m_currentWidth, '*') << std::endl;
659 }
660
661 /**
662 * @brief API to print an entry in the table
663 *
664 * An entry is a separator character followed by the text to print.
665 * The text is centre-aligned.
666 *
667 * @param[in] i_text - text to print
668 * @param[in] i_columnWidth - width of the column
669 *
670 * @throw std::out_of_range, std::length_error, std::bad_alloc
671 */
PrintEntry(const std::string & i_text,std::size_t i_columnWidth) const672 void PrintEntry(const std::string& i_text, std::size_t i_columnWidth) const
673 {
674 const std::size_t l_textLength{i_text.length()};
675
676 constexpr std::size_t l_minFillChars{3};
677 const std::size_t l_numFillChars =
678 ((l_textLength >= i_columnWidth ? l_minFillChars
679 : i_columnWidth - l_textLength)) -
680 1; // -1 for the separator character
681
682 const unsigned l_oddFill = l_numFillChars % 2;
683
684 std::cout << m_separator
685 << std::string((l_numFillChars / 2) + l_oddFill,
686 m_fillCharacter)
687 << i_text << std::string(l_numFillChars / 2, m_fillCharacter);
688 }
689
690 public:
691 /**
692 * @brief Table Constructor
693 *
694 * Parameterized constructor for a Table object
695 *
696 */
Table(const char i_fillCharacter=' ',const char i_separator='|')697 constexpr explicit Table(const char i_fillCharacter = ' ',
698 const char i_separator = '|') noexcept :
699 m_currentWidth{0}, m_fillCharacter{i_fillCharacter},
700 m_separator{i_separator}
701 {}
702
703 // deleted methods
704 Table(const Table&) = delete;
705 Table operator=(const Table&) = delete;
706 Table(const Table&&) = delete;
707 Table operator=(const Table&&) = delete;
708
709 ~Table() = default;
710
711 /**
712 * @brief API to add column to Table
713 *
714 * @param[in] i_name - Name of the column.
715 *
716 * @param[in] i_width - Width to allocate for the column.
717 *
718 * @return On success returns 0, otherwise returns -1.
719 */
AddColumn(const std::string & i_name,std::size_t i_width)720 int AddColumn(const std::string& i_name, std::size_t i_width)
721 {
722 if (i_width < i_name.length())
723 return constants::FAILURE;
724 m_columns.emplace_back(types::TableColumnNameSizePair(i_name, i_width));
725 m_currentWidth += i_width;
726 return constants::SUCCESS;
727 }
728
729 /**
730 * @brief API to print the Table to console.
731 *
732 * This API prints the table data to console.
733 *
734 * @param[in] i_tableData - The data to be printed.
735 *
736 * @return On success returns 0, otherwise returns -1.
737 *
738 * @throw std::out_of_range, std::length_error, std::bad_alloc
739 */
Print(const types::TableInputData & i_tableData) const740 int Print(const types::TableInputData& i_tableData) const
741 {
742 PrintHorizontalLine();
743 PrintHeader();
744 PrintHorizontalLine();
745
746 // print the table data
747 for (const auto& l_row : i_tableData)
748 {
749 unsigned l_columnNumber{0};
750
751 // number of columns in input data is greater than the number of
752 // columns specified in Table
753 if (l_row.size() > m_columns.size())
754 {
755 return constants::FAILURE;
756 }
757
758 for (const auto& l_entry : l_row)
759 {
760 PrintEntry(l_entry, m_columns[l_columnNumber].Width());
761
762 ++l_columnNumber;
763 }
764 std::cout << m_separator << std::endl;
765 }
766 PrintHorizontalLine();
767 return constants::SUCCESS;
768 }
769 };
770
771 /**
772 * @brief API to read value from file.
773 *
774 * The API reads the file and returns the read value.
775 *
776 * @param[in] i_filePath - File path.
777 *
778 * @return - Data from file if any in string format, else empty string.
779 *
780 */
readValueFromFile(const std::string & i_filePath)781 inline std::string readValueFromFile(const std::string& i_filePath)
782 {
783 std::string l_valueRead{};
784
785 std::error_code l_ec;
786 if (!std::filesystem::exists(i_filePath, l_ec))
787 {
788 std::string l_message{
789 "filesystem call exists failed for file [" + i_filePath + "]."};
790
791 if (l_ec)
792 {
793 l_message += " Error: " + l_ec.message();
794 }
795
796 std::cerr << l_message << std::endl;
797 return l_valueRead;
798 }
799
800 if (std::filesystem::is_empty(i_filePath, l_ec))
801 {
802 std::cerr << "File[" << i_filePath << "] is empty." << std::endl;
803 return l_valueRead;
804 }
805 else if (l_ec)
806 {
807 std::cerr << "is_empty file system call failed for file[" << i_filePath
808 << "] , error: " << l_ec.message() << std::endl;
809
810 return l_valueRead;
811 }
812
813 std::ifstream l_fileStream;
814 l_fileStream.exceptions(std::ifstream::badbit | std::ifstream::failbit);
815 try
816 {
817 l_fileStream.open(i_filePath, std::ifstream::in);
818
819 std::getline(l_fileStream, l_valueRead);
820
821 l_fileStream.close();
822 return l_valueRead;
823 }
824 catch (const std::ifstream::failure& l_ex)
825 {
826 if (l_fileStream.is_open())
827 {
828 l_fileStream.close();
829 }
830
831 std::cerr << "File read operation failed for path[" << i_filePath
832 << "], error: " << l_ex.what() << std::endl;
833 }
834
835 return l_valueRead;
836 }
837
838 /**
839 * @brief API to check if chassis is powered off.
840 *
841 * This API queries Phosphor Chassis State Manager to know whether
842 * chassis is powered off.
843 *
844 * @return true if chassis is powered off, false otherwise.
845 */
isChassisPowerOff()846 inline bool isChassisPowerOff()
847 {
848 try
849 {
850 // ToDo: Handle in case system has multiple chassis
851 auto l_powerState = readDbusProperty(
852 constants::chassisStateManagerService,
853 constants::chassisStateManagerObjectPath,
854 constants::chassisStateManagerInfName, "CurrentPowerState");
855
856 if (auto l_curPowerState = std::get_if<std::string>(&l_powerState);
857 l_curPowerState &&
858 ("xyz.openbmc_project.State.Chassis.PowerState.Off" ==
859 *l_curPowerState))
860 {
861 return true;
862 }
863 }
864 catch (const std::exception& l_ex)
865 {
866 // Todo: Enale log when verbose is enabled
867 std::cerr << l_ex.what() << std::endl;
868 }
869 return false;
870 }
871
872 /**
873 * @brief API to check if a D-Bus service is running or not.
874 *
875 * Any failure in calling the method "NameHasOwner" implies that the service
876 * is not in a running state. Hence the API returns false in case of any
877 * exception as well.
878 *
879 * @param[in] i_serviceName - D-Bus service name whose status is to be
880 * checked.
881 * @return bool - True if the service is running, false otherwise.
882 */
isServiceRunning(const std::string & i_serviceName)883 inline bool isServiceRunning(const std::string& i_serviceName) noexcept
884 {
885 bool l_retVal = false;
886
887 try
888 {
889 auto l_bus = sdbusplus::bus::new_default();
890 auto l_method = l_bus.new_method_call(
891 constants::dbusService, constants::dbusObjectPath,
892 constants::dbusInterface, "NameHasOwner");
893 l_method.append(i_serviceName);
894
895 l_bus.call(l_method).read(l_retVal);
896 }
897 catch (const sdbusplus::exception::SdBusError& l_ex)
898 {
899 std::cout << "Call to check service status failed with exception: " +
900 std::string(l_ex.what())
901 << std::endl;
902 }
903
904 return l_retVal;
905 }
906
907 /**
908 * @brief API to call "GetAttribute" method under BIOS Config Manager.
909 *
910 * The API reads the given attribute from BIOS Config Manager and returns a
911 * variant containing current value for that attribute if the value is found.
912 * API returns an empty variant of type BiosAttributeCurrentValue in case of any
913 * error.
914 *
915 * @param[in] i_attributeName - Attribute to be read.
916 *
917 * @return Tuple of PLDM attribute Type, current attribute value and pending
918 * attribute value.
919 */
biosGetAttributeMethodCall(const std::string & i_attributeName)920 inline types::BiosAttributeCurrentValue biosGetAttributeMethodCall(
921 const std::string& i_attributeName) noexcept
922 {
923 types::BiosGetAttrRetType l_attributeVal;
924
925 try
926 {
927 auto l_bus = sdbusplus::bus::new_default();
928 auto l_method = l_bus.new_method_call(
929 constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
930 constants::biosConfigMgrInterface, "GetAttribute");
931 l_method.append(i_attributeName);
932
933 auto l_result = l_bus.call(l_method);
934 l_result.read(std::get<0>(l_attributeVal), std::get<1>(l_attributeVal),
935 std::get<2>(l_attributeVal));
936 }
937 catch (const sdbusplus::exception::SdBusError& l_ex)
938 {
939 // TODO : enable logging when verbose is implemented
940 std::cerr << "Failed to read BIOS Attribute: " + i_attributeName +
941 " due to error " + std::string(l_ex.what())
942 << std::endl;
943 }
944
945 return std::get<1>(l_attributeVal);
946 }
947
948 /**
949 * @brief Converts string to lower case.
950 *
951 * @param [in,out] io_string - Input string.
952 *
953 * @throw std::terminate, std::bad_alloc
954 */
toLower(std::string & io_string)955 inline void toLower(std::string& io_string)
956 {
957 std::transform(io_string.begin(), io_string.end(), io_string.begin(),
958 [](const unsigned char& l_char) {
959 return std::tolower(l_char);
960 });
961 }
962
963 /**
964 * @brief Converts an integral data value to a vector of bytes.
965 * The LSB of integer is copied to MSB of the vector.
966 *
967 * @param[in] i_integralData - Input integral data.
968 * @param[in] i_numBytesCopy - Number of bytes to copy.
969 *
970 * @return - On success, returns the Binary vector representation of the
971 * integral data, empty binary vector otherwise.
972 *
973 * @throw std::length_error
974 */
975 template <typename T>
976 requires std::integral<T>
convertIntegralTypeToBytes(const T & i_integralData,size_t i_numBytesCopy=constants::VALUE_1)977 inline types::BinaryVector convertIntegralTypeToBytes(
978 const T& i_integralData, size_t i_numBytesCopy = constants::VALUE_1)
979 {
980 types::BinaryVector l_result;
981 constexpr auto l_byteMask{0xFF};
982
983 l_result.resize(i_numBytesCopy, constants::VALUE_0);
984
985 // sanitize number of bytes to copy
986 if (i_numBytesCopy > sizeof(T))
987 {
988 i_numBytesCopy = sizeof(T);
989 }
990
991 // LSB of source -> MSB of result
992 for (size_t l_byte = 0; l_byte < i_numBytesCopy; ++l_byte)
993 {
994 l_result[l_result.size() - (l_byte + constants::VALUE_1)] =
995 (i_integralData >> (l_byte * constants::VALUE_8)) & l_byteMask;
996 }
997 return l_result;
998 }
999
1000 /**
1001 * @brief An API to get D-bus representation of given VPD keyword.
1002 *
1003 * @param[in] i_keywordName - VPD keyword name.
1004 *
1005 * @return D-bus representation of given keyword, otherwise empty string.
1006 */
getDbusPropNameForGivenKw(const std::string & i_keywordName)1007 inline std::string getDbusPropNameForGivenKw(
1008 const std::string& i_keywordName) noexcept
1009 {
1010 if (i_keywordName.size() != vpd::constants::KEYWORD_SIZE)
1011 {
1012 return std::string{};
1013 }
1014
1015 // Check for "#" prefixed VPD keyword.
1016 if (i_keywordName.at(0) == constants::POUND_KW)
1017 {
1018 // D-bus doesn't support "#". Replace "#" with "PD_" for those "#"
1019 // prefixed keywords.
1020 return (std::string(constants::POUND_KW_PREFIX) +
1021 i_keywordName.substr(1));
1022 }
1023 else if (std::isdigit(i_keywordName[0]))
1024 {
1025 // D-bus doesn't support numeric property. Add Prefix "N_" for those
1026 // numeric keywords.
1027 return (std::string(constants::NUMERIC_KW_PREFIX) + i_keywordName);
1028 }
1029
1030 // Return the keyword name back, if D-bus representation is same as the VPD
1031 // keyword name.
1032 return i_keywordName;
1033 }
1034
1035 } // namespace utils
1036 } // namespace vpd
1037