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