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