xref: /openbmc/openpower-vpd-parser/vpd-manager/src/manager.cpp (revision fa5e4d325ef9cea3c841fe89d202c340f92bd8c6)
1 #include "config.h"
2 
3 #include "manager.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 #include "types.hpp"
13 #include "utility/dbus_utility.hpp"
14 #include "utility/json_utility.hpp"
15 #include "utility/vpd_specific_utility.hpp"
16 
17 #include <boost/asio/steady_timer.hpp>
18 #include <sdbusplus/bus/match.hpp>
19 #include <sdbusplus/message.hpp>
20 
21 namespace vpd
22 {
Manager(const std::shared_ptr<boost::asio::io_context> & ioCon,const std::shared_ptr<sdbusplus::asio::dbus_interface> & iFace,const std::shared_ptr<sdbusplus::asio::connection> & asioConnection)23 Manager::Manager(
24     const std::shared_ptr<boost::asio::io_context>& ioCon,
25     const std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace,
26     const std::shared_ptr<sdbusplus::asio::connection>& asioConnection) :
27     m_ioContext(ioCon), m_interface(iFace), m_asioConnection(asioConnection)
28 {
29     try
30     {
31 #ifdef IBM_SYSTEM
32         m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
33 
34         // Set up minimal things that is needed before bus name is claimed.
35         m_worker->performInitialSetup();
36 
37         // set callback to detect any asset tag change
38         registerAssetTagChangeCallback();
39 
40         // set async timer to detect if system VPD is published on D-Bus.
41         SetTimerToDetectSVPDOnDbus();
42 
43         // set async timer to detect if VPD collection is done.
44         SetTimerToDetectVpdCollectionStatus();
45 
46         // Instantiate GpioMonitor class
47         m_gpioMonitor = std::make_shared<GpioMonitor>(
48             m_worker->getSysCfgJsonObj(), m_worker, m_ioContext);
49 
50 #endif
51         // set callback to detect host state change.
52         registerHostStateChangeCallback();
53 
54         // For backward compatibility. Should be depricated.
55         iFace->register_method(
56             "WriteKeyword",
57             [this](const sdbusplus::message::object_path i_path,
58                    const std::string i_recordName, const std::string i_keyword,
59                    const types::BinaryVector i_value) -> int {
60                 return this->updateKeyword(
61                     i_path, std::make_tuple(i_recordName, i_keyword, i_value));
62             });
63 
64         // Register methods under com.ibm.VPD.Manager interface
65         iFace->register_method(
66             "UpdateKeyword",
67             [this](const types::Path i_vpdPath,
68                    const types::WriteVpdParams i_paramsToWriteData) -> int {
69                 return this->updateKeyword(i_vpdPath, i_paramsToWriteData);
70             });
71 
72         iFace->register_method(
73             "WriteKeywordOnHardware",
74             [this](const types::Path i_fruPath,
75                    const types::WriteVpdParams i_paramsToWriteData) -> int {
76                 return this->updateKeywordOnHardware(i_fruPath,
77                                                      i_paramsToWriteData);
78             });
79 
80         iFace->register_method(
81             "ReadKeyword",
82             [this](const types::Path i_fruPath,
83                    const types::ReadVpdParams i_paramsToReadData)
84                 -> types::DbusVariantType {
85                 return this->readKeyword(i_fruPath, i_paramsToReadData);
86             });
87 
88         iFace->register_method(
89             "CollectFRUVPD",
90             [this](const sdbusplus::message::object_path& i_dbusObjPath) {
91                 this->collectSingleFruVpd(i_dbusObjPath);
92             });
93 
94         iFace->register_method(
95             "deleteFRUVPD",
96             [this](const sdbusplus::message::object_path& i_dbusObjPath) {
97                 this->deleteSingleFruVpd(i_dbusObjPath);
98             });
99 
100         iFace->register_method(
101             "GetExpandedLocationCode",
102             [this](const std::string& i_unexpandedLocationCode,
103                    uint16_t& i_nodeNumber) -> std::string {
104                 return this->getExpandedLocationCode(i_unexpandedLocationCode,
105                                                      i_nodeNumber);
106             });
107 
108         iFace->register_method("GetFRUsByExpandedLocationCode",
109                                [this](const std::string& i_expandedLocationCode)
110                                    -> types::ListOfPaths {
111                                    return this->getFrusByExpandedLocationCode(
112                                        i_expandedLocationCode);
113                                });
114 
115         iFace->register_method(
116             "GetFRUsByUnexpandedLocationCode",
117             [this](const std::string& i_unexpandedLocationCode,
118                    uint16_t& i_nodeNumber) -> types::ListOfPaths {
119                 return this->getFrusByUnexpandedLocationCode(
120                     i_unexpandedLocationCode, i_nodeNumber);
121             });
122 
123         iFace->register_method(
124             "GetHardwarePath",
125             [this](const sdbusplus::message::object_path& i_dbusObjPath)
126                 -> std::string { return this->getHwPath(i_dbusObjPath); });
127 
128         iFace->register_method("PerformVPDRecollection", [this]() {
129             this->performVpdRecollection();
130         });
131 
132         // Indicates FRU VPD collection for the system has not started.
133         iFace->register_property_rw<std::string>(
134             "CollectionStatus", sdbusplus::vtable::property_::emits_change,
135             [this](const std::string l_currStatus, const auto&) {
136                 m_vpdCollectionStatus = l_currStatus;
137                 return 0;
138             },
139             [this](const auto&) { return m_vpdCollectionStatus; });
140     }
141     catch (const std::exception& e)
142     {
143         logging::logMessage(
144             "VPD-Manager service failed. " + std::string(e.what()));
145         throw;
146     }
147 }
148 
149 #ifdef IBM_SYSTEM
registerAssetTagChangeCallback()150 void Manager::registerAssetTagChangeCallback()
151 {
152     static std::shared_ptr<sdbusplus::bus::match_t> l_assetMatch =
153         std::make_shared<sdbusplus::bus::match_t>(
154             *m_asioConnection,
155             sdbusplus::bus::match::rules::propertiesChanged(
156                 constants::systemInvPath, constants::assetTagInf),
157             [this](sdbusplus::message_t& l_msg) {
158                 processAssetTagChangeCallback(l_msg);
159             });
160 }
161 
processAssetTagChangeCallback(sdbusplus::message_t & i_msg)162 void Manager::processAssetTagChangeCallback(sdbusplus::message_t& i_msg)
163 {
164     try
165     {
166         if (i_msg.is_method_error())
167         {
168             throw std::runtime_error(
169                 "Error reading callback msg for asset tag.");
170         }
171 
172         std::string l_objectPath;
173         types::PropertyMap l_propMap;
174         i_msg.read(l_objectPath, l_propMap);
175 
176         const auto& l_itrToAssetTag = l_propMap.find("AssetTag");
177         if (l_itrToAssetTag != l_propMap.end())
178         {
179             if (auto l_assetTag =
180                     std::get_if<std::string>(&(l_itrToAssetTag->second)))
181             {
182                 // Call Notify to persist the AssetTag
183                 types::ObjectMap l_objectMap = {
184                     {sdbusplus::message::object_path(constants::systemInvPath),
185                      {{constants::assetTagInf, {{"AssetTag", *l_assetTag}}}}}};
186 
187                 // Notify PIM
188                 if (!dbusUtility::callPIM(move(l_objectMap)))
189                 {
190                     throw std::runtime_error(
191                         "Call to PIM failed for asset tag update.");
192                 }
193             }
194         }
195         else
196         {
197             throw std::runtime_error(
198                 "Could not find asset tag in callback message.");
199         }
200     }
201     catch (const std::exception& l_ex)
202     {
203         // TODO: Log PEL with below description.
204         logging::logMessage("Asset tag callback update failed with error: " +
205                             std::string(l_ex.what()));
206     }
207 }
208 
SetTimerToDetectSVPDOnDbus()209 void Manager::SetTimerToDetectSVPDOnDbus()
210 {
211     static boost::asio::steady_timer timer(*m_ioContext);
212 
213     // timer for 2 seconds
214     auto asyncCancelled = timer.expires_after(std::chrono::seconds(2));
215 
216     (asyncCancelled == 0) ? logging::logMessage("Timer started")
217                           : logging::logMessage("Timer re-started");
218 
219     timer.async_wait([this](const boost::system::error_code& ec) {
220         if (ec == boost::asio::error::operation_aborted)
221         {
222             throw std::runtime_error(
223                 "Timer to detect system VPD collection status was aborted");
224         }
225 
226         if (ec)
227         {
228             throw std::runtime_error(
229                 "Timer to detect System VPD collection failed");
230         }
231 
232         if (m_worker->isSystemVPDOnDBus())
233         {
234             // cancel the timer
235             timer.cancel();
236 
237             // Triggering FRU VPD collection. Setting status to "In
238             // Progress".
239             m_interface->set_property("CollectionStatus",
240                                       std::string("InProgress"));
241             m_worker->collectFrusFromJson();
242         }
243     });
244 }
245 
SetTimerToDetectVpdCollectionStatus()246 void Manager::SetTimerToDetectVpdCollectionStatus()
247 {
248     // Keeping max retry for 2 minutes. TODO: Make it cinfigurable based on
249     // system type.
250     static constexpr auto MAX_RETRY = 40;
251 
252     static boost::asio::steady_timer l_timer(*m_ioContext);
253     static uint8_t l_timerRetry = 0;
254 
255     auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(3));
256 
257     (l_asyncCancelled == 0)
258         ? logging::logMessage("Collection Timer started")
259         : logging::logMessage("Collection Timer re-started");
260 
261     l_timer.async_wait([this](const boost::system::error_code& ec) {
262         if (ec == boost::asio::error::operation_aborted)
263         {
264             throw std::runtime_error(
265                 "Timer to detect thread collection status was aborted");
266         }
267 
268         if (ec)
269         {
270             throw std::runtime_error(
271                 "Timer to detect thread collection failed");
272         }
273 
274         if (m_worker->isAllFruCollectionDone())
275         {
276             // cancel the timer
277             l_timer.cancel();
278             m_interface->set_property("CollectionStatus",
279                                       std::string("Completed"));
280 
281             const nlohmann::json& l_sysCfgJsonObj =
282                 m_worker->getSysCfgJsonObj();
283             if (jsonUtility::isBackupAndRestoreRequired(l_sysCfgJsonObj))
284             {
285                 BackupAndRestore l_backupAndRestoreObj(l_sysCfgJsonObj);
286                 l_backupAndRestoreObj.backupAndRestore();
287             }
288         }
289         else
290         {
291             auto l_threadCount = m_worker->getActiveThreadCount();
292             if (l_timerRetry == MAX_RETRY)
293             {
294                 l_timer.cancel();
295                 logging::logMessage("Taking too long. Active thread = " +
296                                     std::to_string(l_threadCount));
297             }
298             else
299             {
300                 l_timerRetry++;
301                 logging::logMessage("Waiting... active thread = " +
302                                     std::to_string(l_threadCount) + "After " +
303                                     std::to_string(l_timerRetry) + " re-tries");
304 
305                 SetTimerToDetectVpdCollectionStatus();
306             }
307         }
308     });
309 }
310 #endif
311 
updateKeyword(const types::Path i_vpdPath,const types::WriteVpdParams i_paramsToWriteData)312 int Manager::updateKeyword(const types::Path i_vpdPath,
313                            const types::WriteVpdParams i_paramsToWriteData)
314 {
315     if (i_vpdPath.empty())
316     {
317         logging::logMessage("Given VPD path is empty.");
318         return -1;
319     }
320 
321     types::Path l_fruPath;
322     nlohmann::json l_sysCfgJsonObj{};
323 
324     if (m_worker.get() != nullptr)
325     {
326         l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
327 
328         // Get the EEPROM path
329         if (!l_sysCfgJsonObj.empty())
330         {
331             try
332             {
333                 l_fruPath =
334                     jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath);
335             }
336             catch (const std::exception& l_exception)
337             {
338                 logging::logMessage(
339                     "Error while getting FRU path, Path: " + i_vpdPath +
340                     ", error: " + std::string(l_exception.what()));
341                 return -1;
342             }
343         }
344     }
345 
346     if (l_fruPath.empty())
347     {
348         l_fruPath = i_vpdPath;
349     }
350 
351     try
352     {
353         std::shared_ptr<Parser> l_parserObj =
354             std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
355         return l_parserObj->updateVpdKeyword(i_paramsToWriteData);
356     }
357     catch (const std::exception& l_exception)
358     {
359         // TODO:: error log needed
360         logging::logMessage("Update keyword failed for file[" + i_vpdPath +
361                             "], reason: " + std::string(l_exception.what()));
362         return -1;
363     }
364 }
365 
updateKeywordOnHardware(const types::Path i_fruPath,const types::WriteVpdParams i_paramsToWriteData)366 int Manager::updateKeywordOnHardware(
367     const types::Path i_fruPath,
368     const types::WriteVpdParams i_paramsToWriteData) noexcept
369 {
370     try
371     {
372         if (i_fruPath.empty())
373         {
374             throw std::runtime_error("Given FRU path is empty");
375         }
376 
377         nlohmann::json l_sysCfgJsonObj{};
378 
379         if (m_worker.get() != nullptr)
380         {
381             l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
382         }
383 
384         std::shared_ptr<Parser> l_parserObj =
385             std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj);
386         return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData);
387     }
388     catch (const std::exception& l_exception)
389     {
390         EventLogger::createAsyncPel(
391             types::ErrorType::InvalidEeprom, types::SeverityType::Informational,
392             __FILE__, __FUNCTION__, 0,
393             "Update keyword on hardware failed for file[" + i_fruPath +
394                 "], reason: " + std::string(l_exception.what()),
395             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
396 
397         return constants::FAILURE;
398     }
399 }
400 
readKeyword(const types::Path i_fruPath,const types::ReadVpdParams i_paramsToReadData)401 types::DbusVariantType Manager::readKeyword(
402     const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData)
403 {
404     try
405     {
406         nlohmann::json l_jsonObj{};
407 
408         if (m_worker.get() != nullptr)
409         {
410             l_jsonObj = m_worker->getSysCfgJsonObj();
411         }
412 
413         std::error_code ec;
414 
415         // Check if given path is filesystem path
416         if (!std::filesystem::exists(i_fruPath, ec) && (ec))
417         {
418             throw std::runtime_error(
419                 "Given file path " + i_fruPath + " not found.");
420         }
421 
422         logging::logMessage("Performing VPD read on " + i_fruPath);
423 
424         std::shared_ptr<vpd::Parser> l_parserObj =
425             std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj);
426 
427         std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance =
428             l_parserObj->getVpdParserInstance();
429 
430         return (
431             l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData));
432     }
433     catch (const std::exception& e)
434     {
435         logging::logMessage(
436             e.what() + std::string(". VPD manager read operation failed for ") +
437             i_fruPath);
438         throw types::DeviceError::ReadFailure();
439     }
440 }
441 
collectSingleFruVpd(const sdbusplus::message::object_path & i_dbusObjPath)442 void Manager::collectSingleFruVpd(
443     const sdbusplus::message::object_path& i_dbusObjPath)
444 {
445     try
446     {
447         if (m_vpdCollectionStatus != "Completed")
448         {
449             throw std::runtime_error(
450                 "Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " +
451                 std::string(i_dbusObjPath));
452         }
453 
454         // Get system config JSON object from worker class
455         nlohmann::json l_sysCfgJsonObj{};
456 
457         if (m_worker.get() != nullptr)
458         {
459             l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
460         }
461 
462         // Check if system config JSON is present
463         if (l_sysCfgJsonObj.empty())
464         {
465             throw std::runtime_error(
466                 "System config JSON object not present. Single FRU VPD collection failed for " +
467                 std::string(i_dbusObjPath));
468         }
469 
470         // Get FRU path for the given D-bus object path from JSON
471         const std::string& l_fruPath =
472             jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_dbusObjPath);
473 
474         if (l_fruPath.empty())
475         {
476             throw std::runtime_error(
477                 "D-bus object path not present in JSON. Single FRU VPD collection failed for " +
478                 std::string(i_dbusObjPath));
479         }
480 
481         // Check if host is up and running
482         if (dbusUtility::isHostRunning())
483         {
484             if (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
485                                                         l_fruPath))
486             {
487                 throw std::runtime_error(
488                     "Given FRU is not replaceable at host runtime. Single FRU VPD collection failed for " +
489                     std::string(i_dbusObjPath));
490             }
491         }
492         else if (dbusUtility::isBMCReady())
493         {
494             if (!jsonUtility::isFruReplaceableAtStandby(l_sysCfgJsonObj,
495                                                         l_fruPath) &&
496                 (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
497                                                          l_fruPath)))
498             {
499                 throw std::runtime_error(
500                     "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection failed for " +
501                     std::string(i_dbusObjPath));
502             }
503         }
504 
505         // Parse VPD
506         types::VPDMapVariant l_parsedVpd = m_worker->parseVpdFile(l_fruPath);
507 
508         // If l_parsedVpd is pointing to std::monostate
509         if (l_parsedVpd.index() == 0)
510         {
511             throw std::runtime_error(
512                 "VPD parsing failed for " + std::string(i_dbusObjPath));
513         }
514 
515         // Get D-bus object map from worker class
516         types::ObjectMap l_dbusObjectMap;
517         m_worker->populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
518 
519         if (l_dbusObjectMap.empty())
520         {
521             throw std::runtime_error(
522                 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
523                 std::string(i_dbusObjPath));
524         }
525 
526         // Call PIM's Notify method
527         if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
528         {
529             throw std::runtime_error(
530                 "Notify PIM failed. Single FRU VPD collection failed for " +
531                 std::string(i_dbusObjPath));
532         }
533     }
534     catch (const std::exception& l_error)
535     {
536         // TODO: Log PEL
537         logging::logMessage(std::string(l_error.what()));
538     }
539 }
540 
deleteSingleFruVpd(const sdbusplus::message::object_path & i_dbusObjPath)541 void Manager::deleteSingleFruVpd(
542     const sdbusplus::message::object_path& i_dbusObjPath)
543 {
544     try
545     {
546         if (std::string(i_dbusObjPath).empty())
547         {
548             throw std::runtime_error(
549                 "Given DBus object path is empty. Aborting FRU VPD deletion.");
550         }
551 
552         if (m_worker.get() == nullptr)
553         {
554             throw std::runtime_error(
555                 "Worker object not found, can't perform FRU VPD deletion for: " +
556                 std::string(i_dbusObjPath));
557         }
558 
559         m_worker->deleteFruVpd(std::string(i_dbusObjPath));
560     }
561     catch (const std::exception& l_ex)
562     {
563         // TODO: Log PEL
564         logging::logMessage(l_ex.what());
565     }
566 }
567 
isValidUnexpandedLocationCode(const std::string & i_unexpandedLocationCode)568 bool Manager::isValidUnexpandedLocationCode(
569     const std::string& i_unexpandedLocationCode)
570 {
571     if ((i_unexpandedLocationCode.length() <
572          constants::UNEXP_LOCATION_CODE_MIN_LENGTH) ||
573         ((i_unexpandedLocationCode.compare(0, 4, "Ufcs") !=
574           constants::STR_CMP_SUCCESS) &&
575          (i_unexpandedLocationCode.compare(0, 4, "Umts") !=
576           constants::STR_CMP_SUCCESS)) ||
577         ((i_unexpandedLocationCode.length() >
578           constants::UNEXP_LOCATION_CODE_MIN_LENGTH) &&
579          (i_unexpandedLocationCode.find("-") != 4)))
580     {
581         return false;
582     }
583 
584     return true;
585 }
586 
getExpandedLocationCode(const std::string & i_unexpandedLocationCode,const uint16_t i_nodeNumber)587 std::string Manager::getExpandedLocationCode(
588     const std::string& i_unexpandedLocationCode,
589     [[maybe_unused]] const uint16_t i_nodeNumber)
590 {
591     if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
592     {
593         phosphor::logging::elog<types::DbusInvalidArgument>(
594             types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
595             types::InvalidArgument::ARGUMENT_VALUE(
596                 i_unexpandedLocationCode.c_str()));
597     }
598 
599     const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
600     if (!l_sysCfgJsonObj.contains("frus"))
601     {
602         logging::logMessage("Missing frus tag in system config JSON");
603     }
604 
605     const nlohmann::json& l_listOfFrus =
606         l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
607 
608     for (const auto& l_frus : l_listOfFrus.items())
609     {
610         for (const auto& l_aFru : l_frus.value())
611         {
612             if (l_aFru["extraInterfaces"].contains(
613                     constants::locationCodeInf) &&
614                 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
615                     "LocationCode", "") == i_unexpandedLocationCode)
616             {
617                 return std::get<std::string>(dbusUtility::readDbusProperty(
618                     l_aFru["serviceName"], l_aFru["inventoryPath"],
619                     constants::locationCodeInf, "LocationCode"));
620             }
621         }
622     }
623     phosphor::logging::elog<types::DbusInvalidArgument>(
624         types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
625         types::InvalidArgument::ARGUMENT_VALUE(
626             i_unexpandedLocationCode.c_str()));
627 }
628 
getFrusByUnexpandedLocationCode(const std::string & i_unexpandedLocationCode,const uint16_t i_nodeNumber)629 types::ListOfPaths Manager::getFrusByUnexpandedLocationCode(
630     const std::string& i_unexpandedLocationCode,
631     [[maybe_unused]] const uint16_t i_nodeNumber)
632 {
633     types::ListOfPaths l_inventoryPaths;
634 
635     if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
636     {
637         phosphor::logging::elog<types::DbusInvalidArgument>(
638             types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
639             types::InvalidArgument::ARGUMENT_VALUE(
640                 i_unexpandedLocationCode.c_str()));
641     }
642 
643     const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
644     if (!l_sysCfgJsonObj.contains("frus"))
645     {
646         logging::logMessage("Missing frus tag in system config JSON");
647     }
648 
649     const nlohmann::json& l_listOfFrus =
650         l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
651 
652     for (const auto& l_frus : l_listOfFrus.items())
653     {
654         for (const auto& l_aFru : l_frus.value())
655         {
656             if (l_aFru["extraInterfaces"].contains(
657                     constants::locationCodeInf) &&
658                 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
659                     "LocationCode", "") == i_unexpandedLocationCode)
660             {
661                 l_inventoryPaths.push_back(
662                     l_aFru.at("inventoryPath")
663                         .get_ref<const nlohmann::json::string_t&>());
664             }
665         }
666     }
667 
668     if (l_inventoryPaths.empty())
669     {
670         phosphor::logging::elog<types::DbusInvalidArgument>(
671             types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
672             types::InvalidArgument::ARGUMENT_VALUE(
673                 i_unexpandedLocationCode.c_str()));
674     }
675 
676     return l_inventoryPaths;
677 }
678 
679 std::string
getHwPath(const sdbusplus::message::object_path & i_dbusObjPath)680     Manager::getHwPath(const sdbusplus::message::object_path& i_dbusObjPath)
681 {
682     // Dummy code to supress unused variable warning. To be removed.
683     logging::logMessage(std::string(i_dbusObjPath));
684 
685     return std::string{};
686 }
687 
getUnexpandedLocationCode(const std::string & i_expandedLocationCode)688 std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode(
689     const std::string& i_expandedLocationCode)
690 {
691     /**
692      * Location code should always start with U and fulfil minimum length
693      * criteria.
694      */
695     if (i_expandedLocationCode[0] != 'U' ||
696         i_expandedLocationCode.length() <
697             constants::EXP_LOCATION_CODE_MIN_LENGTH)
698     {
699         phosphor::logging::elog<types::DbusInvalidArgument>(
700             types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
701             types::InvalidArgument::ARGUMENT_VALUE(
702                 i_expandedLocationCode.c_str()));
703     }
704 
705     std::string l_fcKwd;
706 
707     auto l_fcKwdValue = dbusUtility::readDbusProperty(
708         "xyz.openbmc_project.Inventory.Manager",
709         "/xyz/openbmc_project/inventory/system/chassis/motherboard",
710         "com.ibm.ipzvpd.VCEN", "FC");
711 
712     if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue))
713     {
714         l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
715     }
716 
717     // Get the first part of expanded location code to check for FC or TM.
718     std::string l_firstKwd = i_expandedLocationCode.substr(1, 4);
719 
720     std::string l_unexpandedLocationCode{};
721     uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER;
722 
723     // Check if this value matches the value of FC keyword.
724     if (l_fcKwd.substr(0, 4) == l_firstKwd)
725     {
726         /**
727          * Period(.) should be there in expanded location code to seggregate
728          * FC, node number and SE values.
729          */
730         size_t l_nodeStartPos = i_expandedLocationCode.find('.');
731         if (l_nodeStartPos == std::string::npos)
732         {
733             phosphor::logging::elog<types::DbusInvalidArgument>(
734                 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
735                 types::InvalidArgument::ARGUMENT_VALUE(
736                     i_expandedLocationCode.c_str()));
737         }
738 
739         size_t l_nodeEndPos =
740             i_expandedLocationCode.find('.', l_nodeStartPos + 1);
741         if (l_nodeEndPos == std::string::npos)
742         {
743             phosphor::logging::elog<types::DbusInvalidArgument>(
744                 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
745                 types::InvalidArgument::ARGUMENT_VALUE(
746                     i_expandedLocationCode.c_str()));
747         }
748 
749         // Skip 3 bytes for '.ND'
750         l_nodeNummber = std::stoi(i_expandedLocationCode.substr(
751             l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3)));
752 
753         /**
754          * Confirm if there are other details apart FC, node number and SE
755          * in location code
756          */
757         if (i_expandedLocationCode.length() >
758             constants::EXP_LOCATION_CODE_MIN_LENGTH)
759         {
760             l_unexpandedLocationCode =
761                 i_expandedLocationCode[0] + std::string("fcs") +
762                 i_expandedLocationCode.substr(
763                     l_nodeEndPos + 1 + constants::SE_KWD_LENGTH,
764                     std::string::npos);
765         }
766         else
767         {
768             l_unexpandedLocationCode = "Ufcs";
769         }
770     }
771     else
772     {
773         std::string l_tmKwd;
774         // Read TM keyword value.
775         auto l_tmKwdValue = dbusUtility::readDbusProperty(
776             "xyz.openbmc_project.Inventory.Manager",
777             "/xyz/openbmc_project/inventory/system/chassis/motherboard",
778             "com.ibm.ipzvpd.VSYS", "TM");
779 
780         if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue))
781         {
782             l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
783         }
784 
785         // Check if the substr matches to TM keyword value.
786         if (l_tmKwd.substr(0, 4) == l_firstKwd)
787         {
788             /**
789              * System location code will not have node number and any other
790              * details.
791              */
792             l_unexpandedLocationCode = "Umts";
793         }
794         // The given location code is neither "fcs" or "mts".
795         else
796         {
797             phosphor::logging::elog<types::DbusInvalidArgument>(
798                 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
799                 types::InvalidArgument::ARGUMENT_VALUE(
800                     i_expandedLocationCode.c_str()));
801         }
802     }
803 
804     return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber);
805 }
806 
getFrusByExpandedLocationCode(const std::string & i_expandedLocationCode)807 types::ListOfPaths Manager::getFrusByExpandedLocationCode(
808     const std::string& i_expandedLocationCode)
809 {
810     std::tuple<std::string, uint16_t> l_locationAndNodePair =
811         getUnexpandedLocationCode(i_expandedLocationCode);
812 
813     return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair),
814                                            std::get<1>(l_locationAndNodePair));
815 }
816 
registerHostStateChangeCallback()817 void Manager::registerHostStateChangeCallback()
818 {
819     static std::shared_ptr<sdbusplus::bus::match_t> l_hostState =
820         std::make_shared<sdbusplus::bus::match_t>(
821             *m_asioConnection,
822             sdbusplus::bus::match::rules::propertiesChanged(
823                 constants::hostObjectPath, constants::hostInterface),
824             [this](sdbusplus::message_t& i_msg) {
825                 hostStateChangeCallBack(i_msg);
826             });
827 }
828 
hostStateChangeCallBack(sdbusplus::message_t & i_msg)829 void Manager::hostStateChangeCallBack(sdbusplus::message_t& i_msg)
830 {
831     try
832     {
833         if (i_msg.is_method_error())
834         {
835             throw std::runtime_error(
836                 "Error reading callback message for host state");
837         }
838 
839         std::string l_objectPath;
840         types::PropertyMap l_propMap;
841         i_msg.read(l_objectPath, l_propMap);
842 
843         const auto l_itr = l_propMap.find("CurrentHostState");
844 
845         if (l_itr == l_propMap.end())
846         {
847             throw std::runtime_error(
848                 "CurrentHostState field is missing in callback message");
849         }
850 
851         if (auto l_hostState = std::get_if<std::string>(&(l_itr->second)))
852         {
853             // implies system is moving from standby to power on state
854             if (*l_hostState == "xyz.openbmc_project.State.Host.HostState."
855                                 "TransitioningToRunning")
856             {
857                 // TODO: check for all the essential FRUs in the system.
858 
859                 // Perform recollection.
860                 performVpdRecollection();
861                 return;
862             }
863         }
864         else
865         {
866             throw std::runtime_error(
867                 "Invalid type recieved in variant for host state.");
868         }
869     }
870     catch (const std::exception& l_ex)
871     {
872         // TODO: Log PEL.
873         logging::logMessage(l_ex.what());
874     }
875 }
876 
performVpdRecollection()877 void Manager::performVpdRecollection()
878 {
879     try
880     {
881         if (m_worker.get() != nullptr)
882         {
883             nlohmann::json l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
884 
885             // Check if system config JSON is present
886             if (l_sysCfgJsonObj.empty())
887             {
888                 throw std::runtime_error(
889                     "System config json object is empty, can't process recollection.");
890             }
891 
892             const auto& l_frusReplaceableAtStandby =
893                 jsonUtility::getListOfFrusReplaceableAtStandby(l_sysCfgJsonObj);
894 
895             for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
896             {
897                 // ToDo: Add some logic/trace to know the flow to
898                 // collectSingleFruVpd has been directed via
899                 // performVpdRecollection.
900                 collectSingleFruVpd(l_fruInventoryPath);
901             }
902             return;
903         }
904 
905         throw std::runtime_error(
906             "Worker object not found can't process recollection");
907     }
908     catch (const std::exception& l_ex)
909     {
910         // TODO Log PEL
911         logging::logMessage(
912             "VPD recollection failed with error: " + std::string(l_ex.what()));
913     }
914 }
915 } // namespace vpd
916