1 #pragma once 2 #include "async_resp.hpp" 3 #include "dbus_utility.hpp" 4 #include "error_messages.hpp" 5 #include "generated/enums/resource.hpp" 6 #include "utils/dbus_utils.hpp" 7 8 #include <boost/system/error_code.hpp> 9 #include <sdbusplus/asio/property.hpp> 10 #include <sdbusplus/unpack_properties.hpp> 11 12 #include <algorithm> 13 #include <array> 14 #include <string> 15 #include <string_view> 16 #include <vector> 17 18 namespace redfish 19 { 20 namespace sw_util 21 { 22 /* @brief String that indicates a bios software instance */ 23 constexpr const char* biosPurpose = 24 "xyz.openbmc_project.Software.Version.VersionPurpose.Host"; 25 26 /* @brief String that indicates a BMC software instance */ 27 constexpr const char* bmcPurpose = 28 "xyz.openbmc_project.Software.Version.VersionPurpose.BMC"; 29 30 /** 31 * @brief Populate the running software version and image links 32 * 33 * @param[i,o] aResp Async response object 34 * @param[i] swVersionPurpose Indicates what target to look for 35 * @param[i] activeVersionPropName Index in aResp->res.jsonValue to write 36 * the running software version to 37 * @param[i] populateLinkToImages Populate aResp->res "Links" 38 * "ActiveSoftwareImage" with a link to the running software image and 39 * "SoftwareImages" with a link to the all its software images 40 * 41 * @return void 42 */ 43 inline void 44 populateSoftwareInformation(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 45 const std::string& swVersionPurpose, 46 const std::string& activeVersionPropName, 47 const bool populateLinkToImages) 48 { 49 // Used later to determine running (known on Redfish as active) Sw images 50 sdbusplus::asio::getProperty<std::vector<std::string>>( 51 *crow::connections::systemBus, "xyz.openbmc_project.ObjectMapper", 52 "/xyz/openbmc_project/software/functional", 53 "xyz.openbmc_project.Association", "endpoints", 54 [aResp, swVersionPurpose, activeVersionPropName, 55 populateLinkToImages](const boost::system::error_code ec, 56 const std::vector<std::string>& functionalSw) { 57 BMCWEB_LOG_DEBUG << "populateSoftwareInformation enter"; 58 if (ec) 59 { 60 BMCWEB_LOG_ERROR << "error_code = " << ec; 61 BMCWEB_LOG_ERROR << "error msg = " << ec.message(); 62 messages::internalError(aResp->res); 63 return; 64 } 65 66 if (functionalSw.empty()) 67 { 68 // Could keep going and try to populate SoftwareImages but 69 // something is seriously wrong, so just fail 70 BMCWEB_LOG_ERROR << "Zero functional software in system"; 71 messages::internalError(aResp->res); 72 return; 73 } 74 75 std::vector<std::string> functionalSwIds; 76 // example functionalSw: 77 // v as 2 "/xyz/openbmc_project/software/ace821ef" 78 // "/xyz/openbmc_project/software/230fb078" 79 for (const auto& sw : functionalSw) 80 { 81 sdbusplus::message::object_path path(sw); 82 std::string leaf = path.filename(); 83 if (leaf.empty()) 84 { 85 continue; 86 } 87 88 functionalSwIds.push_back(leaf); 89 } 90 91 constexpr std::array<std::string_view, 1> interfaces = { 92 "xyz.openbmc_project.Software.Version"}; 93 dbus::utility::getSubTree( 94 "/xyz/openbmc_project/software", 0, interfaces, 95 [aResp, swVersionPurpose, activeVersionPropName, 96 populateLinkToImages, functionalSwIds]( 97 const boost::system::error_code& ec2, 98 const dbus::utility::MapperGetSubTreeResponse& subtree) { 99 if (ec2) 100 { 101 BMCWEB_LOG_ERROR << "error_code = " << ec2; 102 BMCWEB_LOG_ERROR << "error msg = " << ec2.message(); 103 messages::internalError(aResp->res); 104 return; 105 } 106 107 BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " images"; 108 109 for (const std::pair<std::string, 110 std::vector<std::pair< 111 std::string, std::vector<std::string>>>>& 112 obj : subtree) 113 { 114 115 sdbusplus::message::object_path path(obj.first); 116 std::string swId = path.filename(); 117 if (swId.empty()) 118 { 119 messages::internalError(aResp->res); 120 BMCWEB_LOG_ERROR << "Invalid software ID"; 121 122 return; 123 } 124 125 bool runningImage = false; 126 // Look at Ids from 127 // /xyz/openbmc_project/software/functional 128 // to determine if this is a running image 129 if (std::find(functionalSwIds.begin(), functionalSwIds.end(), 130 swId) != functionalSwIds.end()) 131 { 132 runningImage = true; 133 } 134 135 // Now grab its version info 136 sdbusplus::asio::getAllProperties( 137 *crow::connections::systemBus, obj.second[0].first, 138 obj.first, "xyz.openbmc_project.Software.Version", 139 [aResp, swId, runningImage, swVersionPurpose, 140 activeVersionPropName, populateLinkToImages]( 141 const boost::system::error_code ec3, 142 const dbus::utility::DBusPropertiesMap& 143 propertiesList) { 144 if (ec3) 145 { 146 BMCWEB_LOG_ERROR << "error_code = " << ec3; 147 BMCWEB_LOG_ERROR << "error msg = " << ec3.message(); 148 // Have seen the code update app delete the D-Bus 149 // object, during code update, between the call to 150 // mapper and here. Just leave these properties off if 151 // resource not found. 152 if (ec3.value() == EBADR) 153 { 154 return; 155 } 156 messages::internalError(aResp->res); 157 return; 158 } 159 // example propertiesList 160 // a{sv} 2 "Version" s 161 // "IBM-witherspoon-OP9-v2.0.10-2.22" "Purpose" 162 // s 163 // "xyz.openbmc_project.Software.Version.VersionPurpose.Host" 164 const std::string* version = nullptr; 165 const std::string* swInvPurpose = nullptr; 166 167 const bool success = sdbusplus::unpackPropertiesNoThrow( 168 dbus_utils::UnpackErrorPrinter(), propertiesList, 169 "Purpose", swInvPurpose, "Version", version); 170 171 if (!success) 172 { 173 messages::internalError(aResp->res); 174 return; 175 } 176 177 if (version == nullptr || version->empty()) 178 { 179 messages::internalError(aResp->res); 180 return; 181 } 182 if (swInvPurpose == nullptr || 183 *swInvPurpose != swVersionPurpose) 184 { 185 // Not purpose we're looking for 186 return; 187 } 188 189 BMCWEB_LOG_DEBUG << "Image ID: " << swId; 190 BMCWEB_LOG_DEBUG << "Running image: " << runningImage; 191 BMCWEB_LOG_DEBUG << "Image purpose: " << *swInvPurpose; 192 193 if (populateLinkToImages) 194 { 195 nlohmann::json& softwareImageMembers = 196 aResp->res.jsonValue["Links"]["SoftwareImages"]; 197 // Firmware images are at 198 // /redfish/v1/UpdateService/FirmwareInventory/<Id> 199 // e.g. .../FirmwareInventory/82d3ec86 200 nlohmann::json::object_t member; 201 member["@odata.id"] = "/redfish/v1/UpdateService/" 202 "FirmwareInventory/" + 203 swId; 204 softwareImageMembers.push_back(std::move(member)); 205 aResp->res 206 .jsonValue["Links"]["SoftwareImages@odata.count"] = 207 softwareImageMembers.size(); 208 209 if (runningImage) 210 { 211 nlohmann::json::object_t runningMember; 212 runningMember["@odata.id"] = 213 "/redfish/v1/UpdateService/" 214 "FirmwareInventory/" + 215 swId; 216 // Create the link to the running image 217 aResp->res 218 .jsonValue["Links"]["ActiveSoftwareImage"] = 219 std::move(runningMember); 220 } 221 } 222 if (!activeVersionPropName.empty() && runningImage) 223 { 224 aResp->res.jsonValue[activeVersionPropName] = *version; 225 } 226 }); 227 } 228 }); 229 }); 230 } 231 232 /** 233 * @brief Translate input swState to Redfish state 234 * 235 * This function will return the corresponding Redfish state 236 * 237 * @param[i] swState The OpenBMC software state 238 * 239 * @return The corresponding Redfish state 240 */ 241 inline resource::State getRedfishSwState(const std::string& swState) 242 { 243 if (swState == "xyz.openbmc_project.Software.Activation.Activations.Active") 244 { 245 return resource::State::Enabled; 246 } 247 if (swState == "xyz.openbmc_project.Software.Activation." 248 "Activations.Activating") 249 { 250 return resource::State::Updating; 251 } 252 if (swState == "xyz.openbmc_project.Software.Activation." 253 "Activations.StandbySpare") 254 { 255 return resource::State::StandbySpare; 256 } 257 BMCWEB_LOG_DEBUG << "Default sw state " << swState << " to Disabled"; 258 return resource::State::Disabled; 259 } 260 261 /** 262 * @brief Translate input swState to Redfish health state 263 * 264 * This function will return the corresponding Redfish health state 265 * 266 * @param[i] swState The OpenBMC software state 267 * 268 * @return The corresponding Redfish health state 269 */ 270 inline std::string getRedfishSwHealth(const std::string& swState) 271 { 272 if ((swState == 273 "xyz.openbmc_project.Software.Activation.Activations.Active") || 274 (swState == "xyz.openbmc_project.Software.Activation.Activations." 275 "Activating") || 276 (swState == 277 "xyz.openbmc_project.Software.Activation.Activations.Ready")) 278 { 279 return "OK"; 280 } 281 BMCWEB_LOG_DEBUG << "Sw state " << swState << " to Warning"; 282 return "Warning"; 283 } 284 285 /** 286 * @brief Put status of input swId into json response 287 * 288 * This function will put the appropriate Redfish state of the input 289 * software id to ["Status"]["State"] within the json response 290 * 291 * @param[i,o] aResp Async response object 292 * @param[i] swId The software ID to get status for 293 * @param[i] dbusSvc The dbus service implementing the software object 294 * 295 * @return void 296 */ 297 inline void getSwStatus(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 298 const std::shared_ptr<std::string>& swId, 299 const std::string& dbusSvc) 300 { 301 BMCWEB_LOG_DEBUG << "getSwStatus: swId " << *swId << " svc " << dbusSvc; 302 303 sdbusplus::asio::getAllProperties( 304 *crow::connections::systemBus, dbusSvc, 305 "/xyz/openbmc_project/software/" + *swId, 306 "xyz.openbmc_project.Software.Activation", 307 [asyncResp, 308 swId](const boost::system::error_code errorCode, 309 const dbus::utility::DBusPropertiesMap& propertiesList) { 310 if (errorCode) 311 { 312 // not all swtypes are updateable, this is ok 313 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 314 return; 315 } 316 317 const std::string* swInvActivation = nullptr; 318 319 const bool success = sdbusplus::unpackPropertiesNoThrow( 320 dbus_utils::UnpackErrorPrinter(), propertiesList, "Activation", 321 swInvActivation); 322 323 if (!success) 324 { 325 messages::internalError(asyncResp->res); 326 return; 327 } 328 329 if (swInvActivation == nullptr) 330 { 331 messages::internalError(asyncResp->res); 332 return; 333 } 334 335 BMCWEB_LOG_DEBUG << "getSwStatus: Activation " << *swInvActivation; 336 asyncResp->res.jsonValue["Status"]["State"] = 337 getRedfishSwState(*swInvActivation); 338 asyncResp->res.jsonValue["Status"]["Health"] = 339 getRedfishSwHealth(*swInvActivation); 340 }); 341 } 342 343 /** 344 * @brief Updates programmable status of input swId into json response 345 * 346 * This function checks whether software inventory component 347 * can be programmable or not and fill's the "Updatable" 348 * Property. 349 * 350 * @param[i,o] asyncResp Async response object 351 * @param[i] swId The software ID 352 */ 353 inline void 354 getSwUpdatableStatus(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 355 const std::shared_ptr<std::string>& swId) 356 { 357 sdbusplus::asio::getProperty<std::vector<std::string>>( 358 *crow::connections::systemBus, "xyz.openbmc_project.ObjectMapper", 359 "/xyz/openbmc_project/software/updateable", 360 "xyz.openbmc_project.Association", "endpoints", 361 [asyncResp, swId](const boost::system::error_code ec, 362 const std::vector<std::string>& objPaths) { 363 if (ec) 364 { 365 BMCWEB_LOG_DEBUG << " error_code = " << ec 366 << " error msg = " << ec.message(); 367 // System can exist with no updateable software, 368 // so don't throw error here. 369 return; 370 } 371 std::string reqSwObjPath = "/xyz/openbmc_project/software/" + *swId; 372 373 if (std::find(objPaths.begin(), objPaths.end(), reqSwObjPath) != 374 objPaths.end()) 375 { 376 asyncResp->res.jsonValue["Updateable"] = true; 377 return; 378 } 379 }); 380 } 381 382 } // namespace sw_util 383 } // namespace redfish 384