xref: /openbmc/openpower-vpd-parser/vpd-tool/src/vpd_tool.cpp (revision 12b01393ec98928b95a748de8f05241e95920a42)
1 #include "config.h"
2 
3 #include "vpd_tool.hpp"
4 
5 #include "tool_constants.hpp"
6 #include "tool_types.hpp"
7 #include "tool_utils.hpp"
8 
9 #include <iostream>
10 #include <regex>
11 #include <tuple>
12 namespace vpd
13 {
14 int VpdTool::readKeyword(
15     const std::string& i_vpdPath, const std::string& i_recordName,
16     const std::string& i_keywordName, const bool i_onHardware,
17     const std::string& i_fileToSave)
18 {
19     int l_rc = constants::FAILURE;
20     try
21     {
22         types::DbusVariantType l_keywordValue;
23         if (i_onHardware)
24         {
25             l_keywordValue = utils::readKeywordFromHardware(
26                 i_vpdPath, std::make_tuple(i_recordName, i_keywordName));
27         }
28         else
29         {
30             std::string l_inventoryObjectPath(
31                 constants::baseInventoryPath + i_vpdPath);
32 
33             l_keywordValue = utils::readDbusProperty(
34                 constants::inventoryManagerService, l_inventoryObjectPath,
35                 constants::ipzVpdInfPrefix + i_recordName, i_keywordName);
36         }
37 
38         if (const auto l_value =
39                 std::get_if<types::BinaryVector>(&l_keywordValue);
40             l_value && !l_value->empty())
41         {
42             // ToDo: Print value in both ASCII and hex formats
43             const std::string& l_keywordStrValue =
44                 utils::getPrintableValue(*l_value);
45 
46             if (i_fileToSave.empty())
47             {
48                 utils::displayOnConsole(i_vpdPath, i_keywordName,
49                                         l_keywordStrValue);
50                 l_rc = constants::SUCCESS;
51             }
52             else
53             {
54                 if (utils::saveToFile(i_fileToSave, l_keywordStrValue))
55                 {
56                     std::cout
57                         << "Value read is saved on the file: " << i_fileToSave
58                         << std::endl;
59                     l_rc = constants::SUCCESS;
60                 }
61                 else
62                 {
63                     std::cerr
64                         << "Error while saving the read value on the file: "
65                         << i_fileToSave
66                         << "\nDisplaying the read value on console"
67                         << std::endl;
68                     utils::displayOnConsole(i_vpdPath, i_keywordName,
69                                             l_keywordStrValue);
70                 }
71             }
72         }
73         else
74         {
75             // TODO: Enable logging when verbose is enabled.
76             std::cout << "Invalid data type or empty data received."
77                       << std::endl;
78         }
79     }
80     catch (const std::exception& l_ex)
81     {
82         // TODO: Enable logging when verbose is enabled.
83         std::cerr << "Read keyword's value failed for path: " << i_vpdPath
84                   << ", Record: " << i_recordName << ", Keyword: "
85                   << i_keywordName << ", error: " << l_ex.what() << std::endl;
86     }
87     return l_rc;
88 }
89 
90 int VpdTool::dumpObject(std::string i_fruPath) const noexcept
91 {
92     int l_rc{constants::FAILURE};
93     try
94     {
95         // ToDo: For PFuture system take only full path from the user.
96         i_fruPath = constants::baseInventoryPath + i_fruPath;
97 
98         nlohmann::json l_resultJsonArray = nlohmann::json::array({});
99         const nlohmann::json l_fruJson = getFruProperties(i_fruPath);
100         if (!l_fruJson.empty())
101         {
102             l_resultJsonArray += l_fruJson;
103 
104             utils::printJson(l_resultJsonArray);
105         }
106         else
107         {
108             std::cout << "FRU [" << i_fruPath
109                       << "] is not present in the system" << std::endl;
110         }
111         l_rc = constants::SUCCESS;
112     }
113     catch (std::exception& l_ex)
114     {
115         // TODO: Enable logging when verbose is enabled.
116         std::cerr << "Dump Object failed for FRU [" << i_fruPath
117                   << "], Error: " << l_ex.what() << std::endl;
118     }
119     return l_rc;
120 }
121 
122 nlohmann::json VpdTool::getFruProperties(const std::string& i_objectPath) const
123 {
124     // check if FRU is present in the system
125     if (!isFruPresent(i_objectPath))
126     {
127         return nlohmann::json::object_t();
128     }
129 
130     nlohmann::json l_fruJson = nlohmann::json::object_t({});
131 
132     // need to trim out the base inventory path in the FRU JSON.
133     const std::string l_displayObjectPath =
134         (i_objectPath.find(constants::baseInventoryPath) == std::string::npos)
135             ? i_objectPath
136             : i_objectPath.substr(strlen(constants::baseInventoryPath));
137 
138     l_fruJson.emplace(l_displayObjectPath, nlohmann::json::object_t({}));
139 
140     auto& l_fruObject = l_fruJson[l_displayObjectPath];
141 
142     const auto l_prettyNameInJson = getInventoryPropertyJson<std::string>(
143         i_objectPath, constants::inventoryItemInf, "PrettyName");
144     if (!l_prettyNameInJson.empty())
145     {
146         l_fruObject.insert(l_prettyNameInJson.cbegin(),
147                            l_prettyNameInJson.cend());
148     }
149 
150     const auto l_locationCodeInJson = getInventoryPropertyJson<std::string>(
151         i_objectPath, constants::locationCodeInf, "LocationCode");
152     if (!l_locationCodeInJson.empty())
153     {
154         l_fruObject.insert(l_locationCodeInJson.cbegin(),
155                            l_locationCodeInJson.cend());
156     }
157 
158     // Get the properties under VINI interface.
159 
160     nlohmann::json l_viniPropertiesInJson = nlohmann::json::object({});
161 
162     auto l_readViniKeyWord = [i_objectPath, &l_viniPropertiesInJson,
163                               this](const std::string& i_keyWord) {
164         const nlohmann::json l_keyWordJson =
165             getInventoryPropertyJson<vpd::types::BinaryVector>(
166                 i_objectPath, constants::kwdVpdInf, i_keyWord);
167         l_viniPropertiesInJson.insert(l_keyWordJson.cbegin(),
168                                       l_keyWordJson.cend());
169     };
170 
171     const std::vector<std::string> l_viniKeywords = {"SN", "PN", "CC", "FN",
172                                                      "DR"};
173 
174     std::for_each(l_viniKeywords.cbegin(), l_viniKeywords.cend(),
175                   l_readViniKeyWord);
176 
177     if (!l_viniPropertiesInJson.empty())
178     {
179         l_fruObject.insert(l_viniPropertiesInJson.cbegin(),
180                            l_viniPropertiesInJson.cend());
181     }
182     // if a FRU doesn't have VINI properties, we need to get the properties from
183     // Decorator.Asset interface
184     else
185     {
186         // Get properties under Decorator.Asset interface
187         const auto l_decoratorAssetPropertiesMap =
188             utils::getPropertyMap(constants::inventoryManagerService,
189                                   i_objectPath, constants::assetInf);
190 
191         for (const auto& l_aProperty : l_decoratorAssetPropertiesMap)
192         {
193             if (const auto l_propertyValueStr =
194                     std::get_if<std::string>(&l_aProperty.second))
195             {
196                 l_fruObject.emplace(l_aProperty.first, *l_propertyValueStr);
197             }
198         }
199     }
200 
201     const auto l_typePropertyJson = getFruTypeProperty(i_objectPath);
202     if (!l_typePropertyJson.empty())
203     {
204         l_fruObject.insert(l_typePropertyJson.cbegin(),
205                            l_typePropertyJson.cend());
206     }
207 
208     // insert FRU "TYPE"
209     l_fruObject.emplace("TYPE", "FRU");
210 
211     return l_fruJson;
212 }
213 
214 template <typename PropertyType>
215 nlohmann::json VpdTool::getInventoryPropertyJson(
216     const std::string& i_objectPath, const std::string& i_interface,
217     const std::string& i_propertyName) const noexcept
218 {
219     nlohmann::json l_resultInJson = nlohmann::json::object({});
220     try
221     {
222         types::DbusVariantType l_keyWordValue;
223 
224         l_keyWordValue =
225             utils::readDbusProperty(constants::inventoryManagerService,
226                                     i_objectPath, i_interface, i_propertyName);
227 
228         if (const auto l_value = std::get_if<PropertyType>(&l_keyWordValue))
229         {
230             if constexpr (std::is_same<PropertyType, std::string>::value)
231             {
232                 l_resultInJson.emplace(i_propertyName, *l_value);
233             }
234             else if constexpr (std::is_same<PropertyType, bool>::value)
235             {
236                 l_resultInJson.emplace(i_propertyName,
237                                        *l_value ? "true" : "false");
238             }
239             else if constexpr (std::is_same<PropertyType,
240                                             types::BinaryVector>::value)
241             {
242                 const std::string& l_keywordStrValue =
243                     vpd::utils::getPrintableValue(*l_value);
244 
245                 l_resultInJson.emplace(i_propertyName, l_keywordStrValue);
246             }
247         }
248         else
249         {
250             // TODO: Enable logging when verbose is enabled.
251             std::cout << "Invalid data type received." << std::endl;
252         }
253     }
254     catch (const std::exception& l_ex)
255     {
256         // TODO: Enable logging when verbose is enabled.
257         std::cerr << "Read " << i_propertyName
258                   << " value for FRU path: " << i_objectPath
259                   << ", failed with exception: " << l_ex.what() << std::endl;
260     }
261     return l_resultInJson;
262 }
263 
264 int VpdTool::fixSystemVpd() const noexcept
265 {
266     int l_rc = constants::FAILURE;
267 
268     nlohmann::json l_backupRestoreCfgJsonObj = getBackupRestoreCfgJsonObj();
269     if (!fetchKeywordInfo(l_backupRestoreCfgJsonObj))
270     {
271         return l_rc;
272     }
273 
274     printSystemVpd(l_backupRestoreCfgJsonObj);
275 
276     do
277     {
278         printFixSystemVpdOption(types::UserOption::UseBackupDataForAll);
279         printFixSystemVpdOption(
280             types::UserOption::UseSystemBackplaneDataForAll);
281         printFixSystemVpdOption(types::UserOption::MoreOptions);
282         printFixSystemVpdOption(types::UserOption::Exit);
283 
284         int l_userSelectedOption = types::UserOption::Exit;
285         std::cin >> l_userSelectedOption;
286 
287         std::cout << std::endl << std::string(191, '=') << std::endl;
288 
289         if (types::UserOption::UseBackupDataForAll == l_userSelectedOption)
290         {
291             l_rc = updateAllKeywords(l_backupRestoreCfgJsonObj, true);
292             break;
293         }
294         else if (types::UserOption::UseSystemBackplaneDataForAll ==
295                  l_userSelectedOption)
296         {
297             l_rc = updateAllKeywords(l_backupRestoreCfgJsonObj, false);
298             break;
299         }
300         else if (types::UserOption::MoreOptions == l_userSelectedOption)
301         {
302             l_rc = handleMoreOption(l_backupRestoreCfgJsonObj);
303             break;
304         }
305         else if (types::UserOption::Exit == l_userSelectedOption)
306         {
307             std::cout << "Exit successfully" << std::endl;
308             break;
309         }
310         else
311         {
312             std::cout << "Provide a valid option. Retry." << std::endl;
313         }
314     } while (true);
315 
316     return l_rc;
317 }
318 
319 int VpdTool::writeKeyword(
320     std::string i_vpdPath, const std::string& i_recordName,
321     const std::string& i_keywordName, const std::string& i_keywordValue,
322     const bool i_onHardware) noexcept
323 {
324     int l_rc = constants::FAILURE;
325     try
326     {
327         if (i_vpdPath.empty() || i_recordName.empty() ||
328             i_keywordName.empty() || i_keywordValue.empty())
329         {
330             throw std::runtime_error("Received input is empty.");
331         }
332 
333         auto l_paramsToWrite =
334             std::make_tuple(i_recordName, i_keywordName,
335                             utils::convertToBinary(i_keywordValue));
336 
337         if (i_onHardware)
338         {
339             l_rc = utils::writeKeywordOnHardware(i_vpdPath, l_paramsToWrite);
340         }
341         else
342         {
343             i_vpdPath = constants::baseInventoryPath + i_vpdPath;
344             l_rc = utils::writeKeyword(i_vpdPath, l_paramsToWrite);
345         }
346 
347         if (l_rc > 0)
348         {
349             std::cout << "Data updated successfully " << std::endl;
350             l_rc = constants::SUCCESS;
351         }
352     }
353     catch (const std::exception& l_ex)
354     {
355         // TODO: Enable log when verbose is enabled.
356         std::cerr << "Write keyword's value for path: " << i_vpdPath
357                   << ", Record: " << i_recordName
358                   << ", Keyword: " << i_keywordName
359                   << " is failed. Exception: " << l_ex.what() << std::endl;
360     }
361     return l_rc;
362 }
363 
364 nlohmann::json VpdTool::getBackupRestoreCfgJsonObj() const noexcept
365 {
366     nlohmann::json l_parsedBackupRestoreJson{};
367     try
368     {
369         nlohmann::json l_parsedSystemJson =
370             utils::getParsedJson(INVENTORY_JSON_SYM_LINK);
371 
372         // check for mandatory fields at this point itself.
373         if (!l_parsedSystemJson.contains("backupRestoreConfigPath"))
374         {
375             throw std::runtime_error(
376                 "backupRestoreConfigPath tag is missing from system config JSON : " +
377                 std::string(INVENTORY_JSON_SYM_LINK));
378         }
379 
380         l_parsedBackupRestoreJson =
381             utils::getParsedJson(l_parsedSystemJson["backupRestoreConfigPath"]);
382     }
383     catch (const std::exception& l_ex)
384     {
385         // TODO: Enable logging when verbose is enabled.
386         std::cerr << l_ex.what() << std::endl;
387     }
388 
389     return l_parsedBackupRestoreJson;
390 }
391 
392 int VpdTool::cleanSystemVpd(bool i_syncBiosAttributes) const noexcept
393 {
394     try
395     {
396         (void)i_syncBiosAttributes;
397 
398         // get the keyword map from backup_restore json
399         // iterate through the keyword map get default value of
400         // l_keywordName.
401         // use writeKeyword API to update default value on hardware,
402         // backup and D - Bus.
403         const nlohmann::json l_parsedBackupRestoreJson =
404             getBackupRestoreCfgJsonObj();
405 
406         // check for mandatory tags
407         if (l_parsedBackupRestoreJson.contains("source") &&
408             l_parsedBackupRestoreJson.contains("backupMap") &&
409             l_parsedBackupRestoreJson["source"].contains("hardwarePath") &&
410             l_parsedBackupRestoreJson["backupMap"].is_array())
411         {
412             // get the source hardware path
413             const auto& l_hardwarePath =
414                 l_parsedBackupRestoreJson["source"]["hardwarePath"];
415 
416             // iterate through the backup map
417             for (const auto& l_aRecordKwInfo :
418                  l_parsedBackupRestoreJson["backupMap"])
419             {
420                 // check if Manufacturing Reset is required for this entry
421                 const bool l_isMfgCleanRequired =
422                     l_aRecordKwInfo.value("isManufactureResetRequired", false);
423 
424                 if (l_isMfgCleanRequired)
425                 {
426                     // get the Record name and Keyword name
427                     const std::string& l_srcRecordName =
428                         l_aRecordKwInfo.value("sourceRecord", "");
429                     const std::string& l_srcKeywordName =
430                         l_aRecordKwInfo.value("sourceKeyword", "");
431 
432                     // validate the Record name, Keyword name and the
433                     // defaultValue
434                     if (!l_srcRecordName.empty() && !l_srcKeywordName.empty() &&
435                         l_aRecordKwInfo.contains("defaultValue") &&
436                         l_aRecordKwInfo["defaultValue"].is_array())
437                     {
438                         const types::BinaryVector l_defaultBinaryValue =
439                             l_aRecordKwInfo["defaultValue"]
440                                 .get<types::BinaryVector>();
441 
442                         // update the Keyword with default value, use D-Bus
443                         // method UpdateKeyword exposed by vpd-manager.
444                         // Note: writing to all paths (Primary EEPROM path,
445                         // Secondary EEPROM path, D-Bus cache and Backup path)
446                         // is the responsibility of vpd-manager's UpdateKeyword
447                         // API
448                         if (constants::FAILURE ==
449                             utils::writeKeyword(
450                                 l_hardwarePath,
451                                 std::make_tuple(l_srcRecordName,
452                                                 l_srcKeywordName,
453                                                 l_defaultBinaryValue)))
454                         {
455                             // TODO: Enable logging when verbose
456                             // is enabled.
457                             std::cerr << "Failed to update " << l_srcRecordName
458                                       << ":" << l_srcKeywordName << std::endl;
459                         }
460                     }
461                     else
462                     {
463                         std::cerr
464                             << "Unrecognized Entry Record [" << l_srcRecordName
465                             << "] Keyword [" << l_srcKeywordName
466                             << "] in Backup Restore JSON backup map"
467                             << std::endl;
468                     }
469                 } // mfgClean required check
470             } // keyword list loop
471         }
472         else // backupRestoreJson is not valid
473         {
474             std::cerr << "Backup Restore JSON is not valid" << std::endl;
475         }
476 
477         // success/failure message
478         std::cout << "The critical keywords from system backplane VPD has "
479                      "been reset successfully."
480                   << std::endl;
481 
482     } // try block end
483     catch (const std::exception& l_ex)
484     {
485         // TODO: Enable logging when verbose is enabled.
486         std::cerr
487             << "Manufacturing reset on system vpd keywords is unsuccessful. Error : "
488             << l_ex.what() << std::endl;
489     }
490     return constants::SUCCESS;
491 }
492 
493 bool VpdTool::fetchKeywordInfo(nlohmann::json& io_parsedJsonObj) const noexcept
494 {
495     bool l_returnValue = false;
496     try
497     {
498         if (io_parsedJsonObj.empty() || !io_parsedJsonObj.contains("source") ||
499             !io_parsedJsonObj.contains("destination") ||
500             !io_parsedJsonObj.contains("backupMap"))
501         {
502             throw std::runtime_error("Invalid JSON");
503         }
504 
505         std::string l_srcVpdPath;
506         std::string l_dstVpdPath;
507 
508         bool l_isSourceOnHardware = false;
509         if (l_srcVpdPath = io_parsedJsonObj["source"].value("hardwarePath", "");
510             !l_srcVpdPath.empty())
511         {
512             l_isSourceOnHardware = true;
513         }
514         else if (l_srcVpdPath =
515                      io_parsedJsonObj["source"].value("inventoryPath", "");
516                  l_srcVpdPath.empty())
517         {
518             throw std::runtime_error("Source path is empty in JSON");
519         }
520 
521         bool l_isDestinationOnHardware = false;
522         if (l_dstVpdPath =
523                 io_parsedJsonObj["destination"].value("hardwarePath", "");
524             !l_dstVpdPath.empty())
525         {
526             l_isDestinationOnHardware = true;
527         }
528         else if (l_dstVpdPath =
529                      io_parsedJsonObj["destination"].value("inventoryPath", "");
530                  l_dstVpdPath.empty())
531         {
532             throw std::runtime_error("Destination path is empty in JSON");
533         }
534 
535         for (auto& l_aRecordKwInfo : io_parsedJsonObj["backupMap"])
536         {
537             const std::string& l_srcRecordName =
538                 l_aRecordKwInfo.value("sourceRecord", "");
539             const std::string& l_srcKeywordName =
540                 l_aRecordKwInfo.value("sourceKeyword", "");
541             const std::string& l_dstRecordName =
542                 l_aRecordKwInfo.value("destinationRecord", "");
543             const std::string& l_dstKeywordName =
544                 l_aRecordKwInfo.value("destinationKeyword", "");
545 
546             if (l_srcRecordName.empty() || l_dstRecordName.empty() ||
547                 l_srcKeywordName.empty() || l_dstKeywordName.empty())
548             {
549                 // TODO: Enable logging when verbose is enabled.
550                 std::cout << "Record or keyword not found in the JSON."
551                           << std::endl;
552                 continue;
553             }
554 
555             types::DbusVariantType l_srcKeywordVariant;
556             if (l_isSourceOnHardware)
557             {
558                 l_srcKeywordVariant = utils::readKeywordFromHardware(
559                     l_srcVpdPath,
560                     std::make_tuple(l_srcRecordName, l_srcKeywordName));
561             }
562             else
563             {
564                 l_srcKeywordVariant = utils::readDbusProperty(
565                     constants::inventoryManagerService, l_srcVpdPath,
566                     constants::ipzVpdInfPrefix + l_srcRecordName,
567                     l_srcKeywordName);
568             }
569 
570             if (auto l_srcKeywordValue =
571                     std::get_if<types::BinaryVector>(&l_srcKeywordVariant);
572                 l_srcKeywordValue && !l_srcKeywordValue->empty())
573             {
574                 l_aRecordKwInfo["sourcekeywordValue"] = *l_srcKeywordValue;
575             }
576             else
577             {
578                 // TODO: Enable logging when verbose is enabled.
579                 std::cout
580                     << "Invalid data type or empty data received, for source record: "
581                     << l_srcRecordName << ", keyword: " << l_srcKeywordName
582                     << std::endl;
583                 continue;
584             }
585 
586             types::DbusVariantType l_dstKeywordVariant;
587             if (l_isDestinationOnHardware)
588             {
589                 l_dstKeywordVariant = utils::readKeywordFromHardware(
590                     l_dstVpdPath,
591                     std::make_tuple(l_dstRecordName, l_dstKeywordName));
592             }
593             else
594             {
595                 l_dstKeywordVariant = utils::readDbusProperty(
596                     constants::inventoryManagerService, l_dstVpdPath,
597                     constants::ipzVpdInfPrefix + l_dstRecordName,
598                     l_dstKeywordName);
599             }
600 
601             if (auto l_dstKeywordValue =
602                     std::get_if<types::BinaryVector>(&l_dstKeywordVariant);
603                 l_dstKeywordValue && !l_dstKeywordValue->empty())
604             {
605                 l_aRecordKwInfo["destinationkeywordValue"] = *l_dstKeywordValue;
606             }
607             else
608             {
609                 // TODO: Enable logging when verbose is enabled.
610                 std::cout
611                     << "Invalid data type or empty data received, for destination record: "
612                     << l_dstRecordName << ", keyword: " << l_dstKeywordName
613                     << std::endl;
614                 continue;
615             }
616         }
617 
618         l_returnValue = true;
619     }
620     catch (const std::exception& l_ex)
621     {
622         // TODO: Enable logging when verbose is enabled.
623         std::cerr << l_ex.what() << std::endl;
624     }
625 
626     return l_returnValue;
627 }
628 
629 nlohmann::json
630     VpdTool::getFruTypeProperty(const std::string& i_objectPath) const noexcept
631 {
632     nlohmann::json l_resultInJson = nlohmann::json::object({});
633     std::vector<std::string> l_pimInfList;
634 
635     auto l_serviceInfMap = utils::GetServiceInterfacesForObject(
636         i_objectPath, std::vector<std::string>{constants::inventoryItemInf});
637     if (l_serviceInfMap.contains(constants::inventoryManagerService))
638     {
639         l_pimInfList = l_serviceInfMap[constants::inventoryManagerService];
640 
641         // iterate through the list and find
642         // "xyz.openbmc_project.Inventory.Item.*"
643         for (const auto& l_interface : l_pimInfList)
644         {
645             if (l_interface.find(constants::inventoryItemInf) !=
646                     std::string::npos &&
647                 l_interface.length() >
648                     std::string(constants::inventoryItemInf).length())
649             {
650                 l_resultInJson.emplace("type", l_interface);
651             }
652         }
653     }
654     return l_resultInJson;
655 }
656 
657 bool VpdTool::isFruPresent(const std::string& i_objectPath) const noexcept
658 {
659     bool l_returnValue{false};
660     try
661     {
662         types::DbusVariantType l_keyWordValue;
663 
664         l_keyWordValue = utils::readDbusProperty(
665             constants::inventoryManagerService, i_objectPath,
666             constants::inventoryItemInf, "Present");
667 
668         if (const auto l_value = std::get_if<bool>(&l_keyWordValue))
669         {
670             l_returnValue = *l_value;
671         }
672     }
673     catch (const std::runtime_error& l_ex)
674     {
675         // TODO: Enable logging when verbose is enabled.
676         // std::cerr << "Failed to check \"Present\" property for FRU "
677         //           << i_objectPath << " Error: " << l_ex.what() <<
678         //           std::endl;
679     }
680     return l_returnValue;
681 }
682 
683 void VpdTool::printFixSystemVpdOption(
684     const types::UserOption& i_option) const noexcept
685 {
686     switch (i_option)
687     {
688         case types::UserOption::Exit:
689             std::cout << "Enter 0 => To exit successfully : ";
690             break;
691         case types::UserOption::UseBackupDataForAll:
692             std::cout << "Enter 1 => If you choose the data on backup for all "
693                          "mismatching record-keyword pairs"
694                       << std::endl;
695             break;
696         case types::UserOption::UseSystemBackplaneDataForAll:
697             std::cout << "Enter 2 => If you choose the data on primary for all "
698                          "mismatching record-keyword pairs"
699                       << std::endl;
700             break;
701         case types::UserOption::MoreOptions:
702             std::cout << "Enter 3 => If you wish to explore more options"
703                       << std::endl;
704             break;
705         case types::UserOption::UseBackupDataForCurrent:
706             std::cout << "Enter 4 => If you choose the data on backup as the "
707                          "right value"
708                       << std::endl;
709             break;
710         case types::UserOption::UseSystemBackplaneDataForCurrent:
711             std::cout << "Enter 5 => If you choose the data on primary as the "
712                          "right value"
713                       << std::endl;
714             break;
715         case types::UserOption::NewValueOnBoth:
716             std::cout
717                 << "Enter 6 => If you wish to enter a new value to update "
718                    "both on backup and primary"
719                 << std::endl;
720             break;
721         case types::UserOption::SkipCurrent:
722             std::cout << "Enter 7 => If you wish to skip the above "
723                          "record-keyword pair"
724                       << std::endl;
725             break;
726     }
727 }
728 
729 int VpdTool::dumpInventory(bool i_dumpTable) const noexcept
730 {
731     int l_rc{constants::FAILURE};
732 
733     try
734     {
735         // get all object paths under PIM
736         const auto l_objectPaths = utils::GetSubTreePaths(
737             constants::baseInventoryPath, 0,
738             std::vector<std::string>{constants::inventoryItemInf});
739 
740         if (!l_objectPaths.empty())
741         {
742             nlohmann::json l_resultInJson = nlohmann::json::array({});
743 
744             std::for_each(l_objectPaths.begin(), l_objectPaths.end(),
745                           [&](const auto& l_objectPath) {
746                               const auto l_fruJson =
747                                   getFruProperties(l_objectPath);
748                               if (!l_fruJson.empty())
749                               {
750                                   if (l_resultInJson.empty())
751                                   {
752                                       l_resultInJson += l_fruJson;
753                                   }
754                                   else
755                                   {
756                                       l_resultInJson.at(0).insert(
757                                           l_fruJson.cbegin(), l_fruJson.cend());
758                                   }
759                               }
760                           });
761 
762             if (i_dumpTable)
763             {
764                 // create Table object
765                 utils::Table l_inventoryTable{};
766 
767                 // columns to be populated in the Inventory table
768                 const std::vector<types::TableColumnNameSizePair>
769                     l_tableColumns = {
770                         {"FRU", 100},         {"CC", 6},  {"DR", 20},
771                         {"LocationCode", 32}, {"PN", 8},  {"PrettyName", 80},
772                         {"SubModel", 10},     {"SN", 15}, {"type", 60}};
773 
774                 types::TableInputData l_tableData;
775 
776                 // First prepare the Table Columns
777                 for (const auto& l_column : l_tableColumns)
778                 {
779                     if (constants::FAILURE ==
780                         l_inventoryTable.AddColumn(l_column.first,
781                                                    l_column.second))
782                     {
783                         // TODO: Enable logging when verbose is enabled.
784                         std::cerr << "Failed to add column " << l_column.first
785                                   << " in Inventory Table." << std::endl;
786                     }
787                 }
788 
789                 // iterate through the json array
790                 for (const auto& l_fruEntry : l_resultInJson[0].items())
791                 {
792                     // if object path ends in "unit([0-9][0-9]?)", skip adding
793                     // the object path in the table
794                     if (std::regex_search(l_fruEntry.key(),
795                                           std::regex("unit([0-9][0-9]?)")))
796                     {
797                         continue;
798                     }
799 
800                     std::vector<std::string> l_row;
801                     for (const auto& l_column : l_tableColumns)
802                     {
803                         const auto& l_fruJson = l_fruEntry.value();
804 
805                         if (l_column.first == "FRU")
806                         {
807                             l_row.push_back(l_fruEntry.key());
808                         }
809                         else
810                         {
811                             if (l_fruJson.contains(l_column.first))
812                             {
813                                 l_row.push_back(l_fruJson[l_column.first]);
814                             }
815                             else
816                             {
817                                 l_row.push_back("");
818                             }
819                         }
820                     }
821 
822                     l_tableData.push_back(l_row);
823                 }
824 
825                 l_rc = l_inventoryTable.Print(l_tableData);
826             }
827             else
828             {
829                 // print JSON to console
830                 utils::printJson(l_resultInJson);
831                 l_rc = constants::SUCCESS;
832             }
833         }
834     }
835     catch (const std::exception& l_ex)
836     {
837         // TODO: Enable logging when verbose is enabled.
838         std::cerr << "Dump inventory failed. Error: " << l_ex.what()
839                   << std::endl;
840     }
841     return l_rc;
842 }
843 
844 void VpdTool::printSystemVpd(
845     const nlohmann::json& i_parsedJsonObj) const noexcept
846 {
847     if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("backupMap"))
848     {
849         // TODO: Enable logging when verbose is enabled.
850         std::cerr << "Invalid JSON to print system VPD" << std::endl;
851     }
852 
853     std::string l_outline(191, '=');
854     std::cout << "\nRestorable record-keyword pairs and their data on backup & "
855                  "primary.\n\n"
856               << l_outline << std::endl;
857 
858     std::cout << std::left << std::setw(6) << "S.No" << std::left
859               << std::setw(8) << "Record" << std::left << std::setw(9)
860               << "Keyword" << std::left << std::setw(75) << "Data On Backup"
861               << std::left << std::setw(75) << "Data On Primary" << std::left
862               << std::setw(14) << "Data Mismatch\n"
863               << l_outline << std::endl;
864 
865     uint8_t l_slNum = 0;
866 
867     for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"])
868     {
869         if (l_aRecordKwInfo.contains("sourceRecord") ||
870             l_aRecordKwInfo.contains("sourceKeyword") ||
871             l_aRecordKwInfo.contains("destinationkeywordValue") ||
872             l_aRecordKwInfo.contains("sourcekeywordValue"))
873         {
874             std::string l_mismatchFound{
875                 (l_aRecordKwInfo["destinationkeywordValue"] !=
876                  l_aRecordKwInfo["sourcekeywordValue"])
877                     ? "YES"
878                     : "NO"};
879 
880             std::string l_splitLine(191, '-');
881 
882             try
883             {
884                 std::cout << std::left << std::setw(6)
885                           << static_cast<int>(++l_slNum) << std::left
886                           << std::setw(8)
887                           << l_aRecordKwInfo.value("sourceRecord", "")
888                           << std::left << std::setw(9)
889                           << l_aRecordKwInfo.value("sourceKeyword", "")
890                           << std::left << std::setw(75) << std::setfill(' ')
891                           << utils::getPrintableValue(
892                                  l_aRecordKwInfo["destinationkeywordValue"])
893                           << std::left << std::setw(75) << std::setfill(' ')
894                           << utils::getPrintableValue(
895                                  l_aRecordKwInfo["sourcekeywordValue"])
896                           << std::left << std::setw(14) << l_mismatchFound
897                           << '\n'
898                           << l_splitLine << std::endl;
899             }
900             catch (const std::exception& l_ex)
901             {
902                 // TODO: Enable logging when verbose is enabled.
903                 std::cerr << l_ex.what() << std::endl;
904             }
905         }
906     }
907 }
908 
909 int VpdTool::updateAllKeywords(const nlohmann::json& i_parsedJsonObj,
910                                bool i_useBackupData) const noexcept
911 {
912     int l_rc = constants::FAILURE;
913 
914     if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("source") ||
915         !i_parsedJsonObj.contains("backupMap"))
916     {
917         // TODO: Enable logging when verbose is enabled.
918         std::cerr << "Invalid JSON" << std::endl;
919         return l_rc;
920     }
921 
922     std::string l_srcVpdPath;
923     if (auto l_vpdPath = i_parsedJsonObj["source"].value("hardwarePath", "");
924         !l_vpdPath.empty())
925     {
926         l_srcVpdPath = l_vpdPath;
927     }
928     else if (auto l_vpdPath =
929                  i_parsedJsonObj["source"].value("inventoryPath", "");
930              !l_vpdPath.empty())
931     {
932         l_srcVpdPath = l_vpdPath;
933     }
934     else
935     {
936         // TODO: Enable logging when verbose is enabled.
937         std::cerr << "source path information is missing in JSON" << std::endl;
938         return l_rc;
939     }
940 
941     bool l_anyMismatchFound = false;
942     for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"])
943     {
944         if (!l_aRecordKwInfo.contains("sourceRecord") ||
945             !l_aRecordKwInfo.contains("sourceKeyword") ||
946             !l_aRecordKwInfo.contains("destinationkeywordValue") ||
947             !l_aRecordKwInfo.contains("sourcekeywordValue"))
948         {
949             // TODO: Enable logging when verbose is enabled.
950             std::cerr << "Missing required information in the JSON"
951                       << std::endl;
952             continue;
953         }
954 
955         if (l_aRecordKwInfo["sourcekeywordValue"] !=
956             l_aRecordKwInfo["destinationkeywordValue"])
957         {
958             l_anyMismatchFound = true;
959 
960             auto l_keywordValue =
961                 i_useBackupData ? l_aRecordKwInfo["destinationkeywordValue"]
962                                 : l_aRecordKwInfo["sourcekeywordValue"];
963 
964             auto l_paramsToWrite = std::make_tuple(
965                 l_aRecordKwInfo["sourceRecord"],
966                 l_aRecordKwInfo["sourceKeyword"], l_keywordValue);
967 
968             try
969             {
970                 l_rc = utils::writeKeyword(l_srcVpdPath, l_paramsToWrite);
971                 if (l_rc > 0)
972                 {
973                     l_rc = constants::SUCCESS;
974                 }
975             }
976             catch (const std::exception& l_ex)
977             {
978                 // TODO: Enable logging when verbose is enabled.
979                 std::cerr << "write keyword failed for record: "
980                           << l_aRecordKwInfo["sourceRecord"]
981                           << ", keyword: " << l_aRecordKwInfo["sourceKeyword"]
982                           << ", error: " << l_ex.what() << std::ends;
983             }
984         }
985     }
986 
987     std::string l_dataUsed =
988         (i_useBackupData ? "data from backup" : "data from primary VPD");
989     if (l_anyMismatchFound)
990     {
991         std::cout << "Data updated successfully for all mismatching "
992                      "record-keyword pairs by choosing their corresponding "
993                   << l_dataUsed << ". Exit successfully." << std::endl;
994     }
995     else
996     {
997         std::cout << "No mismatch found for any of the above mentioned "
998                      "record-keyword pair. Exit successfully."
999                   << std::endl;
1000     }
1001 
1002     return l_rc;
1003 }
1004 
1005 int VpdTool::handleMoreOption(
1006     const nlohmann::json& i_parsedJsonObj) const noexcept
1007 {
1008     int l_rc = constants::FAILURE;
1009 
1010     try
1011     {
1012         if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("backupMap"))
1013         {
1014             throw std::runtime_error("Invalid JSON");
1015         }
1016 
1017         std::string l_srcVpdPath;
1018 
1019         if (auto l_vpdPath =
1020                 i_parsedJsonObj["source"].value("hardwarePath", "");
1021             !l_vpdPath.empty())
1022         {
1023             l_srcVpdPath = l_vpdPath;
1024         }
1025         else if (auto l_vpdPath =
1026                      i_parsedJsonObj["source"].value("inventoryPath", "");
1027                  !l_vpdPath.empty())
1028         {
1029             l_srcVpdPath = l_vpdPath;
1030         }
1031         else
1032         {
1033             throw std::runtime_error(
1034                 "source path information is missing in JSON");
1035         }
1036 
1037         auto updateKeywordValue =
1038             [](std::string io_vpdPath, const std::string& i_recordName,
1039                const std::string& i_keywordName,
1040                const types::BinaryVector& i_keywordValue) -> int {
1041             int l_rc = constants::FAILURE;
1042 
1043             try
1044             {
1045                 auto l_paramsToWrite = std::make_tuple(
1046                     i_recordName, i_keywordName, i_keywordValue);
1047                 l_rc = utils::writeKeyword(io_vpdPath, l_paramsToWrite);
1048 
1049                 if (l_rc > 0)
1050                 {
1051                     std::cout << std::endl
1052                               << "Data updated successfully." << std::endl;
1053                 }
1054             }
1055             catch (const std::exception& l_ex)
1056             {
1057                 // TODO: Enable log when verbose is enabled.
1058                 std::cerr << l_ex.what() << std::endl;
1059             }
1060             return l_rc;
1061         };
1062 
1063         do
1064         {
1065             int l_slNum = 0;
1066             bool l_exit = false;
1067 
1068             for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"])
1069             {
1070                 if (!l_aRecordKwInfo.contains("sourceRecord") ||
1071                     !l_aRecordKwInfo.contains("sourceKeyword") ||
1072                     !l_aRecordKwInfo.contains("destinationkeywordValue") ||
1073                     !l_aRecordKwInfo.contains("sourcekeywordValue"))
1074                 {
1075                     // TODO: Enable logging when verbose is enabled.
1076                     std::cerr
1077                         << "Source or destination information is missing in the JSON."
1078                         << std::endl;
1079                     continue;
1080                 }
1081 
1082                 const std::string l_mismatchFound{
1083                     (l_aRecordKwInfo["sourcekeywordValue"] !=
1084                      l_aRecordKwInfo["destinationkeywordValue"])
1085                         ? "YES"
1086                         : "NO"};
1087 
1088                 std::cout << std::endl
1089                           << std::left << std::setw(6) << "S.No" << std::left
1090                           << std::setw(8) << "Record" << std::left
1091                           << std::setw(9) << "Keyword" << std::left
1092                           << std::setw(75) << std::setfill(' ') << "Backup Data"
1093                           << std::left << std::setw(75) << std::setfill(' ')
1094                           << "Primary Data" << std::left << std::setw(14)
1095                           << "Data Mismatch" << std::endl;
1096 
1097                 std::cout << std::left << std::setw(6)
1098                           << static_cast<int>(++l_slNum) << std::left
1099                           << std::setw(8)
1100                           << l_aRecordKwInfo.value("sourceRecord", "")
1101                           << std::left << std::setw(9)
1102                           << l_aRecordKwInfo.value("sourceKeyword", "")
1103                           << std::left << std::setw(75) << std::setfill(' ')
1104                           << utils::getPrintableValue(
1105                                  l_aRecordKwInfo["destinationkeywordValue"])
1106                           << std::left << std::setw(75) << std::setfill(' ')
1107                           << utils::getPrintableValue(
1108                                  l_aRecordKwInfo["sourcekeywordValue"])
1109                           << std::left << std::setw(14) << l_mismatchFound
1110                           << std::endl;
1111 
1112                 std::cout << std::string(191, '=') << std::endl;
1113 
1114                 if (constants::STR_CMP_SUCCESS ==
1115                     l_mismatchFound.compare("YES"))
1116                 {
1117                     printFixSystemVpdOption(
1118                         types::UserOption::UseBackupDataForCurrent);
1119                     printFixSystemVpdOption(
1120                         types::UserOption::UseSystemBackplaneDataForCurrent);
1121                     printFixSystemVpdOption(types::UserOption::NewValueOnBoth);
1122                     printFixSystemVpdOption(types::UserOption::SkipCurrent);
1123                     printFixSystemVpdOption(types::UserOption::Exit);
1124                 }
1125                 else
1126                 {
1127                     std::cout << "No mismatch found." << std::endl << std::endl;
1128                     printFixSystemVpdOption(types::UserOption::NewValueOnBoth);
1129                     printFixSystemVpdOption(types::UserOption::SkipCurrent);
1130                     printFixSystemVpdOption(types::UserOption::Exit);
1131                 }
1132 
1133                 int l_userSelectedOption = types::UserOption::Exit;
1134                 std::cin >> l_userSelectedOption;
1135 
1136                 if (types::UserOption::UseBackupDataForCurrent ==
1137                     l_userSelectedOption)
1138                 {
1139                     l_rc = updateKeywordValue(
1140                         l_srcVpdPath, l_aRecordKwInfo["sourceRecord"],
1141                         l_aRecordKwInfo["sourceKeyword"],
1142                         l_aRecordKwInfo["destinationkeywordValue"]);
1143                 }
1144                 else if (types::UserOption::UseSystemBackplaneDataForCurrent ==
1145                          l_userSelectedOption)
1146                 {
1147                     l_rc = updateKeywordValue(
1148                         l_srcVpdPath, l_aRecordKwInfo["sourceRecord"],
1149                         l_aRecordKwInfo["sourceKeyword"],
1150                         l_aRecordKwInfo["sourcekeywordValue"]);
1151                 }
1152                 else if (types::UserOption::NewValueOnBoth ==
1153                          l_userSelectedOption)
1154                 {
1155                     std::string l_newValue;
1156                     std::cout
1157                         << std::endl
1158                         << "Enter the new value to update on both "
1159                            "primary & backup. Value should be in ASCII or "
1160                            "in HEX(prefixed with 0x) : ";
1161                     std::cin >> l_newValue;
1162                     std::cout << std::endl
1163                               << std::string(191, '=') << std::endl;
1164 
1165                     try
1166                     {
1167                         l_rc = updateKeywordValue(
1168                             l_srcVpdPath, l_aRecordKwInfo["sourceRecord"],
1169                             l_aRecordKwInfo["sourceKeyword"],
1170                             utils::convertToBinary(l_newValue));
1171                     }
1172                     catch (const std::exception& l_ex)
1173                     {
1174                         // TODO: Enable logging when verbose is enabled.
1175                         std::cerr << l_ex.what() << std::endl;
1176                     }
1177                 }
1178                 else if (types::UserOption::SkipCurrent == l_userSelectedOption)
1179                 {
1180                     std::cout << std::endl
1181                               << "Skipped the above record-keyword pair. "
1182                                  "Continue to the next available pair."
1183                               << std::endl;
1184                 }
1185                 else if (types::UserOption::Exit == l_userSelectedOption)
1186                 {
1187                     std::cout << "Exit successfully" << std::endl;
1188                     l_exit = true;
1189                     break;
1190                 }
1191                 else
1192                 {
1193                     std::cout << "Provide a valid option. Retrying for the "
1194                                  "current record-keyword pair"
1195                               << std::endl;
1196                 }
1197             }
1198             if (l_exit)
1199             {
1200                 l_rc = constants::SUCCESS;
1201                 break;
1202             }
1203         } while (true);
1204     }
1205     catch (const std::exception& l_ex)
1206     {
1207         // TODO: Enable logging when verbose is enabled.
1208         std::cerr << l_ex.what() << std::endl;
1209     }
1210 
1211     return l_rc;
1212 }
1213 
1214 } // namespace vpd
1215