blob: f66450c125a6e40fbba7b21e046d4962a3cc8497 [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#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>
12namespace vpd
13{
14int 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.
Anupama B Rd3539902025-01-20 05:10:00 -060076 std::cout << "Invalid data type or empty data received."
77 << std::endl;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050078 }
79 }
80 catch (const std::exception& l_ex)
81 {
82 // TODO: Enable logging when verbose is enabled.
Anupama B Rd3539902025-01-20 05:10:00 -060083 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;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050086 }
87 return l_rc;
88}
89
90int 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.
Souvik Roy549d0902025-01-22 02:37:37 -0600116 std::cerr << "Dump Object failed for FRU [" << i_fruPath
117 << "], Error: " << l_ex.what() << std::endl;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500118 }
119 return l_rc;
120}
121
122nlohmann::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
Souvik Roy549d0902025-01-22 02:37:37 -0600132 // 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));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500137
Souvik Roy549d0902025-01-22 02:37:37 -0600138 l_fruJson.emplace(l_displayObjectPath, nlohmann::json::object_t({}));
139
140 auto& l_fruObject = l_fruJson[l_displayObjectPath];
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500141
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
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500158 // 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 }
Souvik Roya8bb1662025-01-20 01:12:38 -0600182 // 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 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500200
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
Souvik Roya8bb1662025-01-20 01:12:38 -0600208 // insert FRU "TYPE"
209 l_fruObject.emplace("TYPE", "FRU");
210
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500211 return l_fruJson;
212}
213
214template <typename PropertyType>
215nlohmann::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.
Anupama B Rd3539902025-01-20 05:10:00 -0600251 std::cout << "Invalid data type received." << std::endl;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500252 }
253 }
254 catch (const std::exception& l_ex)
255 {
256 // TODO: Enable logging when verbose is enabled.
Anupama B Rd3539902025-01-20 05:10:00 -0600257 std::cerr << "Read " << i_propertyName
258 << " value for FRU path: " << i_objectPath
259 << ", failed with exception: " << l_ex.what() << std::endl;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500260 }
261 return l_resultInJson;
262}
263
264int 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
319int 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
364nlohmann::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
Souvik Roy7f749a62025-02-10 22:23:41 -0600392int VpdTool::cleanSystemVpd(bool i_syncBiosAttributesRequired) const noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500393{
394 try
395 {
Souvik Roy7f749a62025-02-10 22:23:41 -0600396 // In order to do syncBiosAttributes, we need BIOS Config Manager
397 // service up and running
398 if (i_syncBiosAttributesRequired &&
399 !utils::isServiceRunning(constants::biosConfigMgrService))
400 {
401 std::cerr
402 << "Cannot sync BIOS attributes as BIOS Config Manager service is not running."
403 << std::endl;
404 return constants::FAILURE;
405 }
Souvik Roy63089422025-01-23 02:48:21 -0600406
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500407 // get the keyword map from backup_restore json
408 // iterate through the keyword map get default value of
409 // l_keywordName.
410 // use writeKeyword API to update default value on hardware,
411 // backup and D - Bus.
412 const nlohmann::json l_parsedBackupRestoreJson =
413 getBackupRestoreCfgJsonObj();
414
415 // check for mandatory tags
416 if (l_parsedBackupRestoreJson.contains("source") &&
417 l_parsedBackupRestoreJson.contains("backupMap") &&
418 l_parsedBackupRestoreJson["source"].contains("hardwarePath") &&
419 l_parsedBackupRestoreJson["backupMap"].is_array())
420 {
421 // get the source hardware path
422 const auto& l_hardwarePath =
423 l_parsedBackupRestoreJson["source"]["hardwarePath"];
424
425 // iterate through the backup map
426 for (const auto& l_aRecordKwInfo :
427 l_parsedBackupRestoreJson["backupMap"])
428 {
429 // check if Manufacturing Reset is required for this entry
430 const bool l_isMfgCleanRequired =
431 l_aRecordKwInfo.value("isManufactureResetRequired", false);
432
433 if (l_isMfgCleanRequired)
434 {
435 // get the Record name and Keyword name
436 const std::string& l_srcRecordName =
437 l_aRecordKwInfo.value("sourceRecord", "");
438 const std::string& l_srcKeywordName =
439 l_aRecordKwInfo.value("sourceKeyword", "");
440
441 // validate the Record name, Keyword name and the
442 // defaultValue
443 if (!l_srcRecordName.empty() && !l_srcKeywordName.empty() &&
444 l_aRecordKwInfo.contains("defaultValue") &&
445 l_aRecordKwInfo["defaultValue"].is_array())
446 {
Souvik Roy7f749a62025-02-10 22:23:41 -0600447 // check if this keyword is used for backing up BIOS
448 // attribute
449 const bool l_isUsedForBiosAttributeBackup =
450 l_aRecordKwInfo.value("isBiosSyncRequired", false);
451
452 const types::BinaryVector l_keywordValueToUpdate =
453 (i_syncBiosAttributesRequired &&
454 l_isUsedForBiosAttributeBackup)
455 ? getVpdValueInBiosConfigManager(
456 l_srcRecordName, l_srcKeywordName)
457 : l_aRecordKwInfo["defaultValue"]
458 .get<types::BinaryVector>();
459
460 if (l_keywordValueToUpdate.empty())
461 {
462 std::cerr << "Failed to update " << l_srcRecordName
463 << ":" << l_srcKeywordName
464 << " . Keyword value to update is empty"
465 << std::endl;
466 continue;
467 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500468
469 // update the Keyword with default value, use D-Bus
470 // method UpdateKeyword exposed by vpd-manager.
471 // Note: writing to all paths (Primary EEPROM path,
472 // Secondary EEPROM path, D-Bus cache and Backup path)
473 // is the responsibility of vpd-manager's UpdateKeyword
474 // API
475 if (constants::FAILURE ==
476 utils::writeKeyword(
477 l_hardwarePath,
478 std::make_tuple(l_srcRecordName,
479 l_srcKeywordName,
Souvik Roy7f749a62025-02-10 22:23:41 -0600480 l_keywordValueToUpdate)))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500481 {
482 // TODO: Enable logging when verbose
483 // is enabled.
484 std::cerr << "Failed to update " << l_srcRecordName
485 << ":" << l_srcKeywordName << std::endl;
486 }
487 }
488 else
489 {
490 std::cerr
491 << "Unrecognized Entry Record [" << l_srcRecordName
492 << "] Keyword [" << l_srcKeywordName
493 << "] in Backup Restore JSON backup map"
494 << std::endl;
495 }
496 } // mfgClean required check
497 } // keyword list loop
498 }
499 else // backupRestoreJson is not valid
500 {
501 std::cerr << "Backup Restore JSON is not valid" << std::endl;
502 }
503
504 // success/failure message
505 std::cout << "The critical keywords from system backplane VPD has "
506 "been reset successfully."
507 << std::endl;
508
509 } // try block end
510 catch (const std::exception& l_ex)
511 {
512 // TODO: Enable logging when verbose is enabled.
513 std::cerr
514 << "Manufacturing reset on system vpd keywords is unsuccessful. Error : "
515 << l_ex.what() << std::endl;
516 }
517 return constants::SUCCESS;
518}
519
520bool VpdTool::fetchKeywordInfo(nlohmann::json& io_parsedJsonObj) const noexcept
521{
522 bool l_returnValue = false;
523 try
524 {
525 if (io_parsedJsonObj.empty() || !io_parsedJsonObj.contains("source") ||
526 !io_parsedJsonObj.contains("destination") ||
527 !io_parsedJsonObj.contains("backupMap"))
528 {
529 throw std::runtime_error("Invalid JSON");
530 }
531
532 std::string l_srcVpdPath;
533 std::string l_dstVpdPath;
534
535 bool l_isSourceOnHardware = false;
536 if (l_srcVpdPath = io_parsedJsonObj["source"].value("hardwarePath", "");
537 !l_srcVpdPath.empty())
538 {
539 l_isSourceOnHardware = true;
540 }
541 else if (l_srcVpdPath =
542 io_parsedJsonObj["source"].value("inventoryPath", "");
543 l_srcVpdPath.empty())
544 {
545 throw std::runtime_error("Source path is empty in JSON");
546 }
547
548 bool l_isDestinationOnHardware = false;
549 if (l_dstVpdPath =
550 io_parsedJsonObj["destination"].value("hardwarePath", "");
551 !l_dstVpdPath.empty())
552 {
553 l_isDestinationOnHardware = true;
554 }
555 else if (l_dstVpdPath =
556 io_parsedJsonObj["destination"].value("inventoryPath", "");
557 l_dstVpdPath.empty())
558 {
559 throw std::runtime_error("Destination path is empty in JSON");
560 }
561
562 for (auto& l_aRecordKwInfo : io_parsedJsonObj["backupMap"])
563 {
564 const std::string& l_srcRecordName =
565 l_aRecordKwInfo.value("sourceRecord", "");
566 const std::string& l_srcKeywordName =
567 l_aRecordKwInfo.value("sourceKeyword", "");
568 const std::string& l_dstRecordName =
569 l_aRecordKwInfo.value("destinationRecord", "");
570 const std::string& l_dstKeywordName =
571 l_aRecordKwInfo.value("destinationKeyword", "");
572
573 if (l_srcRecordName.empty() || l_dstRecordName.empty() ||
574 l_srcKeywordName.empty() || l_dstKeywordName.empty())
575 {
576 // TODO: Enable logging when verbose is enabled.
577 std::cout << "Record or keyword not found in the JSON."
578 << std::endl;
579 continue;
580 }
581
582 types::DbusVariantType l_srcKeywordVariant;
583 if (l_isSourceOnHardware)
584 {
585 l_srcKeywordVariant = utils::readKeywordFromHardware(
586 l_srcVpdPath,
587 std::make_tuple(l_srcRecordName, l_srcKeywordName));
588 }
589 else
590 {
591 l_srcKeywordVariant = utils::readDbusProperty(
592 constants::inventoryManagerService, l_srcVpdPath,
593 constants::ipzVpdInfPrefix + l_srcRecordName,
594 l_srcKeywordName);
595 }
596
597 if (auto l_srcKeywordValue =
598 std::get_if<types::BinaryVector>(&l_srcKeywordVariant);
599 l_srcKeywordValue && !l_srcKeywordValue->empty())
600 {
601 l_aRecordKwInfo["sourcekeywordValue"] = *l_srcKeywordValue;
602 }
603 else
604 {
605 // TODO: Enable logging when verbose is enabled.
606 std::cout
607 << "Invalid data type or empty data received, for source record: "
608 << l_srcRecordName << ", keyword: " << l_srcKeywordName
609 << std::endl;
610 continue;
611 }
612
613 types::DbusVariantType l_dstKeywordVariant;
614 if (l_isDestinationOnHardware)
615 {
616 l_dstKeywordVariant = utils::readKeywordFromHardware(
617 l_dstVpdPath,
618 std::make_tuple(l_dstRecordName, l_dstKeywordName));
619 }
620 else
621 {
622 l_dstKeywordVariant = utils::readDbusProperty(
623 constants::inventoryManagerService, l_dstVpdPath,
624 constants::ipzVpdInfPrefix + l_dstRecordName,
625 l_dstKeywordName);
626 }
627
628 if (auto l_dstKeywordValue =
629 std::get_if<types::BinaryVector>(&l_dstKeywordVariant);
630 l_dstKeywordValue && !l_dstKeywordValue->empty())
631 {
632 l_aRecordKwInfo["destinationkeywordValue"] = *l_dstKeywordValue;
633 }
634 else
635 {
636 // TODO: Enable logging when verbose is enabled.
637 std::cout
638 << "Invalid data type or empty data received, for destination record: "
639 << l_dstRecordName << ", keyword: " << l_dstKeywordName
640 << std::endl;
641 continue;
642 }
643 }
644
645 l_returnValue = true;
646 }
647 catch (const std::exception& l_ex)
648 {
649 // TODO: Enable logging when verbose is enabled.
650 std::cerr << l_ex.what() << std::endl;
651 }
652
653 return l_returnValue;
654}
655
Patrick Williams43fedab2025-02-03 14:28:05 -0500656nlohmann::json VpdTool::getFruTypeProperty(
657 const std::string& i_objectPath) const noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500658{
659 nlohmann::json l_resultInJson = nlohmann::json::object({});
660 std::vector<std::string> l_pimInfList;
661
662 auto l_serviceInfMap = utils::GetServiceInterfacesForObject(
663 i_objectPath, std::vector<std::string>{constants::inventoryItemInf});
664 if (l_serviceInfMap.contains(constants::inventoryManagerService))
665 {
666 l_pimInfList = l_serviceInfMap[constants::inventoryManagerService];
667
668 // iterate through the list and find
669 // "xyz.openbmc_project.Inventory.Item.*"
670 for (const auto& l_interface : l_pimInfList)
671 {
672 if (l_interface.find(constants::inventoryItemInf) !=
673 std::string::npos &&
674 l_interface.length() >
675 std::string(constants::inventoryItemInf).length())
676 {
677 l_resultInJson.emplace("type", l_interface);
678 }
679 }
680 }
681 return l_resultInJson;
682}
683
684bool VpdTool::isFruPresent(const std::string& i_objectPath) const noexcept
685{
686 bool l_returnValue{false};
687 try
688 {
689 types::DbusVariantType l_keyWordValue;
690
691 l_keyWordValue = utils::readDbusProperty(
692 constants::inventoryManagerService, i_objectPath,
693 constants::inventoryItemInf, "Present");
694
695 if (const auto l_value = std::get_if<bool>(&l_keyWordValue))
696 {
697 l_returnValue = *l_value;
698 }
699 }
700 catch (const std::runtime_error& l_ex)
701 {
702 // TODO: Enable logging when verbose is enabled.
703 // std::cerr << "Failed to check \"Present\" property for FRU "
704 // << i_objectPath << " Error: " << l_ex.what() <<
705 // std::endl;
706 }
707 return l_returnValue;
708}
709
710void VpdTool::printFixSystemVpdOption(
711 const types::UserOption& i_option) const noexcept
712{
713 switch (i_option)
714 {
715 case types::UserOption::Exit:
716 std::cout << "Enter 0 => To exit successfully : ";
717 break;
718 case types::UserOption::UseBackupDataForAll:
719 std::cout << "Enter 1 => If you choose the data on backup for all "
720 "mismatching record-keyword pairs"
721 << std::endl;
722 break;
723 case types::UserOption::UseSystemBackplaneDataForAll:
724 std::cout << "Enter 2 => If you choose the data on primary for all "
725 "mismatching record-keyword pairs"
726 << std::endl;
727 break;
728 case types::UserOption::MoreOptions:
729 std::cout << "Enter 3 => If you wish to explore more options"
730 << std::endl;
731 break;
732 case types::UserOption::UseBackupDataForCurrent:
733 std::cout << "Enter 4 => If you choose the data on backup as the "
734 "right value"
735 << std::endl;
736 break;
737 case types::UserOption::UseSystemBackplaneDataForCurrent:
738 std::cout << "Enter 5 => If you choose the data on primary as the "
739 "right value"
740 << std::endl;
741 break;
742 case types::UserOption::NewValueOnBoth:
743 std::cout
744 << "Enter 6 => If you wish to enter a new value to update "
745 "both on backup and primary"
746 << std::endl;
747 break;
748 case types::UserOption::SkipCurrent:
749 std::cout << "Enter 7 => If you wish to skip the above "
750 "record-keyword pair"
751 << std::endl;
752 break;
753 }
754}
755
756int VpdTool::dumpInventory(bool i_dumpTable) const noexcept
757{
758 int l_rc{constants::FAILURE};
759
760 try
761 {
762 // get all object paths under PIM
763 const auto l_objectPaths = utils::GetSubTreePaths(
764 constants::baseInventoryPath, 0,
765 std::vector<std::string>{constants::inventoryItemInf});
766
767 if (!l_objectPaths.empty())
768 {
769 nlohmann::json l_resultInJson = nlohmann::json::array({});
770
771 std::for_each(l_objectPaths.begin(), l_objectPaths.end(),
772 [&](const auto& l_objectPath) {
773 const auto l_fruJson =
774 getFruProperties(l_objectPath);
775 if (!l_fruJson.empty())
776 {
777 if (l_resultInJson.empty())
778 {
779 l_resultInJson += l_fruJson;
780 }
781 else
782 {
783 l_resultInJson.at(0).insert(
784 l_fruJson.cbegin(), l_fruJson.cend());
785 }
786 }
787 });
788
789 if (i_dumpTable)
790 {
791 // create Table object
792 utils::Table l_inventoryTable{};
793
794 // columns to be populated in the Inventory table
795 const std::vector<types::TableColumnNameSizePair>
796 l_tableColumns = {
797 {"FRU", 100}, {"CC", 6}, {"DR", 20},
798 {"LocationCode", 32}, {"PN", 8}, {"PrettyName", 80},
799 {"SubModel", 10}, {"SN", 15}, {"type", 60}};
800
801 types::TableInputData l_tableData;
802
803 // First prepare the Table Columns
804 for (const auto& l_column : l_tableColumns)
805 {
806 if (constants::FAILURE ==
807 l_inventoryTable.AddColumn(l_column.first,
808 l_column.second))
809 {
810 // TODO: Enable logging when verbose is enabled.
811 std::cerr << "Failed to add column " << l_column.first
812 << " in Inventory Table." << std::endl;
813 }
814 }
815
816 // iterate through the json array
817 for (const auto& l_fruEntry : l_resultInJson[0].items())
818 {
819 // if object path ends in "unit([0-9][0-9]?)", skip adding
820 // the object path in the table
821 if (std::regex_search(l_fruEntry.key(),
822 std::regex("unit([0-9][0-9]?)")))
823 {
824 continue;
825 }
826
827 std::vector<std::string> l_row;
828 for (const auto& l_column : l_tableColumns)
829 {
830 const auto& l_fruJson = l_fruEntry.value();
831
832 if (l_column.first == "FRU")
833 {
834 l_row.push_back(l_fruEntry.key());
835 }
836 else
837 {
838 if (l_fruJson.contains(l_column.first))
839 {
840 l_row.push_back(l_fruJson[l_column.first]);
841 }
842 else
843 {
844 l_row.push_back("");
845 }
846 }
847 }
848
849 l_tableData.push_back(l_row);
850 }
851
852 l_rc = l_inventoryTable.Print(l_tableData);
853 }
854 else
855 {
856 // print JSON to console
857 utils::printJson(l_resultInJson);
858 l_rc = constants::SUCCESS;
859 }
860 }
861 }
862 catch (const std::exception& l_ex)
863 {
864 // TODO: Enable logging when verbose is enabled.
865 std::cerr << "Dump inventory failed. Error: " << l_ex.what()
866 << std::endl;
867 }
868 return l_rc;
869}
870
871void VpdTool::printSystemVpd(
872 const nlohmann::json& i_parsedJsonObj) const noexcept
873{
874 if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("backupMap"))
875 {
876 // TODO: Enable logging when verbose is enabled.
877 std::cerr << "Invalid JSON to print system VPD" << std::endl;
878 }
879
880 std::string l_outline(191, '=');
881 std::cout << "\nRestorable record-keyword pairs and their data on backup & "
882 "primary.\n\n"
883 << l_outline << std::endl;
884
885 std::cout << std::left << std::setw(6) << "S.No" << std::left
886 << std::setw(8) << "Record" << std::left << std::setw(9)
887 << "Keyword" << std::left << std::setw(75) << "Data On Backup"
888 << std::left << std::setw(75) << "Data On Primary" << std::left
889 << std::setw(14) << "Data Mismatch\n"
890 << l_outline << std::endl;
891
892 uint8_t l_slNum = 0;
893
894 for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"])
895 {
896 if (l_aRecordKwInfo.contains("sourceRecord") ||
897 l_aRecordKwInfo.contains("sourceKeyword") ||
898 l_aRecordKwInfo.contains("destinationkeywordValue") ||
899 l_aRecordKwInfo.contains("sourcekeywordValue"))
900 {
901 std::string l_mismatchFound{
902 (l_aRecordKwInfo["destinationkeywordValue"] !=
903 l_aRecordKwInfo["sourcekeywordValue"])
904 ? "YES"
905 : "NO"};
906
907 std::string l_splitLine(191, '-');
908
909 try
910 {
911 std::cout << std::left << std::setw(6)
912 << static_cast<int>(++l_slNum) << std::left
913 << std::setw(8)
914 << l_aRecordKwInfo.value("sourceRecord", "")
915 << std::left << std::setw(9)
916 << l_aRecordKwInfo.value("sourceKeyword", "")
917 << std::left << std::setw(75) << std::setfill(' ')
918 << utils::getPrintableValue(
919 l_aRecordKwInfo["destinationkeywordValue"])
920 << std::left << std::setw(75) << std::setfill(' ')
921 << utils::getPrintableValue(
922 l_aRecordKwInfo["sourcekeywordValue"])
923 << std::left << std::setw(14) << l_mismatchFound
924 << '\n'
925 << l_splitLine << std::endl;
926 }
927 catch (const std::exception& l_ex)
928 {
929 // TODO: Enable logging when verbose is enabled.
930 std::cerr << l_ex.what() << std::endl;
931 }
932 }
933 }
934}
935
936int VpdTool::updateAllKeywords(const nlohmann::json& i_parsedJsonObj,
937 bool i_useBackupData) const noexcept
938{
939 int l_rc = constants::FAILURE;
940
941 if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("source") ||
942 !i_parsedJsonObj.contains("backupMap"))
943 {
944 // TODO: Enable logging when verbose is enabled.
945 std::cerr << "Invalid JSON" << std::endl;
946 return l_rc;
947 }
948
949 std::string l_srcVpdPath;
950 if (auto l_vpdPath = i_parsedJsonObj["source"].value("hardwarePath", "");
951 !l_vpdPath.empty())
952 {
953 l_srcVpdPath = l_vpdPath;
954 }
955 else if (auto l_vpdPath =
956 i_parsedJsonObj["source"].value("inventoryPath", "");
957 !l_vpdPath.empty())
958 {
959 l_srcVpdPath = l_vpdPath;
960 }
961 else
962 {
963 // TODO: Enable logging when verbose is enabled.
964 std::cerr << "source path information is missing in JSON" << std::endl;
965 return l_rc;
966 }
967
968 bool l_anyMismatchFound = false;
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 // TODO: Enable logging when verbose is enabled.
977 std::cerr << "Missing required information in the JSON"
978 << std::endl;
979 continue;
980 }
981
982 if (l_aRecordKwInfo["sourcekeywordValue"] !=
983 l_aRecordKwInfo["destinationkeywordValue"])
984 {
985 l_anyMismatchFound = true;
986
987 auto l_keywordValue =
988 i_useBackupData ? l_aRecordKwInfo["destinationkeywordValue"]
989 : l_aRecordKwInfo["sourcekeywordValue"];
990
991 auto l_paramsToWrite = std::make_tuple(
992 l_aRecordKwInfo["sourceRecord"],
993 l_aRecordKwInfo["sourceKeyword"], l_keywordValue);
994
995 try
996 {
997 l_rc = utils::writeKeyword(l_srcVpdPath, l_paramsToWrite);
998 if (l_rc > 0)
999 {
1000 l_rc = constants::SUCCESS;
1001 }
1002 }
1003 catch (const std::exception& l_ex)
1004 {
1005 // TODO: Enable logging when verbose is enabled.
1006 std::cerr << "write keyword failed for record: "
1007 << l_aRecordKwInfo["sourceRecord"]
1008 << ", keyword: " << l_aRecordKwInfo["sourceKeyword"]
1009 << ", error: " << l_ex.what() << std::ends;
1010 }
1011 }
1012 }
1013
1014 std::string l_dataUsed =
1015 (i_useBackupData ? "data from backup" : "data from primary VPD");
1016 if (l_anyMismatchFound)
1017 {
1018 std::cout << "Data updated successfully for all mismatching "
1019 "record-keyword pairs by choosing their corresponding "
1020 << l_dataUsed << ". Exit successfully." << std::endl;
1021 }
1022 else
1023 {
1024 std::cout << "No mismatch found for any of the above mentioned "
1025 "record-keyword pair. Exit successfully."
1026 << std::endl;
1027 }
1028
1029 return l_rc;
1030}
1031
1032int VpdTool::handleMoreOption(
1033 const nlohmann::json& i_parsedJsonObj) const noexcept
1034{
1035 int l_rc = constants::FAILURE;
1036
1037 try
1038 {
1039 if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("backupMap"))
1040 {
1041 throw std::runtime_error("Invalid JSON");
1042 }
1043
1044 std::string l_srcVpdPath;
1045
1046 if (auto l_vpdPath =
1047 i_parsedJsonObj["source"].value("hardwarePath", "");
1048 !l_vpdPath.empty())
1049 {
1050 l_srcVpdPath = l_vpdPath;
1051 }
1052 else if (auto l_vpdPath =
1053 i_parsedJsonObj["source"].value("inventoryPath", "");
1054 !l_vpdPath.empty())
1055 {
1056 l_srcVpdPath = l_vpdPath;
1057 }
1058 else
1059 {
1060 throw std::runtime_error(
1061 "source path information is missing in JSON");
1062 }
1063
1064 auto updateKeywordValue =
1065 [](std::string io_vpdPath, const std::string& i_recordName,
1066 const std::string& i_keywordName,
1067 const types::BinaryVector& i_keywordValue) -> int {
1068 int l_rc = constants::FAILURE;
1069
1070 try
1071 {
1072 auto l_paramsToWrite = std::make_tuple(
1073 i_recordName, i_keywordName, i_keywordValue);
1074 l_rc = utils::writeKeyword(io_vpdPath, l_paramsToWrite);
1075
1076 if (l_rc > 0)
1077 {
1078 std::cout << std::endl
1079 << "Data updated successfully." << std::endl;
1080 }
1081 }
1082 catch (const std::exception& l_ex)
1083 {
1084 // TODO: Enable log when verbose is enabled.
1085 std::cerr << l_ex.what() << std::endl;
1086 }
1087 return l_rc;
1088 };
1089
1090 do
1091 {
1092 int l_slNum = 0;
1093 bool l_exit = false;
1094
1095 for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"])
1096 {
1097 if (!l_aRecordKwInfo.contains("sourceRecord") ||
1098 !l_aRecordKwInfo.contains("sourceKeyword") ||
1099 !l_aRecordKwInfo.contains("destinationkeywordValue") ||
1100 !l_aRecordKwInfo.contains("sourcekeywordValue"))
1101 {
1102 // TODO: Enable logging when verbose is enabled.
1103 std::cerr
1104 << "Source or destination information is missing in the JSON."
1105 << std::endl;
1106 continue;
1107 }
1108
1109 const std::string l_mismatchFound{
1110 (l_aRecordKwInfo["sourcekeywordValue"] !=
1111 l_aRecordKwInfo["destinationkeywordValue"])
1112 ? "YES"
1113 : "NO"};
1114
1115 std::cout << std::endl
1116 << std::left << std::setw(6) << "S.No" << std::left
1117 << std::setw(8) << "Record" << std::left
1118 << std::setw(9) << "Keyword" << std::left
1119 << std::setw(75) << std::setfill(' ') << "Backup Data"
1120 << std::left << std::setw(75) << std::setfill(' ')
1121 << "Primary Data" << std::left << std::setw(14)
1122 << "Data Mismatch" << std::endl;
1123
1124 std::cout << std::left << std::setw(6)
1125 << static_cast<int>(++l_slNum) << std::left
1126 << std::setw(8)
1127 << l_aRecordKwInfo.value("sourceRecord", "")
1128 << std::left << std::setw(9)
1129 << l_aRecordKwInfo.value("sourceKeyword", "")
1130 << std::left << std::setw(75) << std::setfill(' ')
1131 << utils::getPrintableValue(
1132 l_aRecordKwInfo["destinationkeywordValue"])
1133 << std::left << std::setw(75) << std::setfill(' ')
1134 << utils::getPrintableValue(
1135 l_aRecordKwInfo["sourcekeywordValue"])
1136 << std::left << std::setw(14) << l_mismatchFound
1137 << std::endl;
1138
1139 std::cout << std::string(191, '=') << std::endl;
1140
1141 if (constants::STR_CMP_SUCCESS ==
1142 l_mismatchFound.compare("YES"))
1143 {
1144 printFixSystemVpdOption(
1145 types::UserOption::UseBackupDataForCurrent);
1146 printFixSystemVpdOption(
1147 types::UserOption::UseSystemBackplaneDataForCurrent);
1148 printFixSystemVpdOption(types::UserOption::NewValueOnBoth);
1149 printFixSystemVpdOption(types::UserOption::SkipCurrent);
1150 printFixSystemVpdOption(types::UserOption::Exit);
1151 }
1152 else
1153 {
1154 std::cout << "No mismatch found." << std::endl << std::endl;
1155 printFixSystemVpdOption(types::UserOption::NewValueOnBoth);
1156 printFixSystemVpdOption(types::UserOption::SkipCurrent);
1157 printFixSystemVpdOption(types::UserOption::Exit);
1158 }
1159
1160 int l_userSelectedOption = types::UserOption::Exit;
1161 std::cin >> l_userSelectedOption;
1162
1163 if (types::UserOption::UseBackupDataForCurrent ==
1164 l_userSelectedOption)
1165 {
1166 l_rc = updateKeywordValue(
1167 l_srcVpdPath, l_aRecordKwInfo["sourceRecord"],
1168 l_aRecordKwInfo["sourceKeyword"],
1169 l_aRecordKwInfo["destinationkeywordValue"]);
1170 }
1171 else if (types::UserOption::UseSystemBackplaneDataForCurrent ==
1172 l_userSelectedOption)
1173 {
1174 l_rc = updateKeywordValue(
1175 l_srcVpdPath, l_aRecordKwInfo["sourceRecord"],
1176 l_aRecordKwInfo["sourceKeyword"],
1177 l_aRecordKwInfo["sourcekeywordValue"]);
1178 }
1179 else if (types::UserOption::NewValueOnBoth ==
1180 l_userSelectedOption)
1181 {
1182 std::string l_newValue;
1183 std::cout
1184 << std::endl
1185 << "Enter the new value to update on both "
1186 "primary & backup. Value should be in ASCII or "
1187 "in HEX(prefixed with 0x) : ";
1188 std::cin >> l_newValue;
1189 std::cout << std::endl
1190 << std::string(191, '=') << std::endl;
1191
1192 try
1193 {
1194 l_rc = updateKeywordValue(
1195 l_srcVpdPath, l_aRecordKwInfo["sourceRecord"],
1196 l_aRecordKwInfo["sourceKeyword"],
1197 utils::convertToBinary(l_newValue));
1198 }
1199 catch (const std::exception& l_ex)
1200 {
1201 // TODO: Enable logging when verbose is enabled.
1202 std::cerr << l_ex.what() << std::endl;
1203 }
1204 }
1205 else if (types::UserOption::SkipCurrent == l_userSelectedOption)
1206 {
1207 std::cout << std::endl
1208 << "Skipped the above record-keyword pair. "
1209 "Continue to the next available pair."
1210 << std::endl;
1211 }
1212 else if (types::UserOption::Exit == l_userSelectedOption)
1213 {
1214 std::cout << "Exit successfully" << std::endl;
1215 l_exit = true;
1216 break;
1217 }
1218 else
1219 {
1220 std::cout << "Provide a valid option. Retrying for the "
1221 "current record-keyword pair"
1222 << std::endl;
1223 }
1224 }
1225 if (l_exit)
1226 {
1227 l_rc = constants::SUCCESS;
1228 break;
1229 }
1230 } while (true);
1231 }
1232 catch (const std::exception& l_ex)
1233 {
1234 // TODO: Enable logging when verbose is enabled.
1235 std::cerr << l_ex.what() << std::endl;
1236 }
1237
1238 return l_rc;
1239}
1240
Anupama B R6be2c012025-01-23 02:59:27 -06001241int VpdTool::resetVpdOnDbus()
1242{
1243 // ToDo: Implementation needs to be added
1244 return constants::SUCCESS;
1245}
1246
Souvik Roy7f749a62025-02-10 22:23:41 -06001247types::BinaryVector VpdTool::getVpdValueInBiosConfigManager(
1248 [[maybe_unused]] const std::string& i_recordName,
1249 [[maybe_unused]] const std::string& i_keywordName) const
1250{
1251 types::BinaryVector l_result;
1252 // TODO: Use Record name, Keyword name to identify BIOS attribute.
1253 // Get BIOS attribute value from BIOS Config Manager.
1254 // Convert BIOS attribute value to VPD format value in binary.
1255 return l_result;
1256}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001257} // namespace vpd