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, 33948fb20b9SEd 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 { 34448fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3458549b951SMyung Bae } 3468549b951SMyung Bae else if (type == 3478549b951SMyung Bae "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure") 3488549b951SMyung Bae { 34948fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res); 3508549b951SMyung Bae } 3518549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure") 3528549b951SMyung Bae { 35348fb20b9SEd 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 { 36248fb20b9SEd 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 { 37648fb20b9SEd 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); 38348fb20b9SEd 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 } 500*6a37140aSEd Tanous // OpenBMC currently only supports HTTPS 501*6a37140aSEd Tanous if (*transferProtocol == "HTTPS") 502e5cf777eSEd Tanous { 503e5cf777eSEd Tanous imageURI = "https://" + imageURI; 504e5cf777eSEd Tanous } 505757178a5SEd Tanous else 506f86bcc87SEd Tanous { 507f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "TransferProtocol", 508f86bcc87SEd Tanous *transferProtocol); 509f86bcc87SEd Tanous BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}", 510f86bcc87SEd Tanous *transferProtocol); 511f86bcc87SEd Tanous return std::nullopt; 512f86bcc87SEd Tanous } 513f86bcc87SEd Tanous } 514f86bcc87SEd Tanous 515f86bcc87SEd Tanous boost::system::result<boost::urls::url> url = 516f86bcc87SEd Tanous boost::urls::parse_absolute_uri(imageURI); 517f86bcc87SEd Tanous if (!url) 518f86bcc87SEd Tanous { 519f86bcc87SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI", 520f86bcc87SEd Tanous "UpdateService.SimpleUpdate"); 521f86bcc87SEd Tanous 522f86bcc87SEd Tanous return std::nullopt; 523f86bcc87SEd Tanous } 524f86bcc87SEd Tanous url->normalize(); 525f86bcc87SEd Tanous 526757178a5SEd Tanous if (url->scheme() == "tftp") 527757178a5SEd Tanous { 528757178a5SEd Tanous if (url->encoded_path().size() < 2) 529757178a5SEd Tanous { 530757178a5SEd Tanous messages::actionParameterNotSupported(res, "ImageURI", 531757178a5SEd Tanous url->buffer()); 532757178a5SEd Tanous return std::nullopt; 533757178a5SEd Tanous } 534757178a5SEd Tanous } 535e5cf777eSEd Tanous else if (url->scheme() == "https") 536e5cf777eSEd Tanous { 537e5cf777eSEd Tanous // Empty paths default to "/" 538e5cf777eSEd Tanous if (url->encoded_path().empty()) 539e5cf777eSEd Tanous { 540e5cf777eSEd Tanous url->set_encoded_path("/"); 541e5cf777eSEd Tanous } 542e5cf777eSEd Tanous } 543757178a5SEd Tanous else 544f86bcc87SEd Tanous { 545f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "ImageURI", imageURI); 546f86bcc87SEd Tanous return std::nullopt; 547f86bcc87SEd Tanous } 548757178a5SEd Tanous 549757178a5SEd Tanous if (url->encoded_path().empty()) 550f86bcc87SEd Tanous { 551757178a5SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI", 552757178a5SEd Tanous "UpdateService.SimpleUpdate"); 553f86bcc87SEd Tanous return std::nullopt; 554f86bcc87SEd Tanous } 555757178a5SEd Tanous 556757178a5SEd Tanous return *url; 557f86bcc87SEd Tanous } 558f86bcc87SEd Tanous 559e5cf777eSEd Tanous inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 560e5cf777eSEd Tanous const boost::urls::url_view_base& url) 561e5cf777eSEd Tanous { 562e5cf777eSEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI", 563e5cf777eSEd Tanous url.buffer()); 564e5cf777eSEd Tanous } 565e5cf777eSEd Tanous 566f5139334SEd Tanous inline void handleUpdateServiceSimpleUpdateAction( 567f5139334SEd Tanous crow::App& app, const crow::Request& req, 568f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 5690554c984SAndrew Geissler { 5703ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 57145ca1b86SEd Tanous { 57245ca1b86SEd Tanous return; 57345ca1b86SEd Tanous } 57445ca1b86SEd Tanous 5750554c984SAndrew Geissler std::optional<std::string> transferProtocol; 5760554c984SAndrew Geissler std::string imageURI; 5770554c984SAndrew Geissler 57862598e31SEd Tanous BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost"); 5790554c984SAndrew Geissler 5800554c984SAndrew Geissler // User can pass in both TransferProtocol and ImageURI parameters or 5814e0453b1SGunnar Mills // they can pass in just the ImageURI with the transfer protocol 5824e0453b1SGunnar Mills // embedded within it. 5830554c984SAndrew Geissler // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin 5840554c984SAndrew Geissler // 2) ImageURI:tftp://1.1.1.1/myfile.bin 5850554c984SAndrew Geissler 586afc474aeSMyung Bae if (!json_util::readJsonAction( // 587afc474aeSMyung Bae req, asyncResp->res, // 588afc474aeSMyung Bae "ImageURI", imageURI, // 589afc474aeSMyung Bae "TransferProtocol", transferProtocol // 590afc474aeSMyung Bae )) 5910554c984SAndrew Geissler { 59262598e31SEd Tanous BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter"); 5930554c984SAndrew Geissler return; 5940554c984SAndrew Geissler } 595f5139334SEd Tanous 596757178a5SEd Tanous std::optional<boost::urls::url> url = 597757178a5SEd Tanous parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res); 598757178a5SEd Tanous if (!url) 5990554c984SAndrew Geissler { 6000554c984SAndrew Geissler return; 6010554c984SAndrew Geissler } 6024e338b23SJagpal Singh Gill if (url->scheme() == "https") 603e5cf777eSEd Tanous { 604e5cf777eSEd Tanous doHttpsUpdate(asyncResp, *url); 605e5cf777eSEd Tanous } 606757178a5SEd Tanous else 607757178a5SEd Tanous { 608757178a5SEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI", 609757178a5SEd Tanous url->buffer()); 610757178a5SEd Tanous return; 611757178a5SEd Tanous } 6120554c984SAndrew Geissler 61362598e31SEd Tanous BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost"); 614729dae72SJennifer Lee } 615729dae72SJennifer Lee 6160ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body) 6170ed80c8cSGeorge Liu { 6182c6ffdb0SEd Tanous std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID()); 6192c6ffdb0SEd Tanous 62062598e31SEd Tanous BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string()); 6210ed80c8cSGeorge Liu std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | 6220ed80c8cSGeorge Liu std::ofstream::trunc); 6230ed80c8cSGeorge Liu // set the permission of the file to 640 624bd79bce8SPatrick Williams std::filesystem::perms permission = 625bd79bce8SPatrick Williams std::filesystem::perms::owner_read | std::filesystem::perms::group_read; 6260ed80c8cSGeorge Liu std::filesystem::permissions(filepath, permission); 6270ed80c8cSGeorge Liu out << body; 6280ed80c8cSGeorge Liu 6290ed80c8cSGeorge Liu if (out.bad()) 6300ed80c8cSGeorge Liu { 6310ed80c8cSGeorge Liu messages::internalError(res); 6320ed80c8cSGeorge Liu cleanUp(); 6330ed80c8cSGeorge Liu } 6340ed80c8cSGeorge Liu } 6350ed80c8cSGeorge Liu 636de0c960cSJagpal Singh Gill // Convert the Request Apply Time to the D-Bus value 637de0c960cSJagpal Singh Gill inline bool convertApplyTime(crow::Response& res, const std::string& applyTime, 638de0c960cSJagpal Singh Gill std::string& applyTimeNewVal) 639de0c960cSJagpal Singh Gill { 640de0c960cSJagpal Singh Gill if (applyTime == "Immediate") 641de0c960cSJagpal Singh Gill { 642de0c960cSJagpal Singh Gill applyTimeNewVal = 643049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate"; 644de0c960cSJagpal Singh Gill } 645de0c960cSJagpal Singh Gill else if (applyTime == "OnReset") 646de0c960cSJagpal Singh Gill { 647de0c960cSJagpal Singh Gill applyTimeNewVal = 648049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset"; 649de0c960cSJagpal Singh Gill } 650de0c960cSJagpal Singh Gill else 651de0c960cSJagpal Singh Gill { 652de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING( 653de0c960cSJagpal Singh Gill "ApplyTime value {} is not in the list of acceptable values", 654de0c960cSJagpal Singh Gill applyTime); 655de0c960cSJagpal Singh Gill messages::propertyValueNotInList(res, applyTime, "ApplyTime"); 656de0c960cSJagpal Singh Gill return false; 657de0c960cSJagpal Singh Gill } 658de0c960cSJagpal Singh Gill return true; 659de0c960cSJagpal Singh Gill } 660de0c960cSJagpal Singh Gill 6610ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 6620ed80c8cSGeorge Liu const std::string& applyTime) 6630ed80c8cSGeorge Liu { 6640ed80c8cSGeorge Liu std::string applyTimeNewVal; 665049079f6SJagpal Singh Gill if (!convertApplyTime(asyncResp->res, applyTime, applyTimeNewVal)) 6660ed80c8cSGeorge Liu { 6670ed80c8cSGeorge Liu return; 6680ed80c8cSGeorge Liu } 6690ed80c8cSGeorge Liu 670e93abac6SGinu George setDbusProperty(asyncResp, "ApplyTime", "xyz.openbmc_project.Settings", 671d02aad39SEd Tanous sdbusplus::message::object_path( 672d02aad39SEd Tanous "/xyz/openbmc_project/software/apply_time"), 673d02aad39SEd Tanous "xyz.openbmc_project.Software.ApplyTime", 674e93abac6SGinu George "RequestedApplyTime", applyTimeNewVal); 6750ed80c8cSGeorge Liu } 6760ed80c8cSGeorge Liu 677ef93eab3SJagpal Singh Gill struct MultiPartUpdateParameters 6780ed80c8cSGeorge Liu { 679ef93eab3SJagpal Singh Gill std::optional<std::string> applyTime; 680ef93eab3SJagpal Singh Gill std::string uploadData; 681de0c960cSJagpal Singh Gill std::vector<std::string> targets; 682ef93eab3SJagpal Singh Gill }; 683ef93eab3SJagpal Singh Gill 684de0c960cSJagpal Singh Gill inline std::optional<std::string> 685de0c960cSJagpal Singh Gill processUrl(boost::system::result<boost::urls::url_view>& url) 686de0c960cSJagpal Singh Gill { 687de0c960cSJagpal Singh Gill if (!url) 688de0c960cSJagpal Singh Gill { 689de0c960cSJagpal Singh Gill return std::nullopt; 690de0c960cSJagpal Singh Gill } 691de0c960cSJagpal Singh Gill if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers", 692de0c960cSJagpal Singh Gill BMCWEB_REDFISH_MANAGER_URI_NAME)) 693de0c960cSJagpal Singh Gill { 694de0c960cSJagpal Singh Gill return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME)); 695de0c960cSJagpal Singh Gill } 696de0c960cSJagpal Singh Gill if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 697de0c960cSJagpal Singh Gill { 698de0c960cSJagpal Singh Gill return std::nullopt; 699de0c960cSJagpal Singh Gill } 700de0c960cSJagpal Singh Gill std::string firmwareId; 701de0c960cSJagpal Singh Gill if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService", 702de0c960cSJagpal Singh Gill "FirmwareInventory", 703de0c960cSJagpal Singh Gill std::ref(firmwareId))) 704de0c960cSJagpal Singh Gill { 705de0c960cSJagpal Singh Gill return std::nullopt; 706de0c960cSJagpal Singh Gill } 707de0c960cSJagpal Singh Gill 708de0c960cSJagpal Singh Gill return std::make_optional(firmwareId); 709de0c960cSJagpal Singh Gill } 710de0c960cSJagpal Singh Gill 711ef93eab3SJagpal Singh Gill inline std::optional<MultiPartUpdateParameters> 712ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters( 713ef93eab3SJagpal Singh Gill const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 714ef93eab3SJagpal Singh Gill MultipartParser parser) 715ef93eab3SJagpal Singh Gill { 716ef93eab3SJagpal Singh Gill MultiPartUpdateParameters multiRet; 717ef93eab3SJagpal Singh Gill for (FormPart& formpart : parser.mime_fields) 7180ed80c8cSGeorge Liu { 7190ed80c8cSGeorge Liu boost::beast::http::fields::const_iterator it = 7200ed80c8cSGeorge Liu formpart.fields.find("Content-Disposition"); 7210ed80c8cSGeorge Liu if (it == formpart.fields.end()) 7220ed80c8cSGeorge Liu { 72362598e31SEd Tanous BMCWEB_LOG_ERROR("Couldn't find Content-Disposition"); 724ef93eab3SJagpal Singh Gill return std::nullopt; 7250ed80c8cSGeorge Liu } 72662598e31SEd Tanous BMCWEB_LOG_INFO("Parsing value {}", it->value()); 7270ed80c8cSGeorge Liu 7280ed80c8cSGeorge Liu // The construction parameters of param_list must start with `;` 7290ed80c8cSGeorge Liu size_t index = it->value().find(';'); 7300ed80c8cSGeorge Liu if (index == std::string::npos) 7310ed80c8cSGeorge Liu { 7320ed80c8cSGeorge Liu continue; 7330ed80c8cSGeorge Liu } 7340ed80c8cSGeorge Liu 73589492a15SPatrick Williams for (const auto& param : 7360ed80c8cSGeorge Liu boost::beast::http::param_list{it->value().substr(index)}) 7370ed80c8cSGeorge Liu { 7380ed80c8cSGeorge Liu if (param.first != "name" || param.second.empty()) 7390ed80c8cSGeorge Liu { 7400ed80c8cSGeorge Liu continue; 7410ed80c8cSGeorge Liu } 7420ed80c8cSGeorge Liu 7430ed80c8cSGeorge Liu if (param.second == "UpdateParameters") 7440ed80c8cSGeorge Liu { 745ef93eab3SJagpal Singh Gill std::vector<std::string> tempTargets; 746bd79bce8SPatrick Williams nlohmann::json content = 747bd79bce8SPatrick Williams nlohmann::json::parse(formpart.content, nullptr, false); 748ac1e1246SEd Tanous if (content.is_discarded()) 749ac1e1246SEd Tanous { 750ac1e1246SEd Tanous return std::nullopt; 751ac1e1246SEd Tanous } 7527cb59f65SEd Tanous nlohmann::json::object_t* obj = 7537cb59f65SEd Tanous content.get_ptr<nlohmann::json::object_t*>(); 7547cb59f65SEd Tanous if (obj == nullptr) 7557cb59f65SEd Tanous { 756ef93eab3SJagpal Singh Gill messages::propertyValueTypeError( 757ef93eab3SJagpal Singh Gill asyncResp->res, formpart.content, "UpdateParameters"); 758ef93eab3SJagpal Singh Gill return std::nullopt; 7597cb59f65SEd Tanous } 7607cb59f65SEd Tanous 761afc474aeSMyung Bae if (!json_util::readJsonObject( // 762afc474aeSMyung Bae *obj, asyncResp->res, // 763afc474aeSMyung Bae "@Redfish.OperationApplyTime", multiRet.applyTime, // 764afc474aeSMyung Bae "Targets", tempTargets // 765afc474aeSMyung Bae )) 7660ed80c8cSGeorge Liu { 767ef93eab3SJagpal Singh Gill return std::nullopt; 7680ed80c8cSGeorge Liu } 769ef93eab3SJagpal Singh Gill 770ef93eab3SJagpal Singh Gill for (size_t urlIndex = 0; urlIndex < tempTargets.size(); 771ef93eab3SJagpal Singh Gill urlIndex++) 7720ed80c8cSGeorge Liu { 773ef93eab3SJagpal Singh Gill const std::string& target = tempTargets[urlIndex]; 774ef93eab3SJagpal Singh Gill boost::system::result<boost::urls::url_view> url = 775ef93eab3SJagpal Singh Gill boost::urls::parse_origin_form(target); 776de0c960cSJagpal Singh Gill auto res = processUrl(url); 777de0c960cSJagpal Singh Gill if (!res.has_value()) 7780ed80c8cSGeorge Liu { 779ef93eab3SJagpal Singh Gill messages::propertyValueFormatError( 780ef93eab3SJagpal Singh Gill asyncResp->res, target, 781ef93eab3SJagpal Singh Gill std::format("Targets/{}", urlIndex)); 782ef93eab3SJagpal Singh Gill return std::nullopt; 7830ed80c8cSGeorge Liu } 784de0c960cSJagpal Singh Gill multiRet.targets.emplace_back(res.value()); 785ef93eab3SJagpal Singh Gill } 786ef93eab3SJagpal Singh Gill if (multiRet.targets.size() != 1) 787ef93eab3SJagpal Singh Gill { 788ef93eab3SJagpal Singh Gill messages::propertyValueFormatError( 789ef93eab3SJagpal Singh Gill asyncResp->res, multiRet.targets, "Targets"); 790ef93eab3SJagpal Singh Gill return std::nullopt; 791ef93eab3SJagpal Singh Gill } 7920ed80c8cSGeorge Liu } 7930ed80c8cSGeorge Liu else if (param.second == "UpdateFile") 7940ed80c8cSGeorge Liu { 795ef93eab3SJagpal Singh Gill multiRet.uploadData = std::move(formpart.content); 7960ed80c8cSGeorge Liu } 7970ed80c8cSGeorge Liu } 7980ed80c8cSGeorge Liu } 7990ed80c8cSGeorge Liu 800ef93eab3SJagpal Singh Gill if (multiRet.uploadData.empty()) 8010ed80c8cSGeorge Liu { 80262598e31SEd Tanous BMCWEB_LOG_ERROR("Upload data is NULL"); 8030ed80c8cSGeorge Liu messages::propertyMissing(asyncResp->res, "UpdateFile"); 804ef93eab3SJagpal Singh Gill return std::nullopt; 8050ed80c8cSGeorge Liu } 806ef93eab3SJagpal Singh Gill if (multiRet.targets.empty()) 8070ed80c8cSGeorge Liu { 808ef93eab3SJagpal Singh Gill messages::propertyMissing(asyncResp->res, "Targets"); 809ef93eab3SJagpal Singh Gill return std::nullopt; 810ef93eab3SJagpal Singh Gill } 811ef93eab3SJagpal Singh Gill return multiRet; 8120ed80c8cSGeorge Liu } 8130ed80c8cSGeorge Liu 814bd79bce8SPatrick Williams inline void handleStartUpdate( 815bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 816bd79bce8SPatrick Williams const std::string& objectPath, const boost::system::error_code& ec, 817de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) 818de0c960cSJagpal Singh Gill { 819de0c960cSJagpal Singh Gill if (ec) 820de0c960cSJagpal Singh Gill { 821de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 822de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 823de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 824de0c960cSJagpal Singh Gill return; 825de0c960cSJagpal Singh Gill } 826de0c960cSJagpal Singh Gill 827587090cdSJagpal Singh Gill BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}", 828587090cdSJagpal Singh Gill objectPath, retPath.str); 829587090cdSJagpal Singh Gill createTask(asyncResp, std::move(payload), retPath); 830de0c960cSJagpal Singh Gill } 831de0c960cSJagpal Singh Gill 832bd79bce8SPatrick Williams inline void startUpdate( 833bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 834bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 835bd79bce8SPatrick Williams const std::string& objectPath, const std::string& serviceName) 836de0c960cSJagpal Singh Gill { 837de0c960cSJagpal Singh Gill crow::connections::systemBus->async_method_call( 838de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), 839de0c960cSJagpal Singh Gill objectPath](const boost::system::error_code& ec1, 840de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) mutable { 841de0c960cSJagpal Singh Gill handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1, 842de0c960cSJagpal Singh Gill retPath); 843de0c960cSJagpal Singh Gill }, 844de0c960cSJagpal Singh Gill serviceName, objectPath, "xyz.openbmc_project.Software.Update", 845de0c960cSJagpal Singh Gill "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime); 846de0c960cSJagpal Singh Gill } 847de0c960cSJagpal Singh Gill 84808f61d53SJagpal Singh Gill inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 84908f61d53SJagpal Singh Gill task::Payload payload, const MemoryFileDescriptor& memfd, 85008f61d53SJagpal Singh Gill const std::string& applyTime, const std::string& target, 851de0c960cSJagpal Singh Gill const boost::system::error_code& ec, 852de0c960cSJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& subtree) 853de0c960cSJagpal Singh Gill { 85408f61d53SJagpal Singh Gill using SwInfoMap = std::unordered_map< 85508f61d53SJagpal Singh Gill std::string, std::pair<sdbusplus::message::object_path, std::string>>; 856de0c960cSJagpal Singh Gill SwInfoMap swInfoMap; 857de0c960cSJagpal Singh Gill 858de0c960cSJagpal Singh Gill if (ec) 859de0c960cSJagpal Singh Gill { 860de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 861de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 862de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 863de0c960cSJagpal Singh Gill return; 864de0c960cSJagpal Singh Gill } 865de0c960cSJagpal Singh Gill BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size()); 866de0c960cSJagpal Singh Gill 86708f61d53SJagpal Singh Gill for (const auto& entry : subtree) 868de0c960cSJagpal Singh Gill { 86908f61d53SJagpal Singh Gill sdbusplus::message::object_path path(entry.first); 870de0c960cSJagpal Singh Gill std::string swId = path.filename(); 87108f61d53SJagpal Singh Gill swInfoMap.emplace(swId, make_pair(path, entry.second[0].first)); 872de0c960cSJagpal Singh Gill } 873de0c960cSJagpal Singh Gill 874de0c960cSJagpal Singh Gill auto swEntry = swInfoMap.find(target); 875de0c960cSJagpal Singh Gill if (swEntry == swInfoMap.end()) 876de0c960cSJagpal Singh Gill { 877de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target); 878de0c960cSJagpal Singh Gill messages::propertyValueFormatError(asyncResp->res, target, "Targets"); 879de0c960cSJagpal Singh Gill return; 880de0c960cSJagpal Singh Gill } 881de0c960cSJagpal Singh Gill 88208f61d53SJagpal Singh Gill BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}", 88308f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 884de0c960cSJagpal Singh Gill 88508f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 88608f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 88708f61d53SJagpal Singh Gill } 88808f61d53SJagpal Singh Gill 889bd79bce8SPatrick Williams inline void handleBMCUpdate( 890bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 891bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 89208f61d53SJagpal Singh Gill const boost::system::error_code& ec, 89308f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& functionalSoftware) 89408f61d53SJagpal Singh Gill { 89508f61d53SJagpal Singh Gill if (ec) 89608f61d53SJagpal Singh Gill { 89708f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 89808f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 89908f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 90008f61d53SJagpal Singh Gill return; 90108f61d53SJagpal Singh Gill } 90208f61d53SJagpal Singh Gill if (functionalSoftware.size() != 1) 90308f61d53SJagpal Singh Gill { 90408f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("Found {} functional software endpoints", 90508f61d53SJagpal Singh Gill functionalSoftware.size()); 90608f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 90708f61d53SJagpal Singh Gill return; 90808f61d53SJagpal Singh Gill } 90908f61d53SJagpal Singh Gill 91008f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 91108f61d53SJagpal Singh Gill functionalSoftware[0], "xyz.openbmc_project.Software.Manager"); 912de0c960cSJagpal Singh Gill } 913de0c960cSJagpal Singh Gill 914bd79bce8SPatrick Williams inline void processUpdateRequest( 915bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 9169dae4deeSJagpal Singh Gill task::Payload&& payload, std::string_view body, 917bd79bce8SPatrick Williams const std::string& applyTime, std::vector<std::string>& targets) 918de0c960cSJagpal Singh Gill { 919de0c960cSJagpal Singh Gill MemoryFileDescriptor memfd("update-image"); 920de0c960cSJagpal Singh Gill if (memfd.fd == -1) 921de0c960cSJagpal Singh Gill { 922de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to create image memfd"); 923de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 924de0c960cSJagpal Singh Gill return; 925de0c960cSJagpal Singh Gill } 926de0c960cSJagpal Singh Gill if (write(memfd.fd, body.data(), body.length()) != 927de0c960cSJagpal Singh Gill static_cast<ssize_t>(body.length())) 928de0c960cSJagpal Singh Gill { 929de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to write to image memfd"); 930de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 931de0c960cSJagpal Singh Gill return; 932de0c960cSJagpal Singh Gill } 933de0c960cSJagpal Singh Gill if (!memfd.rewind()) 934de0c960cSJagpal Singh Gill { 935de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 936de0c960cSJagpal Singh Gill return; 937de0c960cSJagpal Singh Gill } 938de0c960cSJagpal Singh Gill 939de0c960cSJagpal Singh Gill if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME) 940de0c960cSJagpal Singh Gill { 94108f61d53SJagpal Singh Gill dbus::utility::getAssociationEndPoints( 94289449bbeSJagpal Singh Gill "/xyz/openbmc_project/software/bmc/updateable", 94308f61d53SJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 94408f61d53SJagpal Singh Gill applyTime]( 94508f61d53SJagpal Singh Gill const boost::system::error_code& ec, 94608f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& objectPaths) mutable { 947bd79bce8SPatrick Williams handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime, 948bd79bce8SPatrick Williams ec, objectPaths); 94908f61d53SJagpal Singh Gill }); 950de0c960cSJagpal Singh Gill } 951de0c960cSJagpal Singh Gill else 952de0c960cSJagpal Singh Gill { 953de0c960cSJagpal Singh Gill constexpr std::array<std::string_view, 1> interfaces = { 954de0c960cSJagpal Singh Gill "xyz.openbmc_project.Software.Version"}; 95508f61d53SJagpal Singh Gill dbus::utility::getSubTree( 956de0c960cSJagpal Singh Gill "/xyz/openbmc_project/software", 1, interfaces, 957de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 95808f61d53SJagpal Singh Gill applyTime, targets](const boost::system::error_code& ec, 95908f61d53SJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& 960de0c960cSJagpal Singh Gill subtree) mutable { 96108f61d53SJagpal Singh Gill getSwInfo(asyncResp, std::move(payload), memfd, applyTime, 96208f61d53SJagpal Singh Gill targets[0], ec, subtree); 963de0c960cSJagpal Singh Gill }); 964de0c960cSJagpal Singh Gill } 965de0c960cSJagpal Singh Gill } 966de0c960cSJagpal Singh Gill 967de0c960cSJagpal Singh Gill inline void 968ef93eab3SJagpal Singh Gill updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 969ef93eab3SJagpal Singh Gill const crow::Request& req, MultipartParser&& parser) 970ef93eab3SJagpal Singh Gill { 971ef93eab3SJagpal Singh Gill std::optional<MultiPartUpdateParameters> multipart = 972ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters(asyncResp, std::move(parser)); 973ef93eab3SJagpal Singh Gill if (!multipart) 974ef93eab3SJagpal Singh Gill { 975ef93eab3SJagpal Singh Gill return; 976ef93eab3SJagpal Singh Gill } 977ef93eab3SJagpal Singh Gill if (!multipart->applyTime) 978ef93eab3SJagpal Singh Gill { 979ef93eab3SJagpal Singh Gill multipart->applyTime = "OnReset"; 980ef93eab3SJagpal Singh Gill } 981ef93eab3SJagpal Singh Gill 982de0c960cSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 983de0c960cSJagpal Singh Gill { 9849dae4deeSJagpal Singh Gill std::string applyTimeNewVal; 9859dae4deeSJagpal Singh Gill if (!convertApplyTime(asyncResp->res, *multipart->applyTime, 9869dae4deeSJagpal Singh Gill applyTimeNewVal)) 9879dae4deeSJagpal Singh Gill { 9889dae4deeSJagpal Singh Gill return; 9899dae4deeSJagpal Singh Gill } 9909dae4deeSJagpal Singh Gill task::Payload payload(req); 9919dae4deeSJagpal Singh Gill 9929dae4deeSJagpal Singh Gill processUpdateRequest(asyncResp, std::move(payload), 9939dae4deeSJagpal Singh Gill multipart->uploadData, applyTimeNewVal, 9949dae4deeSJagpal Singh Gill multipart->targets); 995de0c960cSJagpal Singh Gill } 996de0c960cSJagpal Singh Gill else 997de0c960cSJagpal Singh Gill { 998ef93eab3SJagpal Singh Gill setApplyTime(asyncResp, *multipart->applyTime); 9990ed80c8cSGeorge Liu 10006b54e4e0SEd Tanous // Setup callback for when new software detected 1001de0c960cSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 1002de0c960cSJagpal Singh Gill "/redfish/v1/UpdateService"); 10036b54e4e0SEd Tanous 1004ef93eab3SJagpal Singh Gill uploadImageFile(asyncResp->res, multipart->uploadData); 10050ed80c8cSGeorge Liu } 1006de0c960cSJagpal Singh Gill } 10070ed80c8cSGeorge Liu 10089dae4deeSJagpal Singh Gill inline void doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 10099dae4deeSJagpal Singh Gill const crow::Request& req) 10109dae4deeSJagpal Singh Gill { 10119dae4deeSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 10129dae4deeSJagpal Singh Gill { 10139dae4deeSJagpal Singh Gill task::Payload payload(req); 10149dae4deeSJagpal Singh Gill // HTTP push only supports BMC updates (with ApplyTime as immediate) for 10159dae4deeSJagpal Singh Gill // backwards compatibility. Specific component updates will be handled 10169dae4deeSJagpal Singh Gill // through Multipart form HTTP push. 10179dae4deeSJagpal Singh Gill std::vector<std::string> targets; 10189dae4deeSJagpal Singh Gill targets.emplace_back(BMCWEB_REDFISH_MANAGER_URI_NAME); 10199dae4deeSJagpal Singh Gill 10209dae4deeSJagpal Singh Gill processUpdateRequest( 10219dae4deeSJagpal Singh Gill asyncResp, std::move(payload), req.body(), 10229dae4deeSJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate", 10239dae4deeSJagpal Singh Gill targets); 10249dae4deeSJagpal Singh Gill } 10259dae4deeSJagpal Singh Gill else 10269dae4deeSJagpal Singh Gill { 10279dae4deeSJagpal Singh Gill // Setup callback for when new software detected 10289dae4deeSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 10299dae4deeSJagpal Singh Gill "/redfish/v1/UpdateService"); 10309dae4deeSJagpal Singh Gill 10319dae4deeSJagpal Singh Gill uploadImageFile(asyncResp->res, req.body()); 10329dae4deeSJagpal Singh Gill } 10339dae4deeSJagpal Singh Gill } 10349dae4deeSJagpal Singh Gill 1035c2051d11SEd Tanous inline void 1036c2051d11SEd Tanous handleUpdateServicePost(App& app, const crow::Request& req, 1037c2051d11SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1038c2051d11SEd Tanous { 10393ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1040c2051d11SEd Tanous { 1041c2051d11SEd Tanous return; 1042c2051d11SEd Tanous } 1043b33a4327SNinad Palsule std::string_view contentType = req.getHeaderValue("Content-Type"); 1044b33a4327SNinad Palsule 104562598e31SEd Tanous BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType); 1046b33a4327SNinad Palsule 1047b33a4327SNinad Palsule // Make sure that content type is application/octet-stream or 1048b33a4327SNinad Palsule // multipart/form-data 104918f8f608SEd Tanous if (bmcweb::asciiIEquals(contentType, "application/octet-stream")) 1050b33a4327SNinad Palsule { 10519dae4deeSJagpal Singh Gill doHTTPUpdate(asyncResp, req); 1052b33a4327SNinad Palsule } 1053b33a4327SNinad Palsule else if (contentType.starts_with("multipart/form-data")) 1054b33a4327SNinad Palsule { 1055b33a4327SNinad Palsule MultipartParser parser; 1056c2051d11SEd Tanous 10570ed80c8cSGeorge Liu ParserError ec = parser.parse(req); 10580ed80c8cSGeorge Liu if (ec != ParserError::PARSER_SUCCESS) 10590ed80c8cSGeorge Liu { 10600ed80c8cSGeorge Liu // handle error 106162598e31SEd Tanous BMCWEB_LOG_ERROR("MIME parse failed, ec : {}", 106262598e31SEd Tanous static_cast<int>(ec)); 10630ed80c8cSGeorge Liu messages::internalError(asyncResp->res); 10640ed80c8cSGeorge Liu return; 10650ed80c8cSGeorge Liu } 10666b54e4e0SEd Tanous 1067ef93eab3SJagpal Singh Gill updateMultipartContext(asyncResp, req, std::move(parser)); 1068c2051d11SEd Tanous } 1069b33a4327SNinad Palsule else 1070b33a4327SNinad Palsule { 107162598e31SEd Tanous BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType); 1072b33a4327SNinad Palsule asyncResp->res.result(boost::beast::http::status::bad_request); 1073b33a4327SNinad Palsule } 1074b33a4327SNinad Palsule } 1075c2051d11SEd Tanous 1076f5139334SEd Tanous inline void 1077f5139334SEd Tanous handleUpdateServiceGet(App& app, const crow::Request& req, 1078f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 10791abe55efSEd Tanous { 10803ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 108145ca1b86SEd Tanous { 108245ca1b86SEd Tanous return; 108345ca1b86SEd Tanous } 10848d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 10850ed80c8cSGeorge Liu "#UpdateService.v1_11_1.UpdateService"; 10868d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService"; 10878d1b46d7Szhanghch05 asyncResp->res.jsonValue["Id"] = "UpdateService"; 1088002d39b4SEd Tanous asyncResp->res.jsonValue["Description"] = "Service for Software Update"; 10898d1b46d7Szhanghch05 asyncResp->res.jsonValue["Name"] = "Update Service"; 10904dc23f3fSEd Tanous 10917e860f15SJohn Edward Broadbent asyncResp->res.jsonValue["HttpPushUri"] = 10924dc23f3fSEd Tanous "/redfish/v1/UpdateService/update"; 10930ed80c8cSGeorge Liu asyncResp->res.jsonValue["MultipartHttpPushUri"] = 10940ed80c8cSGeorge Liu "/redfish/v1/UpdateService/update"; 10954dc23f3fSEd Tanous 10960f74e643SEd Tanous // UpdateService cannot be disabled 10978d1b46d7Szhanghch05 asyncResp->res.jsonValue["ServiceEnabled"] = true; 10981476687dSEd Tanous asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] = 10991476687dSEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1100d61e5194STejas Patil // Get the MaxImageSizeBytes 1101bd79bce8SPatrick Williams asyncResp->res.jsonValue["MaxImageSizeBytes"] = 1102bd79bce8SPatrick Williams BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024; 1103d61e5194STejas Patil 1104*6a37140aSEd Tanous if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE) 1105*6a37140aSEd Tanous { 11060554c984SAndrew Geissler // Update Actions object. 11070554c984SAndrew Geissler nlohmann::json& updateSvcSimpleUpdate = 1108002d39b4SEd Tanous asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"]; 11090554c984SAndrew Geissler updateSvcSimpleUpdate["target"] = 11100554c984SAndrew Geissler "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate"; 1111757178a5SEd Tanous 1112757178a5SEd Tanous nlohmann::json::array_t allowed; 1113e5cf777eSEd Tanous allowed.emplace_back(update_service::TransferProtocolType::HTTPS); 1114757178a5SEd Tanous updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = 1115757178a5SEd Tanous std::move(allowed); 1116*6a37140aSEd Tanous } 1117757178a5SEd Tanous 1118539d8c6bSEd Tanous asyncResp->res 1119539d8c6bSEd Tanous .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] = 1120539d8c6bSEd Tanous update_service::ApplyTime::Immediate; 1121729dae72SJennifer Lee } 1122729dae72SJennifer Lee 1123f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryCollectionGet( 1124f5139334SEd Tanous App& app, const crow::Request& req, 1125f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 11261abe55efSEd Tanous { 11273ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 112845ca1b86SEd Tanous { 112945ca1b86SEd Tanous return; 113045ca1b86SEd Tanous } 11318d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 11320f74e643SEd Tanous "#SoftwareInventoryCollection.SoftwareInventoryCollection"; 11338d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = 11340f74e643SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1135002d39b4SEd Tanous asyncResp->res.jsonValue["Name"] = "Software Inventory Collection"; 113608d81adaSJohn Edward Broadbent const std::array<const std::string_view, 1> iface = { 1137e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 11386c4eb9deSJennifer Lee 113908d81adaSJohn Edward Broadbent redfish::collection_util::getCollectionMembers( 114008d81adaSJohn Edward Broadbent asyncResp, 1141f5139334SEd Tanous boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface, 1142f5139334SEd Tanous "/xyz/openbmc_project/software"); 1143729dae72SJennifer Lee } 1144f5139334SEd Tanous 114587d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */ 1146f5139334SEd Tanous inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 114787d84729SAndrew Geissler const std::string& purpose) 114887d84729SAndrew Geissler { 1149eee0013eSWilly Tu if (purpose == sw_util::bmcPurpose) 115087d84729SAndrew Geissler { 1151ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"]; 11521476687dSEd Tanous nlohmann::json::object_t item; 1153253f11b8SEd Tanous item["@odata.id"] = boost::urls::format( 1154253f11b8SEd Tanous "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); 1155b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item)); 1156ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] = 1157ac106bf6SEd Tanous relatedItem.size(); 115887d84729SAndrew Geissler } 1159eee0013eSWilly Tu else if (purpose == sw_util::biosPurpose) 116087d84729SAndrew Geissler { 1161ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"]; 11621476687dSEd Tanous nlohmann::json::object_t item; 1163253f11b8SEd Tanous item["@odata.id"] = std::format("/redfish/v1/Systems/{}/Bios", 1164253f11b8SEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 1165b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item)); 1166ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] = 1167ac106bf6SEd Tanous relatedItem.size(); 116887d84729SAndrew Geissler } 116987d84729SAndrew Geissler else 117087d84729SAndrew Geissler { 1171bf2ddedeSCarson Labrado BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose); 117287d84729SAndrew Geissler } 117387d84729SAndrew Geissler } 117487d84729SAndrew Geissler 1175af24660dSWilly Tu inline void 1176af24660dSWilly Tu getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1177af24660dSWilly Tu const std::string& service, const std::string& path, 1178af24660dSWilly Tu const std::string& swId) 1179af24660dSWilly Tu { 1180d1bde9e5SKrzysztof Grobelny sdbusplus::asio::getAllProperties( 1181d1bde9e5SKrzysztof Grobelny *crow::connections::systemBus, service, path, 1182d1bde9e5SKrzysztof Grobelny "xyz.openbmc_project.Software.Version", 1183af24660dSWilly Tu [asyncResp, 11848b24275dSEd Tanous swId](const boost::system::error_code& ec, 1185af24660dSWilly Tu const dbus::utility::DBusPropertiesMap& propertiesList) { 11868b24275dSEd Tanous if (ec) 1187af24660dSWilly Tu { 1188af24660dSWilly Tu messages::internalError(asyncResp->res); 1189af24660dSWilly Tu return; 1190af24660dSWilly Tu } 1191d1bde9e5SKrzysztof Grobelny 1192af24660dSWilly Tu const std::string* swInvPurpose = nullptr; 1193af24660dSWilly Tu const std::string* version = nullptr; 1194d1bde9e5SKrzysztof Grobelny 1195d1bde9e5SKrzysztof Grobelny const bool success = sdbusplus::unpackPropertiesNoThrow( 1196d1bde9e5SKrzysztof Grobelny dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose", 1197d1bde9e5SKrzysztof Grobelny swInvPurpose, "Version", version); 1198d1bde9e5SKrzysztof Grobelny 1199d1bde9e5SKrzysztof Grobelny if (!success) 1200af24660dSWilly Tu { 1201d1bde9e5SKrzysztof Grobelny messages::internalError(asyncResp->res); 1202d1bde9e5SKrzysztof Grobelny return; 1203af24660dSWilly Tu } 1204af24660dSWilly Tu 1205af24660dSWilly Tu if (swInvPurpose == nullptr) 1206af24660dSWilly Tu { 120762598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!"); 1208af24660dSWilly Tu messages::internalError(asyncResp->res); 1209af24660dSWilly Tu return; 1210af24660dSWilly Tu } 1211af24660dSWilly Tu 121262598e31SEd Tanous BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose); 1213af24660dSWilly Tu 1214af24660dSWilly Tu if (version == nullptr) 1215af24660dSWilly Tu { 121662598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Version\"!"); 1217af24660dSWilly Tu 1218af24660dSWilly Tu messages::internalError(asyncResp->res); 1219af24660dSWilly Tu 1220af24660dSWilly Tu return; 1221af24660dSWilly Tu } 1222af24660dSWilly Tu asyncResp->res.jsonValue["Version"] = *version; 1223af24660dSWilly Tu asyncResp->res.jsonValue["Id"] = swId; 1224af24660dSWilly Tu 1225af24660dSWilly Tu // swInvPurpose is of format: 1226af24660dSWilly Tu // xyz.openbmc_project.Software.Version.VersionPurpose.ABC 1227af24660dSWilly Tu // Translate this to "ABC image" 1228af24660dSWilly Tu size_t endDesc = swInvPurpose->rfind('.'); 1229af24660dSWilly Tu if (endDesc == std::string::npos) 1230af24660dSWilly Tu { 1231af24660dSWilly Tu messages::internalError(asyncResp->res); 1232af24660dSWilly Tu return; 1233af24660dSWilly Tu } 1234af24660dSWilly Tu endDesc++; 1235af24660dSWilly Tu if (endDesc >= swInvPurpose->size()) 1236af24660dSWilly Tu { 1237af24660dSWilly Tu messages::internalError(asyncResp->res); 1238af24660dSWilly Tu return; 1239af24660dSWilly Tu } 1240af24660dSWilly Tu 1241af24660dSWilly Tu std::string formatDesc = swInvPurpose->substr(endDesc); 1242af24660dSWilly Tu asyncResp->res.jsonValue["Description"] = formatDesc + " image"; 1243af24660dSWilly Tu getRelatedItems(asyncResp, *swInvPurpose); 1244d1bde9e5SKrzysztof Grobelny }); 1245af24660dSWilly Tu } 1246af24660dSWilly Tu 1247f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryGet( 1248f5139334SEd Tanous App& app, const crow::Request& req, 124945ca1b86SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1250f5139334SEd Tanous const std::string& param) 1251f5139334SEd Tanous { 12523ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 125345ca1b86SEd Tanous { 125445ca1b86SEd Tanous return; 125545ca1b86SEd Tanous } 1256f5139334SEd Tanous std::shared_ptr<std::string> swId = std::make_shared<std::string>(param); 1257c711bf86SEd Tanous 1258ef4c65b7SEd Tanous asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1259ef4c65b7SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId); 1260c711bf86SEd Tanous 1261e99073f5SGeorge Liu constexpr std::array<std::string_view, 1> interfaces = { 1262e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 1263e99073f5SGeorge Liu dbus::utility::getSubTree( 1264e99073f5SGeorge Liu "/", 0, interfaces, 1265b9d36b47SEd Tanous [asyncResp, 1266e99073f5SGeorge Liu swId](const boost::system::error_code& ec, 1267b9d36b47SEd Tanous const dbus::utility::MapperGetSubTreeResponse& subtree) { 126862598e31SEd Tanous BMCWEB_LOG_DEBUG("doGet callback..."); 12691abe55efSEd Tanous if (ec) 12701abe55efSEd Tanous { 1271f12894f8SJason M. Bills messages::internalError(asyncResp->res); 12726c4eb9deSJennifer Lee return; 12736c4eb9deSJennifer Lee } 12746c4eb9deSJennifer Lee 12756913228dSAndrew Geissler // Ensure we find our input swId, otherwise return an error 12766913228dSAndrew Geissler bool found = false; 1277bd79bce8SPatrick Williams for (const std::pair<std::string, 1278bd79bce8SPatrick Williams std::vector<std::pair< 1279bd79bce8SPatrick Williams std::string, std::vector<std::string>>>>& 1280002d39b4SEd Tanous obj : subtree) 12811abe55efSEd Tanous { 128211ba3979SEd Tanous if (!obj.first.ends_with(*swId)) 12831abe55efSEd Tanous { 1284acb7cfb4SJennifer Lee continue; 1285acb7cfb4SJennifer Lee } 1286acb7cfb4SJennifer Lee 128726f6976fSEd Tanous if (obj.second.empty()) 12881abe55efSEd Tanous { 1289acb7cfb4SJennifer Lee continue; 1290acb7cfb4SJennifer Lee } 12916c4eb9deSJennifer Lee 12926913228dSAndrew Geissler found = true; 1293eee0013eSWilly Tu sw_util::getSwStatus(asyncResp, swId, obj.second[0].first); 1294af24660dSWilly Tu getSoftwareVersion(asyncResp, obj.second[0].first, obj.first, 1295af24660dSWilly Tu *swId); 12966c4eb9deSJennifer Lee } 12976913228dSAndrew Geissler if (!found) 12986913228dSAndrew Geissler { 129962598e31SEd Tanous BMCWEB_LOG_WARNING("Input swID {} not found!", *swId); 13006913228dSAndrew Geissler messages::resourceMissingAtURI( 1301ef4c65b7SEd Tanous asyncResp->res, 1302ef4c65b7SEd Tanous boost::urls::format( 1303bd79bce8SPatrick Williams "/redfish/v1/UpdateService/FirmwareInventory/{}", 1304bd79bce8SPatrick Williams *swId)); 13056913228dSAndrew Geissler return; 13066913228dSAndrew Geissler } 13074e68c45bSAyushi Smriti asyncResp->res.jsonValue["@odata.type"] = 13084e68c45bSAyushi Smriti "#SoftwareInventory.v1_1_0.SoftwareInventory"; 13094e68c45bSAyushi Smriti asyncResp->res.jsonValue["Name"] = "Software Inventory"; 1310539d8c6bSEd Tanous asyncResp->res.jsonValue["Status"]["HealthRollup"] = 1311539d8c6bSEd Tanous resource::Health::OK; 13123f8a743aSAppaRao Puli 13133f8a743aSAppaRao Puli asyncResp->res.jsonValue["Updateable"] = false; 1314eee0013eSWilly Tu sw_util::getSwUpdatableStatus(asyncResp, swId); 1315e99073f5SGeorge Liu }); 1316f5139334SEd Tanous } 1317f5139334SEd Tanous 1318f5139334SEd Tanous inline void requestRoutesUpdateService(App& app) 1319f5139334SEd Tanous { 1320*6a37140aSEd Tanous if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE) 1321*6a37140aSEd Tanous { 1322f5139334SEd Tanous BMCWEB_ROUTE( 1323*6a37140aSEd Tanous app, 1324*6a37140aSEd Tanous "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/") 1325f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1326f5139334SEd Tanous .methods(boost::beast::http::verb::post)(std::bind_front( 1327f5139334SEd Tanous handleUpdateServiceSimpleUpdateAction, std::ref(app))); 1328*6a37140aSEd Tanous } 1329f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/") 1330f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventory) 1331f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1332f5139334SEd Tanous handleUpdateServiceFirmwareInventoryGet, std::ref(app))); 1333f5139334SEd Tanous 1334f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/") 1335f5139334SEd Tanous .privileges(redfish::privileges::getUpdateService) 1336f5139334SEd Tanous .methods(boost::beast::http::verb::get)( 1337f5139334SEd Tanous std::bind_front(handleUpdateServiceGet, std::ref(app))); 1338f5139334SEd Tanous 1339f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/") 1340f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1341f5139334SEd Tanous .methods(boost::beast::http::verb::post)( 1342f5139334SEd Tanous std::bind_front(handleUpdateServicePost, std::ref(app))); 1343f5139334SEd Tanous 1344f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/") 1345f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventoryCollection) 1346f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1347f5139334SEd Tanous handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app))); 13486c4eb9deSJennifer Lee } 1349729dae72SJennifer Lee 1350729dae72SJennifer Lee } // namespace redfish 1351