140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0 240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors 340e9b92eSEd 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" 9d7857201SEd Tanous #include "async_resp.hpp" 10d7857201SEd Tanous #include "dbus_singleton.hpp" 113ccb3adbSEd Tanous #include "dbus_utility.hpp" 125b90429aSEd Tanous #include "error_messages.hpp" 13d7857201SEd Tanous #include "generated/enums/resource.hpp" 14757178a5SEd Tanous #include "generated/enums/update_service.hpp" 15d7857201SEd Tanous #include "http_request.hpp" 16d7857201SEd Tanous #include "http_response.hpp" 17d98a2f93SEd Tanous #include "io_context_singleton.hpp" 18d7857201SEd Tanous #include "logging.hpp" 190ed80c8cSGeorge Liu #include "multipart_parser.hpp" 202c6ffdb0SEd Tanous #include "ossl_random.hpp" 213ccb3adbSEd Tanous #include "query.hpp" 223ccb3adbSEd Tanous #include "registries/privilege_registry.hpp" 23d7857201SEd Tanous #include "str_utility.hpp" 24a8e884fcSEd Tanous #include "task.hpp" 255b90429aSEd Tanous #include "task_messages.hpp" 26d7857201SEd Tanous #include "utility.hpp" 2708d81adaSJohn Edward Broadbent #include "utils/collection.hpp" 283ccb3adbSEd Tanous #include "utils/dbus_utils.hpp" 295b90429aSEd Tanous #include "utils/json_utils.hpp" 303ccb3adbSEd Tanous #include "utils/sw_utils.hpp" 313ccb3adbSEd Tanous 32de0c960cSJagpal Singh Gill #include <sys/mman.h> 33d7857201SEd Tanous #include <unistd.h> 34de0c960cSJagpal Singh Gill 35d7857201SEd Tanous #include <boost/asio/error.hpp> 36d7857201SEd Tanous #include <boost/asio/steady_timer.hpp> 37d7857201SEd Tanous #include <boost/beast/http/fields.hpp> 38d7857201SEd Tanous #include <boost/beast/http/status.hpp> 39d7857201SEd Tanous #include <boost/beast/http/verb.hpp> 40e99073f5SGeorge Liu #include <boost/system/error_code.hpp> 41d7857201SEd Tanous #include <boost/system/result.hpp> 42ef4c65b7SEd Tanous #include <boost/url/format.hpp> 43d7857201SEd Tanous #include <boost/url/parse.hpp> 44d7857201SEd Tanous #include <boost/url/url.hpp> 45d7857201SEd Tanous #include <boost/url/url_view.hpp> 46d7857201SEd Tanous #include <boost/url/url_view_base.hpp> 471e1e598dSJonathan Doman #include <sdbusplus/asio/property.hpp> 483ccb3adbSEd Tanous #include <sdbusplus/bus/match.hpp> 49d7857201SEd Tanous #include <sdbusplus/message.hpp> 50d7857201SEd Tanous #include <sdbusplus/message/native_types.hpp> 51d1bde9e5SKrzysztof Grobelny #include <sdbusplus/unpack_properties.hpp> 521214b7e7SGunnar Mills 532b73119cSGeorge Liu #include <array> 54d7857201SEd Tanous #include <chrono> 55de0c960cSJagpal Singh Gill #include <cstddef> 56d7857201SEd Tanous #include <cstdint> 57d7857201SEd Tanous #include <cstdio> 580ed80c8cSGeorge Liu #include <filesystem> 59d7857201SEd Tanous #include <format> 60d7857201SEd Tanous #include <fstream> 61c71b6c99SJagpal Singh Gill #include <functional> 62ef93eab3SJagpal Singh Gill #include <memory> 637cb59f65SEd Tanous #include <optional> 647cb59f65SEd Tanous #include <string> 652b73119cSGeorge Liu #include <string_view> 66de0c960cSJagpal Singh Gill #include <unordered_map> 67d7857201SEd Tanous #include <utility> 68d7857201SEd Tanous #include <variant> 69ef93eab3SJagpal Singh Gill #include <vector> 702b73119cSGeorge Liu 711abe55efSEd Tanous namespace redfish 721abe55efSEd Tanous { 7327826b5fSEd Tanous 740e7de46fSAndrew Geissler // Match signals added on software path 75cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 7659d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher; 77cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 7859d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher; 790e7de46fSAndrew Geissler // Only allow one update at a time 80cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 810e7de46fSAndrew Geissler static bool fwUpdateInProgress = false; 8286adcd6dSAndrew Geissler // Timer for software available 83cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 84271584abSEd Tanous static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer; 8586adcd6dSAndrew Geissler 86de0c960cSJagpal Singh Gill struct MemoryFileDescriptor 87de0c960cSJagpal Singh Gill { 88de0c960cSJagpal Singh Gill int fd = -1; 89de0c960cSJagpal Singh Gill 90de0c960cSJagpal Singh Gill explicit MemoryFileDescriptor(const std::string& filename) : 91de0c960cSJagpal Singh Gill fd(memfd_create(filename.c_str(), 0)) 92de0c960cSJagpal Singh Gill {} 93de0c960cSJagpal Singh Gill 94de0c960cSJagpal Singh Gill MemoryFileDescriptor(const MemoryFileDescriptor&) = default; 95de0c960cSJagpal Singh Gill MemoryFileDescriptor(MemoryFileDescriptor&& other) noexcept : fd(other.fd) 96de0c960cSJagpal Singh Gill { 97de0c960cSJagpal Singh Gill other.fd = -1; 98de0c960cSJagpal Singh Gill } 99de0c960cSJagpal Singh Gill MemoryFileDescriptor& operator=(const MemoryFileDescriptor&) = delete; 100de0c960cSJagpal Singh Gill MemoryFileDescriptor& operator=(MemoryFileDescriptor&&) = default; 101de0c960cSJagpal Singh Gill 102de0c960cSJagpal Singh Gill ~MemoryFileDescriptor() 103de0c960cSJagpal Singh Gill { 104de0c960cSJagpal Singh Gill if (fd != -1) 105de0c960cSJagpal Singh Gill { 106de0c960cSJagpal Singh Gill close(fd); 107de0c960cSJagpal Singh Gill } 108de0c960cSJagpal Singh Gill } 109de0c960cSJagpal Singh Gill 110de0c960cSJagpal Singh Gill bool rewind() const 111de0c960cSJagpal Singh Gill { 112de0c960cSJagpal Singh Gill if (lseek(fd, 0, SEEK_SET) == -1) 113de0c960cSJagpal Singh Gill { 114de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to seek to beginning of image memfd"); 115de0c960cSJagpal Singh Gill return false; 116de0c960cSJagpal Singh Gill } 117de0c960cSJagpal Singh Gill return true; 118de0c960cSJagpal Singh Gill } 119de0c960cSJagpal Singh Gill }; 120de0c960cSJagpal Singh Gill 121df254f2cSEd Tanous inline void cleanUp() 12286adcd6dSAndrew Geissler { 12386adcd6dSAndrew Geissler fwUpdateInProgress = false; 12486adcd6dSAndrew Geissler fwUpdateMatcher = nullptr; 1254cde5d90SJames Feist fwUpdateErrorMatcher = nullptr; 12686adcd6dSAndrew Geissler } 127df254f2cSEd Tanous 128df254f2cSEd Tanous inline void activateImage(const std::string& objPath, 12986adcd6dSAndrew Geissler const std::string& service) 13086adcd6dSAndrew Geissler { 13162598e31SEd Tanous BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service); 1329ae226faSGeorge Liu sdbusplus::asio::setProperty( 1339ae226faSGeorge Liu *crow::connections::systemBus, service, objPath, 1349ae226faSGeorge Liu "xyz.openbmc_project.Software.Activation", "RequestedActivation", 1359ae226faSGeorge Liu "xyz.openbmc_project.Software.Activation.RequestedActivations.Active", 1368b24275dSEd Tanous [](const boost::system::error_code& ec) { 1378b24275dSEd Tanous if (ec) 13886adcd6dSAndrew Geissler { 13962598e31SEd Tanous BMCWEB_LOG_DEBUG("error_code = {}", ec); 14062598e31SEd Tanous BMCWEB_LOG_DEBUG("error msg = {}", ec.message()); 14186adcd6dSAndrew Geissler } 1429ae226faSGeorge Liu }); 14386adcd6dSAndrew Geissler } 1440554c984SAndrew Geissler 145c71b6c99SJagpal Singh Gill inline bool handleCreateTask(const boost::system::error_code& ec2, 146c71b6c99SJagpal Singh Gill sdbusplus::message_t& msg, 147c71b6c99SJagpal Singh Gill const std::shared_ptr<task::TaskData>& taskData) 148c71b6c99SJagpal Singh Gill { 149c71b6c99SJagpal Singh Gill if (ec2) 150c71b6c99SJagpal Singh Gill { 151c71b6c99SJagpal Singh Gill return task::completed; 152c71b6c99SJagpal Singh Gill } 153c71b6c99SJagpal Singh Gill 154c71b6c99SJagpal Singh Gill std::string iface; 155c71b6c99SJagpal Singh Gill dbus::utility::DBusPropertiesMap values; 156c71b6c99SJagpal Singh Gill 157c71b6c99SJagpal Singh Gill std::string index = std::to_string(taskData->index); 158c71b6c99SJagpal Singh Gill msg.read(iface, values); 159c71b6c99SJagpal Singh Gill 160c71b6c99SJagpal Singh Gill if (iface == "xyz.openbmc_project.Software.Activation") 161c71b6c99SJagpal Singh Gill { 162c71b6c99SJagpal Singh Gill const std::string* state = nullptr; 163c71b6c99SJagpal Singh Gill for (const auto& property : values) 164c71b6c99SJagpal Singh Gill { 165c71b6c99SJagpal Singh Gill if (property.first == "Activation") 166c71b6c99SJagpal Singh Gill { 167c71b6c99SJagpal Singh Gill state = std::get_if<std::string>(&property.second); 168c71b6c99SJagpal Singh Gill if (state == nullptr) 169c71b6c99SJagpal Singh Gill { 170c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::internalError()); 171c71b6c99SJagpal Singh Gill return task::completed; 172c71b6c99SJagpal Singh Gill } 173c71b6c99SJagpal Singh Gill } 174c71b6c99SJagpal Singh Gill } 175c71b6c99SJagpal Singh Gill 176c71b6c99SJagpal Singh Gill if (state == nullptr) 177c71b6c99SJagpal Singh Gill { 178c71b6c99SJagpal Singh Gill return !task::completed; 179c71b6c99SJagpal Singh Gill } 180c71b6c99SJagpal Singh Gill 181c71b6c99SJagpal Singh Gill if (state->ends_with("Invalid") || state->ends_with("Failed")) 182c71b6c99SJagpal Singh Gill { 183c71b6c99SJagpal Singh Gill taskData->state = "Exception"; 184c71b6c99SJagpal Singh Gill taskData->status = "Warning"; 185c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskAborted(index)); 186c71b6c99SJagpal Singh Gill return task::completed; 187c71b6c99SJagpal Singh Gill } 188c71b6c99SJagpal Singh Gill 189c71b6c99SJagpal Singh Gill if (state->ends_with("Staged")) 190c71b6c99SJagpal Singh Gill { 191c71b6c99SJagpal Singh Gill taskData->state = "Stopping"; 192c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskPaused(index)); 193c71b6c99SJagpal Singh Gill 194c71b6c99SJagpal Singh Gill // its staged, set a long timer to 195c71b6c99SJagpal Singh Gill // allow them time to complete the 196c71b6c99SJagpal Singh Gill // update (probably cycle the 197c71b6c99SJagpal Singh Gill // system) if this expires then 198c71b6c99SJagpal Singh Gill // task will be canceled 199c71b6c99SJagpal Singh Gill taskData->extendTimer(std::chrono::hours(5)); 200c71b6c99SJagpal Singh Gill return !task::completed; 201c71b6c99SJagpal Singh Gill } 202c71b6c99SJagpal Singh Gill 203c71b6c99SJagpal Singh Gill if (state->ends_with("Active")) 204c71b6c99SJagpal Singh Gill { 205c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskCompletedOK(index)); 206c71b6c99SJagpal Singh Gill taskData->state = "Completed"; 207c71b6c99SJagpal Singh Gill return task::completed; 208c71b6c99SJagpal Singh Gill } 209c71b6c99SJagpal Singh Gill } 210c71b6c99SJagpal Singh Gill else if (iface == "xyz.openbmc_project.Software.ActivationProgress") 211c71b6c99SJagpal Singh Gill { 212c71b6c99SJagpal Singh Gill const uint8_t* progress = nullptr; 213c71b6c99SJagpal Singh Gill for (const auto& property : values) 214c71b6c99SJagpal Singh Gill { 215c71b6c99SJagpal Singh Gill if (property.first == "Progress") 216c71b6c99SJagpal Singh Gill { 217c71b6c99SJagpal Singh Gill progress = std::get_if<uint8_t>(&property.second); 218c71b6c99SJagpal Singh Gill if (progress == nullptr) 219c71b6c99SJagpal Singh Gill { 220c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::internalError()); 221c71b6c99SJagpal Singh Gill return task::completed; 222c71b6c99SJagpal Singh Gill } 223c71b6c99SJagpal Singh Gill } 224c71b6c99SJagpal Singh Gill } 225c71b6c99SJagpal Singh Gill 226c71b6c99SJagpal Singh Gill if (progress == nullptr) 227c71b6c99SJagpal Singh Gill { 228c71b6c99SJagpal Singh Gill return !task::completed; 229c71b6c99SJagpal Singh Gill } 230c71b6c99SJagpal Singh Gill taskData->percentComplete = *progress; 231c71b6c99SJagpal Singh Gill taskData->messages.emplace_back( 232c71b6c99SJagpal Singh Gill messages::taskProgressChanged(index, *progress)); 233c71b6c99SJagpal Singh Gill 234c71b6c99SJagpal Singh Gill // if we're getting status updates it's 235c71b6c99SJagpal Singh Gill // still alive, update timer 236c71b6c99SJagpal Singh Gill taskData->extendTimer(std::chrono::minutes(5)); 237c71b6c99SJagpal Singh Gill } 238c71b6c99SJagpal Singh Gill 239c71b6c99SJagpal Singh Gill // as firmware update often results in a 240c71b6c99SJagpal Singh Gill // reboot, the task may never "complete" 241c71b6c99SJagpal Singh Gill // unless it is an error 242c71b6c99SJagpal Singh Gill 243c71b6c99SJagpal Singh Gill return !task::completed; 244c71b6c99SJagpal Singh Gill } 245c71b6c99SJagpal Singh Gill 246c71b6c99SJagpal Singh Gill inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 247c71b6c99SJagpal Singh Gill task::Payload&& payload, 248c71b6c99SJagpal Singh Gill const sdbusplus::message::object_path& objPath) 249c71b6c99SJagpal Singh Gill { 250c71b6c99SJagpal Singh Gill std::shared_ptr<task::TaskData> task = task::TaskData::createTask( 251c71b6c99SJagpal Singh Gill std::bind_front(handleCreateTask), 252c71b6c99SJagpal Singh Gill "type='signal',interface='org.freedesktop.DBus.Properties'," 253c71b6c99SJagpal Singh Gill "member='PropertiesChanged',path='" + 254c71b6c99SJagpal Singh Gill objPath.str + "'"); 255c71b6c99SJagpal Singh Gill task->startTimer(std::chrono::minutes(5)); 256c71b6c99SJagpal Singh Gill task->payload.emplace(std::move(payload)); 25729e2bdd7SChinmay Shripad Hegde task->populateResp(asyncResp->res); 258c71b6c99SJagpal Singh Gill } 259c71b6c99SJagpal Singh Gill 2600554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr 2610554c984SAndrew Geissler // then no asyncResp updates will occur 262504af5a0SPatrick Williams inline void softwareInterfaceAdded( 263504af5a0SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 26459d494eeSPatrick Williams sdbusplus::message_t& m, task::Payload&& payload) 26586adcd6dSAndrew Geissler { 26680f79a40SMichael Shen dbus::utility::DBusInterfacesMap interfacesProperties; 26786adcd6dSAndrew Geissler 26886adcd6dSAndrew Geissler sdbusplus::message::object_path objPath; 26986adcd6dSAndrew Geissler 27086adcd6dSAndrew Geissler m.read(objPath, interfacesProperties); 27186adcd6dSAndrew Geissler 27262598e31SEd Tanous BMCWEB_LOG_DEBUG("obj path = {}", objPath.str); 273e3eb3d63SEd Tanous for (const auto& interface : interfacesProperties) 27486adcd6dSAndrew Geissler { 27562598e31SEd Tanous BMCWEB_LOG_DEBUG("interface = {}", interface.first); 27686adcd6dSAndrew Geissler 27786adcd6dSAndrew Geissler if (interface.first == "xyz.openbmc_project.Software.Activation") 27886adcd6dSAndrew Geissler { 27986adcd6dSAndrew Geissler // Retrieve service and activate 2802b73119cSGeorge Liu constexpr std::array<std::string_view, 1> interfaces = { 2812b73119cSGeorge Liu "xyz.openbmc_project.Software.Activation"}; 2822b73119cSGeorge Liu dbus::utility::getDbusObject( 2832b73119cSGeorge Liu objPath.str, interfaces, 284a3e65892SEd Tanous [objPath, asyncResp, payload(std::move(payload))]( 2858b24275dSEd Tanous const boost::system::error_code& ec, 286a3e65892SEd Tanous const std::vector< 287a3e65892SEd Tanous std::pair<std::string, std::vector<std::string>>>& 288a3e65892SEd Tanous objInfo) mutable { 2898b24275dSEd Tanous if (ec) 29086adcd6dSAndrew Geissler { 29162598e31SEd Tanous BMCWEB_LOG_DEBUG("error_code = {}", ec); 29262598e31SEd Tanous BMCWEB_LOG_DEBUG("error msg = {}", ec.message()); 2930554c984SAndrew Geissler if (asyncResp) 2940554c984SAndrew Geissler { 29586adcd6dSAndrew Geissler messages::internalError(asyncResp->res); 2960554c984SAndrew Geissler } 29786adcd6dSAndrew Geissler cleanUp(); 29886adcd6dSAndrew Geissler return; 29986adcd6dSAndrew Geissler } 30086adcd6dSAndrew Geissler // Ensure we only got one service back 30186adcd6dSAndrew Geissler if (objInfo.size() != 1) 30286adcd6dSAndrew Geissler { 303bd79bce8SPatrick Williams BMCWEB_LOG_ERROR("Invalid Object Size {}", 304bd79bce8SPatrick Williams objInfo.size()); 3050554c984SAndrew Geissler if (asyncResp) 3060554c984SAndrew Geissler { 30786adcd6dSAndrew Geissler messages::internalError(asyncResp->res); 3080554c984SAndrew Geissler } 30986adcd6dSAndrew Geissler cleanUp(); 31086adcd6dSAndrew Geissler return; 31186adcd6dSAndrew Geissler } 31286adcd6dSAndrew Geissler // cancel timer only when 31386adcd6dSAndrew Geissler // xyz.openbmc_project.Software.Activation interface 31486adcd6dSAndrew Geissler // is added 31586adcd6dSAndrew Geissler fwAvailableTimer = nullptr; 31686adcd6dSAndrew Geissler 31786adcd6dSAndrew Geissler activateImage(objPath.str, objInfo[0].first); 3180554c984SAndrew Geissler if (asyncResp) 3190554c984SAndrew Geissler { 320c71b6c99SJagpal Singh Gill createTask(asyncResp, std::move(payload), objPath); 3210554c984SAndrew Geissler } 32286adcd6dSAndrew Geissler fwUpdateInProgress = false; 3232b73119cSGeorge Liu }); 32462bafc01SPatrick Williams 32562bafc01SPatrick Williams break; 32686adcd6dSAndrew Geissler } 32786adcd6dSAndrew Geissler } 32886adcd6dSAndrew Geissler } 32986adcd6dSAndrew Geissler 3308549b951SMyung Bae inline void afterAvailbleTimerAsyncWait( 3318549b951SMyung Bae const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3328549b951SMyung Bae const boost::system::error_code& ec) 3338549b951SMyung Bae { 3348549b951SMyung Bae cleanUp(); 3358549b951SMyung Bae if (ec == boost::asio::error::operation_aborted) 3368549b951SMyung Bae { 3378549b951SMyung Bae // expected, we were canceled before the timer completed. 3388549b951SMyung Bae return; 3398549b951SMyung Bae } 3408549b951SMyung Bae BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created"); 3418549b951SMyung Bae BMCWEB_LOG_ERROR("FW image may has already been uploaded to server"); 3428549b951SMyung Bae if (ec) 3438549b951SMyung Bae { 3448549b951SMyung Bae BMCWEB_LOG_ERROR("Async_wait failed{}", ec); 3458549b951SMyung Bae return; 3468549b951SMyung Bae } 3478549b951SMyung Bae if (asyncResp) 3488549b951SMyung Bae { 3498549b951SMyung Bae redfish::messages::internalError(asyncResp->res); 3508549b951SMyung Bae } 3518549b951SMyung Bae } 3528549b951SMyung Bae 353504af5a0SPatrick Williams inline void handleUpdateErrorType( 354504af5a0SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& url, 355504af5a0SPatrick Williams const std::string& type) 3568549b951SMyung Bae { 357c87294a6SEd Tanous // NOLINTBEGIN(bugprone-branch-clone) 3588549b951SMyung Bae if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure") 3598549b951SMyung Bae { 36048fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3618549b951SMyung Bae } 3628549b951SMyung Bae else if (type == 3638549b951SMyung Bae "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure") 3648549b951SMyung Bae { 36548fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3668549b951SMyung Bae } 3678549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure") 3688549b951SMyung Bae { 36948fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3708549b951SMyung Bae } 3718549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists") 3728549b951SMyung Bae { 373c87294a6SEd Tanous messages::resourceAlreadyExists(asyncResp->res, "UpdateService", 374c87294a6SEd Tanous "Version", "uploaded version"); 3758549b951SMyung Bae } 3768549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure") 3778549b951SMyung Bae { 37848fb20b9SEd Tanous messages::serviceTemporarilyUnavailable(asyncResp->res, url); 3798549b951SMyung Bae } 3804034a652SMyung Bae else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible") 3818549b951SMyung Bae { 382c87294a6SEd Tanous messages::internalError(asyncResp->res); 3834034a652SMyung Bae } 3844034a652SMyung Bae else if (type == 3854034a652SMyung Bae "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey") 3864034a652SMyung Bae { 387c87294a6SEd Tanous messages::internalError(asyncResp->res); 3884034a652SMyung Bae } 3894034a652SMyung Bae else if (type == 3904034a652SMyung Bae "xyz.openbmc_project.Software.Version.Error.InvalidSignature") 3914034a652SMyung Bae { 39248fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3934034a652SMyung Bae } 3944034a652SMyung Bae else if (type == 3954034a652SMyung Bae "xyz.openbmc_project.Software.Image.Error.InternalFailure" || 3964034a652SMyung Bae type == "xyz.openbmc_project.Software.Version.Error.HostFile") 3974034a652SMyung Bae { 3984034a652SMyung Bae BMCWEB_LOG_ERROR("Software Image Error type={}", type); 39948fb20b9SEd Tanous messages::internalError(asyncResp->res); 4008549b951SMyung Bae } 4014034a652SMyung Bae else 4024034a652SMyung Bae { 4034034a652SMyung Bae // Unrelated error types. Ignored 4044034a652SMyung Bae BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type); 4054034a652SMyung Bae return; 4064034a652SMyung Bae } 407c87294a6SEd Tanous // NOLINTEND(bugprone-branch-clone) 4084034a652SMyung Bae // Clear the timer 4094034a652SMyung Bae fwAvailableTimer = nullptr; 4108549b951SMyung Bae } 4118549b951SMyung Bae 412504af5a0SPatrick Williams inline void afterUpdateErrorMatcher( 413504af5a0SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& url, 414504af5a0SPatrick Williams sdbusplus::message_t& m) 4158549b951SMyung Bae { 41680f79a40SMichael Shen dbus::utility::DBusInterfacesMap interfacesProperties; 4178549b951SMyung Bae sdbusplus::message::object_path objPath; 4188549b951SMyung Bae m.read(objPath, interfacesProperties); 4198549b951SMyung Bae BMCWEB_LOG_DEBUG("obj path = {}", objPath.str); 4208549b951SMyung Bae for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>& 4218549b951SMyung Bae interface : interfacesProperties) 4228549b951SMyung Bae { 4238549b951SMyung Bae if (interface.first == "xyz.openbmc_project.Logging.Entry") 4248549b951SMyung Bae { 4258549b951SMyung Bae for (const std::pair<std::string, dbus::utility::DbusVariantType>& 4268549b951SMyung Bae value : interface.second) 4278549b951SMyung Bae { 4288549b951SMyung Bae if (value.first != "Message") 4298549b951SMyung Bae { 4308549b951SMyung Bae continue; 4318549b951SMyung Bae } 4328549b951SMyung Bae const std::string* type = 4338549b951SMyung Bae std::get_if<std::string>(&value.second); 4348549b951SMyung Bae if (type == nullptr) 4358549b951SMyung Bae { 4368549b951SMyung Bae // if this was our message, timeout will cover it 4378549b951SMyung Bae return; 4388549b951SMyung Bae } 4398549b951SMyung Bae handleUpdateErrorType(asyncResp, url, *type); 4408549b951SMyung Bae } 4418549b951SMyung Bae } 4428549b951SMyung Bae } 4438549b951SMyung Bae } 4448549b951SMyung Bae 4450554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr 4460554c984SAndrew Geissler // then no asyncResp updates will occur 447f5139334SEd Tanous inline void monitorForSoftwareAvailable( 4488d1b46d7Szhanghch05 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 4498d1b46d7Szhanghch05 const crow::Request& req, const std::string& url, 4501940677aSGunnar Mills int timeoutTimeSeconds = 50) 45186adcd6dSAndrew Geissler { 45286adcd6dSAndrew Geissler // Only allow one FW update at a time 453e05aec50SEd Tanous if (fwUpdateInProgress) 45486adcd6dSAndrew Geissler { 4550554c984SAndrew Geissler if (asyncResp) 4560554c984SAndrew Geissler { 45786adcd6dSAndrew Geissler messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 4580554c984SAndrew Geissler } 45986adcd6dSAndrew Geissler return; 46086adcd6dSAndrew Geissler } 46186adcd6dSAndrew Geissler 4620554c984SAndrew Geissler fwAvailableTimer = 463d98a2f93SEd Tanous std::make_unique<boost::asio::steady_timer>(getIoContext()); 46486adcd6dSAndrew Geissler 465271584abSEd Tanous fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds)); 46686adcd6dSAndrew Geissler 46786adcd6dSAndrew Geissler fwAvailableTimer->async_wait( 4688549b951SMyung Bae std::bind_front(afterAvailbleTimerAsyncWait, asyncResp)); 4698549b951SMyung Bae 470a3e65892SEd Tanous task::Payload payload(req); 47159d494eeSPatrick Williams auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable { 47262598e31SEd Tanous BMCWEB_LOG_DEBUG("Match fired"); 473a3e65892SEd Tanous softwareInterfaceAdded(asyncResp, m, std::move(payload)); 47486adcd6dSAndrew Geissler }; 47586adcd6dSAndrew Geissler 47686adcd6dSAndrew Geissler fwUpdateInProgress = true; 47786adcd6dSAndrew Geissler 47859d494eeSPatrick Williams fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>( 47986adcd6dSAndrew Geissler *crow::connections::systemBus, 48086adcd6dSAndrew Geissler "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 48186adcd6dSAndrew Geissler "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 48286adcd6dSAndrew Geissler callback); 4834cde5d90SJames Feist 48459d494eeSPatrick Williams fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>( 4854cde5d90SJames Feist *crow::connections::systemBus, 486e1cc4828SBrian Ma "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 487e1cc4828SBrian Ma "member='InterfacesAdded'," 488e1cc4828SBrian Ma "path='/xyz/openbmc_project/logging'", 4898549b951SMyung Bae std::bind_front(afterUpdateErrorMatcher, asyncResp, url)); 49086adcd6dSAndrew Geissler } 491729dae72SJennifer Lee 492bd79bce8SPatrick Williams inline std::optional<boost::urls::url> parseSimpleUpdateUrl( 493bd79bce8SPatrick Williams std::string imageURI, std::optional<std::string> transferProtocol, 494f86bcc87SEd Tanous crow::Response& res) 495f86bcc87SEd Tanous { 496f86bcc87SEd Tanous if (imageURI.find("://") == std::string::npos) 497f86bcc87SEd Tanous { 498f86bcc87SEd Tanous if (imageURI.starts_with("/")) 499f86bcc87SEd Tanous { 500f86bcc87SEd Tanous messages::actionParameterValueTypeError( 501f86bcc87SEd Tanous res, imageURI, "ImageURI", "UpdateService.SimpleUpdate"); 502f86bcc87SEd Tanous return std::nullopt; 503f86bcc87SEd Tanous } 504f86bcc87SEd Tanous if (!transferProtocol) 505f86bcc87SEd Tanous { 506f86bcc87SEd Tanous messages::actionParameterValueTypeError( 507f86bcc87SEd Tanous res, imageURI, "ImageURI", "UpdateService.SimpleUpdate"); 508f86bcc87SEd Tanous return std::nullopt; 509f86bcc87SEd Tanous } 5106a37140aSEd Tanous // OpenBMC currently only supports HTTPS 5116a37140aSEd Tanous if (*transferProtocol == "HTTPS") 512e5cf777eSEd Tanous { 513e5cf777eSEd Tanous imageURI = "https://" + imageURI; 514e5cf777eSEd Tanous } 515757178a5SEd Tanous else 516f86bcc87SEd Tanous { 517f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "TransferProtocol", 518f86bcc87SEd Tanous *transferProtocol); 519f86bcc87SEd Tanous BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}", 520f86bcc87SEd Tanous *transferProtocol); 521f86bcc87SEd Tanous return std::nullopt; 522f86bcc87SEd Tanous } 523f86bcc87SEd Tanous } 524f86bcc87SEd Tanous 525f86bcc87SEd Tanous boost::system::result<boost::urls::url> url = 526f86bcc87SEd Tanous boost::urls::parse_absolute_uri(imageURI); 527f86bcc87SEd Tanous if (!url) 528f86bcc87SEd Tanous { 529f86bcc87SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI", 530f86bcc87SEd Tanous "UpdateService.SimpleUpdate"); 531f86bcc87SEd Tanous 532f86bcc87SEd Tanous return std::nullopt; 533f86bcc87SEd Tanous } 534f86bcc87SEd Tanous url->normalize(); 535f86bcc87SEd Tanous 536757178a5SEd Tanous if (url->scheme() == "tftp") 537757178a5SEd Tanous { 538757178a5SEd Tanous if (url->encoded_path().size() < 2) 539757178a5SEd Tanous { 540757178a5SEd Tanous messages::actionParameterNotSupported(res, "ImageURI", 541757178a5SEd Tanous url->buffer()); 542757178a5SEd Tanous return std::nullopt; 543757178a5SEd Tanous } 544757178a5SEd Tanous } 545e5cf777eSEd Tanous else if (url->scheme() == "https") 546e5cf777eSEd Tanous { 547e5cf777eSEd Tanous // Empty paths default to "/" 548e5cf777eSEd Tanous if (url->encoded_path().empty()) 549e5cf777eSEd Tanous { 550e5cf777eSEd Tanous url->set_encoded_path("/"); 551e5cf777eSEd Tanous } 552e5cf777eSEd Tanous } 553757178a5SEd Tanous else 554f86bcc87SEd Tanous { 555f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "ImageURI", imageURI); 556f86bcc87SEd Tanous return std::nullopt; 557f86bcc87SEd Tanous } 558757178a5SEd Tanous 559757178a5SEd Tanous if (url->encoded_path().empty()) 560f86bcc87SEd Tanous { 561757178a5SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI", 562757178a5SEd Tanous "UpdateService.SimpleUpdate"); 563f86bcc87SEd Tanous return std::nullopt; 564f86bcc87SEd Tanous } 565757178a5SEd Tanous 566757178a5SEd Tanous return *url; 567f86bcc87SEd Tanous } 568f86bcc87SEd Tanous 569e5cf777eSEd Tanous inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 570e5cf777eSEd Tanous const boost::urls::url_view_base& url) 571e5cf777eSEd Tanous { 572e5cf777eSEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI", 573e5cf777eSEd Tanous url.buffer()); 574e5cf777eSEd Tanous } 575e5cf777eSEd Tanous 576f5139334SEd Tanous inline void handleUpdateServiceSimpleUpdateAction( 577f5139334SEd Tanous crow::App& app, const crow::Request& req, 578f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 5790554c984SAndrew Geissler { 5803ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 58145ca1b86SEd Tanous { 58245ca1b86SEd Tanous return; 58345ca1b86SEd Tanous } 58445ca1b86SEd Tanous 5850554c984SAndrew Geissler std::optional<std::string> transferProtocol; 5860554c984SAndrew Geissler std::string imageURI; 5870554c984SAndrew Geissler 58862598e31SEd Tanous BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost"); 5890554c984SAndrew Geissler 5900554c984SAndrew Geissler // User can pass in both TransferProtocol and ImageURI parameters or 5914e0453b1SGunnar Mills // they can pass in just the ImageURI with the transfer protocol 5924e0453b1SGunnar Mills // embedded within it. 5930554c984SAndrew Geissler // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin 5940554c984SAndrew Geissler // 2) ImageURI:tftp://1.1.1.1/myfile.bin 5950554c984SAndrew Geissler 596afc474aeSMyung Bae if (!json_util::readJsonAction( // 597afc474aeSMyung Bae req, asyncResp->res, // 598afc474aeSMyung Bae "ImageURI", imageURI, // 599afc474aeSMyung Bae "TransferProtocol", transferProtocol // 600afc474aeSMyung Bae )) 6010554c984SAndrew Geissler { 60262598e31SEd Tanous BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter"); 6030554c984SAndrew Geissler return; 6040554c984SAndrew Geissler } 605f5139334SEd Tanous 606757178a5SEd Tanous std::optional<boost::urls::url> url = 607757178a5SEd Tanous parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res); 608757178a5SEd Tanous if (!url) 6090554c984SAndrew Geissler { 6100554c984SAndrew Geissler return; 6110554c984SAndrew Geissler } 6124e338b23SJagpal Singh Gill if (url->scheme() == "https") 613e5cf777eSEd Tanous { 614e5cf777eSEd Tanous doHttpsUpdate(asyncResp, *url); 615e5cf777eSEd Tanous } 616757178a5SEd Tanous else 617757178a5SEd Tanous { 618757178a5SEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI", 619757178a5SEd Tanous url->buffer()); 620757178a5SEd Tanous return; 621757178a5SEd Tanous } 6220554c984SAndrew Geissler 62362598e31SEd Tanous BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost"); 624729dae72SJennifer Lee } 625729dae72SJennifer Lee 6260ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body) 6270ed80c8cSGeorge Liu { 6282c6ffdb0SEd Tanous std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID()); 6292c6ffdb0SEd Tanous 63062598e31SEd Tanous BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string()); 6310ed80c8cSGeorge Liu std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | 6320ed80c8cSGeorge Liu std::ofstream::trunc); 6330ed80c8cSGeorge Liu // set the permission of the file to 640 634bd79bce8SPatrick Williams std::filesystem::perms permission = 635bd79bce8SPatrick Williams std::filesystem::perms::owner_read | std::filesystem::perms::group_read; 6360ed80c8cSGeorge Liu std::filesystem::permissions(filepath, permission); 6370ed80c8cSGeorge Liu out << body; 6380ed80c8cSGeorge Liu 6390ed80c8cSGeorge Liu if (out.bad()) 6400ed80c8cSGeorge Liu { 6410ed80c8cSGeorge Liu messages::internalError(res); 6420ed80c8cSGeorge Liu cleanUp(); 6430ed80c8cSGeorge Liu } 6440ed80c8cSGeorge Liu } 6450ed80c8cSGeorge Liu 646de0c960cSJagpal Singh Gill // Convert the Request Apply Time to the D-Bus value 647de0c960cSJagpal Singh Gill inline bool convertApplyTime(crow::Response& res, const std::string& applyTime, 648de0c960cSJagpal Singh Gill std::string& applyTimeNewVal) 649de0c960cSJagpal Singh Gill { 650de0c960cSJagpal Singh Gill if (applyTime == "Immediate") 651de0c960cSJagpal Singh Gill { 652de0c960cSJagpal Singh Gill applyTimeNewVal = 653049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate"; 654de0c960cSJagpal Singh Gill } 655de0c960cSJagpal Singh Gill else if (applyTime == "OnReset") 656de0c960cSJagpal Singh Gill { 657de0c960cSJagpal Singh Gill applyTimeNewVal = 658049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset"; 659de0c960cSJagpal Singh Gill } 660de0c960cSJagpal Singh Gill else 661de0c960cSJagpal Singh Gill { 662de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING( 663de0c960cSJagpal Singh Gill "ApplyTime value {} is not in the list of acceptable values", 664de0c960cSJagpal Singh Gill applyTime); 665de0c960cSJagpal Singh Gill messages::propertyValueNotInList(res, applyTime, "ApplyTime"); 666de0c960cSJagpal Singh Gill return false; 667de0c960cSJagpal Singh Gill } 668de0c960cSJagpal Singh Gill return true; 669de0c960cSJagpal Singh Gill } 670de0c960cSJagpal Singh Gill 6710ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 6720ed80c8cSGeorge Liu const std::string& applyTime) 6730ed80c8cSGeorge Liu { 6740ed80c8cSGeorge Liu std::string applyTimeNewVal; 675049079f6SJagpal Singh Gill if (!convertApplyTime(asyncResp->res, applyTime, applyTimeNewVal)) 6760ed80c8cSGeorge Liu { 6770ed80c8cSGeorge Liu return; 6780ed80c8cSGeorge Liu } 6790ed80c8cSGeorge Liu 680e93abac6SGinu George setDbusProperty(asyncResp, "ApplyTime", "xyz.openbmc_project.Settings", 681d02aad39SEd Tanous sdbusplus::message::object_path( 682d02aad39SEd Tanous "/xyz/openbmc_project/software/apply_time"), 683d02aad39SEd Tanous "xyz.openbmc_project.Software.ApplyTime", 684e93abac6SGinu George "RequestedApplyTime", applyTimeNewVal); 6850ed80c8cSGeorge Liu } 6860ed80c8cSGeorge Liu 6872c6f32a9Srajeeranjan struct MultiPartUpdate 6882c6f32a9Srajeeranjan { 6892c6f32a9Srajeeranjan std::string uploadData; 6902c6f32a9Srajeeranjan struct UpdateParameters 6910ed80c8cSGeorge Liu { 692ef93eab3SJagpal Singh Gill std::optional<std::string> applyTime; 693de0c960cSJagpal Singh Gill std::vector<std::string> targets; 6942c6f32a9Srajeeranjan } params; 695ef93eab3SJagpal Singh Gill }; 696ef93eab3SJagpal Singh Gill 697504af5a0SPatrick Williams inline std::optional<std::string> processUrl( 698504af5a0SPatrick Williams boost::system::result<boost::urls::url_view>& url) 699de0c960cSJagpal Singh Gill { 700de0c960cSJagpal Singh Gill if (!url) 701de0c960cSJagpal Singh Gill { 702de0c960cSJagpal Singh Gill return std::nullopt; 703de0c960cSJagpal Singh Gill } 704de0c960cSJagpal Singh Gill if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers", 705de0c960cSJagpal Singh Gill BMCWEB_REDFISH_MANAGER_URI_NAME)) 706de0c960cSJagpal Singh Gill { 707de0c960cSJagpal Singh Gill return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME)); 708de0c960cSJagpal Singh Gill } 709de0c960cSJagpal Singh Gill if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 710de0c960cSJagpal Singh Gill { 711de0c960cSJagpal Singh Gill return std::nullopt; 712de0c960cSJagpal Singh Gill } 713de0c960cSJagpal Singh Gill std::string firmwareId; 714de0c960cSJagpal Singh Gill if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService", 715de0c960cSJagpal Singh Gill "FirmwareInventory", 716de0c960cSJagpal Singh Gill std::ref(firmwareId))) 717de0c960cSJagpal Singh Gill { 718de0c960cSJagpal Singh Gill return std::nullopt; 719de0c960cSJagpal Singh Gill } 720de0c960cSJagpal Singh Gill 721de0c960cSJagpal Singh Gill return std::make_optional(firmwareId); 722de0c960cSJagpal Singh Gill } 723de0c960cSJagpal Singh Gill 7242c6f32a9Srajeeranjan inline std::optional<std::string> parseFormPartName( 7252c6f32a9Srajeeranjan const boost::beast::http::fields::const_iterator& contentDisposition) 726ef93eab3SJagpal Singh Gill { 7272c6f32a9Srajeeranjan size_t semicolonPos = contentDisposition->value().find(';'); 7282c6f32a9Srajeeranjan if (semicolonPos == std::string::npos) 7292c6f32a9Srajeeranjan { 7302c6f32a9Srajeeranjan return std::nullopt; 7312c6f32a9Srajeeranjan } 7322c6f32a9Srajeeranjan 7332c6f32a9Srajeeranjan for (const auto& param : boost::beast::http::param_list{ 7342c6f32a9Srajeeranjan contentDisposition->value().substr(semicolonPos)}) 7352c6f32a9Srajeeranjan { 7362c6f32a9Srajeeranjan if (param.first == "name" && !param.second.empty()) 7372c6f32a9Srajeeranjan { 7382c6f32a9Srajeeranjan return std::string(param.second); 7392c6f32a9Srajeeranjan } 7402c6f32a9Srajeeranjan } 7412c6f32a9Srajeeranjan return std::nullopt; 7422c6f32a9Srajeeranjan } 7432c6f32a9Srajeeranjan 7442c6f32a9Srajeeranjan inline std::optional<MultiPartUpdate::UpdateParameters> processUpdateParameters( 7452c6f32a9Srajeeranjan const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 7462c6f32a9Srajeeranjan std::string_view content) 7472c6f32a9Srajeeranjan { 7482c6f32a9Srajeeranjan MultiPartUpdate::UpdateParameters multiRet; 7492c6f32a9Srajeeranjan nlohmann::json jsonContent = nlohmann::json::parse(content, nullptr, false); 7502c6f32a9Srajeeranjan if (jsonContent.is_discarded()) 7512c6f32a9Srajeeranjan { 7522c6f32a9Srajeeranjan return std::nullopt; 7532c6f32a9Srajeeranjan } 7542c6f32a9Srajeeranjan nlohmann::json::object_t* obj = 7552c6f32a9Srajeeranjan jsonContent.get_ptr<nlohmann::json::object_t*>(); 7562c6f32a9Srajeeranjan if (obj == nullptr) 7572c6f32a9Srajeeranjan { 7582c6f32a9Srajeeranjan messages::propertyValueTypeError(asyncResp->res, content, 7592c6f32a9Srajeeranjan "UpdateParameters"); 7602c6f32a9Srajeeranjan return std::nullopt; 7612c6f32a9Srajeeranjan } 7622c6f32a9Srajeeranjan 7632c6f32a9Srajeeranjan std::vector<std::string> tempTargets; 7642c6f32a9Srajeeranjan if (!json_util::readJsonObject( // 7652c6f32a9Srajeeranjan *obj, asyncResp->res, // 7662c6f32a9Srajeeranjan "@Redfish.OperationApplyTime", multiRet.applyTime, // 7672c6f32a9Srajeeranjan "Targets", tempTargets // 7682c6f32a9Srajeeranjan )) 7692c6f32a9Srajeeranjan { 7702c6f32a9Srajeeranjan return std::nullopt; 7712c6f32a9Srajeeranjan } 7722c6f32a9Srajeeranjan 7732c6f32a9Srajeeranjan for (size_t urlIndex = 0; urlIndex < tempTargets.size(); urlIndex++) 7742c6f32a9Srajeeranjan { 7752c6f32a9Srajeeranjan const std::string& target = tempTargets[urlIndex]; 7762c6f32a9Srajeeranjan boost::system::result<boost::urls::url_view> url = 7772c6f32a9Srajeeranjan boost::urls::parse_origin_form(target); 7782c6f32a9Srajeeranjan auto res = processUrl(url); 7792c6f32a9Srajeeranjan if (!res.has_value()) 7802c6f32a9Srajeeranjan { 7812c6f32a9Srajeeranjan messages::propertyValueFormatError( 7822c6f32a9Srajeeranjan asyncResp->res, target, std::format("Targets/{}", urlIndex)); 7832c6f32a9Srajeeranjan return std::nullopt; 7842c6f32a9Srajeeranjan } 7852c6f32a9Srajeeranjan multiRet.targets.emplace_back(res.value()); 7862c6f32a9Srajeeranjan } 7872c6f32a9Srajeeranjan if (multiRet.targets.size() != 1) 7882c6f32a9Srajeeranjan { 7892c6f32a9Srajeeranjan messages::propertyValueFormatError(asyncResp->res, multiRet.targets, 7902c6f32a9Srajeeranjan "Targets"); 7912c6f32a9Srajeeranjan return std::nullopt; 7922c6f32a9Srajeeranjan } 7932c6f32a9Srajeeranjan return multiRet; 7942c6f32a9Srajeeranjan } 7952c6f32a9Srajeeranjan 7962c6f32a9Srajeeranjan inline std::optional<MultiPartUpdate> extractMultipartUpdateParameters( 7972c6f32a9Srajeeranjan const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, MultipartParser parser) 7982c6f32a9Srajeeranjan { 7992c6f32a9Srajeeranjan MultiPartUpdate multiRet; 800ef93eab3SJagpal Singh Gill for (FormPart& formpart : parser.mime_fields) 8010ed80c8cSGeorge Liu { 8020ed80c8cSGeorge Liu boost::beast::http::fields::const_iterator it = 8030ed80c8cSGeorge Liu formpart.fields.find("Content-Disposition"); 8040ed80c8cSGeorge Liu if (it == formpart.fields.end()) 8050ed80c8cSGeorge Liu { 80662598e31SEd Tanous BMCWEB_LOG_ERROR("Couldn't find Content-Disposition"); 807ef93eab3SJagpal Singh Gill return std::nullopt; 8080ed80c8cSGeorge Liu } 80962598e31SEd Tanous BMCWEB_LOG_INFO("Parsing value {}", it->value()); 8100ed80c8cSGeorge Liu 8112c6f32a9Srajeeranjan auto formFieldNameOpt = parseFormPartName(it); 8122c6f32a9Srajeeranjan if (!formFieldNameOpt.has_value()) 8130ed80c8cSGeorge Liu { 8140ed80c8cSGeorge Liu continue; 8150ed80c8cSGeorge Liu } 8160ed80c8cSGeorge Liu 8172c6f32a9Srajeeranjan const std::string& formFieldName = formFieldNameOpt.value(); 8180ed80c8cSGeorge Liu 8192c6f32a9Srajeeranjan if (formFieldName == "UpdateParameters") 8200ed80c8cSGeorge Liu { 8212c6f32a9Srajeeranjan std::optional<MultiPartUpdate::UpdateParameters> params = 8222c6f32a9Srajeeranjan processUpdateParameters(asyncResp, formpart.content); 8232c6f32a9Srajeeranjan if (!params) 824ac1e1246SEd Tanous { 825ac1e1246SEd Tanous return std::nullopt; 826ac1e1246SEd Tanous } 8272c6f32a9Srajeeranjan multiRet.params = std::move(*params); 8287cb59f65SEd Tanous } 8292c6f32a9Srajeeranjan else if (formFieldName == "UpdateFile") 8300ed80c8cSGeorge Liu { 831ef93eab3SJagpal Singh Gill multiRet.uploadData = std::move(formpart.content); 8320ed80c8cSGeorge Liu } 8330ed80c8cSGeorge Liu } 8340ed80c8cSGeorge Liu 835ef93eab3SJagpal Singh Gill if (multiRet.uploadData.empty()) 8360ed80c8cSGeorge Liu { 83762598e31SEd Tanous BMCWEB_LOG_ERROR("Upload data is NULL"); 8380ed80c8cSGeorge Liu messages::propertyMissing(asyncResp->res, "UpdateFile"); 839ef93eab3SJagpal Singh Gill return std::nullopt; 8400ed80c8cSGeorge Liu } 8412c6f32a9Srajeeranjan if (multiRet.params.targets.empty()) 8420ed80c8cSGeorge Liu { 843ef93eab3SJagpal Singh Gill messages::propertyMissing(asyncResp->res, "Targets"); 844ef93eab3SJagpal Singh Gill return std::nullopt; 845ef93eab3SJagpal Singh Gill } 846ef93eab3SJagpal Singh Gill return multiRet; 8470ed80c8cSGeorge Liu } 8480ed80c8cSGeorge Liu 849bd79bce8SPatrick Williams inline void handleStartUpdate( 850bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 851bd79bce8SPatrick Williams const std::string& objectPath, const boost::system::error_code& ec, 852de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) 853de0c960cSJagpal Singh Gill { 854de0c960cSJagpal Singh Gill if (ec) 855de0c960cSJagpal Singh Gill { 856de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 857de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 858de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 859de0c960cSJagpal Singh Gill return; 860de0c960cSJagpal Singh Gill } 861de0c960cSJagpal Singh Gill 862587090cdSJagpal Singh Gill BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}", 863587090cdSJagpal Singh Gill objectPath, retPath.str); 864587090cdSJagpal Singh Gill createTask(asyncResp, std::move(payload), retPath); 865de0c960cSJagpal Singh Gill } 866de0c960cSJagpal Singh Gill 867bd79bce8SPatrick Williams inline void startUpdate( 868bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 869bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 870bd79bce8SPatrick Williams const std::string& objectPath, const std::string& serviceName) 871de0c960cSJagpal Singh Gill { 872177612aaSEd Tanous dbus::utility::async_method_call( 873177612aaSEd Tanous asyncResp, 874de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), 875de0c960cSJagpal Singh Gill objectPath](const boost::system::error_code& ec1, 876de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) mutable { 877de0c960cSJagpal Singh Gill handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1, 878de0c960cSJagpal Singh Gill retPath); 879de0c960cSJagpal Singh Gill }, 880de0c960cSJagpal Singh Gill serviceName, objectPath, "xyz.openbmc_project.Software.Update", 881de0c960cSJagpal Singh Gill "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime); 882de0c960cSJagpal Singh Gill } 883de0c960cSJagpal Singh Gill 88408f61d53SJagpal Singh Gill inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 88508f61d53SJagpal Singh Gill task::Payload payload, const MemoryFileDescriptor& memfd, 88608f61d53SJagpal Singh Gill const std::string& applyTime, const std::string& target, 887de0c960cSJagpal Singh Gill const boost::system::error_code& ec, 888de0c960cSJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& subtree) 889de0c960cSJagpal Singh Gill { 89008f61d53SJagpal Singh Gill using SwInfoMap = std::unordered_map< 89108f61d53SJagpal Singh Gill std::string, std::pair<sdbusplus::message::object_path, std::string>>; 892de0c960cSJagpal Singh Gill SwInfoMap swInfoMap; 893de0c960cSJagpal Singh Gill 894de0c960cSJagpal Singh Gill if (ec) 895de0c960cSJagpal Singh Gill { 896de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 897de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 898de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 899de0c960cSJagpal Singh Gill return; 900de0c960cSJagpal Singh Gill } 901de0c960cSJagpal Singh Gill BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size()); 902de0c960cSJagpal Singh Gill 90308f61d53SJagpal Singh Gill for (const auto& entry : subtree) 904de0c960cSJagpal Singh Gill { 90508f61d53SJagpal Singh Gill sdbusplus::message::object_path path(entry.first); 906de0c960cSJagpal Singh Gill std::string swId = path.filename(); 90708f61d53SJagpal Singh Gill swInfoMap.emplace(swId, make_pair(path, entry.second[0].first)); 908de0c960cSJagpal Singh Gill } 909de0c960cSJagpal Singh Gill 910de0c960cSJagpal Singh Gill auto swEntry = swInfoMap.find(target); 911de0c960cSJagpal Singh Gill if (swEntry == swInfoMap.end()) 912de0c960cSJagpal Singh Gill { 913de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target); 914de0c960cSJagpal Singh Gill messages::propertyValueFormatError(asyncResp->res, target, "Targets"); 915de0c960cSJagpal Singh Gill return; 916de0c960cSJagpal Singh Gill } 917de0c960cSJagpal Singh Gill 91808f61d53SJagpal Singh Gill BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}", 91908f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 920de0c960cSJagpal Singh Gill 92108f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 92208f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 92308f61d53SJagpal Singh Gill } 92408f61d53SJagpal Singh Gill 925bd79bce8SPatrick Williams inline void handleBMCUpdate( 926bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 927bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 92808f61d53SJagpal Singh Gill const boost::system::error_code& ec, 92908f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& functionalSoftware) 93008f61d53SJagpal Singh Gill { 93108f61d53SJagpal Singh Gill if (ec) 93208f61d53SJagpal Singh Gill { 93308f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 93408f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 93508f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 93608f61d53SJagpal Singh Gill return; 93708f61d53SJagpal Singh Gill } 93808f61d53SJagpal Singh Gill if (functionalSoftware.size() != 1) 93908f61d53SJagpal Singh Gill { 94008f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("Found {} functional software endpoints", 94108f61d53SJagpal Singh Gill functionalSoftware.size()); 94208f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 94308f61d53SJagpal Singh Gill return; 94408f61d53SJagpal Singh Gill } 94508f61d53SJagpal Singh Gill 94608f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 94708f61d53SJagpal Singh Gill functionalSoftware[0], "xyz.openbmc_project.Software.Manager"); 948de0c960cSJagpal Singh Gill } 949de0c960cSJagpal Singh Gill 950bd79bce8SPatrick Williams inline void processUpdateRequest( 951bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 9529dae4deeSJagpal Singh Gill task::Payload&& payload, std::string_view body, 953bd79bce8SPatrick Williams const std::string& applyTime, std::vector<std::string>& targets) 954de0c960cSJagpal Singh Gill { 955de0c960cSJagpal Singh Gill MemoryFileDescriptor memfd("update-image"); 956de0c960cSJagpal Singh Gill if (memfd.fd == -1) 957de0c960cSJagpal Singh Gill { 958de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to create image memfd"); 959de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 960de0c960cSJagpal Singh Gill return; 961de0c960cSJagpal Singh Gill } 962de0c960cSJagpal Singh Gill if (write(memfd.fd, body.data(), body.length()) != 963de0c960cSJagpal Singh Gill static_cast<ssize_t>(body.length())) 964de0c960cSJagpal Singh Gill { 965de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to write to image memfd"); 966de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 967de0c960cSJagpal Singh Gill return; 968de0c960cSJagpal Singh Gill } 969de0c960cSJagpal Singh Gill if (!memfd.rewind()) 970de0c960cSJagpal Singh Gill { 971de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 972de0c960cSJagpal Singh Gill return; 973de0c960cSJagpal Singh Gill } 974de0c960cSJagpal Singh Gill 975de0c960cSJagpal Singh Gill if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME) 976de0c960cSJagpal Singh Gill { 97708f61d53SJagpal Singh Gill dbus::utility::getAssociationEndPoints( 97889449bbeSJagpal Singh Gill "/xyz/openbmc_project/software/bmc/updateable", 97908f61d53SJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 98008f61d53SJagpal Singh Gill applyTime]( 98108f61d53SJagpal Singh Gill const boost::system::error_code& ec, 98208f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& objectPaths) mutable { 983bd79bce8SPatrick Williams handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime, 984bd79bce8SPatrick Williams ec, objectPaths); 98508f61d53SJagpal Singh Gill }); 986de0c960cSJagpal Singh Gill } 987de0c960cSJagpal Singh Gill else 988de0c960cSJagpal Singh Gill { 989de0c960cSJagpal Singh Gill constexpr std::array<std::string_view, 1> interfaces = { 990de0c960cSJagpal Singh Gill "xyz.openbmc_project.Software.Version"}; 99108f61d53SJagpal Singh Gill dbus::utility::getSubTree( 992de0c960cSJagpal Singh Gill "/xyz/openbmc_project/software", 1, interfaces, 993de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 99408f61d53SJagpal Singh Gill applyTime, targets](const boost::system::error_code& ec, 99508f61d53SJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& 996de0c960cSJagpal Singh Gill subtree) mutable { 99708f61d53SJagpal Singh Gill getSwInfo(asyncResp, std::move(payload), memfd, applyTime, 99808f61d53SJagpal Singh Gill targets[0], ec, subtree); 999de0c960cSJagpal Singh Gill }); 1000de0c960cSJagpal Singh Gill } 1001de0c960cSJagpal Singh Gill } 1002de0c960cSJagpal Singh Gill 1003504af5a0SPatrick Williams inline void updateMultipartContext( 1004504af5a0SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1005ef93eab3SJagpal Singh Gill const crow::Request& req, MultipartParser&& parser) 1006ef93eab3SJagpal Singh Gill { 10072c6f32a9Srajeeranjan std::optional<MultiPartUpdate> multipart = 1008ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters(asyncResp, std::move(parser)); 1009ef93eab3SJagpal Singh Gill if (!multipart) 1010ef93eab3SJagpal Singh Gill { 1011ef93eab3SJagpal Singh Gill return; 1012ef93eab3SJagpal Singh Gill } 10132c6f32a9Srajeeranjan if (!multipart->params.applyTime) 1014ef93eab3SJagpal Singh Gill { 10152c6f32a9Srajeeranjan multipart->params.applyTime = "OnReset"; 1016ef93eab3SJagpal Singh Gill } 1017ef93eab3SJagpal Singh Gill 1018de0c960cSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 1019de0c960cSJagpal Singh Gill { 10209dae4deeSJagpal Singh Gill std::string applyTimeNewVal; 10212c6f32a9Srajeeranjan if (!convertApplyTime(asyncResp->res, *multipart->params.applyTime, 10229dae4deeSJagpal Singh Gill applyTimeNewVal)) 10239dae4deeSJagpal Singh Gill { 10249dae4deeSJagpal Singh Gill return; 10259dae4deeSJagpal Singh Gill } 10269dae4deeSJagpal Singh Gill task::Payload payload(req); 10279dae4deeSJagpal Singh Gill 10289dae4deeSJagpal Singh Gill processUpdateRequest(asyncResp, std::move(payload), 10299dae4deeSJagpal Singh Gill multipart->uploadData, applyTimeNewVal, 10302c6f32a9Srajeeranjan multipart->params.targets); 1031de0c960cSJagpal Singh Gill } 1032de0c960cSJagpal Singh Gill else 1033de0c960cSJagpal Singh Gill { 10342c6f32a9Srajeeranjan setApplyTime(asyncResp, *multipart->params.applyTime); 10350ed80c8cSGeorge Liu 10366b54e4e0SEd Tanous // Setup callback for when new software detected 1037de0c960cSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 1038de0c960cSJagpal Singh Gill "/redfish/v1/UpdateService"); 10396b54e4e0SEd Tanous 1040ef93eab3SJagpal Singh Gill uploadImageFile(asyncResp->res, multipart->uploadData); 10410ed80c8cSGeorge Liu } 1042de0c960cSJagpal Singh Gill } 10430ed80c8cSGeorge Liu 10449dae4deeSJagpal Singh Gill inline void doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 10459dae4deeSJagpal Singh Gill const crow::Request& req) 10469dae4deeSJagpal Singh Gill { 10479dae4deeSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 10489dae4deeSJagpal Singh Gill { 10499dae4deeSJagpal Singh Gill task::Payload payload(req); 10509dae4deeSJagpal Singh Gill // HTTP push only supports BMC updates (with ApplyTime as immediate) for 10519dae4deeSJagpal Singh Gill // backwards compatibility. Specific component updates will be handled 10529dae4deeSJagpal Singh Gill // through Multipart form HTTP push. 10539dae4deeSJagpal Singh Gill std::vector<std::string> targets; 10549dae4deeSJagpal Singh Gill targets.emplace_back(BMCWEB_REDFISH_MANAGER_URI_NAME); 10559dae4deeSJagpal Singh Gill 10569dae4deeSJagpal Singh Gill processUpdateRequest( 10579dae4deeSJagpal Singh Gill asyncResp, std::move(payload), req.body(), 10589dae4deeSJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate", 10599dae4deeSJagpal Singh Gill targets); 10609dae4deeSJagpal Singh Gill } 10619dae4deeSJagpal Singh Gill else 10629dae4deeSJagpal Singh Gill { 10639dae4deeSJagpal Singh Gill // Setup callback for when new software detected 10649dae4deeSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 10659dae4deeSJagpal Singh Gill "/redfish/v1/UpdateService"); 10669dae4deeSJagpal Singh Gill 10679dae4deeSJagpal Singh Gill uploadImageFile(asyncResp->res, req.body()); 10689dae4deeSJagpal Singh Gill } 10699dae4deeSJagpal Singh Gill } 10709dae4deeSJagpal Singh Gill 1071504af5a0SPatrick Williams inline void handleUpdateServicePost( 1072504af5a0SPatrick Williams App& app, const crow::Request& req, 1073c2051d11SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1074c2051d11SEd Tanous { 10753ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1076c2051d11SEd Tanous { 1077c2051d11SEd Tanous return; 1078c2051d11SEd Tanous } 1079b33a4327SNinad Palsule std::string_view contentType = req.getHeaderValue("Content-Type"); 1080b33a4327SNinad Palsule 108162598e31SEd Tanous BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType); 1082b33a4327SNinad Palsule 10830c814aa6Srajeeranjan // Make sure that content type is application/octet-stream 108418f8f608SEd Tanous if (bmcweb::asciiIEquals(contentType, "application/octet-stream")) 1085b33a4327SNinad Palsule { 10869dae4deeSJagpal Singh Gill doHTTPUpdate(asyncResp, req); 1087b33a4327SNinad Palsule } 10880c814aa6Srajeeranjan else 10890c814aa6Srajeeranjan { 10900c814aa6Srajeeranjan BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType); 10910c814aa6Srajeeranjan asyncResp->res.result(boost::beast::http::status::bad_request); 10920c814aa6Srajeeranjan } 10930c814aa6Srajeeranjan } 10940c814aa6Srajeeranjan 10950c814aa6Srajeeranjan inline void handleUpdateServiceMultipartUpdatePost( 10960c814aa6Srajeeranjan App& app, const crow::Request& req, 10970c814aa6Srajeeranjan const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 10980c814aa6Srajeeranjan { 10990c814aa6Srajeeranjan if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 11000c814aa6Srajeeranjan { 11010c814aa6Srajeeranjan return; 11020c814aa6Srajeeranjan } 11030c814aa6Srajeeranjan 11040c814aa6Srajeeranjan std::string_view contentType = req.getHeaderValue("Content-Type"); 11050c814aa6Srajeeranjan // Make sure that content type is multipart/form-data 11060c814aa6Srajeeranjan if (contentType.starts_with("multipart/form-data")) 1107b33a4327SNinad Palsule { 1108b33a4327SNinad Palsule MultipartParser parser; 1109c2051d11SEd Tanous 11100ed80c8cSGeorge Liu ParserError ec = parser.parse(req); 11110ed80c8cSGeorge Liu if (ec != ParserError::PARSER_SUCCESS) 11120ed80c8cSGeorge Liu { 11130ed80c8cSGeorge Liu // handle error 111462598e31SEd Tanous BMCWEB_LOG_ERROR("MIME parse failed, ec : {}", 111562598e31SEd Tanous static_cast<int>(ec)); 11160ed80c8cSGeorge Liu messages::internalError(asyncResp->res); 11170ed80c8cSGeorge Liu return; 11180ed80c8cSGeorge Liu } 11196b54e4e0SEd Tanous 1120ef93eab3SJagpal Singh Gill updateMultipartContext(asyncResp, req, std::move(parser)); 1121c2051d11SEd Tanous } 1122b33a4327SNinad Palsule else 1123b33a4327SNinad Palsule { 112462598e31SEd Tanous BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType); 1125*de5259d1SSandeep asyncResp->res.result( 1126*de5259d1SSandeep boost::beast::http::status::unsupported_media_type); 1127b33a4327SNinad Palsule } 1128b33a4327SNinad Palsule } 1129c2051d11SEd Tanous 1130504af5a0SPatrick Williams inline void handleUpdateServiceGet( 1131504af5a0SPatrick Williams App& app, const crow::Request& req, 1132f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 11331abe55efSEd Tanous { 11343ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 113545ca1b86SEd Tanous { 113645ca1b86SEd Tanous return; 113745ca1b86SEd Tanous } 11388d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 11390ed80c8cSGeorge Liu "#UpdateService.v1_11_1.UpdateService"; 11408d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService"; 11418d1b46d7Szhanghch05 asyncResp->res.jsonValue["Id"] = "UpdateService"; 1142002d39b4SEd Tanous asyncResp->res.jsonValue["Description"] = "Service for Software Update"; 11438d1b46d7Szhanghch05 asyncResp->res.jsonValue["Name"] = "Update Service"; 11444dc23f3fSEd Tanous 11457e860f15SJohn Edward Broadbent asyncResp->res.jsonValue["HttpPushUri"] = 11464dc23f3fSEd Tanous "/redfish/v1/UpdateService/update"; 11470ed80c8cSGeorge Liu asyncResp->res.jsonValue["MultipartHttpPushUri"] = 11480c814aa6Srajeeranjan "/redfish/v1/UpdateService/update-multipart"; 11494dc23f3fSEd Tanous 11500f74e643SEd Tanous // UpdateService cannot be disabled 11518d1b46d7Szhanghch05 asyncResp->res.jsonValue["ServiceEnabled"] = true; 11521476687dSEd Tanous asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] = 11531476687dSEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1154d61e5194STejas Patil // Get the MaxImageSizeBytes 1155bd79bce8SPatrick Williams asyncResp->res.jsonValue["MaxImageSizeBytes"] = 1156bd79bce8SPatrick Williams BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024; 1157d61e5194STejas Patil 11586a37140aSEd Tanous if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE) 11596a37140aSEd Tanous { 11600554c984SAndrew Geissler // Update Actions object. 11610554c984SAndrew Geissler nlohmann::json& updateSvcSimpleUpdate = 1162002d39b4SEd Tanous asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"]; 11630554c984SAndrew Geissler updateSvcSimpleUpdate["target"] = 11640554c984SAndrew Geissler "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate"; 1165757178a5SEd Tanous 1166757178a5SEd Tanous nlohmann::json::array_t allowed; 1167e5cf777eSEd Tanous allowed.emplace_back(update_service::TransferProtocolType::HTTPS); 1168757178a5SEd Tanous updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = 1169757178a5SEd Tanous std::move(allowed); 11706a37140aSEd Tanous } 1171757178a5SEd Tanous 1172539d8c6bSEd Tanous asyncResp->res 1173539d8c6bSEd Tanous .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] = 1174539d8c6bSEd Tanous update_service::ApplyTime::Immediate; 1175729dae72SJennifer Lee } 1176729dae72SJennifer Lee 1177f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryCollectionGet( 1178f5139334SEd Tanous App& app, const crow::Request& req, 1179f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 11801abe55efSEd Tanous { 11813ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 118245ca1b86SEd Tanous { 118345ca1b86SEd Tanous return; 118445ca1b86SEd Tanous } 11858d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 11860f74e643SEd Tanous "#SoftwareInventoryCollection.SoftwareInventoryCollection"; 11878d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = 11880f74e643SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1189002d39b4SEd Tanous asyncResp->res.jsonValue["Name"] = "Software Inventory Collection"; 119008d81adaSJohn Edward Broadbent const std::array<const std::string_view, 1> iface = { 1191e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 11926c4eb9deSJennifer Lee 119308d81adaSJohn Edward Broadbent redfish::collection_util::getCollectionMembers( 119408d81adaSJohn Edward Broadbent asyncResp, 1195f5139334SEd Tanous boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface, 1196f5139334SEd Tanous "/xyz/openbmc_project/software"); 1197729dae72SJennifer Lee } 1198f5139334SEd Tanous 119950ab50dbSAlexander Hansen inline void addRelatedItem(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 120050ab50dbSAlexander Hansen const boost::urls::url& url) 120150ab50dbSAlexander Hansen { 120250ab50dbSAlexander Hansen nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"]; 120350ab50dbSAlexander Hansen nlohmann::json::object_t item; 120450ab50dbSAlexander Hansen item["@odata.id"] = url; 120550ab50dbSAlexander Hansen relatedItem.emplace_back(std::move(item)); 120650ab50dbSAlexander Hansen asyncResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size(); 120750ab50dbSAlexander Hansen } 120850ab50dbSAlexander Hansen 120987d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */ 1210f5139334SEd Tanous inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 121187d84729SAndrew Geissler const std::string& purpose) 121287d84729SAndrew Geissler { 1213eee0013eSWilly Tu if (purpose == sw_util::bmcPurpose) 121487d84729SAndrew Geissler { 121550ab50dbSAlexander Hansen auto url = boost::urls::format("/redfish/v1/Managers/{}", 121650ab50dbSAlexander Hansen BMCWEB_REDFISH_MANAGER_URI_NAME); 121750ab50dbSAlexander Hansen addRelatedItem(asyncResp, url); 121887d84729SAndrew Geissler } 1219eee0013eSWilly Tu else if (purpose == sw_util::biosPurpose) 122087d84729SAndrew Geissler { 122150ab50dbSAlexander Hansen auto url = boost::urls::format("/redfish/v1/Systems/{}/Bios", 1222253f11b8SEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 122350ab50dbSAlexander Hansen 122450ab50dbSAlexander Hansen addRelatedItem(asyncResp, url); 122587d84729SAndrew Geissler } 122687d84729SAndrew Geissler else 122787d84729SAndrew Geissler { 1228bf2ddedeSCarson Labrado BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose); 122987d84729SAndrew Geissler } 123087d84729SAndrew Geissler } 123187d84729SAndrew Geissler 123278e69027SAlexander Hansen inline void getSoftwareVersionCallback( 1233504af5a0SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 123478e69027SAlexander Hansen const std::string& swId, const boost::system::error_code& ec, 123578e69027SAlexander Hansen const dbus::utility::DBusPropertiesMap& propertiesList) 1236af24660dSWilly Tu { 12378b24275dSEd Tanous if (ec) 1238af24660dSWilly Tu { 1239af24660dSWilly Tu messages::internalError(asyncResp->res); 1240af24660dSWilly Tu return; 1241af24660dSWilly Tu } 1242af24660dSWilly Tu const std::string* swInvPurpose = nullptr; 1243af24660dSWilly Tu const std::string* version = nullptr; 1244d1bde9e5SKrzysztof Grobelny const bool success = sdbusplus::unpackPropertiesNoThrow( 1245d1bde9e5SKrzysztof Grobelny dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose", 1246d1bde9e5SKrzysztof Grobelny swInvPurpose, "Version", version); 1247d1bde9e5SKrzysztof Grobelny if (!success) 1248af24660dSWilly Tu { 1249d1bde9e5SKrzysztof Grobelny messages::internalError(asyncResp->res); 1250d1bde9e5SKrzysztof Grobelny return; 1251af24660dSWilly Tu } 1252af24660dSWilly Tu if (swInvPurpose == nullptr) 1253af24660dSWilly Tu { 125462598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!"); 1255af24660dSWilly Tu messages::internalError(asyncResp->res); 1256af24660dSWilly Tu return; 1257af24660dSWilly Tu } 125862598e31SEd Tanous BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose); 1259af24660dSWilly Tu if (version == nullptr) 1260af24660dSWilly Tu { 126162598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Version\"!"); 1262af24660dSWilly Tu messages::internalError(asyncResp->res); 1263af24660dSWilly Tu return; 1264af24660dSWilly Tu } 1265af24660dSWilly Tu asyncResp->res.jsonValue["Version"] = *version; 1266af24660dSWilly Tu asyncResp->res.jsonValue["Id"] = swId; 1267af24660dSWilly Tu // swInvPurpose is of format: 1268af24660dSWilly Tu // xyz.openbmc_project.Software.Version.VersionPurpose.ABC 1269af24660dSWilly Tu // Translate this to "ABC image" 1270af24660dSWilly Tu size_t endDesc = swInvPurpose->rfind('.'); 1271af24660dSWilly Tu if (endDesc == std::string::npos) 1272af24660dSWilly Tu { 1273af24660dSWilly Tu messages::internalError(asyncResp->res); 1274af24660dSWilly Tu return; 1275af24660dSWilly Tu } 1276af24660dSWilly Tu endDesc++; 1277af24660dSWilly Tu if (endDesc >= swInvPurpose->size()) 1278af24660dSWilly Tu { 1279af24660dSWilly Tu messages::internalError(asyncResp->res); 1280af24660dSWilly Tu return; 1281af24660dSWilly Tu } 1282af24660dSWilly Tu std::string formatDesc = swInvPurpose->substr(endDesc); 1283af24660dSWilly Tu asyncResp->res.jsonValue["Description"] = formatDesc + " image"; 1284af24660dSWilly Tu getRelatedItems(asyncResp, *swInvPurpose); 128578e69027SAlexander Hansen } 128678e69027SAlexander Hansen 128778e69027SAlexander Hansen inline void getSoftwareVersion( 128878e69027SAlexander Hansen const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 128978e69027SAlexander Hansen const std::string& service, const std::string& path, 129078e69027SAlexander Hansen const std::string& swId) 129178e69027SAlexander Hansen { 129278e69027SAlexander Hansen dbus::utility::getAllProperties( 129378e69027SAlexander Hansen service, path, "xyz.openbmc_project.Software.Version", 129478e69027SAlexander Hansen std::bind_front(getSoftwareVersionCallback, asyncResp, swId)); 1295af24660dSWilly Tu } 1296af24660dSWilly Tu 1297609b4ab0SAlexander Hansen inline void handleUpdateServiceFirmwareInventoryGetCallback( 1298609b4ab0SAlexander Hansen const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1299609b4ab0SAlexander Hansen const std::shared_ptr<std::string>& swId, 1300609b4ab0SAlexander Hansen const boost::system::error_code& ec, 1301609b4ab0SAlexander Hansen const dbus::utility::MapperGetSubTreeResponse& subtree) 1302609b4ab0SAlexander Hansen { 1303609b4ab0SAlexander Hansen BMCWEB_LOG_DEBUG("doGet callback..."); 1304609b4ab0SAlexander Hansen if (ec) 1305609b4ab0SAlexander Hansen { 1306609b4ab0SAlexander Hansen messages::internalError(asyncResp->res); 1307609b4ab0SAlexander Hansen return; 1308609b4ab0SAlexander Hansen } 1309609b4ab0SAlexander Hansen // Ensure we find our input swId, otherwise return an error 1310609b4ab0SAlexander Hansen bool found = false; 1311609b4ab0SAlexander Hansen for (const std::pair< 1312609b4ab0SAlexander Hansen std::string, 1313609b4ab0SAlexander Hansen std::vector<std::pair<std::string, std::vector<std::string>>>>& 1314609b4ab0SAlexander Hansen obj : subtree) 1315609b4ab0SAlexander Hansen { 1316609b4ab0SAlexander Hansen sdbusplus::message::object_path path(obj.first); 1317609b4ab0SAlexander Hansen std::string id = path.filename(); 1318609b4ab0SAlexander Hansen if (id.empty()) 1319609b4ab0SAlexander Hansen { 1320609b4ab0SAlexander Hansen BMCWEB_LOG_DEBUG("Failed to find software id in {}", obj.first); 1321609b4ab0SAlexander Hansen continue; 1322609b4ab0SAlexander Hansen } 1323609b4ab0SAlexander Hansen if (id != *swId) 1324609b4ab0SAlexander Hansen { 1325609b4ab0SAlexander Hansen continue; 1326609b4ab0SAlexander Hansen } 1327609b4ab0SAlexander Hansen if (obj.second.empty()) 1328609b4ab0SAlexander Hansen { 1329609b4ab0SAlexander Hansen continue; 1330609b4ab0SAlexander Hansen } 1331609b4ab0SAlexander Hansen found = true; 1332609b4ab0SAlexander Hansen sw_util::getSwStatus(asyncResp, swId, obj.second[0].first); 1333609b4ab0SAlexander Hansen sw_util::getSwMinimumVersion(asyncResp, swId, obj.second[0].first); 1334609b4ab0SAlexander Hansen getSoftwareVersion(asyncResp, obj.second[0].first, obj.first, *swId); 1335609b4ab0SAlexander Hansen } 1336609b4ab0SAlexander Hansen if (!found) 1337609b4ab0SAlexander Hansen { 1338609b4ab0SAlexander Hansen BMCWEB_LOG_WARNING("Input swID {} not found!", *swId); 1339609b4ab0SAlexander Hansen messages::resourceMissingAtURI( 1340609b4ab0SAlexander Hansen asyncResp->res, 1341609b4ab0SAlexander Hansen boost::urls::format( 1342609b4ab0SAlexander Hansen "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId)); 1343609b4ab0SAlexander Hansen return; 1344609b4ab0SAlexander Hansen } 1345609b4ab0SAlexander Hansen asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1346609b4ab0SAlexander Hansen "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId); 1347609b4ab0SAlexander Hansen asyncResp->res.jsonValue["@odata.type"] = 1348609b4ab0SAlexander Hansen "#SoftwareInventory.v1_1_0.SoftwareInventory"; 1349609b4ab0SAlexander Hansen asyncResp->res.jsonValue["Name"] = "Software Inventory"; 1350609b4ab0SAlexander Hansen asyncResp->res.jsonValue["Status"]["HealthRollup"] = resource::Health::OK; 1351609b4ab0SAlexander Hansen asyncResp->res.jsonValue["Updateable"] = false; 1352609b4ab0SAlexander Hansen sw_util::getSwUpdatableStatus(asyncResp, swId); 1353609b4ab0SAlexander Hansen } 1354609b4ab0SAlexander Hansen 1355f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryGet( 1356f5139334SEd Tanous App& app, const crow::Request& req, 135745ca1b86SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1358f5139334SEd Tanous const std::string& param) 1359f5139334SEd Tanous { 13603ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 136145ca1b86SEd Tanous { 136245ca1b86SEd Tanous return; 136345ca1b86SEd Tanous } 1364f5139334SEd Tanous std::shared_ptr<std::string> swId = std::make_shared<std::string>(param); 1365c711bf86SEd Tanous 1366e99073f5SGeorge Liu constexpr std::array<std::string_view, 1> interfaces = { 1367e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 1368e99073f5SGeorge Liu dbus::utility::getSubTree( 1369f90af522SAbiola Asojo "/xyz/openbmc_project/software/", 0, interfaces, 1370609b4ab0SAlexander Hansen std::bind_front(handleUpdateServiceFirmwareInventoryGetCallback, 1371609b4ab0SAlexander Hansen asyncResp, swId)); 1372f5139334SEd Tanous } 1373f5139334SEd Tanous 1374f5139334SEd Tanous inline void requestRoutesUpdateService(App& app) 1375f5139334SEd Tanous { 13766a37140aSEd Tanous if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE) 13776a37140aSEd Tanous { 1378f5139334SEd Tanous BMCWEB_ROUTE( 13796a37140aSEd Tanous app, 13806a37140aSEd Tanous "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/") 1381f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1382f5139334SEd Tanous .methods(boost::beast::http::verb::post)(std::bind_front( 1383f5139334SEd Tanous handleUpdateServiceSimpleUpdateAction, std::ref(app))); 13846a37140aSEd Tanous } 1385f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/") 1386f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventory) 1387f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1388f5139334SEd Tanous handleUpdateServiceFirmwareInventoryGet, std::ref(app))); 1389f5139334SEd Tanous 1390f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/") 1391f5139334SEd Tanous .privileges(redfish::privileges::getUpdateService) 1392f5139334SEd Tanous .methods(boost::beast::http::verb::get)( 1393f5139334SEd Tanous std::bind_front(handleUpdateServiceGet, std::ref(app))); 1394f5139334SEd Tanous 1395f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/") 1396f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1397f5139334SEd Tanous .methods(boost::beast::http::verb::post)( 1398f5139334SEd Tanous std::bind_front(handleUpdateServicePost, std::ref(app))); 1399f5139334SEd Tanous 14000c814aa6Srajeeranjan BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update-multipart/") 14010c814aa6Srajeeranjan .privileges(redfish::privileges::postUpdateService) 14020c814aa6Srajeeranjan .methods(boost::beast::http::verb::post)(std::bind_front( 14030c814aa6Srajeeranjan handleUpdateServiceMultipartUpdatePost, std::ref(app))); 14040c814aa6Srajeeranjan 1405f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/") 1406f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventoryCollection) 1407f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1408f5139334SEd Tanous handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app))); 14096c4eb9deSJennifer Lee } 1410729dae72SJennifer Lee 1411729dae72SJennifer Lee } // namespace redfish 1412