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