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