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