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