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