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->populateResp(asyncResp->res); 257c71b6c99SJagpal Singh Gill task->payload.emplace(std::move(payload)); 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 687ef93eab3SJagpal Singh Gill struct MultiPartUpdateParameters 6880ed80c8cSGeorge Liu { 689ef93eab3SJagpal Singh Gill std::optional<std::string> applyTime; 690ef93eab3SJagpal Singh Gill std::string uploadData; 691de0c960cSJagpal Singh Gill std::vector<std::string> targets; 692ef93eab3SJagpal Singh Gill }; 693ef93eab3SJagpal Singh Gill 694504af5a0SPatrick Williams inline std::optional<std::string> processUrl( 695504af5a0SPatrick Williams boost::system::result<boost::urls::url_view>& url) 696de0c960cSJagpal Singh Gill { 697de0c960cSJagpal Singh Gill if (!url) 698de0c960cSJagpal Singh Gill { 699de0c960cSJagpal Singh Gill return std::nullopt; 700de0c960cSJagpal Singh Gill } 701de0c960cSJagpal Singh Gill if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers", 702de0c960cSJagpal Singh Gill BMCWEB_REDFISH_MANAGER_URI_NAME)) 703de0c960cSJagpal Singh Gill { 704de0c960cSJagpal Singh Gill return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME)); 705de0c960cSJagpal Singh Gill } 706de0c960cSJagpal Singh Gill if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 707de0c960cSJagpal Singh Gill { 708de0c960cSJagpal Singh Gill return std::nullopt; 709de0c960cSJagpal Singh Gill } 710de0c960cSJagpal Singh Gill std::string firmwareId; 711de0c960cSJagpal Singh Gill if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService", 712de0c960cSJagpal Singh Gill "FirmwareInventory", 713de0c960cSJagpal Singh Gill std::ref(firmwareId))) 714de0c960cSJagpal Singh Gill { 715de0c960cSJagpal Singh Gill return std::nullopt; 716de0c960cSJagpal Singh Gill } 717de0c960cSJagpal Singh Gill 718de0c960cSJagpal Singh Gill return std::make_optional(firmwareId); 719de0c960cSJagpal Singh Gill } 720de0c960cSJagpal Singh Gill 721ef93eab3SJagpal Singh Gill inline std::optional<MultiPartUpdateParameters> 722ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters( 723ef93eab3SJagpal Singh Gill const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 724ef93eab3SJagpal Singh Gill MultipartParser parser) 725ef93eab3SJagpal Singh Gill { 726ef93eab3SJagpal Singh Gill MultiPartUpdateParameters multiRet; 727ef93eab3SJagpal Singh Gill for (FormPart& formpart : parser.mime_fields) 7280ed80c8cSGeorge Liu { 7290ed80c8cSGeorge Liu boost::beast::http::fields::const_iterator it = 7300ed80c8cSGeorge Liu formpart.fields.find("Content-Disposition"); 7310ed80c8cSGeorge Liu if (it == formpart.fields.end()) 7320ed80c8cSGeorge Liu { 73362598e31SEd Tanous BMCWEB_LOG_ERROR("Couldn't find Content-Disposition"); 734ef93eab3SJagpal Singh Gill return std::nullopt; 7350ed80c8cSGeorge Liu } 73662598e31SEd Tanous BMCWEB_LOG_INFO("Parsing value {}", it->value()); 7370ed80c8cSGeorge Liu 7380ed80c8cSGeorge Liu // The construction parameters of param_list must start with `;` 7390ed80c8cSGeorge Liu size_t index = it->value().find(';'); 7400ed80c8cSGeorge Liu if (index == std::string::npos) 7410ed80c8cSGeorge Liu { 7420ed80c8cSGeorge Liu continue; 7430ed80c8cSGeorge Liu } 7440ed80c8cSGeorge Liu 74589492a15SPatrick Williams for (const auto& param : 7460ed80c8cSGeorge Liu boost::beast::http::param_list{it->value().substr(index)}) 7470ed80c8cSGeorge Liu { 7480ed80c8cSGeorge Liu if (param.first != "name" || param.second.empty()) 7490ed80c8cSGeorge Liu { 7500ed80c8cSGeorge Liu continue; 7510ed80c8cSGeorge Liu } 7520ed80c8cSGeorge Liu 7530ed80c8cSGeorge Liu if (param.second == "UpdateParameters") 7540ed80c8cSGeorge Liu { 755ef93eab3SJagpal Singh Gill std::vector<std::string> tempTargets; 756bd79bce8SPatrick Williams nlohmann::json content = 757bd79bce8SPatrick Williams nlohmann::json::parse(formpart.content, nullptr, false); 758ac1e1246SEd Tanous if (content.is_discarded()) 759ac1e1246SEd Tanous { 760ac1e1246SEd Tanous return std::nullopt; 761ac1e1246SEd Tanous } 7627cb59f65SEd Tanous nlohmann::json::object_t* obj = 7637cb59f65SEd Tanous content.get_ptr<nlohmann::json::object_t*>(); 7647cb59f65SEd Tanous if (obj == nullptr) 7657cb59f65SEd Tanous { 766ef93eab3SJagpal Singh Gill messages::propertyValueTypeError( 767ef93eab3SJagpal Singh Gill asyncResp->res, formpart.content, "UpdateParameters"); 768ef93eab3SJagpal Singh Gill return std::nullopt; 7697cb59f65SEd Tanous } 7707cb59f65SEd Tanous 771afc474aeSMyung Bae if (!json_util::readJsonObject( // 772afc474aeSMyung Bae *obj, asyncResp->res, // 773afc474aeSMyung Bae "@Redfish.OperationApplyTime", multiRet.applyTime, // 774afc474aeSMyung Bae "Targets", tempTargets // 775afc474aeSMyung Bae )) 7760ed80c8cSGeorge Liu { 777ef93eab3SJagpal Singh Gill return std::nullopt; 7780ed80c8cSGeorge Liu } 779ef93eab3SJagpal Singh Gill 780ef93eab3SJagpal Singh Gill for (size_t urlIndex = 0; urlIndex < tempTargets.size(); 781ef93eab3SJagpal Singh Gill urlIndex++) 7820ed80c8cSGeorge Liu { 783ef93eab3SJagpal Singh Gill const std::string& target = tempTargets[urlIndex]; 784ef93eab3SJagpal Singh Gill boost::system::result<boost::urls::url_view> url = 785ef93eab3SJagpal Singh Gill boost::urls::parse_origin_form(target); 786de0c960cSJagpal Singh Gill auto res = processUrl(url); 787de0c960cSJagpal Singh Gill if (!res.has_value()) 7880ed80c8cSGeorge Liu { 789ef93eab3SJagpal Singh Gill messages::propertyValueFormatError( 790ef93eab3SJagpal Singh Gill asyncResp->res, target, 791ef93eab3SJagpal Singh Gill std::format("Targets/{}", urlIndex)); 792ef93eab3SJagpal Singh Gill return std::nullopt; 7930ed80c8cSGeorge Liu } 794de0c960cSJagpal Singh Gill multiRet.targets.emplace_back(res.value()); 795ef93eab3SJagpal Singh Gill } 796ef93eab3SJagpal Singh Gill if (multiRet.targets.size() != 1) 797ef93eab3SJagpal Singh Gill { 798ef93eab3SJagpal Singh Gill messages::propertyValueFormatError( 799ef93eab3SJagpal Singh Gill asyncResp->res, multiRet.targets, "Targets"); 800ef93eab3SJagpal Singh Gill return std::nullopt; 801ef93eab3SJagpal Singh Gill } 8020ed80c8cSGeorge Liu } 8030ed80c8cSGeorge Liu else if (param.second == "UpdateFile") 8040ed80c8cSGeorge Liu { 805ef93eab3SJagpal Singh Gill multiRet.uploadData = std::move(formpart.content); 8060ed80c8cSGeorge Liu } 8070ed80c8cSGeorge Liu } 8080ed80c8cSGeorge Liu } 8090ed80c8cSGeorge Liu 810ef93eab3SJagpal Singh Gill if (multiRet.uploadData.empty()) 8110ed80c8cSGeorge Liu { 81262598e31SEd Tanous BMCWEB_LOG_ERROR("Upload data is NULL"); 8130ed80c8cSGeorge Liu messages::propertyMissing(asyncResp->res, "UpdateFile"); 814ef93eab3SJagpal Singh Gill return std::nullopt; 8150ed80c8cSGeorge Liu } 816ef93eab3SJagpal Singh Gill if (multiRet.targets.empty()) 8170ed80c8cSGeorge Liu { 818ef93eab3SJagpal Singh Gill messages::propertyMissing(asyncResp->res, "Targets"); 819ef93eab3SJagpal Singh Gill return std::nullopt; 820ef93eab3SJagpal Singh Gill } 821ef93eab3SJagpal Singh Gill return multiRet; 8220ed80c8cSGeorge Liu } 8230ed80c8cSGeorge Liu 824bd79bce8SPatrick Williams inline void handleStartUpdate( 825bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 826bd79bce8SPatrick Williams const std::string& objectPath, const boost::system::error_code& ec, 827de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) 828de0c960cSJagpal Singh Gill { 829de0c960cSJagpal Singh Gill if (ec) 830de0c960cSJagpal Singh Gill { 831de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 832de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 833de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 834de0c960cSJagpal Singh Gill return; 835de0c960cSJagpal Singh Gill } 836de0c960cSJagpal Singh Gill 837587090cdSJagpal Singh Gill BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}", 838587090cdSJagpal Singh Gill objectPath, retPath.str); 839587090cdSJagpal Singh Gill createTask(asyncResp, std::move(payload), retPath); 840de0c960cSJagpal Singh Gill } 841de0c960cSJagpal Singh Gill 842bd79bce8SPatrick Williams inline void startUpdate( 843bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 844bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 845bd79bce8SPatrick Williams const std::string& objectPath, const std::string& serviceName) 846de0c960cSJagpal Singh Gill { 847177612aaSEd Tanous dbus::utility::async_method_call( 848177612aaSEd Tanous asyncResp, 849de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), 850de0c960cSJagpal Singh Gill objectPath](const boost::system::error_code& ec1, 851de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) mutable { 852de0c960cSJagpal Singh Gill handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1, 853de0c960cSJagpal Singh Gill retPath); 854de0c960cSJagpal Singh Gill }, 855de0c960cSJagpal Singh Gill serviceName, objectPath, "xyz.openbmc_project.Software.Update", 856de0c960cSJagpal Singh Gill "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime); 857de0c960cSJagpal Singh Gill } 858de0c960cSJagpal Singh Gill 85908f61d53SJagpal Singh Gill inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 86008f61d53SJagpal Singh Gill task::Payload payload, const MemoryFileDescriptor& memfd, 86108f61d53SJagpal Singh Gill const std::string& applyTime, const std::string& target, 862de0c960cSJagpal Singh Gill const boost::system::error_code& ec, 863de0c960cSJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& subtree) 864de0c960cSJagpal Singh Gill { 86508f61d53SJagpal Singh Gill using SwInfoMap = std::unordered_map< 86608f61d53SJagpal Singh Gill std::string, std::pair<sdbusplus::message::object_path, std::string>>; 867de0c960cSJagpal Singh Gill SwInfoMap swInfoMap; 868de0c960cSJagpal Singh Gill 869de0c960cSJagpal Singh Gill if (ec) 870de0c960cSJagpal Singh Gill { 871de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 872de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 873de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 874de0c960cSJagpal Singh Gill return; 875de0c960cSJagpal Singh Gill } 876de0c960cSJagpal Singh Gill BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size()); 877de0c960cSJagpal Singh Gill 87808f61d53SJagpal Singh Gill for (const auto& entry : subtree) 879de0c960cSJagpal Singh Gill { 88008f61d53SJagpal Singh Gill sdbusplus::message::object_path path(entry.first); 881de0c960cSJagpal Singh Gill std::string swId = path.filename(); 88208f61d53SJagpal Singh Gill swInfoMap.emplace(swId, make_pair(path, entry.second[0].first)); 883de0c960cSJagpal Singh Gill } 884de0c960cSJagpal Singh Gill 885de0c960cSJagpal Singh Gill auto swEntry = swInfoMap.find(target); 886de0c960cSJagpal Singh Gill if (swEntry == swInfoMap.end()) 887de0c960cSJagpal Singh Gill { 888de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target); 889de0c960cSJagpal Singh Gill messages::propertyValueFormatError(asyncResp->res, target, "Targets"); 890de0c960cSJagpal Singh Gill return; 891de0c960cSJagpal Singh Gill } 892de0c960cSJagpal Singh Gill 89308f61d53SJagpal Singh Gill BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}", 89408f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 895de0c960cSJagpal Singh Gill 89608f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 89708f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 89808f61d53SJagpal Singh Gill } 89908f61d53SJagpal Singh Gill 900bd79bce8SPatrick Williams inline void handleBMCUpdate( 901bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 902bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 90308f61d53SJagpal Singh Gill const boost::system::error_code& ec, 90408f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& functionalSoftware) 90508f61d53SJagpal Singh Gill { 90608f61d53SJagpal Singh Gill if (ec) 90708f61d53SJagpal Singh Gill { 90808f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 90908f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 91008f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 91108f61d53SJagpal Singh Gill return; 91208f61d53SJagpal Singh Gill } 91308f61d53SJagpal Singh Gill if (functionalSoftware.size() != 1) 91408f61d53SJagpal Singh Gill { 91508f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("Found {} functional software endpoints", 91608f61d53SJagpal Singh Gill functionalSoftware.size()); 91708f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 91808f61d53SJagpal Singh Gill return; 91908f61d53SJagpal Singh Gill } 92008f61d53SJagpal Singh Gill 92108f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 92208f61d53SJagpal Singh Gill functionalSoftware[0], "xyz.openbmc_project.Software.Manager"); 923de0c960cSJagpal Singh Gill } 924de0c960cSJagpal Singh Gill 925bd79bce8SPatrick Williams inline void processUpdateRequest( 926bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 9279dae4deeSJagpal Singh Gill task::Payload&& payload, std::string_view body, 928bd79bce8SPatrick Williams const std::string& applyTime, std::vector<std::string>& targets) 929de0c960cSJagpal Singh Gill { 930de0c960cSJagpal Singh Gill MemoryFileDescriptor memfd("update-image"); 931de0c960cSJagpal Singh Gill if (memfd.fd == -1) 932de0c960cSJagpal Singh Gill { 933de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to create image memfd"); 934de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 935de0c960cSJagpal Singh Gill return; 936de0c960cSJagpal Singh Gill } 937de0c960cSJagpal Singh Gill if (write(memfd.fd, body.data(), body.length()) != 938de0c960cSJagpal Singh Gill static_cast<ssize_t>(body.length())) 939de0c960cSJagpal Singh Gill { 940de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to write to image memfd"); 941de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 942de0c960cSJagpal Singh Gill return; 943de0c960cSJagpal Singh Gill } 944de0c960cSJagpal Singh Gill if (!memfd.rewind()) 945de0c960cSJagpal Singh Gill { 946de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 947de0c960cSJagpal Singh Gill return; 948de0c960cSJagpal Singh Gill } 949de0c960cSJagpal Singh Gill 950de0c960cSJagpal Singh Gill if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME) 951de0c960cSJagpal Singh Gill { 95208f61d53SJagpal Singh Gill dbus::utility::getAssociationEndPoints( 95389449bbeSJagpal Singh Gill "/xyz/openbmc_project/software/bmc/updateable", 95408f61d53SJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 95508f61d53SJagpal Singh Gill applyTime]( 95608f61d53SJagpal Singh Gill const boost::system::error_code& ec, 95708f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& objectPaths) mutable { 958bd79bce8SPatrick Williams handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime, 959bd79bce8SPatrick Williams ec, objectPaths); 96008f61d53SJagpal Singh Gill }); 961de0c960cSJagpal Singh Gill } 962de0c960cSJagpal Singh Gill else 963de0c960cSJagpal Singh Gill { 964de0c960cSJagpal Singh Gill constexpr std::array<std::string_view, 1> interfaces = { 965de0c960cSJagpal Singh Gill "xyz.openbmc_project.Software.Version"}; 96608f61d53SJagpal Singh Gill dbus::utility::getSubTree( 967de0c960cSJagpal Singh Gill "/xyz/openbmc_project/software", 1, interfaces, 968de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 96908f61d53SJagpal Singh Gill applyTime, targets](const boost::system::error_code& ec, 97008f61d53SJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& 971de0c960cSJagpal Singh Gill subtree) mutable { 97208f61d53SJagpal Singh Gill getSwInfo(asyncResp, std::move(payload), memfd, applyTime, 97308f61d53SJagpal Singh Gill targets[0], ec, subtree); 974de0c960cSJagpal Singh Gill }); 975de0c960cSJagpal Singh Gill } 976de0c960cSJagpal Singh Gill } 977de0c960cSJagpal Singh Gill 978504af5a0SPatrick Williams inline void updateMultipartContext( 979504af5a0SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 980ef93eab3SJagpal Singh Gill const crow::Request& req, MultipartParser&& parser) 981ef93eab3SJagpal Singh Gill { 982ef93eab3SJagpal Singh Gill std::optional<MultiPartUpdateParameters> multipart = 983ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters(asyncResp, std::move(parser)); 984ef93eab3SJagpal Singh Gill if (!multipart) 985ef93eab3SJagpal Singh Gill { 986ef93eab3SJagpal Singh Gill return; 987ef93eab3SJagpal Singh Gill } 988ef93eab3SJagpal Singh Gill if (!multipart->applyTime) 989ef93eab3SJagpal Singh Gill { 990ef93eab3SJagpal Singh Gill multipart->applyTime = "OnReset"; 991ef93eab3SJagpal Singh Gill } 992ef93eab3SJagpal Singh Gill 993de0c960cSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 994de0c960cSJagpal Singh Gill { 9959dae4deeSJagpal Singh Gill std::string applyTimeNewVal; 9969dae4deeSJagpal Singh Gill if (!convertApplyTime(asyncResp->res, *multipart->applyTime, 9979dae4deeSJagpal Singh Gill applyTimeNewVal)) 9989dae4deeSJagpal Singh Gill { 9999dae4deeSJagpal Singh Gill return; 10009dae4deeSJagpal Singh Gill } 10019dae4deeSJagpal Singh Gill task::Payload payload(req); 10029dae4deeSJagpal Singh Gill 10039dae4deeSJagpal Singh Gill processUpdateRequest(asyncResp, std::move(payload), 10049dae4deeSJagpal Singh Gill multipart->uploadData, applyTimeNewVal, 10059dae4deeSJagpal Singh Gill multipart->targets); 1006de0c960cSJagpal Singh Gill } 1007de0c960cSJagpal Singh Gill else 1008de0c960cSJagpal Singh Gill { 1009ef93eab3SJagpal Singh Gill setApplyTime(asyncResp, *multipart->applyTime); 10100ed80c8cSGeorge Liu 10116b54e4e0SEd Tanous // Setup callback for when new software detected 1012de0c960cSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 1013de0c960cSJagpal Singh Gill "/redfish/v1/UpdateService"); 10146b54e4e0SEd Tanous 1015ef93eab3SJagpal Singh Gill uploadImageFile(asyncResp->res, multipart->uploadData); 10160ed80c8cSGeorge Liu } 1017de0c960cSJagpal Singh Gill } 10180ed80c8cSGeorge Liu 10199dae4deeSJagpal Singh Gill inline void doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 10209dae4deeSJagpal Singh Gill const crow::Request& req) 10219dae4deeSJagpal Singh Gill { 10229dae4deeSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 10239dae4deeSJagpal Singh Gill { 10249dae4deeSJagpal Singh Gill task::Payload payload(req); 10259dae4deeSJagpal Singh Gill // HTTP push only supports BMC updates (with ApplyTime as immediate) for 10269dae4deeSJagpal Singh Gill // backwards compatibility. Specific component updates will be handled 10279dae4deeSJagpal Singh Gill // through Multipart form HTTP push. 10289dae4deeSJagpal Singh Gill std::vector<std::string> targets; 10299dae4deeSJagpal Singh Gill targets.emplace_back(BMCWEB_REDFISH_MANAGER_URI_NAME); 10309dae4deeSJagpal Singh Gill 10319dae4deeSJagpal Singh Gill processUpdateRequest( 10329dae4deeSJagpal Singh Gill asyncResp, std::move(payload), req.body(), 10339dae4deeSJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate", 10349dae4deeSJagpal Singh Gill targets); 10359dae4deeSJagpal Singh Gill } 10369dae4deeSJagpal Singh Gill else 10379dae4deeSJagpal Singh Gill { 10389dae4deeSJagpal Singh Gill // Setup callback for when new software detected 10399dae4deeSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 10409dae4deeSJagpal Singh Gill "/redfish/v1/UpdateService"); 10419dae4deeSJagpal Singh Gill 10429dae4deeSJagpal Singh Gill uploadImageFile(asyncResp->res, req.body()); 10439dae4deeSJagpal Singh Gill } 10449dae4deeSJagpal Singh Gill } 10459dae4deeSJagpal Singh Gill 1046504af5a0SPatrick Williams inline void handleUpdateServicePost( 1047504af5a0SPatrick Williams App& app, const crow::Request& req, 1048c2051d11SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1049c2051d11SEd Tanous { 10503ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1051c2051d11SEd Tanous { 1052c2051d11SEd Tanous return; 1053c2051d11SEd Tanous } 1054b33a4327SNinad Palsule std::string_view contentType = req.getHeaderValue("Content-Type"); 1055b33a4327SNinad Palsule 105662598e31SEd Tanous BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType); 1057b33a4327SNinad Palsule 10580c814aa6Srajeeranjan // Make sure that content type is application/octet-stream 105918f8f608SEd Tanous if (bmcweb::asciiIEquals(contentType, "application/octet-stream")) 1060b33a4327SNinad Palsule { 10619dae4deeSJagpal Singh Gill doHTTPUpdate(asyncResp, req); 1062b33a4327SNinad Palsule } 10630c814aa6Srajeeranjan else 10640c814aa6Srajeeranjan { 10650c814aa6Srajeeranjan BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType); 10660c814aa6Srajeeranjan asyncResp->res.result(boost::beast::http::status::bad_request); 10670c814aa6Srajeeranjan } 10680c814aa6Srajeeranjan } 10690c814aa6Srajeeranjan 10700c814aa6Srajeeranjan inline void handleUpdateServiceMultipartUpdatePost( 10710c814aa6Srajeeranjan App& app, const crow::Request& req, 10720c814aa6Srajeeranjan const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 10730c814aa6Srajeeranjan { 10740c814aa6Srajeeranjan if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 10750c814aa6Srajeeranjan { 10760c814aa6Srajeeranjan return; 10770c814aa6Srajeeranjan } 10780c814aa6Srajeeranjan 10790c814aa6Srajeeranjan std::string_view contentType = req.getHeaderValue("Content-Type"); 10800c814aa6Srajeeranjan // Make sure that content type is multipart/form-data 10810c814aa6Srajeeranjan if (contentType.starts_with("multipart/form-data")) 1082b33a4327SNinad Palsule { 1083b33a4327SNinad Palsule MultipartParser parser; 1084c2051d11SEd Tanous 10850ed80c8cSGeorge Liu ParserError ec = parser.parse(req); 10860ed80c8cSGeorge Liu if (ec != ParserError::PARSER_SUCCESS) 10870ed80c8cSGeorge Liu { 10880ed80c8cSGeorge Liu // handle error 108962598e31SEd Tanous BMCWEB_LOG_ERROR("MIME parse failed, ec : {}", 109062598e31SEd Tanous static_cast<int>(ec)); 10910ed80c8cSGeorge Liu messages::internalError(asyncResp->res); 10920ed80c8cSGeorge Liu return; 10930ed80c8cSGeorge Liu } 10946b54e4e0SEd Tanous 1095ef93eab3SJagpal Singh Gill updateMultipartContext(asyncResp, req, std::move(parser)); 1096c2051d11SEd Tanous } 1097b33a4327SNinad Palsule else 1098b33a4327SNinad Palsule { 109962598e31SEd Tanous BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType); 1100b33a4327SNinad Palsule asyncResp->res.result(boost::beast::http::status::bad_request); 1101b33a4327SNinad Palsule } 1102b33a4327SNinad Palsule } 1103c2051d11SEd Tanous 1104504af5a0SPatrick Williams inline void handleUpdateServiceGet( 1105504af5a0SPatrick Williams App& app, const crow::Request& req, 1106f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 11071abe55efSEd Tanous { 11083ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 110945ca1b86SEd Tanous { 111045ca1b86SEd Tanous return; 111145ca1b86SEd Tanous } 11128d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 11130ed80c8cSGeorge Liu "#UpdateService.v1_11_1.UpdateService"; 11148d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService"; 11158d1b46d7Szhanghch05 asyncResp->res.jsonValue["Id"] = "UpdateService"; 1116002d39b4SEd Tanous asyncResp->res.jsonValue["Description"] = "Service for Software Update"; 11178d1b46d7Szhanghch05 asyncResp->res.jsonValue["Name"] = "Update Service"; 11184dc23f3fSEd Tanous 11197e860f15SJohn Edward Broadbent asyncResp->res.jsonValue["HttpPushUri"] = 11204dc23f3fSEd Tanous "/redfish/v1/UpdateService/update"; 11210ed80c8cSGeorge Liu asyncResp->res.jsonValue["MultipartHttpPushUri"] = 11220c814aa6Srajeeranjan "/redfish/v1/UpdateService/update-multipart"; 11234dc23f3fSEd Tanous 11240f74e643SEd Tanous // UpdateService cannot be disabled 11258d1b46d7Szhanghch05 asyncResp->res.jsonValue["ServiceEnabled"] = true; 11261476687dSEd Tanous asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] = 11271476687dSEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1128d61e5194STejas Patil // Get the MaxImageSizeBytes 1129bd79bce8SPatrick Williams asyncResp->res.jsonValue["MaxImageSizeBytes"] = 1130bd79bce8SPatrick Williams BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024; 1131d61e5194STejas Patil 11326a37140aSEd Tanous if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE) 11336a37140aSEd Tanous { 11340554c984SAndrew Geissler // Update Actions object. 11350554c984SAndrew Geissler nlohmann::json& updateSvcSimpleUpdate = 1136002d39b4SEd Tanous asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"]; 11370554c984SAndrew Geissler updateSvcSimpleUpdate["target"] = 11380554c984SAndrew Geissler "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate"; 1139757178a5SEd Tanous 1140757178a5SEd Tanous nlohmann::json::array_t allowed; 1141e5cf777eSEd Tanous allowed.emplace_back(update_service::TransferProtocolType::HTTPS); 1142757178a5SEd Tanous updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = 1143757178a5SEd Tanous std::move(allowed); 11446a37140aSEd Tanous } 1145757178a5SEd Tanous 1146539d8c6bSEd Tanous asyncResp->res 1147539d8c6bSEd Tanous .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] = 1148539d8c6bSEd Tanous update_service::ApplyTime::Immediate; 1149729dae72SJennifer Lee } 1150729dae72SJennifer Lee 1151f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryCollectionGet( 1152f5139334SEd Tanous App& app, const crow::Request& req, 1153f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 11541abe55efSEd Tanous { 11553ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 115645ca1b86SEd Tanous { 115745ca1b86SEd Tanous return; 115845ca1b86SEd Tanous } 11598d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 11600f74e643SEd Tanous "#SoftwareInventoryCollection.SoftwareInventoryCollection"; 11618d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = 11620f74e643SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1163002d39b4SEd Tanous asyncResp->res.jsonValue["Name"] = "Software Inventory Collection"; 116408d81adaSJohn Edward Broadbent const std::array<const std::string_view, 1> iface = { 1165e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 11666c4eb9deSJennifer Lee 116708d81adaSJohn Edward Broadbent redfish::collection_util::getCollectionMembers( 116808d81adaSJohn Edward Broadbent asyncResp, 1169f5139334SEd Tanous boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface, 1170f5139334SEd Tanous "/xyz/openbmc_project/software"); 1171729dae72SJennifer Lee } 1172f5139334SEd Tanous 117387d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */ 1174f5139334SEd Tanous inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 117587d84729SAndrew Geissler const std::string& purpose) 117687d84729SAndrew Geissler { 1177eee0013eSWilly Tu if (purpose == sw_util::bmcPurpose) 117887d84729SAndrew Geissler { 1179ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"]; 11801476687dSEd Tanous nlohmann::json::object_t item; 1181253f11b8SEd Tanous item["@odata.id"] = boost::urls::format( 1182253f11b8SEd Tanous "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); 1183b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item)); 1184ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] = 1185ac106bf6SEd Tanous relatedItem.size(); 118687d84729SAndrew Geissler } 1187eee0013eSWilly Tu else if (purpose == sw_util::biosPurpose) 118887d84729SAndrew Geissler { 1189ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"]; 11901476687dSEd Tanous nlohmann::json::object_t item; 1191253f11b8SEd Tanous item["@odata.id"] = std::format("/redfish/v1/Systems/{}/Bios", 1192253f11b8SEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 1193b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item)); 1194ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] = 1195ac106bf6SEd Tanous relatedItem.size(); 119687d84729SAndrew Geissler } 119787d84729SAndrew Geissler else 119887d84729SAndrew Geissler { 1199bf2ddedeSCarson Labrado BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose); 120087d84729SAndrew Geissler } 120187d84729SAndrew Geissler } 120287d84729SAndrew Geissler 1203504af5a0SPatrick Williams inline void getSoftwareVersion( 1204504af5a0SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1205af24660dSWilly Tu const std::string& service, const std::string& path, 1206af24660dSWilly Tu const std::string& swId) 1207af24660dSWilly Tu { 1208deae6a78SEd Tanous dbus::utility::getAllProperties( 1209deae6a78SEd Tanous service, path, "xyz.openbmc_project.Software.Version", 1210af24660dSWilly Tu [asyncResp, 12118b24275dSEd Tanous swId](const boost::system::error_code& ec, 1212af24660dSWilly Tu const dbus::utility::DBusPropertiesMap& propertiesList) { 12138b24275dSEd Tanous if (ec) 1214af24660dSWilly Tu { 1215af24660dSWilly Tu messages::internalError(asyncResp->res); 1216af24660dSWilly Tu return; 1217af24660dSWilly Tu } 1218d1bde9e5SKrzysztof Grobelny 1219af24660dSWilly Tu const std::string* swInvPurpose = nullptr; 1220af24660dSWilly Tu const std::string* version = nullptr; 1221d1bde9e5SKrzysztof Grobelny 1222d1bde9e5SKrzysztof Grobelny const bool success = sdbusplus::unpackPropertiesNoThrow( 1223d1bde9e5SKrzysztof Grobelny dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose", 1224d1bde9e5SKrzysztof Grobelny swInvPurpose, "Version", version); 1225d1bde9e5SKrzysztof Grobelny 1226d1bde9e5SKrzysztof Grobelny if (!success) 1227af24660dSWilly Tu { 1228d1bde9e5SKrzysztof Grobelny messages::internalError(asyncResp->res); 1229d1bde9e5SKrzysztof Grobelny return; 1230af24660dSWilly Tu } 1231af24660dSWilly Tu 1232af24660dSWilly Tu if (swInvPurpose == nullptr) 1233af24660dSWilly Tu { 123462598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!"); 1235af24660dSWilly Tu messages::internalError(asyncResp->res); 1236af24660dSWilly Tu return; 1237af24660dSWilly Tu } 1238af24660dSWilly Tu 123962598e31SEd Tanous BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose); 1240af24660dSWilly Tu 1241af24660dSWilly Tu if (version == nullptr) 1242af24660dSWilly Tu { 124362598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Version\"!"); 1244af24660dSWilly Tu 1245af24660dSWilly Tu messages::internalError(asyncResp->res); 1246af24660dSWilly Tu 1247af24660dSWilly Tu return; 1248af24660dSWilly Tu } 1249af24660dSWilly Tu asyncResp->res.jsonValue["Version"] = *version; 1250af24660dSWilly Tu asyncResp->res.jsonValue["Id"] = swId; 1251af24660dSWilly Tu 1252af24660dSWilly Tu // swInvPurpose is of format: 1253af24660dSWilly Tu // xyz.openbmc_project.Software.Version.VersionPurpose.ABC 1254af24660dSWilly Tu // Translate this to "ABC image" 1255af24660dSWilly Tu size_t endDesc = swInvPurpose->rfind('.'); 1256af24660dSWilly Tu if (endDesc == std::string::npos) 1257af24660dSWilly Tu { 1258af24660dSWilly Tu messages::internalError(asyncResp->res); 1259af24660dSWilly Tu return; 1260af24660dSWilly Tu } 1261af24660dSWilly Tu endDesc++; 1262af24660dSWilly Tu if (endDesc >= swInvPurpose->size()) 1263af24660dSWilly Tu { 1264af24660dSWilly Tu messages::internalError(asyncResp->res); 1265af24660dSWilly Tu return; 1266af24660dSWilly Tu } 1267af24660dSWilly Tu 1268af24660dSWilly Tu std::string formatDesc = swInvPurpose->substr(endDesc); 1269af24660dSWilly Tu asyncResp->res.jsonValue["Description"] = formatDesc + " image"; 1270af24660dSWilly Tu getRelatedItems(asyncResp, *swInvPurpose); 1271d1bde9e5SKrzysztof Grobelny }); 1272af24660dSWilly Tu } 1273af24660dSWilly Tu 1274f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryGet( 1275f5139334SEd Tanous App& app, const crow::Request& req, 127645ca1b86SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1277f5139334SEd Tanous const std::string& param) 1278f5139334SEd Tanous { 12793ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 128045ca1b86SEd Tanous { 128145ca1b86SEd Tanous return; 128245ca1b86SEd Tanous } 1283f5139334SEd Tanous std::shared_ptr<std::string> swId = std::make_shared<std::string>(param); 1284c711bf86SEd Tanous 1285e99073f5SGeorge Liu constexpr std::array<std::string_view, 1> interfaces = { 1286e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 1287e99073f5SGeorge Liu dbus::utility::getSubTree( 1288f90af522SAbiola Asojo "/xyz/openbmc_project/software/", 0, interfaces, 1289b9d36b47SEd Tanous [asyncResp, 1290e99073f5SGeorge Liu swId](const boost::system::error_code& ec, 1291b9d36b47SEd Tanous const dbus::utility::MapperGetSubTreeResponse& subtree) { 129262598e31SEd Tanous BMCWEB_LOG_DEBUG("doGet callback..."); 12931abe55efSEd Tanous if (ec) 12941abe55efSEd Tanous { 1295f12894f8SJason M. Bills messages::internalError(asyncResp->res); 12966c4eb9deSJennifer Lee return; 12976c4eb9deSJennifer Lee } 12986c4eb9deSJennifer Lee 12996913228dSAndrew Geissler // Ensure we find our input swId, otherwise return an error 13006913228dSAndrew Geissler bool found = false; 1301bd79bce8SPatrick Williams for (const std::pair<std::string, 1302bd79bce8SPatrick Williams std::vector<std::pair< 1303bd79bce8SPatrick Williams std::string, std::vector<std::string>>>>& 1304002d39b4SEd Tanous obj : subtree) 13051abe55efSEd Tanous { 1306f90af522SAbiola Asojo sdbusplus::message::object_path path(obj.first); 1307f90af522SAbiola Asojo std::string id = path.filename(); 1308f90af522SAbiola Asojo if (id.empty()) 1309f90af522SAbiola Asojo { 1310f90af522SAbiola Asojo BMCWEB_LOG_DEBUG("Failed to find software id in {}", 1311f90af522SAbiola Asojo obj.first); 1312f90af522SAbiola Asojo continue; 1313f90af522SAbiola Asojo } 1314f90af522SAbiola Asojo if (id != *swId) 13151abe55efSEd Tanous { 1316acb7cfb4SJennifer Lee continue; 1317acb7cfb4SJennifer Lee } 1318acb7cfb4SJennifer Lee 131926f6976fSEd Tanous if (obj.second.empty()) 13201abe55efSEd Tanous { 1321acb7cfb4SJennifer Lee continue; 1322acb7cfb4SJennifer Lee } 13236c4eb9deSJennifer Lee 13246913228dSAndrew Geissler found = true; 1325eee0013eSWilly Tu sw_util::getSwStatus(asyncResp, swId, obj.second[0].first); 1326*c9bd0924SGunnar Mills sw_util::getSwMinimumVersion(asyncResp, swId, 1327*c9bd0924SGunnar Mills obj.second[0].first); 1328af24660dSWilly Tu getSoftwareVersion(asyncResp, obj.second[0].first, obj.first, 1329af24660dSWilly Tu *swId); 13306c4eb9deSJennifer Lee } 13316913228dSAndrew Geissler if (!found) 13326913228dSAndrew Geissler { 133362598e31SEd Tanous BMCWEB_LOG_WARNING("Input swID {} not found!", *swId); 13346913228dSAndrew Geissler messages::resourceMissingAtURI( 1335ef4c65b7SEd Tanous asyncResp->res, 1336ef4c65b7SEd Tanous boost::urls::format( 1337bd79bce8SPatrick Williams "/redfish/v1/UpdateService/FirmwareInventory/{}", 1338bd79bce8SPatrick Williams *swId)); 13396913228dSAndrew Geissler return; 13406913228dSAndrew Geissler } 1341f90af522SAbiola Asojo asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1342f90af522SAbiola Asojo "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId); 13434e68c45bSAyushi Smriti asyncResp->res.jsonValue["@odata.type"] = 13444e68c45bSAyushi Smriti "#SoftwareInventory.v1_1_0.SoftwareInventory"; 13454e68c45bSAyushi Smriti asyncResp->res.jsonValue["Name"] = "Software Inventory"; 1346539d8c6bSEd Tanous asyncResp->res.jsonValue["Status"]["HealthRollup"] = 1347539d8c6bSEd Tanous resource::Health::OK; 13483f8a743aSAppaRao Puli 13493f8a743aSAppaRao Puli asyncResp->res.jsonValue["Updateable"] = false; 1350eee0013eSWilly Tu sw_util::getSwUpdatableStatus(asyncResp, swId); 1351e99073f5SGeorge Liu }); 1352f5139334SEd Tanous } 1353f5139334SEd Tanous 1354f5139334SEd Tanous inline void requestRoutesUpdateService(App& app) 1355f5139334SEd Tanous { 13566a37140aSEd Tanous if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE) 13576a37140aSEd Tanous { 1358f5139334SEd Tanous BMCWEB_ROUTE( 13596a37140aSEd Tanous app, 13606a37140aSEd Tanous "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/") 1361f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1362f5139334SEd Tanous .methods(boost::beast::http::verb::post)(std::bind_front( 1363f5139334SEd Tanous handleUpdateServiceSimpleUpdateAction, std::ref(app))); 13646a37140aSEd Tanous } 1365f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/") 1366f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventory) 1367f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1368f5139334SEd Tanous handleUpdateServiceFirmwareInventoryGet, std::ref(app))); 1369f5139334SEd Tanous 1370f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/") 1371f5139334SEd Tanous .privileges(redfish::privileges::getUpdateService) 1372f5139334SEd Tanous .methods(boost::beast::http::verb::get)( 1373f5139334SEd Tanous std::bind_front(handleUpdateServiceGet, std::ref(app))); 1374f5139334SEd Tanous 1375f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/") 1376f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1377f5139334SEd Tanous .methods(boost::beast::http::verb::post)( 1378f5139334SEd Tanous std::bind_front(handleUpdateServicePost, std::ref(app))); 1379f5139334SEd Tanous 13800c814aa6Srajeeranjan BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update-multipart/") 13810c814aa6Srajeeranjan .privileges(redfish::privileges::postUpdateService) 13820c814aa6Srajeeranjan .methods(boost::beast::http::verb::post)(std::bind_front( 13830c814aa6Srajeeranjan handleUpdateServiceMultipartUpdatePost, std::ref(app))); 13840c814aa6Srajeeranjan 1385f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/") 1386f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventoryCollection) 1387f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1388f5139334SEd Tanous handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app))); 13896c4eb9deSJennifer Lee } 1390729dae72SJennifer Lee 1391729dae72SJennifer Lee } // namespace redfish 1392