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