xref: /openbmc/openpower-vpd-parser/vpd-manager/src/listener.cpp (revision 7aaf1b098d8ce806983aaf796fe82694c733de1a)
1 #include "listener.hpp"
2 
3 #include "constants.hpp"
4 #include "exceptions.hpp"
5 #include "logger.hpp"
6 #include "utility/common_utility.hpp"
7 #include "utility/dbus_utility.hpp"
8 #include "utility/event_logger_utility.hpp"
9 #include "utility/json_utility.hpp"
10 #include "utility/vpd_specific_utility.hpp"
11 
12 namespace vpd
13 {
Listener(const std::shared_ptr<Worker> & i_worker,const std::shared_ptr<sdbusplus::asio::connection> & i_asioConnection)14 Listener::Listener(
15     const std::shared_ptr<Worker>& i_worker,
16     const std::shared_ptr<sdbusplus::asio::connection>& i_asioConnection) :
17     m_worker(i_worker), m_asioConnection(i_asioConnection)
18 {
19     if (m_worker == nullptr)
20     {
21         throw std::runtime_error(
22             "Cannot instantiate Listener as Worker is not initialized");
23     }
24 }
25 
registerHostStateChangeCallback() const26 void Listener::registerHostStateChangeCallback() const noexcept
27 {
28     try
29     {
30         static std::shared_ptr<sdbusplus::bus::match_t> l_hostState =
31             std::make_shared<sdbusplus::bus::match_t>(
32                 *m_asioConnection,
33                 sdbusplus::bus::match::rules::propertiesChanged(
34                     constants::hostObjectPath, constants::hostInterface),
35                 [this](sdbusplus::message_t& i_msg) {
36                     hostStateChangeCallBack(i_msg);
37                 });
38     }
39     catch (const std::exception& l_ex)
40     {
41         Logger::getLoggerInstance()->logMessage(
42             std::string(
43                 "Register Host state change callback failed, reason: ") +
44                 std::string(l_ex.what()),
45             PlaceHolder::PEL,
46             types::PelInfoTuple{EventLogger::getErrorType(l_ex),
47                                 types::SeverityType::Informational, 0,
48                                 std::nullopt, std::nullopt, std::nullopt,
49                                 std::nullopt});
50     }
51 }
52 
hostStateChangeCallBack(sdbusplus::message_t & i_msg) const53 void Listener::hostStateChangeCallBack(
54     sdbusplus::message_t& i_msg) const noexcept
55 {
56     try
57     {
58         if (i_msg.is_method_error())
59         {
60             throw std::runtime_error(
61                 "Error reading callback message for host state");
62         }
63 
64         std::string l_objectPath;
65         types::PropertyMap l_propMap;
66         i_msg.read(l_objectPath, l_propMap);
67 
68         const auto l_itr = l_propMap.find("CurrentHostState");
69 
70         if (l_itr == l_propMap.end())
71         {
72             // CurrentHostState is not found in the callback message
73             return;
74         }
75 
76         if (auto l_hostState = std::get_if<std::string>(&(l_itr->second)))
77         {
78             // implies system is moving from standby to power on state
79             if (*l_hostState == "xyz.openbmc_project.State.Host.HostState."
80                                 "TransitioningToRunning")
81             {
82                 // TODO: check for all the essential FRUs in the system.
83 
84                 if (m_worker.get() != nullptr)
85                 {
86                     // Perform recollection.
87                     m_worker->performVpdRecollection();
88                 }
89                 else
90                 {
91                     logging::logMessage(
92                         "Failed to get worker object, Abort re-collection");
93                 }
94             }
95         }
96         else
97         {
98             throw std::runtime_error(
99                 "Invalid type recieved in variant for host state.");
100         }
101     }
102     catch (const std::exception& l_ex)
103     {
104         Logger::getLoggerInstance()->logMessage(
105             std::string("Host state change callback failed, reason: ") +
106                 std::string(l_ex.what()),
107             PlaceHolder::PEL,
108             types::PelInfoTuple{EventLogger::getErrorType(l_ex),
109                                 types::SeverityType::Informational, 0,
110                                 std::nullopt, std::nullopt, std::nullopt,
111                                 std::nullopt});
112     }
113 }
114 
registerAssetTagChangeCallback() const115 void Listener::registerAssetTagChangeCallback() const noexcept
116 {
117     try
118     {
119         static std::shared_ptr<sdbusplus::bus::match_t> l_assetMatch =
120             std::make_shared<sdbusplus::bus::match_t>(
121                 *m_asioConnection,
122                 sdbusplus::bus::match::rules::propertiesChanged(
123                     constants::systemInvPath, constants::assetTagInf),
124                 [this](sdbusplus::message_t& l_msg) {
125                     assetTagChangeCallback(l_msg);
126                 });
127     }
128     catch (const std::exception& l_ex)
129     {
130         Logger::getLoggerInstance()->logMessage(
131             std::string("Register AssetTag change callback failed, reason: ") +
132                 std::string(l_ex.what()),
133             PlaceHolder::PEL,
134             types::PelInfoTuple{EventLogger::getErrorType(l_ex),
135                                 types::SeverityType::Informational, 0,
136                                 std::nullopt, std::nullopt, std::nullopt,
137                                 std::nullopt});
138     }
139 }
140 
assetTagChangeCallback(sdbusplus::message_t & i_msg) const141 void Listener::assetTagChangeCallback(
142     sdbusplus::message_t& i_msg) const noexcept
143 {
144     try
145     {
146         if (i_msg.is_method_error())
147         {
148             throw std::runtime_error(
149                 "Error reading callback msg for asset tag.");
150         }
151 
152         std::string l_objectPath;
153         types::PropertyMap l_propMap;
154         i_msg.read(l_objectPath, l_propMap);
155 
156         const auto& l_itrToAssetTag = l_propMap.find("AssetTag");
157         if (l_itrToAssetTag != l_propMap.end())
158         {
159             if (auto l_assetTag =
160                     std::get_if<std::string>(&(l_itrToAssetTag->second)))
161             {
162                 // Call Notify to persist the AssetTag
163                 types::ObjectMap l_objectMap = {
164                     {sdbusplus::message::object_path(constants::systemInvPath),
165                      {{constants::assetTagInf, {{"AssetTag", *l_assetTag}}}}}};
166 
167                 // Call method to update the dbus
168                 if (!dbusUtility::publishVpdOnDBus(move(l_objectMap)))
169                 {
170                     throw std::runtime_error(
171                         "Call to PIM failed for asset tag update.");
172                 }
173             }
174         }
175         else
176         {
177             throw std::runtime_error(
178                 "Could not find asset tag in callback message.");
179         }
180     }
181     catch (const std::exception& l_ex)
182     {
183         Logger::getLoggerInstance()->logMessage(
184             std::string("AssetTag update failed, reason: ") +
185                 std::string(l_ex.what()),
186             PlaceHolder::PEL,
187             types::PelInfoTuple{EventLogger::getErrorType(l_ex),
188                                 types::SeverityType::Informational, 0,
189                                 std::nullopt, std::nullopt, std::nullopt,
190                                 std::nullopt});
191     }
192 }
193 
registerPresenceChangeCallback()194 void Listener::registerPresenceChangeCallback() noexcept
195 {
196     try
197     {
198         uint16_t l_errCode = 0;
199         // get list of FRUs for which presence monitoring is required
200         const auto& l_listOfFrus = jsonUtility::getFrusWithPresenceMonitoring(
201             m_worker->getSysCfgJsonObj(), l_errCode);
202 
203         if (l_errCode)
204         {
205             logging::logMessage(
206                 "Failed to get list of FRUs with presence monitoring, error: " +
207                 commonUtility::getErrCodeMsg(l_errCode));
208             return;
209         }
210 
211         for (const auto& l_inventoryPath : l_listOfFrus)
212         {
213             std::shared_ptr<sdbusplus::bus::match_t> l_fruPresenceMatch =
214                 std::make_shared<sdbusplus::bus::match_t>(
215                     *m_asioConnection,
216                     sdbusplus::bus::match::rules::propertiesChanged(
217                         l_inventoryPath, constants::inventoryItemInf),
218                     [this](sdbusplus::message_t& i_msg) {
219                         presentPropertyChangeCallback(i_msg);
220                     });
221 
222             // save the match object to map
223             m_fruPresenceMatchObjectMap[l_inventoryPath] = l_fruPresenceMatch;
224         }
225     }
226     catch (const std::exception& l_ex)
227     {
228         Logger::getLoggerInstance()->logMessage(
229             std::string("Register presence change callback failed, reason: ") +
230                 std::string(l_ex.what()),
231             PlaceHolder::PEL,
232             types::PelInfoTuple{EventLogger::getErrorType(l_ex),
233                                 types::SeverityType::Informational, 0,
234                                 std::nullopt, std::nullopt, std::nullopt,
235                                 std::nullopt});
236     }
237 }
238 
presentPropertyChangeCallback(sdbusplus::message_t & i_msg) const239 void Listener::presentPropertyChangeCallback(
240     sdbusplus::message_t& i_msg) const noexcept
241 {
242     try
243     {
244         if (i_msg.is_method_error())
245         {
246             throw DbusException(
247                 "Error reading callback message for Present property change");
248         }
249 
250         std::string l_interface;
251         types::PropertyMap l_propMap;
252         i_msg.read(l_interface, l_propMap);
253 
254         const std::string l_objectPath{i_msg.get_path()};
255 
256         const auto l_itr = l_propMap.find("Present");
257         if (l_itr == l_propMap.end())
258         {
259             // Present is not found in the callback message
260             return;
261         }
262 
263         if (auto l_present = std::get_if<bool>(&(l_itr->second)))
264         {
265             *l_present ? m_worker->collectSingleFruVpd(l_objectPath)
266                        : m_worker->deleteFruVpd(l_objectPath);
267         }
268         else
269         {
270             throw DbusException(
271                 "Invalid type recieved in variant for present property");
272         }
273     }
274     catch (const std::exception& l_ex)
275     {
276         Logger::getLoggerInstance()->logMessage(
277             std::string("Process presence change callback failed, reason: ") +
278                 std::string(l_ex.what()),
279             PlaceHolder::PEL,
280             types::PelInfoTuple{EventLogger::getErrorType(l_ex),
281                                 types::SeverityType::Informational, 0,
282                                 std::nullopt, std::nullopt, std::nullopt,
283                                 std::nullopt});
284     }
285 }
286 
registerCorrPropCallBack(const std::string & i_correlatedPropJsonFile)287 void Listener::registerCorrPropCallBack(
288     const std::string& i_correlatedPropJsonFile) noexcept
289 {
290     try
291     {
292         uint16_t l_errCode = 0;
293         m_correlatedPropJson =
294             jsonUtility::getParsedJson(i_correlatedPropJsonFile, l_errCode);
295 
296         if (l_errCode)
297         {
298             throw JsonException("Failed to parse correlated properties JSON [" +
299                                     i_correlatedPropJsonFile + "], error : " +
300                                     commonUtility::getErrCodeMsg(l_errCode),
301                                 i_correlatedPropJsonFile);
302         }
303 
304         const nlohmann::json& l_serviceJsonObjectList =
305             m_correlatedPropJson.get_ref<const nlohmann::json::object_t&>();
306 
307         // Iterate through all services in the correlated properties json
308         for (const auto& l_serviceJsonObject : l_serviceJsonObjectList.items())
309         {
310             const auto& l_serviceName = l_serviceJsonObject.key();
311 
312             const nlohmann::json& l_correlatedIntfJsonObj =
313                 m_correlatedPropJson[l_serviceName]
314                     .get_ref<const nlohmann::json::object_t&>();
315 
316             // register properties changed D-Bus signal callback
317             // for all interfaces under this service.
318             std::for_each(l_correlatedIntfJsonObj.items().begin(),
319                           l_correlatedIntfJsonObj.items().end(),
320                           [this, &l_serviceName = std::as_const(l_serviceName)](
321                               const auto& i_interfaceJsonObj) {
322                               registerPropChangeCallBack(
323                                   l_serviceName, i_interfaceJsonObj.key(),
324                                   [this](sdbusplus::message_t& i_msg) {
325                                       correlatedPropChangedCallBack(i_msg);
326                                   });
327                           });
328         } // service loop
329     }
330     catch (const std::exception& l_ex)
331     {
332         Logger::getLoggerInstance()->logMessage(
333             EventLogger::getErrorMsg(l_ex), PlaceHolder::PEL,
334             types::PelInfoTuple{EventLogger::getErrorType(l_ex),
335                                 types::SeverityType::Informational, 0,
336                                 std::nullopt, std::nullopt, std::nullopt,
337                                 std::nullopt});
338     }
339 }
340 
registerPropChangeCallBack(const std::string & i_service,const std::string & i_interface,std::function<void (sdbusplus::message_t & i_msg)> i_callBackFunction)341 void Listener::registerPropChangeCallBack(
342     const std::string& i_service, const std::string& i_interface,
343     std::function<void(sdbusplus::message_t& i_msg)> i_callBackFunction)
344 {
345     try
346     {
347         if (i_service.empty() || i_interface.empty())
348         {
349             throw std::runtime_error("Invalid service name or interface name");
350         }
351 
352         std::shared_ptr<sdbusplus::bus::match_t> l_matchObj =
353             std::make_unique<sdbusplus::bus::match_t>(
354                 static_cast<sdbusplus::bus_t&>(*m_asioConnection),
355                 "type='signal',member='PropertiesChanged',"
356                 "interface='org.freedesktop.DBus.Properties',"
357                 "arg0='" +
358                     i_interface + "'",
359                 i_callBackFunction);
360 
361         // save the match object in map
362         m_matchObjectMap[i_service][i_interface] = l_matchObj;
363     }
364     catch (const std::exception& l_ex)
365     {
366         throw FirmwareException(l_ex.what());
367     }
368 }
369 
correlatedPropChangedCallBack(sdbusplus::message_t & i_msg)370 void Listener::correlatedPropChangedCallBack(
371     sdbusplus::message_t& i_msg) noexcept
372 {
373     try
374     {
375         if (i_msg.is_method_error())
376         {
377             throw DbusException("Error in reading property change signal.");
378         }
379 
380         std::string l_interface;
381         types::PropertyMap l_propMap;
382         i_msg.read(l_interface, l_propMap);
383 
384         const std::string l_objectPath{i_msg.get_path()};
385 
386         std::string l_serviceName =
387             dbusUtility::getServiceNameFromConnectionId(i_msg.get_sender());
388 
389         if (l_serviceName.empty())
390         {
391             throw DbusException(
392                 "Failed to get service name from connection ID: " +
393                 std::string(i_msg.get_sender()));
394         }
395 
396         // if service name contains .service suffix, strip it
397         const std::size_t l_pos = l_serviceName.find(".service");
398         if (l_pos != std::string::npos)
399         {
400             l_serviceName = l_serviceName.substr(0, l_pos);
401         }
402 
403         // iterate through all properties in map
404         for (const auto& l_propertyEntry : l_propMap)
405         {
406             const std::string& l_propertyName = l_propertyEntry.first;
407             const auto& l_propertyValue = l_propertyEntry.second;
408 
409             // Use correlated JSON to find target {object path,
410             // interface,property/properties} to update
411             const auto& l_correlatedPropList = getCorrelatedProps(
412                 l_serviceName, l_objectPath, l_interface, l_propertyName);
413 
414             // update all target correlated properties
415             std::for_each(
416                 l_correlatedPropList.begin(), l_correlatedPropList.end(),
417                 [this, &l_propertyValue = std::as_const(l_propertyValue),
418                  &l_serviceName = std::as_const(l_serviceName),
419                  &l_objectPath = std::as_const(l_objectPath),
420                  &l_interface = std::as_const(l_interface),
421                  &l_propertyName = std::as_const(l_propertyName)](
422                     const auto& i_corrProperty) {
423                     if (!updateCorrelatedProperty(l_serviceName, i_corrProperty,
424                                                   l_propertyValue))
425                     {
426                         logging::logMessage(
427                             "Failed to update correlated property: " +
428                             l_serviceName + " : " +
429                             std::get<0>(i_corrProperty) + " : " +
430                             std::get<1>(i_corrProperty) + " : " +
431                             std::get<2>(i_corrProperty) + " when " +
432                             l_objectPath + " : " + l_interface + " : " +
433                             l_propertyName + " got updated.");
434                     }
435                 });
436         }
437     }
438     catch (const std::exception& l_ex)
439     {
440         Logger::getLoggerInstance()->logMessage(
441             EventLogger::getErrorMsg(l_ex), PlaceHolder::PEL,
442             types::PelInfoTuple{EventLogger::getErrorType(l_ex),
443                                 types::SeverityType::Informational, 0,
444                                 std::nullopt, std::nullopt, std::nullopt,
445                                 std::nullopt});
446     }
447 }
448 
getCorrelatedProps(const std::string & i_serviceName,const std::string & i_objectPath,const std::string & i_interface,const std::string & i_property) const449 types::DbusPropertyList Listener::getCorrelatedProps(
450     const std::string& i_serviceName, const std::string& i_objectPath,
451     const std::string& i_interface, const std::string& i_property) const
452 {
453     types::DbusPropertyList l_result;
454     try
455     {
456         if (m_correlatedPropJson.contains(i_serviceName) &&
457             m_correlatedPropJson[i_serviceName].contains(i_interface) &&
458             m_correlatedPropJson[i_serviceName][i_interface].contains(
459                 i_property))
460         {
461             const nlohmann::json& l_destinationJsonObj =
462                 m_correlatedPropJson[i_serviceName][i_interface][i_property];
463 
464             // check if any matching paths pair entry is present
465             if (l_destinationJsonObj.contains("pathsPair") &&
466                 l_destinationJsonObj["pathsPair"].contains(i_objectPath) &&
467                 l_destinationJsonObj["pathsPair"][i_objectPath].contains(
468                     "destinationInventoryPath") &&
469                 l_destinationJsonObj["pathsPair"][i_objectPath].contains(
470                     "interfaces"))
471             {
472                 // iterate through all the destination interface and property
473                 // name
474                 for (const auto& l_destinationInterfaceJsonObj :
475                      l_destinationJsonObj["pathsPair"][i_objectPath]
476                                          ["interfaces"]
477                                              .items())
478                 {
479                     // iterate through all destination inventory paths
480                     for (const auto& l_destinationInventoryPath :
481                          l_destinationJsonObj["pathsPair"][i_objectPath]
482                                              ["destinationInventoryPath"])
483                     {
484                         l_result.emplace_back(
485                             l_destinationInventoryPath,
486                             l_destinationInterfaceJsonObj.key(),
487                             l_destinationInterfaceJsonObj.value());
488                     } // destination inventory paths
489                 } // destination interfaces
490             }
491             // get the default interface, property to update
492             else if (l_destinationJsonObj.contains("defaultInterfaces"))
493             {
494                 // iterate through all default interfaces to update
495                 for (const auto& l_destinationIfcPropEntry :
496                      l_destinationJsonObj["defaultInterfaces"].items())
497                 {
498                     l_result.emplace_back(i_objectPath,
499                                           l_destinationIfcPropEntry.key(),
500                                           l_destinationIfcPropEntry.value());
501                 }
502             }
503         }
504     }
505     catch (const std::exception& l_ex)
506     {
507         throw FirmwareException(l_ex.what());
508     }
509     return l_result;
510 }
511 
updateCorrelatedProperty(const std::string & i_serviceName,const types::DbusPropertyEntry & i_corrProperty,const types::DbusVariantType & i_propertyValue) const512 bool Listener::updateCorrelatedProperty(
513     const std::string& i_serviceName,
514     const types::DbusPropertyEntry& i_corrProperty,
515     const types::DbusVariantType& i_propertyValue) const noexcept
516 {
517     const auto& l_destinationObjectPath{std::get<0>(i_corrProperty)};
518     const auto& l_destinationInterface{std::get<1>(i_corrProperty)};
519     const auto& l_destinationPropertyName{std::get<2>(i_corrProperty)};
520 
521     try
522     {
523         types::DbusVariantType l_valueToUpdate;
524 
525         // destination interface is ipz vpd
526         if (l_destinationInterface.find(constants::ipzVpdInf) !=
527             std::string::npos)
528         {
529             uint16_t l_errCode = 0;
530             if (const auto l_val = std::get_if<std::string>(&i_propertyValue))
531             {
532                 // convert value to binary vector before updating
533                 l_valueToUpdate =
534                     commonUtility::convertToBinary(*l_val, l_errCode);
535 
536                 if (l_errCode)
537                 {
538                     throw std::runtime_error(
539                         "Failed to get value [" + std::string(*l_val) +
540                         "] in binary vector, error : " +
541                         commonUtility::getErrCodeMsg(l_errCode));
542                 }
543             }
544             else if (const auto l_val =
545                          std::get_if<types::BinaryVector>(&i_propertyValue))
546             {
547                 l_valueToUpdate = *l_val;
548             }
549         }
550         else
551         {
552             // destination interface is not ipz vpd, assume target
553             // property type is of string type
554             if (const auto l_val =
555                     std::get_if<types::BinaryVector>(&i_propertyValue))
556             {
557                 // convert property value to string before updating
558                 uint16_t l_errCode = 0;
559                 l_valueToUpdate =
560                     commonUtility::getPrintableValue(*l_val, l_errCode);
561 
562                 if (l_errCode)
563                 {
564                     throw std::runtime_error(
565                         "Failed to get binary value in string, error : " +
566                         commonUtility::getErrCodeMsg(l_errCode));
567                 }
568             }
569             else if (const auto l_val =
570                          std::get_if<std::string>(&i_propertyValue))
571             {
572                 l_valueToUpdate = *l_val;
573             }
574         }
575 
576         if (i_serviceName == constants::pimServiceName)
577         {
578             // Call dbus method to update on dbus
579             return dbusUtility::publishVpdOnDBus(types::ObjectMap{
580                 {l_destinationObjectPath,
581                  {{l_destinationInterface,
582                    {{l_destinationPropertyName, l_valueToUpdate}}}}}});
583         }
584         else
585         {
586             return dbusUtility::writeDbusProperty(
587                 i_serviceName, l_destinationObjectPath, l_destinationInterface,
588                 l_destinationPropertyName, l_valueToUpdate);
589         }
590     }
591     catch (const std::exception& l_ex)
592     {
593         std::string l_msg =
594             "Failed to update correlated property: " + i_serviceName + " : " +
595             l_destinationObjectPath + " : " + l_destinationInterface + " : " +
596             l_destinationPropertyName + ". Error: " + std::string(l_ex.what());
597 
598         logging::logMessage(l_msg);
599 
600         Logger::getLoggerInstance()->logMessage(
601             l_msg, PlaceHolder::PEL,
602             types::PelInfoTuple{EventLogger::getErrorType(l_ex),
603                                 types::SeverityType::Informational, 0,
604                                 std::nullopt, std::nullopt, std::nullopt,
605                                 std::nullopt});
606     }
607     return false;
608 }
609 
610 } // namespace vpd
611