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