xref: /openbmc/openpower-vpd-parser/vpd-manager/src/worker.cpp (revision 315488401d4eaf1ba7dd0d82772a7be5652e13ee)
1 #include "config.h"
2 
3 #include "worker.hpp"
4 
5 #include "backup_restore.hpp"
6 #include "configuration.hpp"
7 #include "constants.hpp"
8 #include "event_logger.hpp"
9 #include "exceptions.hpp"
10 #include "logger.hpp"
11 #include "parser.hpp"
12 #include "parser_factory.hpp"
13 #include "parser_interface.hpp"
14 
15 #include <utility/common_utility.hpp>
16 #include <utility/dbus_utility.hpp>
17 #include <utility/json_utility.hpp>
18 #include <utility/vpd_specific_utility.hpp>
19 
20 #include <filesystem>
21 #include <fstream>
22 #include <future>
23 #include <typeindex>
24 #include <unordered_set>
25 
26 namespace vpd
27 {
28 
Worker(std::string pathToConfigJson,uint8_t i_maxThreadCount,types::VpdCollectionMode i_vpdCollectionMode)29 Worker::Worker(std::string pathToConfigJson, uint8_t i_maxThreadCount,
30                types::VpdCollectionMode i_vpdCollectionMode) :
31     m_configJsonPath(pathToConfigJson), m_semaphore(i_maxThreadCount),
32     m_vpdCollectionMode(i_vpdCollectionMode)
33 {
34     // Implies the processing is based on some config JSON
35     if (!m_configJsonPath.empty())
36     {
37         // Check if symlink is already there to confirm fresh boot/factory
38         // reset.
39         if (std::filesystem::exists(INVENTORY_JSON_SYM_LINK))
40         {
41             logging::logMessage("Sym Link already present");
42             m_configJsonPath = INVENTORY_JSON_SYM_LINK;
43             m_isSymlinkPresent = true;
44         }
45 
46         try
47         {
48             uint16_t l_errCode = 0;
49             m_parsedJson =
50                 jsonUtility::getParsedJson(m_configJsonPath, l_errCode);
51 
52             if (l_errCode)
53             {
54                 throw std::runtime_error(
55                     "JSON parsing failed for file [ " + m_configJsonPath +
56                     " ], error : " + commonUtility::getErrCodeMsg(l_errCode));
57             }
58 
59             // check for mandatory fields at this point itself.
60             if (!m_parsedJson.contains("frus"))
61             {
62                 throw std::runtime_error("Mandatory tag(s) missing from JSON");
63             }
64         }
65         catch (const std::exception& ex)
66         {
67             throw(JsonException(ex.what(), m_configJsonPath));
68         }
69     }
70     else
71     {
72         logging::logMessage("Processing in not based on any config JSON");
73     }
74 }
75 
readFitConfigValue()76 static std::string readFitConfigValue()
77 {
78     std::vector<std::string> output =
79         commonUtility::executeCmd("/sbin/fw_printenv");
80     std::string fitConfigValue;
81 
82     for (const auto& entry : output)
83     {
84         auto pos = entry.find("=");
85         auto key = entry.substr(0, pos);
86         if (key != "fitconfig")
87         {
88             continue;
89         }
90 
91         if (pos + 1 < entry.size())
92         {
93             fitConfigValue = entry.substr(pos + 1);
94         }
95     }
96 
97     return fitConfigValue;
98 }
99 
isSystemVPDOnDBus() const100 bool Worker::isSystemVPDOnDBus() const
101 {
102     const std::string& mboardPath =
103         m_parsedJson["frus"][SYSTEM_VPD_FILE_PATH].at(0).value(
104             "inventoryPath", "");
105 
106     if (mboardPath.empty())
107     {
108         throw JsonException("System vpd file path missing in JSON",
109                             INVENTORY_JSON_SYM_LINK);
110     }
111 
112     std::vector<std::string> interfaces = {
113         "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
114 
115     const types::MapperGetObject& objectMap =
116         dbusUtility::getObjectMap(mboardPath, interfaces);
117 
118     if (objectMap.empty())
119     {
120         return false;
121     }
122     return true;
123 }
124 
getIMValue(const types::IPZVpdMap & parsedVpd) const125 std::string Worker::getIMValue(const types::IPZVpdMap& parsedVpd) const
126 {
127     if (parsedVpd.empty())
128     {
129         throw std::runtime_error("Empty VPD map. Can't Extract IM value");
130     }
131 
132     const auto& itrToVSBP = parsedVpd.find("VSBP");
133     if (itrToVSBP == parsedVpd.end())
134     {
135         throw DataException("VSBP record missing.");
136     }
137 
138     const auto& itrToIM = (itrToVSBP->second).find("IM");
139     if (itrToIM == (itrToVSBP->second).end())
140     {
141         throw DataException("IM keyword missing.");
142     }
143 
144     types::BinaryVector imVal;
145     std::copy(itrToIM->second.begin(), itrToIM->second.end(),
146               back_inserter(imVal));
147 
148     std::ostringstream imData;
149     for (auto& aByte : imVal)
150     {
151         imData << std::setw(2) << std::setfill('0') << std::hex
152                << static_cast<int>(aByte);
153     }
154 
155     return imData.str();
156 }
157 
getHWVersion(const types::IPZVpdMap & parsedVpd) const158 std::string Worker::getHWVersion(const types::IPZVpdMap& parsedVpd) const
159 {
160     if (parsedVpd.empty())
161     {
162         throw std::runtime_error("Empty VPD map. Can't Extract HW value");
163     }
164 
165     const auto& itrToVINI = parsedVpd.find("VINI");
166     if (itrToVINI == parsedVpd.end())
167     {
168         throw DataException("VINI record missing.");
169     }
170 
171     const auto& itrToHW = (itrToVINI->second).find("HW");
172     if (itrToHW == (itrToVINI->second).end())
173     {
174         throw DataException("HW keyword missing.");
175     }
176 
177     types::BinaryVector hwVal;
178     std::copy(itrToHW->second.begin(), itrToHW->second.end(),
179               back_inserter(hwVal));
180 
181     // The planar pass only comes from the LSB of the HW keyword,
182     // where as the MSB is used for other purposes such as signifying clock
183     // termination.
184     hwVal[0] = 0x00;
185 
186     std::ostringstream hwString;
187     for (auto& aByte : hwVal)
188     {
189         hwString << std::setw(2) << std::setfill('0') << std::hex
190                  << static_cast<int>(aByte);
191     }
192 
193     return hwString.str();
194 }
195 
fillVPDMap(const std::string & vpdFilePath,types::VPDMapVariant & vpdMap)196 void Worker::fillVPDMap(const std::string& vpdFilePath,
197                         types::VPDMapVariant& vpdMap)
198 {
199     if (vpdFilePath.empty())
200     {
201         throw std::runtime_error("Invalid file path passed to fillVPDMap API.");
202     }
203 
204     if (!std::filesystem::exists(vpdFilePath))
205     {
206         throw std::runtime_error("Can't Find physical file");
207     }
208 
209     std::shared_ptr<Parser> vpdParser =
210         std::make_shared<Parser>(vpdFilePath, m_parsedJson);
211     vpdMap = vpdParser->parse();
212 }
213 
getSystemJson(std::string & systemJson,const types::VPDMapVariant & parsedVpdMap)214 void Worker::getSystemJson(std::string& systemJson,
215                            const types::VPDMapVariant& parsedVpdMap)
216 {
217     if (auto pVal = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
218     {
219         std::string hwKWdValue = getHWVersion(*pVal);
220         if (hwKWdValue.empty())
221         {
222             throw DataException("HW value fetched is empty.");
223         }
224 
225         const std::string& imKwdValue = getIMValue(*pVal);
226         if (imKwdValue.empty())
227         {
228             throw DataException("IM value fetched is empty.");
229         }
230 
231         auto itrToIM = config::systemType.find(imKwdValue);
232         if (itrToIM == config::systemType.end())
233         {
234             throw DataException("IM keyword does not map to any system type");
235         }
236 
237         const types::HWVerList hwVersionList = itrToIM->second.second;
238         if (!hwVersionList.empty())
239         {
240             transform(hwKWdValue.begin(), hwKWdValue.end(), hwKWdValue.begin(),
241                       ::toupper);
242 
243             auto itrToHW =
244                 std::find_if(hwVersionList.begin(), hwVersionList.end(),
245                              [&hwKWdValue](const auto& aPair) {
246                                  return aPair.first == hwKWdValue;
247                              });
248 
249             if (itrToHW != hwVersionList.end())
250             {
251                 if (!(*itrToHW).second.empty())
252                 {
253                     systemJson += (*itrToIM).first + "_" + (*itrToHW).second +
254                                   ".json";
255                 }
256                 else
257                 {
258                     systemJson += (*itrToIM).first + ".json";
259                 }
260                 return;
261             }
262         }
263         systemJson += itrToIM->second.first + ".json";
264         return;
265     }
266 
267     throw DataException(
268         "Invalid VPD type returned from Parser. Can't get system JSON.");
269 }
270 
setEnvAndReboot(const std::string & key,const std::string & value)271 static void setEnvAndReboot(const std::string& key, const std::string& value)
272 {
273     // set env and reboot and break.
274     commonUtility::executeCmd("/sbin/fw_setenv", key, value);
275     logging::logMessage("Rebooting BMC to pick up new device tree");
276 
277     // make dbus call to reboot
278     auto bus = sdbusplus::bus::new_default_system();
279     auto method = bus.new_method_call(
280         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
281         "org.freedesktop.systemd1.Manager", "Reboot");
282     bus.call_noreply(method);
283 }
284 
setJsonSymbolicLink(const std::string & i_systemJson)285 void Worker::setJsonSymbolicLink(const std::string& i_systemJson)
286 {
287     std::error_code l_ec;
288     l_ec.clear();
289 
290     // Check if symlink file path exists and if the JSON at this location is a
291     // symlink.
292     if (m_isSymlinkPresent &&
293         std::filesystem::is_symlink(INVENTORY_JSON_SYM_LINK, l_ec))
294     { // Don't care about exception in "is_symlink". Will continue with creation
295       // of symlink.
296 
297         const auto& l_symlinkFilePth =
298             std::filesystem::read_symlink(INVENTORY_JSON_SYM_LINK, l_ec);
299 
300         if (l_ec)
301         {
302             logging::logMessage(
303                 "Can't read existing symlink. Error =" + l_ec.message() +
304                 "Trying removal of symlink and creation of new symlink.");
305         }
306 
307         // If currently set JSON is the required one. No further processing
308         // required.
309         if (i_systemJson == l_symlinkFilePth)
310         {
311             // Correct symlink already set.
312             return;
313         }
314 
315         if (!std::filesystem::remove(INVENTORY_JSON_SYM_LINK, l_ec))
316         {
317             // No point going further. If removal fails for existing symlink,
318             // create will anyways throw.
319             throw std::runtime_error(
320                 "Removal of symlink failed with Error = " + l_ec.message() +
321                 ". Can't proceed with create_symlink.");
322         }
323     }
324 
325     if (!std::filesystem::exists(VPD_SYMLIMK_PATH, l_ec))
326     {
327         if (l_ec)
328         {
329             throw std::runtime_error(
330                 "File system call to exist failed with error = " +
331                 l_ec.message());
332         }
333 
334         // implies it is a fresh boot/factory reset.
335         // Create the directory for hosting the symlink
336         if (!std::filesystem::create_directories(VPD_SYMLIMK_PATH, l_ec))
337         {
338             if (l_ec)
339             {
340                 throw std::runtime_error(
341                     "File system call to create directory failed with error = " +
342                     l_ec.message());
343             }
344         }
345     }
346 
347     // create a new symlink based on the system
348     std::filesystem::create_symlink(i_systemJson, INVENTORY_JSON_SYM_LINK,
349                                     l_ec);
350 
351     if (l_ec)
352     {
353         throw std::runtime_error(
354             "create_symlink system call failed with error: " + l_ec.message());
355     }
356 
357     // If the flow is at this point implies the symlink was not present there.
358     // Considering this as factory reset.
359     m_isFactoryResetDone = true;
360 }
361 
setDeviceTreeAndJson()362 void Worker::setDeviceTreeAndJson()
363 {
364     setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH,
365                                 constants::vpdCollectionInProgress);
366 
367     // JSON is madatory for processing of this API.
368     if (m_parsedJson.empty())
369     {
370         throw JsonException("System config JSON is empty", m_configJsonPath);
371     }
372 
373     types::VPDMapVariant parsedVpdMap;
374     fillVPDMap(SYSTEM_VPD_FILE_PATH, parsedVpdMap);
375 
376     // Implies it is default JSON.
377     std::string systemJson{JSON_ABSOLUTE_PATH_PREFIX};
378 
379     // ToDo: Need to check if INVENTORY_JSON_SYM_LINK pointing to correct system
380     // This is required to support movement from rainier to Blue Ridge on the
381     // fly.
382 
383     getSystemJson(systemJson, parsedVpdMap);
384 
385     if (!systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX))
386     {
387         throw DataException(
388             "No system JSON found corresponding to IM read from VPD.");
389     }
390 
391     uint16_t l_errCode = 0;
392 
393     // re-parse the JSON once appropriate JSON has been selected.
394     m_parsedJson = jsonUtility::getParsedJson(systemJson, l_errCode);
395 
396     if (l_errCode)
397     {
398         throw(JsonException(
399             "JSON parsing failed for file [ " + systemJson +
400                 " ], error : " + commonUtility::getErrCodeMsg(l_errCode),
401             systemJson));
402     }
403 
404     std::string devTreeFromJson;
405     if (m_parsedJson.contains("devTree"))
406     {
407         devTreeFromJson = m_parsedJson["devTree"];
408 
409         if (devTreeFromJson.empty())
410         {
411             EventLogger::createSyncPel(
412                 types::ErrorType::JsonFailure, types::SeverityType::Error,
413                 __FILE__, __FUNCTION__, 0,
414                 "Mandatory value for device tree missing from JSON[" +
415                     systemJson + "]",
416                 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
417         }
418     }
419 
420     auto fitConfigVal = readFitConfigValue();
421 
422     if (devTreeFromJson.empty() ||
423         fitConfigVal.find(devTreeFromJson) != std::string::npos)
424     { // Skipping setting device tree as either devtree info is missing from
425       // Json or it is rightly set.
426 
427         setJsonSymbolicLink(systemJson);
428 
429         if (isSystemVPDOnDBus())
430         {
431             uint16_t l_errCode = 0;
432             if (jsonUtility::isBackupAndRestoreRequired(m_parsedJson,
433                                                         l_errCode))
434             {
435                 performBackupAndRestore(parsedVpdMap);
436             }
437             else if (l_errCode)
438             {
439                 logging::logMessage(
440                     "Failed to check if backup and restore required. Reason : " +
441                     commonUtility::getErrCodeMsg(l_errCode));
442             }
443         }
444 
445         // proceed to publish system VPD.
446         publishSystemVPD(parsedVpdMap);
447         setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH,
448                                     constants::vpdCollectionCompleted);
449         return;
450     }
451 
452     setEnvAndReboot("fitconfig", devTreeFromJson);
453     exit(EXIT_SUCCESS);
454 }
455 
populateIPZVPDpropertyMap(types::InterfaceMap & interfacePropMap,const types::IPZKwdValueMap & keyordValueMap,const std::string & interfaceName)456 void Worker::populateIPZVPDpropertyMap(
457     types::InterfaceMap& interfacePropMap,
458     const types::IPZKwdValueMap& keyordValueMap,
459     const std::string& interfaceName)
460 {
461     types::PropertyMap propertyValueMap;
462     for (const auto& kwdVal : keyordValueMap)
463     {
464         auto kwd = kwdVal.first;
465 
466         if (kwd[0] == '#')
467         {
468             kwd = std::string("PD_") + kwd[1];
469         }
470         else if (isdigit(kwd[0]))
471         {
472             kwd = std::string("N_") + kwd;
473         }
474 
475         types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end());
476         propertyValueMap.emplace(move(kwd), move(value));
477     }
478 
479     if (!propertyValueMap.empty())
480     {
481         interfacePropMap.emplace(interfaceName, propertyValueMap);
482     }
483 }
484 
populateKwdVPDpropertyMap(const types::KeywordVpdMap & keyordVPDMap,types::InterfaceMap & interfaceMap)485 void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
486                                        types::InterfaceMap& interfaceMap)
487 {
488     for (const auto& kwdValMap : keyordVPDMap)
489     {
490         types::PropertyMap propertyValueMap;
491         auto kwd = kwdValMap.first;
492 
493         if (kwd[0] == '#')
494         {
495             kwd = std::string("PD_") + kwd[1];
496         }
497         else if (isdigit(kwd[0]))
498         {
499             kwd = std::string("N_") + kwd;
500         }
501 
502         if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second))
503         {
504             types::BinaryVector value((*keywordValue).begin(),
505                                       (*keywordValue).end());
506             propertyValueMap.emplace(move(kwd), move(value));
507         }
508         else if (auto keywordValue = get_if<std::string>(&kwdValMap.second))
509         {
510             types::BinaryVector value((*keywordValue).begin(),
511                                       (*keywordValue).end());
512             propertyValueMap.emplace(move(kwd), move(value));
513         }
514         else if (auto keywordValue = get_if<size_t>(&kwdValMap.second))
515         {
516             if (kwd == "MemorySizeInKB")
517             {
518                 types::PropertyMap memProp;
519                 memProp.emplace(move(kwd), ((*keywordValue)));
520                 interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm",
521                                      move(memProp));
522                 continue;
523             }
524             else
525             {
526                 logging::logMessage(
527                     "Unknown Keyword =" + kwd + " found in keyword VPD map");
528                 continue;
529             }
530         }
531         else
532         {
533             logging::logMessage(
534                 "Unknown variant type found in keyword VPD map.");
535             continue;
536         }
537 
538         if (!propertyValueMap.empty())
539         {
540             uint16_t l_errCode = 0;
541             vpdSpecificUtility::insertOrMerge(
542                 interfaceMap, constants::kwdVpdInf, move(propertyValueMap),
543                 l_errCode);
544 
545             if (l_errCode)
546             {
547                 logging::logMessage(
548                     "Failed to insert value into map, error : " +
549                     commonUtility::getErrCodeMsg(l_errCode));
550             }
551         }
552     }
553 }
554 
populateInterfaces(const nlohmann::json & interfaceJson,types::InterfaceMap & interfaceMap,const types::VPDMapVariant & parsedVpdMap)555 void Worker::populateInterfaces(const nlohmann::json& interfaceJson,
556                                 types::InterfaceMap& interfaceMap,
557                                 const types::VPDMapVariant& parsedVpdMap)
558 {
559     for (const auto& interfacesPropPair : interfaceJson.items())
560     {
561         uint16_t l_errCode = 0;
562         const std::string& interface = interfacesPropPair.key();
563         types::PropertyMap propertyMap;
564 
565         for (const auto& propValuePair : interfacesPropPair.value().items())
566         {
567             const std::string property = propValuePair.key();
568 
569             if (propValuePair.value().is_boolean())
570             {
571                 propertyMap.emplace(property,
572                                     propValuePair.value().get<bool>());
573             }
574             else if (propValuePair.value().is_string())
575             {
576                 if (property.compare("LocationCode") == 0 &&
577                     interface.compare("com.ibm.ipzvpd.Location") == 0)
578                 {
579                     std::string value =
580                         vpdSpecificUtility::getExpandedLocationCode(
581                             propValuePair.value().get<std::string>(),
582                             parsedVpdMap);
583                     propertyMap.emplace(property, value);
584 
585                     auto l_locCodeProperty = propertyMap;
586                     vpdSpecificUtility::insertOrMerge(
587                         interfaceMap,
588                         std::string(constants::xyzLocationCodeInf),
589                         move(l_locCodeProperty), l_errCode);
590 
591                     if (l_errCode)
592                     {
593                         logging::logMessage(
594                             "Failed to insert value into map, error : " +
595                             commonUtility::getErrCodeMsg(l_errCode));
596                     }
597                 }
598                 else
599                 {
600                     propertyMap.emplace(
601                         property, propValuePair.value().get<std::string>());
602                 }
603             }
604             else if (propValuePair.value().is_array())
605             {
606                 try
607                 {
608                     propertyMap.emplace(
609                         property,
610                         propValuePair.value().get<types::BinaryVector>());
611                 }
612                 catch (const nlohmann::detail::type_error& e)
613                 {
614                     std::cerr << "Type exception: " << e.what() << "\n";
615                 }
616             }
617             else if (propValuePair.value().is_number())
618             {
619                 // For now assume the value is a size_t.  In the future it would
620                 // be nice to come up with a way to get the type from the JSON.
621                 propertyMap.emplace(property,
622                                     propValuePair.value().get<size_t>());
623             }
624             else if (propValuePair.value().is_object())
625             {
626                 const std::string& record =
627                     propValuePair.value().value("recordName", "");
628                 const std::string& keyword =
629                     propValuePair.value().value("keywordName", "");
630                 const std::string& encoding =
631                     propValuePair.value().value("encoding", "");
632 
633                 uint16_t l_errCode = 0;
634 
635                 if (auto ipzVpdMap =
636                         std::get_if<types::IPZVpdMap>(&parsedVpdMap))
637                 {
638                     if (!record.empty() && !keyword.empty() &&
639                         (*ipzVpdMap).count(record) &&
640                         (*ipzVpdMap).at(record).count(keyword))
641                     {
642                         auto encoded = vpdSpecificUtility::encodeKeyword(
643                             ((*ipzVpdMap).at(record).at(keyword)), encoding,
644                             l_errCode);
645 
646                         if (l_errCode)
647                         {
648                             logging::logMessage(
649                                 std::string(
650                                     "Failed to get encoded keyword value for : ") +
651                                 keyword + std::string(", error : ") +
652                                 commonUtility::getErrCodeMsg(l_errCode));
653                         }
654 
655                         propertyMap.emplace(property, encoded);
656                     }
657                 }
658                 else if (auto kwdVpdMap =
659                              std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
660                 {
661                     if (!keyword.empty() && (*kwdVpdMap).count(keyword))
662                     {
663                         if (auto kwValue = std::get_if<types::BinaryVector>(
664                                 &(*kwdVpdMap).at(keyword)))
665                         {
666                             auto encodedValue =
667                                 vpdSpecificUtility::encodeKeyword(
668                                     std::string((*kwValue).begin(),
669                                                 (*kwValue).end()),
670                                     encoding, l_errCode);
671 
672                             if (l_errCode)
673                             {
674                                 logging::logMessage(
675                                     std::string(
676                                         "Failed to get encoded keyword value for : ") +
677                                     keyword + std::string(", error : ") +
678                                     commonUtility::getErrCodeMsg(l_errCode));
679                             }
680 
681                             propertyMap.emplace(property, encodedValue);
682                         }
683                         else if (auto kwValue = std::get_if<std::string>(
684                                      &(*kwdVpdMap).at(keyword)))
685                         {
686                             auto encodedValue =
687                                 vpdSpecificUtility::encodeKeyword(
688                                     std::string((*kwValue).begin(),
689                                                 (*kwValue).end()),
690                                     encoding, l_errCode);
691 
692                             if (l_errCode)
693                             {
694                                 logging::logMessage(
695                                     "Failed to get encoded keyword value for : " +
696                                     keyword + ", error : " +
697                                     commonUtility::getErrCodeMsg(l_errCode));
698                             }
699 
700                             propertyMap.emplace(property, encodedValue);
701                         }
702                         else if (auto uintValue = std::get_if<size_t>(
703                                      &(*kwdVpdMap).at(keyword)))
704                         {
705                             propertyMap.emplace(property, *uintValue);
706                         }
707                         else
708                         {
709                             logging::logMessage(
710                                 "Unknown keyword found, Keywrod = " + keyword);
711                         }
712                     }
713                 }
714             }
715         }
716         l_errCode = 0;
717         vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
718                                           move(propertyMap), l_errCode);
719 
720         if (l_errCode)
721         {
722             logging::logMessage("Failed to insert value into map, error : " +
723                                 commonUtility::getErrCodeMsg(l_errCode));
724         }
725     }
726 }
727 
isCPUIOGoodOnly(const std::string & i_pgKeyword)728 bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
729 {
730     const unsigned char l_io[] = {
731         0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
732         0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
733 
734     // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
735     // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
736     // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
737     // IO.
738     if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
739                constants::SIZE_OF_8EQ_IN_PG) == 0)
740     {
741         return true;
742     }
743 
744     // The CPU is not an IO
745     return false;
746 }
747 
processEmbeddedAndSynthesizedFrus(const nlohmann::json & singleFru,types::InterfaceMap & interfaces)748 void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
749                                                types::InterfaceMap& interfaces)
750 {
751     // embedded property(true or false) says whether the subfru is embedded
752     // into the parent fru (or) not. VPD sets Present property only for
753     // embedded frus. If the subfru is not an embedded FRU, the subfru may
754     // or may not be physically present. Those non embedded frus will always
755     // have Present=false irrespective of its physical presence or absence.
756     // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
757     // Present to true for such sub frus.
758     // Eg: ethernet port is embedded into bmc card. So set Present to true
759     // for such sub frus. Also donot populate present property for embedded
760     // subfru which is synthesized. Currently there is no subfru which are
761     // both embedded and synthesized. But still the case is handled here.
762 
763     // Check if its required to handle presence for this FRU.
764     if (singleFru.value("handlePresence", true))
765     {
766         uint16_t l_errCode = 0;
767         types::PropertyMap presProp;
768         presProp.emplace("Present", true);
769         vpdSpecificUtility::insertOrMerge(interfaces,
770                                           "xyz.openbmc_project.Inventory.Item",
771                                           move(presProp), l_errCode);
772 
773         if (l_errCode)
774         {
775             logging::logMessage("Failed to insert value into map, error : " +
776                                 commonUtility::getErrCodeMsg(l_errCode));
777         }
778     }
779 }
780 
processExtraInterfaces(const nlohmann::json & singleFru,types::InterfaceMap & interfaces,const types::VPDMapVariant & parsedVpdMap)781 void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
782                                     types::InterfaceMap& interfaces,
783                                     const types::VPDMapVariant& parsedVpdMap)
784 {
785     populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
786     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
787     {
788         if (singleFru["extraInterfaces"].contains(
789                 "xyz.openbmc_project.Inventory.Item.Cpu"))
790         {
791             auto itrToRec = (*ipzVpdMap).find("CP00");
792             if (itrToRec == (*ipzVpdMap).end())
793             {
794                 return;
795             }
796 
797             uint16_t l_errCode = 0;
798             const std::string pgKeywordValue{vpdSpecificUtility::getKwVal(
799                 itrToRec->second, "PG", l_errCode)};
800 
801             if (!pgKeywordValue.empty())
802             {
803                 if (isCPUIOGoodOnly(pgKeywordValue))
804                 {
805                     interfaces["xyz.openbmc_project.Inventory.Item"]
806                               ["PrettyName"] = "IO Module";
807                 }
808             }
809             else
810             {
811                 throw DataException(
812                     std::string(__FUNCTION__) +
813                     "Failed to get value for keyword PG, error : " +
814                     commonUtility::getErrCodeMsg(l_errCode));
815             }
816         }
817     }
818 }
819 
processCopyRecordFlag(const nlohmann::json & singleFru,const types::VPDMapVariant & parsedVpdMap,types::InterfaceMap & interfaces)820 void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
821                                    const types::VPDMapVariant& parsedVpdMap,
822                                    types::InterfaceMap& interfaces)
823 {
824     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
825     {
826         for (const auto& record : singleFru["copyRecords"])
827         {
828             const std::string& recordName = record;
829             if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
830             {
831                 populateIPZVPDpropertyMap(interfaces,
832                                           (*ipzVpdMap).at(recordName),
833                                           constants::ipzVpdInf + recordName);
834             }
835         }
836     }
837 }
838 
processInheritFlag(const types::VPDMapVariant & parsedVpdMap,types::InterfaceMap & interfaces)839 void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
840                                 types::InterfaceMap& interfaces)
841 {
842     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
843     {
844         for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
845         {
846             populateIPZVPDpropertyMap(interfaces, kwdValueMap,
847                                       constants::ipzVpdInf + recordName);
848         }
849     }
850     else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
851     {
852         populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
853     }
854 
855     if (m_parsedJson.contains("commonInterfaces"))
856     {
857         populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
858                            parsedVpdMap);
859     }
860 }
861 
processFruWithCCIN(const nlohmann::json & singleFru,const types::VPDMapVariant & parsedVpdMap)862 bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
863                                 const types::VPDMapVariant& parsedVpdMap)
864 {
865     if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
866     {
867         auto itrToRec = (*ipzVPDMap).find("VINI");
868         if (itrToRec == (*ipzVPDMap).end())
869         {
870             return false;
871         }
872 
873         uint16_t l_errCode = 0;
874         std::string ccinFromVpd{
875             vpdSpecificUtility::getKwVal(itrToRec->second, "CC", l_errCode)};
876 
877         if (ccinFromVpd.empty())
878         {
879             logging::logMessage("Failed to get CCIN kwd value, error : " +
880                                 commonUtility::getErrCodeMsg(l_errCode));
881             return false;
882         }
883 
884         transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
885                   ::toupper);
886 
887         std::vector<std::string> ccinList;
888         for (std::string ccin : singleFru["ccin"])
889         {
890             transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
891             ccinList.push_back(ccin);
892         }
893 
894         if (ccinList.empty())
895         {
896             return false;
897         }
898 
899         if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
900             ccinList.end())
901         {
902             return false;
903         }
904     }
905     return true;
906 }
907 
processFunctionalProperty(const std::string & i_inventoryObjPath,types::InterfaceMap & io_interfaces)908 void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
909                                        types::InterfaceMap& io_interfaces)
910 {
911     if (!dbusUtility::isChassisPowerOn())
912     {
913         std::vector<std::string> l_operationalStatusInf = {
914             constants::operationalStatusInf};
915 
916         auto mapperObjectMap = dbusUtility::getObjectMap(
917             i_inventoryObjPath, l_operationalStatusInf);
918 
919         // If the object has been found. Check if it is under PIM.
920         if (mapperObjectMap.size() != 0)
921         {
922             for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
923             {
924                 if (l_serviceName == constants::pimServiceName)
925                 {
926                     // The object is already under PIM. No need to process
927                     // again. Retain the old value.
928                     return;
929                 }
930             }
931         }
932 
933         // Implies value is not there in D-Bus. Populate it with default
934         // value "true".
935         uint16_t l_errCode = 0;
936         types::PropertyMap l_functionalProp;
937         l_functionalProp.emplace("Functional", true);
938         vpdSpecificUtility::insertOrMerge(io_interfaces,
939                                           constants::operationalStatusInf,
940                                           move(l_functionalProp), l_errCode);
941 
942         if (l_errCode)
943         {
944             logging::logMessage(
945                 "Failed to insert interface into map, error : " +
946                 commonUtility::getErrCodeMsg(l_errCode));
947         }
948     }
949 
950     // if chassis is power on. Functional property should be there on D-Bus.
951     // Don't process.
952     return;
953 }
954 
processEnabledProperty(const std::string & i_inventoryObjPath,types::InterfaceMap & io_interfaces)955 void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
956                                     types::InterfaceMap& io_interfaces)
957 {
958     if (!dbusUtility::isChassisPowerOn())
959     {
960         std::vector<std::string> l_enableInf = {constants::enableInf};
961 
962         auto mapperObjectMap =
963             dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
964 
965         // If the object has been found. Check if it is under PIM.
966         if (mapperObjectMap.size() != 0)
967         {
968             for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
969             {
970                 if (l_serviceName == constants::pimServiceName)
971                 {
972                     // The object is already under PIM. No need to process
973                     // again. Retain the old value.
974                     return;
975                 }
976             }
977         }
978 
979         // Implies value is not there in D-Bus. Populate it with default
980         // value "true".
981         uint16_t l_errCode = 0;
982         types::PropertyMap l_enabledProp;
983         l_enabledProp.emplace("Enabled", true);
984         vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
985                                           move(l_enabledProp), l_errCode);
986 
987         if (l_errCode)
988         {
989             logging::logMessage(
990                 "Failed to insert interface into map, error : " +
991                 commonUtility::getErrCodeMsg(l_errCode));
992         }
993     }
994 
995     // if chassis is power on. Enabled property should be there on D-Bus.
996     // Don't process.
997     return;
998 }
999 
populateDbus(const types::VPDMapVariant & parsedVpdMap,types::ObjectMap & objectInterfaceMap,const std::string & vpdFilePath)1000 void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
1001                           types::ObjectMap& objectInterfaceMap,
1002                           const std::string& vpdFilePath)
1003 {
1004     if (vpdFilePath.empty())
1005     {
1006         throw std::runtime_error(
1007             std::string(__FUNCTION__) +
1008             "Invalid parameter passed to populateDbus API.");
1009     }
1010 
1011     // JSON config is mandatory for processing of "if". Add "else" for any
1012     // processing without config JSON.
1013     if (!m_parsedJson.empty())
1014     {
1015         types::InterfaceMap interfaces;
1016 
1017         for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
1018         {
1019             const auto& inventoryPath = aFru["inventoryPath"];
1020             sdbusplus::message::object_path fruObjectPath(inventoryPath);
1021             if (aFru.contains("ccin"))
1022             {
1023                 if (!processFruWithCCIN(aFru, parsedVpdMap))
1024                 {
1025                     continue;
1026                 }
1027             }
1028 
1029             if (aFru.value("inherit", true))
1030             {
1031                 processInheritFlag(parsedVpdMap, interfaces);
1032             }
1033 
1034             // If specific record needs to be copied.
1035             if (aFru.contains("copyRecords"))
1036             {
1037                 processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
1038             }
1039 
1040             if (aFru.contains("extraInterfaces"))
1041             {
1042                 // Process extra interfaces w.r.t a FRU.
1043                 processExtraInterfaces(aFru, interfaces, parsedVpdMap);
1044             }
1045 
1046             // Process FRUS which are embedded in the parent FRU and whose VPD
1047             // will be synthesized.
1048             if ((aFru.value("embedded", true)) &&
1049                 (!aFru.value("synthesized", false)))
1050             {
1051                 processEmbeddedAndSynthesizedFrus(aFru, interfaces);
1052             }
1053 
1054             processFunctionalProperty(inventoryPath, interfaces);
1055             processEnabledProperty(inventoryPath, interfaces);
1056 
1057             objectInterfaceMap.emplace(std::move(fruObjectPath),
1058                                        std::move(interfaces));
1059         }
1060     }
1061 }
1062 
createAssetTagString(const types::VPDMapVariant & i_parsedVpdMap)1063 std::string Worker::createAssetTagString(
1064     const types::VPDMapVariant& i_parsedVpdMap)
1065 {
1066     std::string l_assetTag;
1067 
1068     // system VPD will be in IPZ format.
1069     if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
1070     {
1071         auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
1072         if (l_itrToVsys != (*l_parsedVpdMap).end())
1073         {
1074             uint16_t l_errCode = 0;
1075             const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal(
1076                 l_itrToVsys->second, constants::kwdTM, l_errCode)};
1077 
1078             if (l_tmKwdValue.empty())
1079             {
1080                 throw std::runtime_error(
1081                     std::string("Failed to get value for keyword [") +
1082                     constants::kwdTM +
1083                     std::string("] while creating Asset tag. Error : " +
1084                                 commonUtility::getErrCodeMsg(l_errCode)));
1085             }
1086 
1087             const std::string l_seKwdValue{vpdSpecificUtility::getKwVal(
1088                 l_itrToVsys->second, constants::kwdSE, l_errCode)};
1089 
1090             if (l_seKwdValue.empty())
1091             {
1092                 throw std::runtime_error(
1093                     std::string("Failed to get value for keyword [") +
1094                     constants::kwdSE +
1095                     std::string("] while creating Asset tag. Error : " +
1096                                 commonUtility::getErrCodeMsg(l_errCode)));
1097             }
1098 
1099             l_assetTag = std::string{"Server-"} + l_tmKwdValue +
1100                          std::string{"-"} + l_seKwdValue;
1101         }
1102         else
1103         {
1104             throw std::runtime_error(
1105                 "VSYS record not found in parsed VPD map to create Asset tag.");
1106         }
1107     }
1108     else
1109     {
1110         throw std::runtime_error(
1111             "Invalid VPD type recieved to create Asset tag.");
1112     }
1113 
1114     return l_assetTag;
1115 }
1116 
publishSystemVPD(const types::VPDMapVariant & parsedVpdMap)1117 void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
1118 {
1119     types::ObjectMap objectInterfaceMap;
1120 
1121     if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
1122     {
1123         populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
1124 
1125         try
1126         {
1127             if (m_isFactoryResetDone)
1128             {
1129                 const auto& l_assetTag = createAssetTagString(parsedVpdMap);
1130 
1131                 auto l_itrToSystemPath = objectInterfaceMap.find(
1132                     sdbusplus::message::object_path(constants::systemInvPath));
1133                 if (l_itrToSystemPath == objectInterfaceMap.end())
1134                 {
1135                     throw std::runtime_error(
1136                         "Asset tag update failed. System Path not found in object map.");
1137                 }
1138 
1139                 types::PropertyMap l_assetTagProperty;
1140                 l_assetTagProperty.emplace("AssetTag", l_assetTag);
1141 
1142                 (l_itrToSystemPath->second)
1143                     .emplace(constants::assetTagInf,
1144                              std::move(l_assetTagProperty));
1145             }
1146         }
1147         catch (const std::exception& l_ex)
1148         {
1149             EventLogger::createSyncPel(
1150                 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1151                 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1152                 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1153         }
1154 
1155         // Notify PIM
1156         if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1157         {
1158             throw std::runtime_error("Call to PIM failed for system VPD");
1159         }
1160     }
1161     else
1162     {
1163         throw DataException("Invalid format of parsed VPD map.");
1164     }
1165 }
1166 
processPreAction(const std::string & i_vpdFilePath,const std::string & i_flagToProcess,uint16_t & i_errCode)1167 bool Worker::processPreAction(const std::string& i_vpdFilePath,
1168                               const std::string& i_flagToProcess,
1169                               uint16_t& i_errCode)
1170 {
1171     if (i_vpdFilePath.empty() || i_flagToProcess.empty())
1172     {
1173         i_errCode = error_code::INVALID_INPUT_PARAMETER;
1174         return false;
1175     }
1176 
1177     if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
1178                                          i_vpdFilePath, i_flagToProcess,
1179                                          i_errCode)) &&
1180         (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
1181     {
1182         // TODO: Need a way to delete inventory object from Dbus and persisted
1183         // data section in case any FRU is not present or there is any
1184         // problem in collecting it. Once it has been deleted, it can be
1185         // re-created in the flow of priming the inventory. This needs to be
1186         // done either here or in the exception section of "parseAndPublishVPD"
1187         // API. Any failure in the process of collecting FRU will land up in the
1188         // excpetion of "parseAndPublishVPD".
1189 
1190         // If the FRU is not there, clear the VINI/CCIN data.
1191         // Enity manager probes for this keyword to look for this
1192         // FRU, now if the data is persistent on BMC and FRU is
1193         // removed this can lead to ambiguity. Hence clearing this
1194         // Keyword if FRU is absent.
1195         const auto& inventoryPath =
1196             m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
1197                                                             "");
1198 
1199         if (!inventoryPath.empty())
1200         {
1201             types::ObjectMap l_pimObjMap{
1202                 {inventoryPath,
1203                  {{constants::kwdVpdInf,
1204                    {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
1205 
1206             if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
1207             {
1208                 logging::logMessage(
1209                     "Call to PIM failed for file " + i_vpdFilePath);
1210             }
1211         }
1212         else
1213         {
1214             logging::logMessage(
1215                 "Inventory path is empty in Json for file " + i_vpdFilePath);
1216         }
1217 
1218         return false;
1219     }
1220     return true;
1221 }
1222 
processPostAction(const std::string & i_vpdFruPath,const std::string & i_flagToProcess,const std::optional<types::VPDMapVariant> i_parsedVpd)1223 bool Worker::processPostAction(
1224     const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
1225     const std::optional<types::VPDMapVariant> i_parsedVpd)
1226 {
1227     if (i_vpdFruPath.empty() || i_flagToProcess.empty())
1228     {
1229         logging::logMessage(
1230             "Invalid input parameter. Abort processing post action");
1231         return false;
1232     }
1233 
1234     // Check if post action tag is to be triggered in the flow of collection
1235     // based on some CCIN value?
1236     if (m_parsedJson["frus"][i_vpdFruPath]
1237             .at(0)["postAction"][i_flagToProcess]
1238             .contains("ccin"))
1239     {
1240         if (!i_parsedVpd.has_value())
1241         {
1242             logging::logMessage("Empty VPD Map");
1243             return false;
1244         }
1245 
1246         // CCIN match is required to process post action for this FRU as it
1247         // contains the flag.
1248         if (!vpdSpecificUtility::findCcinInVpd(
1249                 m_parsedJson["frus"][i_vpdFruPath].at(
1250                     0)["postAction"]["collection"],
1251                 i_parsedVpd.value()))
1252         {
1253             // If CCIN is not found, implies post action processing is not
1254             // required for this FRU. Let the flow continue.
1255             return true;
1256         }
1257     }
1258 
1259     uint16_t l_errCode = 0;
1260     if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
1261                                         i_vpdFruPath, i_flagToProcess,
1262                                         l_errCode))
1263     {
1264         logging::logMessage(
1265             "Execution of post action failed for path: " + i_vpdFruPath +
1266             " . Reason: " + commonUtility::getErrCodeMsg(l_errCode));
1267 
1268         // If post action was required and failed only in that case return
1269         // false. In all other case post action is considered passed.
1270         return false;
1271     }
1272 
1273     return true;
1274 }
1275 
parseVpdFile(const std::string & i_vpdFilePath)1276 types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
1277 {
1278     try
1279     {
1280         uint16_t l_errCode = 0;
1281 
1282         if (i_vpdFilePath.empty())
1283         {
1284             throw std::runtime_error(
1285                 std::string(__FUNCTION__) +
1286                 " Empty VPD file path passed. Abort processing");
1287         }
1288 
1289         bool isPreActionRequired = false;
1290         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1291                                           "preAction", "collection", l_errCode))
1292         {
1293             l_errCode = 0;
1294             isPreActionRequired = true;
1295             if (!processPreAction(i_vpdFilePath, "collection", l_errCode))
1296             {
1297                 if (l_errCode == error_code::DEVICE_NOT_PRESENT)
1298                 {
1299                     logging::logMessage(
1300                         commonUtility::getErrCodeMsg(l_errCode) +
1301                         i_vpdFilePath);
1302                     // Presence pin has been read successfully and has been read
1303                     // as false, so this is not a failure case, hence returning
1304                     // empty variant so that pre action is not marked as failed.
1305                     return types::VPDMapVariant{};
1306                 }
1307                 throw std::runtime_error(
1308                     std::string(__FUNCTION__) +
1309                     " Pre-Action failed with error: " +
1310                     commonUtility::getErrCodeMsg(l_errCode));
1311             }
1312         }
1313         else if (l_errCode)
1314         {
1315             logging::logMessage(
1316                 "Failed to check if pre action required for FRU [" +
1317                 i_vpdFilePath +
1318                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1319         }
1320 
1321         if (!std::filesystem::exists(i_vpdFilePath))
1322         {
1323             if (isPreActionRequired)
1324             {
1325                 throw std::runtime_error(
1326                     std::string(__FUNCTION__) + " Could not find file path " +
1327                     i_vpdFilePath + "Skipping parser trigger for the EEPROM");
1328             }
1329             return types::VPDMapVariant{};
1330         }
1331 
1332         std::shared_ptr<Parser> vpdParser =
1333             std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
1334 
1335         types::VPDMapVariant l_parsedVpd = vpdParser->parse();
1336 
1337         // Before returning, as collection is over, check if FRU qualifies for
1338         // any post action in the flow of collection.
1339         // Note: Don't change the order, post action needs to be processed only
1340         // after collection for FRU is successfully done.
1341         l_errCode = 0;
1342 
1343         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1344                                           "postAction", "collection",
1345                                           l_errCode))
1346         {
1347             if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
1348             {
1349                 // Post action was required but failed while executing.
1350                 // Behaviour can be undefined.
1351                 EventLogger::createSyncPel(
1352                     types::ErrorType::InternalFailure,
1353                     types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
1354                     std::string("Required post action failed for path [" +
1355                                 i_vpdFilePath + "]"),
1356                     std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1357             }
1358         }
1359         else if (l_errCode)
1360         {
1361             logging::logMessage(
1362                 "Error while checking if post action required for FRU [" +
1363                 i_vpdFilePath +
1364                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1365         }
1366 
1367         return l_parsedVpd;
1368     }
1369     catch (std::exception& l_ex)
1370     {
1371         uint16_t l_errCode = 0;
1372         std::string l_exMsg{
1373             std::string(__FUNCTION__) + " : VPD parsing failed for " +
1374             i_vpdFilePath + " due to error: " + l_ex.what()};
1375 
1376         // If post fail action is required, execute it.
1377         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1378                                           "postFailAction", "collection",
1379                                           l_errCode))
1380         {
1381             if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
1382                                                     "collection", l_errCode))
1383             {
1384                 l_exMsg += ". Post fail action also failed. Error : " +
1385                            commonUtility::getErrCodeMsg(l_errCode) +
1386                            " Aborting collection for this FRU.";
1387             }
1388         }
1389         else if (l_errCode)
1390         {
1391             l_exMsg +=
1392                 ". Failed to check if post fail action required, error : " +
1393                 commonUtility::getErrCodeMsg(l_errCode);
1394         }
1395 
1396         if (typeid(l_ex) == typeid(DataException))
1397         {
1398             throw DataException(l_exMsg);
1399         }
1400         else if (typeid(l_ex) == typeid(EccException))
1401         {
1402             throw EccException(l_exMsg);
1403         }
1404         throw std::runtime_error(l_exMsg);
1405     }
1406 }
1407 
parseAndPublishVPD(const std::string & i_vpdFilePath)1408 std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1409     const std::string& i_vpdFilePath)
1410 {
1411     std::string l_inventoryPath{};
1412 
1413     try
1414     {
1415         m_semaphore.acquire();
1416 
1417         // Thread launched.
1418         m_mutex.lock();
1419         m_activeCollectionThreadCount++;
1420         m_mutex.unlock();
1421 
1422         setCollectionStatusProperty(i_vpdFilePath,
1423                                     constants::vpdCollectionInProgress);
1424 
1425         const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
1426         if (!std::holds_alternative<std::monostate>(parsedVpdMap))
1427         {
1428             types::ObjectMap objectInterfaceMap;
1429             populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1430 
1431             // Notify PIM
1432             if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1433             {
1434                 throw std::runtime_error(
1435                     std::string(__FUNCTION__) +
1436                     "Call to PIM failed while publishing VPD.");
1437             }
1438         }
1439         else
1440         {
1441             logging::logMessage("Empty parsedVpdMap recieved for path [" +
1442                                 i_vpdFilePath + "]. Check PEL for reason.");
1443 
1444             // As empty parsedVpdMap recieved for some reason, but still
1445             // considered VPD collection is completed. Hence FRU collection
1446             // Status will be set as completed.
1447         }
1448     }
1449     catch (const std::exception& ex)
1450     {
1451         setCollectionStatusProperty(i_vpdFilePath,
1452                                     constants::vpdCollectionFailed);
1453 
1454         // handle all the exceptions internally. Return only true/false
1455         // based on status of execution.
1456         if (typeid(ex) == std::type_index(typeid(DataException)))
1457         {
1458             uint16_t l_errCode = 0;
1459             // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1460             // logging error for these cases.
1461             if (vpdSpecificUtility::isPass1Planar())
1462             {
1463                 std::string l_invPath =
1464                     jsonUtility::getInventoryObjPathFromJson(
1465                         m_parsedJson, i_vpdFilePath, l_errCode);
1466 
1467                 if (l_errCode != 0)
1468                 {
1469                     logging::logMessage(
1470                         "Failed to get inventory object path from JSON for FRU [" +
1471                         i_vpdFilePath +
1472                         "], error: " + commonUtility::getErrCodeMsg(l_errCode));
1473                 }
1474 
1475                 const std::string& l_invPathLeafValue =
1476                     sdbusplus::message::object_path(l_invPath).filename();
1477 
1478                 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1479                      std::string::npos))
1480                 {
1481                     // skip logging any PEL for PCIe cards on pass 1 planar.
1482                     return std::make_tuple(false, i_vpdFilePath);
1483                 }
1484             }
1485         }
1486 
1487         EventLogger::createSyncPel(
1488             EventLogger::getErrorType(ex),
1489             (typeid(ex) == typeid(DataException)) ||
1490                     (typeid(ex) == typeid(EccException))
1491                 ? types::SeverityType::Warning
1492                 : types::SeverityType::Informational,
1493             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1494             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1495 
1496         // TODO: Figure out a way to clear data in case of any failure at
1497         // runtime.
1498 
1499         // set present property to false for any error case. In future this will
1500         // be replaced by presence logic.
1501         // Update Present property for this FRU only if we handle Present
1502         // property for the FRU.
1503         if (isPresentPropertyHandlingRequired(
1504                 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1505         {
1506             setPresentProperty(i_vpdFilePath, false);
1507         }
1508 
1509         m_semaphore.release();
1510         return std::make_tuple(false, i_vpdFilePath);
1511     }
1512 
1513     setCollectionStatusProperty(i_vpdFilePath,
1514                                 constants::vpdCollectionCompleted);
1515     m_semaphore.release();
1516     return std::make_tuple(true, i_vpdFilePath);
1517 }
1518 
skipPathForCollection(const std::string & i_vpdFilePath)1519 bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1520 {
1521     if (i_vpdFilePath.empty())
1522     {
1523         return true;
1524     }
1525 
1526     // skip processing of system VPD again as it has been already collected.
1527     if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1528     {
1529         return true;
1530     }
1531 
1532     if (dbusUtility::isChassisPowerOn())
1533     {
1534         // If chassis is powered on, skip collecting FRUs which are
1535         // powerOffOnly.
1536 
1537         uint16_t l_errCode = 0;
1538         if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath,
1539                                            l_errCode))
1540         {
1541             return true;
1542         }
1543         else if (l_errCode)
1544         {
1545             logging::logMessage(
1546                 "Failed to check if FRU is power off only for FRU [" +
1547                 i_vpdFilePath +
1548                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1549         }
1550 
1551         l_errCode = 0;
1552         std::string l_invPath = jsonUtility::getInventoryObjPathFromJson(
1553             m_parsedJson, i_vpdFilePath, l_errCode);
1554 
1555         if (l_errCode)
1556         {
1557             logging::logMessage(
1558                 "Failed to get inventory path from JSON for FRU [" +
1559                 i_vpdFilePath +
1560                 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
1561 
1562             return false;
1563         }
1564 
1565         const std::string& l_invPathLeafValue =
1566             sdbusplus::message::object_path(l_invPath).filename();
1567 
1568         if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1569         {
1570             return true;
1571         }
1572     }
1573 
1574     return false;
1575 }
1576 
collectFrusFromJson()1577 void Worker::collectFrusFromJson()
1578 {
1579     // A parsed JSON file should be present to pick FRUs EEPROM paths
1580     if (m_parsedJson.empty())
1581     {
1582         throw JsonException(
1583             std::string(__FUNCTION__) +
1584                 ": Config JSON is mandatory for processing of FRUs through this API.",
1585             m_configJsonPath);
1586     }
1587 
1588     const nlohmann::json& listOfFrus =
1589         m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1590 
1591     for (const auto& itemFRUS : listOfFrus.items())
1592     {
1593         const std::string& vpdFilePath = itemFRUS.key();
1594 
1595         if (skipPathForCollection(vpdFilePath))
1596         {
1597             continue;
1598         }
1599 
1600         try
1601         {
1602             std::thread{[vpdFilePath, this]() {
1603                 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
1604 
1605                 m_mutex.lock();
1606                 m_activeCollectionThreadCount--;
1607                 m_mutex.unlock();
1608 
1609                 if (!m_activeCollectionThreadCount)
1610                 {
1611                     m_isAllFruCollected = true;
1612                 }
1613             }}.detach();
1614         }
1615         catch (const std::exception& l_ex)
1616         {
1617             // add vpdFilePath(EEPROM path) to failed list
1618             m_failedEepromPaths.push_front(vpdFilePath);
1619         }
1620     }
1621 }
1622 
1623 // ToDo: Move the API under IBM_SYSTEM
performBackupAndRestore(types::VPDMapVariant & io_srcVpdMap)1624 void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1625 {
1626     try
1627     {
1628         uint16_t l_errCode = 0;
1629         std::string l_backupAndRestoreCfgFilePath =
1630             m_parsedJson.value("backupRestoreConfigPath", "");
1631 
1632         nlohmann::json l_backupAndRestoreCfgJsonObj =
1633             jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath,
1634                                        l_errCode);
1635 
1636         if (l_errCode)
1637         {
1638             throw JsonException(
1639                 "JSON parsing failed for file [ " +
1640                     l_backupAndRestoreCfgFilePath +
1641                     " ], error : " + commonUtility::getErrCodeMsg(l_errCode),
1642                 l_backupAndRestoreCfgFilePath);
1643         }
1644 
1645         // check if either of "source" or "destination" has inventory path.
1646         // this indicates that this sytem has System VPD on hardware
1647         // and other copy on D-Bus (BMC cache).
1648         if (!l_backupAndRestoreCfgJsonObj.empty() &&
1649             ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1650               l_backupAndRestoreCfgJsonObj["source"].contains(
1651                   "inventoryPath")) ||
1652              (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1653               l_backupAndRestoreCfgJsonObj["destination"].contains(
1654                   "inventoryPath"))))
1655         {
1656             BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1657             auto [l_srcVpdVariant,
1658                   l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1659 
1660             // ToDo: Revisit is this check is required or not.
1661             if (auto l_srcVpdMap =
1662                     std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1663                 l_srcVpdMap && !(*l_srcVpdMap).empty())
1664             {
1665                 io_srcVpdMap = std::move(l_srcVpdVariant);
1666             }
1667         }
1668     }
1669     catch (const std::exception& l_ex)
1670     {
1671         EventLogger::createSyncPel(
1672             EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1673             __FILE__, __FUNCTION__, 0,
1674             std::string(
1675                 "Exception caught while backup and restore VPD keyword's.") +
1676                 EventLogger::getErrorMsg(l_ex),
1677             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1678     }
1679 }
1680 
deleteFruVpd(const std::string & i_dbusObjPath)1681 void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1682 {
1683     if (i_dbusObjPath.empty())
1684     {
1685         throw std::runtime_error("Given DBus object path is empty.");
1686     }
1687 
1688     uint16_t l_errCode = 0;
1689     const std::string& l_fruPath =
1690         jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath, l_errCode);
1691 
1692     if (l_errCode)
1693     {
1694         logging::logMessage(
1695             "Failed to get FRU path for inventory path [" + i_dbusObjPath +
1696             "], error : " + commonUtility::getErrCodeMsg(l_errCode) +
1697             " Aborting FRU VPD deletion.");
1698         return;
1699     }
1700 
1701     try
1702     {
1703         auto l_presentPropValue = dbusUtility::readDbusProperty(
1704             constants::pimServiceName, i_dbusObjPath,
1705             constants::inventoryItemInf, "Present");
1706 
1707         if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1708         {
1709             uint16_t l_errCode = 0;
1710             // check if FRU's Present property is handled by vpd-manager
1711             const auto& l_isFruPresenceHandled =
1712                 jsonUtility::isFruPresenceHandled(m_parsedJson, l_fruPath,
1713                                                   l_errCode);
1714 
1715             if (l_errCode)
1716             {
1717                 throw std::runtime_error(
1718                     "Failed to check if FRU's presence is handled, reason: " +
1719                     commonUtility::getErrCodeMsg(l_errCode));
1720             }
1721 
1722             if (!(*l_value) && l_isFruPresenceHandled)
1723             {
1724                 throw std::runtime_error("Given FRU is not present");
1725             }
1726             else if (*l_value && !l_isFruPresenceHandled)
1727             {
1728                 throw std::runtime_error(
1729                     "Given FRU is present and its presence is not handled by vpd-manager.");
1730             }
1731             else
1732             {
1733                 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1734                                                   "preAction", "deletion",
1735                                                   l_errCode))
1736                 {
1737                     if (!processPreAction(l_fruPath, "deletion", l_errCode))
1738                     {
1739                         std::string l_msg = "Pre action failed";
1740                         if (l_errCode)
1741                         {
1742                             l_msg += " Reason: " +
1743                                      commonUtility::getErrCodeMsg(l_errCode);
1744                         }
1745                         throw std::runtime_error(l_msg);
1746                     }
1747                 }
1748                 else if (l_errCode)
1749                 {
1750                     logging::logMessage(
1751                         "Failed to check if pre action required for FRU [" +
1752                         l_fruPath + "], error : " +
1753                         commonUtility::getErrCodeMsg(l_errCode));
1754                 }
1755 
1756                 std::vector<std::string> l_interfaceList{
1757                     constants::operationalStatusInf};
1758 
1759                 types::MapperGetSubTree l_subTreeMap =
1760                     dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1761                                                   l_interfaceList);
1762 
1763                 types::ObjectMap l_objectMap;
1764 
1765                 // Updates VPD specific interfaces property value under PIM for
1766                 // sub FRUs.
1767                 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1768                      l_subTreeMap)
1769                 {
1770                     l_errCode = 0;
1771                     types::InterfaceMap l_interfaceMap;
1772                     vpdSpecificUtility::resetDataUnderPIM(
1773                         l_objectPath, l_interfaceMap, l_errCode);
1774 
1775                     if (l_errCode)
1776                     {
1777                         throw std::runtime_error(
1778                             "Failed to reset data under PIM for sub FRU [" +
1779                             l_objectPath + "], error : " +
1780                             commonUtility::getErrCodeMsg(l_errCode));
1781                     }
1782 
1783                     l_objectMap.emplace(l_objectPath,
1784                                         std::move(l_interfaceMap));
1785                 }
1786 
1787                 l_errCode = 0;
1788                 types::InterfaceMap l_interfaceMap;
1789                 vpdSpecificUtility::resetDataUnderPIM(
1790                     i_dbusObjPath, l_interfaceMap, l_errCode);
1791 
1792                 if (l_errCode)
1793                 {
1794                     throw std::runtime_error(
1795                         "Failed to reset data under PIM, error : " +
1796                         commonUtility::getErrCodeMsg(l_errCode));
1797                 }
1798 
1799                 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1800 
1801                 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1802                 {
1803                     throw std::runtime_error("Call to PIM failed.");
1804                 }
1805 
1806                 l_errCode = 0;
1807 
1808                 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1809                                                   "postAction", "deletion",
1810                                                   l_errCode))
1811                 {
1812                     if (!processPostAction(l_fruPath, "deletion"))
1813                     {
1814                         throw std::runtime_error("Post action failed");
1815                     }
1816                 }
1817                 else if (l_errCode)
1818                 {
1819                     logging::logMessage(
1820                         "Failed to check if post action required during deletion for FRU [" +
1821                         l_fruPath + "], error : " +
1822                         commonUtility::getErrCodeMsg(l_errCode));
1823                 }
1824             }
1825         }
1826         else
1827         {
1828             logging::logMessage(
1829                 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1830                 "] as unable to read present property");
1831             return;
1832         }
1833 
1834         logging::logMessage(
1835             "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1836     }
1837     catch (const std::exception& l_ex)
1838     {
1839         uint16_t l_errCode = 0;
1840         std::string l_errMsg =
1841             "Failed to delete VPD for FRU : " + i_dbusObjPath +
1842             " error: " + std::string(l_ex.what());
1843 
1844         if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1845                                           "postFailAction", "deletion",
1846                                           l_errCode))
1847         {
1848             if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
1849                                                     "deletion", l_errCode))
1850             {
1851                 l_errMsg += ". Post fail action also failed, error : " +
1852                             commonUtility::getErrCodeMsg(l_errCode);
1853             }
1854         }
1855         else if (l_errCode)
1856         {
1857             l_errMsg +=
1858                 ". Failed to check if post fail action required, error : " +
1859                 commonUtility::getErrCodeMsg(l_errCode);
1860         }
1861 
1862         logging::logMessage(l_errMsg);
1863     }
1864 }
1865 
setPresentProperty(const std::string & i_vpdPath,const bool & i_value)1866 void Worker::setPresentProperty(const std::string& i_vpdPath,
1867                                 const bool& i_value)
1868 {
1869     try
1870     {
1871         if (i_vpdPath.empty())
1872         {
1873             throw std::runtime_error(
1874                 "Path is empty. Can't set present property");
1875         }
1876 
1877         types::ObjectMap l_objectInterfaceMap;
1878 
1879         // If the given path is EEPROM path.
1880         if (m_parsedJson["frus"].contains(i_vpdPath))
1881         {
1882             for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1883             {
1884                 sdbusplus::message::object_path l_fruObjectPath(
1885                     l_Fru["inventoryPath"]);
1886 
1887                 types::PropertyMap l_propertyValueMap;
1888                 l_propertyValueMap.emplace("Present", i_value);
1889 
1890                 uint16_t l_errCode = 0;
1891                 types::InterfaceMap l_interfaces;
1892                 vpdSpecificUtility::insertOrMerge(
1893                     l_interfaces, constants::inventoryItemInf,
1894                     move(l_propertyValueMap), l_errCode);
1895 
1896                 if (l_errCode)
1897                 {
1898                     logging::logMessage(
1899                         "Failed to insert value into map, error : " +
1900                         commonUtility::getErrCodeMsg(l_errCode));
1901                 }
1902 
1903                 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1904                                              std::move(l_interfaces));
1905             }
1906         }
1907         else
1908         {
1909             // consider it as an inventory path.
1910             if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1911             {
1912                 throw std::runtime_error(
1913                     "Invalid inventory path: " + i_vpdPath);
1914             }
1915 
1916             types::PropertyMap l_propertyValueMap;
1917             l_propertyValueMap.emplace("Present", i_value);
1918 
1919             uint16_t l_errCode = 0;
1920             types::InterfaceMap l_interfaces;
1921             vpdSpecificUtility::insertOrMerge(
1922                 l_interfaces, constants::inventoryItemInf,
1923                 move(l_propertyValueMap), l_errCode);
1924 
1925             if (l_errCode)
1926             {
1927                 logging::logMessage(
1928                     "Failed to insert value into map, error : " +
1929                     commonUtility::getErrCodeMsg(l_errCode));
1930             }
1931 
1932             l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1933         }
1934 
1935         // Notify PIM
1936         if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1937         {
1938             throw DbusException(
1939                 std::string(__FUNCTION__) +
1940                 "Call to PIM failed while setting present property for path " +
1941                 i_vpdPath);
1942         }
1943     }
1944     catch (const std::exception& l_ex)
1945     {
1946         EventLogger::createSyncPel(
1947             EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1948             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1949             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1950     }
1951 }
1952 
performVpdRecollection()1953 void Worker::performVpdRecollection()
1954 {
1955     try
1956     {
1957         // Check if system config JSON is present
1958         if (m_parsedJson.empty())
1959         {
1960             throw std::runtime_error(
1961                 "System config json object is empty, can't process recollection.");
1962         }
1963 
1964         uint16_t l_errCode = 0;
1965         const auto& l_frusReplaceableAtStandby =
1966             jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson,
1967                                                            l_errCode);
1968 
1969         if (l_errCode)
1970         {
1971             logging::logMessage(
1972                 "Failed to get list of FRUs replaceable at runtime, error : " +
1973                 commonUtility::getErrCodeMsg(l_errCode));
1974             return;
1975         }
1976 
1977         for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1978         {
1979             // ToDo: Add some logic/trace to know the flow to
1980             // collectSingleFruVpd has been directed via
1981             // performVpdRecollection.
1982             collectSingleFruVpd(l_fruInventoryPath);
1983         }
1984         return;
1985     }
1986 
1987     catch (const std::exception& l_ex)
1988     {
1989         // TODO Log PEL
1990         logging::logMessage(
1991             "VPD recollection failed with error: " + std::string(l_ex.what()));
1992     }
1993 }
1994 
collectSingleFruVpd(const sdbusplus::message::object_path & i_dbusObjPath)1995 void Worker::collectSingleFruVpd(
1996     const sdbusplus::message::object_path& i_dbusObjPath)
1997 {
1998     std::string l_fruPath{};
1999     uint16_t l_errCode = 0;
2000 
2001     try
2002     {
2003         // Check if system config JSON is present
2004         if (m_parsedJson.empty())
2005         {
2006             logging::logMessage(
2007                 "System config JSON object not present. Single FRU VPD collection is not performed for " +
2008                 std::string(i_dbusObjPath));
2009             return;
2010         }
2011 
2012         // Get FRU path for the given D-bus object path from JSON
2013         l_fruPath = jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath,
2014                                                     l_errCode);
2015 
2016         if (l_fruPath.empty())
2017         {
2018             if (l_errCode)
2019             {
2020                 logging::logMessage(
2021                     "Failed to get FRU path for [" +
2022                     std::string(i_dbusObjPath) +
2023                     "], error : " + commonUtility::getErrCodeMsg(l_errCode) +
2024                     " Aborting single FRU VPD collection.");
2025                 return;
2026             }
2027 
2028             logging::logMessage(
2029                 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
2030                 std::string(i_dbusObjPath));
2031             return;
2032         }
2033 
2034         // Check if host is up and running
2035         if (dbusUtility::isHostRunning())
2036         {
2037             uint16_t l_errCode = 0;
2038             bool isFruReplaceableAtRuntime =
2039                 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
2040                                                        l_errCode);
2041 
2042             if (l_errCode)
2043             {
2044                 logging::logMessage(
2045                     "Failed to check if FRU is replaceable at runtime for FRU : [" +
2046                     std::string(i_dbusObjPath) +
2047                     "], error : " + commonUtility::getErrCodeMsg(l_errCode));
2048                 return;
2049             }
2050 
2051             if (!isFruReplaceableAtRuntime)
2052             {
2053                 logging::logMessage(
2054                     "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
2055                     std::string(i_dbusObjPath));
2056                 return;
2057             }
2058         }
2059         else if (dbusUtility::isBMCReady())
2060         {
2061             uint16_t l_errCode = 0;
2062             bool isFruReplaceableAtStandby =
2063                 jsonUtility::isFruReplaceableAtStandby(m_parsedJson, l_fruPath,
2064                                                        l_errCode);
2065 
2066             if (l_errCode)
2067             {
2068                 logging::logMessage(
2069                     "Error while checking if FRU is replaceable at standby for FRU [" +
2070                     std::string(i_dbusObjPath) +
2071                     "], error : " + commonUtility::getErrCodeMsg(l_errCode));
2072             }
2073 
2074             l_errCode = 0;
2075             bool isFruReplaceableAtRuntime =
2076                 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
2077                                                        l_errCode);
2078 
2079             if (l_errCode)
2080             {
2081                 logging::logMessage(
2082                     "Failed to check if FRU is replaceable at runtime for FRU : [" +
2083                     std::string(i_dbusObjPath) +
2084                     "], error : " + commonUtility::getErrCodeMsg(l_errCode));
2085                 return;
2086             }
2087 
2088             if (!isFruReplaceableAtStandby && (!isFruReplaceableAtRuntime))
2089             {
2090                 logging::logMessage(
2091                     "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
2092                     std::string(i_dbusObjPath));
2093                 return;
2094             }
2095         }
2096 
2097         // Set collection Status as InProgress. Since it's an intermediate state
2098         // D-bus set-property call is good enough to update the status.
2099         const std::string& l_collStatusProp = "Status";
2100 
2101         setCollectionStatusProperty(l_fruPath,
2102                                     constants::vpdCollectionInProgress);
2103 
2104         // Parse VPD
2105         types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
2106 
2107         // If l_parsedVpd is pointing to std::monostate
2108         if (l_parsedVpd.index() == 0)
2109         {
2110             throw std::runtime_error(
2111                 "VPD parsing failed for " + std::string(i_dbusObjPath));
2112         }
2113 
2114         // Get D-bus object map from worker class
2115         types::ObjectMap l_dbusObjectMap;
2116         populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
2117 
2118         if (l_dbusObjectMap.empty())
2119         {
2120             throw std::runtime_error(
2121                 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
2122                 std::string(i_dbusObjPath));
2123         }
2124 
2125         // Call PIM's Notify method
2126         if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
2127         {
2128             throw std::runtime_error(
2129                 "Notify PIM failed. Single FRU VPD collection failed for " +
2130                 std::string(i_dbusObjPath));
2131         }
2132         setCollectionStatusProperty(l_fruPath,
2133                                     constants::vpdCollectionCompleted);
2134     }
2135     catch (const std::exception& l_error)
2136     {
2137         setCollectionStatusProperty(l_fruPath, constants::vpdCollectionFailed);
2138         // TODO: Log PEL
2139         logging::logMessage(std::string(l_error.what()));
2140     }
2141 }
2142 
setCollectionStatusProperty(const std::string & i_vpdPath,const std::string & i_value) const2143 void Worker::setCollectionStatusProperty(
2144     const std::string& i_vpdPath, const std::string& i_value) const noexcept
2145 {
2146     try
2147     {
2148         if (i_vpdPath.empty())
2149         {
2150             throw std::runtime_error(
2151                 "Given path is empty. Can't set collection Status property");
2152         }
2153 
2154         types::PropertyMap l_timeStampMap;
2155         if (i_value == constants::vpdCollectionCompleted ||
2156             i_value == constants::vpdCollectionFailed)
2157         {
2158             l_timeStampMap.emplace(
2159                 "CompletedTime",
2160                 types::DbusVariantType{
2161                     commonUtility::getCurrentTimeSinceEpoch()});
2162         }
2163         else if (i_value == constants::vpdCollectionInProgress)
2164         {
2165             l_timeStampMap.emplace(
2166                 "StartTime", types::DbusVariantType{
2167                                  commonUtility::getCurrentTimeSinceEpoch()});
2168         }
2169         else if (i_value == constants::vpdCollectionNotStarted)
2170         {
2171             l_timeStampMap.emplace("StartTime", 0);
2172             l_timeStampMap.emplace("CompletedTime", 0);
2173         }
2174 
2175         types::ObjectMap l_objectInterfaceMap;
2176 
2177         if (m_parsedJson["frus"].contains(i_vpdPath))
2178         {
2179             for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
2180             {
2181                 sdbusplus::message::object_path l_fruObjectPath(
2182                     l_Fru["inventoryPath"]);
2183 
2184                 types::PropertyMap l_propertyValueMap;
2185                 l_propertyValueMap.emplace("Status", i_value);
2186                 l_propertyValueMap.insert(l_timeStampMap.begin(),
2187                                           l_timeStampMap.end());
2188 
2189                 uint16_t l_errCode = 0;
2190                 types::InterfaceMap l_interfaces;
2191                 vpdSpecificUtility::insertOrMerge(
2192                     l_interfaces, constants::vpdCollectionInterface,
2193                     move(l_propertyValueMap), l_errCode);
2194 
2195                 if (l_errCode)
2196                 {
2197                     logging::logMessage(
2198                         "Failed to insert value into map, error : " +
2199                         commonUtility::getErrCodeMsg(l_errCode));
2200                 }
2201 
2202                 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
2203                                              std::move(l_interfaces));
2204             }
2205         }
2206         else
2207         {
2208             // consider it as an inventory path.
2209             if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
2210             {
2211                 throw std::runtime_error(
2212                     "Invalid inventory path: " + i_vpdPath +
2213                     ". Can't set collection Status property");
2214             }
2215 
2216             types::PropertyMap l_propertyValueMap;
2217             l_propertyValueMap.emplace("Status", i_value);
2218             l_propertyValueMap.insert(l_timeStampMap.begin(),
2219                                       l_timeStampMap.end());
2220 
2221             uint16_t l_errCode = 0;
2222             types::InterfaceMap l_interfaces;
2223             vpdSpecificUtility::insertOrMerge(
2224                 l_interfaces, constants::vpdCollectionInterface,
2225                 move(l_propertyValueMap), l_errCode);
2226 
2227             if (l_errCode)
2228             {
2229                 logging::logMessage(
2230                     "Failed to insert value into map, error : " +
2231                     commonUtility::getErrCodeMsg(l_errCode));
2232             }
2233 
2234             l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
2235         }
2236 
2237         // Notify PIM
2238         if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
2239         {
2240             throw DbusException(
2241                 std::string(__FUNCTION__) +
2242                 "Call to PIM failed while setting collection Status property for path " +
2243                 i_vpdPath);
2244         }
2245     }
2246     catch (const std::exception& l_ex)
2247     {
2248         EventLogger::createSyncPel(
2249             EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
2250             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
2251             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
2252     }
2253 }
2254 } // namespace vpd
2255