1729dae72SJennifer Lee /* 26be832e2SEd Tanous Copyright (c) 2018 Intel Corporation 36be832e2SEd Tanous 46be832e2SEd Tanous Licensed under the Apache License, Version 2.0 (the "License"); 56be832e2SEd Tanous you may not use this file except in compliance with the License. 66be832e2SEd Tanous You may obtain a copy of the License at 76be832e2SEd Tanous 86be832e2SEd Tanous http://www.apache.org/licenses/LICENSE-2.0 96be832e2SEd Tanous 106be832e2SEd Tanous Unless required by applicable law or agreed to in writing, software 116be832e2SEd Tanous distributed under the License is distributed on an "AS IS" BASIS, 126be832e2SEd Tanous WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136be832e2SEd Tanous See the License for the specific language governing permissions and 146be832e2SEd Tanous limitations under the License. 15729dae72SJennifer Lee */ 16729dae72SJennifer Lee #pragma once 17729dae72SJennifer Lee 18d61e5194STejas Patil #include "bmcweb_config.h" 19d61e5194STejas Patil 203ccb3adbSEd Tanous #include "app.hpp" 213ccb3adbSEd Tanous #include "dbus_utility.hpp" 225b90429aSEd Tanous #include "error_messages.hpp" 23757178a5SEd Tanous #include "generated/enums/update_service.hpp" 240ed80c8cSGeorge Liu #include "multipart_parser.hpp" 252c6ffdb0SEd Tanous #include "ossl_random.hpp" 263ccb3adbSEd Tanous #include "query.hpp" 273ccb3adbSEd Tanous #include "registries/privilege_registry.hpp" 28a8e884fcSEd Tanous #include "task.hpp" 295b90429aSEd Tanous #include "task_messages.hpp" 3008d81adaSJohn Edward Broadbent #include "utils/collection.hpp" 313ccb3adbSEd Tanous #include "utils/dbus_utils.hpp" 325b90429aSEd Tanous #include "utils/json_utils.hpp" 333ccb3adbSEd Tanous #include "utils/sw_utils.hpp" 343ccb3adbSEd Tanous 35de0c960cSJagpal Singh Gill #include <sys/mman.h> 36de0c960cSJagpal Singh Gill 37e99073f5SGeorge Liu #include <boost/system/error_code.hpp> 38ef4c65b7SEd Tanous #include <boost/url/format.hpp> 391e1e598dSJonathan Doman #include <sdbusplus/asio/property.hpp> 403ccb3adbSEd Tanous #include <sdbusplus/bus/match.hpp> 41d1bde9e5SKrzysztof Grobelny #include <sdbusplus/unpack_properties.hpp> 421214b7e7SGunnar Mills 432b73119cSGeorge Liu #include <array> 44de0c960cSJagpal Singh Gill #include <cstddef> 450ed80c8cSGeorge Liu #include <filesystem> 46c71b6c99SJagpal Singh Gill #include <functional> 47de0c960cSJagpal Singh Gill #include <iterator> 48ef93eab3SJagpal Singh Gill #include <memory> 497cb59f65SEd Tanous #include <optional> 507cb59f65SEd Tanous #include <string> 512b73119cSGeorge Liu #include <string_view> 52de0c960cSJagpal Singh Gill #include <unordered_map> 53ef93eab3SJagpal Singh Gill #include <vector> 542b73119cSGeorge Liu 551abe55efSEd Tanous namespace redfish 561abe55efSEd Tanous { 5727826b5fSEd Tanous 580e7de46fSAndrew Geissler // Match signals added on software path 59cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 6059d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher; 61cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 6259d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher; 630e7de46fSAndrew Geissler // Only allow one update at a time 64cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 650e7de46fSAndrew Geissler static bool fwUpdateInProgress = false; 6686adcd6dSAndrew Geissler // Timer for software available 67cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 68271584abSEd Tanous static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer; 6986adcd6dSAndrew Geissler 70de0c960cSJagpal Singh Gill struct MemoryFileDescriptor 71de0c960cSJagpal Singh Gill { 72de0c960cSJagpal Singh Gill int fd = -1; 73de0c960cSJagpal Singh Gill 74de0c960cSJagpal Singh Gill explicit MemoryFileDescriptor(const std::string& filename) : 75de0c960cSJagpal Singh Gill fd(memfd_create(filename.c_str(), 0)) 76de0c960cSJagpal Singh Gill {} 77de0c960cSJagpal Singh Gill 78de0c960cSJagpal Singh Gill MemoryFileDescriptor(const MemoryFileDescriptor&) = default; 79de0c960cSJagpal Singh Gill MemoryFileDescriptor(MemoryFileDescriptor&& other) noexcept : fd(other.fd) 80de0c960cSJagpal Singh Gill { 81de0c960cSJagpal Singh Gill other.fd = -1; 82de0c960cSJagpal Singh Gill } 83de0c960cSJagpal Singh Gill MemoryFileDescriptor& operator=(const MemoryFileDescriptor&) = delete; 84de0c960cSJagpal Singh Gill MemoryFileDescriptor& operator=(MemoryFileDescriptor&&) = default; 85de0c960cSJagpal Singh Gill 86de0c960cSJagpal Singh Gill ~MemoryFileDescriptor() 87de0c960cSJagpal Singh Gill { 88de0c960cSJagpal Singh Gill if (fd != -1) 89de0c960cSJagpal Singh Gill { 90de0c960cSJagpal Singh Gill close(fd); 91de0c960cSJagpal Singh Gill } 92de0c960cSJagpal Singh Gill } 93de0c960cSJagpal Singh Gill 94de0c960cSJagpal Singh Gill bool rewind() const 95de0c960cSJagpal Singh Gill { 96de0c960cSJagpal Singh Gill if (lseek(fd, 0, SEEK_SET) == -1) 97de0c960cSJagpal Singh Gill { 98de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to seek to beginning of image memfd"); 99de0c960cSJagpal Singh Gill return false; 100de0c960cSJagpal Singh Gill } 101de0c960cSJagpal Singh Gill return true; 102de0c960cSJagpal Singh Gill } 103de0c960cSJagpal Singh Gill }; 104de0c960cSJagpal Singh Gill 105df254f2cSEd Tanous inline void cleanUp() 10686adcd6dSAndrew Geissler { 10786adcd6dSAndrew Geissler fwUpdateInProgress = false; 10886adcd6dSAndrew Geissler fwUpdateMatcher = nullptr; 1094cde5d90SJames Feist fwUpdateErrorMatcher = nullptr; 11086adcd6dSAndrew Geissler } 111df254f2cSEd Tanous 112df254f2cSEd Tanous inline void activateImage(const std::string& objPath, 11386adcd6dSAndrew Geissler const std::string& service) 11486adcd6dSAndrew Geissler { 11562598e31SEd Tanous BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service); 1169ae226faSGeorge Liu sdbusplus::asio::setProperty( 1179ae226faSGeorge Liu *crow::connections::systemBus, service, objPath, 1189ae226faSGeorge Liu "xyz.openbmc_project.Software.Activation", "RequestedActivation", 1199ae226faSGeorge Liu "xyz.openbmc_project.Software.Activation.RequestedActivations.Active", 1208b24275dSEd Tanous [](const boost::system::error_code& ec) { 1218b24275dSEd Tanous if (ec) 12286adcd6dSAndrew Geissler { 12362598e31SEd Tanous BMCWEB_LOG_DEBUG("error_code = {}", ec); 12462598e31SEd Tanous BMCWEB_LOG_DEBUG("error msg = {}", ec.message()); 12586adcd6dSAndrew Geissler } 1269ae226faSGeorge Liu }); 12786adcd6dSAndrew Geissler } 1280554c984SAndrew Geissler 129c71b6c99SJagpal Singh Gill inline bool handleCreateTask(const boost::system::error_code& ec2, 130c71b6c99SJagpal Singh Gill sdbusplus::message_t& msg, 131c71b6c99SJagpal Singh Gill const std::shared_ptr<task::TaskData>& taskData) 132c71b6c99SJagpal Singh Gill { 133c71b6c99SJagpal Singh Gill if (ec2) 134c71b6c99SJagpal Singh Gill { 135c71b6c99SJagpal Singh Gill return task::completed; 136c71b6c99SJagpal Singh Gill } 137c71b6c99SJagpal Singh Gill 138c71b6c99SJagpal Singh Gill std::string iface; 139c71b6c99SJagpal Singh Gill dbus::utility::DBusPropertiesMap values; 140c71b6c99SJagpal Singh Gill 141c71b6c99SJagpal Singh Gill std::string index = std::to_string(taskData->index); 142c71b6c99SJagpal Singh Gill msg.read(iface, values); 143c71b6c99SJagpal Singh Gill 144c71b6c99SJagpal Singh Gill if (iface == "xyz.openbmc_project.Software.Activation") 145c71b6c99SJagpal Singh Gill { 146c71b6c99SJagpal Singh Gill const std::string* state = nullptr; 147c71b6c99SJagpal Singh Gill for (const auto& property : values) 148c71b6c99SJagpal Singh Gill { 149c71b6c99SJagpal Singh Gill if (property.first == "Activation") 150c71b6c99SJagpal Singh Gill { 151c71b6c99SJagpal Singh Gill state = std::get_if<std::string>(&property.second); 152c71b6c99SJagpal Singh Gill if (state == nullptr) 153c71b6c99SJagpal Singh Gill { 154c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::internalError()); 155c71b6c99SJagpal Singh Gill return task::completed; 156c71b6c99SJagpal Singh Gill } 157c71b6c99SJagpal Singh Gill } 158c71b6c99SJagpal Singh Gill } 159c71b6c99SJagpal Singh Gill 160c71b6c99SJagpal Singh Gill if (state == nullptr) 161c71b6c99SJagpal Singh Gill { 162c71b6c99SJagpal Singh Gill return !task::completed; 163c71b6c99SJagpal Singh Gill } 164c71b6c99SJagpal Singh Gill 165c71b6c99SJagpal Singh Gill if (state->ends_with("Invalid") || state->ends_with("Failed")) 166c71b6c99SJagpal Singh Gill { 167c71b6c99SJagpal Singh Gill taskData->state = "Exception"; 168c71b6c99SJagpal Singh Gill taskData->status = "Warning"; 169c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskAborted(index)); 170c71b6c99SJagpal Singh Gill return task::completed; 171c71b6c99SJagpal Singh Gill } 172c71b6c99SJagpal Singh Gill 173c71b6c99SJagpal Singh Gill if (state->ends_with("Staged")) 174c71b6c99SJagpal Singh Gill { 175c71b6c99SJagpal Singh Gill taskData->state = "Stopping"; 176c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskPaused(index)); 177c71b6c99SJagpal Singh Gill 178c71b6c99SJagpal Singh Gill // its staged, set a long timer to 179c71b6c99SJagpal Singh Gill // allow them time to complete the 180c71b6c99SJagpal Singh Gill // update (probably cycle the 181c71b6c99SJagpal Singh Gill // system) if this expires then 182c71b6c99SJagpal Singh Gill // task will be canceled 183c71b6c99SJagpal Singh Gill taskData->extendTimer(std::chrono::hours(5)); 184c71b6c99SJagpal Singh Gill return !task::completed; 185c71b6c99SJagpal Singh Gill } 186c71b6c99SJagpal Singh Gill 187c71b6c99SJagpal Singh Gill if (state->ends_with("Active")) 188c71b6c99SJagpal Singh Gill { 189c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskCompletedOK(index)); 190c71b6c99SJagpal Singh Gill taskData->state = "Completed"; 191c71b6c99SJagpal Singh Gill return task::completed; 192c71b6c99SJagpal Singh Gill } 193c71b6c99SJagpal Singh Gill } 194c71b6c99SJagpal Singh Gill else if (iface == "xyz.openbmc_project.Software.ActivationProgress") 195c71b6c99SJagpal Singh Gill { 196c71b6c99SJagpal Singh Gill const uint8_t* progress = nullptr; 197c71b6c99SJagpal Singh Gill for (const auto& property : values) 198c71b6c99SJagpal Singh Gill { 199c71b6c99SJagpal Singh Gill if (property.first == "Progress") 200c71b6c99SJagpal Singh Gill { 201c71b6c99SJagpal Singh Gill progress = std::get_if<uint8_t>(&property.second); 202c71b6c99SJagpal Singh Gill if (progress == nullptr) 203c71b6c99SJagpal Singh Gill { 204c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::internalError()); 205c71b6c99SJagpal Singh Gill return task::completed; 206c71b6c99SJagpal Singh Gill } 207c71b6c99SJagpal Singh Gill } 208c71b6c99SJagpal Singh Gill } 209c71b6c99SJagpal Singh Gill 210c71b6c99SJagpal Singh Gill if (progress == nullptr) 211c71b6c99SJagpal Singh Gill { 212c71b6c99SJagpal Singh Gill return !task::completed; 213c71b6c99SJagpal Singh Gill } 214c71b6c99SJagpal Singh Gill taskData->percentComplete = *progress; 215c71b6c99SJagpal Singh Gill taskData->messages.emplace_back( 216c71b6c99SJagpal Singh Gill messages::taskProgressChanged(index, *progress)); 217c71b6c99SJagpal Singh Gill 218c71b6c99SJagpal Singh Gill // if we're getting status updates it's 219c71b6c99SJagpal Singh Gill // still alive, update timer 220c71b6c99SJagpal Singh Gill taskData->extendTimer(std::chrono::minutes(5)); 221c71b6c99SJagpal Singh Gill } 222c71b6c99SJagpal Singh Gill 223c71b6c99SJagpal Singh Gill // as firmware update often results in a 224c71b6c99SJagpal Singh Gill // reboot, the task may never "complete" 225c71b6c99SJagpal Singh Gill // unless it is an error 226c71b6c99SJagpal Singh Gill 227c71b6c99SJagpal Singh Gill return !task::completed; 228c71b6c99SJagpal Singh Gill } 229c71b6c99SJagpal Singh Gill 230c71b6c99SJagpal Singh Gill inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 231c71b6c99SJagpal Singh Gill task::Payload&& payload, 232c71b6c99SJagpal Singh Gill const sdbusplus::message::object_path& objPath) 233c71b6c99SJagpal Singh Gill { 234c71b6c99SJagpal Singh Gill std::shared_ptr<task::TaskData> task = task::TaskData::createTask( 235c71b6c99SJagpal Singh Gill std::bind_front(handleCreateTask), 236c71b6c99SJagpal Singh Gill "type='signal',interface='org.freedesktop.DBus.Properties'," 237c71b6c99SJagpal Singh Gill "member='PropertiesChanged',path='" + 238c71b6c99SJagpal Singh Gill objPath.str + "'"); 239c71b6c99SJagpal Singh Gill task->startTimer(std::chrono::minutes(5)); 240c71b6c99SJagpal Singh Gill task->populateResp(asyncResp->res); 241c71b6c99SJagpal Singh Gill task->payload.emplace(std::move(payload)); 242c71b6c99SJagpal Singh Gill } 243c71b6c99SJagpal Singh Gill 2440554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr 2450554c984SAndrew Geissler // then no asyncResp updates will occur 2464ff0f1f4SEd Tanous inline void 2478d1b46d7Szhanghch05 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 24859d494eeSPatrick Williams sdbusplus::message_t& m, task::Payload&& payload) 24986adcd6dSAndrew Geissler { 25080f79a40SMichael Shen dbus::utility::DBusInterfacesMap interfacesProperties; 25186adcd6dSAndrew Geissler 25286adcd6dSAndrew Geissler sdbusplus::message::object_path objPath; 25386adcd6dSAndrew Geissler 25486adcd6dSAndrew Geissler m.read(objPath, interfacesProperties); 25586adcd6dSAndrew Geissler 25662598e31SEd Tanous BMCWEB_LOG_DEBUG("obj path = {}", objPath.str); 257e3eb3d63SEd Tanous for (const auto& interface : interfacesProperties) 25886adcd6dSAndrew Geissler { 25962598e31SEd Tanous BMCWEB_LOG_DEBUG("interface = {}", interface.first); 26086adcd6dSAndrew Geissler 26186adcd6dSAndrew Geissler if (interface.first == "xyz.openbmc_project.Software.Activation") 26286adcd6dSAndrew Geissler { 26386adcd6dSAndrew Geissler // Retrieve service and activate 2642b73119cSGeorge Liu constexpr std::array<std::string_view, 1> interfaces = { 2652b73119cSGeorge Liu "xyz.openbmc_project.Software.Activation"}; 2662b73119cSGeorge Liu dbus::utility::getDbusObject( 2672b73119cSGeorge Liu objPath.str, interfaces, 268a3e65892SEd Tanous [objPath, asyncResp, payload(std::move(payload))]( 2698b24275dSEd Tanous const boost::system::error_code& ec, 270a3e65892SEd Tanous const std::vector< 271a3e65892SEd Tanous std::pair<std::string, std::vector<std::string>>>& 272a3e65892SEd Tanous objInfo) mutable { 2738b24275dSEd Tanous if (ec) 27486adcd6dSAndrew Geissler { 27562598e31SEd Tanous BMCWEB_LOG_DEBUG("error_code = {}", ec); 27662598e31SEd Tanous BMCWEB_LOG_DEBUG("error msg = {}", ec.message()); 2770554c984SAndrew Geissler if (asyncResp) 2780554c984SAndrew Geissler { 27986adcd6dSAndrew Geissler messages::internalError(asyncResp->res); 2800554c984SAndrew Geissler } 28186adcd6dSAndrew Geissler cleanUp(); 28286adcd6dSAndrew Geissler return; 28386adcd6dSAndrew Geissler } 28486adcd6dSAndrew Geissler // Ensure we only got one service back 28586adcd6dSAndrew Geissler if (objInfo.size() != 1) 28686adcd6dSAndrew Geissler { 287bd79bce8SPatrick Williams BMCWEB_LOG_ERROR("Invalid Object Size {}", 288bd79bce8SPatrick Williams objInfo.size()); 2890554c984SAndrew Geissler if (asyncResp) 2900554c984SAndrew Geissler { 29186adcd6dSAndrew Geissler messages::internalError(asyncResp->res); 2920554c984SAndrew Geissler } 29386adcd6dSAndrew Geissler cleanUp(); 29486adcd6dSAndrew Geissler return; 29586adcd6dSAndrew Geissler } 29686adcd6dSAndrew Geissler // cancel timer only when 29786adcd6dSAndrew Geissler // xyz.openbmc_project.Software.Activation interface 29886adcd6dSAndrew Geissler // is added 29986adcd6dSAndrew Geissler fwAvailableTimer = nullptr; 30086adcd6dSAndrew Geissler 30186adcd6dSAndrew Geissler activateImage(objPath.str, objInfo[0].first); 3020554c984SAndrew Geissler if (asyncResp) 3030554c984SAndrew Geissler { 304c71b6c99SJagpal Singh Gill createTask(asyncResp, std::move(payload), objPath); 3050554c984SAndrew Geissler } 30686adcd6dSAndrew Geissler fwUpdateInProgress = false; 3072b73119cSGeorge Liu }); 30862bafc01SPatrick Williams 30962bafc01SPatrick Williams break; 31086adcd6dSAndrew Geissler } 31186adcd6dSAndrew Geissler } 31286adcd6dSAndrew Geissler } 31386adcd6dSAndrew Geissler 3148549b951SMyung Bae inline void afterAvailbleTimerAsyncWait( 3158549b951SMyung Bae const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3168549b951SMyung Bae const boost::system::error_code& ec) 3178549b951SMyung Bae { 3188549b951SMyung Bae cleanUp(); 3198549b951SMyung Bae if (ec == boost::asio::error::operation_aborted) 3208549b951SMyung Bae { 3218549b951SMyung Bae // expected, we were canceled before the timer completed. 3228549b951SMyung Bae return; 3238549b951SMyung Bae } 3248549b951SMyung Bae BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created"); 3258549b951SMyung Bae BMCWEB_LOG_ERROR("FW image may has already been uploaded to server"); 3268549b951SMyung Bae if (ec) 3278549b951SMyung Bae { 3288549b951SMyung Bae BMCWEB_LOG_ERROR("Async_wait failed{}", ec); 3298549b951SMyung Bae return; 3308549b951SMyung Bae } 3318549b951SMyung Bae if (asyncResp) 3328549b951SMyung Bae { 3338549b951SMyung Bae redfish::messages::internalError(asyncResp->res); 3348549b951SMyung Bae } 3358549b951SMyung Bae } 3368549b951SMyung Bae 3378549b951SMyung Bae inline void 3388549b951SMyung Bae handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 339*48fb20b9SEd Tanous const std::string& url, const std::string& type) 3408549b951SMyung Bae { 341c87294a6SEd Tanous // NOLINTBEGIN(bugprone-branch-clone) 3428549b951SMyung Bae if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure") 3438549b951SMyung Bae { 344*48fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3458549b951SMyung Bae } 3468549b951SMyung Bae else if (type == 3478549b951SMyung Bae "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure") 3488549b951SMyung Bae { 349*48fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3508549b951SMyung Bae } 3518549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure") 3528549b951SMyung Bae { 353*48fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3548549b951SMyung Bae } 3558549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists") 3568549b951SMyung Bae { 357c87294a6SEd Tanous messages::resourceAlreadyExists(asyncResp->res, "UpdateService", 358c87294a6SEd Tanous "Version", "uploaded version"); 3598549b951SMyung Bae } 3608549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure") 3618549b951SMyung Bae { 362*48fb20b9SEd Tanous messages::serviceTemporarilyUnavailable(asyncResp->res, url); 3638549b951SMyung Bae } 3644034a652SMyung Bae else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible") 3658549b951SMyung Bae { 366c87294a6SEd Tanous messages::internalError(asyncResp->res); 3674034a652SMyung Bae } 3684034a652SMyung Bae else if (type == 3694034a652SMyung Bae "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey") 3704034a652SMyung Bae { 371c87294a6SEd Tanous messages::internalError(asyncResp->res); 3724034a652SMyung Bae } 3734034a652SMyung Bae else if (type == 3744034a652SMyung Bae "xyz.openbmc_project.Software.Version.Error.InvalidSignature") 3754034a652SMyung Bae { 376*48fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3774034a652SMyung Bae } 3784034a652SMyung Bae else if (type == 3794034a652SMyung Bae "xyz.openbmc_project.Software.Image.Error.InternalFailure" || 3804034a652SMyung Bae type == "xyz.openbmc_project.Software.Version.Error.HostFile") 3814034a652SMyung Bae { 3824034a652SMyung Bae BMCWEB_LOG_ERROR("Software Image Error type={}", type); 383*48fb20b9SEd Tanous messages::internalError(asyncResp->res); 3848549b951SMyung Bae } 3854034a652SMyung Bae else 3864034a652SMyung Bae { 3874034a652SMyung Bae // Unrelated error types. Ignored 3884034a652SMyung Bae BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type); 3894034a652SMyung Bae return; 3904034a652SMyung Bae } 391c87294a6SEd Tanous // NOLINTEND(bugprone-branch-clone) 3924034a652SMyung Bae // Clear the timer 3934034a652SMyung Bae fwAvailableTimer = nullptr; 3948549b951SMyung Bae } 3958549b951SMyung Bae 3968549b951SMyung Bae inline void 3978549b951SMyung Bae afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3988549b951SMyung Bae const std::string& url, sdbusplus::message_t& m) 3998549b951SMyung Bae { 40080f79a40SMichael Shen dbus::utility::DBusInterfacesMap interfacesProperties; 4018549b951SMyung Bae sdbusplus::message::object_path objPath; 4028549b951SMyung Bae m.read(objPath, interfacesProperties); 4038549b951SMyung Bae BMCWEB_LOG_DEBUG("obj path = {}", objPath.str); 4048549b951SMyung Bae for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>& 4058549b951SMyung Bae interface : interfacesProperties) 4068549b951SMyung Bae { 4078549b951SMyung Bae if (interface.first == "xyz.openbmc_project.Logging.Entry") 4088549b951SMyung Bae { 4098549b951SMyung Bae for (const std::pair<std::string, dbus::utility::DbusVariantType>& 4108549b951SMyung Bae value : interface.second) 4118549b951SMyung Bae { 4128549b951SMyung Bae if (value.first != "Message") 4138549b951SMyung Bae { 4148549b951SMyung Bae continue; 4158549b951SMyung Bae } 4168549b951SMyung Bae const std::string* type = 4178549b951SMyung Bae std::get_if<std::string>(&value.second); 4188549b951SMyung Bae if (type == nullptr) 4198549b951SMyung Bae { 4208549b951SMyung Bae // if this was our message, timeout will cover it 4218549b951SMyung Bae return; 4228549b951SMyung Bae } 4238549b951SMyung Bae handleUpdateErrorType(asyncResp, url, *type); 4248549b951SMyung Bae } 4258549b951SMyung Bae } 4268549b951SMyung Bae } 4278549b951SMyung Bae } 4288549b951SMyung Bae 4290554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr 4300554c984SAndrew Geissler // then no asyncResp updates will occur 431f5139334SEd Tanous inline void monitorForSoftwareAvailable( 4328d1b46d7Szhanghch05 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 4338d1b46d7Szhanghch05 const crow::Request& req, const std::string& url, 4345d138943SGunnar Mills int timeoutTimeSeconds = 25) 43586adcd6dSAndrew Geissler { 43686adcd6dSAndrew Geissler // Only allow one FW update at a time 437e05aec50SEd Tanous if (fwUpdateInProgress) 43886adcd6dSAndrew Geissler { 4390554c984SAndrew Geissler if (asyncResp) 4400554c984SAndrew Geissler { 44186adcd6dSAndrew Geissler messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 4420554c984SAndrew Geissler } 44386adcd6dSAndrew Geissler return; 44486adcd6dSAndrew Geissler } 44586adcd6dSAndrew Geissler 4468e8245dbSEd Tanous if (req.ioService == nullptr) 4478e8245dbSEd Tanous { 4488e8245dbSEd Tanous messages::internalError(asyncResp->res); 4498e8245dbSEd Tanous return; 4508e8245dbSEd Tanous } 4518e8245dbSEd Tanous 4520554c984SAndrew Geissler fwAvailableTimer = 453271584abSEd Tanous std::make_unique<boost::asio::steady_timer>(*req.ioService); 45486adcd6dSAndrew Geissler 455271584abSEd Tanous fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds)); 45686adcd6dSAndrew Geissler 45786adcd6dSAndrew Geissler fwAvailableTimer->async_wait( 4588549b951SMyung Bae std::bind_front(afterAvailbleTimerAsyncWait, asyncResp)); 4598549b951SMyung Bae 460a3e65892SEd Tanous task::Payload payload(req); 46159d494eeSPatrick Williams auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable { 46262598e31SEd Tanous BMCWEB_LOG_DEBUG("Match fired"); 463a3e65892SEd Tanous softwareInterfaceAdded(asyncResp, m, std::move(payload)); 46486adcd6dSAndrew Geissler }; 46586adcd6dSAndrew Geissler 46686adcd6dSAndrew Geissler fwUpdateInProgress = true; 46786adcd6dSAndrew Geissler 46859d494eeSPatrick Williams fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>( 46986adcd6dSAndrew Geissler *crow::connections::systemBus, 47086adcd6dSAndrew Geissler "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 47186adcd6dSAndrew Geissler "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 47286adcd6dSAndrew Geissler callback); 4734cde5d90SJames Feist 47459d494eeSPatrick Williams fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>( 4754cde5d90SJames Feist *crow::connections::systemBus, 476e1cc4828SBrian Ma "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 477e1cc4828SBrian Ma "member='InterfacesAdded'," 478e1cc4828SBrian Ma "path='/xyz/openbmc_project/logging'", 4798549b951SMyung Bae std::bind_front(afterUpdateErrorMatcher, asyncResp, url)); 48086adcd6dSAndrew Geissler } 481729dae72SJennifer Lee 482bd79bce8SPatrick Williams inline std::optional<boost::urls::url> parseSimpleUpdateUrl( 483bd79bce8SPatrick Williams std::string imageURI, std::optional<std::string> transferProtocol, 484f86bcc87SEd Tanous crow::Response& res) 485f86bcc87SEd Tanous { 486f86bcc87SEd Tanous if (imageURI.find("://") == std::string::npos) 487f86bcc87SEd Tanous { 488f86bcc87SEd Tanous if (imageURI.starts_with("/")) 489f86bcc87SEd Tanous { 490f86bcc87SEd Tanous messages::actionParameterValueTypeError( 491f86bcc87SEd Tanous res, imageURI, "ImageURI", "UpdateService.SimpleUpdate"); 492f86bcc87SEd Tanous return std::nullopt; 493f86bcc87SEd Tanous } 494f86bcc87SEd Tanous if (!transferProtocol) 495f86bcc87SEd Tanous { 496f86bcc87SEd Tanous messages::actionParameterValueTypeError( 497f86bcc87SEd Tanous res, imageURI, "ImageURI", "UpdateService.SimpleUpdate"); 498f86bcc87SEd Tanous return std::nullopt; 499f86bcc87SEd Tanous } 500e5cf777eSEd Tanous // OpenBMC currently only supports TFTP or HTTPS 501757178a5SEd Tanous if (*transferProtocol == "TFTP") 502757178a5SEd Tanous { 503757178a5SEd Tanous imageURI = "tftp://" + imageURI; 504757178a5SEd Tanous } 505e5cf777eSEd Tanous else if (*transferProtocol == "HTTPS") 506e5cf777eSEd Tanous { 507e5cf777eSEd Tanous imageURI = "https://" + imageURI; 508e5cf777eSEd Tanous } 509757178a5SEd Tanous else 510f86bcc87SEd Tanous { 511f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "TransferProtocol", 512f86bcc87SEd Tanous *transferProtocol); 513f86bcc87SEd Tanous BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}", 514f86bcc87SEd Tanous *transferProtocol); 515f86bcc87SEd Tanous return std::nullopt; 516f86bcc87SEd Tanous } 517f86bcc87SEd Tanous } 518f86bcc87SEd Tanous 519f86bcc87SEd Tanous boost::system::result<boost::urls::url> url = 520f86bcc87SEd Tanous boost::urls::parse_absolute_uri(imageURI); 521f86bcc87SEd Tanous if (!url) 522f86bcc87SEd Tanous { 523f86bcc87SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI", 524f86bcc87SEd Tanous "UpdateService.SimpleUpdate"); 525f86bcc87SEd Tanous 526f86bcc87SEd Tanous return std::nullopt; 527f86bcc87SEd Tanous } 528f86bcc87SEd Tanous url->normalize(); 529f86bcc87SEd Tanous 530757178a5SEd Tanous if (url->scheme() == "tftp") 531757178a5SEd Tanous { 532757178a5SEd Tanous if (url->encoded_path().size() < 2) 533757178a5SEd Tanous { 534757178a5SEd Tanous messages::actionParameterNotSupported(res, "ImageURI", 535757178a5SEd Tanous url->buffer()); 536757178a5SEd Tanous return std::nullopt; 537757178a5SEd Tanous } 538757178a5SEd Tanous } 539e5cf777eSEd Tanous else if (url->scheme() == "https") 540e5cf777eSEd Tanous { 541e5cf777eSEd Tanous // Empty paths default to "/" 542e5cf777eSEd Tanous if (url->encoded_path().empty()) 543e5cf777eSEd Tanous { 544e5cf777eSEd Tanous url->set_encoded_path("/"); 545e5cf777eSEd Tanous } 546e5cf777eSEd Tanous } 547757178a5SEd Tanous else 548f86bcc87SEd Tanous { 549f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "ImageURI", imageURI); 550f86bcc87SEd Tanous return std::nullopt; 551f86bcc87SEd Tanous } 552757178a5SEd Tanous 553757178a5SEd Tanous if (url->encoded_path().empty()) 554f86bcc87SEd Tanous { 555757178a5SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI", 556757178a5SEd Tanous "UpdateService.SimpleUpdate"); 557f86bcc87SEd Tanous return std::nullopt; 558f86bcc87SEd Tanous } 559757178a5SEd Tanous 560757178a5SEd Tanous return *url; 561f86bcc87SEd Tanous } 562f86bcc87SEd Tanous 563e5cf777eSEd Tanous inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 564e5cf777eSEd Tanous const boost::urls::url_view_base& url) 565e5cf777eSEd Tanous { 566e5cf777eSEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI", 567e5cf777eSEd Tanous url.buffer()); 568e5cf777eSEd Tanous } 569e5cf777eSEd Tanous 570f5139334SEd Tanous inline void handleUpdateServiceSimpleUpdateAction( 571f5139334SEd Tanous crow::App& app, const crow::Request& req, 572f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 5730554c984SAndrew Geissler { 5743ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 57545ca1b86SEd Tanous { 57645ca1b86SEd Tanous return; 57745ca1b86SEd Tanous } 57845ca1b86SEd Tanous 5790554c984SAndrew Geissler std::optional<std::string> transferProtocol; 5800554c984SAndrew Geissler std::string imageURI; 5810554c984SAndrew Geissler 58262598e31SEd Tanous BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost"); 5830554c984SAndrew Geissler 5840554c984SAndrew Geissler // User can pass in both TransferProtocol and ImageURI parameters or 5854e0453b1SGunnar Mills // they can pass in just the ImageURI with the transfer protocol 5864e0453b1SGunnar Mills // embedded within it. 5870554c984SAndrew Geissler // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin 5880554c984SAndrew Geissler // 2) ImageURI:tftp://1.1.1.1/myfile.bin 5890554c984SAndrew Geissler 590afc474aeSMyung Bae if (!json_util::readJsonAction( // 591afc474aeSMyung Bae req, asyncResp->res, // 592afc474aeSMyung Bae "ImageURI", imageURI, // 593afc474aeSMyung Bae "TransferProtocol", transferProtocol // 594afc474aeSMyung Bae )) 5950554c984SAndrew Geissler { 59662598e31SEd Tanous BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter"); 5970554c984SAndrew Geissler return; 5980554c984SAndrew Geissler } 599f5139334SEd Tanous 600757178a5SEd Tanous std::optional<boost::urls::url> url = 601757178a5SEd Tanous parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res); 602757178a5SEd Tanous if (!url) 6030554c984SAndrew Geissler { 6040554c984SAndrew Geissler return; 6050554c984SAndrew Geissler } 6064e338b23SJagpal Singh Gill if (url->scheme() == "https") 607e5cf777eSEd Tanous { 608e5cf777eSEd Tanous doHttpsUpdate(asyncResp, *url); 609e5cf777eSEd Tanous } 610757178a5SEd Tanous else 611757178a5SEd Tanous { 612757178a5SEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI", 613757178a5SEd Tanous url->buffer()); 614757178a5SEd Tanous return; 615757178a5SEd Tanous } 6160554c984SAndrew Geissler 61762598e31SEd Tanous BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost"); 618729dae72SJennifer Lee } 619729dae72SJennifer Lee 6200ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body) 6210ed80c8cSGeorge Liu { 6222c6ffdb0SEd Tanous std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID()); 6232c6ffdb0SEd Tanous 62462598e31SEd Tanous BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string()); 6250ed80c8cSGeorge Liu std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | 6260ed80c8cSGeorge Liu std::ofstream::trunc); 6270ed80c8cSGeorge Liu // set the permission of the file to 640 628bd79bce8SPatrick Williams std::filesystem::perms permission = 629bd79bce8SPatrick Williams std::filesystem::perms::owner_read | std::filesystem::perms::group_read; 6300ed80c8cSGeorge Liu std::filesystem::permissions(filepath, permission); 6310ed80c8cSGeorge Liu out << body; 6320ed80c8cSGeorge Liu 6330ed80c8cSGeorge Liu if (out.bad()) 6340ed80c8cSGeorge Liu { 6350ed80c8cSGeorge Liu messages::internalError(res); 6360ed80c8cSGeorge Liu cleanUp(); 6370ed80c8cSGeorge Liu } 6380ed80c8cSGeorge Liu } 6390ed80c8cSGeorge Liu 640de0c960cSJagpal Singh Gill // Convert the Request Apply Time to the D-Bus value 641de0c960cSJagpal Singh Gill inline bool convertApplyTime(crow::Response& res, const std::string& applyTime, 642de0c960cSJagpal Singh Gill std::string& applyTimeNewVal) 643de0c960cSJagpal Singh Gill { 644de0c960cSJagpal Singh Gill if (applyTime == "Immediate") 645de0c960cSJagpal Singh Gill { 646de0c960cSJagpal Singh Gill applyTimeNewVal = 647049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate"; 648de0c960cSJagpal Singh Gill } 649de0c960cSJagpal Singh Gill else if (applyTime == "OnReset") 650de0c960cSJagpal Singh Gill { 651de0c960cSJagpal Singh Gill applyTimeNewVal = 652049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset"; 653de0c960cSJagpal Singh Gill } 654de0c960cSJagpal Singh Gill else 655de0c960cSJagpal Singh Gill { 656de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING( 657de0c960cSJagpal Singh Gill "ApplyTime value {} is not in the list of acceptable values", 658de0c960cSJagpal Singh Gill applyTime); 659de0c960cSJagpal Singh Gill messages::propertyValueNotInList(res, applyTime, "ApplyTime"); 660de0c960cSJagpal Singh Gill return false; 661de0c960cSJagpal Singh Gill } 662de0c960cSJagpal Singh Gill return true; 663de0c960cSJagpal Singh Gill } 664de0c960cSJagpal Singh Gill 6650ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 6660ed80c8cSGeorge Liu const std::string& applyTime) 6670ed80c8cSGeorge Liu { 6680ed80c8cSGeorge Liu std::string applyTimeNewVal; 669049079f6SJagpal Singh Gill if (!convertApplyTime(asyncResp->res, applyTime, applyTimeNewVal)) 6700ed80c8cSGeorge Liu { 6710ed80c8cSGeorge Liu return; 6720ed80c8cSGeorge Liu } 6730ed80c8cSGeorge Liu 674e93abac6SGinu George setDbusProperty(asyncResp, "ApplyTime", "xyz.openbmc_project.Settings", 675d02aad39SEd Tanous sdbusplus::message::object_path( 676d02aad39SEd Tanous "/xyz/openbmc_project/software/apply_time"), 677d02aad39SEd Tanous "xyz.openbmc_project.Software.ApplyTime", 678e93abac6SGinu George "RequestedApplyTime", applyTimeNewVal); 6790ed80c8cSGeorge Liu } 6800ed80c8cSGeorge Liu 681ef93eab3SJagpal Singh Gill struct MultiPartUpdateParameters 6820ed80c8cSGeorge Liu { 683ef93eab3SJagpal Singh Gill std::optional<std::string> applyTime; 684ef93eab3SJagpal Singh Gill std::string uploadData; 685de0c960cSJagpal Singh Gill std::vector<std::string> targets; 686ef93eab3SJagpal Singh Gill }; 687ef93eab3SJagpal Singh Gill 688de0c960cSJagpal Singh Gill inline std::optional<std::string> 689de0c960cSJagpal Singh Gill processUrl(boost::system::result<boost::urls::url_view>& url) 690de0c960cSJagpal Singh Gill { 691de0c960cSJagpal Singh Gill if (!url) 692de0c960cSJagpal Singh Gill { 693de0c960cSJagpal Singh Gill return std::nullopt; 694de0c960cSJagpal Singh Gill } 695de0c960cSJagpal Singh Gill if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers", 696de0c960cSJagpal Singh Gill BMCWEB_REDFISH_MANAGER_URI_NAME)) 697de0c960cSJagpal Singh Gill { 698de0c960cSJagpal Singh Gill return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME)); 699de0c960cSJagpal Singh Gill } 700de0c960cSJagpal Singh Gill if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 701de0c960cSJagpal Singh Gill { 702de0c960cSJagpal Singh Gill return std::nullopt; 703de0c960cSJagpal Singh Gill } 704de0c960cSJagpal Singh Gill std::string firmwareId; 705de0c960cSJagpal Singh Gill if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService", 706de0c960cSJagpal Singh Gill "FirmwareInventory", 707de0c960cSJagpal Singh Gill std::ref(firmwareId))) 708de0c960cSJagpal Singh Gill { 709de0c960cSJagpal Singh Gill return std::nullopt; 710de0c960cSJagpal Singh Gill } 711de0c960cSJagpal Singh Gill 712de0c960cSJagpal Singh Gill return std::make_optional(firmwareId); 713de0c960cSJagpal Singh Gill } 714de0c960cSJagpal Singh Gill 715ef93eab3SJagpal Singh Gill inline std::optional<MultiPartUpdateParameters> 716ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters( 717ef93eab3SJagpal Singh Gill const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 718ef93eab3SJagpal Singh Gill MultipartParser parser) 719ef93eab3SJagpal Singh Gill { 720ef93eab3SJagpal Singh Gill MultiPartUpdateParameters multiRet; 721ef93eab3SJagpal Singh Gill for (FormPart& formpart : parser.mime_fields) 7220ed80c8cSGeorge Liu { 7230ed80c8cSGeorge Liu boost::beast::http::fields::const_iterator it = 7240ed80c8cSGeorge Liu formpart.fields.find("Content-Disposition"); 7250ed80c8cSGeorge Liu if (it == formpart.fields.end()) 7260ed80c8cSGeorge Liu { 72762598e31SEd Tanous BMCWEB_LOG_ERROR("Couldn't find Content-Disposition"); 728ef93eab3SJagpal Singh Gill return std::nullopt; 7290ed80c8cSGeorge Liu } 73062598e31SEd Tanous BMCWEB_LOG_INFO("Parsing value {}", it->value()); 7310ed80c8cSGeorge Liu 7320ed80c8cSGeorge Liu // The construction parameters of param_list must start with `;` 7330ed80c8cSGeorge Liu size_t index = it->value().find(';'); 7340ed80c8cSGeorge Liu if (index == std::string::npos) 7350ed80c8cSGeorge Liu { 7360ed80c8cSGeorge Liu continue; 7370ed80c8cSGeorge Liu } 7380ed80c8cSGeorge Liu 73989492a15SPatrick Williams for (const auto& param : 7400ed80c8cSGeorge Liu boost::beast::http::param_list{it->value().substr(index)}) 7410ed80c8cSGeorge Liu { 7420ed80c8cSGeorge Liu if (param.first != "name" || param.second.empty()) 7430ed80c8cSGeorge Liu { 7440ed80c8cSGeorge Liu continue; 7450ed80c8cSGeorge Liu } 7460ed80c8cSGeorge Liu 7470ed80c8cSGeorge Liu if (param.second == "UpdateParameters") 7480ed80c8cSGeorge Liu { 749ef93eab3SJagpal Singh Gill std::vector<std::string> tempTargets; 750bd79bce8SPatrick Williams nlohmann::json content = 751bd79bce8SPatrick Williams nlohmann::json::parse(formpart.content, nullptr, false); 752ac1e1246SEd Tanous if (content.is_discarded()) 753ac1e1246SEd Tanous { 754ac1e1246SEd Tanous return std::nullopt; 755ac1e1246SEd Tanous } 7567cb59f65SEd Tanous nlohmann::json::object_t* obj = 7577cb59f65SEd Tanous content.get_ptr<nlohmann::json::object_t*>(); 7587cb59f65SEd Tanous if (obj == nullptr) 7597cb59f65SEd Tanous { 760ef93eab3SJagpal Singh Gill messages::propertyValueTypeError( 761ef93eab3SJagpal Singh Gill asyncResp->res, formpart.content, "UpdateParameters"); 762ef93eab3SJagpal Singh Gill return std::nullopt; 7637cb59f65SEd Tanous } 7647cb59f65SEd Tanous 765afc474aeSMyung Bae if (!json_util::readJsonObject( // 766afc474aeSMyung Bae *obj, asyncResp->res, // 767afc474aeSMyung Bae "@Redfish.OperationApplyTime", multiRet.applyTime, // 768afc474aeSMyung Bae "Targets", tempTargets // 769afc474aeSMyung Bae )) 7700ed80c8cSGeorge Liu { 771ef93eab3SJagpal Singh Gill return std::nullopt; 7720ed80c8cSGeorge Liu } 773ef93eab3SJagpal Singh Gill 774ef93eab3SJagpal Singh Gill for (size_t urlIndex = 0; urlIndex < tempTargets.size(); 775ef93eab3SJagpal Singh Gill urlIndex++) 7760ed80c8cSGeorge Liu { 777ef93eab3SJagpal Singh Gill const std::string& target = tempTargets[urlIndex]; 778ef93eab3SJagpal Singh Gill boost::system::result<boost::urls::url_view> url = 779ef93eab3SJagpal Singh Gill boost::urls::parse_origin_form(target); 780de0c960cSJagpal Singh Gill auto res = processUrl(url); 781de0c960cSJagpal Singh Gill if (!res.has_value()) 7820ed80c8cSGeorge Liu { 783ef93eab3SJagpal Singh Gill messages::propertyValueFormatError( 784ef93eab3SJagpal Singh Gill asyncResp->res, target, 785ef93eab3SJagpal Singh Gill std::format("Targets/{}", urlIndex)); 786ef93eab3SJagpal Singh Gill return std::nullopt; 7870ed80c8cSGeorge Liu } 788de0c960cSJagpal Singh Gill multiRet.targets.emplace_back(res.value()); 789ef93eab3SJagpal Singh Gill } 790ef93eab3SJagpal Singh Gill if (multiRet.targets.size() != 1) 791ef93eab3SJagpal Singh Gill { 792ef93eab3SJagpal Singh Gill messages::propertyValueFormatError( 793ef93eab3SJagpal Singh Gill asyncResp->res, multiRet.targets, "Targets"); 794ef93eab3SJagpal Singh Gill return std::nullopt; 795ef93eab3SJagpal Singh Gill } 7960ed80c8cSGeorge Liu } 7970ed80c8cSGeorge Liu else if (param.second == "UpdateFile") 7980ed80c8cSGeorge Liu { 799ef93eab3SJagpal Singh Gill multiRet.uploadData = std::move(formpart.content); 8000ed80c8cSGeorge Liu } 8010ed80c8cSGeorge Liu } 8020ed80c8cSGeorge Liu } 8030ed80c8cSGeorge Liu 804ef93eab3SJagpal Singh Gill if (multiRet.uploadData.empty()) 8050ed80c8cSGeorge Liu { 80662598e31SEd Tanous BMCWEB_LOG_ERROR("Upload data is NULL"); 8070ed80c8cSGeorge Liu messages::propertyMissing(asyncResp->res, "UpdateFile"); 808ef93eab3SJagpal Singh Gill return std::nullopt; 8090ed80c8cSGeorge Liu } 810ef93eab3SJagpal Singh Gill if (multiRet.targets.empty()) 8110ed80c8cSGeorge Liu { 812ef93eab3SJagpal Singh Gill messages::propertyMissing(asyncResp->res, "Targets"); 813ef93eab3SJagpal Singh Gill return std::nullopt; 814ef93eab3SJagpal Singh Gill } 815ef93eab3SJagpal Singh Gill return multiRet; 8160ed80c8cSGeorge Liu } 8170ed80c8cSGeorge Liu 818bd79bce8SPatrick Williams inline void handleStartUpdate( 819bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 820bd79bce8SPatrick Williams const std::string& objectPath, const boost::system::error_code& ec, 821de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) 822de0c960cSJagpal Singh Gill { 823de0c960cSJagpal Singh Gill if (ec) 824de0c960cSJagpal Singh Gill { 825de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 826de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 827de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 828de0c960cSJagpal Singh Gill return; 829de0c960cSJagpal Singh Gill } 830de0c960cSJagpal Singh Gill 831587090cdSJagpal Singh Gill BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}", 832587090cdSJagpal Singh Gill objectPath, retPath.str); 833587090cdSJagpal Singh Gill createTask(asyncResp, std::move(payload), retPath); 834de0c960cSJagpal Singh Gill } 835de0c960cSJagpal Singh Gill 836bd79bce8SPatrick Williams inline void startUpdate( 837bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 838bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 839bd79bce8SPatrick Williams const std::string& objectPath, const std::string& serviceName) 840de0c960cSJagpal Singh Gill { 841de0c960cSJagpal Singh Gill crow::connections::systemBus->async_method_call( 842de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), 843de0c960cSJagpal Singh Gill objectPath](const boost::system::error_code& ec1, 844de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) mutable { 845de0c960cSJagpal Singh Gill handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1, 846de0c960cSJagpal Singh Gill retPath); 847de0c960cSJagpal Singh Gill }, 848de0c960cSJagpal Singh Gill serviceName, objectPath, "xyz.openbmc_project.Software.Update", 849de0c960cSJagpal Singh Gill "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime); 850de0c960cSJagpal Singh Gill } 851de0c960cSJagpal Singh Gill 85208f61d53SJagpal Singh Gill inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 85308f61d53SJagpal Singh Gill task::Payload payload, const MemoryFileDescriptor& memfd, 85408f61d53SJagpal Singh Gill const std::string& applyTime, const std::string& target, 855de0c960cSJagpal Singh Gill const boost::system::error_code& ec, 856de0c960cSJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& subtree) 857de0c960cSJagpal Singh Gill { 85808f61d53SJagpal Singh Gill using SwInfoMap = std::unordered_map< 85908f61d53SJagpal Singh Gill std::string, std::pair<sdbusplus::message::object_path, std::string>>; 860de0c960cSJagpal Singh Gill SwInfoMap swInfoMap; 861de0c960cSJagpal Singh Gill 862de0c960cSJagpal Singh Gill if (ec) 863de0c960cSJagpal Singh Gill { 864de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 865de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 866de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 867de0c960cSJagpal Singh Gill return; 868de0c960cSJagpal Singh Gill } 869de0c960cSJagpal Singh Gill BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size()); 870de0c960cSJagpal Singh Gill 87108f61d53SJagpal Singh Gill for (const auto& entry : subtree) 872de0c960cSJagpal Singh Gill { 87308f61d53SJagpal Singh Gill sdbusplus::message::object_path path(entry.first); 874de0c960cSJagpal Singh Gill std::string swId = path.filename(); 87508f61d53SJagpal Singh Gill swInfoMap.emplace(swId, make_pair(path, entry.second[0].first)); 876de0c960cSJagpal Singh Gill } 877de0c960cSJagpal Singh Gill 878de0c960cSJagpal Singh Gill auto swEntry = swInfoMap.find(target); 879de0c960cSJagpal Singh Gill if (swEntry == swInfoMap.end()) 880de0c960cSJagpal Singh Gill { 881de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target); 882de0c960cSJagpal Singh Gill messages::propertyValueFormatError(asyncResp->res, target, "Targets"); 883de0c960cSJagpal Singh Gill return; 884de0c960cSJagpal Singh Gill } 885de0c960cSJagpal Singh Gill 88608f61d53SJagpal Singh Gill BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}", 88708f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 888de0c960cSJagpal Singh Gill 88908f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 89008f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 89108f61d53SJagpal Singh Gill } 89208f61d53SJagpal Singh Gill 893bd79bce8SPatrick Williams inline void handleBMCUpdate( 894bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 895bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 89608f61d53SJagpal Singh Gill const boost::system::error_code& ec, 89708f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& functionalSoftware) 89808f61d53SJagpal Singh Gill { 89908f61d53SJagpal Singh Gill if (ec) 90008f61d53SJagpal Singh Gill { 90108f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 90208f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 90308f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 90408f61d53SJagpal Singh Gill return; 90508f61d53SJagpal Singh Gill } 90608f61d53SJagpal Singh Gill if (functionalSoftware.size() != 1) 90708f61d53SJagpal Singh Gill { 90808f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("Found {} functional software endpoints", 90908f61d53SJagpal Singh Gill functionalSoftware.size()); 91008f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 91108f61d53SJagpal Singh Gill return; 91208f61d53SJagpal Singh Gill } 91308f61d53SJagpal Singh Gill 91408f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 91508f61d53SJagpal Singh Gill functionalSoftware[0], "xyz.openbmc_project.Software.Manager"); 916de0c960cSJagpal Singh Gill } 917de0c960cSJagpal Singh Gill 918bd79bce8SPatrick Williams inline void processUpdateRequest( 919bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 9209dae4deeSJagpal Singh Gill task::Payload&& payload, std::string_view body, 921bd79bce8SPatrick Williams const std::string& applyTime, std::vector<std::string>& targets) 922de0c960cSJagpal Singh Gill { 923de0c960cSJagpal Singh Gill MemoryFileDescriptor memfd("update-image"); 924de0c960cSJagpal Singh Gill if (memfd.fd == -1) 925de0c960cSJagpal Singh Gill { 926de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to create image memfd"); 927de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 928de0c960cSJagpal Singh Gill return; 929de0c960cSJagpal Singh Gill } 930de0c960cSJagpal Singh Gill if (write(memfd.fd, body.data(), body.length()) != 931de0c960cSJagpal Singh Gill static_cast<ssize_t>(body.length())) 932de0c960cSJagpal Singh Gill { 933de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to write to image memfd"); 934de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 935de0c960cSJagpal Singh Gill return; 936de0c960cSJagpal Singh Gill } 937de0c960cSJagpal Singh Gill if (!memfd.rewind()) 938de0c960cSJagpal Singh Gill { 939de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 940de0c960cSJagpal Singh Gill return; 941de0c960cSJagpal Singh Gill } 942de0c960cSJagpal Singh Gill 943de0c960cSJagpal Singh Gill if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME) 944de0c960cSJagpal Singh Gill { 94508f61d53SJagpal Singh Gill dbus::utility::getAssociationEndPoints( 94689449bbeSJagpal Singh Gill "/xyz/openbmc_project/software/bmc/updateable", 94708f61d53SJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 94808f61d53SJagpal Singh Gill applyTime]( 94908f61d53SJagpal Singh Gill const boost::system::error_code& ec, 95008f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& objectPaths) mutable { 951bd79bce8SPatrick Williams handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime, 952bd79bce8SPatrick Williams ec, objectPaths); 95308f61d53SJagpal Singh Gill }); 954de0c960cSJagpal Singh Gill } 955de0c960cSJagpal Singh Gill else 956de0c960cSJagpal Singh Gill { 957de0c960cSJagpal Singh Gill constexpr std::array<std::string_view, 1> interfaces = { 958de0c960cSJagpal Singh Gill "xyz.openbmc_project.Software.Version"}; 95908f61d53SJagpal Singh Gill dbus::utility::getSubTree( 960de0c960cSJagpal Singh Gill "/xyz/openbmc_project/software", 1, interfaces, 961de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 96208f61d53SJagpal Singh Gill applyTime, targets](const boost::system::error_code& ec, 96308f61d53SJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& 964de0c960cSJagpal Singh Gill subtree) mutable { 96508f61d53SJagpal Singh Gill getSwInfo(asyncResp, std::move(payload), memfd, applyTime, 96608f61d53SJagpal Singh Gill targets[0], ec, subtree); 967de0c960cSJagpal Singh Gill }); 968de0c960cSJagpal Singh Gill } 969de0c960cSJagpal Singh Gill } 970de0c960cSJagpal Singh Gill 971de0c960cSJagpal Singh Gill inline void 972ef93eab3SJagpal Singh Gill updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 973ef93eab3SJagpal Singh Gill const crow::Request& req, MultipartParser&& parser) 974ef93eab3SJagpal Singh Gill { 975ef93eab3SJagpal Singh Gill std::optional<MultiPartUpdateParameters> multipart = 976ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters(asyncResp, std::move(parser)); 977ef93eab3SJagpal Singh Gill if (!multipart) 978ef93eab3SJagpal Singh Gill { 979ef93eab3SJagpal Singh Gill return; 980ef93eab3SJagpal Singh Gill } 981ef93eab3SJagpal Singh Gill if (!multipart->applyTime) 982ef93eab3SJagpal Singh Gill { 983ef93eab3SJagpal Singh Gill multipart->applyTime = "OnReset"; 984ef93eab3SJagpal Singh Gill } 985ef93eab3SJagpal Singh Gill 986de0c960cSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 987de0c960cSJagpal Singh Gill { 9889dae4deeSJagpal Singh Gill std::string applyTimeNewVal; 9899dae4deeSJagpal Singh Gill if (!convertApplyTime(asyncResp->res, *multipart->applyTime, 9909dae4deeSJagpal Singh Gill applyTimeNewVal)) 9919dae4deeSJagpal Singh Gill { 9929dae4deeSJagpal Singh Gill return; 9939dae4deeSJagpal Singh Gill } 9949dae4deeSJagpal Singh Gill task::Payload payload(req); 9959dae4deeSJagpal Singh Gill 9969dae4deeSJagpal Singh Gill processUpdateRequest(asyncResp, std::move(payload), 9979dae4deeSJagpal Singh Gill multipart->uploadData, applyTimeNewVal, 9989dae4deeSJagpal Singh Gill multipart->targets); 999de0c960cSJagpal Singh Gill } 1000de0c960cSJagpal Singh Gill else 1001de0c960cSJagpal Singh Gill { 1002ef93eab3SJagpal Singh Gill setApplyTime(asyncResp, *multipart->applyTime); 10030ed80c8cSGeorge Liu 10046b54e4e0SEd Tanous // Setup callback for when new software detected 1005de0c960cSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 1006de0c960cSJagpal Singh Gill "/redfish/v1/UpdateService"); 10076b54e4e0SEd Tanous 1008ef93eab3SJagpal Singh Gill uploadImageFile(asyncResp->res, multipart->uploadData); 10090ed80c8cSGeorge Liu } 1010de0c960cSJagpal Singh Gill } 10110ed80c8cSGeorge Liu 10129dae4deeSJagpal Singh Gill inline void doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 10139dae4deeSJagpal Singh Gill const crow::Request& req) 10149dae4deeSJagpal Singh Gill { 10159dae4deeSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 10169dae4deeSJagpal Singh Gill { 10179dae4deeSJagpal Singh Gill task::Payload payload(req); 10189dae4deeSJagpal Singh Gill // HTTP push only supports BMC updates (with ApplyTime as immediate) for 10199dae4deeSJagpal Singh Gill // backwards compatibility. Specific component updates will be handled 10209dae4deeSJagpal Singh Gill // through Multipart form HTTP push. 10219dae4deeSJagpal Singh Gill std::vector<std::string> targets; 10229dae4deeSJagpal Singh Gill targets.emplace_back(BMCWEB_REDFISH_MANAGER_URI_NAME); 10239dae4deeSJagpal Singh Gill 10249dae4deeSJagpal Singh Gill processUpdateRequest( 10259dae4deeSJagpal Singh Gill asyncResp, std::move(payload), req.body(), 10269dae4deeSJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate", 10279dae4deeSJagpal Singh Gill targets); 10289dae4deeSJagpal Singh Gill } 10299dae4deeSJagpal Singh Gill else 10309dae4deeSJagpal Singh Gill { 10319dae4deeSJagpal Singh Gill // Setup callback for when new software detected 10329dae4deeSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 10339dae4deeSJagpal Singh Gill "/redfish/v1/UpdateService"); 10349dae4deeSJagpal Singh Gill 10359dae4deeSJagpal Singh Gill uploadImageFile(asyncResp->res, req.body()); 10369dae4deeSJagpal Singh Gill } 10379dae4deeSJagpal Singh Gill } 10389dae4deeSJagpal Singh Gill 1039c2051d11SEd Tanous inline void 1040c2051d11SEd Tanous handleUpdateServicePost(App& app, const crow::Request& req, 1041c2051d11SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1042c2051d11SEd Tanous { 10433ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1044c2051d11SEd Tanous { 1045c2051d11SEd Tanous return; 1046c2051d11SEd Tanous } 1047b33a4327SNinad Palsule std::string_view contentType = req.getHeaderValue("Content-Type"); 1048b33a4327SNinad Palsule 104962598e31SEd Tanous BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType); 1050b33a4327SNinad Palsule 1051b33a4327SNinad Palsule // Make sure that content type is application/octet-stream or 1052b33a4327SNinad Palsule // multipart/form-data 105318f8f608SEd Tanous if (bmcweb::asciiIEquals(contentType, "application/octet-stream")) 1054b33a4327SNinad Palsule { 10559dae4deeSJagpal Singh Gill doHTTPUpdate(asyncResp, req); 1056b33a4327SNinad Palsule } 1057b33a4327SNinad Palsule else if (contentType.starts_with("multipart/form-data")) 1058b33a4327SNinad Palsule { 1059b33a4327SNinad Palsule MultipartParser parser; 1060c2051d11SEd Tanous 10610ed80c8cSGeorge Liu ParserError ec = parser.parse(req); 10620ed80c8cSGeorge Liu if (ec != ParserError::PARSER_SUCCESS) 10630ed80c8cSGeorge Liu { 10640ed80c8cSGeorge Liu // handle error 106562598e31SEd Tanous BMCWEB_LOG_ERROR("MIME parse failed, ec : {}", 106662598e31SEd Tanous static_cast<int>(ec)); 10670ed80c8cSGeorge Liu messages::internalError(asyncResp->res); 10680ed80c8cSGeorge Liu return; 10690ed80c8cSGeorge Liu } 10706b54e4e0SEd Tanous 1071ef93eab3SJagpal Singh Gill updateMultipartContext(asyncResp, req, std::move(parser)); 1072c2051d11SEd Tanous } 1073b33a4327SNinad Palsule else 1074b33a4327SNinad Palsule { 107562598e31SEd Tanous BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType); 1076b33a4327SNinad Palsule asyncResp->res.result(boost::beast::http::status::bad_request); 1077b33a4327SNinad Palsule } 1078b33a4327SNinad Palsule } 1079c2051d11SEd Tanous 1080f5139334SEd Tanous inline void 1081f5139334SEd Tanous handleUpdateServiceGet(App& app, const crow::Request& req, 1082f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 10831abe55efSEd Tanous { 10843ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 108545ca1b86SEd Tanous { 108645ca1b86SEd Tanous return; 108745ca1b86SEd Tanous } 10888d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 10890ed80c8cSGeorge Liu "#UpdateService.v1_11_1.UpdateService"; 10908d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService"; 10918d1b46d7Szhanghch05 asyncResp->res.jsonValue["Id"] = "UpdateService"; 1092002d39b4SEd Tanous asyncResp->res.jsonValue["Description"] = "Service for Software Update"; 10938d1b46d7Szhanghch05 asyncResp->res.jsonValue["Name"] = "Update Service"; 10944dc23f3fSEd Tanous 10957e860f15SJohn Edward Broadbent asyncResp->res.jsonValue["HttpPushUri"] = 10964dc23f3fSEd Tanous "/redfish/v1/UpdateService/update"; 10970ed80c8cSGeorge Liu asyncResp->res.jsonValue["MultipartHttpPushUri"] = 10980ed80c8cSGeorge Liu "/redfish/v1/UpdateService/update"; 10994dc23f3fSEd Tanous 11000f74e643SEd Tanous // UpdateService cannot be disabled 11018d1b46d7Szhanghch05 asyncResp->res.jsonValue["ServiceEnabled"] = true; 11021476687dSEd Tanous asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] = 11031476687dSEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1104d61e5194STejas Patil // Get the MaxImageSizeBytes 1105bd79bce8SPatrick Williams asyncResp->res.jsonValue["MaxImageSizeBytes"] = 1106bd79bce8SPatrick Williams BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024; 1107d61e5194STejas Patil 11080554c984SAndrew Geissler // Update Actions object. 11090554c984SAndrew Geissler nlohmann::json& updateSvcSimpleUpdate = 1110002d39b4SEd Tanous asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"]; 11110554c984SAndrew Geissler updateSvcSimpleUpdate["target"] = 11120554c984SAndrew Geissler "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate"; 1113757178a5SEd Tanous 1114757178a5SEd Tanous nlohmann::json::array_t allowed; 1115e5cf777eSEd Tanous allowed.emplace_back(update_service::TransferProtocolType::HTTPS); 1116757178a5SEd Tanous 111725b54dbaSEd Tanous if constexpr (BMCWEB_INSECURE_PUSH_STYLE_NOTIFICATION) 111825b54dbaSEd Tanous { 1119757178a5SEd Tanous allowed.emplace_back(update_service::TransferProtocolType::TFTP); 112025b54dbaSEd Tanous } 1121757178a5SEd Tanous 1122757178a5SEd Tanous updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = 1123757178a5SEd Tanous std::move(allowed); 1124757178a5SEd Tanous 1125539d8c6bSEd Tanous asyncResp->res 1126539d8c6bSEd Tanous .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] = 1127539d8c6bSEd Tanous update_service::ApplyTime::Immediate; 1128729dae72SJennifer Lee } 1129729dae72SJennifer Lee 1130f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryCollectionGet( 1131f5139334SEd Tanous App& app, const crow::Request& req, 1132f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 11331abe55efSEd Tanous { 11343ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 113545ca1b86SEd Tanous { 113645ca1b86SEd Tanous return; 113745ca1b86SEd Tanous } 11388d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 11390f74e643SEd Tanous "#SoftwareInventoryCollection.SoftwareInventoryCollection"; 11408d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = 11410f74e643SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1142002d39b4SEd Tanous asyncResp->res.jsonValue["Name"] = "Software Inventory Collection"; 114308d81adaSJohn Edward Broadbent const std::array<const std::string_view, 1> iface = { 1144e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 11456c4eb9deSJennifer Lee 114608d81adaSJohn Edward Broadbent redfish::collection_util::getCollectionMembers( 114708d81adaSJohn Edward Broadbent asyncResp, 1148f5139334SEd Tanous boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface, 1149f5139334SEd Tanous "/xyz/openbmc_project/software"); 1150729dae72SJennifer Lee } 1151f5139334SEd Tanous 115287d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */ 1153f5139334SEd Tanous inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 115487d84729SAndrew Geissler const std::string& purpose) 115587d84729SAndrew Geissler { 1156eee0013eSWilly Tu if (purpose == sw_util::bmcPurpose) 115787d84729SAndrew Geissler { 1158ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"]; 11591476687dSEd Tanous nlohmann::json::object_t item; 1160253f11b8SEd Tanous item["@odata.id"] = boost::urls::format( 1161253f11b8SEd Tanous "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); 1162b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item)); 1163ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] = 1164ac106bf6SEd Tanous relatedItem.size(); 116587d84729SAndrew Geissler } 1166eee0013eSWilly Tu else if (purpose == sw_util::biosPurpose) 116787d84729SAndrew Geissler { 1168ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"]; 11691476687dSEd Tanous nlohmann::json::object_t item; 1170253f11b8SEd Tanous item["@odata.id"] = std::format("/redfish/v1/Systems/{}/Bios", 1171253f11b8SEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 1172b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item)); 1173ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] = 1174ac106bf6SEd Tanous relatedItem.size(); 117587d84729SAndrew Geissler } 117687d84729SAndrew Geissler else 117787d84729SAndrew Geissler { 1178bf2ddedeSCarson Labrado BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose); 117987d84729SAndrew Geissler } 118087d84729SAndrew Geissler } 118187d84729SAndrew Geissler 1182af24660dSWilly Tu inline void 1183af24660dSWilly Tu getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1184af24660dSWilly Tu const std::string& service, const std::string& path, 1185af24660dSWilly Tu const std::string& swId) 1186af24660dSWilly Tu { 1187d1bde9e5SKrzysztof Grobelny sdbusplus::asio::getAllProperties( 1188d1bde9e5SKrzysztof Grobelny *crow::connections::systemBus, service, path, 1189d1bde9e5SKrzysztof Grobelny "xyz.openbmc_project.Software.Version", 1190af24660dSWilly Tu [asyncResp, 11918b24275dSEd Tanous swId](const boost::system::error_code& ec, 1192af24660dSWilly Tu const dbus::utility::DBusPropertiesMap& propertiesList) { 11938b24275dSEd Tanous if (ec) 1194af24660dSWilly Tu { 1195af24660dSWilly Tu messages::internalError(asyncResp->res); 1196af24660dSWilly Tu return; 1197af24660dSWilly Tu } 1198d1bde9e5SKrzysztof Grobelny 1199af24660dSWilly Tu const std::string* swInvPurpose = nullptr; 1200af24660dSWilly Tu const std::string* version = nullptr; 1201d1bde9e5SKrzysztof Grobelny 1202d1bde9e5SKrzysztof Grobelny const bool success = sdbusplus::unpackPropertiesNoThrow( 1203d1bde9e5SKrzysztof Grobelny dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose", 1204d1bde9e5SKrzysztof Grobelny swInvPurpose, "Version", version); 1205d1bde9e5SKrzysztof Grobelny 1206d1bde9e5SKrzysztof Grobelny if (!success) 1207af24660dSWilly Tu { 1208d1bde9e5SKrzysztof Grobelny messages::internalError(asyncResp->res); 1209d1bde9e5SKrzysztof Grobelny return; 1210af24660dSWilly Tu } 1211af24660dSWilly Tu 1212af24660dSWilly Tu if (swInvPurpose == nullptr) 1213af24660dSWilly Tu { 121462598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!"); 1215af24660dSWilly Tu messages::internalError(asyncResp->res); 1216af24660dSWilly Tu return; 1217af24660dSWilly Tu } 1218af24660dSWilly Tu 121962598e31SEd Tanous BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose); 1220af24660dSWilly Tu 1221af24660dSWilly Tu if (version == nullptr) 1222af24660dSWilly Tu { 122362598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Version\"!"); 1224af24660dSWilly Tu 1225af24660dSWilly Tu messages::internalError(asyncResp->res); 1226af24660dSWilly Tu 1227af24660dSWilly Tu return; 1228af24660dSWilly Tu } 1229af24660dSWilly Tu asyncResp->res.jsonValue["Version"] = *version; 1230af24660dSWilly Tu asyncResp->res.jsonValue["Id"] = swId; 1231af24660dSWilly Tu 1232af24660dSWilly Tu // swInvPurpose is of format: 1233af24660dSWilly Tu // xyz.openbmc_project.Software.Version.VersionPurpose.ABC 1234af24660dSWilly Tu // Translate this to "ABC image" 1235af24660dSWilly Tu size_t endDesc = swInvPurpose->rfind('.'); 1236af24660dSWilly Tu if (endDesc == std::string::npos) 1237af24660dSWilly Tu { 1238af24660dSWilly Tu messages::internalError(asyncResp->res); 1239af24660dSWilly Tu return; 1240af24660dSWilly Tu } 1241af24660dSWilly Tu endDesc++; 1242af24660dSWilly Tu if (endDesc >= swInvPurpose->size()) 1243af24660dSWilly Tu { 1244af24660dSWilly Tu messages::internalError(asyncResp->res); 1245af24660dSWilly Tu return; 1246af24660dSWilly Tu } 1247af24660dSWilly Tu 1248af24660dSWilly Tu std::string formatDesc = swInvPurpose->substr(endDesc); 1249af24660dSWilly Tu asyncResp->res.jsonValue["Description"] = formatDesc + " image"; 1250af24660dSWilly Tu getRelatedItems(asyncResp, *swInvPurpose); 1251d1bde9e5SKrzysztof Grobelny }); 1252af24660dSWilly Tu } 1253af24660dSWilly Tu 1254f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryGet( 1255f5139334SEd Tanous App& app, const crow::Request& req, 125645ca1b86SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1257f5139334SEd Tanous const std::string& param) 1258f5139334SEd Tanous { 12593ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 126045ca1b86SEd Tanous { 126145ca1b86SEd Tanous return; 126245ca1b86SEd Tanous } 1263f5139334SEd Tanous std::shared_ptr<std::string> swId = std::make_shared<std::string>(param); 1264c711bf86SEd Tanous 1265ef4c65b7SEd Tanous asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1266ef4c65b7SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId); 1267c711bf86SEd Tanous 1268e99073f5SGeorge Liu constexpr std::array<std::string_view, 1> interfaces = { 1269e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 1270e99073f5SGeorge Liu dbus::utility::getSubTree( 1271e99073f5SGeorge Liu "/", 0, interfaces, 1272b9d36b47SEd Tanous [asyncResp, 1273e99073f5SGeorge Liu swId](const boost::system::error_code& ec, 1274b9d36b47SEd Tanous const dbus::utility::MapperGetSubTreeResponse& subtree) { 127562598e31SEd Tanous BMCWEB_LOG_DEBUG("doGet callback..."); 12761abe55efSEd Tanous if (ec) 12771abe55efSEd Tanous { 1278f12894f8SJason M. Bills messages::internalError(asyncResp->res); 12796c4eb9deSJennifer Lee return; 12806c4eb9deSJennifer Lee } 12816c4eb9deSJennifer Lee 12826913228dSAndrew Geissler // Ensure we find our input swId, otherwise return an error 12836913228dSAndrew Geissler bool found = false; 1284bd79bce8SPatrick Williams for (const std::pair<std::string, 1285bd79bce8SPatrick Williams std::vector<std::pair< 1286bd79bce8SPatrick Williams std::string, std::vector<std::string>>>>& 1287002d39b4SEd Tanous obj : subtree) 12881abe55efSEd Tanous { 128911ba3979SEd Tanous if (!obj.first.ends_with(*swId)) 12901abe55efSEd Tanous { 1291acb7cfb4SJennifer Lee continue; 1292acb7cfb4SJennifer Lee } 1293acb7cfb4SJennifer Lee 129426f6976fSEd Tanous if (obj.second.empty()) 12951abe55efSEd Tanous { 1296acb7cfb4SJennifer Lee continue; 1297acb7cfb4SJennifer Lee } 12986c4eb9deSJennifer Lee 12996913228dSAndrew Geissler found = true; 1300eee0013eSWilly Tu sw_util::getSwStatus(asyncResp, swId, obj.second[0].first); 1301af24660dSWilly Tu getSoftwareVersion(asyncResp, obj.second[0].first, obj.first, 1302af24660dSWilly Tu *swId); 13036c4eb9deSJennifer Lee } 13046913228dSAndrew Geissler if (!found) 13056913228dSAndrew Geissler { 130662598e31SEd Tanous BMCWEB_LOG_WARNING("Input swID {} not found!", *swId); 13076913228dSAndrew Geissler messages::resourceMissingAtURI( 1308ef4c65b7SEd Tanous asyncResp->res, 1309ef4c65b7SEd Tanous boost::urls::format( 1310bd79bce8SPatrick Williams "/redfish/v1/UpdateService/FirmwareInventory/{}", 1311bd79bce8SPatrick Williams *swId)); 13126913228dSAndrew Geissler return; 13136913228dSAndrew Geissler } 13144e68c45bSAyushi Smriti asyncResp->res.jsonValue["@odata.type"] = 13154e68c45bSAyushi Smriti "#SoftwareInventory.v1_1_0.SoftwareInventory"; 13164e68c45bSAyushi Smriti asyncResp->res.jsonValue["Name"] = "Software Inventory"; 1317539d8c6bSEd Tanous asyncResp->res.jsonValue["Status"]["HealthRollup"] = 1318539d8c6bSEd Tanous resource::Health::OK; 13193f8a743aSAppaRao Puli 13203f8a743aSAppaRao Puli asyncResp->res.jsonValue["Updateable"] = false; 1321eee0013eSWilly Tu sw_util::getSwUpdatableStatus(asyncResp, swId); 1322e99073f5SGeorge Liu }); 1323f5139334SEd Tanous } 1324f5139334SEd Tanous 1325f5139334SEd Tanous inline void requestRoutesUpdateService(App& app) 1326f5139334SEd Tanous { 1327f5139334SEd Tanous BMCWEB_ROUTE( 1328f5139334SEd Tanous app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/") 1329f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1330f5139334SEd Tanous .methods(boost::beast::http::verb::post)(std::bind_front( 1331f5139334SEd Tanous handleUpdateServiceSimpleUpdateAction, std::ref(app))); 1332f5139334SEd Tanous 1333f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/") 1334f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventory) 1335f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1336f5139334SEd Tanous handleUpdateServiceFirmwareInventoryGet, std::ref(app))); 1337f5139334SEd Tanous 1338f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/") 1339f5139334SEd Tanous .privileges(redfish::privileges::getUpdateService) 1340f5139334SEd Tanous .methods(boost::beast::http::verb::get)( 1341f5139334SEd Tanous std::bind_front(handleUpdateServiceGet, std::ref(app))); 1342f5139334SEd Tanous 1343f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/") 1344f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1345f5139334SEd Tanous .methods(boost::beast::http::verb::post)( 1346f5139334SEd Tanous std::bind_front(handleUpdateServicePost, std::ref(app))); 1347f5139334SEd Tanous 1348f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/") 1349f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventoryCollection) 1350f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1351f5139334SEd Tanous handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app))); 13526c4eb9deSJennifer Lee } 1353729dae72SJennifer Lee 1354729dae72SJennifer Lee } // namespace redfish 1355