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