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