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