blob: ce10e6cfa8569ba884de796b442dc6fc030df44b [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 Roy63089422025-01-23 02:48:21 -0600392int VpdTool::cleanSystemVpd(bool i_syncBiosAttributes) const noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500393{
394 try
395 {
Souvik Roy63089422025-01-23 02:48:21 -0600396 (void)i_syncBiosAttributes;
397
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500398 // 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
493bool 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
629nlohmann::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
657bool 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
683void 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
729int 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
844void 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
909int 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
1005int 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