From d3f989bc8b50d78389f01e93de838fd23ba8eac1 Mon Sep 17 00:00:00 2001 From: roly Date: Fri, 10 Jan 2025 11:29:50 +0800 Subject: [PATCH] Add luxshare oem redfish update interface --- include/image_upload.hpp | 152 +- include/luxshare/firmware_update.hpp | 67 + include/luxshare/upload_update.hpp | 1827 +++++++++++++++++++++++ meson.build | 23 +- redfish-core/include/utils/sw_utils.hpp | 30 + src/webserver_main.cpp | 2 + 6 files changed, 2093 insertions(+), 8 deletions(-) create mode 100755 include/luxshare/firmware_update.hpp create mode 100755 include/luxshare/upload_update.hpp diff --git a/include/image_upload.hpp b/include/image_upload.hpp index 521ff659..e4704fc7 100644 --- a/include/image_upload.hpp +++ b/include/image_upload.hpp @@ -10,14 +10,131 @@ #include #include #include +#include namespace crow { + +namespace luxshare_update +{ + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static std::unique_ptr fwUpdateMatcher; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static std::string bmcId; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static std::string biosId; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static std::string mbCpldId; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static std::string fcbCpldId; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static std::string leaf; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static std::unordered_map firmwareTypeAndId; + +static const std::unordered_map purposeToTarget = { + {"BMC", "BMC"}, {"Host", "BIOS"}, + {"CPLD", "MBCPLD"}, {"FCB", "FCBCPLD"}, +}; + +inline bool + getTargetFirmwareInfo(const dbus::utility::DBusPropertiesMap& propertys, + std::string& target, std::string& version) +{ + const std::string* dbusPurpose = nullptr; + const std::string* dbusversion = nullptr; + std::string purpose; + for (const auto& property : propertys) + { + if (property.first == "Purpose") + { + dbusPurpose = std::get_if(&property.second); + } + + if (property.first == "Version") + { + dbusversion = std::get_if(&property.second); + } + } + + if (dbusPurpose == nullptr || dbusversion == nullptr) + { + BMCWEB_LOG_ERROR("dbusPurpose or version error."); + return false; + } + version = *dbusversion; + // xyz.openbmc_project.Software.Version.VersionPurpose.BMC + // Firmware type after intercepting the last dot. + purpose = dbusPurpose->substr(dbusPurpose->find_last_of('.') + 1); + if (purposeToTarget.find(purpose) == purposeToTarget.end()) + { + BMCWEB_LOG_ERROR("find purpose error."); + return false; + } + // Convert to the key Redfish requires to display. + target = purposeToTarget.at(purpose); + + return true; +} + +static void updateGlobalFirmwareId(const std::string& target) +{ + if (target == "BMC") + { + bmcId = leaf; + } + else if (target == "BIOS") + { + biosId = leaf; + } + else if (target == "MBCPLD") + { + mbCpldId = leaf; + } + else if (target == "FCBCPLD") + { + fcbCpldId = leaf; + } + // Add to global Map + firmwareTypeAndId[target] = leaf; +} + +inline void deleteLastUpgradedImage(const std::string& target) +{ + if (firmwareTypeAndId.find(target) != firmwareTypeAndId.end()) + { + std::string deletedLeaf = firmwareTypeAndId.at(target); + // Whether the last firmware deletion was successful + // does not affect the result of the current firmware upload, + // so "ec" is not processed. + crow::connections::systemBus->async_method_call( + [](const boost::system::error_code& /*ec*/) {}, + "xyz.openbmc_project.Software.BMC.Updater", + "/xyz/openbmc_project/software/" + deletedLeaf, + "xyz.openbmc_project.Object.Delete", "Delete"); + } +} + +inline void deleteThisUpgradedImage(const std::string& target) +{ + // delete leaf file + crow::connections::systemBus->async_method_call( + [](const boost::system::error_code& /*ec*/) {}, + "xyz.openbmc_project.Software.BMC.Updater", + "/xyz/openbmc_project/software/" + target, + "xyz.openbmc_project.Object.Delete", "Delete"); +} + +} // namespace luxshare_update + namespace image_upload { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static std::unique_ptr fwUpdateMatcher; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static std::string leaf; inline void uploadImageHandler(const crow::Request& req, @@ -66,18 +183,45 @@ inline void dbus::utility::DBusInteracesMap interfaces; m.read(path, interfaces); - if (std::find_if(interfaces.begin(), interfaces.end(), - [](const auto& i) { + auto iter = std::find_if(interfaces.begin(), interfaces.end(), + [](const auto& i) { return i.first == "xyz.openbmc_project.Software.Version"; - }) != interfaces.end()) + }); + + if (iter != interfaces.end()) { + std::string version; + std::string firmwareType; + timeout.cancel(); - std::string leaf = path.filename(); + leaf = path.filename(); if (leaf.empty()) { leaf = path.str; } + luxshare_update::leaf = leaf; + // Get target and version + const bool success = luxshare_update::getTargetFirmwareInfo( + iter->second, firmwareType, version); + + if (!success) + { + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + asyncResp->res.jsonValue["data"]["description"] = + "The request failed due to an internal service error."; + asyncResp->res.jsonValue["message"] = + "500 Internal Server Error"; + asyncResp->res.jsonValue["status"] = "error"; + return; + } + + // delete the last upgraded image + luxshare_update::deleteLastUpgradedImage(firmwareType); + + luxshare_update::updateGlobalFirmwareId(firmwareType); + asyncResp->res.jsonValue["data"] = leaf; asyncResp->res.jsonValue["message"] = "200 OK"; asyncResp->res.jsonValue["status"] = "ok"; diff --git a/include/luxshare/firmware_update.hpp b/include/luxshare/firmware_update.hpp new file mode 100755 index 00000000..e2aa9b80 --- /dev/null +++ b/include/luxshare/firmware_update.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include "upload_update.hpp" + +namespace crow +{ +namespace luxshare_update_firmware +{ + +inline void requestRoutesOemLuxshareImageUpload(App& app) +{ + // Add post method to post image + BMCWEB_ROUTE(app, + "/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareFile") + .privileges(redfish::privileges::privilegeSetConfigureManager) + .methods(boost::beast::http::verb::post)( + [](const crow::Request& req, + const std::shared_ptr& asyncResp) { + crow::luxshare_update::uploadImageHandler(req, asyncResp, "Firmware"); + }); +} + +inline void requestRoutesOemLuxshareImageUpdate(App& app) +{ + // Add post method to post update + BMCWEB_ROUTE( + app, "/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareUpdate") + .privileges(redfish::privileges::privilegeSetConfigureManager) + .methods(boost::beast::http::verb::post)( + [](const crow::Request& req, + const std::shared_ptr& asyncResp) { + crow::luxshare_update::handlePostFirmwareUpdate(req, asyncResp); + }); + + // Add delete method to delete Image + BMCWEB_ROUTE( + app, "/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareUpdate") + .privileges(redfish::privileges::privilegeSetConfigureManager) + .methods(boost::beast::http::verb::delete_)( + [](const crow::Request& req, + const std::shared_ptr& asyncResp) { + crow::luxshare_update::handleDeleteFirmwareUpdate(req, asyncResp); + }); +} + +inline void requestRoutesOemLuxshareUpdateStatus(App& app) +{ + // Add get method to get update status + BMCWEB_ROUTE( + app, "/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareStatus") + .privileges(redfish::privileges::privilegeSetLogin) + .methods(boost::beast::http::verb::get)( + [](const crow::Request& req, + const std::shared_ptr& asyncResp) { + crow::luxshare_update::handleFirmwareStatus(req, asyncResp); + }); +} + +inline void requestRoutes(App& app) +{ + requestRoutesOemLuxshareImageUpload(app); + requestRoutesOemLuxshareImageUpdate(app); + requestRoutesOemLuxshareUpdateStatus(app); +} + +} // namespace luxshare_update_firmware +} // namespace crow diff --git a/include/luxshare/upload_update.hpp b/include/luxshare/upload_update.hpp new file mode 100755 index 00000000..895ecac2 --- /dev/null +++ b/include/luxshare/upload_update.hpp @@ -0,0 +1,1827 @@ +#pragma once +#include "image_upload.hpp" +#include "multipart_parser.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +constexpr auto updateService = "xyz.openbmc_project.Software.BMC.Updater"; +constexpr auto asyncUpdateService = "xyz.openbmc_project.Software.AsyncUpdate"; +constexpr auto updateObjPath = "/xyz/openbmc_project/software/"; +constexpr auto asyncUpdateObjPath = "/xyz/openbmc_project/Software/AsyncUpdate"; +constexpr auto updateActivationInterface = + "xyz.openbmc_project.Software.Activation"; +constexpr auto updateApplyOptionsInterface = + "xyz.openbmc_project.Software.ApplyOptions"; +constexpr auto updateVersionInterface = "xyz.openbmc_project.Software.Version"; +constexpr auto activationProgressInterface = + "xyz.openbmc_project.Software.ActivationProgress"; + +constexpr auto deleteInterface = "xyz.openbmc_project.Object.Delete"; +constexpr auto deleteProperty = "Delete"; +constexpr auto updateProperty = "Activation"; +constexpr auto versionProperty = "Version"; +constexpr auto progressProperty = "Progress"; +constexpr auto requsetedUpdateProperty = "RequestedActivation"; + +constexpr auto updateApplyProperty = "RequestedApplyTime"; +constexpr auto updateClearConfig = "ClearConfig"; +constexpr auto asyncUpdateMethod = "AddQueue"; +constexpr auto asyncUpdateStatus = "AsyncUpdateStatus"; +constexpr auto applytimeService = "xyz.openbmc_project.Settings"; +constexpr auto applytimeobjPath = "/xyz/openbmc_project/software/apply_time"; +constexpr auto applytimeInterface = "xyz.openbmc_project.Software.ApplyTime"; +constexpr auto ready = + "xyz.openbmc_project.Software.Activation.Activations.Ready"; +constexpr auto activating = + "xyz.openbmc_project.Software.Activation.Activations.Activating"; +constexpr auto active = + "xyz.openbmc_project.Software.Activation.Activations.Active"; +constexpr auto addQueue = + "xyz.openbmc_project.Software.Activation.Activations.AddQueue"; +constexpr auto interrupted = + "xyz.openbmc_project.Software.Activation.Activations.Interrupted"; +constexpr auto inVaild = + "xyz.openbmc_project.Software.Activation.Activations.Invalid"; +constexpr auto requestedActive = + "xyz.openbmc_project.Software.Activation.RequestedActivations.Active"; + +constexpr auto immediate = + "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate"; +constexpr auto onReset = + "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset"; +constexpr auto versionPurposeHost = + "xyz.openbmc_project.Software.Version.VersionPurpose.Host"; +constexpr auto versionPurposeMBCPLD = + "xyz.openbmc_project.Software.Version.VersionPurpose.CPLD"; +constexpr auto versionPurposeFCBCPLD = + "xyz.openbmc_project.Software.Version.VersionPurpose.FCB"; + +constexpr auto failed = + "xyz.openbmc_project.Software.Activation.Activations.Failed"; +constexpr auto bmcUpdateStatusFile = "/tmp/bmc_update_status"; +constexpr auto biosUpdateStatusFile = "/tmp/bios_update_status"; +constexpr auto mbcpldUpdateStatusFile = "/tmp/mbcpld_update_status"; +constexpr auto fcbcpldUpdateStatusFile = "/tmp/fcbcpld_update_status"; + +constexpr auto updateImageFolder = "/tmp/images/"; +constexpr auto updateTargetInterface = + "xyz.openbmc_project.Software.UpdateTarget"; +constexpr auto updateTargetSlot = "UpdateTargetSlot"; +using TargetSlot = + sdbusplus::xyz::openbmc_project::Software::server::UpdateTarget::TargetSlot; +using ConfigManagement = sdbusplus::xyz::openbmc_project::Software::server:: + ApplyOptions::ConfigManagement; + +static std::string fancpldLastUpgradeStatus; + +namespace crow +{ +namespace luxshare_update +{ + +static const std::unordered_set bmcRefreshStrategy = { + "Keep", "Clear", "PartialKeep", "FactoryReset"}; + +/** + * @brief Converts the target slot to enum + * + */ +inline TargetSlot convertTargetSlot(const std::optional& slot) +{ + if (slot == "Secondary") + { + return TargetSlot::Secondary; + } + if (slot == "Both") + { + return TargetSlot::Both; + } + + return TargetSlot::Primary; +} + +inline ConfigManagement convertConfigStrategy(const std::string& configStrategy) +{ + if (configStrategy == "Clear") + { + return ConfigManagement::Clear; + } + if (configStrategy == "PartialKeep") + { + return ConfigManagement::PartialKeep; + } + if (configStrategy == "FactoryReset") + { + return ConfigManagement::FactoryReset; + } + + return ConfigManagement::Keep; +} + +inline std::string getManifestPurpose(const std::string& folderName) +{ + std::ifstream ifs(updateImageFolder + folderName + "/MANIFEST"); + + if (!ifs.is_open()) + { + BMCWEB_LOG_ERROR("Error while reading MANIFEST file"); + return {}; + } + std::string purpose; + while (std::getline(ifs, purpose)) + { + auto pos = purpose.find("purpose="); + if (pos != std::string::npos) + { + return purpose.substr(pos + strlen("purpose=")); + } + } + + return {}; +} + +inline bool isUploadImageValid(const std::string& target) +{ + if (std::filesystem::exists(updateImageFolder + leaf + "/MANIFEST")) + { + std::string fwPurpose = getManifestPurpose(leaf); + return (target == "BMC" && + (fwPurpose.find("BMC") != std::string::npos)) || + (target == "BIOS" && + (fwPurpose.find("Host") != std::string::npos)) || + (target == "MBCPLD" && + (fwPurpose.find("CPLD") != std::string::npos)) || + (target == "FCBCPLD" && + (fwPurpose.find("FCB") != std::string::npos)) + ; + } + return false; +} + +inline std::string getFwId(const std::string& purpose, + const std::string& firmwareID) +{ + std::string fwId; + std::string fwPurpose; + if (std::filesystem::exists(updateImageFolder + image_upload::leaf + + "/MANIFEST")) + { + fwPurpose = getManifestPurpose(image_upload::leaf); + if (fwPurpose.find(purpose) != std::string::npos) + { + fwId = image_upload::leaf; + return fwId; + } + } + + if (std::filesystem::exists(updateImageFolder + firmwareID + "/MANIFEST")) + { + fwPurpose = getManifestPurpose(firmwareID); + if (fwPurpose.find(purpose) != std::string::npos) + { + fwId = firmwareID; + } + } + return fwId; +} + +inline void getUpdateStatusFileInfo(const std::string& target, + std::string& fwId, + std::string& updateStatusflag, + bool& fileExist) +{ + std::string fileName; + + if (target == "BMC") + { + fwId = bmcId; + std::ifstream in(bmcUpdateStatusFile); + in >> updateStatusflag; + fileName = bmcUpdateStatusFile; + } + else if (target == "BIOS") + { + fwId = getFwId("Host", biosId); + std::ifstream in(biosUpdateStatusFile); + in >> updateStatusflag; + fileName = biosUpdateStatusFile; + } + else if (target == "MBCPLD") + { + fwId = getFwId("CPLD", mbCpldId); + std::ifstream in(mbcpldUpdateStatusFile); + in >> updateStatusflag; + fileName = mbcpldUpdateStatusFile; + } + else if (target == "FCBCPLD") + { + fwId = getFwId("FCB", fcbCpldId); + std::ifstream in(fcbcpldUpdateStatusFile); + in >> updateStatusflag; + fileName = fcbcpldUpdateStatusFile; + } + + fileExist = std::filesystem::exists(fileName); +} + +/* + * UpdateStatus: + * 0: complete - bios_update_status file exist and value is 0 + * 1: fail - bios_update_status file exist and value is not 0 and not null + * 2: incomming - dbus obj exist and status activting + * 3: no task - dbus obj not exist and bios_update_status file not exist + * 4: waiting - dbus obj exist and status ready + * 5: interrupted - dbus obj exist and status interrupted + */ + +inline bool getFirmwareUpgradeStatus( + const boost::asio::yield_context& yield, const std::string& fwId, + const std::string& target, const std::string& updateStatusflag, + const bool& fileExist, std::string& statusString, uint8_t& statusNumber) +{ + boost::system::error_code ec; + auto response = crow::connections::systemBus + ->yield_method_call>( + yield, ec, updateService, updateObjPath + fwId, + "org.freedesktop.DBus.Properties", "Get", + updateActivationInterface, updateProperty); + + if (ec && !fileExist) + { + statusNumber = 3; + return true; + } + + const std::string* updateStatus = std::get_if(&response); + if (updateStatus == nullptr) + { + return false; + } + + if (target == "BMC") + { + if (((ec == boost::system::errc::invalid_argument || + ec.value() == + boost::system::linux_error::bad_request_descriptor) && + updateStatusflag == "0" && fileExist) || + (*updateStatus == active)) + { + statusNumber = 0; + statusString = "Updated"; + } + } + else + { + if ((ec == boost::system::errc::invalid_argument || + ec.value() == + boost::system::linux_error::bad_request_descriptor) && + updateStatusflag == "0" && fileExist) + { + statusNumber = 0; + statusString = "Updated"; + } + } + + if (*updateStatus == ready) + { + statusNumber = 4; + statusString = "Uploaded"; + } + + else if (*updateStatus == activating) + { + statusNumber = 2; + statusString = "Updating"; + } + else if (*updateStatus == addQueue) + { + statusNumber = 4; + statusString = "Queued"; + } + else if (*updateStatus == interrupted) + { + statusNumber = 5; + statusString = "Interrupted"; + } + else if (*updateStatus == inVaild || *updateStatus == failed || + (updateStatusflag != "0" && fileExist)) + { + statusNumber = 1; + statusString = "Failed"; + } + + return true; +} + +static void clearGlobalFirmwareId(const std::string& target) +{ + if (target == "BMC") + { + bmcId.clear(); + } + else if (target == "BIOS") + { + biosId.clear(); + } + else if (target == "MBCPLD") + { + mbCpldId.clear(); + } + else if (target == "FCBCPLD") + { + fcbCpldId.clear(); + } +} + +inline void updateGlobalUpdateStatus(const std::string& target, + const std::string& status) +{ + if (target == "FCBCPLD") + { + fancpldLastUpgradeStatus = status; + } +} + +inline void clearGlobalUpdateStatus(const std::string& target) +{ + if (target == "FCBCPLD") + { + fancpldLastUpgradeStatus.clear(); + } +} + +inline void updateResp(const std::string& firmwareType, + const std::shared_ptr& asyncResp, + const std::string& version) +{ + deleteLastUpgradedImage(firmwareType); + updateGlobalFirmwareId(firmwareType); + clearGlobalUpdateStatus(firmwareType); + + nlohmann::json targetStatus; + // Version only supports the following exception + // returns:Mismatch,Invalid. + if (version != "Mismatch" && version != "Invalid") + { + targetStatus[firmwareType]["UpdateStatus"] = "Uploaded"; + } + else + { + targetStatus[firmwareType]["UpdateStatus"] = "Failed"; + } + targetStatus[firmwareType]["VersionStatus"] = version; + targetStatus[firmwareType]["UpdateProgress"] = + nlohmann::json::value_t::null; + asyncResp->res.jsonValue["Oem"]["Luxshare"].push_back(targetStatus); +} + +inline void getFirmwareUpgradeStatusAsync( + const std::string& firmwareType, const std::string& fwId, + const std::string& version, + const std::shared_ptr& asyncResp, const bool& fileExist) +{ + crow::connections::systemBus->async_method_call( + [firmwareType, version, asyncResp, + fileExist](boost::system::error_code ec1, + std::variant statusInfo) { + if (ec1) + { + // fileExist indicates that the firmware has been updated, + // returning ec1 is normal, and the upload can continue. + if (fileExist) + { + updateResp(firmwareType, asyncResp, version); + return; + } + + BMCWEB_LOG_ERROR("Error getting currentState: {}", ec1.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + const std::string* status = std::get_if(&statusInfo); + if (status == nullptr) + { + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + if (*status == addQueue) + { + deleteThisUpgradedImage(leaf); + leaf.clear(); + BMCWEB_LOG_ERROR("firmware addqueue Conflict !"); + asyncResp->res.result(boost::beast::http::status::conflict); + return; + } + + updateResp(firmwareType, asyncResp, version); + }, + updateService, updateObjPath + fwId, "org.freedesktop.DBus.Properties", + "Get", updateActivationInterface, updateProperty); +} + +inline void + uploadImageHandler(const crow::Request& req, + const std::shared_ptr& asyncResp, + const std::string& target) +{ + // Only allow one FW update at a time + if (fwUpdateMatcher != nullptr) + { + asyncResp->res.addHeader("Retry-After", "30"); + asyncResp->res.result(boost::beast::http::status::service_unavailable); + return; + } + + // Make sure that content type is or multipart/form-data + std::string_view contentType = req.getHeaderValue("content-type"); + if (!boost::starts_with(contentType, "multipart/form-data")) + { + BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType); + asyncResp->res.result(boost::beast::http::status::bad_request); + return; + } + + auto parser = std::make_shared(); + ParserError ec = parser->parse(req); + if (ec != ParserError::PARSER_SUCCESS) + { + // handle error + BMCWEB_LOG_ERROR("MIME parse failed, ec : {}", static_cast(ec)); + asyncResp->res.result(boost::beast::http::status::bad_request); + return; + } + + bool fwimage = false; + for (const FormPart& formpart : parser->mime_fields) + { + boost::beast::http::fields::const_iterator it = + formpart.fields.find("Content-Disposition"); + if (it == formpart.fields.end()) + { + BMCWEB_LOG_ERROR("Couldn't find Content-Disposition"); + return; + } + + // The construction parameters of param_list must start with `;` + size_t index = it->value().find(';'); + if (index == std::string::npos) + { + continue; + } + + for (const auto& param : + boost::beast::http::param_list{it->value().substr(index)}) + { + if (param.first != "name" || param.second.empty()) + { + continue; + } + + if (param.second == "fwimage") + { + fwimage = true; + } + } + } + + if (!fwimage) + { + // handle error + BMCWEB_LOG_ERROR("parse fwimage failed"); + asyncResp->res.result(boost::beast::http::status::bad_request); + return; + } + + boost::asio::spawn( + crow::connections::systemBus->get_io_context(), + [asyncResp, parser, target](const boost::asio::yield_context& yield) { + bool commonInterface = target == "Firmware"; + if (commonInterface) + { + bool isUpdating = false; + // Get the upgrade status of all firmware in the firmware list. + for (const auto& [fwType, fwId] : firmwareTypeAndId) + { + std::string statusString; + uint8_t statusNumber = 0; + std::string updateStatusflag; + std::string tempFwId; + bool fileExist = false; + + getUpdateStatusFileInfo(fwType, tempFwId, updateStatusflag, + fileExist); + const bool success = getFirmwareUpgradeStatus( + yield, fwId, fwType, updateStatusflag, fileExist, + statusString, statusNumber); + if (!success) + { + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + if (statusString == "Updating") + { + isUpdating = true; + break; + } + } + + if (isUpdating) + { + BMCWEB_LOG_ERROR("Status Conflict !"); + asyncResp->res.result(boost::beast::http::status::conflict); + return; + } + } + + static boost::asio::steady_timer timeout( + crow::connections::systemBus->get_io_context(), + std::chrono::seconds(5)); + + timeout.expires_after(std::chrono::seconds(15)); + + auto timeoutHandler = + [asyncResp, commonInterface](const boost::system::error_code& ec1) { + fwUpdateMatcher = nullptr; + if (ec1 == boost::asio::error::operation_aborted) + { + return; + } + asyncResp->res.result(boost::beast::http::status::bad_request); + // Fail + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + + return; + }; + + auto callback = [asyncResp, target, + commonInterface](sdbusplus::message::message& m) { + BMCWEB_LOG_DEBUG("Match fired"); + sdbusplus::message::object_path path; + dbus::utility::DBusInteracesMap interfaces; + m.read(path, interfaces); + + auto iter = std::find_if(interfaces.begin(), interfaces.end(), + [](const auto& i) { + return i.first == "xyz.openbmc_project.Software.Version"; + }); + + if (iter != interfaces.end()) + { + std::string version; + std::string firmwareType; + timeout.cancel(); + + leaf = path.filename(); + if (leaf.empty()) + { + leaf = path.str; + } + + if (commonInterface) + { + // Get target and version + const bool success = getTargetFirmwareInfo( + iter->second, firmwareType, version); + if (!success) + { + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + std::string statusString; + std::string updateStatusflag; + std::string tempFwId; + bool fileExist = false; + std::string firmwareID; + auto it = firmwareTypeAndId.find(firmwareType); + + // Find the firmware in the list that has the same + // firmwareType as the current one + if (it != firmwareTypeAndId.end()) + { + firmwareID = it->second; + getUpdateStatusFileInfo(firmwareType, tempFwId, + updateStatusflag, fileExist); + getFirmwareUpgradeStatusAsync(firmwareType, firmwareID, + version, asyncResp, + fileExist); + } + else + { + updateResp(firmwareType, asyncResp, version); + } + } + else + { + updateGlobalFirmwareId(target); + + clearGlobalUpdateStatus(target); + + if (isUploadImageValid(target)) + { + // Success + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = + 0; + } + else + { + crow::connections::systemBus->async_method_call( + [](const boost::system::error_code& /*ec*/) {}, + updateService, updateObjPath + leaf, + deleteInterface, deleteProperty); + firmwareTypeAndId.erase(target); + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = + 1; + } + } + + BMCWEB_LOG_DEBUG("ending response"); + fwUpdateMatcher = nullptr; + } + }; + fwUpdateMatcher = std::make_unique( + *crow::connections::systemBus, + "interface='org.freedesktop.DBus.ObjectManager',type='signal'," + "member='InterfacesAdded',path='/xyz/openbmc_project/software'", + callback); + + std::string filepath( + "/tmp/images/" + + boost::uuids::to_string(boost::uuids::random_generator()())); + BMCWEB_LOG_DEBUG("Writing file to {}", filepath); + std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | + std::ofstream::trunc); + out << parser->mime_fields[0].content; + out.close(); + timeout.async_wait(timeoutHandler); + }); +} + +inline bool + parseUpgradeStrategy(const crow::Request& req, + const std::shared_ptr& asyncResp, + const std::string& target, + std::tuple& updateStrategy) +{ + constexpr auto attribute = "action"; + std::string action; + constexpr auto targetAttribute = "TargetSlot"; + std::optional targetSlot{}; + if (!redfish::json_util::readJsonPatch(req, asyncResp->res, attribute, + action)) + { + BMCWEB_LOG_DEBUG("Missing property action."); + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + return false; + } + + if (target == "BMC") + { + if (!redfish::json_util::readJsonPatch(req, asyncResp->res, + targetAttribute, targetSlot)) + { + BMCWEB_LOG_DEBUG("Reading property targetSlot error."); + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + return false; + } + + if (targetSlot == std::nullopt) + { + targetSlot = "Primary"; + } + } + + if (targetSlot) + { + updateStrategy = {action, *targetSlot}; + } + + return true; +} + +inline bool isUploadedStatus(const boost::asio::yield_context& yield, + const std::string& target) +{ + std::string fwId; + bool fileExist = false; + std::string updateStatusflag; + std::string statusString; + uint8_t statusNumber = 1; + + getUpdateStatusFileInfo(target, fwId, updateStatusflag, fileExist); + + const bool success = getFirmwareUpgradeStatus(yield, fwId, target, + updateStatusflag, fileExist, + statusString, statusNumber); + return (success) && (statusString == "Uploaded"); +} + +inline bool isUpgradeCompleted(const boost::asio::yield_context& yield, + const std::string& target) +{ + std::string fwId; + bool fileExist = false; + std::string updateStatusflag; + std::string statusString; + uint8_t statusNumber = 1; + + getUpdateStatusFileInfo(target, fwId, updateStatusflag, fileExist); + + const bool success = getFirmwareUpgradeStatus(yield, fwId, target, + updateStatusflag, fileExist, + statusString, statusNumber); + return (success) && (statusString == "Updated" || statusString == "Failed"); +} + +inline bool + isPeerNodeUpgrading(const boost::asio::yield_context& yield, + const std::shared_ptr& asyncResp, + const std::string& upgradeFlagProperty, + const bool& commonInterface = false) +{ + boost::system::error_code ec; + auto property = + crow::connections::systemBus->yield_method_call>( + yield, ec, "xyz.openbmc_project.Settings", + "/xyz/openbmc_project/control/upgrade_flag", + "org.freedesktop.DBus.Properties", "Get", + "xyz.openbmc_project.Control.MultNodeFw.UpgradeFlag", + upgradeFlagProperty); + if (ec) + { + BMCWEB_LOG_ERROR("Error getting {} property: {}", upgradeFlagProperty, + ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return false; + } + + const uint8_t* upgradeFlag = std::get_if(&property); + if (upgradeFlag == nullptr) + { + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return false; + } + + if (*upgradeFlag != 0U) + { + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + + return true; + } + + return false; +} + +inline void activatingImage(const boost::asio::yield_context& yield, + const std::shared_ptr& asyncResp, + std::string& fwId, + const bool& commonInterface = false) +{ + // Activating Image + boost::system::error_code ec; + crow::connections::systemBus->yield_method_call( + yield, ec, updateService, updateObjPath + fwId, + "org.freedesktop.DBus.Properties", "Set", updateActivationInterface, + requsetedUpdateProperty, std::variant(requestedActive)); + if (ec) + { + BMCWEB_LOG_ERROR( + "Error setting RequestedActivation property from {}: {}", + updateObjPath + fwId, ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return; + } + + // Success + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 0; + } +} + +inline void syncUpdateBMCHandler( + const boost::asio::yield_context& yield, + + const std::shared_ptr& asyncResp, + const bool& commonInterface = false, + const std::tuple& updateStrategy = {"Keep", + "Primary"}) +{ + auto action = std::get<0>(updateStrategy); + auto targetSlot = std::get<1>(updateStrategy); + + if (bmcRefreshStrategy.find(action) == bmcRefreshStrategy.end()) + { + BMCWEB_LOG_ERROR("active property not found.."); + asyncResp->res.result(boost::beast::http::status::bad_request); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return; + } + + if (action == "FactoryReset") + { + boost::system::error_code ec; + crow::connections::systemBus->yield_method_call( + yield, ec, "xyz.openbmc_project.Software.BMC.Updater", + "/xyz/openbmc_project/software", + "xyz.openbmc_project.Common.FactoryReset", "Reset"); + if (ec) + { + BMCWEB_LOG_ERROR("Error calling Reset method: {}", ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return; + } + } + + // Set Config + boost::system::error_code configEc; + std::string a = + sdbusplus::common::xyz::openbmc_project::software::ApplyOptions:: + convertConfigManagementToString(convertConfigStrategy(action)); + + crow::connections::systemBus->yield_method_call( + yield, configEc, updateService, updateObjPath + bmcId, + "org.freedesktop.DBus.Properties", "Set", updateApplyOptionsInterface, + updateClearConfig, std::variant(a)); + if (configEc) + { + std::cout<res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return; + } + + if (targetSlot == "Primary" || targetSlot == "Secondary" || + targetSlot == "Both") + { + boost::system::error_code ec; + std::string t = + sdbusplus::common::xyz::openbmc_project::software::UpdateTarget:: + convertTargetSlotToString(convertTargetSlot(targetSlot)); + + crow::connections::systemBus->yield_method_call( + yield, ec, updateService, updateObjPath + bmcId, + "org.freedesktop.DBus.Properties", "Set", updateTargetInterface, + updateTargetSlot, std::variant(t)); + if (ec) + { + BMCWEB_LOG_ERROR( + "Error setting UpdateTargetSlot property from {}: {}", + updateObjPath + bmcId, ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return; + } + } + + bool immediateApply = true; + std::string bmcRunningSlot = redfish::sw_util::getBMCRunningSlot(); + + if (bmcRunningSlot == "0" && targetSlot == "Secondary") + { + immediateApply = false; + } + + if (immediateApply) + { + // Set Apply Time Immediate + // We depend on ApplyTime to trigger BMC reboot after BMC active + // success + boost::system::error_code ec; + crow::connections::systemBus->yield_method_call( + yield, ec, applytimeService, applytimeobjPath, + "org.freedesktop.DBus.Properties", "Set", applytimeInterface, + updateApplyProperty, std::variant(immediate)); + if (ec) + { + BMCWEB_LOG_ERROR("Error setting RequestedApplyTime property: {}", + ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return; + } + } + else + { + // Set Apply Time OnReset + // We depend on ApplyTime to trigger BMC reboot after BMC active + // success + boost::system::error_code ec; + crow::connections::systemBus->yield_method_call( + yield, ec, applytimeService, applytimeobjPath, + "org.freedesktop.DBus.Properties", "Set", applytimeInterface, + updateApplyProperty, std::variant(onReset)); + if (ec) + { + BMCWEB_LOG_ERROR("Error setting RequestedApplyTime property: {}", + ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return; + } + } + + // Activating Image + activatingImage(yield, asyncResp, bmcId, commonInterface); +} + +inline void syncUpdateImageHandler( + const boost::asio::yield_context& yield, + const std::shared_ptr& asyncResp, + const std::string& target, const bool& commonInterface = false, + const std::tuple& updateStrategy = {"Keep", + "Primary"}) +{ + if (target == "BMC") + { + boost::system::error_code ec; + auto chassisState = + crow::connections::systemBus + ->yield_method_call>( + yield, ec, "xyz.openbmc_project.State.Chassis", + "/xyz/openbmc_project/state/chassis0", + "org.freedesktop.DBus.Properties", "Get", + "xyz.openbmc_project.State.Chassis", "CurrentPowerState"); + if (ec) + { + BMCWEB_LOG_ERROR("Error getting CurrentPowerState property: {}", + ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return; + } + + const std::string* s = std::get_if(&chassisState); + BMCWEB_LOG_DEBUG("Chassis state: {}", *s); + if (s != nullptr) + { + // Verify Chassis State + if (*s == "xyz.openbmc_project.State.Chassis.PowerState.On") + { + auto operatingSystemState = + crow::connections::systemBus + ->yield_method_call>( + yield, ec, "xyz.openbmc_project.Host.Misc.Manager", + "/xyz/openbmc_project/misc/platform_state", + "org.freedesktop.DBus.Properties", "Get", + "xyz.openbmc_project.State.Host.Misc", + "PostComplete"); + if (ec) + { + BMCWEB_LOG_ERROR( + "Error getting OperatingSystemState property: {}", + ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = + 1; + } + return; + } + const bool* value = + std::get_if(&operatingSystemState); + if (value == nullptr) + { + // illegal value + BMCWEB_LOG_DEBUG( + "Failed to get bios post complete: property value == nullptr"); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = + 1; + } + return; + } + + // Check post complete + if (*value) + { + syncUpdateBMCHandler(yield, asyncResp, commonInterface, + updateStrategy); + } + else + { + BMCWEB_LOG_DEBUG("BIOS post is not yet completed."); + asyncResp->res.result( + boost::beast::http::status::service_unavailable); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = + 1; + } + } + } + else + { + syncUpdateBMCHandler(yield, asyncResp, commonInterface, + updateStrategy); + } + } + } +} + +inline void asyncUpdateImageHandler( + const boost::asio::yield_context& yield, + const std::shared_ptr& asyncResp, + const std::string& target, const bool& commonInterface = false, + const std::tuple& updateStrategy = {"Keep", ""}) +{ + std::string fwId; + std::string versionPurpose; + if (target == "BIOS") + { + auto action = std::get<0>(updateStrategy); + auto targetSlot = std::get<1>(updateStrategy); + // Set ClearConfig + if (action == "Keep" || action == "Clear") + { + boost::system::error_code ec; + std::string configStrategy = + sdbusplus::common::xyz::openbmc_project::software:: + ApplyOptions::convertConfigManagementToString( + convertConfigStrategy(action)); + crow::connections::systemBus->yield_method_call( + yield, ec, updateService, updateObjPath + biosId, + "org.freedesktop.DBus.Properties", "Set", + updateApplyOptionsInterface, updateClearConfig, + std::variant(configStrategy)); + if (ec) + { + BMCWEB_LOG_ERROR( + "Error setting ClearConfig property from {}: {}", + updateObjPath + biosId, ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return; + } + } + else + { + BMCWEB_LOG_ERROR("active property not found.."); + asyncResp->res.result(boost::beast::http::status::bad_request); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return; + } + + // Set UpdateSlot + if (targetSlot == "Primary" || targetSlot == "Secondary" || + targetSlot == "Both") + { + boost::system::error_code ec; + std::string t = + sdbusplus::common::xyz::openbmc_project::software::UpdateTarget:: + convertTargetSlotToString(convertTargetSlot(targetSlot)); + + crow::connections::systemBus->yield_method_call( + yield, ec, updateService, updateObjPath + biosId, + "org.freedesktop.DBus.Properties", "Set", updateTargetInterface, + updateTargetSlot, std::variant(t)); + if (ec) + { + BMCWEB_LOG_ERROR( + "Error setting UpdateTargetSlot property from {}: {}", + updateObjPath + bmcId, ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return; + } + } + + versionPurpose = versionPurposeHost; + fwId = biosId; + } + else if (target == "MBCPLD") + { + fwId = mbCpldId; + versionPurpose = versionPurposeMBCPLD; + } + else if (target == "FCBCPLD") + { + fwId = fcbCpldId; + versionPurpose = versionPurposeFCBCPLD; + } + + if (asyncResp->res.result() != boost::beast::http::status::no_content) + { + return; + } + + // Add Async Queue + boost::system::error_code ec; + crow::connections::systemBus->yield_method_call( + yield, ec, asyncUpdateService, asyncUpdateObjPath, + "xyz.openbmc_project.Software.AsyncUpdate", asyncUpdateMethod, + versionPurpose, fwId); + if (ec) + { + BMCWEB_LOG_ERROR("Error calling AddQueue method: {}", ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + } + return; + } + + //crow::connections::systemBus->yield_method_call( + // yield, ec, asyncUpdateService, asyncUpdateObjPath, + // "xyz.openbmc_project.Software.AsyncUpdate", "Start"); + //if (ec) + //{ + // BMCWEB_LOG_ERROR("Error calling Start method: {}", ec.message()); + // asyncResp->res.result( + // boost::beast::http::status::internal_server_error); + // if (!commonInterface) + // { + // asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1; + // } + // return; + //} + + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 0; + } +} + +inline bool getFirmwareVersion(const boost::asio::yield_context& yield, + const std::string& fwId, std::string& version) +{ + boost::system::error_code ec; + auto response = crow::connections::systemBus + ->yield_method_call>( + yield, ec, updateService, updateObjPath + fwId, + "org.freedesktop.DBus.Properties", "Get", + updateVersionInterface, versionProperty); + if (ec) + { + BMCWEB_LOG_ERROR("Error getting Version property from {} : {}", + updateObjPath + fwId, ec.message()); + return false; + } + + const std::string* versionPtr = std::get_if(&response); + if (versionPtr == nullptr) + { + return false; + } + + version = *versionPtr; + + return true; +} + +inline void getUpdateStatus(const std::shared_ptr& asyncResp, + const std::string& target, + const bool& commonInterface = false) +{ + boost::asio::spawn(crow::connections::systemBus->get_io_context(), + [asyncResp, target, commonInterface]( + const boost::asio::yield_context& yield) { + std::string fwId; + bool fileExist = false; + std::string updateStatusflag; + std::string statusString; + uint8_t statusNumber = 1; + + getUpdateStatusFileInfo(target, fwId, updateStatusflag, fileExist); + + const bool success = + getFirmwareUpgradeStatus(yield, fwId, target, updateStatusflag, + fileExist, statusString, statusNumber); + if (!success) + { + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + if (!commonInterface) + { + asyncResp->res.jsonValue["Oem"]["Luxshare"]["UpdateStatus"] = + statusNumber; + } + else + { + nlohmann::json targetStatus; + bool peerNodeUpgrading = false; + + // 1: Failed + if (statusNumber == 1) + { + if (target == "FCBCPLD") + { + peerNodeUpgrading = isPeerNodeUpgrading( + yield, asyncResp, "FanCpldUpgradeFlag", + commonInterface); + if (fancpldLastUpgradeStatus != "Failed" && + peerNodeUpgrading) + { + statusString = "Conflicted"; + } + } + + if (asyncResp->res.result() != boost::beast::http::status::ok) + { + return; + } + } + + // 5: Interrupted + if (statusNumber == 5) + { + if (asyncResp->res.result() != boost::beast::http::status::ok) + { + return; + } + } + + updateGlobalUpdateStatus(target, statusString); + + targetStatus[target]["UpdateStatus"] = statusString; + targetStatus[target]["UpdateProgress"] = + nlohmann::json::value_t::null; + + // 2: Updating + if (statusNumber == 2) + { + boost::system::error_code ec; + auto response = + crow::connections::systemBus + ->yield_method_call>( + yield, ec, updateService, updateObjPath + fwId, + "org.freedesktop.DBus.Properties", "Get", + activationProgressInterface, progressProperty); + if (ec) + { + BMCWEB_LOG_ERROR( + "Error getting Progress property from {} : {}", + updateObjPath + fwId, ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + const uint8_t* progress = std::get_if(&response); + if (progress == nullptr) + { + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + targetStatus[target]["UpdateProgress"] = *progress; + } + + std::string version; + boost::system::error_code ec; + // 0: Updated + if (statusNumber == 0 && target != "BMC") + { + auto dbusData = + crow::connections::systemBus + ->yield_method_call( + yield, ec, updateService, + "/xyz/openbmc_project/software", + "org.freedesktop.DBus.ObjectManager", + "GetManagedObjects"); + if (ec) + { + BMCWEB_LOG_ERROR("BMCUpdater GetManagedObjects error: {}", + ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + for (const auto& object : dbusData) + { + const std::string* dbusPurpose = nullptr; + const std::string* dbusVersion = nullptr; + const std::string* dbusActivation = nullptr; + for (const auto& interface : object.second) + { + if (interface.first == updateVersionInterface) + { + for (const auto& property : interface.second) + { + if (property.first == "Purpose") + { + dbusPurpose = std::get_if( + &property.second); + } + if (property.first == "Version") + { + dbusVersion = std::get_if( + &property.second); + } + } + } + else if (interface.first == updateActivationInterface) + { + for (const auto& property : interface.second) + { + if (property.first == "Activation") + { + dbusActivation = std::get_if( + &property.second); + break; + } + } + } + + if (dbusPurpose != nullptr && dbusVersion != nullptr && + dbusActivation != nullptr) + { + break; + } + } + + if (dbusPurpose != nullptr && dbusVersion != nullptr && + dbusActivation != nullptr) + { + std::string purpose = dbusPurpose->substr( + dbusPurpose->find_last_of('.') + 1); + if (dbusActivation->ends_with("Active") && + purposeToTarget.find(purpose) != + purposeToTarget.end() && + purposeToTarget.at(purpose) == target) + { + version = *dbusVersion; + break; + } + } + } + } + else + { + auto response = + crow::connections::systemBus + ->yield_method_call>( + yield, ec, updateService, updateObjPath + fwId, + "org.freedesktop.DBus.Properties", "Get", + updateVersionInterface, versionProperty); + if (ec) + { + BMCWEB_LOG_ERROR( + "Error getting Version property from {} : {}", + updateObjPath + fwId, ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + const std::string* versionPtr = + std::get_if(&response); + if (versionPtr == nullptr) + { + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + version = *versionPtr; + } + + if (version.empty()) + { + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + targetStatus[target]["BothUpdateEnable"] = true; + targetStatus[target]["VersionStatus"] = version; + asyncResp->res.jsonValue["Oem"]["Luxshare"].push_back( + targetStatus); + } + }); +} + +inline void + syncUpdateStatusHandler(const crow::Request& /*unused*/, + const std::shared_ptr& asyncResp, + const std::string& target, + const bool& commonInterface = false) +{ + getUpdateStatus(asyncResp, target, commonInterface); +} + +inline void asyncUpdateStatusHandler( + const crow::Request& /*unused*/, + const std::shared_ptr& asyncResp, + const std::string& target, const bool& commonInterface = false) +{ + getUpdateStatus(asyncResp, target, commonInterface); +} + +inline void handlePostFirmwareUpdate( + const crow::Request& req, + const std::shared_ptr& asyncResp) +{ + std::string target; + std::optional action; + std::string targetSlot; + + asyncResp->res.result(boost::beast::http::status::no_content); + + if (!redfish::json_util::readJsonPatch(req, asyncResp->res, "Target", + target, "Action", action)) + { + BMCWEB_LOG_ERROR("readJsonPatch error"); + return; + } + + // If the target is BMC, first clarify the Slot to be upgraded. + if (target == "BMC") + { + targetSlot = "Primary"; + } + else if (target == "BMC_Secondary") + { + targetSlot = "Secondary"; + target = "BMC"; + } + + if (target == "BIOS") + { + targetSlot = "Primary"; + } + else if (target == "BIOS_Secondary") + { + targetSlot = "Secondary"; + target = "BIOS"; + } + + // When the target does not exist, return 404 Not Found. + if (firmwareTypeAndId.find(target) == firmwareTypeAndId.end() && + target != "All") + { + BMCWEB_LOG_ERROR("target property error."); + asyncResp->res.result(boost::beast::http::status::not_found); + return; + } + + if (action == std::nullopt) + { + action = "Keep"; + } + + std::tuple updateStrategy = + std::make_tuple(*action, targetSlot); + + boost::asio::spawn(crow::connections::systemBus->get_io_context(), + [asyncResp, target, action, updateStrategy]( + const boost::asio::yield_context& yield) mutable { + if (target == "All") + { + // When the upgrade list includes BMC and the target is All, + auto bmcIter = firmwareTypeAndId.find("BMC"); + if (bmcIter != firmwareTypeAndId.end()) + { + bool isBmcUpdated = isUpgradeCompleted(yield, bmcIter->first); + + if (!isBmcUpdated) + { + bool allowedBmcUpgrade = true; + for (const auto& [fwType, fwId] : firmwareTypeAndId) + { + if (((fwType != "BMC") && + (!isUpgradeCompleted(yield, fwType)))) + { + allowedBmcUpgrade = false; + break; + } + } + + if (!allowedBmcUpgrade) + { + // When the upgrade list includes BMC and other firmware + // in the upgrade, 405 Method Not Allowed is returned. + BMCWEB_LOG_ERROR( + " when targent is All,BMC is not allowed."); + asyncResp->res.result( + boost::beast::http::status::method_not_allowed); + return; + } + + // When the upgrade list only contains upgradeable BMC and + // the target is All, the primary and secondary partitions + // are upgraded. + updateStrategy = {*action, "Both"}; + } + } + + bool existUploadedFirmware = false; + for (const auto& [fwType, fwId] : firmwareTypeAndId) + { + if (isUploadedStatus(yield, fwType)) + { + existUploadedFirmware = true; + if (fwType == "BMC") + { + syncUpdateImageHandler(yield, asyncResp, fwType, true, + updateStrategy); + } + else if(fwType == "BIOS") + { + updateStrategy = {*action, "Both"}; + asyncUpdateImageHandler(yield, asyncResp, fwType, true, + updateStrategy); + } + else + { + asyncUpdateImageHandler(yield, asyncResp, fwType, true, + updateStrategy); + } + + if (asyncResp->res.result() != + boost::beast::http::status::no_content) + { + return; + } + } + } + + if (!existUploadedFirmware) + { + BMCWEB_LOG_ERROR(" Uploaded target not exist."); + asyncResp->res.result(boost::beast::http::status::not_found); + return; + } + } + else + { + if (!isUploadedStatus(yield, target)) + { + BMCWEB_LOG_ERROR(" Uploaded target not exist."); + asyncResp->res.result(boost::beast::http::status::not_found); + return; + } + if (target == "BMC") + { + syncUpdateImageHandler(yield, asyncResp, target, true, + updateStrategy); + } + else + { + asyncUpdateImageHandler(yield, asyncResp, target, true, + updateStrategy); + } + } + }); +} + +inline void + deleteSpecifiedFirmware(const std::shared_ptr& asyncResp, + const std::string& fwId, const std::string& target) +{ + crow::connections::systemBus->async_method_call( + [asyncResp, target](const boost::system::error_code& ec) { + // After the upgrade fails, the corresponding dbus path may + // have been deleted and no longer exists. In this case, + // calling the Delete method will return an + // "Invalid request descriptor" error code. + // The corresponding Linux code of this error code is + // "EBADR". + if (ec && ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("Error calling Delete method: {}", ec.message()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + firmwareTypeAndId.erase(target); + clearGlobalFirmwareId(target); + clearGlobalUpdateStatus(target); + }, + updateService, updateObjPath + fwId, deleteInterface, deleteProperty); +} + +inline bool isNotSupportedDeletion(const boost::asio::yield_context& yield, + const std::string& target) +{ + std::string updateStatusflag; + std::string tempFwId; + bool fileExist = false; + std::string statusString; + uint8_t statusNumber = 0; + std::string version; + + getUpdateStatusFileInfo(target, tempFwId, updateStatusflag, fileExist); + const bool success = getFirmwareUpgradeStatus(yield, tempFwId, target, + updateStatusflag, fileExist, + statusString, statusNumber); + if (success) + { + if (statusString == "Updating" || statusString == "Queued" || + statusString == "Updated") + { + return true; + } + } + + return false; +} + +inline void handleDeleteFirmwareUpdate( + const crow::Request& req, + const std::shared_ptr& asyncResp) +{ + std::string target; + auto fwTypeAndStatus = + std::make_shared>(); + + asyncResp->res.result(boost::beast::http::status::no_content); + + if (!redfish::json_util::readJsonPatch(req, asyncResp->res, "Target", + target)) + { + BMCWEB_LOG_ERROR("readJsonPatch error"); + return; + } + + if (firmwareTypeAndId.find(target) == firmwareTypeAndId.end() && + target != "All") + { + BMCWEB_LOG_ERROR("target property error."); + asyncResp->res.result(boost::beast::http::status::not_found); + return; + } + + boost::asio::spawn( + crow::connections::systemBus->get_io_context(), + [asyncResp, target](const boost::asio::yield_context& yield) { + if (target == "All") + { + // Get the upgrade status of all firmware in the firmware list. + for (const auto& [fwType, fwId] : firmwareTypeAndId) + { + if (isNotSupportedDeletion(yield, fwType)) + { + BMCWEB_LOG_ERROR("Status Conflict !"); + asyncResp->res.result( + boost::beast::http::status::method_not_allowed); + return; + } + } + + for (const auto& [fwType, fwId] : firmwareTypeAndId) + { + deleteSpecifiedFirmware(asyncResp, fwId, fwType); + } + } + else + { + if (isNotSupportedDeletion(yield, target)) + { + BMCWEB_LOG_ERROR("Status Conflict !"); + asyncResp->res.result( + boost::beast::http::status::method_not_allowed); + return; + } + + deleteSpecifiedFirmware(asyncResp, firmwareTypeAndId.at(target), + target); + } + }); +} + +inline bool NeedDelete(const std::string& target) +{ + std::string updateStatusflag; + std::string tempFwId; + bool fileExist = false; + + getUpdateStatusFileInfo(target, tempFwId, updateStatusflag, fileExist); + + if(tempFwId.empty()) + { + return true; + } + + return false; +} + +inline void + handleFirmwareStatus(const crow::Request& /*unused*/, + const std::shared_ptr& asyncResp) +{ + std::string target; + asyncResp->res.jsonValue["Oem"]["Luxshare"] = nlohmann::json::array(); + + for (const auto& [fwType, fwId] : firmwareTypeAndId) + { + if(NeedDelete(fwType)) + { + target = fwType; + } + } + + if(!target.empty()) + { + firmwareTypeAndId.erase(target); + } + + for (const auto& [fwType, fwId] : firmwareTypeAndId) + { + getUpdateStatus(asyncResp, fwType, true); + } +} +} // namespace luxshare_update +} // namespace crow diff --git a/meson.build b/meson.build index 3afb44e1..6ddf2be9 100644 --- a/meson.build +++ b/meson.build @@ -238,6 +238,7 @@ endif add_project_arguments( cxx.get_supported_arguments([ + '-DBOOST_ASIO_DISABLE_CONCEPTS', '-DBOOST_ALL_NO_LIB', '-DBOOST_ALLOW_DEPRECATED_HEADERS', '-DBOOST_ASIO_DISABLE_THREADS', @@ -245,11 +246,9 @@ cxx.get_supported_arguments([ '-DBOOST_ASIO_SEPARATE_COMPILATION', '-DBOOST_BEAST_SEPARATE_COMPILATION', '-DBOOST_EXCEPTION_DISABLE', - '-DBOOST_NO_EXCEPTIONS', '-DBOOST_URL_NO_SOURCE_LOCATION', '-DJSON_NOEXCEPTION', '-DOPENSSL_NO_FILENAMES', - '-DSDBUSPLUS_DISABLE_BOOST_COROUTINES', ]), language : 'cpp') @@ -307,12 +306,28 @@ if not nlohmann_json.found() endif bmcweb_dependencies += nlohmann_json -boost = dependency('boost',version : '>=1.82.0', required : false, include_type: 'system') +boost = dependency( + 'boost', + modules: [ + 'coroutine', + 'context', + ], + version : '>=1.82.0', + required : false, + include_type: 'system' +) + if not boost.found() boost = subproject('boost', required: true).get_variable('boost_dep') boost = boost.as_system('system') endif -bmcweb_dependencies += boost + +bmcweb_dependencies += [boost] +boost_coroutine = cxx.find_library('boost_coroutine', required: true) +bmcweb_dependencies += [boost_coroutine] + +pdi_dep = dependency('phosphor-dbus-interfaces') +bmcweb_dependencies += pdi_dep if get_option('tests').enabled() gtest = dependency('gtest', main: true, disabler: true, required : false) diff --git a/redfish-core/include/utils/sw_utils.hpp b/redfish-core/include/utils/sw_utils.hpp index cffc637f..0d6e2ba3 100644 --- a/redfish-core/include/utils/sw_utils.hpp +++ b/redfish-core/include/utils/sw_utils.hpp @@ -29,6 +29,8 @@ constexpr const char* biosPurpose = constexpr const char* bmcPurpose = "xyz.openbmc_project.Software.Version.VersionPurpose.BMC"; +constexpr auto bmcRuningSlot = "/run/media/slot"; + /** * @brief Populate the running software version and image links * @@ -376,5 +378,33 @@ inline void }); } +inline std::string getBMCRunningSlot() +{ + std::string slot; + std::ifstream in(bmcRuningSlot); + in >> slot; + + return slot; +} + +inline void + getSoftwareActiveSlot(const std::shared_ptr& asyncResp) +{ + const auto& activeSlotStr = getBMCRunningSlot(); + uint8_t activeSlot = 0; + + try + { + activeSlot = static_cast(std::stoi(activeSlotStr)); + } + catch (const std::exception&) + { + BMCWEB_LOG_ERROR(" bmc slot convert error"); + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["Oem"]["Luxshare"]["ActiveSlot"] = activeSlot; +} + } // namespace sw_util } // namespace redfish diff --git a/src/webserver_main.cpp b/src/webserver_main.cpp index 0008ad52..f4f43254 100644 --- a/src/webserver_main.cpp +++ b/src/webserver_main.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -120,6 +121,7 @@ static int run() #ifdef BMCWEB_ENABLE_LUXSHARE_API crow::luxshare_sel_api::requestRoutes(app); + crow::luxshare_update_firmware::requestRoutes(app); #endif if (bmcwebInsecureDisableXssPrevention != 0) -- 2.25.1