xref: /openbmc/openpower-vpd-parser/vpd-tool/include/tool_utils.hpp (revision 7f749a6f3d581ec2fd573ff940f91770a6660dde)
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