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