1*40e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0 2*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation 4729dae72SJennifer Lee #pragma once 5729dae72SJennifer Lee 6d61e5194STejas Patil #include "bmcweb_config.h" 7d61e5194STejas Patil 83ccb3adbSEd Tanous #include "app.hpp" 93ccb3adbSEd Tanous #include "dbus_utility.hpp" 105b90429aSEd Tanous #include "error_messages.hpp" 11757178a5SEd Tanous #include "generated/enums/update_service.hpp" 120ed80c8cSGeorge Liu #include "multipart_parser.hpp" 132c6ffdb0SEd Tanous #include "ossl_random.hpp" 143ccb3adbSEd Tanous #include "query.hpp" 153ccb3adbSEd Tanous #include "registries/privilege_registry.hpp" 16a8e884fcSEd Tanous #include "task.hpp" 175b90429aSEd Tanous #include "task_messages.hpp" 1808d81adaSJohn Edward Broadbent #include "utils/collection.hpp" 193ccb3adbSEd Tanous #include "utils/dbus_utils.hpp" 205b90429aSEd Tanous #include "utils/json_utils.hpp" 213ccb3adbSEd Tanous #include "utils/sw_utils.hpp" 223ccb3adbSEd Tanous 23de0c960cSJagpal Singh Gill #include <sys/mman.h> 24de0c960cSJagpal Singh Gill 25e99073f5SGeorge Liu #include <boost/system/error_code.hpp> 26ef4c65b7SEd Tanous #include <boost/url/format.hpp> 271e1e598dSJonathan Doman #include <sdbusplus/asio/property.hpp> 283ccb3adbSEd Tanous #include <sdbusplus/bus/match.hpp> 29d1bde9e5SKrzysztof Grobelny #include <sdbusplus/unpack_properties.hpp> 301214b7e7SGunnar Mills 312b73119cSGeorge Liu #include <array> 32de0c960cSJagpal Singh Gill #include <cstddef> 330ed80c8cSGeorge Liu #include <filesystem> 34c71b6c99SJagpal Singh Gill #include <functional> 35de0c960cSJagpal Singh Gill #include <iterator> 36ef93eab3SJagpal Singh Gill #include <memory> 377cb59f65SEd Tanous #include <optional> 387cb59f65SEd Tanous #include <string> 392b73119cSGeorge Liu #include <string_view> 40de0c960cSJagpal Singh Gill #include <unordered_map> 41ef93eab3SJagpal Singh Gill #include <vector> 422b73119cSGeorge Liu 431abe55efSEd Tanous namespace redfish 441abe55efSEd Tanous { 4527826b5fSEd Tanous 460e7de46fSAndrew Geissler // Match signals added on software path 47cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 4859d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher; 49cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 5059d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher; 510e7de46fSAndrew Geissler // Only allow one update at a time 52cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 530e7de46fSAndrew Geissler static bool fwUpdateInProgress = false; 5486adcd6dSAndrew Geissler // Timer for software available 55cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 56271584abSEd Tanous static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer; 5786adcd6dSAndrew Geissler 58de0c960cSJagpal Singh Gill struct MemoryFileDescriptor 59de0c960cSJagpal Singh Gill { 60de0c960cSJagpal Singh Gill int fd = -1; 61de0c960cSJagpal Singh Gill 62de0c960cSJagpal Singh Gill explicit MemoryFileDescriptor(const std::string& filename) : 63de0c960cSJagpal Singh Gill fd(memfd_create(filename.c_str(), 0)) 64de0c960cSJagpal Singh Gill {} 65de0c960cSJagpal Singh Gill 66de0c960cSJagpal Singh Gill MemoryFileDescriptor(const MemoryFileDescriptor&) = default; 67de0c960cSJagpal Singh Gill MemoryFileDescriptor(MemoryFileDescriptor&& other) noexcept : fd(other.fd) 68de0c960cSJagpal Singh Gill { 69de0c960cSJagpal Singh Gill other.fd = -1; 70de0c960cSJagpal Singh Gill } 71de0c960cSJagpal Singh Gill MemoryFileDescriptor& operator=(const MemoryFileDescriptor&) = delete; 72de0c960cSJagpal Singh Gill MemoryFileDescriptor& operator=(MemoryFileDescriptor&&) = default; 73de0c960cSJagpal Singh Gill 74de0c960cSJagpal Singh Gill ~MemoryFileDescriptor() 75de0c960cSJagpal Singh Gill { 76de0c960cSJagpal Singh Gill if (fd != -1) 77de0c960cSJagpal Singh Gill { 78de0c960cSJagpal Singh Gill close(fd); 79de0c960cSJagpal Singh Gill } 80de0c960cSJagpal Singh Gill } 81de0c960cSJagpal Singh Gill 82de0c960cSJagpal Singh Gill bool rewind() const 83de0c960cSJagpal Singh Gill { 84de0c960cSJagpal Singh Gill if (lseek(fd, 0, SEEK_SET) == -1) 85de0c960cSJagpal Singh Gill { 86de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to seek to beginning of image memfd"); 87de0c960cSJagpal Singh Gill return false; 88de0c960cSJagpal Singh Gill } 89de0c960cSJagpal Singh Gill return true; 90de0c960cSJagpal Singh Gill } 91de0c960cSJagpal Singh Gill }; 92de0c960cSJagpal Singh Gill 93df254f2cSEd Tanous inline void cleanUp() 9486adcd6dSAndrew Geissler { 9586adcd6dSAndrew Geissler fwUpdateInProgress = false; 9686adcd6dSAndrew Geissler fwUpdateMatcher = nullptr; 974cde5d90SJames Feist fwUpdateErrorMatcher = nullptr; 9886adcd6dSAndrew Geissler } 99df254f2cSEd Tanous 100df254f2cSEd Tanous inline void activateImage(const std::string& objPath, 10186adcd6dSAndrew Geissler const std::string& service) 10286adcd6dSAndrew Geissler { 10362598e31SEd Tanous BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service); 1049ae226faSGeorge Liu sdbusplus::asio::setProperty( 1059ae226faSGeorge Liu *crow::connections::systemBus, service, objPath, 1069ae226faSGeorge Liu "xyz.openbmc_project.Software.Activation", "RequestedActivation", 1079ae226faSGeorge Liu "xyz.openbmc_project.Software.Activation.RequestedActivations.Active", 1088b24275dSEd Tanous [](const boost::system::error_code& ec) { 1098b24275dSEd Tanous if (ec) 11086adcd6dSAndrew Geissler { 11162598e31SEd Tanous BMCWEB_LOG_DEBUG("error_code = {}", ec); 11262598e31SEd Tanous BMCWEB_LOG_DEBUG("error msg = {}", ec.message()); 11386adcd6dSAndrew Geissler } 1149ae226faSGeorge Liu }); 11586adcd6dSAndrew Geissler } 1160554c984SAndrew Geissler 117c71b6c99SJagpal Singh Gill inline bool handleCreateTask(const boost::system::error_code& ec2, 118c71b6c99SJagpal Singh Gill sdbusplus::message_t& msg, 119c71b6c99SJagpal Singh Gill const std::shared_ptr<task::TaskData>& taskData) 120c71b6c99SJagpal Singh Gill { 121c71b6c99SJagpal Singh Gill if (ec2) 122c71b6c99SJagpal Singh Gill { 123c71b6c99SJagpal Singh Gill return task::completed; 124c71b6c99SJagpal Singh Gill } 125c71b6c99SJagpal Singh Gill 126c71b6c99SJagpal Singh Gill std::string iface; 127c71b6c99SJagpal Singh Gill dbus::utility::DBusPropertiesMap values; 128c71b6c99SJagpal Singh Gill 129c71b6c99SJagpal Singh Gill std::string index = std::to_string(taskData->index); 130c71b6c99SJagpal Singh Gill msg.read(iface, values); 131c71b6c99SJagpal Singh Gill 132c71b6c99SJagpal Singh Gill if (iface == "xyz.openbmc_project.Software.Activation") 133c71b6c99SJagpal Singh Gill { 134c71b6c99SJagpal Singh Gill const std::string* state = nullptr; 135c71b6c99SJagpal Singh Gill for (const auto& property : values) 136c71b6c99SJagpal Singh Gill { 137c71b6c99SJagpal Singh Gill if (property.first == "Activation") 138c71b6c99SJagpal Singh Gill { 139c71b6c99SJagpal Singh Gill state = std::get_if<std::string>(&property.second); 140c71b6c99SJagpal Singh Gill if (state == nullptr) 141c71b6c99SJagpal Singh Gill { 142c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::internalError()); 143c71b6c99SJagpal Singh Gill return task::completed; 144c71b6c99SJagpal Singh Gill } 145c71b6c99SJagpal Singh Gill } 146c71b6c99SJagpal Singh Gill } 147c71b6c99SJagpal Singh Gill 148c71b6c99SJagpal Singh Gill if (state == nullptr) 149c71b6c99SJagpal Singh Gill { 150c71b6c99SJagpal Singh Gill return !task::completed; 151c71b6c99SJagpal Singh Gill } 152c71b6c99SJagpal Singh Gill 153c71b6c99SJagpal Singh Gill if (state->ends_with("Invalid") || state->ends_with("Failed")) 154c71b6c99SJagpal Singh Gill { 155c71b6c99SJagpal Singh Gill taskData->state = "Exception"; 156c71b6c99SJagpal Singh Gill taskData->status = "Warning"; 157c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskAborted(index)); 158c71b6c99SJagpal Singh Gill return task::completed; 159c71b6c99SJagpal Singh Gill } 160c71b6c99SJagpal Singh Gill 161c71b6c99SJagpal Singh Gill if (state->ends_with("Staged")) 162c71b6c99SJagpal Singh Gill { 163c71b6c99SJagpal Singh Gill taskData->state = "Stopping"; 164c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskPaused(index)); 165c71b6c99SJagpal Singh Gill 166c71b6c99SJagpal Singh Gill // its staged, set a long timer to 167c71b6c99SJagpal Singh Gill // allow them time to complete the 168c71b6c99SJagpal Singh Gill // update (probably cycle the 169c71b6c99SJagpal Singh Gill // system) if this expires then 170c71b6c99SJagpal Singh Gill // task will be canceled 171c71b6c99SJagpal Singh Gill taskData->extendTimer(std::chrono::hours(5)); 172c71b6c99SJagpal Singh Gill return !task::completed; 173c71b6c99SJagpal Singh Gill } 174c71b6c99SJagpal Singh Gill 175c71b6c99SJagpal Singh Gill if (state->ends_with("Active")) 176c71b6c99SJagpal Singh Gill { 177c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskCompletedOK(index)); 178c71b6c99SJagpal Singh Gill taskData->state = "Completed"; 179c71b6c99SJagpal Singh Gill return task::completed; 180c71b6c99SJagpal Singh Gill } 181c71b6c99SJagpal Singh Gill } 182c71b6c99SJagpal Singh Gill else if (iface == "xyz.openbmc_project.Software.ActivationProgress") 183c71b6c99SJagpal Singh Gill { 184c71b6c99SJagpal Singh Gill const uint8_t* progress = nullptr; 185c71b6c99SJagpal Singh Gill for (const auto& property : values) 186c71b6c99SJagpal Singh Gill { 187c71b6c99SJagpal Singh Gill if (property.first == "Progress") 188c71b6c99SJagpal Singh Gill { 189c71b6c99SJagpal Singh Gill progress = std::get_if<uint8_t>(&property.second); 190c71b6c99SJagpal Singh Gill if (progress == nullptr) 191c71b6c99SJagpal Singh Gill { 192c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::internalError()); 193c71b6c99SJagpal Singh Gill return task::completed; 194c71b6c99SJagpal Singh Gill } 195c71b6c99SJagpal Singh Gill } 196c71b6c99SJagpal Singh Gill } 197c71b6c99SJagpal Singh Gill 198c71b6c99SJagpal Singh Gill if (progress == nullptr) 199c71b6c99SJagpal Singh Gill { 200c71b6c99SJagpal Singh Gill return !task::completed; 201c71b6c99SJagpal Singh Gill } 202c71b6c99SJagpal Singh Gill taskData->percentComplete = *progress; 203c71b6c99SJagpal Singh Gill taskData->messages.emplace_back( 204c71b6c99SJagpal Singh Gill messages::taskProgressChanged(index, *progress)); 205c71b6c99SJagpal Singh Gill 206c71b6c99SJagpal Singh Gill // if we're getting status updates it's 207c71b6c99SJagpal Singh Gill // still alive, update timer 208c71b6c99SJagpal Singh Gill taskData->extendTimer(std::chrono::minutes(5)); 209c71b6c99SJagpal Singh Gill } 210c71b6c99SJagpal Singh Gill 211c71b6c99SJagpal Singh Gill // as firmware update often results in a 212c71b6c99SJagpal Singh Gill // reboot, the task may never "complete" 213c71b6c99SJagpal Singh Gill // unless it is an error 214c71b6c99SJagpal Singh Gill 215c71b6c99SJagpal Singh Gill return !task::completed; 216c71b6c99SJagpal Singh Gill } 217c71b6c99SJagpal Singh Gill 218c71b6c99SJagpal Singh Gill inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 219c71b6c99SJagpal Singh Gill task::Payload&& payload, 220c71b6c99SJagpal Singh Gill const sdbusplus::message::object_path& objPath) 221c71b6c99SJagpal Singh Gill { 222c71b6c99SJagpal Singh Gill std::shared_ptr<task::TaskData> task = task::TaskData::createTask( 223c71b6c99SJagpal Singh Gill std::bind_front(handleCreateTask), 224c71b6c99SJagpal Singh Gill "type='signal',interface='org.freedesktop.DBus.Properties'," 225c71b6c99SJagpal Singh Gill "member='PropertiesChanged',path='" + 226c71b6c99SJagpal Singh Gill objPath.str + "'"); 227c71b6c99SJagpal Singh Gill task->startTimer(std::chrono::minutes(5)); 228c71b6c99SJagpal Singh Gill task->populateResp(asyncResp->res); 229c71b6c99SJagpal Singh Gill task->payload.emplace(std::move(payload)); 230c71b6c99SJagpal Singh Gill } 231c71b6c99SJagpal Singh Gill 2320554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr 2330554c984SAndrew Geissler // then no asyncResp updates will occur 2344ff0f1f4SEd Tanous inline void 2358d1b46d7Szhanghch05 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 23659d494eeSPatrick Williams sdbusplus::message_t& m, task::Payload&& payload) 23786adcd6dSAndrew Geissler { 23880f79a40SMichael Shen dbus::utility::DBusInterfacesMap interfacesProperties; 23986adcd6dSAndrew Geissler 24086adcd6dSAndrew Geissler sdbusplus::message::object_path objPath; 24186adcd6dSAndrew Geissler 24286adcd6dSAndrew Geissler m.read(objPath, interfacesProperties); 24386adcd6dSAndrew Geissler 24462598e31SEd Tanous BMCWEB_LOG_DEBUG("obj path = {}", objPath.str); 245e3eb3d63SEd Tanous for (const auto& interface : interfacesProperties) 24686adcd6dSAndrew Geissler { 24762598e31SEd Tanous BMCWEB_LOG_DEBUG("interface = {}", interface.first); 24886adcd6dSAndrew Geissler 24986adcd6dSAndrew Geissler if (interface.first == "xyz.openbmc_project.Software.Activation") 25086adcd6dSAndrew Geissler { 25186adcd6dSAndrew Geissler // Retrieve service and activate 2522b73119cSGeorge Liu constexpr std::array<std::string_view, 1> interfaces = { 2532b73119cSGeorge Liu "xyz.openbmc_project.Software.Activation"}; 2542b73119cSGeorge Liu dbus::utility::getDbusObject( 2552b73119cSGeorge Liu objPath.str, interfaces, 256a3e65892SEd Tanous [objPath, asyncResp, payload(std::move(payload))]( 2578b24275dSEd Tanous const boost::system::error_code& ec, 258a3e65892SEd Tanous const std::vector< 259a3e65892SEd Tanous std::pair<std::string, std::vector<std::string>>>& 260a3e65892SEd Tanous objInfo) mutable { 2618b24275dSEd Tanous if (ec) 26286adcd6dSAndrew Geissler { 26362598e31SEd Tanous BMCWEB_LOG_DEBUG("error_code = {}", ec); 26462598e31SEd Tanous BMCWEB_LOG_DEBUG("error msg = {}", ec.message()); 2650554c984SAndrew Geissler if (asyncResp) 2660554c984SAndrew Geissler { 26786adcd6dSAndrew Geissler messages::internalError(asyncResp->res); 2680554c984SAndrew Geissler } 26986adcd6dSAndrew Geissler cleanUp(); 27086adcd6dSAndrew Geissler return; 27186adcd6dSAndrew Geissler } 27286adcd6dSAndrew Geissler // Ensure we only got one service back 27386adcd6dSAndrew Geissler if (objInfo.size() != 1) 27486adcd6dSAndrew Geissler { 275bd79bce8SPatrick Williams BMCWEB_LOG_ERROR("Invalid Object Size {}", 276bd79bce8SPatrick Williams objInfo.size()); 2770554c984SAndrew Geissler if (asyncResp) 2780554c984SAndrew Geissler { 27986adcd6dSAndrew Geissler messages::internalError(asyncResp->res); 2800554c984SAndrew Geissler } 28186adcd6dSAndrew Geissler cleanUp(); 28286adcd6dSAndrew Geissler return; 28386adcd6dSAndrew Geissler } 28486adcd6dSAndrew Geissler // cancel timer only when 28586adcd6dSAndrew Geissler // xyz.openbmc_project.Software.Activation interface 28686adcd6dSAndrew Geissler // is added 28786adcd6dSAndrew Geissler fwAvailableTimer = nullptr; 28886adcd6dSAndrew Geissler 28986adcd6dSAndrew Geissler activateImage(objPath.str, objInfo[0].first); 2900554c984SAndrew Geissler if (asyncResp) 2910554c984SAndrew Geissler { 292c71b6c99SJagpal Singh Gill createTask(asyncResp, std::move(payload), objPath); 2930554c984SAndrew Geissler } 29486adcd6dSAndrew Geissler fwUpdateInProgress = false; 2952b73119cSGeorge Liu }); 29662bafc01SPatrick Williams 29762bafc01SPatrick Williams break; 29886adcd6dSAndrew Geissler } 29986adcd6dSAndrew Geissler } 30086adcd6dSAndrew Geissler } 30186adcd6dSAndrew Geissler 3028549b951SMyung Bae inline void afterAvailbleTimerAsyncWait( 3038549b951SMyung Bae const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3048549b951SMyung Bae const boost::system::error_code& ec) 3058549b951SMyung Bae { 3068549b951SMyung Bae cleanUp(); 3078549b951SMyung Bae if (ec == boost::asio::error::operation_aborted) 3088549b951SMyung Bae { 3098549b951SMyung Bae // expected, we were canceled before the timer completed. 3108549b951SMyung Bae return; 3118549b951SMyung Bae } 3128549b951SMyung Bae BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created"); 3138549b951SMyung Bae BMCWEB_LOG_ERROR("FW image may has already been uploaded to server"); 3148549b951SMyung Bae if (ec) 3158549b951SMyung Bae { 3168549b951SMyung Bae BMCWEB_LOG_ERROR("Async_wait failed{}", ec); 3178549b951SMyung Bae return; 3188549b951SMyung Bae } 3198549b951SMyung Bae if (asyncResp) 3208549b951SMyung Bae { 3218549b951SMyung Bae redfish::messages::internalError(asyncResp->res); 3228549b951SMyung Bae } 3238549b951SMyung Bae } 3248549b951SMyung Bae 3258549b951SMyung Bae inline void 3268549b951SMyung Bae handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 32748fb20b9SEd Tanous const std::string& url, const std::string& type) 3288549b951SMyung Bae { 329c87294a6SEd Tanous // NOLINTBEGIN(bugprone-branch-clone) 3308549b951SMyung Bae if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure") 3318549b951SMyung Bae { 33248fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3338549b951SMyung Bae } 3348549b951SMyung Bae else if (type == 3358549b951SMyung Bae "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure") 3368549b951SMyung Bae { 33748fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3388549b951SMyung Bae } 3398549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure") 3408549b951SMyung Bae { 34148fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3428549b951SMyung Bae } 3438549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists") 3448549b951SMyung Bae { 345c87294a6SEd Tanous messages::resourceAlreadyExists(asyncResp->res, "UpdateService", 346c87294a6SEd Tanous "Version", "uploaded version"); 3478549b951SMyung Bae } 3488549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure") 3498549b951SMyung Bae { 35048fb20b9SEd Tanous messages::serviceTemporarilyUnavailable(asyncResp->res, url); 3518549b951SMyung Bae } 3524034a652SMyung Bae else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible") 3538549b951SMyung Bae { 354c87294a6SEd Tanous messages::internalError(asyncResp->res); 3554034a652SMyung Bae } 3564034a652SMyung Bae else if (type == 3574034a652SMyung Bae "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey") 3584034a652SMyung Bae { 359c87294a6SEd Tanous messages::internalError(asyncResp->res); 3604034a652SMyung Bae } 3614034a652SMyung Bae else if (type == 3624034a652SMyung Bae "xyz.openbmc_project.Software.Version.Error.InvalidSignature") 3634034a652SMyung Bae { 36448fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3654034a652SMyung Bae } 3664034a652SMyung Bae else if (type == 3674034a652SMyung Bae "xyz.openbmc_project.Software.Image.Error.InternalFailure" || 3684034a652SMyung Bae type == "xyz.openbmc_project.Software.Version.Error.HostFile") 3694034a652SMyung Bae { 3704034a652SMyung Bae BMCWEB_LOG_ERROR("Software Image Error type={}", type); 37148fb20b9SEd Tanous messages::internalError(asyncResp->res); 3728549b951SMyung Bae } 3734034a652SMyung Bae else 3744034a652SMyung Bae { 3754034a652SMyung Bae // Unrelated error types. Ignored 3764034a652SMyung Bae BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type); 3774034a652SMyung Bae return; 3784034a652SMyung Bae } 379c87294a6SEd Tanous // NOLINTEND(bugprone-branch-clone) 3804034a652SMyung Bae // Clear the timer 3814034a652SMyung Bae fwAvailableTimer = nullptr; 3828549b951SMyung Bae } 3838549b951SMyung Bae 3848549b951SMyung Bae inline void 3858549b951SMyung Bae afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3868549b951SMyung Bae const std::string& url, sdbusplus::message_t& m) 3878549b951SMyung Bae { 38880f79a40SMichael Shen dbus::utility::DBusInterfacesMap interfacesProperties; 3898549b951SMyung Bae sdbusplus::message::object_path objPath; 3908549b951SMyung Bae m.read(objPath, interfacesProperties); 3918549b951SMyung Bae BMCWEB_LOG_DEBUG("obj path = {}", objPath.str); 3928549b951SMyung Bae for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>& 3938549b951SMyung Bae interface : interfacesProperties) 3948549b951SMyung Bae { 3958549b951SMyung Bae if (interface.first == "xyz.openbmc_project.Logging.Entry") 3968549b951SMyung Bae { 3978549b951SMyung Bae for (const std::pair<std::string, dbus::utility::DbusVariantType>& 3988549b951SMyung Bae value : interface.second) 3998549b951SMyung Bae { 4008549b951SMyung Bae if (value.first != "Message") 4018549b951SMyung Bae { 4028549b951SMyung Bae continue; 4038549b951SMyung Bae } 4048549b951SMyung Bae const std::string* type = 4058549b951SMyung Bae std::get_if<std::string>(&value.second); 4068549b951SMyung Bae if (type == nullptr) 4078549b951SMyung Bae { 4088549b951SMyung Bae // if this was our message, timeout will cover it 4098549b951SMyung Bae return; 4108549b951SMyung Bae } 4118549b951SMyung Bae handleUpdateErrorType(asyncResp, url, *type); 4128549b951SMyung Bae } 4138549b951SMyung Bae } 4148549b951SMyung Bae } 4158549b951SMyung Bae } 4168549b951SMyung Bae 4170554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr 4180554c984SAndrew Geissler // then no asyncResp updates will occur 419f5139334SEd Tanous inline void monitorForSoftwareAvailable( 4208d1b46d7Szhanghch05 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 4218d1b46d7Szhanghch05 const crow::Request& req, const std::string& url, 4225d138943SGunnar Mills int timeoutTimeSeconds = 25) 42386adcd6dSAndrew Geissler { 42486adcd6dSAndrew Geissler // Only allow one FW update at a time 425e05aec50SEd Tanous if (fwUpdateInProgress) 42686adcd6dSAndrew Geissler { 4270554c984SAndrew Geissler if (asyncResp) 4280554c984SAndrew Geissler { 42986adcd6dSAndrew Geissler messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 4300554c984SAndrew Geissler } 43186adcd6dSAndrew Geissler return; 43286adcd6dSAndrew Geissler } 43386adcd6dSAndrew Geissler 4348e8245dbSEd Tanous if (req.ioService == nullptr) 4358e8245dbSEd Tanous { 4368e8245dbSEd Tanous messages::internalError(asyncResp->res); 4378e8245dbSEd Tanous return; 4388e8245dbSEd Tanous } 4398e8245dbSEd Tanous 4400554c984SAndrew Geissler fwAvailableTimer = 441271584abSEd Tanous std::make_unique<boost::asio::steady_timer>(*req.ioService); 44286adcd6dSAndrew Geissler 443271584abSEd Tanous fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds)); 44486adcd6dSAndrew Geissler 44586adcd6dSAndrew Geissler fwAvailableTimer->async_wait( 4468549b951SMyung Bae std::bind_front(afterAvailbleTimerAsyncWait, asyncResp)); 4478549b951SMyung Bae 448a3e65892SEd Tanous task::Payload payload(req); 44959d494eeSPatrick Williams auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable { 45062598e31SEd Tanous BMCWEB_LOG_DEBUG("Match fired"); 451a3e65892SEd Tanous softwareInterfaceAdded(asyncResp, m, std::move(payload)); 45286adcd6dSAndrew Geissler }; 45386adcd6dSAndrew Geissler 45486adcd6dSAndrew Geissler fwUpdateInProgress = true; 45586adcd6dSAndrew Geissler 45659d494eeSPatrick Williams fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>( 45786adcd6dSAndrew Geissler *crow::connections::systemBus, 45886adcd6dSAndrew Geissler "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 45986adcd6dSAndrew Geissler "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 46086adcd6dSAndrew Geissler callback); 4614cde5d90SJames Feist 46259d494eeSPatrick Williams fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>( 4634cde5d90SJames Feist *crow::connections::systemBus, 464e1cc4828SBrian Ma "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 465e1cc4828SBrian Ma "member='InterfacesAdded'," 466e1cc4828SBrian Ma "path='/xyz/openbmc_project/logging'", 4678549b951SMyung Bae std::bind_front(afterUpdateErrorMatcher, asyncResp, url)); 46886adcd6dSAndrew Geissler } 469729dae72SJennifer Lee 470bd79bce8SPatrick Williams inline std::optional<boost::urls::url> parseSimpleUpdateUrl( 471bd79bce8SPatrick Williams std::string imageURI, std::optional<std::string> transferProtocol, 472f86bcc87SEd Tanous crow::Response& res) 473f86bcc87SEd Tanous { 474f86bcc87SEd Tanous if (imageURI.find("://") == std::string::npos) 475f86bcc87SEd Tanous { 476f86bcc87SEd Tanous if (imageURI.starts_with("/")) 477f86bcc87SEd Tanous { 478f86bcc87SEd Tanous messages::actionParameterValueTypeError( 479f86bcc87SEd Tanous res, imageURI, "ImageURI", "UpdateService.SimpleUpdate"); 480f86bcc87SEd Tanous return std::nullopt; 481f86bcc87SEd Tanous } 482f86bcc87SEd Tanous if (!transferProtocol) 483f86bcc87SEd Tanous { 484f86bcc87SEd Tanous messages::actionParameterValueTypeError( 485f86bcc87SEd Tanous res, imageURI, "ImageURI", "UpdateService.SimpleUpdate"); 486f86bcc87SEd Tanous return std::nullopt; 487f86bcc87SEd Tanous } 4886a37140aSEd Tanous // OpenBMC currently only supports HTTPS 4896a37140aSEd Tanous if (*transferProtocol == "HTTPS") 490e5cf777eSEd Tanous { 491e5cf777eSEd Tanous imageURI = "https://" + imageURI; 492e5cf777eSEd Tanous } 493757178a5SEd Tanous else 494f86bcc87SEd Tanous { 495f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "TransferProtocol", 496f86bcc87SEd Tanous *transferProtocol); 497f86bcc87SEd Tanous BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}", 498f86bcc87SEd Tanous *transferProtocol); 499f86bcc87SEd Tanous return std::nullopt; 500f86bcc87SEd Tanous } 501f86bcc87SEd Tanous } 502f86bcc87SEd Tanous 503f86bcc87SEd Tanous boost::system::result<boost::urls::url> url = 504f86bcc87SEd Tanous boost::urls::parse_absolute_uri(imageURI); 505f86bcc87SEd Tanous if (!url) 506f86bcc87SEd Tanous { 507f86bcc87SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI", 508f86bcc87SEd Tanous "UpdateService.SimpleUpdate"); 509f86bcc87SEd Tanous 510f86bcc87SEd Tanous return std::nullopt; 511f86bcc87SEd Tanous } 512f86bcc87SEd Tanous url->normalize(); 513f86bcc87SEd Tanous 514757178a5SEd Tanous if (url->scheme() == "tftp") 515757178a5SEd Tanous { 516757178a5SEd Tanous if (url->encoded_path().size() < 2) 517757178a5SEd Tanous { 518757178a5SEd Tanous messages::actionParameterNotSupported(res, "ImageURI", 519757178a5SEd Tanous url->buffer()); 520757178a5SEd Tanous return std::nullopt; 521757178a5SEd Tanous } 522757178a5SEd Tanous } 523e5cf777eSEd Tanous else if (url->scheme() == "https") 524e5cf777eSEd Tanous { 525e5cf777eSEd Tanous // Empty paths default to "/" 526e5cf777eSEd Tanous if (url->encoded_path().empty()) 527e5cf777eSEd Tanous { 528e5cf777eSEd Tanous url->set_encoded_path("/"); 529e5cf777eSEd Tanous } 530e5cf777eSEd Tanous } 531757178a5SEd Tanous else 532f86bcc87SEd Tanous { 533f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "ImageURI", imageURI); 534f86bcc87SEd Tanous return std::nullopt; 535f86bcc87SEd Tanous } 536757178a5SEd Tanous 537757178a5SEd Tanous if (url->encoded_path().empty()) 538f86bcc87SEd Tanous { 539757178a5SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI", 540757178a5SEd Tanous "UpdateService.SimpleUpdate"); 541f86bcc87SEd Tanous return std::nullopt; 542f86bcc87SEd Tanous } 543757178a5SEd Tanous 544757178a5SEd Tanous return *url; 545f86bcc87SEd Tanous } 546f86bcc87SEd Tanous 547e5cf777eSEd Tanous inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 548e5cf777eSEd Tanous const boost::urls::url_view_base& url) 549e5cf777eSEd Tanous { 550e5cf777eSEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI", 551e5cf777eSEd Tanous url.buffer()); 552e5cf777eSEd Tanous } 553e5cf777eSEd Tanous 554f5139334SEd Tanous inline void handleUpdateServiceSimpleUpdateAction( 555f5139334SEd Tanous crow::App& app, const crow::Request& req, 556f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 5570554c984SAndrew Geissler { 5583ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 55945ca1b86SEd Tanous { 56045ca1b86SEd Tanous return; 56145ca1b86SEd Tanous } 56245ca1b86SEd Tanous 5630554c984SAndrew Geissler std::optional<std::string> transferProtocol; 5640554c984SAndrew Geissler std::string imageURI; 5650554c984SAndrew Geissler 56662598e31SEd Tanous BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost"); 5670554c984SAndrew Geissler 5680554c984SAndrew Geissler // User can pass in both TransferProtocol and ImageURI parameters or 5694e0453b1SGunnar Mills // they can pass in just the ImageURI with the transfer protocol 5704e0453b1SGunnar Mills // embedded within it. 5710554c984SAndrew Geissler // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin 5720554c984SAndrew Geissler // 2) ImageURI:tftp://1.1.1.1/myfile.bin 5730554c984SAndrew Geissler 574afc474aeSMyung Bae if (!json_util::readJsonAction( // 575afc474aeSMyung Bae req, asyncResp->res, // 576afc474aeSMyung Bae "ImageURI", imageURI, // 577afc474aeSMyung Bae "TransferProtocol", transferProtocol // 578afc474aeSMyung Bae )) 5790554c984SAndrew Geissler { 58062598e31SEd Tanous BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter"); 5810554c984SAndrew Geissler return; 5820554c984SAndrew Geissler } 583f5139334SEd Tanous 584757178a5SEd Tanous std::optional<boost::urls::url> url = 585757178a5SEd Tanous parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res); 586757178a5SEd Tanous if (!url) 5870554c984SAndrew Geissler { 5880554c984SAndrew Geissler return; 5890554c984SAndrew Geissler } 5904e338b23SJagpal Singh Gill if (url->scheme() == "https") 591e5cf777eSEd Tanous { 592e5cf777eSEd Tanous doHttpsUpdate(asyncResp, *url); 593e5cf777eSEd Tanous } 594757178a5SEd Tanous else 595757178a5SEd Tanous { 596757178a5SEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI", 597757178a5SEd Tanous url->buffer()); 598757178a5SEd Tanous return; 599757178a5SEd Tanous } 6000554c984SAndrew Geissler 60162598e31SEd Tanous BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost"); 602729dae72SJennifer Lee } 603729dae72SJennifer Lee 6040ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body) 6050ed80c8cSGeorge Liu { 6062c6ffdb0SEd Tanous std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID()); 6072c6ffdb0SEd Tanous 60862598e31SEd Tanous BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string()); 6090ed80c8cSGeorge Liu std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | 6100ed80c8cSGeorge Liu std::ofstream::trunc); 6110ed80c8cSGeorge Liu // set the permission of the file to 640 612bd79bce8SPatrick Williams std::filesystem::perms permission = 613bd79bce8SPatrick Williams std::filesystem::perms::owner_read | std::filesystem::perms::group_read; 6140ed80c8cSGeorge Liu std::filesystem::permissions(filepath, permission); 6150ed80c8cSGeorge Liu out << body; 6160ed80c8cSGeorge Liu 6170ed80c8cSGeorge Liu if (out.bad()) 6180ed80c8cSGeorge Liu { 6190ed80c8cSGeorge Liu messages::internalError(res); 6200ed80c8cSGeorge Liu cleanUp(); 6210ed80c8cSGeorge Liu } 6220ed80c8cSGeorge Liu } 6230ed80c8cSGeorge Liu 624de0c960cSJagpal Singh Gill // Convert the Request Apply Time to the D-Bus value 625de0c960cSJagpal Singh Gill inline bool convertApplyTime(crow::Response& res, const std::string& applyTime, 626de0c960cSJagpal Singh Gill std::string& applyTimeNewVal) 627de0c960cSJagpal Singh Gill { 628de0c960cSJagpal Singh Gill if (applyTime == "Immediate") 629de0c960cSJagpal Singh Gill { 630de0c960cSJagpal Singh Gill applyTimeNewVal = 631049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate"; 632de0c960cSJagpal Singh Gill } 633de0c960cSJagpal Singh Gill else if (applyTime == "OnReset") 634de0c960cSJagpal Singh Gill { 635de0c960cSJagpal Singh Gill applyTimeNewVal = 636049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset"; 637de0c960cSJagpal Singh Gill } 638de0c960cSJagpal Singh Gill else 639de0c960cSJagpal Singh Gill { 640de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING( 641de0c960cSJagpal Singh Gill "ApplyTime value {} is not in the list of acceptable values", 642de0c960cSJagpal Singh Gill applyTime); 643de0c960cSJagpal Singh Gill messages::propertyValueNotInList(res, applyTime, "ApplyTime"); 644de0c960cSJagpal Singh Gill return false; 645de0c960cSJagpal Singh Gill } 646de0c960cSJagpal Singh Gill return true; 647de0c960cSJagpal Singh Gill } 648de0c960cSJagpal Singh Gill 6490ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 6500ed80c8cSGeorge Liu const std::string& applyTime) 6510ed80c8cSGeorge Liu { 6520ed80c8cSGeorge Liu std::string applyTimeNewVal; 653049079f6SJagpal Singh Gill if (!convertApplyTime(asyncResp->res, applyTime, applyTimeNewVal)) 6540ed80c8cSGeorge Liu { 6550ed80c8cSGeorge Liu return; 6560ed80c8cSGeorge Liu } 6570ed80c8cSGeorge Liu 658e93abac6SGinu George setDbusProperty(asyncResp, "ApplyTime", "xyz.openbmc_project.Settings", 659d02aad39SEd Tanous sdbusplus::message::object_path( 660d02aad39SEd Tanous "/xyz/openbmc_project/software/apply_time"), 661d02aad39SEd Tanous "xyz.openbmc_project.Software.ApplyTime", 662e93abac6SGinu George "RequestedApplyTime", applyTimeNewVal); 6630ed80c8cSGeorge Liu } 6640ed80c8cSGeorge Liu 665ef93eab3SJagpal Singh Gill struct MultiPartUpdateParameters 6660ed80c8cSGeorge Liu { 667ef93eab3SJagpal Singh Gill std::optional<std::string> applyTime; 668ef93eab3SJagpal Singh Gill std::string uploadData; 669de0c960cSJagpal Singh Gill std::vector<std::string> targets; 670ef93eab3SJagpal Singh Gill }; 671ef93eab3SJagpal Singh Gill 672de0c960cSJagpal Singh Gill inline std::optional<std::string> 673de0c960cSJagpal Singh Gill processUrl(boost::system::result<boost::urls::url_view>& url) 674de0c960cSJagpal Singh Gill { 675de0c960cSJagpal Singh Gill if (!url) 676de0c960cSJagpal Singh Gill { 677de0c960cSJagpal Singh Gill return std::nullopt; 678de0c960cSJagpal Singh Gill } 679de0c960cSJagpal Singh Gill if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers", 680de0c960cSJagpal Singh Gill BMCWEB_REDFISH_MANAGER_URI_NAME)) 681de0c960cSJagpal Singh Gill { 682de0c960cSJagpal Singh Gill return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME)); 683de0c960cSJagpal Singh Gill } 684de0c960cSJagpal Singh Gill if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 685de0c960cSJagpal Singh Gill { 686de0c960cSJagpal Singh Gill return std::nullopt; 687de0c960cSJagpal Singh Gill } 688de0c960cSJagpal Singh Gill std::string firmwareId; 689de0c960cSJagpal Singh Gill if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService", 690de0c960cSJagpal Singh Gill "FirmwareInventory", 691de0c960cSJagpal Singh Gill std::ref(firmwareId))) 692de0c960cSJagpal Singh Gill { 693de0c960cSJagpal Singh Gill return std::nullopt; 694de0c960cSJagpal Singh Gill } 695de0c960cSJagpal Singh Gill 696de0c960cSJagpal Singh Gill return std::make_optional(firmwareId); 697de0c960cSJagpal Singh Gill } 698de0c960cSJagpal Singh Gill 699ef93eab3SJagpal Singh Gill inline std::optional<MultiPartUpdateParameters> 700ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters( 701ef93eab3SJagpal Singh Gill const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 702ef93eab3SJagpal Singh Gill MultipartParser parser) 703ef93eab3SJagpal Singh Gill { 704ef93eab3SJagpal Singh Gill MultiPartUpdateParameters multiRet; 705ef93eab3SJagpal Singh Gill for (FormPart& formpart : parser.mime_fields) 7060ed80c8cSGeorge Liu { 7070ed80c8cSGeorge Liu boost::beast::http::fields::const_iterator it = 7080ed80c8cSGeorge Liu formpart.fields.find("Content-Disposition"); 7090ed80c8cSGeorge Liu if (it == formpart.fields.end()) 7100ed80c8cSGeorge Liu { 71162598e31SEd Tanous BMCWEB_LOG_ERROR("Couldn't find Content-Disposition"); 712ef93eab3SJagpal Singh Gill return std::nullopt; 7130ed80c8cSGeorge Liu } 71462598e31SEd Tanous BMCWEB_LOG_INFO("Parsing value {}", it->value()); 7150ed80c8cSGeorge Liu 7160ed80c8cSGeorge Liu // The construction parameters of param_list must start with `;` 7170ed80c8cSGeorge Liu size_t index = it->value().find(';'); 7180ed80c8cSGeorge Liu if (index == std::string::npos) 7190ed80c8cSGeorge Liu { 7200ed80c8cSGeorge Liu continue; 7210ed80c8cSGeorge Liu } 7220ed80c8cSGeorge Liu 72389492a15SPatrick Williams for (const auto& param : 7240ed80c8cSGeorge Liu boost::beast::http::param_list{it->value().substr(index)}) 7250ed80c8cSGeorge Liu { 7260ed80c8cSGeorge Liu if (param.first != "name" || param.second.empty()) 7270ed80c8cSGeorge Liu { 7280ed80c8cSGeorge Liu continue; 7290ed80c8cSGeorge Liu } 7300ed80c8cSGeorge Liu 7310ed80c8cSGeorge Liu if (param.second == "UpdateParameters") 7320ed80c8cSGeorge Liu { 733ef93eab3SJagpal Singh Gill std::vector<std::string> tempTargets; 734bd79bce8SPatrick Williams nlohmann::json content = 735bd79bce8SPatrick Williams nlohmann::json::parse(formpart.content, nullptr, false); 736ac1e1246SEd Tanous if (content.is_discarded()) 737ac1e1246SEd Tanous { 738ac1e1246SEd Tanous return std::nullopt; 739ac1e1246SEd Tanous } 7407cb59f65SEd Tanous nlohmann::json::object_t* obj = 7417cb59f65SEd Tanous content.get_ptr<nlohmann::json::object_t*>(); 7427cb59f65SEd Tanous if (obj == nullptr) 7437cb59f65SEd Tanous { 744ef93eab3SJagpal Singh Gill messages::propertyValueTypeError( 745ef93eab3SJagpal Singh Gill asyncResp->res, formpart.content, "UpdateParameters"); 746ef93eab3SJagpal Singh Gill return std::nullopt; 7477cb59f65SEd Tanous } 7487cb59f65SEd Tanous 749afc474aeSMyung Bae if (!json_util::readJsonObject( // 750afc474aeSMyung Bae *obj, asyncResp->res, // 751afc474aeSMyung Bae "@Redfish.OperationApplyTime", multiRet.applyTime, // 752afc474aeSMyung Bae "Targets", tempTargets // 753afc474aeSMyung Bae )) 7540ed80c8cSGeorge Liu { 755ef93eab3SJagpal Singh Gill return std::nullopt; 7560ed80c8cSGeorge Liu } 757ef93eab3SJagpal Singh Gill 758ef93eab3SJagpal Singh Gill for (size_t urlIndex = 0; urlIndex < tempTargets.size(); 759ef93eab3SJagpal Singh Gill urlIndex++) 7600ed80c8cSGeorge Liu { 761ef93eab3SJagpal Singh Gill const std::string& target = tempTargets[urlIndex]; 762ef93eab3SJagpal Singh Gill boost::system::result<boost::urls::url_view> url = 763ef93eab3SJagpal Singh Gill boost::urls::parse_origin_form(target); 764de0c960cSJagpal Singh Gill auto res = processUrl(url); 765de0c960cSJagpal Singh Gill if (!res.has_value()) 7660ed80c8cSGeorge Liu { 767ef93eab3SJagpal Singh Gill messages::propertyValueFormatError( 768ef93eab3SJagpal Singh Gill asyncResp->res, target, 769ef93eab3SJagpal Singh Gill std::format("Targets/{}", urlIndex)); 770ef93eab3SJagpal Singh Gill return std::nullopt; 7710ed80c8cSGeorge Liu } 772de0c960cSJagpal Singh Gill multiRet.targets.emplace_back(res.value()); 773ef93eab3SJagpal Singh Gill } 774ef93eab3SJagpal Singh Gill if (multiRet.targets.size() != 1) 775ef93eab3SJagpal Singh Gill { 776ef93eab3SJagpal Singh Gill messages::propertyValueFormatError( 777ef93eab3SJagpal Singh Gill asyncResp->res, multiRet.targets, "Targets"); 778ef93eab3SJagpal Singh Gill return std::nullopt; 779ef93eab3SJagpal Singh Gill } 7800ed80c8cSGeorge Liu } 7810ed80c8cSGeorge Liu else if (param.second == "UpdateFile") 7820ed80c8cSGeorge Liu { 783ef93eab3SJagpal Singh Gill multiRet.uploadData = std::move(formpart.content); 7840ed80c8cSGeorge Liu } 7850ed80c8cSGeorge Liu } 7860ed80c8cSGeorge Liu } 7870ed80c8cSGeorge Liu 788ef93eab3SJagpal Singh Gill if (multiRet.uploadData.empty()) 7890ed80c8cSGeorge Liu { 79062598e31SEd Tanous BMCWEB_LOG_ERROR("Upload data is NULL"); 7910ed80c8cSGeorge Liu messages::propertyMissing(asyncResp->res, "UpdateFile"); 792ef93eab3SJagpal Singh Gill return std::nullopt; 7930ed80c8cSGeorge Liu } 794ef93eab3SJagpal Singh Gill if (multiRet.targets.empty()) 7950ed80c8cSGeorge Liu { 796ef93eab3SJagpal Singh Gill messages::propertyMissing(asyncResp->res, "Targets"); 797ef93eab3SJagpal Singh Gill return std::nullopt; 798ef93eab3SJagpal Singh Gill } 799ef93eab3SJagpal Singh Gill return multiRet; 8000ed80c8cSGeorge Liu } 8010ed80c8cSGeorge Liu 802bd79bce8SPatrick Williams inline void handleStartUpdate( 803bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 804bd79bce8SPatrick Williams const std::string& objectPath, const boost::system::error_code& ec, 805de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) 806de0c960cSJagpal Singh Gill { 807de0c960cSJagpal Singh Gill if (ec) 808de0c960cSJagpal Singh Gill { 809de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 810de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 811de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 812de0c960cSJagpal Singh Gill return; 813de0c960cSJagpal Singh Gill } 814de0c960cSJagpal Singh Gill 815587090cdSJagpal Singh Gill BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}", 816587090cdSJagpal Singh Gill objectPath, retPath.str); 817587090cdSJagpal Singh Gill createTask(asyncResp, std::move(payload), retPath); 818de0c960cSJagpal Singh Gill } 819de0c960cSJagpal Singh Gill 820bd79bce8SPatrick Williams inline void startUpdate( 821bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 822bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 823bd79bce8SPatrick Williams const std::string& objectPath, const std::string& serviceName) 824de0c960cSJagpal Singh Gill { 825de0c960cSJagpal Singh Gill crow::connections::systemBus->async_method_call( 826de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), 827de0c960cSJagpal Singh Gill objectPath](const boost::system::error_code& ec1, 828de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) mutable { 829de0c960cSJagpal Singh Gill handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1, 830de0c960cSJagpal Singh Gill retPath); 831de0c960cSJagpal Singh Gill }, 832de0c960cSJagpal Singh Gill serviceName, objectPath, "xyz.openbmc_project.Software.Update", 833de0c960cSJagpal Singh Gill "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime); 834de0c960cSJagpal Singh Gill } 835de0c960cSJagpal Singh Gill 83608f61d53SJagpal Singh Gill inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 83708f61d53SJagpal Singh Gill task::Payload payload, const MemoryFileDescriptor& memfd, 83808f61d53SJagpal Singh Gill const std::string& applyTime, const std::string& target, 839de0c960cSJagpal Singh Gill const boost::system::error_code& ec, 840de0c960cSJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& subtree) 841de0c960cSJagpal Singh Gill { 84208f61d53SJagpal Singh Gill using SwInfoMap = std::unordered_map< 84308f61d53SJagpal Singh Gill std::string, std::pair<sdbusplus::message::object_path, std::string>>; 844de0c960cSJagpal Singh Gill SwInfoMap swInfoMap; 845de0c960cSJagpal Singh Gill 846de0c960cSJagpal Singh Gill if (ec) 847de0c960cSJagpal Singh Gill { 848de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 849de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 850de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 851de0c960cSJagpal Singh Gill return; 852de0c960cSJagpal Singh Gill } 853de0c960cSJagpal Singh Gill BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size()); 854de0c960cSJagpal Singh Gill 85508f61d53SJagpal Singh Gill for (const auto& entry : subtree) 856de0c960cSJagpal Singh Gill { 85708f61d53SJagpal Singh Gill sdbusplus::message::object_path path(entry.first); 858de0c960cSJagpal Singh Gill std::string swId = path.filename(); 85908f61d53SJagpal Singh Gill swInfoMap.emplace(swId, make_pair(path, entry.second[0].first)); 860de0c960cSJagpal Singh Gill } 861de0c960cSJagpal Singh Gill 862de0c960cSJagpal Singh Gill auto swEntry = swInfoMap.find(target); 863de0c960cSJagpal Singh Gill if (swEntry == swInfoMap.end()) 864de0c960cSJagpal Singh Gill { 865de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target); 866de0c960cSJagpal Singh Gill messages::propertyValueFormatError(asyncResp->res, target, "Targets"); 867de0c960cSJagpal Singh Gill return; 868de0c960cSJagpal Singh Gill } 869de0c960cSJagpal Singh Gill 87008f61d53SJagpal Singh Gill BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}", 87108f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 872de0c960cSJagpal Singh Gill 87308f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 87408f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 87508f61d53SJagpal Singh Gill } 87608f61d53SJagpal Singh Gill 877bd79bce8SPatrick Williams inline void handleBMCUpdate( 878bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 879bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 88008f61d53SJagpal Singh Gill const boost::system::error_code& ec, 88108f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& functionalSoftware) 88208f61d53SJagpal Singh Gill { 88308f61d53SJagpal Singh Gill if (ec) 88408f61d53SJagpal Singh Gill { 88508f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 88608f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 88708f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 88808f61d53SJagpal Singh Gill return; 88908f61d53SJagpal Singh Gill } 89008f61d53SJagpal Singh Gill if (functionalSoftware.size() != 1) 89108f61d53SJagpal Singh Gill { 89208f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("Found {} functional software endpoints", 89308f61d53SJagpal Singh Gill functionalSoftware.size()); 89408f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 89508f61d53SJagpal Singh Gill return; 89608f61d53SJagpal Singh Gill } 89708f61d53SJagpal Singh Gill 89808f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 89908f61d53SJagpal Singh Gill functionalSoftware[0], "xyz.openbmc_project.Software.Manager"); 900de0c960cSJagpal Singh Gill } 901de0c960cSJagpal Singh Gill 902bd79bce8SPatrick Williams inline void processUpdateRequest( 903bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 9049dae4deeSJagpal Singh Gill task::Payload&& payload, std::string_view body, 905bd79bce8SPatrick Williams const std::string& applyTime, std::vector<std::string>& targets) 906de0c960cSJagpal Singh Gill { 907de0c960cSJagpal Singh Gill MemoryFileDescriptor memfd("update-image"); 908de0c960cSJagpal Singh Gill if (memfd.fd == -1) 909de0c960cSJagpal Singh Gill { 910de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to create image memfd"); 911de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 912de0c960cSJagpal Singh Gill return; 913de0c960cSJagpal Singh Gill } 914de0c960cSJagpal Singh Gill if (write(memfd.fd, body.data(), body.length()) != 915de0c960cSJagpal Singh Gill static_cast<ssize_t>(body.length())) 916de0c960cSJagpal Singh Gill { 917de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to write to image memfd"); 918de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 919de0c960cSJagpal Singh Gill return; 920de0c960cSJagpal Singh Gill } 921de0c960cSJagpal Singh Gill if (!memfd.rewind()) 922de0c960cSJagpal Singh Gill { 923de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 924de0c960cSJagpal Singh Gill return; 925de0c960cSJagpal Singh Gill } 926de0c960cSJagpal Singh Gill 927de0c960cSJagpal Singh Gill if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME) 928de0c960cSJagpal Singh Gill { 92908f61d53SJagpal Singh Gill dbus::utility::getAssociationEndPoints( 93089449bbeSJagpal Singh Gill "/xyz/openbmc_project/software/bmc/updateable", 93108f61d53SJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 93208f61d53SJagpal Singh Gill applyTime]( 93308f61d53SJagpal Singh Gill const boost::system::error_code& ec, 93408f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& objectPaths) mutable { 935bd79bce8SPatrick Williams handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime, 936bd79bce8SPatrick Williams ec, objectPaths); 93708f61d53SJagpal Singh Gill }); 938de0c960cSJagpal Singh Gill } 939de0c960cSJagpal Singh Gill else 940de0c960cSJagpal Singh Gill { 941de0c960cSJagpal Singh Gill constexpr std::array<std::string_view, 1> interfaces = { 942de0c960cSJagpal Singh Gill "xyz.openbmc_project.Software.Version"}; 94308f61d53SJagpal Singh Gill dbus::utility::getSubTree( 944de0c960cSJagpal Singh Gill "/xyz/openbmc_project/software", 1, interfaces, 945de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 94608f61d53SJagpal Singh Gill applyTime, targets](const boost::system::error_code& ec, 94708f61d53SJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& 948de0c960cSJagpal Singh Gill subtree) mutable { 94908f61d53SJagpal Singh Gill getSwInfo(asyncResp, std::move(payload), memfd, applyTime, 95008f61d53SJagpal Singh Gill targets[0], ec, subtree); 951de0c960cSJagpal Singh Gill }); 952de0c960cSJagpal Singh Gill } 953de0c960cSJagpal Singh Gill } 954de0c960cSJagpal Singh Gill 955de0c960cSJagpal Singh Gill inline void 956ef93eab3SJagpal Singh Gill updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 957ef93eab3SJagpal Singh Gill const crow::Request& req, MultipartParser&& parser) 958ef93eab3SJagpal Singh Gill { 959ef93eab3SJagpal Singh Gill std::optional<MultiPartUpdateParameters> multipart = 960ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters(asyncResp, std::move(parser)); 961ef93eab3SJagpal Singh Gill if (!multipart) 962ef93eab3SJagpal Singh Gill { 963ef93eab3SJagpal Singh Gill return; 964ef93eab3SJagpal Singh Gill } 965ef93eab3SJagpal Singh Gill if (!multipart->applyTime) 966ef93eab3SJagpal Singh Gill { 967ef93eab3SJagpal Singh Gill multipart->applyTime = "OnReset"; 968ef93eab3SJagpal Singh Gill } 969ef93eab3SJagpal Singh Gill 970de0c960cSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 971de0c960cSJagpal Singh Gill { 9729dae4deeSJagpal Singh Gill std::string applyTimeNewVal; 9739dae4deeSJagpal Singh Gill if (!convertApplyTime(asyncResp->res, *multipart->applyTime, 9749dae4deeSJagpal Singh Gill applyTimeNewVal)) 9759dae4deeSJagpal Singh Gill { 9769dae4deeSJagpal Singh Gill return; 9779dae4deeSJagpal Singh Gill } 9789dae4deeSJagpal Singh Gill task::Payload payload(req); 9799dae4deeSJagpal Singh Gill 9809dae4deeSJagpal Singh Gill processUpdateRequest(asyncResp, std::move(payload), 9819dae4deeSJagpal Singh Gill multipart->uploadData, applyTimeNewVal, 9829dae4deeSJagpal Singh Gill multipart->targets); 983de0c960cSJagpal Singh Gill } 984de0c960cSJagpal Singh Gill else 985de0c960cSJagpal Singh Gill { 986ef93eab3SJagpal Singh Gill setApplyTime(asyncResp, *multipart->applyTime); 9870ed80c8cSGeorge Liu 9886b54e4e0SEd Tanous // Setup callback for when new software detected 989de0c960cSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 990de0c960cSJagpal Singh Gill "/redfish/v1/UpdateService"); 9916b54e4e0SEd Tanous 992ef93eab3SJagpal Singh Gill uploadImageFile(asyncResp->res, multipart->uploadData); 9930ed80c8cSGeorge Liu } 994de0c960cSJagpal Singh Gill } 9950ed80c8cSGeorge Liu 9969dae4deeSJagpal Singh Gill inline void doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 9979dae4deeSJagpal Singh Gill const crow::Request& req) 9989dae4deeSJagpal Singh Gill { 9999dae4deeSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 10009dae4deeSJagpal Singh Gill { 10019dae4deeSJagpal Singh Gill task::Payload payload(req); 10029dae4deeSJagpal Singh Gill // HTTP push only supports BMC updates (with ApplyTime as immediate) for 10039dae4deeSJagpal Singh Gill // backwards compatibility. Specific component updates will be handled 10049dae4deeSJagpal Singh Gill // through Multipart form HTTP push. 10059dae4deeSJagpal Singh Gill std::vector<std::string> targets; 10069dae4deeSJagpal Singh Gill targets.emplace_back(BMCWEB_REDFISH_MANAGER_URI_NAME); 10079dae4deeSJagpal Singh Gill 10089dae4deeSJagpal Singh Gill processUpdateRequest( 10099dae4deeSJagpal Singh Gill asyncResp, std::move(payload), req.body(), 10109dae4deeSJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate", 10119dae4deeSJagpal Singh Gill targets); 10129dae4deeSJagpal Singh Gill } 10139dae4deeSJagpal Singh Gill else 10149dae4deeSJagpal Singh Gill { 10159dae4deeSJagpal Singh Gill // Setup callback for when new software detected 10169dae4deeSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 10179dae4deeSJagpal Singh Gill "/redfish/v1/UpdateService"); 10189dae4deeSJagpal Singh Gill 10199dae4deeSJagpal Singh Gill uploadImageFile(asyncResp->res, req.body()); 10209dae4deeSJagpal Singh Gill } 10219dae4deeSJagpal Singh Gill } 10229dae4deeSJagpal Singh Gill 1023c2051d11SEd Tanous inline void 1024c2051d11SEd Tanous handleUpdateServicePost(App& app, const crow::Request& req, 1025c2051d11SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1026c2051d11SEd Tanous { 10273ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1028c2051d11SEd Tanous { 1029c2051d11SEd Tanous return; 1030c2051d11SEd Tanous } 1031b33a4327SNinad Palsule std::string_view contentType = req.getHeaderValue("Content-Type"); 1032b33a4327SNinad Palsule 103362598e31SEd Tanous BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType); 1034b33a4327SNinad Palsule 1035b33a4327SNinad Palsule // Make sure that content type is application/octet-stream or 1036b33a4327SNinad Palsule // multipart/form-data 103718f8f608SEd Tanous if (bmcweb::asciiIEquals(contentType, "application/octet-stream")) 1038b33a4327SNinad Palsule { 10399dae4deeSJagpal Singh Gill doHTTPUpdate(asyncResp, req); 1040b33a4327SNinad Palsule } 1041b33a4327SNinad Palsule else if (contentType.starts_with("multipart/form-data")) 1042b33a4327SNinad Palsule { 1043b33a4327SNinad Palsule MultipartParser parser; 1044c2051d11SEd Tanous 10450ed80c8cSGeorge Liu ParserError ec = parser.parse(req); 10460ed80c8cSGeorge Liu if (ec != ParserError::PARSER_SUCCESS) 10470ed80c8cSGeorge Liu { 10480ed80c8cSGeorge Liu // handle error 104962598e31SEd Tanous BMCWEB_LOG_ERROR("MIME parse failed, ec : {}", 105062598e31SEd Tanous static_cast<int>(ec)); 10510ed80c8cSGeorge Liu messages::internalError(asyncResp->res); 10520ed80c8cSGeorge Liu return; 10530ed80c8cSGeorge Liu } 10546b54e4e0SEd Tanous 1055ef93eab3SJagpal Singh Gill updateMultipartContext(asyncResp, req, std::move(parser)); 1056c2051d11SEd Tanous } 1057b33a4327SNinad Palsule else 1058b33a4327SNinad Palsule { 105962598e31SEd Tanous BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType); 1060b33a4327SNinad Palsule asyncResp->res.result(boost::beast::http::status::bad_request); 1061b33a4327SNinad Palsule } 1062b33a4327SNinad Palsule } 1063c2051d11SEd Tanous 1064f5139334SEd Tanous inline void 1065f5139334SEd Tanous handleUpdateServiceGet(App& app, const crow::Request& req, 1066f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 10671abe55efSEd Tanous { 10683ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 106945ca1b86SEd Tanous { 107045ca1b86SEd Tanous return; 107145ca1b86SEd Tanous } 10728d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 10730ed80c8cSGeorge Liu "#UpdateService.v1_11_1.UpdateService"; 10748d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService"; 10758d1b46d7Szhanghch05 asyncResp->res.jsonValue["Id"] = "UpdateService"; 1076002d39b4SEd Tanous asyncResp->res.jsonValue["Description"] = "Service for Software Update"; 10778d1b46d7Szhanghch05 asyncResp->res.jsonValue["Name"] = "Update Service"; 10784dc23f3fSEd Tanous 10797e860f15SJohn Edward Broadbent asyncResp->res.jsonValue["HttpPushUri"] = 10804dc23f3fSEd Tanous "/redfish/v1/UpdateService/update"; 10810ed80c8cSGeorge Liu asyncResp->res.jsonValue["MultipartHttpPushUri"] = 10820ed80c8cSGeorge Liu "/redfish/v1/UpdateService/update"; 10834dc23f3fSEd Tanous 10840f74e643SEd Tanous // UpdateService cannot be disabled 10858d1b46d7Szhanghch05 asyncResp->res.jsonValue["ServiceEnabled"] = true; 10861476687dSEd Tanous asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] = 10871476687dSEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1088d61e5194STejas Patil // Get the MaxImageSizeBytes 1089bd79bce8SPatrick Williams asyncResp->res.jsonValue["MaxImageSizeBytes"] = 1090bd79bce8SPatrick Williams BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024; 1091d61e5194STejas Patil 10926a37140aSEd Tanous if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE) 10936a37140aSEd Tanous { 10940554c984SAndrew Geissler // Update Actions object. 10950554c984SAndrew Geissler nlohmann::json& updateSvcSimpleUpdate = 1096002d39b4SEd Tanous asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"]; 10970554c984SAndrew Geissler updateSvcSimpleUpdate["target"] = 10980554c984SAndrew Geissler "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate"; 1099757178a5SEd Tanous 1100757178a5SEd Tanous nlohmann::json::array_t allowed; 1101e5cf777eSEd Tanous allowed.emplace_back(update_service::TransferProtocolType::HTTPS); 1102757178a5SEd Tanous updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = 1103757178a5SEd Tanous std::move(allowed); 11046a37140aSEd Tanous } 1105757178a5SEd Tanous 1106539d8c6bSEd Tanous asyncResp->res 1107539d8c6bSEd Tanous .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] = 1108539d8c6bSEd Tanous update_service::ApplyTime::Immediate; 1109729dae72SJennifer Lee } 1110729dae72SJennifer Lee 1111f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryCollectionGet( 1112f5139334SEd Tanous App& app, const crow::Request& req, 1113f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 11141abe55efSEd Tanous { 11153ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 111645ca1b86SEd Tanous { 111745ca1b86SEd Tanous return; 111845ca1b86SEd Tanous } 11198d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 11200f74e643SEd Tanous "#SoftwareInventoryCollection.SoftwareInventoryCollection"; 11218d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = 11220f74e643SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1123002d39b4SEd Tanous asyncResp->res.jsonValue["Name"] = "Software Inventory Collection"; 112408d81adaSJohn Edward Broadbent const std::array<const std::string_view, 1> iface = { 1125e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 11266c4eb9deSJennifer Lee 112708d81adaSJohn Edward Broadbent redfish::collection_util::getCollectionMembers( 112808d81adaSJohn Edward Broadbent asyncResp, 1129f5139334SEd Tanous boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface, 1130f5139334SEd Tanous "/xyz/openbmc_project/software"); 1131729dae72SJennifer Lee } 1132f5139334SEd Tanous 113387d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */ 1134f5139334SEd Tanous inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 113587d84729SAndrew Geissler const std::string& purpose) 113687d84729SAndrew Geissler { 1137eee0013eSWilly Tu if (purpose == sw_util::bmcPurpose) 113887d84729SAndrew Geissler { 1139ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"]; 11401476687dSEd Tanous nlohmann::json::object_t item; 1141253f11b8SEd Tanous item["@odata.id"] = boost::urls::format( 1142253f11b8SEd Tanous "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); 1143b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item)); 1144ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] = 1145ac106bf6SEd Tanous relatedItem.size(); 114687d84729SAndrew Geissler } 1147eee0013eSWilly Tu else if (purpose == sw_util::biosPurpose) 114887d84729SAndrew Geissler { 1149ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"]; 11501476687dSEd Tanous nlohmann::json::object_t item; 1151253f11b8SEd Tanous item["@odata.id"] = std::format("/redfish/v1/Systems/{}/Bios", 1152253f11b8SEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 1153b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item)); 1154ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] = 1155ac106bf6SEd Tanous relatedItem.size(); 115687d84729SAndrew Geissler } 115787d84729SAndrew Geissler else 115887d84729SAndrew Geissler { 1159bf2ddedeSCarson Labrado BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose); 116087d84729SAndrew Geissler } 116187d84729SAndrew Geissler } 116287d84729SAndrew Geissler 1163af24660dSWilly Tu inline void 1164af24660dSWilly Tu getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1165af24660dSWilly Tu const std::string& service, const std::string& path, 1166af24660dSWilly Tu const std::string& swId) 1167af24660dSWilly Tu { 1168deae6a78SEd Tanous dbus::utility::getAllProperties( 1169deae6a78SEd Tanous service, path, "xyz.openbmc_project.Software.Version", 1170af24660dSWilly Tu [asyncResp, 11718b24275dSEd Tanous swId](const boost::system::error_code& ec, 1172af24660dSWilly Tu const dbus::utility::DBusPropertiesMap& propertiesList) { 11738b24275dSEd Tanous if (ec) 1174af24660dSWilly Tu { 1175af24660dSWilly Tu messages::internalError(asyncResp->res); 1176af24660dSWilly Tu return; 1177af24660dSWilly Tu } 1178d1bde9e5SKrzysztof Grobelny 1179af24660dSWilly Tu const std::string* swInvPurpose = nullptr; 1180af24660dSWilly Tu const std::string* version = nullptr; 1181d1bde9e5SKrzysztof Grobelny 1182d1bde9e5SKrzysztof Grobelny const bool success = sdbusplus::unpackPropertiesNoThrow( 1183d1bde9e5SKrzysztof Grobelny dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose", 1184d1bde9e5SKrzysztof Grobelny swInvPurpose, "Version", version); 1185d1bde9e5SKrzysztof Grobelny 1186d1bde9e5SKrzysztof Grobelny if (!success) 1187af24660dSWilly Tu { 1188d1bde9e5SKrzysztof Grobelny messages::internalError(asyncResp->res); 1189d1bde9e5SKrzysztof Grobelny return; 1190af24660dSWilly Tu } 1191af24660dSWilly Tu 1192af24660dSWilly Tu if (swInvPurpose == nullptr) 1193af24660dSWilly Tu { 119462598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!"); 1195af24660dSWilly Tu messages::internalError(asyncResp->res); 1196af24660dSWilly Tu return; 1197af24660dSWilly Tu } 1198af24660dSWilly Tu 119962598e31SEd Tanous BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose); 1200af24660dSWilly Tu 1201af24660dSWilly Tu if (version == nullptr) 1202af24660dSWilly Tu { 120362598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Version\"!"); 1204af24660dSWilly Tu 1205af24660dSWilly Tu messages::internalError(asyncResp->res); 1206af24660dSWilly Tu 1207af24660dSWilly Tu return; 1208af24660dSWilly Tu } 1209af24660dSWilly Tu asyncResp->res.jsonValue["Version"] = *version; 1210af24660dSWilly Tu asyncResp->res.jsonValue["Id"] = swId; 1211af24660dSWilly Tu 1212af24660dSWilly Tu // swInvPurpose is of format: 1213af24660dSWilly Tu // xyz.openbmc_project.Software.Version.VersionPurpose.ABC 1214af24660dSWilly Tu // Translate this to "ABC image" 1215af24660dSWilly Tu size_t endDesc = swInvPurpose->rfind('.'); 1216af24660dSWilly Tu if (endDesc == std::string::npos) 1217af24660dSWilly Tu { 1218af24660dSWilly Tu messages::internalError(asyncResp->res); 1219af24660dSWilly Tu return; 1220af24660dSWilly Tu } 1221af24660dSWilly Tu endDesc++; 1222af24660dSWilly Tu if (endDesc >= swInvPurpose->size()) 1223af24660dSWilly Tu { 1224af24660dSWilly Tu messages::internalError(asyncResp->res); 1225af24660dSWilly Tu return; 1226af24660dSWilly Tu } 1227af24660dSWilly Tu 1228af24660dSWilly Tu std::string formatDesc = swInvPurpose->substr(endDesc); 1229af24660dSWilly Tu asyncResp->res.jsonValue["Description"] = formatDesc + " image"; 1230af24660dSWilly Tu getRelatedItems(asyncResp, *swInvPurpose); 1231d1bde9e5SKrzysztof Grobelny }); 1232af24660dSWilly Tu } 1233af24660dSWilly Tu 1234f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryGet( 1235f5139334SEd Tanous App& app, const crow::Request& req, 123645ca1b86SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1237f5139334SEd Tanous const std::string& param) 1238f5139334SEd Tanous { 12393ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 124045ca1b86SEd Tanous { 124145ca1b86SEd Tanous return; 124245ca1b86SEd Tanous } 1243f5139334SEd Tanous std::shared_ptr<std::string> swId = std::make_shared<std::string>(param); 1244c711bf86SEd Tanous 1245ef4c65b7SEd Tanous asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1246ef4c65b7SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId); 1247c711bf86SEd Tanous 1248e99073f5SGeorge Liu constexpr std::array<std::string_view, 1> interfaces = { 1249e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 1250e99073f5SGeorge Liu dbus::utility::getSubTree( 1251e99073f5SGeorge Liu "/", 0, interfaces, 1252b9d36b47SEd Tanous [asyncResp, 1253e99073f5SGeorge Liu swId](const boost::system::error_code& ec, 1254b9d36b47SEd Tanous const dbus::utility::MapperGetSubTreeResponse& subtree) { 125562598e31SEd Tanous BMCWEB_LOG_DEBUG("doGet callback..."); 12561abe55efSEd Tanous if (ec) 12571abe55efSEd Tanous { 1258f12894f8SJason M. Bills messages::internalError(asyncResp->res); 12596c4eb9deSJennifer Lee return; 12606c4eb9deSJennifer Lee } 12616c4eb9deSJennifer Lee 12626913228dSAndrew Geissler // Ensure we find our input swId, otherwise return an error 12636913228dSAndrew Geissler bool found = false; 1264bd79bce8SPatrick Williams for (const std::pair<std::string, 1265bd79bce8SPatrick Williams std::vector<std::pair< 1266bd79bce8SPatrick Williams std::string, std::vector<std::string>>>>& 1267002d39b4SEd Tanous obj : subtree) 12681abe55efSEd Tanous { 126911ba3979SEd Tanous if (!obj.first.ends_with(*swId)) 12701abe55efSEd Tanous { 1271acb7cfb4SJennifer Lee continue; 1272acb7cfb4SJennifer Lee } 1273acb7cfb4SJennifer Lee 127426f6976fSEd Tanous if (obj.second.empty()) 12751abe55efSEd Tanous { 1276acb7cfb4SJennifer Lee continue; 1277acb7cfb4SJennifer Lee } 12786c4eb9deSJennifer Lee 12796913228dSAndrew Geissler found = true; 1280eee0013eSWilly Tu sw_util::getSwStatus(asyncResp, swId, obj.second[0].first); 1281af24660dSWilly Tu getSoftwareVersion(asyncResp, obj.second[0].first, obj.first, 1282af24660dSWilly Tu *swId); 12836c4eb9deSJennifer Lee } 12846913228dSAndrew Geissler if (!found) 12856913228dSAndrew Geissler { 128662598e31SEd Tanous BMCWEB_LOG_WARNING("Input swID {} not found!", *swId); 12876913228dSAndrew Geissler messages::resourceMissingAtURI( 1288ef4c65b7SEd Tanous asyncResp->res, 1289ef4c65b7SEd Tanous boost::urls::format( 1290bd79bce8SPatrick Williams "/redfish/v1/UpdateService/FirmwareInventory/{}", 1291bd79bce8SPatrick Williams *swId)); 12926913228dSAndrew Geissler return; 12936913228dSAndrew Geissler } 12944e68c45bSAyushi Smriti asyncResp->res.jsonValue["@odata.type"] = 12954e68c45bSAyushi Smriti "#SoftwareInventory.v1_1_0.SoftwareInventory"; 12964e68c45bSAyushi Smriti asyncResp->res.jsonValue["Name"] = "Software Inventory"; 1297539d8c6bSEd Tanous asyncResp->res.jsonValue["Status"]["HealthRollup"] = 1298539d8c6bSEd Tanous resource::Health::OK; 12993f8a743aSAppaRao Puli 13003f8a743aSAppaRao Puli asyncResp->res.jsonValue["Updateable"] = false; 1301eee0013eSWilly Tu sw_util::getSwUpdatableStatus(asyncResp, swId); 1302e99073f5SGeorge Liu }); 1303f5139334SEd Tanous } 1304f5139334SEd Tanous 1305f5139334SEd Tanous inline void requestRoutesUpdateService(App& app) 1306f5139334SEd Tanous { 13076a37140aSEd Tanous if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE) 13086a37140aSEd Tanous { 1309f5139334SEd Tanous BMCWEB_ROUTE( 13106a37140aSEd Tanous app, 13116a37140aSEd Tanous "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/") 1312f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1313f5139334SEd Tanous .methods(boost::beast::http::verb::post)(std::bind_front( 1314f5139334SEd Tanous handleUpdateServiceSimpleUpdateAction, std::ref(app))); 13156a37140aSEd Tanous } 1316f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/") 1317f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventory) 1318f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1319f5139334SEd Tanous handleUpdateServiceFirmwareInventoryGet, std::ref(app))); 1320f5139334SEd Tanous 1321f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/") 1322f5139334SEd Tanous .privileges(redfish::privileges::getUpdateService) 1323f5139334SEd Tanous .methods(boost::beast::http::verb::get)( 1324f5139334SEd Tanous std::bind_front(handleUpdateServiceGet, std::ref(app))); 1325f5139334SEd Tanous 1326f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/") 1327f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1328f5139334SEd Tanous .methods(boost::beast::http::verb::post)( 1329f5139334SEd Tanous std::bind_front(handleUpdateServicePost, std::ref(app))); 1330f5139334SEd Tanous 1331f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/") 1332f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventoryCollection) 1333f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1334f5139334SEd Tanous handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app))); 13356c4eb9deSJennifer Lee } 1336729dae72SJennifer Lee 1337729dae72SJennifer Lee } // namespace redfish 1338