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