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" 9*d7857201SEd Tanous #include "async_resp.hpp" 10*d7857201SEd Tanous #include "dbus_singleton.hpp" 113ccb3adbSEd Tanous #include "dbus_utility.hpp" 125b90429aSEd Tanous #include "error_messages.hpp" 13*d7857201SEd Tanous #include "generated/enums/resource.hpp" 14757178a5SEd Tanous #include "generated/enums/update_service.hpp" 15*d7857201SEd Tanous #include "http_request.hpp" 16*d7857201SEd Tanous #include "http_response.hpp" 17*d7857201SEd Tanous #include "logging.hpp" 180ed80c8cSGeorge Liu #include "multipart_parser.hpp" 192c6ffdb0SEd Tanous #include "ossl_random.hpp" 203ccb3adbSEd Tanous #include "query.hpp" 213ccb3adbSEd Tanous #include "registries/privilege_registry.hpp" 22*d7857201SEd Tanous #include "str_utility.hpp" 23a8e884fcSEd Tanous #include "task.hpp" 245b90429aSEd Tanous #include "task_messages.hpp" 25*d7857201SEd Tanous #include "utility.hpp" 2608d81adaSJohn Edward Broadbent #include "utils/collection.hpp" 273ccb3adbSEd Tanous #include "utils/dbus_utils.hpp" 285b90429aSEd Tanous #include "utils/json_utils.hpp" 293ccb3adbSEd Tanous #include "utils/sw_utils.hpp" 303ccb3adbSEd Tanous 31de0c960cSJagpal Singh Gill #include <sys/mman.h> 32*d7857201SEd Tanous #include <unistd.h> 33de0c960cSJagpal Singh Gill 34*d7857201SEd Tanous #include <boost/asio/error.hpp> 35*d7857201SEd Tanous #include <boost/asio/steady_timer.hpp> 36*d7857201SEd Tanous #include <boost/beast/http/fields.hpp> 37*d7857201SEd Tanous #include <boost/beast/http/status.hpp> 38*d7857201SEd Tanous #include <boost/beast/http/verb.hpp> 39e99073f5SGeorge Liu #include <boost/system/error_code.hpp> 40*d7857201SEd Tanous #include <boost/system/result.hpp> 41ef4c65b7SEd Tanous #include <boost/url/format.hpp> 42*d7857201SEd Tanous #include <boost/url/parse.hpp> 43*d7857201SEd Tanous #include <boost/url/url.hpp> 44*d7857201SEd Tanous #include <boost/url/url_view.hpp> 45*d7857201SEd Tanous #include <boost/url/url_view_base.hpp> 461e1e598dSJonathan Doman #include <sdbusplus/asio/property.hpp> 473ccb3adbSEd Tanous #include <sdbusplus/bus/match.hpp> 48*d7857201SEd Tanous #include <sdbusplus/message.hpp> 49*d7857201SEd Tanous #include <sdbusplus/message/native_types.hpp> 50d1bde9e5SKrzysztof Grobelny #include <sdbusplus/unpack_properties.hpp> 511214b7e7SGunnar Mills 522b73119cSGeorge Liu #include <array> 53*d7857201SEd Tanous #include <chrono> 54de0c960cSJagpal Singh Gill #include <cstddef> 55*d7857201SEd Tanous #include <cstdint> 56*d7857201SEd Tanous #include <cstdio> 570ed80c8cSGeorge Liu #include <filesystem> 58*d7857201SEd Tanous #include <format> 59*d7857201SEd Tanous #include <fstream> 60c71b6c99SJagpal Singh Gill #include <functional> 61ef93eab3SJagpal Singh Gill #include <memory> 627cb59f65SEd Tanous #include <optional> 637cb59f65SEd Tanous #include <string> 642b73119cSGeorge Liu #include <string_view> 65de0c960cSJagpal Singh Gill #include <unordered_map> 66*d7857201SEd Tanous #include <utility> 67*d7857201SEd Tanous #include <variant> 68ef93eab3SJagpal Singh Gill #include <vector> 692b73119cSGeorge Liu 701abe55efSEd Tanous namespace redfish 711abe55efSEd Tanous { 7227826b5fSEd Tanous 730e7de46fSAndrew Geissler // Match signals added on software path 74cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 7559d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher; 76cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 7759d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher; 780e7de46fSAndrew Geissler // Only allow one update at a time 79cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 800e7de46fSAndrew Geissler static bool fwUpdateInProgress = false; 8186adcd6dSAndrew Geissler // Timer for software available 82cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 83271584abSEd Tanous static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer; 8486adcd6dSAndrew Geissler 85de0c960cSJagpal Singh Gill struct MemoryFileDescriptor 86de0c960cSJagpal Singh Gill { 87de0c960cSJagpal Singh Gill int fd = -1; 88de0c960cSJagpal Singh Gill 89de0c960cSJagpal Singh Gill explicit MemoryFileDescriptor(const std::string& filename) : 90de0c960cSJagpal Singh Gill fd(memfd_create(filename.c_str(), 0)) 91de0c960cSJagpal Singh Gill {} 92de0c960cSJagpal Singh Gill 93de0c960cSJagpal Singh Gill MemoryFileDescriptor(const MemoryFileDescriptor&) = default; 94de0c960cSJagpal Singh Gill MemoryFileDescriptor(MemoryFileDescriptor&& other) noexcept : fd(other.fd) 95de0c960cSJagpal Singh Gill { 96de0c960cSJagpal Singh Gill other.fd = -1; 97de0c960cSJagpal Singh Gill } 98de0c960cSJagpal Singh Gill MemoryFileDescriptor& operator=(const MemoryFileDescriptor&) = delete; 99de0c960cSJagpal Singh Gill MemoryFileDescriptor& operator=(MemoryFileDescriptor&&) = default; 100de0c960cSJagpal Singh Gill 101de0c960cSJagpal Singh Gill ~MemoryFileDescriptor() 102de0c960cSJagpal Singh Gill { 103de0c960cSJagpal Singh Gill if (fd != -1) 104de0c960cSJagpal Singh Gill { 105de0c960cSJagpal Singh Gill close(fd); 106de0c960cSJagpal Singh Gill } 107de0c960cSJagpal Singh Gill } 108de0c960cSJagpal Singh Gill 109de0c960cSJagpal Singh Gill bool rewind() const 110de0c960cSJagpal Singh Gill { 111de0c960cSJagpal Singh Gill if (lseek(fd, 0, SEEK_SET) == -1) 112de0c960cSJagpal Singh Gill { 113de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to seek to beginning of image memfd"); 114de0c960cSJagpal Singh Gill return false; 115de0c960cSJagpal Singh Gill } 116de0c960cSJagpal Singh Gill return true; 117de0c960cSJagpal Singh Gill } 118de0c960cSJagpal Singh Gill }; 119de0c960cSJagpal Singh Gill 120df254f2cSEd Tanous inline void cleanUp() 12186adcd6dSAndrew Geissler { 12286adcd6dSAndrew Geissler fwUpdateInProgress = false; 12386adcd6dSAndrew Geissler fwUpdateMatcher = nullptr; 1244cde5d90SJames Feist fwUpdateErrorMatcher = nullptr; 12586adcd6dSAndrew Geissler } 126df254f2cSEd Tanous 127df254f2cSEd Tanous inline void activateImage(const std::string& objPath, 12886adcd6dSAndrew Geissler const std::string& service) 12986adcd6dSAndrew Geissler { 13062598e31SEd Tanous BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service); 1319ae226faSGeorge Liu sdbusplus::asio::setProperty( 1329ae226faSGeorge Liu *crow::connections::systemBus, service, objPath, 1339ae226faSGeorge Liu "xyz.openbmc_project.Software.Activation", "RequestedActivation", 1349ae226faSGeorge Liu "xyz.openbmc_project.Software.Activation.RequestedActivations.Active", 1358b24275dSEd Tanous [](const boost::system::error_code& ec) { 1368b24275dSEd Tanous if (ec) 13786adcd6dSAndrew Geissler { 13862598e31SEd Tanous BMCWEB_LOG_DEBUG("error_code = {}", ec); 13962598e31SEd Tanous BMCWEB_LOG_DEBUG("error msg = {}", ec.message()); 14086adcd6dSAndrew Geissler } 1419ae226faSGeorge Liu }); 14286adcd6dSAndrew Geissler } 1430554c984SAndrew Geissler 144c71b6c99SJagpal Singh Gill inline bool handleCreateTask(const boost::system::error_code& ec2, 145c71b6c99SJagpal Singh Gill sdbusplus::message_t& msg, 146c71b6c99SJagpal Singh Gill const std::shared_ptr<task::TaskData>& taskData) 147c71b6c99SJagpal Singh Gill { 148c71b6c99SJagpal Singh Gill if (ec2) 149c71b6c99SJagpal Singh Gill { 150c71b6c99SJagpal Singh Gill return task::completed; 151c71b6c99SJagpal Singh Gill } 152c71b6c99SJagpal Singh Gill 153c71b6c99SJagpal Singh Gill std::string iface; 154c71b6c99SJagpal Singh Gill dbus::utility::DBusPropertiesMap values; 155c71b6c99SJagpal Singh Gill 156c71b6c99SJagpal Singh Gill std::string index = std::to_string(taskData->index); 157c71b6c99SJagpal Singh Gill msg.read(iface, values); 158c71b6c99SJagpal Singh Gill 159c71b6c99SJagpal Singh Gill if (iface == "xyz.openbmc_project.Software.Activation") 160c71b6c99SJagpal Singh Gill { 161c71b6c99SJagpal Singh Gill const std::string* state = nullptr; 162c71b6c99SJagpal Singh Gill for (const auto& property : values) 163c71b6c99SJagpal Singh Gill { 164c71b6c99SJagpal Singh Gill if (property.first == "Activation") 165c71b6c99SJagpal Singh Gill { 166c71b6c99SJagpal Singh Gill state = std::get_if<std::string>(&property.second); 167c71b6c99SJagpal Singh Gill if (state == nullptr) 168c71b6c99SJagpal Singh Gill { 169c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::internalError()); 170c71b6c99SJagpal Singh Gill return task::completed; 171c71b6c99SJagpal Singh Gill } 172c71b6c99SJagpal Singh Gill } 173c71b6c99SJagpal Singh Gill } 174c71b6c99SJagpal Singh Gill 175c71b6c99SJagpal Singh Gill if (state == nullptr) 176c71b6c99SJagpal Singh Gill { 177c71b6c99SJagpal Singh Gill return !task::completed; 178c71b6c99SJagpal Singh Gill } 179c71b6c99SJagpal Singh Gill 180c71b6c99SJagpal Singh Gill if (state->ends_with("Invalid") || state->ends_with("Failed")) 181c71b6c99SJagpal Singh Gill { 182c71b6c99SJagpal Singh Gill taskData->state = "Exception"; 183c71b6c99SJagpal Singh Gill taskData->status = "Warning"; 184c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskAborted(index)); 185c71b6c99SJagpal Singh Gill return task::completed; 186c71b6c99SJagpal Singh Gill } 187c71b6c99SJagpal Singh Gill 188c71b6c99SJagpal Singh Gill if (state->ends_with("Staged")) 189c71b6c99SJagpal Singh Gill { 190c71b6c99SJagpal Singh Gill taskData->state = "Stopping"; 191c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskPaused(index)); 192c71b6c99SJagpal Singh Gill 193c71b6c99SJagpal Singh Gill // its staged, set a long timer to 194c71b6c99SJagpal Singh Gill // allow them time to complete the 195c71b6c99SJagpal Singh Gill // update (probably cycle the 196c71b6c99SJagpal Singh Gill // system) if this expires then 197c71b6c99SJagpal Singh Gill // task will be canceled 198c71b6c99SJagpal Singh Gill taskData->extendTimer(std::chrono::hours(5)); 199c71b6c99SJagpal Singh Gill return !task::completed; 200c71b6c99SJagpal Singh Gill } 201c71b6c99SJagpal Singh Gill 202c71b6c99SJagpal Singh Gill if (state->ends_with("Active")) 203c71b6c99SJagpal Singh Gill { 204c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskCompletedOK(index)); 205c71b6c99SJagpal Singh Gill taskData->state = "Completed"; 206c71b6c99SJagpal Singh Gill return task::completed; 207c71b6c99SJagpal Singh Gill } 208c71b6c99SJagpal Singh Gill } 209c71b6c99SJagpal Singh Gill else if (iface == "xyz.openbmc_project.Software.ActivationProgress") 210c71b6c99SJagpal Singh Gill { 211c71b6c99SJagpal Singh Gill const uint8_t* progress = nullptr; 212c71b6c99SJagpal Singh Gill for (const auto& property : values) 213c71b6c99SJagpal Singh Gill { 214c71b6c99SJagpal Singh Gill if (property.first == "Progress") 215c71b6c99SJagpal Singh Gill { 216c71b6c99SJagpal Singh Gill progress = std::get_if<uint8_t>(&property.second); 217c71b6c99SJagpal Singh Gill if (progress == nullptr) 218c71b6c99SJagpal Singh Gill { 219c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::internalError()); 220c71b6c99SJagpal Singh Gill return task::completed; 221c71b6c99SJagpal Singh Gill } 222c71b6c99SJagpal Singh Gill } 223c71b6c99SJagpal Singh Gill } 224c71b6c99SJagpal Singh Gill 225c71b6c99SJagpal Singh Gill if (progress == nullptr) 226c71b6c99SJagpal Singh Gill { 227c71b6c99SJagpal Singh Gill return !task::completed; 228c71b6c99SJagpal Singh Gill } 229c71b6c99SJagpal Singh Gill taskData->percentComplete = *progress; 230c71b6c99SJagpal Singh Gill taskData->messages.emplace_back( 231c71b6c99SJagpal Singh Gill messages::taskProgressChanged(index, *progress)); 232c71b6c99SJagpal Singh Gill 233c71b6c99SJagpal Singh Gill // if we're getting status updates it's 234c71b6c99SJagpal Singh Gill // still alive, update timer 235c71b6c99SJagpal Singh Gill taskData->extendTimer(std::chrono::minutes(5)); 236c71b6c99SJagpal Singh Gill } 237c71b6c99SJagpal Singh Gill 238c71b6c99SJagpal Singh Gill // as firmware update often results in a 239c71b6c99SJagpal Singh Gill // reboot, the task may never "complete" 240c71b6c99SJagpal Singh Gill // unless it is an error 241c71b6c99SJagpal Singh Gill 242c71b6c99SJagpal Singh Gill return !task::completed; 243c71b6c99SJagpal Singh Gill } 244c71b6c99SJagpal Singh Gill 245c71b6c99SJagpal Singh Gill inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 246c71b6c99SJagpal Singh Gill task::Payload&& payload, 247c71b6c99SJagpal Singh Gill const sdbusplus::message::object_path& objPath) 248c71b6c99SJagpal Singh Gill { 249c71b6c99SJagpal Singh Gill std::shared_ptr<task::TaskData> task = task::TaskData::createTask( 250c71b6c99SJagpal Singh Gill std::bind_front(handleCreateTask), 251c71b6c99SJagpal Singh Gill "type='signal',interface='org.freedesktop.DBus.Properties'," 252c71b6c99SJagpal Singh Gill "member='PropertiesChanged',path='" + 253c71b6c99SJagpal Singh Gill objPath.str + "'"); 254c71b6c99SJagpal Singh Gill task->startTimer(std::chrono::minutes(5)); 255c71b6c99SJagpal Singh Gill task->populateResp(asyncResp->res); 256c71b6c99SJagpal Singh Gill task->payload.emplace(std::move(payload)); 257c71b6c99SJagpal Singh Gill } 258c71b6c99SJagpal Singh Gill 2590554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr 2600554c984SAndrew Geissler // then no asyncResp updates will occur 2614ff0f1f4SEd Tanous inline void 2628d1b46d7Szhanghch05 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 26359d494eeSPatrick Williams sdbusplus::message_t& m, task::Payload&& payload) 26486adcd6dSAndrew Geissler { 26580f79a40SMichael Shen dbus::utility::DBusInterfacesMap interfacesProperties; 26686adcd6dSAndrew Geissler 26786adcd6dSAndrew Geissler sdbusplus::message::object_path objPath; 26886adcd6dSAndrew Geissler 26986adcd6dSAndrew Geissler m.read(objPath, interfacesProperties); 27086adcd6dSAndrew Geissler 27162598e31SEd Tanous BMCWEB_LOG_DEBUG("obj path = {}", objPath.str); 272e3eb3d63SEd Tanous for (const auto& interface : interfacesProperties) 27386adcd6dSAndrew Geissler { 27462598e31SEd Tanous BMCWEB_LOG_DEBUG("interface = {}", interface.first); 27586adcd6dSAndrew Geissler 27686adcd6dSAndrew Geissler if (interface.first == "xyz.openbmc_project.Software.Activation") 27786adcd6dSAndrew Geissler { 27886adcd6dSAndrew Geissler // Retrieve service and activate 2792b73119cSGeorge Liu constexpr std::array<std::string_view, 1> interfaces = { 2802b73119cSGeorge Liu "xyz.openbmc_project.Software.Activation"}; 2812b73119cSGeorge Liu dbus::utility::getDbusObject( 2822b73119cSGeorge Liu objPath.str, interfaces, 283a3e65892SEd Tanous [objPath, asyncResp, payload(std::move(payload))]( 2848b24275dSEd Tanous const boost::system::error_code& ec, 285a3e65892SEd Tanous const std::vector< 286a3e65892SEd Tanous std::pair<std::string, std::vector<std::string>>>& 287a3e65892SEd Tanous objInfo) mutable { 2888b24275dSEd Tanous if (ec) 28986adcd6dSAndrew Geissler { 29062598e31SEd Tanous BMCWEB_LOG_DEBUG("error_code = {}", ec); 29162598e31SEd Tanous BMCWEB_LOG_DEBUG("error msg = {}", ec.message()); 2920554c984SAndrew Geissler if (asyncResp) 2930554c984SAndrew Geissler { 29486adcd6dSAndrew Geissler messages::internalError(asyncResp->res); 2950554c984SAndrew Geissler } 29686adcd6dSAndrew Geissler cleanUp(); 29786adcd6dSAndrew Geissler return; 29886adcd6dSAndrew Geissler } 29986adcd6dSAndrew Geissler // Ensure we only got one service back 30086adcd6dSAndrew Geissler if (objInfo.size() != 1) 30186adcd6dSAndrew Geissler { 302bd79bce8SPatrick Williams BMCWEB_LOG_ERROR("Invalid Object Size {}", 303bd79bce8SPatrick Williams objInfo.size()); 3040554c984SAndrew Geissler if (asyncResp) 3050554c984SAndrew Geissler { 30686adcd6dSAndrew Geissler messages::internalError(asyncResp->res); 3070554c984SAndrew Geissler } 30886adcd6dSAndrew Geissler cleanUp(); 30986adcd6dSAndrew Geissler return; 31086adcd6dSAndrew Geissler } 31186adcd6dSAndrew Geissler // cancel timer only when 31286adcd6dSAndrew Geissler // xyz.openbmc_project.Software.Activation interface 31386adcd6dSAndrew Geissler // is added 31486adcd6dSAndrew Geissler fwAvailableTimer = nullptr; 31586adcd6dSAndrew Geissler 31686adcd6dSAndrew Geissler activateImage(objPath.str, objInfo[0].first); 3170554c984SAndrew Geissler if (asyncResp) 3180554c984SAndrew Geissler { 319c71b6c99SJagpal Singh Gill createTask(asyncResp, std::move(payload), objPath); 3200554c984SAndrew Geissler } 32186adcd6dSAndrew Geissler fwUpdateInProgress = false; 3222b73119cSGeorge Liu }); 32362bafc01SPatrick Williams 32462bafc01SPatrick Williams break; 32586adcd6dSAndrew Geissler } 32686adcd6dSAndrew Geissler } 32786adcd6dSAndrew Geissler } 32886adcd6dSAndrew Geissler 3298549b951SMyung Bae inline void afterAvailbleTimerAsyncWait( 3308549b951SMyung Bae const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3318549b951SMyung Bae const boost::system::error_code& ec) 3328549b951SMyung Bae { 3338549b951SMyung Bae cleanUp(); 3348549b951SMyung Bae if (ec == boost::asio::error::operation_aborted) 3358549b951SMyung Bae { 3368549b951SMyung Bae // expected, we were canceled before the timer completed. 3378549b951SMyung Bae return; 3388549b951SMyung Bae } 3398549b951SMyung Bae BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created"); 3408549b951SMyung Bae BMCWEB_LOG_ERROR("FW image may has already been uploaded to server"); 3418549b951SMyung Bae if (ec) 3428549b951SMyung Bae { 3438549b951SMyung Bae BMCWEB_LOG_ERROR("Async_wait failed{}", ec); 3448549b951SMyung Bae return; 3458549b951SMyung Bae } 3468549b951SMyung Bae if (asyncResp) 3478549b951SMyung Bae { 3488549b951SMyung Bae redfish::messages::internalError(asyncResp->res); 3498549b951SMyung Bae } 3508549b951SMyung Bae } 3518549b951SMyung Bae 3528549b951SMyung Bae inline void 3538549b951SMyung Bae handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 35448fb20b9SEd Tanous const std::string& url, const std::string& type) 3558549b951SMyung Bae { 356c87294a6SEd Tanous // NOLINTBEGIN(bugprone-branch-clone) 3578549b951SMyung Bae if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure") 3588549b951SMyung Bae { 35948fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3608549b951SMyung Bae } 3618549b951SMyung Bae else if (type == 3628549b951SMyung Bae "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure") 3638549b951SMyung Bae { 36448fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3658549b951SMyung Bae } 3668549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure") 3678549b951SMyung Bae { 36848fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3698549b951SMyung Bae } 3708549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists") 3718549b951SMyung Bae { 372c87294a6SEd Tanous messages::resourceAlreadyExists(asyncResp->res, "UpdateService", 373c87294a6SEd Tanous "Version", "uploaded version"); 3748549b951SMyung Bae } 3758549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure") 3768549b951SMyung Bae { 37748fb20b9SEd Tanous messages::serviceTemporarilyUnavailable(asyncResp->res, url); 3788549b951SMyung Bae } 3794034a652SMyung Bae else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible") 3808549b951SMyung Bae { 381c87294a6SEd Tanous messages::internalError(asyncResp->res); 3824034a652SMyung Bae } 3834034a652SMyung Bae else if (type == 3844034a652SMyung Bae "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey") 3854034a652SMyung Bae { 386c87294a6SEd Tanous messages::internalError(asyncResp->res); 3874034a652SMyung Bae } 3884034a652SMyung Bae else if (type == 3894034a652SMyung Bae "xyz.openbmc_project.Software.Version.Error.InvalidSignature") 3904034a652SMyung Bae { 39148fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3924034a652SMyung Bae } 3934034a652SMyung Bae else if (type == 3944034a652SMyung Bae "xyz.openbmc_project.Software.Image.Error.InternalFailure" || 3954034a652SMyung Bae type == "xyz.openbmc_project.Software.Version.Error.HostFile") 3964034a652SMyung Bae { 3974034a652SMyung Bae BMCWEB_LOG_ERROR("Software Image Error type={}", type); 39848fb20b9SEd Tanous messages::internalError(asyncResp->res); 3998549b951SMyung Bae } 4004034a652SMyung Bae else 4014034a652SMyung Bae { 4024034a652SMyung Bae // Unrelated error types. Ignored 4034034a652SMyung Bae BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type); 4044034a652SMyung Bae return; 4054034a652SMyung Bae } 406c87294a6SEd Tanous // NOLINTEND(bugprone-branch-clone) 4074034a652SMyung Bae // Clear the timer 4084034a652SMyung Bae fwAvailableTimer = nullptr; 4098549b951SMyung Bae } 4108549b951SMyung Bae 4118549b951SMyung Bae inline void 4128549b951SMyung Bae afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 4138549b951SMyung Bae const std::string& url, sdbusplus::message_t& m) 4148549b951SMyung Bae { 41580f79a40SMichael Shen dbus::utility::DBusInterfacesMap interfacesProperties; 4168549b951SMyung Bae sdbusplus::message::object_path objPath; 4178549b951SMyung Bae m.read(objPath, interfacesProperties); 4188549b951SMyung Bae BMCWEB_LOG_DEBUG("obj path = {}", objPath.str); 4198549b951SMyung Bae for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>& 4208549b951SMyung Bae interface : interfacesProperties) 4218549b951SMyung Bae { 4228549b951SMyung Bae if (interface.first == "xyz.openbmc_project.Logging.Entry") 4238549b951SMyung Bae { 4248549b951SMyung Bae for (const std::pair<std::string, dbus::utility::DbusVariantType>& 4258549b951SMyung Bae value : interface.second) 4268549b951SMyung Bae { 4278549b951SMyung Bae if (value.first != "Message") 4288549b951SMyung Bae { 4298549b951SMyung Bae continue; 4308549b951SMyung Bae } 4318549b951SMyung Bae const std::string* type = 4328549b951SMyung Bae std::get_if<std::string>(&value.second); 4338549b951SMyung Bae if (type == nullptr) 4348549b951SMyung Bae { 4358549b951SMyung Bae // if this was our message, timeout will cover it 4368549b951SMyung Bae return; 4378549b951SMyung Bae } 4388549b951SMyung Bae handleUpdateErrorType(asyncResp, url, *type); 4398549b951SMyung Bae } 4408549b951SMyung Bae } 4418549b951SMyung Bae } 4428549b951SMyung Bae } 4438549b951SMyung Bae 4440554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr 4450554c984SAndrew Geissler // then no asyncResp updates will occur 446f5139334SEd Tanous inline void monitorForSoftwareAvailable( 4478d1b46d7Szhanghch05 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 4488d1b46d7Szhanghch05 const crow::Request& req, const std::string& url, 4495d138943SGunnar Mills int timeoutTimeSeconds = 25) 45086adcd6dSAndrew Geissler { 45186adcd6dSAndrew Geissler // Only allow one FW update at a time 452e05aec50SEd Tanous if (fwUpdateInProgress) 45386adcd6dSAndrew Geissler { 4540554c984SAndrew Geissler if (asyncResp) 4550554c984SAndrew Geissler { 45686adcd6dSAndrew Geissler messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 4570554c984SAndrew Geissler } 45886adcd6dSAndrew Geissler return; 45986adcd6dSAndrew Geissler } 46086adcd6dSAndrew Geissler 4618e8245dbSEd Tanous if (req.ioService == nullptr) 4628e8245dbSEd Tanous { 4638e8245dbSEd Tanous messages::internalError(asyncResp->res); 4648e8245dbSEd Tanous return; 4658e8245dbSEd Tanous } 4668e8245dbSEd Tanous 4670554c984SAndrew Geissler fwAvailableTimer = 468271584abSEd Tanous std::make_unique<boost::asio::steady_timer>(*req.ioService); 46986adcd6dSAndrew Geissler 470271584abSEd Tanous fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds)); 47186adcd6dSAndrew Geissler 47286adcd6dSAndrew Geissler fwAvailableTimer->async_wait( 4738549b951SMyung Bae std::bind_front(afterAvailbleTimerAsyncWait, asyncResp)); 4748549b951SMyung Bae 475a3e65892SEd Tanous task::Payload payload(req); 47659d494eeSPatrick Williams auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable { 47762598e31SEd Tanous BMCWEB_LOG_DEBUG("Match fired"); 478a3e65892SEd Tanous softwareInterfaceAdded(asyncResp, m, std::move(payload)); 47986adcd6dSAndrew Geissler }; 48086adcd6dSAndrew Geissler 48186adcd6dSAndrew Geissler fwUpdateInProgress = true; 48286adcd6dSAndrew Geissler 48359d494eeSPatrick Williams fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>( 48486adcd6dSAndrew Geissler *crow::connections::systemBus, 48586adcd6dSAndrew Geissler "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 48686adcd6dSAndrew Geissler "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 48786adcd6dSAndrew Geissler callback); 4884cde5d90SJames Feist 48959d494eeSPatrick Williams fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>( 4904cde5d90SJames Feist *crow::connections::systemBus, 491e1cc4828SBrian Ma "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 492e1cc4828SBrian Ma "member='InterfacesAdded'," 493e1cc4828SBrian Ma "path='/xyz/openbmc_project/logging'", 4948549b951SMyung Bae std::bind_front(afterUpdateErrorMatcher, asyncResp, url)); 49586adcd6dSAndrew Geissler } 496729dae72SJennifer Lee 497bd79bce8SPatrick Williams inline std::optional<boost::urls::url> parseSimpleUpdateUrl( 498bd79bce8SPatrick Williams std::string imageURI, std::optional<std::string> transferProtocol, 499f86bcc87SEd Tanous crow::Response& res) 500f86bcc87SEd Tanous { 501f86bcc87SEd Tanous if (imageURI.find("://") == std::string::npos) 502f86bcc87SEd Tanous { 503f86bcc87SEd Tanous if (imageURI.starts_with("/")) 504f86bcc87SEd Tanous { 505f86bcc87SEd Tanous messages::actionParameterValueTypeError( 506f86bcc87SEd Tanous res, imageURI, "ImageURI", "UpdateService.SimpleUpdate"); 507f86bcc87SEd Tanous return std::nullopt; 508f86bcc87SEd Tanous } 509f86bcc87SEd Tanous if (!transferProtocol) 510f86bcc87SEd Tanous { 511f86bcc87SEd Tanous messages::actionParameterValueTypeError( 512f86bcc87SEd Tanous res, imageURI, "ImageURI", "UpdateService.SimpleUpdate"); 513f86bcc87SEd Tanous return std::nullopt; 514f86bcc87SEd Tanous } 5156a37140aSEd Tanous // OpenBMC currently only supports HTTPS 5166a37140aSEd Tanous if (*transferProtocol == "HTTPS") 517e5cf777eSEd Tanous { 518e5cf777eSEd Tanous imageURI = "https://" + imageURI; 519e5cf777eSEd Tanous } 520757178a5SEd Tanous else 521f86bcc87SEd Tanous { 522f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "TransferProtocol", 523f86bcc87SEd Tanous *transferProtocol); 524f86bcc87SEd Tanous BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}", 525f86bcc87SEd Tanous *transferProtocol); 526f86bcc87SEd Tanous return std::nullopt; 527f86bcc87SEd Tanous } 528f86bcc87SEd Tanous } 529f86bcc87SEd Tanous 530f86bcc87SEd Tanous boost::system::result<boost::urls::url> url = 531f86bcc87SEd Tanous boost::urls::parse_absolute_uri(imageURI); 532f86bcc87SEd Tanous if (!url) 533f86bcc87SEd Tanous { 534f86bcc87SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI", 535f86bcc87SEd Tanous "UpdateService.SimpleUpdate"); 536f86bcc87SEd Tanous 537f86bcc87SEd Tanous return std::nullopt; 538f86bcc87SEd Tanous } 539f86bcc87SEd Tanous url->normalize(); 540f86bcc87SEd Tanous 541757178a5SEd Tanous if (url->scheme() == "tftp") 542757178a5SEd Tanous { 543757178a5SEd Tanous if (url->encoded_path().size() < 2) 544757178a5SEd Tanous { 545757178a5SEd Tanous messages::actionParameterNotSupported(res, "ImageURI", 546757178a5SEd Tanous url->buffer()); 547757178a5SEd Tanous return std::nullopt; 548757178a5SEd Tanous } 549757178a5SEd Tanous } 550e5cf777eSEd Tanous else if (url->scheme() == "https") 551e5cf777eSEd Tanous { 552e5cf777eSEd Tanous // Empty paths default to "/" 553e5cf777eSEd Tanous if (url->encoded_path().empty()) 554e5cf777eSEd Tanous { 555e5cf777eSEd Tanous url->set_encoded_path("/"); 556e5cf777eSEd Tanous } 557e5cf777eSEd Tanous } 558757178a5SEd Tanous else 559f86bcc87SEd Tanous { 560f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "ImageURI", imageURI); 561f86bcc87SEd Tanous return std::nullopt; 562f86bcc87SEd Tanous } 563757178a5SEd Tanous 564757178a5SEd Tanous if (url->encoded_path().empty()) 565f86bcc87SEd Tanous { 566757178a5SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI", 567757178a5SEd Tanous "UpdateService.SimpleUpdate"); 568f86bcc87SEd Tanous return std::nullopt; 569f86bcc87SEd Tanous } 570757178a5SEd Tanous 571757178a5SEd Tanous return *url; 572f86bcc87SEd Tanous } 573f86bcc87SEd Tanous 574e5cf777eSEd Tanous inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 575e5cf777eSEd Tanous const boost::urls::url_view_base& url) 576e5cf777eSEd Tanous { 577e5cf777eSEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI", 578e5cf777eSEd Tanous url.buffer()); 579e5cf777eSEd Tanous } 580e5cf777eSEd Tanous 581f5139334SEd Tanous inline void handleUpdateServiceSimpleUpdateAction( 582f5139334SEd Tanous crow::App& app, const crow::Request& req, 583f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 5840554c984SAndrew Geissler { 5853ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 58645ca1b86SEd Tanous { 58745ca1b86SEd Tanous return; 58845ca1b86SEd Tanous } 58945ca1b86SEd Tanous 5900554c984SAndrew Geissler std::optional<std::string> transferProtocol; 5910554c984SAndrew Geissler std::string imageURI; 5920554c984SAndrew Geissler 59362598e31SEd Tanous BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost"); 5940554c984SAndrew Geissler 5950554c984SAndrew Geissler // User can pass in both TransferProtocol and ImageURI parameters or 5964e0453b1SGunnar Mills // they can pass in just the ImageURI with the transfer protocol 5974e0453b1SGunnar Mills // embedded within it. 5980554c984SAndrew Geissler // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin 5990554c984SAndrew Geissler // 2) ImageURI:tftp://1.1.1.1/myfile.bin 6000554c984SAndrew Geissler 601afc474aeSMyung Bae if (!json_util::readJsonAction( // 602afc474aeSMyung Bae req, asyncResp->res, // 603afc474aeSMyung Bae "ImageURI", imageURI, // 604afc474aeSMyung Bae "TransferProtocol", transferProtocol // 605afc474aeSMyung Bae )) 6060554c984SAndrew Geissler { 60762598e31SEd Tanous BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter"); 6080554c984SAndrew Geissler return; 6090554c984SAndrew Geissler } 610f5139334SEd Tanous 611757178a5SEd Tanous std::optional<boost::urls::url> url = 612757178a5SEd Tanous parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res); 613757178a5SEd Tanous if (!url) 6140554c984SAndrew Geissler { 6150554c984SAndrew Geissler return; 6160554c984SAndrew Geissler } 6174e338b23SJagpal Singh Gill if (url->scheme() == "https") 618e5cf777eSEd Tanous { 619e5cf777eSEd Tanous doHttpsUpdate(asyncResp, *url); 620e5cf777eSEd Tanous } 621757178a5SEd Tanous else 622757178a5SEd Tanous { 623757178a5SEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI", 624757178a5SEd Tanous url->buffer()); 625757178a5SEd Tanous return; 626757178a5SEd Tanous } 6270554c984SAndrew Geissler 62862598e31SEd Tanous BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost"); 629729dae72SJennifer Lee } 630729dae72SJennifer Lee 6310ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body) 6320ed80c8cSGeorge Liu { 6332c6ffdb0SEd Tanous std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID()); 6342c6ffdb0SEd Tanous 63562598e31SEd Tanous BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string()); 6360ed80c8cSGeorge Liu std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | 6370ed80c8cSGeorge Liu std::ofstream::trunc); 6380ed80c8cSGeorge Liu // set the permission of the file to 640 639bd79bce8SPatrick Williams std::filesystem::perms permission = 640bd79bce8SPatrick Williams std::filesystem::perms::owner_read | std::filesystem::perms::group_read; 6410ed80c8cSGeorge Liu std::filesystem::permissions(filepath, permission); 6420ed80c8cSGeorge Liu out << body; 6430ed80c8cSGeorge Liu 6440ed80c8cSGeorge Liu if (out.bad()) 6450ed80c8cSGeorge Liu { 6460ed80c8cSGeorge Liu messages::internalError(res); 6470ed80c8cSGeorge Liu cleanUp(); 6480ed80c8cSGeorge Liu } 6490ed80c8cSGeorge Liu } 6500ed80c8cSGeorge Liu 651de0c960cSJagpal Singh Gill // Convert the Request Apply Time to the D-Bus value 652de0c960cSJagpal Singh Gill inline bool convertApplyTime(crow::Response& res, const std::string& applyTime, 653de0c960cSJagpal Singh Gill std::string& applyTimeNewVal) 654de0c960cSJagpal Singh Gill { 655de0c960cSJagpal Singh Gill if (applyTime == "Immediate") 656de0c960cSJagpal Singh Gill { 657de0c960cSJagpal Singh Gill applyTimeNewVal = 658049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate"; 659de0c960cSJagpal Singh Gill } 660de0c960cSJagpal Singh Gill else if (applyTime == "OnReset") 661de0c960cSJagpal Singh Gill { 662de0c960cSJagpal Singh Gill applyTimeNewVal = 663049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset"; 664de0c960cSJagpal Singh Gill } 665de0c960cSJagpal Singh Gill else 666de0c960cSJagpal Singh Gill { 667de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING( 668de0c960cSJagpal Singh Gill "ApplyTime value {} is not in the list of acceptable values", 669de0c960cSJagpal Singh Gill applyTime); 670de0c960cSJagpal Singh Gill messages::propertyValueNotInList(res, applyTime, "ApplyTime"); 671de0c960cSJagpal Singh Gill return false; 672de0c960cSJagpal Singh Gill } 673de0c960cSJagpal Singh Gill return true; 674de0c960cSJagpal Singh Gill } 675de0c960cSJagpal Singh Gill 6760ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 6770ed80c8cSGeorge Liu const std::string& applyTime) 6780ed80c8cSGeorge Liu { 6790ed80c8cSGeorge Liu std::string applyTimeNewVal; 680049079f6SJagpal Singh Gill if (!convertApplyTime(asyncResp->res, applyTime, applyTimeNewVal)) 6810ed80c8cSGeorge Liu { 6820ed80c8cSGeorge Liu return; 6830ed80c8cSGeorge Liu } 6840ed80c8cSGeorge Liu 685e93abac6SGinu George setDbusProperty(asyncResp, "ApplyTime", "xyz.openbmc_project.Settings", 686d02aad39SEd Tanous sdbusplus::message::object_path( 687d02aad39SEd Tanous "/xyz/openbmc_project/software/apply_time"), 688d02aad39SEd Tanous "xyz.openbmc_project.Software.ApplyTime", 689e93abac6SGinu George "RequestedApplyTime", applyTimeNewVal); 6900ed80c8cSGeorge Liu } 6910ed80c8cSGeorge Liu 692ef93eab3SJagpal Singh Gill struct MultiPartUpdateParameters 6930ed80c8cSGeorge Liu { 694ef93eab3SJagpal Singh Gill std::optional<std::string> applyTime; 695ef93eab3SJagpal Singh Gill std::string uploadData; 696de0c960cSJagpal Singh Gill std::vector<std::string> targets; 697ef93eab3SJagpal Singh Gill }; 698ef93eab3SJagpal Singh Gill 699de0c960cSJagpal Singh Gill inline std::optional<std::string> 700de0c960cSJagpal Singh Gill processUrl(boost::system::result<boost::urls::url_view>& url) 701de0c960cSJagpal Singh Gill { 702de0c960cSJagpal Singh Gill if (!url) 703de0c960cSJagpal Singh Gill { 704de0c960cSJagpal Singh Gill return std::nullopt; 705de0c960cSJagpal Singh Gill } 706de0c960cSJagpal Singh Gill if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers", 707de0c960cSJagpal Singh Gill BMCWEB_REDFISH_MANAGER_URI_NAME)) 708de0c960cSJagpal Singh Gill { 709de0c960cSJagpal Singh Gill return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME)); 710de0c960cSJagpal Singh Gill } 711de0c960cSJagpal Singh Gill if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 712de0c960cSJagpal Singh Gill { 713de0c960cSJagpal Singh Gill return std::nullopt; 714de0c960cSJagpal Singh Gill } 715de0c960cSJagpal Singh Gill std::string firmwareId; 716de0c960cSJagpal Singh Gill if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService", 717de0c960cSJagpal Singh Gill "FirmwareInventory", 718de0c960cSJagpal Singh Gill std::ref(firmwareId))) 719de0c960cSJagpal Singh Gill { 720de0c960cSJagpal Singh Gill return std::nullopt; 721de0c960cSJagpal Singh Gill } 722de0c960cSJagpal Singh Gill 723de0c960cSJagpal Singh Gill return std::make_optional(firmwareId); 724de0c960cSJagpal Singh Gill } 725de0c960cSJagpal Singh Gill 726ef93eab3SJagpal Singh Gill inline std::optional<MultiPartUpdateParameters> 727ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters( 728ef93eab3SJagpal Singh Gill const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 729ef93eab3SJagpal Singh Gill MultipartParser parser) 730ef93eab3SJagpal Singh Gill { 731ef93eab3SJagpal Singh Gill MultiPartUpdateParameters multiRet; 732ef93eab3SJagpal Singh Gill for (FormPart& formpart : parser.mime_fields) 7330ed80c8cSGeorge Liu { 7340ed80c8cSGeorge Liu boost::beast::http::fields::const_iterator it = 7350ed80c8cSGeorge Liu formpart.fields.find("Content-Disposition"); 7360ed80c8cSGeorge Liu if (it == formpart.fields.end()) 7370ed80c8cSGeorge Liu { 73862598e31SEd Tanous BMCWEB_LOG_ERROR("Couldn't find Content-Disposition"); 739ef93eab3SJagpal Singh Gill return std::nullopt; 7400ed80c8cSGeorge Liu } 74162598e31SEd Tanous BMCWEB_LOG_INFO("Parsing value {}", it->value()); 7420ed80c8cSGeorge Liu 7430ed80c8cSGeorge Liu // The construction parameters of param_list must start with `;` 7440ed80c8cSGeorge Liu size_t index = it->value().find(';'); 7450ed80c8cSGeorge Liu if (index == std::string::npos) 7460ed80c8cSGeorge Liu { 7470ed80c8cSGeorge Liu continue; 7480ed80c8cSGeorge Liu } 7490ed80c8cSGeorge Liu 75089492a15SPatrick Williams for (const auto& param : 7510ed80c8cSGeorge Liu boost::beast::http::param_list{it->value().substr(index)}) 7520ed80c8cSGeorge Liu { 7530ed80c8cSGeorge Liu if (param.first != "name" || param.second.empty()) 7540ed80c8cSGeorge Liu { 7550ed80c8cSGeorge Liu continue; 7560ed80c8cSGeorge Liu } 7570ed80c8cSGeorge Liu 7580ed80c8cSGeorge Liu if (param.second == "UpdateParameters") 7590ed80c8cSGeorge Liu { 760ef93eab3SJagpal Singh Gill std::vector<std::string> tempTargets; 761bd79bce8SPatrick Williams nlohmann::json content = 762bd79bce8SPatrick Williams nlohmann::json::parse(formpart.content, nullptr, false); 763ac1e1246SEd Tanous if (content.is_discarded()) 764ac1e1246SEd Tanous { 765ac1e1246SEd Tanous return std::nullopt; 766ac1e1246SEd Tanous } 7677cb59f65SEd Tanous nlohmann::json::object_t* obj = 7687cb59f65SEd Tanous content.get_ptr<nlohmann::json::object_t*>(); 7697cb59f65SEd Tanous if (obj == nullptr) 7707cb59f65SEd Tanous { 771ef93eab3SJagpal Singh Gill messages::propertyValueTypeError( 772ef93eab3SJagpal Singh Gill asyncResp->res, formpart.content, "UpdateParameters"); 773ef93eab3SJagpal Singh Gill return std::nullopt; 7747cb59f65SEd Tanous } 7757cb59f65SEd Tanous 776afc474aeSMyung Bae if (!json_util::readJsonObject( // 777afc474aeSMyung Bae *obj, asyncResp->res, // 778afc474aeSMyung Bae "@Redfish.OperationApplyTime", multiRet.applyTime, // 779afc474aeSMyung Bae "Targets", tempTargets // 780afc474aeSMyung Bae )) 7810ed80c8cSGeorge Liu { 782ef93eab3SJagpal Singh Gill return std::nullopt; 7830ed80c8cSGeorge Liu } 784ef93eab3SJagpal Singh Gill 785ef93eab3SJagpal Singh Gill for (size_t urlIndex = 0; urlIndex < tempTargets.size(); 786ef93eab3SJagpal Singh Gill urlIndex++) 7870ed80c8cSGeorge Liu { 788ef93eab3SJagpal Singh Gill const std::string& target = tempTargets[urlIndex]; 789ef93eab3SJagpal Singh Gill boost::system::result<boost::urls::url_view> url = 790ef93eab3SJagpal Singh Gill boost::urls::parse_origin_form(target); 791de0c960cSJagpal Singh Gill auto res = processUrl(url); 792de0c960cSJagpal Singh Gill if (!res.has_value()) 7930ed80c8cSGeorge Liu { 794ef93eab3SJagpal Singh Gill messages::propertyValueFormatError( 795ef93eab3SJagpal Singh Gill asyncResp->res, target, 796ef93eab3SJagpal Singh Gill std::format("Targets/{}", urlIndex)); 797ef93eab3SJagpal Singh Gill return std::nullopt; 7980ed80c8cSGeorge Liu } 799de0c960cSJagpal Singh Gill multiRet.targets.emplace_back(res.value()); 800ef93eab3SJagpal Singh Gill } 801ef93eab3SJagpal Singh Gill if (multiRet.targets.size() != 1) 802ef93eab3SJagpal Singh Gill { 803ef93eab3SJagpal Singh Gill messages::propertyValueFormatError( 804ef93eab3SJagpal Singh Gill asyncResp->res, multiRet.targets, "Targets"); 805ef93eab3SJagpal Singh Gill return std::nullopt; 806ef93eab3SJagpal Singh Gill } 8070ed80c8cSGeorge Liu } 8080ed80c8cSGeorge Liu else if (param.second == "UpdateFile") 8090ed80c8cSGeorge Liu { 810ef93eab3SJagpal Singh Gill multiRet.uploadData = std::move(formpart.content); 8110ed80c8cSGeorge Liu } 8120ed80c8cSGeorge Liu } 8130ed80c8cSGeorge Liu } 8140ed80c8cSGeorge Liu 815ef93eab3SJagpal Singh Gill if (multiRet.uploadData.empty()) 8160ed80c8cSGeorge Liu { 81762598e31SEd Tanous BMCWEB_LOG_ERROR("Upload data is NULL"); 8180ed80c8cSGeorge Liu messages::propertyMissing(asyncResp->res, "UpdateFile"); 819ef93eab3SJagpal Singh Gill return std::nullopt; 8200ed80c8cSGeorge Liu } 821ef93eab3SJagpal Singh Gill if (multiRet.targets.empty()) 8220ed80c8cSGeorge Liu { 823ef93eab3SJagpal Singh Gill messages::propertyMissing(asyncResp->res, "Targets"); 824ef93eab3SJagpal Singh Gill return std::nullopt; 825ef93eab3SJagpal Singh Gill } 826ef93eab3SJagpal Singh Gill return multiRet; 8270ed80c8cSGeorge Liu } 8280ed80c8cSGeorge Liu 829bd79bce8SPatrick Williams inline void handleStartUpdate( 830bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 831bd79bce8SPatrick Williams const std::string& objectPath, const boost::system::error_code& ec, 832de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) 833de0c960cSJagpal Singh Gill { 834de0c960cSJagpal Singh Gill if (ec) 835de0c960cSJagpal Singh Gill { 836de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 837de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 838de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 839de0c960cSJagpal Singh Gill return; 840de0c960cSJagpal Singh Gill } 841de0c960cSJagpal Singh Gill 842587090cdSJagpal Singh Gill BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}", 843587090cdSJagpal Singh Gill objectPath, retPath.str); 844587090cdSJagpal Singh Gill createTask(asyncResp, std::move(payload), retPath); 845de0c960cSJagpal Singh Gill } 846de0c960cSJagpal Singh Gill 847bd79bce8SPatrick Williams inline void startUpdate( 848bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 849bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 850bd79bce8SPatrick Williams const std::string& objectPath, const std::string& serviceName) 851de0c960cSJagpal Singh Gill { 852de0c960cSJagpal Singh Gill crow::connections::systemBus->async_method_call( 853de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), 854de0c960cSJagpal Singh Gill objectPath](const boost::system::error_code& ec1, 855de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) mutable { 856de0c960cSJagpal Singh Gill handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1, 857de0c960cSJagpal Singh Gill retPath); 858de0c960cSJagpal Singh Gill }, 859de0c960cSJagpal Singh Gill serviceName, objectPath, "xyz.openbmc_project.Software.Update", 860de0c960cSJagpal Singh Gill "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime); 861de0c960cSJagpal Singh Gill } 862de0c960cSJagpal Singh Gill 86308f61d53SJagpal Singh Gill inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 86408f61d53SJagpal Singh Gill task::Payload payload, const MemoryFileDescriptor& memfd, 86508f61d53SJagpal Singh Gill const std::string& applyTime, const std::string& target, 866de0c960cSJagpal Singh Gill const boost::system::error_code& ec, 867de0c960cSJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& subtree) 868de0c960cSJagpal Singh Gill { 86908f61d53SJagpal Singh Gill using SwInfoMap = std::unordered_map< 87008f61d53SJagpal Singh Gill std::string, std::pair<sdbusplus::message::object_path, std::string>>; 871de0c960cSJagpal Singh Gill SwInfoMap swInfoMap; 872de0c960cSJagpal Singh Gill 873de0c960cSJagpal Singh Gill if (ec) 874de0c960cSJagpal Singh Gill { 875de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 876de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 877de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 878de0c960cSJagpal Singh Gill return; 879de0c960cSJagpal Singh Gill } 880de0c960cSJagpal Singh Gill BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size()); 881de0c960cSJagpal Singh Gill 88208f61d53SJagpal Singh Gill for (const auto& entry : subtree) 883de0c960cSJagpal Singh Gill { 88408f61d53SJagpal Singh Gill sdbusplus::message::object_path path(entry.first); 885de0c960cSJagpal Singh Gill std::string swId = path.filename(); 88608f61d53SJagpal Singh Gill swInfoMap.emplace(swId, make_pair(path, entry.second[0].first)); 887de0c960cSJagpal Singh Gill } 888de0c960cSJagpal Singh Gill 889de0c960cSJagpal Singh Gill auto swEntry = swInfoMap.find(target); 890de0c960cSJagpal Singh Gill if (swEntry == swInfoMap.end()) 891de0c960cSJagpal Singh Gill { 892de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target); 893de0c960cSJagpal Singh Gill messages::propertyValueFormatError(asyncResp->res, target, "Targets"); 894de0c960cSJagpal Singh Gill return; 895de0c960cSJagpal Singh Gill } 896de0c960cSJagpal Singh Gill 89708f61d53SJagpal Singh Gill BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}", 89808f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 899de0c960cSJagpal Singh Gill 90008f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 90108f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 90208f61d53SJagpal Singh Gill } 90308f61d53SJagpal Singh Gill 904bd79bce8SPatrick Williams inline void handleBMCUpdate( 905bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 906bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 90708f61d53SJagpal Singh Gill const boost::system::error_code& ec, 90808f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& functionalSoftware) 90908f61d53SJagpal Singh Gill { 91008f61d53SJagpal Singh Gill if (ec) 91108f61d53SJagpal Singh Gill { 91208f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 91308f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 91408f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 91508f61d53SJagpal Singh Gill return; 91608f61d53SJagpal Singh Gill } 91708f61d53SJagpal Singh Gill if (functionalSoftware.size() != 1) 91808f61d53SJagpal Singh Gill { 91908f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("Found {} functional software endpoints", 92008f61d53SJagpal Singh Gill functionalSoftware.size()); 92108f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 92208f61d53SJagpal Singh Gill return; 92308f61d53SJagpal Singh Gill } 92408f61d53SJagpal Singh Gill 92508f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 92608f61d53SJagpal Singh Gill functionalSoftware[0], "xyz.openbmc_project.Software.Manager"); 927de0c960cSJagpal Singh Gill } 928de0c960cSJagpal Singh Gill 929bd79bce8SPatrick Williams inline void processUpdateRequest( 930bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 9319dae4deeSJagpal Singh Gill task::Payload&& payload, std::string_view body, 932bd79bce8SPatrick Williams const std::string& applyTime, std::vector<std::string>& targets) 933de0c960cSJagpal Singh Gill { 934de0c960cSJagpal Singh Gill MemoryFileDescriptor memfd("update-image"); 935de0c960cSJagpal Singh Gill if (memfd.fd == -1) 936de0c960cSJagpal Singh Gill { 937de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to create image memfd"); 938de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 939de0c960cSJagpal Singh Gill return; 940de0c960cSJagpal Singh Gill } 941de0c960cSJagpal Singh Gill if (write(memfd.fd, body.data(), body.length()) != 942de0c960cSJagpal Singh Gill static_cast<ssize_t>(body.length())) 943de0c960cSJagpal Singh Gill { 944de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to write to image memfd"); 945de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 946de0c960cSJagpal Singh Gill return; 947de0c960cSJagpal Singh Gill } 948de0c960cSJagpal Singh Gill if (!memfd.rewind()) 949de0c960cSJagpal Singh Gill { 950de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 951de0c960cSJagpal Singh Gill return; 952de0c960cSJagpal Singh Gill } 953de0c960cSJagpal Singh Gill 954de0c960cSJagpal Singh Gill if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME) 955de0c960cSJagpal Singh Gill { 95608f61d53SJagpal Singh Gill dbus::utility::getAssociationEndPoints( 95789449bbeSJagpal Singh Gill "/xyz/openbmc_project/software/bmc/updateable", 95808f61d53SJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 95908f61d53SJagpal Singh Gill applyTime]( 96008f61d53SJagpal Singh Gill const boost::system::error_code& ec, 96108f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& objectPaths) mutable { 962bd79bce8SPatrick Williams handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime, 963bd79bce8SPatrick Williams ec, objectPaths); 96408f61d53SJagpal Singh Gill }); 965de0c960cSJagpal Singh Gill } 966de0c960cSJagpal Singh Gill else 967de0c960cSJagpal Singh Gill { 968de0c960cSJagpal Singh Gill constexpr std::array<std::string_view, 1> interfaces = { 969de0c960cSJagpal Singh Gill "xyz.openbmc_project.Software.Version"}; 97008f61d53SJagpal Singh Gill dbus::utility::getSubTree( 971de0c960cSJagpal Singh Gill "/xyz/openbmc_project/software", 1, interfaces, 972de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 97308f61d53SJagpal Singh Gill applyTime, targets](const boost::system::error_code& ec, 97408f61d53SJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& 975de0c960cSJagpal Singh Gill subtree) mutable { 97608f61d53SJagpal Singh Gill getSwInfo(asyncResp, std::move(payload), memfd, applyTime, 97708f61d53SJagpal Singh Gill targets[0], ec, subtree); 978de0c960cSJagpal Singh Gill }); 979de0c960cSJagpal Singh Gill } 980de0c960cSJagpal Singh Gill } 981de0c960cSJagpal Singh Gill 982de0c960cSJagpal Singh Gill inline void 983ef93eab3SJagpal Singh Gill updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 984ef93eab3SJagpal Singh Gill const crow::Request& req, MultipartParser&& parser) 985ef93eab3SJagpal Singh Gill { 986ef93eab3SJagpal Singh Gill std::optional<MultiPartUpdateParameters> multipart = 987ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters(asyncResp, std::move(parser)); 988ef93eab3SJagpal Singh Gill if (!multipart) 989ef93eab3SJagpal Singh Gill { 990ef93eab3SJagpal Singh Gill return; 991ef93eab3SJagpal Singh Gill } 992ef93eab3SJagpal Singh Gill if (!multipart->applyTime) 993ef93eab3SJagpal Singh Gill { 994ef93eab3SJagpal Singh Gill multipart->applyTime = "OnReset"; 995ef93eab3SJagpal Singh Gill } 996ef93eab3SJagpal Singh Gill 997de0c960cSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 998de0c960cSJagpal Singh Gill { 9999dae4deeSJagpal Singh Gill std::string applyTimeNewVal; 10009dae4deeSJagpal Singh Gill if (!convertApplyTime(asyncResp->res, *multipart->applyTime, 10019dae4deeSJagpal Singh Gill applyTimeNewVal)) 10029dae4deeSJagpal Singh Gill { 10039dae4deeSJagpal Singh Gill return; 10049dae4deeSJagpal Singh Gill } 10059dae4deeSJagpal Singh Gill task::Payload payload(req); 10069dae4deeSJagpal Singh Gill 10079dae4deeSJagpal Singh Gill processUpdateRequest(asyncResp, std::move(payload), 10089dae4deeSJagpal Singh Gill multipart->uploadData, applyTimeNewVal, 10099dae4deeSJagpal Singh Gill multipart->targets); 1010de0c960cSJagpal Singh Gill } 1011de0c960cSJagpal Singh Gill else 1012de0c960cSJagpal Singh Gill { 1013ef93eab3SJagpal Singh Gill setApplyTime(asyncResp, *multipart->applyTime); 10140ed80c8cSGeorge Liu 10156b54e4e0SEd Tanous // Setup callback for when new software detected 1016de0c960cSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 1017de0c960cSJagpal Singh Gill "/redfish/v1/UpdateService"); 10186b54e4e0SEd Tanous 1019ef93eab3SJagpal Singh Gill uploadImageFile(asyncResp->res, multipart->uploadData); 10200ed80c8cSGeorge Liu } 1021de0c960cSJagpal Singh Gill } 10220ed80c8cSGeorge Liu 10239dae4deeSJagpal Singh Gill inline void doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 10249dae4deeSJagpal Singh Gill const crow::Request& req) 10259dae4deeSJagpal Singh Gill { 10269dae4deeSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 10279dae4deeSJagpal Singh Gill { 10289dae4deeSJagpal Singh Gill task::Payload payload(req); 10299dae4deeSJagpal Singh Gill // HTTP push only supports BMC updates (with ApplyTime as immediate) for 10309dae4deeSJagpal Singh Gill // backwards compatibility. Specific component updates will be handled 10319dae4deeSJagpal Singh Gill // through Multipart form HTTP push. 10329dae4deeSJagpal Singh Gill std::vector<std::string> targets; 10339dae4deeSJagpal Singh Gill targets.emplace_back(BMCWEB_REDFISH_MANAGER_URI_NAME); 10349dae4deeSJagpal Singh Gill 10359dae4deeSJagpal Singh Gill processUpdateRequest( 10369dae4deeSJagpal Singh Gill asyncResp, std::move(payload), req.body(), 10379dae4deeSJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate", 10389dae4deeSJagpal Singh Gill targets); 10399dae4deeSJagpal Singh Gill } 10409dae4deeSJagpal Singh Gill else 10419dae4deeSJagpal Singh Gill { 10429dae4deeSJagpal Singh Gill // Setup callback for when new software detected 10439dae4deeSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 10449dae4deeSJagpal Singh Gill "/redfish/v1/UpdateService"); 10459dae4deeSJagpal Singh Gill 10469dae4deeSJagpal Singh Gill uploadImageFile(asyncResp->res, req.body()); 10479dae4deeSJagpal Singh Gill } 10489dae4deeSJagpal Singh Gill } 10499dae4deeSJagpal Singh Gill 1050c2051d11SEd Tanous inline void 1051c2051d11SEd Tanous handleUpdateServicePost(App& app, const crow::Request& req, 1052c2051d11SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1053c2051d11SEd Tanous { 10543ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1055c2051d11SEd Tanous { 1056c2051d11SEd Tanous return; 1057c2051d11SEd Tanous } 1058b33a4327SNinad Palsule std::string_view contentType = req.getHeaderValue("Content-Type"); 1059b33a4327SNinad Palsule 106062598e31SEd Tanous BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType); 1061b33a4327SNinad Palsule 1062b33a4327SNinad Palsule // Make sure that content type is application/octet-stream or 1063b33a4327SNinad Palsule // multipart/form-data 106418f8f608SEd Tanous if (bmcweb::asciiIEquals(contentType, "application/octet-stream")) 1065b33a4327SNinad Palsule { 10669dae4deeSJagpal Singh Gill doHTTPUpdate(asyncResp, req); 1067b33a4327SNinad Palsule } 1068b33a4327SNinad Palsule else if (contentType.starts_with("multipart/form-data")) 1069b33a4327SNinad Palsule { 1070b33a4327SNinad Palsule MultipartParser parser; 1071c2051d11SEd Tanous 10720ed80c8cSGeorge Liu ParserError ec = parser.parse(req); 10730ed80c8cSGeorge Liu if (ec != ParserError::PARSER_SUCCESS) 10740ed80c8cSGeorge Liu { 10750ed80c8cSGeorge Liu // handle error 107662598e31SEd Tanous BMCWEB_LOG_ERROR("MIME parse failed, ec : {}", 107762598e31SEd Tanous static_cast<int>(ec)); 10780ed80c8cSGeorge Liu messages::internalError(asyncResp->res); 10790ed80c8cSGeorge Liu return; 10800ed80c8cSGeorge Liu } 10816b54e4e0SEd Tanous 1082ef93eab3SJagpal Singh Gill updateMultipartContext(asyncResp, req, std::move(parser)); 1083c2051d11SEd Tanous } 1084b33a4327SNinad Palsule else 1085b33a4327SNinad Palsule { 108662598e31SEd Tanous BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType); 1087b33a4327SNinad Palsule asyncResp->res.result(boost::beast::http::status::bad_request); 1088b33a4327SNinad Palsule } 1089b33a4327SNinad Palsule } 1090c2051d11SEd Tanous 1091f5139334SEd Tanous inline void 1092f5139334SEd Tanous handleUpdateServiceGet(App& app, const crow::Request& req, 1093f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 10941abe55efSEd Tanous { 10953ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 109645ca1b86SEd Tanous { 109745ca1b86SEd Tanous return; 109845ca1b86SEd Tanous } 10998d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 11000ed80c8cSGeorge Liu "#UpdateService.v1_11_1.UpdateService"; 11018d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService"; 11028d1b46d7Szhanghch05 asyncResp->res.jsonValue["Id"] = "UpdateService"; 1103002d39b4SEd Tanous asyncResp->res.jsonValue["Description"] = "Service for Software Update"; 11048d1b46d7Szhanghch05 asyncResp->res.jsonValue["Name"] = "Update Service"; 11054dc23f3fSEd Tanous 11067e860f15SJohn Edward Broadbent asyncResp->res.jsonValue["HttpPushUri"] = 11074dc23f3fSEd Tanous "/redfish/v1/UpdateService/update"; 11080ed80c8cSGeorge Liu asyncResp->res.jsonValue["MultipartHttpPushUri"] = 11090ed80c8cSGeorge Liu "/redfish/v1/UpdateService/update"; 11104dc23f3fSEd Tanous 11110f74e643SEd Tanous // UpdateService cannot be disabled 11128d1b46d7Szhanghch05 asyncResp->res.jsonValue["ServiceEnabled"] = true; 11131476687dSEd Tanous asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] = 11141476687dSEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1115d61e5194STejas Patil // Get the MaxImageSizeBytes 1116bd79bce8SPatrick Williams asyncResp->res.jsonValue["MaxImageSizeBytes"] = 1117bd79bce8SPatrick Williams BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024; 1118d61e5194STejas Patil 11196a37140aSEd Tanous if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE) 11206a37140aSEd Tanous { 11210554c984SAndrew Geissler // Update Actions object. 11220554c984SAndrew Geissler nlohmann::json& updateSvcSimpleUpdate = 1123002d39b4SEd Tanous asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"]; 11240554c984SAndrew Geissler updateSvcSimpleUpdate["target"] = 11250554c984SAndrew Geissler "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate"; 1126757178a5SEd Tanous 1127757178a5SEd Tanous nlohmann::json::array_t allowed; 1128e5cf777eSEd Tanous allowed.emplace_back(update_service::TransferProtocolType::HTTPS); 1129757178a5SEd Tanous updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = 1130757178a5SEd Tanous std::move(allowed); 11316a37140aSEd Tanous } 1132757178a5SEd Tanous 1133539d8c6bSEd Tanous asyncResp->res 1134539d8c6bSEd Tanous .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] = 1135539d8c6bSEd Tanous update_service::ApplyTime::Immediate; 1136729dae72SJennifer Lee } 1137729dae72SJennifer Lee 1138f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryCollectionGet( 1139f5139334SEd Tanous App& app, const crow::Request& req, 1140f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 11411abe55efSEd Tanous { 11423ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 114345ca1b86SEd Tanous { 114445ca1b86SEd Tanous return; 114545ca1b86SEd Tanous } 11468d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 11470f74e643SEd Tanous "#SoftwareInventoryCollection.SoftwareInventoryCollection"; 11488d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = 11490f74e643SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1150002d39b4SEd Tanous asyncResp->res.jsonValue["Name"] = "Software Inventory Collection"; 115108d81adaSJohn Edward Broadbent const std::array<const std::string_view, 1> iface = { 1152e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 11536c4eb9deSJennifer Lee 115408d81adaSJohn Edward Broadbent redfish::collection_util::getCollectionMembers( 115508d81adaSJohn Edward Broadbent asyncResp, 1156f5139334SEd Tanous boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface, 1157f5139334SEd Tanous "/xyz/openbmc_project/software"); 1158729dae72SJennifer Lee } 1159f5139334SEd Tanous 116087d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */ 1161f5139334SEd Tanous inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 116287d84729SAndrew Geissler const std::string& purpose) 116387d84729SAndrew Geissler { 1164eee0013eSWilly Tu if (purpose == sw_util::bmcPurpose) 116587d84729SAndrew Geissler { 1166ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"]; 11671476687dSEd Tanous nlohmann::json::object_t item; 1168253f11b8SEd Tanous item["@odata.id"] = boost::urls::format( 1169253f11b8SEd Tanous "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); 1170b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item)); 1171ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] = 1172ac106bf6SEd Tanous relatedItem.size(); 117387d84729SAndrew Geissler } 1174eee0013eSWilly Tu else if (purpose == sw_util::biosPurpose) 117587d84729SAndrew Geissler { 1176ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"]; 11771476687dSEd Tanous nlohmann::json::object_t item; 1178253f11b8SEd Tanous item["@odata.id"] = std::format("/redfish/v1/Systems/{}/Bios", 1179253f11b8SEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 1180b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item)); 1181ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] = 1182ac106bf6SEd Tanous relatedItem.size(); 118387d84729SAndrew Geissler } 118487d84729SAndrew Geissler else 118587d84729SAndrew Geissler { 1186bf2ddedeSCarson Labrado BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose); 118787d84729SAndrew Geissler } 118887d84729SAndrew Geissler } 118987d84729SAndrew Geissler 1190af24660dSWilly Tu inline void 1191af24660dSWilly Tu getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1192af24660dSWilly Tu const std::string& service, const std::string& path, 1193af24660dSWilly Tu const std::string& swId) 1194af24660dSWilly Tu { 1195deae6a78SEd Tanous dbus::utility::getAllProperties( 1196deae6a78SEd Tanous service, path, "xyz.openbmc_project.Software.Version", 1197af24660dSWilly Tu [asyncResp, 11988b24275dSEd Tanous swId](const boost::system::error_code& ec, 1199af24660dSWilly Tu const dbus::utility::DBusPropertiesMap& propertiesList) { 12008b24275dSEd Tanous if (ec) 1201af24660dSWilly Tu { 1202af24660dSWilly Tu messages::internalError(asyncResp->res); 1203af24660dSWilly Tu return; 1204af24660dSWilly Tu } 1205d1bde9e5SKrzysztof Grobelny 1206af24660dSWilly Tu const std::string* swInvPurpose = nullptr; 1207af24660dSWilly Tu const std::string* version = nullptr; 1208d1bde9e5SKrzysztof Grobelny 1209d1bde9e5SKrzysztof Grobelny const bool success = sdbusplus::unpackPropertiesNoThrow( 1210d1bde9e5SKrzysztof Grobelny dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose", 1211d1bde9e5SKrzysztof Grobelny swInvPurpose, "Version", version); 1212d1bde9e5SKrzysztof Grobelny 1213d1bde9e5SKrzysztof Grobelny if (!success) 1214af24660dSWilly Tu { 1215d1bde9e5SKrzysztof Grobelny messages::internalError(asyncResp->res); 1216d1bde9e5SKrzysztof Grobelny return; 1217af24660dSWilly Tu } 1218af24660dSWilly Tu 1219af24660dSWilly Tu if (swInvPurpose == nullptr) 1220af24660dSWilly Tu { 122162598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!"); 1222af24660dSWilly Tu messages::internalError(asyncResp->res); 1223af24660dSWilly Tu return; 1224af24660dSWilly Tu } 1225af24660dSWilly Tu 122662598e31SEd Tanous BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose); 1227af24660dSWilly Tu 1228af24660dSWilly Tu if (version == nullptr) 1229af24660dSWilly Tu { 123062598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Version\"!"); 1231af24660dSWilly Tu 1232af24660dSWilly Tu messages::internalError(asyncResp->res); 1233af24660dSWilly Tu 1234af24660dSWilly Tu return; 1235af24660dSWilly Tu } 1236af24660dSWilly Tu asyncResp->res.jsonValue["Version"] = *version; 1237af24660dSWilly Tu asyncResp->res.jsonValue["Id"] = swId; 1238af24660dSWilly Tu 1239af24660dSWilly Tu // swInvPurpose is of format: 1240af24660dSWilly Tu // xyz.openbmc_project.Software.Version.VersionPurpose.ABC 1241af24660dSWilly Tu // Translate this to "ABC image" 1242af24660dSWilly Tu size_t endDesc = swInvPurpose->rfind('.'); 1243af24660dSWilly Tu if (endDesc == std::string::npos) 1244af24660dSWilly Tu { 1245af24660dSWilly Tu messages::internalError(asyncResp->res); 1246af24660dSWilly Tu return; 1247af24660dSWilly Tu } 1248af24660dSWilly Tu endDesc++; 1249af24660dSWilly Tu if (endDesc >= swInvPurpose->size()) 1250af24660dSWilly Tu { 1251af24660dSWilly Tu messages::internalError(asyncResp->res); 1252af24660dSWilly Tu return; 1253af24660dSWilly Tu } 1254af24660dSWilly Tu 1255af24660dSWilly Tu std::string formatDesc = swInvPurpose->substr(endDesc); 1256af24660dSWilly Tu asyncResp->res.jsonValue["Description"] = formatDesc + " image"; 1257af24660dSWilly Tu getRelatedItems(asyncResp, *swInvPurpose); 1258d1bde9e5SKrzysztof Grobelny }); 1259af24660dSWilly Tu } 1260af24660dSWilly Tu 1261f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryGet( 1262f5139334SEd Tanous App& app, const crow::Request& req, 126345ca1b86SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1264f5139334SEd Tanous const std::string& param) 1265f5139334SEd Tanous { 12663ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 126745ca1b86SEd Tanous { 126845ca1b86SEd Tanous return; 126945ca1b86SEd Tanous } 1270f5139334SEd Tanous std::shared_ptr<std::string> swId = std::make_shared<std::string>(param); 1271c711bf86SEd Tanous 1272ef4c65b7SEd Tanous asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1273ef4c65b7SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId); 1274c711bf86SEd Tanous 1275e99073f5SGeorge Liu constexpr std::array<std::string_view, 1> interfaces = { 1276e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 1277e99073f5SGeorge Liu dbus::utility::getSubTree( 1278e99073f5SGeorge Liu "/", 0, interfaces, 1279b9d36b47SEd Tanous [asyncResp, 1280e99073f5SGeorge Liu swId](const boost::system::error_code& ec, 1281b9d36b47SEd Tanous const dbus::utility::MapperGetSubTreeResponse& subtree) { 128262598e31SEd Tanous BMCWEB_LOG_DEBUG("doGet callback..."); 12831abe55efSEd Tanous if (ec) 12841abe55efSEd Tanous { 1285f12894f8SJason M. Bills messages::internalError(asyncResp->res); 12866c4eb9deSJennifer Lee return; 12876c4eb9deSJennifer Lee } 12886c4eb9deSJennifer Lee 12896913228dSAndrew Geissler // Ensure we find our input swId, otherwise return an error 12906913228dSAndrew Geissler bool found = false; 1291bd79bce8SPatrick Williams for (const std::pair<std::string, 1292bd79bce8SPatrick Williams std::vector<std::pair< 1293bd79bce8SPatrick Williams std::string, std::vector<std::string>>>>& 1294002d39b4SEd Tanous obj : subtree) 12951abe55efSEd Tanous { 129611ba3979SEd Tanous if (!obj.first.ends_with(*swId)) 12971abe55efSEd Tanous { 1298acb7cfb4SJennifer Lee continue; 1299acb7cfb4SJennifer Lee } 1300acb7cfb4SJennifer Lee 130126f6976fSEd Tanous if (obj.second.empty()) 13021abe55efSEd Tanous { 1303acb7cfb4SJennifer Lee continue; 1304acb7cfb4SJennifer Lee } 13056c4eb9deSJennifer Lee 13066913228dSAndrew Geissler found = true; 1307eee0013eSWilly Tu sw_util::getSwStatus(asyncResp, swId, obj.second[0].first); 1308af24660dSWilly Tu getSoftwareVersion(asyncResp, obj.second[0].first, obj.first, 1309af24660dSWilly Tu *swId); 13106c4eb9deSJennifer Lee } 13116913228dSAndrew Geissler if (!found) 13126913228dSAndrew Geissler { 131362598e31SEd Tanous BMCWEB_LOG_WARNING("Input swID {} not found!", *swId); 13146913228dSAndrew Geissler messages::resourceMissingAtURI( 1315ef4c65b7SEd Tanous asyncResp->res, 1316ef4c65b7SEd Tanous boost::urls::format( 1317bd79bce8SPatrick Williams "/redfish/v1/UpdateService/FirmwareInventory/{}", 1318bd79bce8SPatrick Williams *swId)); 13196913228dSAndrew Geissler return; 13206913228dSAndrew Geissler } 13214e68c45bSAyushi Smriti asyncResp->res.jsonValue["@odata.type"] = 13224e68c45bSAyushi Smriti "#SoftwareInventory.v1_1_0.SoftwareInventory"; 13234e68c45bSAyushi Smriti asyncResp->res.jsonValue["Name"] = "Software Inventory"; 1324539d8c6bSEd Tanous asyncResp->res.jsonValue["Status"]["HealthRollup"] = 1325539d8c6bSEd Tanous resource::Health::OK; 13263f8a743aSAppaRao Puli 13273f8a743aSAppaRao Puli asyncResp->res.jsonValue["Updateable"] = false; 1328eee0013eSWilly Tu sw_util::getSwUpdatableStatus(asyncResp, swId); 1329e99073f5SGeorge Liu }); 1330f5139334SEd Tanous } 1331f5139334SEd Tanous 1332f5139334SEd Tanous inline void requestRoutesUpdateService(App& app) 1333f5139334SEd Tanous { 13346a37140aSEd Tanous if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE) 13356a37140aSEd Tanous { 1336f5139334SEd Tanous BMCWEB_ROUTE( 13376a37140aSEd Tanous app, 13386a37140aSEd Tanous "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/") 1339f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1340f5139334SEd Tanous .methods(boost::beast::http::verb::post)(std::bind_front( 1341f5139334SEd Tanous handleUpdateServiceSimpleUpdateAction, std::ref(app))); 13426a37140aSEd Tanous } 1343f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/") 1344f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventory) 1345f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1346f5139334SEd Tanous handleUpdateServiceFirmwareInventoryGet, std::ref(app))); 1347f5139334SEd Tanous 1348f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/") 1349f5139334SEd Tanous .privileges(redfish::privileges::getUpdateService) 1350f5139334SEd Tanous .methods(boost::beast::http::verb::get)( 1351f5139334SEd Tanous std::bind_front(handleUpdateServiceGet, std::ref(app))); 1352f5139334SEd Tanous 1353f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/") 1354f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1355f5139334SEd Tanous .methods(boost::beast::http::verb::post)( 1356f5139334SEd Tanous std::bind_front(handleUpdateServicePost, std::ref(app))); 1357f5139334SEd Tanous 1358f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/") 1359f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventoryCollection) 1360f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1361f5139334SEd Tanous handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app))); 13626c4eb9deSJennifer Lee } 1363729dae72SJennifer Lee 1364729dae72SJennifer Lee } // namespace redfish 1365