xref: /openbmc/openpower-vpd-parser/vpd-manager/src/listener.cpp (revision e621c063f1f293f510d5e10ee63f33c1ea27d70e)
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 {
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 
20 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 
44 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 
104 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 
128 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 
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                 vpdSpecificUtility::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 
221 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 
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(
279                 "Failed to parse correlated properties JSON [" +
280                     i_correlatedPropJsonFile + "], error : " +
281                     vpdSpecificUtility::getErrCodeMsg(l_errCode),
282                 i_correlatedPropJsonFile);
283         }
284 
285         const nlohmann::json& l_serviceJsonObjectList =
286             m_correlatedPropJson.get_ref<const nlohmann::json::object_t&>();
287 
288         // Iterate through all services in the correlated properties json
289         for (const auto& l_serviceJsonObject : l_serviceJsonObjectList.items())
290         {
291             const auto& l_serviceName = l_serviceJsonObject.key();
292 
293             const nlohmann::json& l_correlatedIntfJsonObj =
294                 m_correlatedPropJson[l_serviceName]
295                     .get_ref<const nlohmann::json::object_t&>();
296 
297             // register properties changed D-Bus signal callback
298             // for all interfaces under this service.
299             std::for_each(l_correlatedIntfJsonObj.items().begin(),
300                           l_correlatedIntfJsonObj.items().end(),
301                           [this, &l_serviceName = std::as_const(l_serviceName)](
302                               const auto& i_interfaceJsonObj) {
303                               registerPropChangeCallBack(
304                                   l_serviceName, i_interfaceJsonObj.key(),
305                                   [this](sdbusplus::message_t& i_msg) {
306                                       correlatedPropChangedCallBack(i_msg);
307                                   });
308                           });
309         } // service loop
310     }
311     catch (const std::exception& l_ex)
312     {
313         EventLogger::createSyncPel(
314             EventLogger::getErrorType(l_ex), types::SeverityType::Informational,
315             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
316             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
317     }
318 }
319 
320 void Listener::registerPropChangeCallBack(
321     const std::string& i_service, const std::string& i_interface,
322     std::function<void(sdbusplus::message_t& i_msg)> i_callBackFunction)
323 {
324     try
325     {
326         if (i_service.empty() || i_interface.empty())
327         {
328             throw std::runtime_error("Invalid service name or interface name");
329         }
330 
331         std::shared_ptr<sdbusplus::bus::match_t> l_matchObj =
332             std::make_unique<sdbusplus::bus::match_t>(
333                 static_cast<sdbusplus::bus_t&>(*m_asioConnection),
334                 "type='signal',member='PropertiesChanged',"
335                 "interface='org.freedesktop.DBus.Properties',"
336                 "arg0='" +
337                     i_interface + "'",
338                 i_callBackFunction);
339 
340         // save the match object in map
341         m_matchObjectMap[i_service][i_interface] = l_matchObj;
342     }
343     catch (const std::exception& l_ex)
344     {
345         throw FirmwareException(l_ex.what());
346     }
347 }
348 
349 void Listener::correlatedPropChangedCallBack(
350     sdbusplus::message_t& i_msg) noexcept
351 {
352     try
353     {
354         if (i_msg.is_method_error())
355         {
356             throw DbusException("Error in reading property change signal.");
357         }
358 
359         std::string l_interface;
360         types::PropertyMap l_propMap;
361         i_msg.read(l_interface, l_propMap);
362 
363         const std::string l_objectPath{i_msg.get_path()};
364 
365         std::string l_serviceName =
366             dbusUtility::getServiceNameFromConnectionId(i_msg.get_sender());
367 
368         if (l_serviceName.empty())
369         {
370             throw DbusException(
371                 "Failed to get service name from connection ID: " +
372                 std::string(i_msg.get_sender()));
373         }
374 
375         // if service name contains .service suffix, strip it
376         const std::size_t l_pos = l_serviceName.find(".service");
377         if (l_pos != std::string::npos)
378         {
379             l_serviceName = l_serviceName.substr(0, l_pos);
380         }
381 
382         // iterate through all properties in map
383         for (const auto& l_propertyEntry : l_propMap)
384         {
385             const std::string& l_propertyName = l_propertyEntry.first;
386             const auto& l_propertyValue = l_propertyEntry.second;
387 
388             // Use correlated JSON to find target {object path,
389             // interface,property/properties} to update
390             const auto& l_correlatedPropList = getCorrelatedProps(
391                 l_serviceName, l_objectPath, l_interface, l_propertyName);
392 
393             // update all target correlated properties
394             std::for_each(
395                 l_correlatedPropList.begin(), l_correlatedPropList.end(),
396                 [this, &l_propertyValue = std::as_const(l_propertyValue),
397                  &l_serviceName = std::as_const(l_serviceName),
398                  &l_objectPath = std::as_const(l_objectPath),
399                  &l_interface = std::as_const(l_interface),
400                  &l_propertyName = std::as_const(l_propertyName)](
401                     const auto& i_corrProperty) {
402                     if (!updateCorrelatedProperty(l_serviceName, i_corrProperty,
403                                                   l_propertyValue))
404                     {
405                         logging::logMessage(
406                             "Failed to update correlated property: " +
407                             l_serviceName + " : " +
408                             std::get<0>(i_corrProperty) + " : " +
409                             std::get<1>(i_corrProperty) + " : " +
410                             std::get<2>(i_corrProperty) + " when " +
411                             l_objectPath + " : " + l_interface + " : " +
412                             l_propertyName + " got updated.");
413                     }
414                 });
415         }
416     }
417     catch (const std::exception& l_ex)
418     {
419         EventLogger::createSyncPel(
420             EventLogger::getErrorType(l_ex), types::SeverityType::Informational,
421             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
422             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
423     }
424 }
425 
426 types::DbusPropertyList Listener::getCorrelatedProps(
427     const std::string& i_serviceName, const std::string& i_objectPath,
428     const std::string& i_interface, const std::string& i_property) const
429 {
430     types::DbusPropertyList l_result;
431     try
432     {
433         if (m_correlatedPropJson.contains(i_serviceName) &&
434             m_correlatedPropJson[i_serviceName].contains(i_interface) &&
435             m_correlatedPropJson[i_serviceName][i_interface].contains(
436                 i_property))
437         {
438             const nlohmann::json& l_destinationJsonObj =
439                 m_correlatedPropJson[i_serviceName][i_interface][i_property];
440 
441             // check if any matching paths pair entry is present
442             if (l_destinationJsonObj.contains("pathsPair") &&
443                 l_destinationJsonObj["pathsPair"].contains(i_objectPath) &&
444                 l_destinationJsonObj["pathsPair"][i_objectPath].contains(
445                     "destinationInventoryPath") &&
446                 l_destinationJsonObj["pathsPair"][i_objectPath].contains(
447                     "interfaces"))
448             {
449                 // iterate through all the destination interface and property
450                 // name
451                 for (const auto& l_destinationInterfaceJsonObj :
452                      l_destinationJsonObj["pathsPair"][i_objectPath]
453                                          ["interfaces"]
454                                              .items())
455                 {
456                     // iterate through all destination inventory paths
457                     for (const auto& l_destinationInventoryPath :
458                          l_destinationJsonObj["pathsPair"][i_objectPath]
459                                              ["destinationInventoryPath"])
460                     {
461                         l_result.emplace_back(
462                             l_destinationInventoryPath,
463                             l_destinationInterfaceJsonObj.key(),
464                             l_destinationInterfaceJsonObj.value());
465                     } // destination inventory paths
466                 } // destination interfaces
467             }
468             // get the default interface, property to update
469             else if (l_destinationJsonObj.contains("defaultInterfaces"))
470             {
471                 // iterate through all default interfaces to update
472                 for (const auto& l_destinationIfcPropEntry :
473                      l_destinationJsonObj["defaultInterfaces"].items())
474                 {
475                     l_result.emplace_back(i_objectPath,
476                                           l_destinationIfcPropEntry.key(),
477                                           l_destinationIfcPropEntry.value());
478                 }
479             }
480         }
481     }
482     catch (const std::exception& l_ex)
483     {
484         throw FirmwareException(l_ex.what());
485     }
486     return l_result;
487 }
488 
489 bool Listener::updateCorrelatedProperty(
490     const std::string& i_serviceName,
491     const types::DbusPropertyEntry& i_corrProperty,
492     const types::DbusVariantType& i_propertyValue) const noexcept
493 {
494     const auto& l_destinationObjectPath{std::get<0>(i_corrProperty)};
495     const auto& l_destinationInterface{std::get<1>(i_corrProperty)};
496     const auto& l_destinationPropertyName{std::get<2>(i_corrProperty)};
497 
498     try
499     {
500         types::DbusVariantType l_valueToUpdate;
501 
502         // destination interface is ipz vpd
503         if (l_destinationInterface.find(constants::ipzVpdInf) !=
504             std::string::npos)
505         {
506             if (const auto l_val = std::get_if<std::string>(&i_propertyValue))
507             {
508                 // convert value to binary vector before updating
509                 l_valueToUpdate = commonUtility::convertToBinary(*l_val);
510             }
511             else if (const auto l_val =
512                          std::get_if<types::BinaryVector>(&i_propertyValue))
513             {
514                 l_valueToUpdate = *l_val;
515             }
516         }
517         else
518         {
519             // destination interface is not ipz vpd, assume target
520             // property type is of string type
521             if (const auto l_val =
522                     std::get_if<types::BinaryVector>(&i_propertyValue))
523             {
524                 // convert property value to string before updating
525 
526                 l_valueToUpdate = commonUtility::getPrintableValue(*l_val);
527             }
528             else if (const auto l_val =
529                          std::get_if<std::string>(&i_propertyValue))
530             {
531                 l_valueToUpdate = *l_val;
532             }
533         }
534 
535         if (i_serviceName == constants::pimServiceName)
536         {
537             return dbusUtility::callPIM(types::ObjectMap{
538                 {l_destinationObjectPath,
539                  {{l_destinationInterface,
540                    {{l_destinationPropertyName, l_valueToUpdate}}}}}});
541         }
542         else
543         {
544             return dbusUtility::writeDbusProperty(
545                 i_serviceName, l_destinationObjectPath, l_destinationInterface,
546                 l_destinationPropertyName, l_valueToUpdate);
547         }
548     }
549     catch (const std::exception& l_ex)
550     {
551         logging::logMessage(
552             "Failed to update correlated property: " + i_serviceName + " : " +
553             l_destinationObjectPath + " : " + l_destinationInterface + " : " +
554             l_destinationPropertyName + ". Error: " + std::string(l_ex.what()));
555     }
556     return false;
557 }
558 
559 } // namespace vpd
560