1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "node.hpp" 19 20 #include <boost/container/flat_map.hpp> 21 #include <utils/fw_utils.hpp> 22 #include <variant> 23 24 namespace redfish 25 { 26 27 // Match signals added on software path 28 static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher; 29 // Only allow one update at a time 30 static bool fwUpdateInProgress = false; 31 // Timer for software available 32 static std::unique_ptr<boost::asio::deadline_timer> fwAvailableTimer; 33 34 static void cleanUp() 35 { 36 fwUpdateInProgress = false; 37 fwUpdateMatcher = nullptr; 38 } 39 static void activateImage(const std::string &objPath, 40 const std::string &service) 41 { 42 BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service; 43 crow::connections::systemBus->async_method_call( 44 [](const boost::system::error_code error_code) { 45 if (error_code) 46 { 47 BMCWEB_LOG_DEBUG << "error_code = " << error_code; 48 BMCWEB_LOG_DEBUG << "error msg = " << error_code.message(); 49 } 50 }, 51 service, objPath, "org.freedesktop.DBus.Properties", "Set", 52 "xyz.openbmc_project.Software.Activation", "RequestedActivation", 53 std::variant<std::string>( 54 "xyz.openbmc_project.Software.Activation.RequestedActivations." 55 "Active")); 56 } 57 58 // Note that asyncResp can be either a valid pointer or nullptr. If nullptr 59 // then no asyncResp updates will occur 60 static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp, 61 sdbusplus::message::message &m) 62 { 63 std::vector<std::pair< 64 std::string, 65 std::vector<std::pair<std::string, std::variant<std::string>>>>> 66 interfacesProperties; 67 68 sdbusplus::message::object_path objPath; 69 70 m.read(objPath, interfacesProperties); 71 72 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str; 73 for (auto &interface : interfacesProperties) 74 { 75 BMCWEB_LOG_DEBUG << "interface = " << interface.first; 76 77 if (interface.first == "xyz.openbmc_project.Software.Activation") 78 { 79 // Found our interface, disable callbacks 80 fwUpdateMatcher = nullptr; 81 82 // Retrieve service and activate 83 crow::connections::systemBus->async_method_call( 84 [objPath, asyncResp]( 85 const boost::system::error_code error_code, 86 const std::vector<std::pair< 87 std::string, std::vector<std::string>>> &objInfo) { 88 if (error_code) 89 { 90 BMCWEB_LOG_DEBUG << "error_code = " << error_code; 91 BMCWEB_LOG_DEBUG << "error msg = " 92 << error_code.message(); 93 if (asyncResp) 94 { 95 messages::internalError(asyncResp->res); 96 } 97 cleanUp(); 98 return; 99 } 100 // Ensure we only got one service back 101 if (objInfo.size() != 1) 102 { 103 BMCWEB_LOG_ERROR << "Invalid Object Size " 104 << objInfo.size(); 105 if (asyncResp) 106 { 107 messages::internalError(asyncResp->res); 108 } 109 cleanUp(); 110 return; 111 } 112 // cancel timer only when 113 // xyz.openbmc_project.Software.Activation interface 114 // is added 115 fwAvailableTimer = nullptr; 116 117 activateImage(objPath.str, objInfo[0].first); 118 if (asyncResp) 119 { 120 redfish::messages::success(asyncResp->res); 121 } 122 fwUpdateInProgress = false; 123 }, 124 "xyz.openbmc_project.ObjectMapper", 125 "/xyz/openbmc_project/object_mapper", 126 "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str, 127 std::array<const char *, 1>{ 128 "xyz.openbmc_project.Software.Activation"}); 129 } 130 } 131 } 132 133 // Note that asyncResp can be either a valid pointer or nullptr. If nullptr 134 // then no asyncResp updates will occur 135 static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp, 136 const crow::Request &req, 137 int timeoutTimeSeconds = 5) 138 { 139 // Only allow one FW update at a time 140 if (fwUpdateInProgress != false) 141 { 142 if (asyncResp) 143 { 144 asyncResp->res.addHeader("Retry-After", "30"); 145 messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 146 } 147 return; 148 } 149 150 fwAvailableTimer = 151 std::make_unique<boost::asio::deadline_timer>(*req.ioService); 152 153 fwAvailableTimer->expires_from_now( 154 boost::posix_time::seconds(timeoutTimeSeconds)); 155 156 fwAvailableTimer->async_wait( 157 [asyncResp](const boost::system::error_code &ec) { 158 cleanUp(); 159 if (ec == boost::asio::error::operation_aborted) 160 { 161 // expected, we were canceled before the timer completed. 162 return; 163 } 164 BMCWEB_LOG_ERROR 165 << "Timed out waiting for firmware object being created"; 166 BMCWEB_LOG_ERROR 167 << "FW image may has already been uploaded to server"; 168 if (ec) 169 { 170 BMCWEB_LOG_ERROR << "Async_wait failed" << ec; 171 return; 172 } 173 if (asyncResp) 174 { 175 redfish::messages::internalError(asyncResp->res); 176 } 177 }); 178 179 auto callback = [asyncResp](sdbusplus::message::message &m) { 180 BMCWEB_LOG_DEBUG << "Match fired"; 181 softwareInterfaceAdded(asyncResp, m); 182 }; 183 184 fwUpdateInProgress = true; 185 186 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>( 187 *crow::connections::systemBus, 188 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 189 "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 190 callback); 191 } 192 193 /** 194 * UpdateServiceActionsSimpleUpdate class supports handle POST method for 195 * SimpleUpdate action. 196 */ 197 class UpdateServiceActionsSimpleUpdate : public Node 198 { 199 public: 200 UpdateServiceActionsSimpleUpdate(CrowApp &app) : 201 Node(app, 202 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/") 203 { 204 entityPrivileges = { 205 {boost::beast::http::verb::get, {{"Login"}}}, 206 {boost::beast::http::verb::head, {{"Login"}}}, 207 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 208 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 209 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 210 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 211 } 212 213 private: 214 void doPost(crow::Response &res, const crow::Request &req, 215 const std::vector<std::string> ¶ms) override 216 { 217 std::optional<std::string> transferProtocol; 218 std::string imageURI; 219 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 220 221 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost"; 222 223 // User can pass in both TransferProtocol and ImageURI parameters or 224 // they can pass in just the ImageURI with the transfer protocl embedded 225 // within it. 226 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin 227 // 2) ImageURI:tftp://1.1.1.1/myfile.bin 228 229 if (!json_util::readJson(req, asyncResp->res, "TransferProtocol", 230 transferProtocol, "ImageURI", imageURI)) 231 { 232 BMCWEB_LOG_DEBUG 233 << "Missing TransferProtocol or ImageURI parameter"; 234 return; 235 } 236 if (!transferProtocol) 237 { 238 // Must be option 2 239 // Verify ImageURI has transfer protocol in it 240 size_t separator = imageURI.find(":"); 241 if ((separator == std::string::npos) || 242 ((separator + 1) > imageURI.size())) 243 { 244 messages::actionParameterValueTypeError( 245 asyncResp->res, imageURI, "ImageURI", 246 "UpdateService.SimpleUpdate"); 247 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: " 248 << imageURI; 249 return; 250 } 251 transferProtocol = imageURI.substr(0, separator); 252 // Ensure protocol is upper case for a common comparison path below 253 boost::to_upper(*transferProtocol); 254 BMCWEB_LOG_DEBUG << "Encoded transfer protocol " 255 << *transferProtocol; 256 257 // Adjust imageURI to not have the protocol on it for parsing 258 // below 259 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin 260 imageURI = imageURI.substr(separator + 3); 261 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI; 262 } 263 264 // OpenBMC currently only supports TFTP 265 if (*transferProtocol != "TFTP") 266 { 267 messages::actionParameterNotSupported(asyncResp->res, 268 "TransferProtocol", 269 "UpdateService.SimpleUpdate"); 270 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: " 271 << *transferProtocol; 272 return; 273 } 274 275 // Format should be <IP or Hostname>/<file> for imageURI 276 size_t separator = imageURI.find("/"); 277 if ((separator == std::string::npos) || 278 ((separator + 1) > imageURI.size())) 279 { 280 messages::actionParameterValueTypeError( 281 asyncResp->res, imageURI, "ImageURI", 282 "UpdateService.SimpleUpdate"); 283 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI; 284 return; 285 } 286 287 std::string tftpServer = imageURI.substr(0, separator); 288 std::string fwFile = imageURI.substr(separator + 1); 289 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile; 290 291 // Setup callback for when new software detected 292 // Give TFTP 2 minutes to complete 293 monitorForSoftwareAvailable(nullptr, req, 120); 294 295 // TFTP can take up to 2 minutes depending on image size and 296 // connection speed. Return to caller as soon as the TFTP operation 297 // has been started. The callback above will ensure the activate 298 // is started once the download has completed 299 redfish::messages::success(asyncResp->res); 300 301 // Call TFTP service 302 crow::connections::systemBus->async_method_call( 303 [](const boost::system::error_code ec) { 304 if (ec) 305 { 306 // messages::internalError(asyncResp->res); 307 cleanUp(); 308 BMCWEB_LOG_DEBUG << "error_code = " << ec; 309 BMCWEB_LOG_DEBUG << "error msg = " << ec.message(); 310 } 311 else 312 { 313 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success"; 314 } 315 }, 316 "xyz.openbmc_project.Software.Download", 317 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP", 318 "DownloadViaTFTP", fwFile, tftpServer); 319 320 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost"; 321 } 322 }; 323 324 class UpdateService : public Node 325 { 326 public: 327 UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/") 328 { 329 entityPrivileges = { 330 {boost::beast::http::verb::get, {{"Login"}}}, 331 {boost::beast::http::verb::head, {{"Login"}}}, 332 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 333 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 334 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 335 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 336 } 337 338 private: 339 void doGet(crow::Response &res, const crow::Request &req, 340 const std::vector<std::string> ¶ms) override 341 { 342 res.jsonValue["@odata.type"] = "#UpdateService.v1_2_0.UpdateService"; 343 res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService"; 344 res.jsonValue["@odata.context"] = 345 "/redfish/v1/$metadata#UpdateService.UpdateService"; 346 res.jsonValue["Id"] = "UpdateService"; 347 res.jsonValue["Description"] = "Service for Software Update"; 348 res.jsonValue["Name"] = "Update Service"; 349 res.jsonValue["HttpPushUri"] = "/redfish/v1/UpdateService"; 350 // UpdateService cannot be disabled 351 res.jsonValue["ServiceEnabled"] = true; 352 res.jsonValue["FirmwareInventory"] = { 353 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}}; 354 #ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE 355 // Update Actions object. 356 nlohmann::json &updateSvcSimpleUpdate = 357 res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"]; 358 updateSvcSimpleUpdate["target"] = 359 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate"; 360 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = { 361 "TFTP"}; 362 #endif 363 res.end(); 364 } 365 366 void doPatch(crow::Response &res, const crow::Request &req, 367 const std::vector<std::string> ¶ms) override 368 { 369 BMCWEB_LOG_DEBUG << "doPatch..."; 370 371 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 372 std::string applyTime; 373 374 if (!json_util::readJson(req, res, "ApplyTime", applyTime)) 375 { 376 return; 377 } 378 379 if ((applyTime == "Immediate") || (applyTime == "OnReset")) 380 { 381 std::string applyTimeNewVal; 382 if (applyTime == "Immediate") 383 { 384 applyTimeNewVal = "xyz.openbmc_project.Software.ApplyTime." 385 "RequestedApplyTimes.Immediate"; 386 } 387 else 388 { 389 applyTimeNewVal = "xyz.openbmc_project.Software.ApplyTime." 390 "RequestedApplyTimes.OnReset"; 391 } 392 393 // Set the requested image apply time value 394 crow::connections::systemBus->async_method_call( 395 [asyncResp](const boost::system::error_code ec) { 396 if (ec) 397 { 398 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 399 messages::internalError(asyncResp->res); 400 return; 401 } 402 messages::success(asyncResp->res); 403 }, 404 "xyz.openbmc_project.Settings", 405 "/xyz/openbmc_project/software/apply_time", 406 "org.freedesktop.DBus.Properties", "Set", 407 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime", 408 std::variant<std::string>{applyTimeNewVal}); 409 } 410 else 411 { 412 BMCWEB_LOG_INFO << "ApplyTime value is not in the list of " 413 "acceptable values"; 414 messages::propertyValueNotInList(asyncResp->res, applyTime, 415 "ApplyTime"); 416 } 417 } 418 419 void doPost(crow::Response &res, const crow::Request &req, 420 const std::vector<std::string> ¶ms) override 421 { 422 BMCWEB_LOG_DEBUG << "doPost..."; 423 424 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 425 426 // Setup callback for when new software detected 427 monitorForSoftwareAvailable(asyncResp, req); 428 429 std::string filepath( 430 "/tmp/images/" + 431 boost::uuids::to_string(boost::uuids::random_generator()())); 432 BMCWEB_LOG_DEBUG << "Writing file to " << filepath; 433 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | 434 std::ofstream::trunc); 435 out << req.body; 436 out.close(); 437 BMCWEB_LOG_DEBUG << "file upload complete!!"; 438 } 439 }; 440 441 class SoftwareInventoryCollection : public Node 442 { 443 public: 444 template <typename CrowApp> 445 SoftwareInventoryCollection(CrowApp &app) : 446 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/") 447 { 448 entityPrivileges = { 449 {boost::beast::http::verb::get, {{"Login"}}}, 450 {boost::beast::http::verb::head, {{"Login"}}}, 451 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 452 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 453 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 454 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 455 } 456 457 private: 458 void doGet(crow::Response &res, const crow::Request &req, 459 const std::vector<std::string> ¶ms) override 460 { 461 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 462 res.jsonValue["@odata.type"] = 463 "#SoftwareInventoryCollection.SoftwareInventoryCollection"; 464 res.jsonValue["@odata.id"] = 465 "/redfish/v1/UpdateService/FirmwareInventory"; 466 res.jsonValue["@odata.context"] = 467 "/redfish/v1/" 468 "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection"; 469 res.jsonValue["Name"] = "Software Inventory Collection"; 470 471 crow::connections::systemBus->async_method_call( 472 [asyncResp]( 473 const boost::system::error_code ec, 474 const std::vector<std::pair< 475 std::string, std::vector<std::pair< 476 std::string, std::vector<std::string>>>>> 477 &subtree) { 478 if (ec) 479 { 480 messages::internalError(asyncResp->res); 481 return; 482 } 483 asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 484 asyncResp->res.jsonValue["Members@odata.count"] = 0; 485 486 for (auto &obj : subtree) 487 { 488 const std::vector< 489 std::pair<std::string, std::vector<std::string>>> 490 &connections = obj.second; 491 492 // if can't parse fw id then return 493 std::size_t idPos; 494 if ((idPos = obj.first.rfind("/")) == std::string::npos) 495 { 496 messages::internalError(asyncResp->res); 497 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!"; 498 return; 499 } 500 std::string swId = obj.first.substr(idPos + 1); 501 502 for (auto &conn : connections) 503 { 504 const std::string &connectionName = conn.first; 505 BMCWEB_LOG_DEBUG << "connectionName = " 506 << connectionName; 507 BMCWEB_LOG_DEBUG << "obj.first = " << obj.first; 508 509 crow::connections::systemBus->async_method_call( 510 [asyncResp, 511 swId](const boost::system::error_code error_code, 512 const VariantType &activation) { 513 BMCWEB_LOG_DEBUG 514 << "safe returned in lambda function"; 515 if (error_code) 516 { 517 messages::internalError(asyncResp->res); 518 return; 519 } 520 521 const std::string *swActivationStatus = 522 std::get_if<std::string>(&activation); 523 if (swActivationStatus == nullptr) 524 { 525 messages::internalError(asyncResp->res); 526 return; 527 } 528 if (swActivationStatus != nullptr && 529 *swActivationStatus != 530 "xyz.openbmc_project.Software." 531 "Activation." 532 "Activations.Active") 533 { 534 // The activation status of this software is 535 // not currently active, so does not need to 536 // be listed in the response 537 return; 538 } 539 nlohmann::json &members = 540 asyncResp->res.jsonValue["Members"]; 541 members.push_back( 542 {{"@odata.id", "/redfish/v1/UpdateService/" 543 "FirmwareInventory/" + 544 swId}}); 545 asyncResp->res 546 .jsonValue["Members@odata.count"] = 547 members.size(); 548 }, 549 connectionName, obj.first, 550 "org.freedesktop.DBus.Properties", "Get", 551 "xyz.openbmc_project.Software.Activation", 552 "Activation"); 553 } 554 } 555 }, 556 "xyz.openbmc_project.ObjectMapper", 557 "/xyz/openbmc_project/object_mapper", 558 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 559 "/xyz/openbmc_project/software", int32_t(1), 560 std::array<const char *, 1>{ 561 "xyz.openbmc_project.Software.Version"}); 562 } 563 }; 564 565 class SoftwareInventory : public Node 566 { 567 public: 568 template <typename CrowApp> 569 SoftwareInventory(CrowApp &app) : 570 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/", 571 std::string()) 572 { 573 entityPrivileges = { 574 {boost::beast::http::verb::get, {{"Login"}}}, 575 {boost::beast::http::verb::head, {{"Login"}}}, 576 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 577 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 578 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 579 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 580 } 581 582 private: 583 /* Fill related item links (i.e. bmc, bios) in for inventory */ 584 static void getRelatedItems(std::shared_ptr<AsyncResp> aResp, 585 const std::string &purpose) 586 { 587 if (purpose == fw_util::bmcPurpose) 588 { 589 nlohmann::json &members = aResp->res.jsonValue["RelatedItem"]; 590 members.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}}); 591 aResp->res.jsonValue["Members@odata.count"] = members.size(); 592 } 593 else if (purpose == fw_util::biosPurpose) 594 { 595 // TODO(geissonator) Need BIOS schema support added for this 596 // to be valid 597 // nlohmann::json &members = aResp->res.jsonValue["RelatedItem"]; 598 // members.push_back( 599 // {{"@odata.id", "/redfish/v1/Systems/system/BIOS"}}); 600 // aResp->res.jsonValue["Members@odata.count"] = members.size(); 601 } 602 else 603 { 604 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose; 605 } 606 } 607 608 void doGet(crow::Response &res, const crow::Request &req, 609 const std::vector<std::string> ¶ms) override 610 { 611 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 612 res.jsonValue["@odata.type"] = 613 "#SoftwareInventory.v1_1_0.SoftwareInventory"; 614 res.jsonValue["@odata.context"] = 615 "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory"; 616 res.jsonValue["Name"] = "Software Inventory"; 617 res.jsonValue["Updateable"] = false; 618 res.jsonValue["Status"]["Health"] = "OK"; 619 res.jsonValue["Status"]["HealthRollup"] = "OK"; 620 res.jsonValue["Status"]["State"] = "Enabled"; 621 622 if (params.size() != 1) 623 { 624 messages::internalError(res); 625 res.end(); 626 return; 627 } 628 629 std::shared_ptr<std::string> swId = 630 std::make_shared<std::string>(params[0]); 631 632 res.jsonValue["@odata.id"] = 633 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId; 634 635 crow::connections::systemBus->async_method_call( 636 [asyncResp, swId]( 637 const boost::system::error_code ec, 638 const std::vector<std::pair< 639 std::string, std::vector<std::pair< 640 std::string, std::vector<std::string>>>>> 641 &subtree) { 642 BMCWEB_LOG_DEBUG << "doGet callback..."; 643 if (ec) 644 { 645 messages::internalError(asyncResp->res); 646 return; 647 } 648 649 for (const std::pair< 650 std::string, 651 std::vector< 652 std::pair<std::string, std::vector<std::string>>>> 653 &obj : subtree) 654 { 655 if (boost::ends_with(obj.first, *swId) != true) 656 { 657 continue; 658 } 659 660 if (obj.second.size() < 1) 661 { 662 continue; 663 } 664 665 crow::connections::systemBus->async_method_call( 666 [asyncResp, 667 swId](const boost::system::error_code error_code, 668 const boost::container::flat_map< 669 std::string, VariantType> &propertiesList) { 670 if (error_code) 671 { 672 messages::internalError(asyncResp->res); 673 return; 674 } 675 boost::container::flat_map< 676 std::string, VariantType>::const_iterator it = 677 propertiesList.find("Purpose"); 678 if (it == propertiesList.end()) 679 { 680 BMCWEB_LOG_DEBUG 681 << "Can't find property \"Purpose\"!"; 682 messages::propertyMissing(asyncResp->res, 683 "Purpose"); 684 return; 685 } 686 const std::string *swInvPurpose = 687 std::get_if<std::string>(&it->second); 688 if (swInvPurpose == nullptr) 689 { 690 BMCWEB_LOG_DEBUG 691 << "wrong types for property\"Purpose\"!"; 692 messages::propertyValueTypeError(asyncResp->res, 693 "", "Purpose"); 694 return; 695 } 696 697 BMCWEB_LOG_DEBUG << "swInvPurpose = " 698 << *swInvPurpose; 699 it = propertiesList.find("Version"); 700 if (it == propertiesList.end()) 701 { 702 BMCWEB_LOG_DEBUG 703 << "Can't find property \"Version\"!"; 704 messages::propertyMissing(asyncResp->res, 705 "Version"); 706 return; 707 } 708 709 BMCWEB_LOG_DEBUG << "Version found!"; 710 711 const std::string *version = 712 std::get_if<std::string>(&it->second); 713 714 if (version == nullptr) 715 { 716 BMCWEB_LOG_DEBUG 717 << "Can't find property \"Version\"!"; 718 719 messages::propertyValueTypeError(asyncResp->res, 720 "", "Version"); 721 return; 722 } 723 asyncResp->res.jsonValue["Version"] = *version; 724 asyncResp->res.jsonValue["Id"] = *swId; 725 726 // swInvPurpose is of format: 727 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC 728 // Translate this to "ABC update" 729 size_t endDesc = swInvPurpose->rfind("."); 730 if (endDesc == std::string::npos) 731 { 732 messages::internalError(asyncResp->res); 733 return; 734 } 735 endDesc++; 736 if (endDesc >= swInvPurpose->size()) 737 { 738 messages::internalError(asyncResp->res); 739 return; 740 } 741 742 std::string formatDesc = 743 swInvPurpose->substr(endDesc); 744 asyncResp->res.jsonValue["Description"] = 745 formatDesc + " update"; 746 getRelatedItems(asyncResp, *swInvPurpose); 747 }, 748 obj.second[0].first, obj.first, 749 "org.freedesktop.DBus.Properties", "GetAll", 750 "xyz.openbmc_project.Software.Version"); 751 } 752 }, 753 "xyz.openbmc_project.ObjectMapper", 754 "/xyz/openbmc_project/object_mapper", 755 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 756 "/xyz/openbmc_project/software", int32_t(1), 757 std::array<const char *, 1>{ 758 "xyz.openbmc_project.Software.Version"}); 759 } 760 }; 761 762 } // namespace redfish 763