1 #include "vpd_tool_impl.hpp"
2 
3 #include "impl.hpp"
4 #include "parser_factory.hpp"
5 #include "vpd_exceptions.hpp"
6 
7 #include <cstdlib>
8 #include <filesystem>
9 #include <iostream>
10 #include <sdbusplus/bus.hpp>
11 #include <variant>
12 #include <vector>
13 
14 using namespace std;
15 using namespace openpower::vpd;
16 using namespace inventory;
17 using namespace openpower::vpd::manager::editor;
18 namespace fs = std::filesystem;
19 using json = nlohmann::json;
20 using namespace openpower::vpd::exceptions;
21 using namespace openpower::vpd::parser;
22 using namespace openpower::vpd::parser::factory;
23 using namespace openpower::vpd::parser::interface;
24 
25 Binary VpdTool::toBinary(const std::string& value)
26 {
27     Binary val{};
28     if (value.find("0x") == string::npos)
29     {
30         val.assign(value.begin(), value.end());
31     }
32     else if (value.find("0x") != string::npos)
33     {
34         stringstream ss;
35         ss.str(value.substr(2));
36         string byteStr{};
37 
38         if (value.length() % 2 != 0)
39         {
40             throw runtime_error(
41                 "VPD-TOOL write option accepts 2 digit hex numbers. (Eg. 0x1 "
42                 "should be given as 0x01). Aborting the write operation.");
43         }
44 
45         if (value.find_first_not_of("0123456789abcdefABCDEF", 2) !=
46             std::string::npos)
47         {
48             throw runtime_error("Provide a valid hexadecimal input.");
49         }
50 
51         while (ss >> setw(2) >> byteStr)
52         {
53             uint8_t byte = strtoul(byteStr.c_str(), nullptr, 16);
54 
55             val.push_back(byte);
56         }
57     }
58 
59     else
60     {
61         throw runtime_error("The value to be updated should be either in ascii "
62                             "or in hex. Refer --help option");
63     }
64     return val;
65 }
66 
67 void VpdTool::printReturnCode(int returnCode)
68 {
69     if (returnCode)
70     {
71         cout << "\n Command failed with the return code " << returnCode
72              << ". Continuing the execution. " << endl;
73     }
74 }
75 
76 void VpdTool::eraseInventoryPath(string& fru)
77 {
78     // Power supply frupath comes with INVENTORY_PATH appended in prefix.
79     // Stripping it off inorder to avoid INVENTORY_PATH duplication
80     // during getVINIProperties() execution.
81     fru.erase(0, sizeof(INVENTORY_PATH) - 1);
82 }
83 
84 void VpdTool::debugger(json output)
85 {
86     cout << output.dump(4) << '\n';
87 }
88 
89 auto VpdTool::makeDBusCall(const string& objectName, const string& interface,
90                            const string& kw)
91 {
92     auto bus = sdbusplus::bus::new_default();
93     auto properties =
94         bus.new_method_call(INVENTORY_MANAGER_SERVICE, objectName.c_str(),
95                             "org.freedesktop.DBus.Properties", "Get");
96     properties.append(interface);
97     properties.append(kw);
98     auto result = bus.call(properties);
99 
100     if (result.is_method_error())
101     {
102         throw runtime_error("Get api failed");
103     }
104     return result;
105 }
106 
107 json VpdTool::getVINIProperties(string invPath)
108 {
109     variant<Binary> response;
110     json kwVal = json::object({});
111 
112     vector<string> keyword{"CC", "SN", "PN", "FN", "DR"};
113     string interface = "com.ibm.ipzvpd.VINI";
114     string objectName = {};
115 
116     if (invPath.find(INVENTORY_PATH) != string::npos)
117     {
118         objectName = invPath;
119         eraseInventoryPath(invPath);
120     }
121     else
122     {
123         objectName = INVENTORY_PATH + invPath;
124     }
125     for (string kw : keyword)
126     {
127         try
128         {
129             makeDBusCall(objectName, interface, kw).read(response);
130 
131             if (auto vec = get_if<Binary>(&response))
132             {
133                 string printableVal = getPrintableValue(*vec);
134                 kwVal.emplace(kw, printableVal);
135             }
136         }
137         catch (const sdbusplus::exception_t& e)
138         {
139             if (string(e.name()) ==
140                 string("org.freedesktop.DBus.Error.UnknownObject"))
141             {
142                 kwVal.emplace(invPath, json::object({}));
143                 objFound = false;
144                 break;
145             }
146         }
147     }
148 
149     return kwVal;
150 }
151 
152 void VpdTool::getExtraInterfaceProperties(const string& invPath,
153                                           const string& extraInterface,
154                                           const json& prop, json& output)
155 {
156     variant<string> response;
157 
158     string objectName = INVENTORY_PATH + invPath;
159 
160     for (const auto& itProp : prop.items())
161     {
162         string kw = itProp.key();
163         try
164         {
165             makeDBusCall(objectName, extraInterface, kw).read(response);
166 
167             if (auto str = get_if<string>(&response))
168             {
169                 output.emplace(kw, *str);
170             }
171         }
172         catch (const sdbusplus::exception_t& e)
173         {
174             if (std::string(e.name()) ==
175                 std::string("org.freedesktop.DBus.Error.UnknownObject"))
176             {
177                 objFound = false;
178                 break;
179             }
180             else if (std::string(e.name()) ==
181                      std::string("org.freedesktop.DBus.Error.UnknownProperty"))
182             {
183                 output.emplace(kw, "");
184             }
185         }
186     }
187 }
188 
189 json VpdTool::interfaceDecider(json& itemEEPROM)
190 {
191     if (itemEEPROM.find("inventoryPath") == itemEEPROM.end())
192     {
193         throw runtime_error("Inventory Path not found");
194     }
195 
196     if (itemEEPROM.find("extraInterfaces") == itemEEPROM.end())
197     {
198         throw runtime_error("Extra Interfaces not found");
199     }
200 
201     json subOutput = json::object({});
202     fruType = "FRU";
203 
204     json j;
205     objFound = true;
206     string invPath = itemEEPROM.at("inventoryPath");
207 
208     j = getVINIProperties(invPath);
209 
210     if (objFound)
211     {
212         subOutput.insert(j.begin(), j.end());
213         json js;
214         if (itemEEPROM.find("type") != itemEEPROM.end())
215         {
216             fruType = itemEEPROM.at("type");
217         }
218         js.emplace("TYPE", fruType);
219 
220         if (invPath.find("powersupply") != string::npos)
221         {
222             js.emplace("type", POWER_SUPPLY_TYPE_INTERFACE);
223         }
224         else if (invPath.find("fan") != string::npos)
225         {
226             js.emplace("type", FAN_INTERFACE);
227         }
228 
229         for (const auto& ex : itemEEPROM["extraInterfaces"].items())
230         {
231             if (!(ex.value().is_null()))
232             {
233                 // TODO: Remove this if condition check once inventory json is
234                 // updated with xyz location code interface.
235                 if (ex.key() == "com.ibm.ipzvpd.Location")
236                 {
237                     getExtraInterfaceProperties(
238                         invPath,
239                         "xyz.openbmc_project.Inventory.Decorator.LocationCode",
240                         ex.value(), js);
241                 }
242                 else
243                 {
244                     getExtraInterfaceProperties(invPath, ex.key(), ex.value(),
245                                                 js);
246                 }
247             }
248             if ((ex.key().find("Item") != string::npos) &&
249                 (ex.value().is_null()))
250             {
251                 js.emplace("type", ex.key());
252             }
253             subOutput.insert(js.begin(), js.end());
254         }
255     }
256     return subOutput;
257 }
258 
259 json VpdTool::getPresentPropJson(const std::string& invPath,
260                                  std::string& parentPresence)
261 {
262     std::variant<bool> response;
263     makeDBusCall(invPath, "xyz.openbmc_project.Inventory.Item", "Present")
264         .read(response);
265 
266     std::string presence{};
267 
268     if (auto pVal = get_if<bool>(&response))
269     {
270         presence = *pVal ? "true" : "false";
271         if (parentPresence.empty())
272         {
273             parentPresence = presence;
274         }
275     }
276     else
277     {
278         presence = parentPresence;
279     }
280 
281     json js;
282     js.emplace("Present", presence);
283     return js;
284 }
285 
286 json VpdTool::parseInvJson(const json& jsObject, char flag, string fruPath)
287 {
288     json output = json::object({});
289     bool validObject = false;
290 
291     if (jsObject.find("frus") == jsObject.end())
292     {
293         throw runtime_error("Frus missing in Inventory json");
294     }
295     else
296     {
297         for (const auto& itemFRUS : jsObject["frus"].items())
298         {
299             string parentPresence{};
300             for (auto itemEEPROM : itemFRUS.value())
301             {
302                 json subOutput = json::object({});
303                 try
304                 {
305                     if (flag == 'O')
306                     {
307                         if (itemEEPROM.find("inventoryPath") ==
308                             itemEEPROM.end())
309                         {
310                             throw runtime_error("Inventory Path not found");
311                         }
312                         else if (itemEEPROM.at("inventoryPath") == fruPath)
313                         {
314                             validObject = true;
315                             subOutput = interfaceDecider(itemEEPROM);
316                             json presentJs = getPresentPropJson(
317                                 "/xyz/openbmc_project/inventory" + fruPath,
318                                 parentPresence);
319                             subOutput.insert(presentJs.begin(),
320                                              presentJs.end());
321                             output.emplace(fruPath, subOutput);
322                             return output;
323                         }
324                         else // this else is to keep track of parent present
325                              // property.
326                         {
327                             json presentJs = getPresentPropJson(
328                                 "/xyz/openbmc_project/inventory" +
329                                     string(itemEEPROM.at("inventoryPath")),
330                                 parentPresence);
331                         }
332                     }
333                     else
334                     {
335                         subOutput = interfaceDecider(itemEEPROM);
336                         json presentJs = getPresentPropJson(
337                             "/xyz/openbmc_project/inventory" +
338                                 string(itemEEPROM.at("inventoryPath")),
339                             parentPresence);
340                         subOutput.insert(presentJs.begin(), presentJs.end());
341                         output.emplace(string(itemEEPROM.at("inventoryPath")),
342                                        subOutput);
343                     }
344                 }
345                 catch (const sdbusplus::exception::SdBusError& e)
346                 {
347                     // if any of frupath doesn't have Present property of its
348                     // own, emplace its parent's present property value.
349                     if (e.name() == std::string("org.freedesktop.DBus.Error."
350                                                 "UnknownProperty") &&
351                         (((flag == 'O') && validObject) || flag == 'I'))
352                     {
353                         json presentJs;
354                         presentJs.emplace("Present", parentPresence);
355                         subOutput.insert(presentJs.begin(), presentJs.end());
356                         output.emplace(string(itemEEPROM.at("inventoryPath")),
357                                        subOutput);
358                     }
359 
360                     // for the user given child frupath which doesn't have
361                     // Present prop (vpd-tool -o).
362                     if ((flag == 'O') && validObject)
363                     {
364                         return output;
365                     }
366                 }
367                 catch (const exception& e)
368                 {
369                     cerr << e.what();
370                 }
371             }
372         }
373         if ((flag == 'O') && (!validObject))
374         {
375             throw runtime_error(
376                 "Invalid object path. Refer --dumpInventory/-i option.");
377         }
378     }
379     return output;
380 }
381 
382 void VpdTool::dumpInventory(const nlohmann::basic_json<>& jsObject)
383 {
384     char flag = 'I';
385     json output = json::array({});
386     output.emplace_back(parseInvJson(jsObject, flag, ""));
387     debugger(output);
388 }
389 
390 void VpdTool::dumpObject(const nlohmann::basic_json<>& jsObject)
391 {
392     char flag = 'O';
393     json output = json::array({});
394     output.emplace_back(parseInvJson(jsObject, flag, fruPath));
395     debugger(output);
396 }
397 
398 void VpdTool::readKeyword()
399 {
400     string interface = "com.ibm.ipzvpd.";
401     variant<Binary> response;
402 
403     try
404     {
405         json output = json::object({});
406         json kwVal = json::object({});
407         makeDBusCall(INVENTORY_PATH + fruPath, interface + recordName, keyword)
408             .read(response);
409 
410         string printableVal{};
411         if (auto vec = get_if<Binary>(&response))
412         {
413             printableVal = getPrintableValue(*vec);
414         }
415         kwVal.emplace(keyword, printableVal);
416 
417         output.emplace(fruPath, kwVal);
418 
419         debugger(output);
420     }
421     catch (const json::exception& e)
422     {
423         json output = json::object({});
424         json kwVal = json::object({});
425     }
426 }
427 
428 int VpdTool::updateKeyword()
429 {
430     Binary val = toBinary(value);
431     auto bus = sdbusplus::bus::new_default();
432     auto properties =
433         bus.new_method_call(BUSNAME, OBJPATH, IFACE, "WriteKeyword");
434     properties.append(static_cast<sdbusplus::message::object_path>(fruPath));
435     properties.append(recordName);
436     properties.append(keyword);
437     properties.append(val);
438     auto result = bus.call(properties);
439 
440     if (result.is_method_error())
441     {
442         throw runtime_error("Get api failed");
443     }
444     return 0;
445 }
446 
447 void VpdTool::forceReset(const nlohmann::basic_json<>& jsObject)
448 {
449     for (const auto& itemFRUS : jsObject["frus"].items())
450     {
451         for (const auto& itemEEPROM : itemFRUS.value().items())
452         {
453             string fru = itemEEPROM.value().at("inventoryPath");
454 
455             fs::path fruCachePath = INVENTORY_MANAGER_CACHE;
456             fruCachePath += INVENTORY_PATH;
457             fruCachePath += fru;
458 
459             try
460             {
461                 for (const auto& it : fs::directory_iterator(fruCachePath))
462                 {
463                     if (fs::is_regular_file(it.status()))
464                     {
465                         fs::remove(it);
466                     }
467                 }
468             }
469             catch (const fs::filesystem_error& e)
470             {
471             }
472         }
473     }
474 
475     cout.flush();
476     string udevRemove = "udevadm trigger -c remove -s \"*nvmem*\" -v";
477     int returnCode = system(udevRemove.c_str());
478     printReturnCode(returnCode);
479 
480     string invManagerRestart =
481         "systemctl restart xyz.openbmc_project.Inventory.Manager.service";
482     returnCode = system(invManagerRestart.c_str());
483     printReturnCode(returnCode);
484 
485     string sysVpdRestart = "systemctl restart system-vpd.service";
486     returnCode = system(sysVpdRestart.c_str());
487     printReturnCode(returnCode);
488 
489     string udevAdd = "udevadm trigger -c add -s \"*nvmem*\" -v";
490     returnCode = system(udevAdd.c_str());
491     printReturnCode(returnCode);
492 }
493 
494 int VpdTool::updateHardware(const uint32_t offset)
495 {
496     int rc = 0;
497     const Binary& val = static_cast<const Binary&>(toBinary(value));
498     ifstream inventoryJson(INVENTORY_JSON_SYM_LINK);
499     try
500     {
501         auto json = nlohmann::json::parse(inventoryJson);
502         EditorImpl edit(fruPath, json, recordName, keyword);
503 
504         edit.updateKeyword(val, offset, false);
505     }
506     catch (const json::parse_error& ex)
507     {
508         throw(VpdJsonException("Json Parsing failed", INVENTORY_JSON_SYM_LINK));
509     }
510     return rc;
511 }
512 
513 void VpdTool::readKwFromHw(const uint32_t& startOffset)
514 {
515     ifstream inventoryJson(INVENTORY_JSON_SYM_LINK);
516     auto jsonFile = nlohmann::json::parse(inventoryJson);
517 
518     Binary completeVPDFile;
519     completeVPDFile.resize(65504);
520     fstream vpdFileStream;
521     vpdFileStream.open(fruPath,
522                        std::ios::in | std::ios::out | std::ios::binary);
523 
524     vpdFileStream.seekg(startOffset, ios_base::cur);
525     vpdFileStream.read(reinterpret_cast<char*>(&completeVPDFile[0]), 65504);
526     completeVPDFile.resize(vpdFileStream.gcount());
527     vpdFileStream.clear(std::ios_base::eofbit);
528 
529     if (completeVPDFile.empty())
530     {
531         throw std::runtime_error("Invalid File");
532     }
533 
534     const std::string& inventoryPath =
535         jsonFile["frus"][fruPath][0]["inventoryPath"];
536 
537     Impl obj(completeVPDFile, (constants::pimPath + inventoryPath));
538     std::string keywordVal = obj.readKwFromHw(recordName, keyword);
539 
540     if (!keywordVal.empty())
541     {
542         json output = json::object({});
543         json kwVal = json::object({});
544         kwVal.emplace(keyword, keywordVal);
545 
546         output.emplace(fruPath, kwVal);
547 
548         debugger(output);
549     }
550     else
551     {
552         std::cerr << "The given keyword " << keyword << " or record "
553                   << recordName
554                   << " or both are not present in the given FRU path "
555                   << fruPath << std::endl;
556     }
557 }
558 
559 void VpdTool::printFixSystemVPDOption(UserOption option)
560 {
561     switch (option)
562     {
563         case VpdTool::EXIT:
564             cout << "\nEnter 0 => To exit successfully : ";
565             break;
566         case VpdTool::BMC_DATA_FOR_ALL:
567             cout << "\n\nEnter 1 => If you choose the data on BMC for all "
568                     "mismatching record-keyword pairs";
569             break;
570         case VpdTool::SYSTEM_BACKPLANE_DATA_FOR_ALL:
571             cout << "\nEnter 2 => If you choose the data on System "
572                     "Backplane for all mismatching record-keyword pairs";
573             break;
574         case VpdTool::MORE_OPTIONS:
575             cout << "\nEnter 3 => If you wish to explore more options";
576             break;
577         case VpdTool::BMC_DATA_FOR_CURRENT:
578             cout << "\nEnter 4 => If you choose the data on BMC as the "
579                     "right value";
580             break;
581         case VpdTool::SYSTEM_BACKPLANE_DATA_FOR_CURRENT:
582             cout << "\nEnter 5 => If you choose the data on System "
583                     "Backplane as the right value";
584             break;
585         case VpdTool::NEW_VALUE_ON_BOTH:
586             cout << "\nEnter 6 => If you wish to enter a new value to "
587                     "update both on BMC and System Backplane";
588             break;
589         case VpdTool::SKIP_CURRENT:
590             cout << "\nEnter 7 => If you wish to skip the above "
591                     "record-keyword pair";
592             break;
593     }
594 }
595 
596 int VpdTool::fixSystemVPD()
597 {
598     std::string outline(191, '=');
599     cout << "\nRestorable record-keyword pairs and their data on BMC & "
600             "System Backplane.\n\n"
601          << outline << std::endl;
602 
603     cout << left << setw(6) << "S.No" << left << setw(8) << "Record" << left
604          << setw(9) << "Keyword" << left << setw(75) << "Data On BMC" << left
605          << setw(75) << "Data On System Backplane" << left << setw(14)
606          << "Data Mismatch\n"
607          << outline << std::endl;
608 
609     int num = 0;
610 
611     // Get system VPD data in map
612     Binary vpdVector{};
613     json js{};
614 
615     auto jsonToParse = INVENTORY_JSON_DEFAULT;
616     if (fs::exists(INVENTORY_JSON_SYM_LINK))
617     {
618         jsonToParse = INVENTORY_JSON_SYM_LINK;
619     }
620 
621     ifstream inventoryJson(jsonToParse);
622     if (!inventoryJson)
623     {
624         throw runtime_error("VPD JSON file not found");
625     }
626     js = json::parse(inventoryJson);
627 
628     vpdVector = getVpdDataInVector(js, constants::systemVpdFilePath);
629     ParserInterface* parser = ParserFactory::getParser(
630         vpdVector, constants::pimPath + std::string(constants::SYSTEM_OBJECT));
631     auto parseResult = parser->parse();
632     ParserFactory::freeParser(parser);
633 
634     unordered_map<string, DbusPropertyMap> vpdMap;
635 
636     if (auto pVal = get_if<Store>(&parseResult))
637     {
638         vpdMap = pVal->getVpdMap();
639     }
640     else
641     {
642         std::cerr << "\n System backplane VPD is not of type IPZ. Unable to "
643                      "parse the VPD "
644                   << constants::systemVpdFilePath << " . Exit with error."
645                   << std::endl;
646         exit(1);
647     }
648 
649     // Get system VPD D-Bus Data and store it in a map
650     using GetAllResultType =
651         std::vector<std::pair<std::string, std::variant<Binary>>>;
652     using IntfPropMap = std::map<std::string, GetAllResultType>;
653 
654     IntfPropMap svpdBusData;
655 
656     const auto vsys = getAllDBusProperty<GetAllResultType>(
657         constants::pimIntf,
658         "/xyz/openbmc_project/inventory/system/chassis/motherboard",
659         "com.ibm.ipzvpd.VSYS");
660     svpdBusData.emplace("VSYS", vsys);
661 
662     const auto vcen = getAllDBusProperty<GetAllResultType>(
663         constants::pimIntf,
664         "/xyz/openbmc_project/inventory/system/chassis/motherboard",
665         "com.ibm.ipzvpd.VCEN");
666     svpdBusData.emplace("VCEN", vcen);
667 
668     const auto lxr0 = getAllDBusProperty<GetAllResultType>(
669         constants::pimIntf,
670         "/xyz/openbmc_project/inventory/system/chassis/motherboard",
671         "com.ibm.ipzvpd.LXR0");
672     svpdBusData.emplace("LXR0", lxr0);
673 
674     const auto util = getAllDBusProperty<GetAllResultType>(
675         constants::pimIntf,
676         "/xyz/openbmc_project/inventory/system/chassis/motherboard",
677         "com.ibm.ipzvpd.UTIL");
678     svpdBusData.emplace("UTIL", util);
679 
680     for (const auto& recordKw : svpdKwdMap)
681     {
682         string record = recordKw.first;
683 
684         // Extract specific record data from the svpdBusData map.
685         const auto& rec = svpdBusData.find(record);
686 
687         if (rec == svpdBusData.end())
688         {
689             std::cerr << record << " not a part of critical system VPD records."
690                       << std::endl;
691             continue;
692         }
693 
694         const auto& recData = svpdBusData.find(record)->second;
695 
696         string busStr{}, hwValStr{};
697         string mismatch = "NO"; // no mismatch
698 
699         for (const auto& keyword : recordKw.second)
700         {
701             string hardwareValue{};
702             auto recItr = vpdMap.find(record);
703 
704             if (recItr != vpdMap.end())
705             {
706                 DbusPropertyMap& kwValMap = recItr->second;
707                 auto kwItr = kwValMap.find(keyword);
708                 if (kwItr != kwValMap.end())
709                 {
710                     hardwareValue = kwItr->second;
711                 }
712             }
713 
714             std::variant<Binary> kwValue;
715             for (auto& kwData : recData)
716             {
717                 if (kwData.first == keyword)
718                 {
719                     kwValue = kwData.second;
720                     break;
721                 }
722             }
723 
724             if (keyword != "SE")
725             {
726                 ostringstream hwValStream;
727                 hwValStream << "0x";
728                 hwValStr = hwValStream.str();
729 
730                 for (uint16_t byte : hardwareValue)
731                 {
732                     hwValStream << setfill('0') << setw(2) << hex << byte;
733                     hwValStr = hwValStream.str();
734                 }
735 
736                 if (const auto value = get_if<Binary>(&kwValue))
737                 {
738                     busStr = byteArrayToHexString(*value);
739                 }
740                 if (busStr != hwValStr)
741                 {
742                     mismatch = "YES";
743                 }
744             }
745             else
746             {
747                 if (const auto value = get_if<Binary>(&kwValue))
748                 {
749                     busStr = getPrintableValue(*value);
750                 }
751                 if (busStr != hardwareValue)
752                 {
753                     mismatch = "YES";
754                 }
755                 hwValStr = hardwareValue;
756             }
757             recKwData.push_back(
758                 make_tuple(++num, record, keyword, busStr, hwValStr, mismatch));
759 
760             std::string splitLine(191, '-');
761             cout << left << setw(6) << num << left << setw(8) << record << left
762                  << setw(9) << keyword << left << setw(75) << setfill(' ')
763                  << busStr << left << setw(75) << setfill(' ') << hwValStr
764                  << left << setw(14) << mismatch << '\n'
765                  << splitLine << endl;
766         }
767     }
768     parseSVPDOptions(js);
769     return 0;
770 }
771 
772 void VpdTool::parseSVPDOptions(const nlohmann::json& json)
773 {
774     do
775     {
776         printFixSystemVPDOption(VpdTool::BMC_DATA_FOR_ALL);
777         printFixSystemVPDOption(VpdTool::SYSTEM_BACKPLANE_DATA_FOR_ALL);
778         printFixSystemVPDOption(VpdTool::MORE_OPTIONS);
779         printFixSystemVPDOption(VpdTool::EXIT);
780 
781         int option = 0;
782         cin >> option;
783 
784         std::string outline(191, '=');
785         cout << '\n' << outline << endl;
786 
787         if (json.find("frus") == json.end())
788         {
789             throw runtime_error("Frus not found in json");
790         }
791 
792         bool mismatchFound = false;
793 
794         if (option == VpdTool::BMC_DATA_FOR_ALL)
795         {
796             for (const auto& data : recKwData)
797             {
798                 if (get<5>(data) == "YES")
799                 {
800                     EditorImpl edit(constants::systemVpdFilePath, json,
801                                     get<1>(data), get<2>(data));
802                     edit.updateKeyword(toBinary(get<3>(data)), 0, true);
803                     mismatchFound = true;
804                 }
805             }
806 
807             if (mismatchFound)
808             {
809                 cout << "\nData updated successfully for all mismatching "
810                         "record-keyword pairs by choosing their corresponding "
811                         "data on BMC. Exit successfully.\n"
812                      << endl;
813             }
814             else
815             {
816                 cout << "\nNo mismatch found for any of the above mentioned "
817                         "record-keyword pair. Exit successfully.\n";
818             }
819 
820             exit(0);
821         }
822         else if (option == VpdTool::SYSTEM_BACKPLANE_DATA_FOR_ALL)
823         {
824             for (const auto& data : recKwData)
825             {
826                 if (get<5>(data) == "YES")
827                 {
828                     EditorImpl edit(constants::systemVpdFilePath, json,
829                                     get<1>(data), get<2>(data));
830                     edit.updateKeyword(toBinary(get<4>(data)), 0, true);
831                     mismatchFound = true;
832                 }
833             }
834 
835             if (mismatchFound)
836             {
837                 cout << "\nData updated successfully for all mismatching "
838                         "record-keyword pairs by choosing their corresponding "
839                         "data on System Backplane.\n"
840                      << endl;
841             }
842             else
843             {
844                 cout << "\nNo mismatch found for any of the above mentioned "
845                         "record-keyword pair. Exit successfully.\n";
846             }
847 
848             exit(0);
849         }
850         else if (option == VpdTool::MORE_OPTIONS)
851         {
852             cout << "\nIterate through all restorable record-keyword pairs\n";
853 
854             for (const auto& data : recKwData)
855             {
856                 do
857                 {
858                     cout << '\n' << outline << endl;
859 
860                     cout << left << setw(6) << "S.No" << left << setw(8)
861                          << "Record" << left << setw(9) << "Keyword" << left
862                          << setw(75) << setfill(' ') << "Data On BMC" << left
863                          << setw(75) << setfill(' ')
864                          << "Data On System Backplane" << left << setw(14)
865                          << "Data Mismatch" << endl;
866 
867                     cout << left << setw(6) << get<0>(data) << left << setw(8)
868                          << get<1>(data) << left << setw(9) << get<2>(data)
869                          << left << setw(75) << setfill(' ') << get<3>(data)
870                          << left << setw(75) << setfill(' ') << get<4>(data)
871                          << left << setw(14) << get<5>(data);
872 
873                     cout << '\n' << outline << endl;
874 
875                     if (get<5>(data) == "NO")
876                     {
877                         cout << "\nNo mismatch found.\n";
878                         printFixSystemVPDOption(VpdTool::NEW_VALUE_ON_BOTH);
879                         printFixSystemVPDOption(VpdTool::SKIP_CURRENT);
880                         printFixSystemVPDOption(VpdTool::EXIT);
881                     }
882                     else
883                     {
884                         printFixSystemVPDOption(VpdTool::BMC_DATA_FOR_CURRENT);
885                         printFixSystemVPDOption(
886                             VpdTool::SYSTEM_BACKPLANE_DATA_FOR_CURRENT);
887                         printFixSystemVPDOption(VpdTool::NEW_VALUE_ON_BOTH);
888                         printFixSystemVPDOption(VpdTool::SKIP_CURRENT);
889                         printFixSystemVPDOption(VpdTool::EXIT);
890                     }
891 
892                     cin >> option;
893                     cout << '\n' << outline << endl;
894 
895                     EditorImpl edit(constants::systemVpdFilePath, json,
896                                     get<1>(data), get<2>(data));
897 
898                     if (option == VpdTool::BMC_DATA_FOR_CURRENT)
899                     {
900                         edit.updateKeyword(toBinary(get<3>(data)), 0, true);
901                         cout << "\nData updated successfully.\n";
902                         break;
903                     }
904                     else if (option ==
905                              VpdTool::SYSTEM_BACKPLANE_DATA_FOR_CURRENT)
906                     {
907                         edit.updateKeyword(toBinary(get<4>(data)), 0, true);
908                         cout << "\nData updated successfully.\n";
909                         break;
910                     }
911                     else if (option == VpdTool::NEW_VALUE_ON_BOTH)
912                     {
913                         string value;
914                         cout << "\nEnter the new value to update both on BMC & "
915                                 "System Backplane (Value should be in ASCII or "
916                                 "in HEX(prefixed with 0x)) : ";
917                         cin >> value;
918                         cout << '\n' << outline << endl;
919 
920                         edit.updateKeyword(toBinary(value), 0, true);
921                         cout << "\nData updated successfully.\n";
922                         break;
923                     }
924                     else if (option == VpdTool::SKIP_CURRENT)
925                     {
926                         cout << "\nSkipped the above record-keyword pair. "
927                                 "Continue to the next available pair.\n";
928                         break;
929                     }
930                     else if (option == VpdTool::EXIT)
931                     {
932                         cout << "\nExit successfully\n";
933                         exit(0);
934                     }
935                     else
936                     {
937                         cout << "\nProvide a valid option. Retrying for the "
938                                 "current record-keyword pair\n";
939                     }
940                 } while (1);
941             }
942             exit(0);
943         }
944         else if (option == VpdTool::EXIT)
945         {
946             cout << "\nExit successfully";
947             exit(0);
948         }
949         else
950         {
951             cout << "\nProvide a valid option. Retry.";
952             continue;
953         }
954 
955     } while (true);
956 }
957