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