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, 3398549b951SMyung Bae const std::string& url, const std::string& type) 3408549b951SMyung Bae { 3418549b951SMyung Bae if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure") 3428549b951SMyung Bae { 3438549b951SMyung Bae redfish::messages::invalidUpload(asyncResp->res, url, 3448549b951SMyung Bae "Invalid archive"); 3458549b951SMyung Bae } 3468549b951SMyung Bae else if (type == 3478549b951SMyung Bae "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure") 3488549b951SMyung Bae { 3498549b951SMyung Bae redfish::messages::invalidUpload(asyncResp->res, url, 3508549b951SMyung Bae "Invalid manifest"); 3518549b951SMyung Bae } 3528549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure") 3538549b951SMyung Bae { 3548549b951SMyung Bae redfish::messages::invalidUpload(asyncResp->res, url, 3558549b951SMyung Bae "Invalid image format"); 3568549b951SMyung Bae } 3578549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists") 3588549b951SMyung Bae { 3598549b951SMyung Bae redfish::messages::invalidUpload(asyncResp->res, url, 3608549b951SMyung Bae "Image version already exists"); 3618549b951SMyung Bae 3628549b951SMyung Bae redfish::messages::resourceAlreadyExists( 3638549b951SMyung Bae asyncResp->res, "UpdateService", "Version", "uploaded version"); 3648549b951SMyung Bae } 3658549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure") 3668549b951SMyung Bae { 3678549b951SMyung Bae redfish::messages::resourceExhaustion(asyncResp->res, url); 3688549b951SMyung Bae } 3694034a652SMyung Bae else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible") 3708549b951SMyung Bae { 3714034a652SMyung Bae redfish::messages::invalidUpload(asyncResp->res, url, 3724034a652SMyung Bae "Incompatible image version"); 3734034a652SMyung Bae } 3744034a652SMyung Bae else if (type == 3754034a652SMyung Bae "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey") 3764034a652SMyung Bae { 3774034a652SMyung Bae redfish::messages::invalidUpload(asyncResp->res, url, 3784034a652SMyung Bae "Update Access Key Expired"); 3794034a652SMyung Bae } 3804034a652SMyung Bae else if (type == 3814034a652SMyung Bae "xyz.openbmc_project.Software.Version.Error.InvalidSignature") 3824034a652SMyung Bae { 3834034a652SMyung Bae redfish::messages::invalidUpload(asyncResp->res, url, 3844034a652SMyung Bae "Invalid image signature"); 3854034a652SMyung Bae } 3864034a652SMyung Bae else if (type == 3874034a652SMyung Bae "xyz.openbmc_project.Software.Image.Error.InternalFailure" || 3884034a652SMyung Bae type == "xyz.openbmc_project.Software.Version.Error.HostFile") 3894034a652SMyung Bae { 3904034a652SMyung Bae BMCWEB_LOG_ERROR("Software Image Error type={}", type); 3918549b951SMyung Bae redfish::messages::internalError(asyncResp->res); 3928549b951SMyung Bae } 3934034a652SMyung Bae else 3944034a652SMyung Bae { 3954034a652SMyung Bae // Unrelated error types. Ignored 3964034a652SMyung Bae BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type); 3974034a652SMyung Bae return; 3984034a652SMyung Bae } 3994034a652SMyung Bae // Clear the timer 4004034a652SMyung Bae fwAvailableTimer = nullptr; 4018549b951SMyung Bae } 4028549b951SMyung Bae 4038549b951SMyung Bae inline void 4048549b951SMyung Bae afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 4058549b951SMyung Bae const std::string& url, sdbusplus::message_t& m) 4068549b951SMyung Bae { 40780f79a40SMichael Shen dbus::utility::DBusInterfacesMap interfacesProperties; 4088549b951SMyung Bae sdbusplus::message::object_path objPath; 4098549b951SMyung Bae m.read(objPath, interfacesProperties); 4108549b951SMyung Bae BMCWEB_LOG_DEBUG("obj path = {}", objPath.str); 4118549b951SMyung Bae for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>& 4128549b951SMyung Bae interface : interfacesProperties) 4138549b951SMyung Bae { 4148549b951SMyung Bae if (interface.first == "xyz.openbmc_project.Logging.Entry") 4158549b951SMyung Bae { 4168549b951SMyung Bae for (const std::pair<std::string, dbus::utility::DbusVariantType>& 4178549b951SMyung Bae value : interface.second) 4188549b951SMyung Bae { 4198549b951SMyung Bae if (value.first != "Message") 4208549b951SMyung Bae { 4218549b951SMyung Bae continue; 4228549b951SMyung Bae } 4238549b951SMyung Bae const std::string* type = 4248549b951SMyung Bae std::get_if<std::string>(&value.second); 4258549b951SMyung Bae if (type == nullptr) 4268549b951SMyung Bae { 4278549b951SMyung Bae // if this was our message, timeout will cover it 4288549b951SMyung Bae return; 4298549b951SMyung Bae } 4308549b951SMyung Bae handleUpdateErrorType(asyncResp, url, *type); 4318549b951SMyung Bae } 4328549b951SMyung Bae } 4338549b951SMyung Bae } 4348549b951SMyung Bae } 4358549b951SMyung Bae 4360554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr 4370554c984SAndrew Geissler // then no asyncResp updates will occur 438f5139334SEd Tanous inline void monitorForSoftwareAvailable( 4398d1b46d7Szhanghch05 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 4408d1b46d7Szhanghch05 const crow::Request& req, const std::string& url, 4415d138943SGunnar Mills int timeoutTimeSeconds = 25) 44286adcd6dSAndrew Geissler { 44386adcd6dSAndrew Geissler // Only allow one FW update at a time 444e05aec50SEd Tanous if (fwUpdateInProgress) 44586adcd6dSAndrew Geissler { 4460554c984SAndrew Geissler if (asyncResp) 4470554c984SAndrew Geissler { 44886adcd6dSAndrew Geissler messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 4490554c984SAndrew Geissler } 45086adcd6dSAndrew Geissler return; 45186adcd6dSAndrew Geissler } 45286adcd6dSAndrew Geissler 4538e8245dbSEd Tanous if (req.ioService == nullptr) 4548e8245dbSEd Tanous { 4558e8245dbSEd Tanous messages::internalError(asyncResp->res); 4568e8245dbSEd Tanous return; 4578e8245dbSEd Tanous } 4588e8245dbSEd Tanous 4590554c984SAndrew Geissler fwAvailableTimer = 460271584abSEd Tanous std::make_unique<boost::asio::steady_timer>(*req.ioService); 46186adcd6dSAndrew Geissler 462271584abSEd Tanous fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds)); 46386adcd6dSAndrew Geissler 46486adcd6dSAndrew Geissler fwAvailableTimer->async_wait( 4658549b951SMyung Bae std::bind_front(afterAvailbleTimerAsyncWait, asyncResp)); 4668549b951SMyung Bae 467a3e65892SEd Tanous task::Payload payload(req); 46859d494eeSPatrick Williams auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable { 46962598e31SEd Tanous BMCWEB_LOG_DEBUG("Match fired"); 470a3e65892SEd Tanous softwareInterfaceAdded(asyncResp, m, std::move(payload)); 47186adcd6dSAndrew Geissler }; 47286adcd6dSAndrew Geissler 47386adcd6dSAndrew Geissler fwUpdateInProgress = true; 47486adcd6dSAndrew Geissler 47559d494eeSPatrick Williams fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>( 47686adcd6dSAndrew Geissler *crow::connections::systemBus, 47786adcd6dSAndrew Geissler "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 47886adcd6dSAndrew Geissler "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 47986adcd6dSAndrew Geissler callback); 4804cde5d90SJames Feist 48159d494eeSPatrick Williams fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>( 4824cde5d90SJames Feist *crow::connections::systemBus, 483e1cc4828SBrian Ma "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 484e1cc4828SBrian Ma "member='InterfacesAdded'," 485e1cc4828SBrian Ma "path='/xyz/openbmc_project/logging'", 4868549b951SMyung Bae std::bind_front(afterUpdateErrorMatcher, asyncResp, url)); 48786adcd6dSAndrew Geissler } 488729dae72SJennifer Lee 489bd79bce8SPatrick Williams inline std::optional<boost::urls::url> parseSimpleUpdateUrl( 490bd79bce8SPatrick Williams std::string imageURI, std::optional<std::string> transferProtocol, 491f86bcc87SEd Tanous crow::Response& res) 492f86bcc87SEd Tanous { 493f86bcc87SEd Tanous if (imageURI.find("://") == std::string::npos) 494f86bcc87SEd Tanous { 495f86bcc87SEd Tanous if (imageURI.starts_with("/")) 496f86bcc87SEd Tanous { 497f86bcc87SEd Tanous messages::actionParameterValueTypeError( 498f86bcc87SEd Tanous res, imageURI, "ImageURI", "UpdateService.SimpleUpdate"); 499f86bcc87SEd Tanous return std::nullopt; 500f86bcc87SEd Tanous } 501f86bcc87SEd Tanous if (!transferProtocol) 502f86bcc87SEd Tanous { 503f86bcc87SEd Tanous messages::actionParameterValueTypeError( 504f86bcc87SEd Tanous res, imageURI, "ImageURI", "UpdateService.SimpleUpdate"); 505f86bcc87SEd Tanous return std::nullopt; 506f86bcc87SEd Tanous } 507e5cf777eSEd Tanous // OpenBMC currently only supports TFTP or HTTPS 508757178a5SEd Tanous if (*transferProtocol == "TFTP") 509757178a5SEd Tanous { 510757178a5SEd Tanous imageURI = "tftp://" + imageURI; 511757178a5SEd Tanous } 512e5cf777eSEd Tanous else if (*transferProtocol == "HTTPS") 513e5cf777eSEd Tanous { 514e5cf777eSEd Tanous imageURI = "https://" + imageURI; 515e5cf777eSEd Tanous } 516757178a5SEd Tanous else 517f86bcc87SEd Tanous { 518f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "TransferProtocol", 519f86bcc87SEd Tanous *transferProtocol); 520f86bcc87SEd Tanous BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}", 521f86bcc87SEd Tanous *transferProtocol); 522f86bcc87SEd Tanous return std::nullopt; 523f86bcc87SEd Tanous } 524f86bcc87SEd Tanous } 525f86bcc87SEd Tanous 526f86bcc87SEd Tanous boost::system::result<boost::urls::url> url = 527f86bcc87SEd Tanous boost::urls::parse_absolute_uri(imageURI); 528f86bcc87SEd Tanous if (!url) 529f86bcc87SEd Tanous { 530f86bcc87SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI", 531f86bcc87SEd Tanous "UpdateService.SimpleUpdate"); 532f86bcc87SEd Tanous 533f86bcc87SEd Tanous return std::nullopt; 534f86bcc87SEd Tanous } 535f86bcc87SEd Tanous url->normalize(); 536f86bcc87SEd Tanous 537757178a5SEd Tanous if (url->scheme() == "tftp") 538757178a5SEd Tanous { 539757178a5SEd Tanous if (url->encoded_path().size() < 2) 540757178a5SEd Tanous { 541757178a5SEd Tanous messages::actionParameterNotSupported(res, "ImageURI", 542757178a5SEd Tanous url->buffer()); 543757178a5SEd Tanous return std::nullopt; 544757178a5SEd Tanous } 545757178a5SEd Tanous } 546e5cf777eSEd Tanous else if (url->scheme() == "https") 547e5cf777eSEd Tanous { 548e5cf777eSEd Tanous // Empty paths default to "/" 549e5cf777eSEd Tanous if (url->encoded_path().empty()) 550e5cf777eSEd Tanous { 551e5cf777eSEd Tanous url->set_encoded_path("/"); 552e5cf777eSEd Tanous } 553e5cf777eSEd Tanous } 554757178a5SEd Tanous else 555f86bcc87SEd Tanous { 556f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "ImageURI", imageURI); 557f86bcc87SEd Tanous return std::nullopt; 558f86bcc87SEd Tanous } 559757178a5SEd Tanous 560757178a5SEd Tanous if (url->encoded_path().empty()) 561f86bcc87SEd Tanous { 562757178a5SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI", 563757178a5SEd Tanous "UpdateService.SimpleUpdate"); 564f86bcc87SEd Tanous return std::nullopt; 565f86bcc87SEd Tanous } 566757178a5SEd Tanous 567757178a5SEd Tanous return *url; 568f86bcc87SEd Tanous } 569f86bcc87SEd Tanous 570e5cf777eSEd Tanous inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 571e5cf777eSEd Tanous const boost::urls::url_view_base& url) 572e5cf777eSEd Tanous { 573e5cf777eSEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI", 574e5cf777eSEd Tanous url.buffer()); 575e5cf777eSEd Tanous } 576e5cf777eSEd Tanous 577f5139334SEd Tanous inline void handleUpdateServiceSimpleUpdateAction( 578f5139334SEd Tanous crow::App& app, const crow::Request& req, 579f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 5800554c984SAndrew Geissler { 5813ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 58245ca1b86SEd Tanous { 58345ca1b86SEd Tanous return; 58445ca1b86SEd Tanous } 58545ca1b86SEd Tanous 5860554c984SAndrew Geissler std::optional<std::string> transferProtocol; 5870554c984SAndrew Geissler std::string imageURI; 5880554c984SAndrew Geissler 58962598e31SEd Tanous BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost"); 5900554c984SAndrew Geissler 5910554c984SAndrew Geissler // User can pass in both TransferProtocol and ImageURI parameters or 5924e0453b1SGunnar Mills // they can pass in just the ImageURI with the transfer protocol 5934e0453b1SGunnar Mills // embedded within it. 5940554c984SAndrew Geissler // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin 5950554c984SAndrew Geissler // 2) ImageURI:tftp://1.1.1.1/myfile.bin 5960554c984SAndrew Geissler 597*afc474aeSMyung Bae if (!json_util::readJsonAction( // 598*afc474aeSMyung Bae req, asyncResp->res, // 599*afc474aeSMyung Bae "ImageURI", imageURI, // 600*afc474aeSMyung Bae "TransferProtocol", transferProtocol // 601*afc474aeSMyung Bae )) 6020554c984SAndrew Geissler { 60362598e31SEd Tanous BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter"); 6040554c984SAndrew Geissler return; 6050554c984SAndrew Geissler } 606f5139334SEd Tanous 607757178a5SEd Tanous std::optional<boost::urls::url> url = 608757178a5SEd Tanous parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res); 609757178a5SEd Tanous if (!url) 6100554c984SAndrew Geissler { 6110554c984SAndrew Geissler return; 6120554c984SAndrew Geissler } 6134e338b23SJagpal Singh Gill if (url->scheme() == "https") 614e5cf777eSEd Tanous { 615e5cf777eSEd Tanous doHttpsUpdate(asyncResp, *url); 616e5cf777eSEd Tanous } 617757178a5SEd Tanous else 618757178a5SEd Tanous { 619757178a5SEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI", 620757178a5SEd Tanous url->buffer()); 621757178a5SEd Tanous return; 622757178a5SEd Tanous } 6230554c984SAndrew Geissler 62462598e31SEd Tanous BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost"); 625729dae72SJennifer Lee } 626729dae72SJennifer Lee 6270ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body) 6280ed80c8cSGeorge Liu { 6292c6ffdb0SEd Tanous std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID()); 6302c6ffdb0SEd Tanous 63162598e31SEd Tanous BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string()); 6320ed80c8cSGeorge Liu std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | 6330ed80c8cSGeorge Liu std::ofstream::trunc); 6340ed80c8cSGeorge Liu // set the permission of the file to 640 635bd79bce8SPatrick Williams std::filesystem::perms permission = 636bd79bce8SPatrick Williams std::filesystem::perms::owner_read | std::filesystem::perms::group_read; 6370ed80c8cSGeorge Liu std::filesystem::permissions(filepath, permission); 6380ed80c8cSGeorge Liu out << body; 6390ed80c8cSGeorge Liu 6400ed80c8cSGeorge Liu if (out.bad()) 6410ed80c8cSGeorge Liu { 6420ed80c8cSGeorge Liu messages::internalError(res); 6430ed80c8cSGeorge Liu cleanUp(); 6440ed80c8cSGeorge Liu } 6450ed80c8cSGeorge Liu } 6460ed80c8cSGeorge Liu 647de0c960cSJagpal Singh Gill // Convert the Request Apply Time to the D-Bus value 648de0c960cSJagpal Singh Gill inline bool convertApplyTime(crow::Response& res, const std::string& applyTime, 649de0c960cSJagpal Singh Gill std::string& applyTimeNewVal) 650de0c960cSJagpal Singh Gill { 651de0c960cSJagpal Singh Gill if (applyTime == "Immediate") 652de0c960cSJagpal Singh Gill { 653de0c960cSJagpal Singh Gill applyTimeNewVal = 654049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate"; 655de0c960cSJagpal Singh Gill } 656de0c960cSJagpal Singh Gill else if (applyTime == "OnReset") 657de0c960cSJagpal Singh Gill { 658de0c960cSJagpal Singh Gill applyTimeNewVal = 659049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset"; 660de0c960cSJagpal Singh Gill } 661de0c960cSJagpal Singh Gill else 662de0c960cSJagpal Singh Gill { 663de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING( 664de0c960cSJagpal Singh Gill "ApplyTime value {} is not in the list of acceptable values", 665de0c960cSJagpal Singh Gill applyTime); 666de0c960cSJagpal Singh Gill messages::propertyValueNotInList(res, applyTime, "ApplyTime"); 667de0c960cSJagpal Singh Gill return false; 668de0c960cSJagpal Singh Gill } 669de0c960cSJagpal Singh Gill return true; 670de0c960cSJagpal Singh Gill } 671de0c960cSJagpal Singh Gill 6720ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 6730ed80c8cSGeorge Liu const std::string& applyTime) 6740ed80c8cSGeorge Liu { 6750ed80c8cSGeorge Liu std::string applyTimeNewVal; 676049079f6SJagpal Singh Gill if (!convertApplyTime(asyncResp->res, applyTime, applyTimeNewVal)) 6770ed80c8cSGeorge Liu { 6780ed80c8cSGeorge Liu return; 6790ed80c8cSGeorge Liu } 6800ed80c8cSGeorge Liu 681e93abac6SGinu George setDbusProperty(asyncResp, "ApplyTime", "xyz.openbmc_project.Settings", 682d02aad39SEd Tanous sdbusplus::message::object_path( 683d02aad39SEd Tanous "/xyz/openbmc_project/software/apply_time"), 684d02aad39SEd Tanous "xyz.openbmc_project.Software.ApplyTime", 685e93abac6SGinu George "RequestedApplyTime", applyTimeNewVal); 6860ed80c8cSGeorge Liu } 6870ed80c8cSGeorge Liu 688ef93eab3SJagpal Singh Gill struct MultiPartUpdateParameters 6890ed80c8cSGeorge Liu { 690ef93eab3SJagpal Singh Gill std::optional<std::string> applyTime; 691ef93eab3SJagpal Singh Gill std::string uploadData; 692de0c960cSJagpal Singh Gill std::vector<std::string> targets; 693ef93eab3SJagpal Singh Gill }; 694ef93eab3SJagpal Singh Gill 695de0c960cSJagpal Singh Gill inline std::optional<std::string> 696de0c960cSJagpal Singh Gill processUrl(boost::system::result<boost::urls::url_view>& url) 697de0c960cSJagpal Singh Gill { 698de0c960cSJagpal Singh Gill if (!url) 699de0c960cSJagpal Singh Gill { 700de0c960cSJagpal Singh Gill return std::nullopt; 701de0c960cSJagpal Singh Gill } 702de0c960cSJagpal Singh Gill if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers", 703de0c960cSJagpal Singh Gill BMCWEB_REDFISH_MANAGER_URI_NAME)) 704de0c960cSJagpal Singh Gill { 705de0c960cSJagpal Singh Gill return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME)); 706de0c960cSJagpal Singh Gill } 707de0c960cSJagpal Singh Gill if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 708de0c960cSJagpal Singh Gill { 709de0c960cSJagpal Singh Gill return std::nullopt; 710de0c960cSJagpal Singh Gill } 711de0c960cSJagpal Singh Gill std::string firmwareId; 712de0c960cSJagpal Singh Gill if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService", 713de0c960cSJagpal Singh Gill "FirmwareInventory", 714de0c960cSJagpal Singh Gill std::ref(firmwareId))) 715de0c960cSJagpal Singh Gill { 716de0c960cSJagpal Singh Gill return std::nullopt; 717de0c960cSJagpal Singh Gill } 718de0c960cSJagpal Singh Gill 719de0c960cSJagpal Singh Gill return std::make_optional(firmwareId); 720de0c960cSJagpal Singh Gill } 721de0c960cSJagpal Singh Gill 722ef93eab3SJagpal Singh Gill inline std::optional<MultiPartUpdateParameters> 723ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters( 724ef93eab3SJagpal Singh Gill const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 725ef93eab3SJagpal Singh Gill MultipartParser parser) 726ef93eab3SJagpal Singh Gill { 727ef93eab3SJagpal Singh Gill MultiPartUpdateParameters multiRet; 728ef93eab3SJagpal Singh Gill for (FormPart& formpart : parser.mime_fields) 7290ed80c8cSGeorge Liu { 7300ed80c8cSGeorge Liu boost::beast::http::fields::const_iterator it = 7310ed80c8cSGeorge Liu formpart.fields.find("Content-Disposition"); 7320ed80c8cSGeorge Liu if (it == formpart.fields.end()) 7330ed80c8cSGeorge Liu { 73462598e31SEd Tanous BMCWEB_LOG_ERROR("Couldn't find Content-Disposition"); 735ef93eab3SJagpal Singh Gill return std::nullopt; 7360ed80c8cSGeorge Liu } 73762598e31SEd Tanous BMCWEB_LOG_INFO("Parsing value {}", it->value()); 7380ed80c8cSGeorge Liu 7390ed80c8cSGeorge Liu // The construction parameters of param_list must start with `;` 7400ed80c8cSGeorge Liu size_t index = it->value().find(';'); 7410ed80c8cSGeorge Liu if (index == std::string::npos) 7420ed80c8cSGeorge Liu { 7430ed80c8cSGeorge Liu continue; 7440ed80c8cSGeorge Liu } 7450ed80c8cSGeorge Liu 74689492a15SPatrick Williams for (const auto& param : 7470ed80c8cSGeorge Liu boost::beast::http::param_list{it->value().substr(index)}) 7480ed80c8cSGeorge Liu { 7490ed80c8cSGeorge Liu if (param.first != "name" || param.second.empty()) 7500ed80c8cSGeorge Liu { 7510ed80c8cSGeorge Liu continue; 7520ed80c8cSGeorge Liu } 7530ed80c8cSGeorge Liu 7540ed80c8cSGeorge Liu if (param.second == "UpdateParameters") 7550ed80c8cSGeorge Liu { 756ef93eab3SJagpal Singh Gill std::vector<std::string> tempTargets; 757bd79bce8SPatrick Williams nlohmann::json content = 758bd79bce8SPatrick Williams nlohmann::json::parse(formpart.content, nullptr, false); 759ac1e1246SEd Tanous if (content.is_discarded()) 760ac1e1246SEd Tanous { 761ac1e1246SEd Tanous return std::nullopt; 762ac1e1246SEd Tanous } 7637cb59f65SEd Tanous nlohmann::json::object_t* obj = 7647cb59f65SEd Tanous content.get_ptr<nlohmann::json::object_t*>(); 7657cb59f65SEd Tanous if (obj == nullptr) 7667cb59f65SEd Tanous { 767ef93eab3SJagpal Singh Gill messages::propertyValueTypeError( 768ef93eab3SJagpal Singh Gill asyncResp->res, formpart.content, "UpdateParameters"); 769ef93eab3SJagpal Singh Gill return std::nullopt; 7707cb59f65SEd Tanous } 7717cb59f65SEd Tanous 772*afc474aeSMyung Bae if (!json_util::readJsonObject( // 773*afc474aeSMyung Bae *obj, asyncResp->res, // 774*afc474aeSMyung Bae "@Redfish.OperationApplyTime", multiRet.applyTime, // 775*afc474aeSMyung Bae "Targets", tempTargets // 776*afc474aeSMyung Bae )) 7770ed80c8cSGeorge Liu { 778ef93eab3SJagpal Singh Gill return std::nullopt; 7790ed80c8cSGeorge Liu } 780ef93eab3SJagpal Singh Gill 781ef93eab3SJagpal Singh Gill for (size_t urlIndex = 0; urlIndex < tempTargets.size(); 782ef93eab3SJagpal Singh Gill urlIndex++) 7830ed80c8cSGeorge Liu { 784ef93eab3SJagpal Singh Gill const std::string& target = tempTargets[urlIndex]; 785ef93eab3SJagpal Singh Gill boost::system::result<boost::urls::url_view> url = 786ef93eab3SJagpal Singh Gill boost::urls::parse_origin_form(target); 787de0c960cSJagpal Singh Gill auto res = processUrl(url); 788de0c960cSJagpal Singh Gill if (!res.has_value()) 7890ed80c8cSGeorge Liu { 790ef93eab3SJagpal Singh Gill messages::propertyValueFormatError( 791ef93eab3SJagpal Singh Gill asyncResp->res, target, 792ef93eab3SJagpal Singh Gill std::format("Targets/{}", urlIndex)); 793ef93eab3SJagpal Singh Gill return std::nullopt; 7940ed80c8cSGeorge Liu } 795de0c960cSJagpal Singh Gill multiRet.targets.emplace_back(res.value()); 796ef93eab3SJagpal Singh Gill } 797ef93eab3SJagpal Singh Gill if (multiRet.targets.size() != 1) 798ef93eab3SJagpal Singh Gill { 799ef93eab3SJagpal Singh Gill messages::propertyValueFormatError( 800ef93eab3SJagpal Singh Gill asyncResp->res, multiRet.targets, "Targets"); 801ef93eab3SJagpal Singh Gill return std::nullopt; 802ef93eab3SJagpal Singh Gill } 8030ed80c8cSGeorge Liu } 8040ed80c8cSGeorge Liu else if (param.second == "UpdateFile") 8050ed80c8cSGeorge Liu { 806ef93eab3SJagpal Singh Gill multiRet.uploadData = std::move(formpart.content); 8070ed80c8cSGeorge Liu } 8080ed80c8cSGeorge Liu } 8090ed80c8cSGeorge Liu } 8100ed80c8cSGeorge Liu 811ef93eab3SJagpal Singh Gill if (multiRet.uploadData.empty()) 8120ed80c8cSGeorge Liu { 81362598e31SEd Tanous BMCWEB_LOG_ERROR("Upload data is NULL"); 8140ed80c8cSGeorge Liu messages::propertyMissing(asyncResp->res, "UpdateFile"); 815ef93eab3SJagpal Singh Gill return std::nullopt; 8160ed80c8cSGeorge Liu } 817ef93eab3SJagpal Singh Gill if (multiRet.targets.empty()) 8180ed80c8cSGeorge Liu { 819ef93eab3SJagpal Singh Gill messages::propertyMissing(asyncResp->res, "Targets"); 820ef93eab3SJagpal Singh Gill return std::nullopt; 821ef93eab3SJagpal Singh Gill } 822ef93eab3SJagpal Singh Gill return multiRet; 8230ed80c8cSGeorge Liu } 8240ed80c8cSGeorge Liu 825bd79bce8SPatrick Williams inline void handleStartUpdate( 826bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 827bd79bce8SPatrick Williams const std::string& objectPath, const boost::system::error_code& ec, 828de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) 829de0c960cSJagpal Singh Gill { 830de0c960cSJagpal Singh Gill if (ec) 831de0c960cSJagpal Singh Gill { 832de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 833de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 834de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 835de0c960cSJagpal Singh Gill return; 836de0c960cSJagpal Singh Gill } 837de0c960cSJagpal Singh Gill 838587090cdSJagpal Singh Gill BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}", 839587090cdSJagpal Singh Gill objectPath, retPath.str); 840587090cdSJagpal Singh Gill createTask(asyncResp, std::move(payload), retPath); 841de0c960cSJagpal Singh Gill } 842de0c960cSJagpal Singh Gill 843bd79bce8SPatrick Williams inline void startUpdate( 844bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 845bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 846bd79bce8SPatrick Williams const std::string& objectPath, const std::string& serviceName) 847de0c960cSJagpal Singh Gill { 848de0c960cSJagpal Singh Gill crow::connections::systemBus->async_method_call( 849de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), 850de0c960cSJagpal Singh Gill objectPath](const boost::system::error_code& ec1, 851de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) mutable { 852de0c960cSJagpal Singh Gill handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1, 853de0c960cSJagpal Singh Gill retPath); 854de0c960cSJagpal Singh Gill }, 855de0c960cSJagpal Singh Gill serviceName, objectPath, "xyz.openbmc_project.Software.Update", 856de0c960cSJagpal Singh Gill "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime); 857de0c960cSJagpal Singh Gill } 858de0c960cSJagpal Singh Gill 85908f61d53SJagpal Singh Gill inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 86008f61d53SJagpal Singh Gill task::Payload payload, const MemoryFileDescriptor& memfd, 86108f61d53SJagpal Singh Gill const std::string& applyTime, const std::string& target, 862de0c960cSJagpal Singh Gill const boost::system::error_code& ec, 863de0c960cSJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& subtree) 864de0c960cSJagpal Singh Gill { 86508f61d53SJagpal Singh Gill using SwInfoMap = std::unordered_map< 86608f61d53SJagpal Singh Gill std::string, std::pair<sdbusplus::message::object_path, std::string>>; 867de0c960cSJagpal Singh Gill SwInfoMap swInfoMap; 868de0c960cSJagpal Singh Gill 869de0c960cSJagpal Singh Gill if (ec) 870de0c960cSJagpal Singh Gill { 871de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 872de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 873de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 874de0c960cSJagpal Singh Gill return; 875de0c960cSJagpal Singh Gill } 876de0c960cSJagpal Singh Gill BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size()); 877de0c960cSJagpal Singh Gill 87808f61d53SJagpal Singh Gill for (const auto& entry : subtree) 879de0c960cSJagpal Singh Gill { 88008f61d53SJagpal Singh Gill sdbusplus::message::object_path path(entry.first); 881de0c960cSJagpal Singh Gill std::string swId = path.filename(); 88208f61d53SJagpal Singh Gill swInfoMap.emplace(swId, make_pair(path, entry.second[0].first)); 883de0c960cSJagpal Singh Gill } 884de0c960cSJagpal Singh Gill 885de0c960cSJagpal Singh Gill auto swEntry = swInfoMap.find(target); 886de0c960cSJagpal Singh Gill if (swEntry == swInfoMap.end()) 887de0c960cSJagpal Singh Gill { 888de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target); 889de0c960cSJagpal Singh Gill messages::propertyValueFormatError(asyncResp->res, target, "Targets"); 890de0c960cSJagpal Singh Gill return; 891de0c960cSJagpal Singh Gill } 892de0c960cSJagpal Singh Gill 89308f61d53SJagpal Singh Gill BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}", 89408f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 895de0c960cSJagpal Singh Gill 89608f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 89708f61d53SJagpal Singh Gill swEntry->second.first.str, swEntry->second.second); 89808f61d53SJagpal Singh Gill } 89908f61d53SJagpal Singh Gill 900bd79bce8SPatrick Williams inline void handleBMCUpdate( 901bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, 902bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime, 90308f61d53SJagpal Singh Gill const boost::system::error_code& ec, 90408f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& functionalSoftware) 90508f61d53SJagpal Singh Gill { 90608f61d53SJagpal Singh Gill if (ec) 90708f61d53SJagpal Singh Gill { 90808f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec); 90908f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 91008f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 91108f61d53SJagpal Singh Gill return; 91208f61d53SJagpal Singh Gill } 91308f61d53SJagpal Singh Gill if (functionalSoftware.size() != 1) 91408f61d53SJagpal Singh Gill { 91508f61d53SJagpal Singh Gill BMCWEB_LOG_ERROR("Found {} functional software endpoints", 91608f61d53SJagpal Singh Gill functionalSoftware.size()); 91708f61d53SJagpal Singh Gill messages::internalError(asyncResp->res); 91808f61d53SJagpal Singh Gill return; 91908f61d53SJagpal Singh Gill } 92008f61d53SJagpal Singh Gill 92108f61d53SJagpal Singh Gill startUpdate(asyncResp, std::move(payload), memfd, applyTime, 92208f61d53SJagpal Singh Gill functionalSoftware[0], "xyz.openbmc_project.Software.Manager"); 923de0c960cSJagpal Singh Gill } 924de0c960cSJagpal Singh Gill 925bd79bce8SPatrick Williams inline void processUpdateRequest( 926bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 9279dae4deeSJagpal Singh Gill task::Payload&& payload, std::string_view body, 928bd79bce8SPatrick Williams const std::string& applyTime, std::vector<std::string>& targets) 929de0c960cSJagpal Singh Gill { 930de0c960cSJagpal Singh Gill MemoryFileDescriptor memfd("update-image"); 931de0c960cSJagpal Singh Gill if (memfd.fd == -1) 932de0c960cSJagpal Singh Gill { 933de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to create image memfd"); 934de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 935de0c960cSJagpal Singh Gill return; 936de0c960cSJagpal Singh Gill } 937de0c960cSJagpal Singh Gill if (write(memfd.fd, body.data(), body.length()) != 938de0c960cSJagpal Singh Gill static_cast<ssize_t>(body.length())) 939de0c960cSJagpal Singh Gill { 940de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to write to image memfd"); 941de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 942de0c960cSJagpal Singh Gill return; 943de0c960cSJagpal Singh Gill } 944de0c960cSJagpal Singh Gill if (!memfd.rewind()) 945de0c960cSJagpal Singh Gill { 946de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res); 947de0c960cSJagpal Singh Gill return; 948de0c960cSJagpal Singh Gill } 949de0c960cSJagpal Singh Gill 950de0c960cSJagpal Singh Gill if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME) 951de0c960cSJagpal Singh Gill { 95208f61d53SJagpal Singh Gill dbus::utility::getAssociationEndPoints( 95389449bbeSJagpal Singh Gill "/xyz/openbmc_project/software/bmc/updateable", 95408f61d53SJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 95508f61d53SJagpal Singh Gill applyTime]( 95608f61d53SJagpal Singh Gill const boost::system::error_code& ec, 95708f61d53SJagpal Singh Gill const dbus::utility::MapperEndPoints& objectPaths) mutable { 958bd79bce8SPatrick Williams handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime, 959bd79bce8SPatrick Williams ec, objectPaths); 96008f61d53SJagpal Singh Gill }); 961de0c960cSJagpal Singh Gill } 962de0c960cSJagpal Singh Gill else 963de0c960cSJagpal Singh Gill { 964de0c960cSJagpal Singh Gill constexpr std::array<std::string_view, 1> interfaces = { 965de0c960cSJagpal Singh Gill "xyz.openbmc_project.Software.Version"}; 96608f61d53SJagpal Singh Gill dbus::utility::getSubTree( 967de0c960cSJagpal Singh Gill "/xyz/openbmc_project/software", 1, interfaces, 968de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload), memfd = std::move(memfd), 96908f61d53SJagpal Singh Gill applyTime, targets](const boost::system::error_code& ec, 97008f61d53SJagpal Singh Gill const dbus::utility::MapperGetSubTreeResponse& 971de0c960cSJagpal Singh Gill subtree) mutable { 97208f61d53SJagpal Singh Gill getSwInfo(asyncResp, std::move(payload), memfd, applyTime, 97308f61d53SJagpal Singh Gill targets[0], ec, subtree); 974de0c960cSJagpal Singh Gill }); 975de0c960cSJagpal Singh Gill } 976de0c960cSJagpal Singh Gill } 977de0c960cSJagpal Singh Gill 978de0c960cSJagpal Singh Gill inline void 979ef93eab3SJagpal Singh Gill updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 980ef93eab3SJagpal Singh Gill const crow::Request& req, MultipartParser&& parser) 981ef93eab3SJagpal Singh Gill { 982ef93eab3SJagpal Singh Gill std::optional<MultiPartUpdateParameters> multipart = 983ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters(asyncResp, std::move(parser)); 984ef93eab3SJagpal Singh Gill if (!multipart) 985ef93eab3SJagpal Singh Gill { 986ef93eab3SJagpal Singh Gill return; 987ef93eab3SJagpal Singh Gill } 988ef93eab3SJagpal Singh Gill if (!multipart->applyTime) 989ef93eab3SJagpal Singh Gill { 990ef93eab3SJagpal Singh Gill multipart->applyTime = "OnReset"; 991ef93eab3SJagpal Singh Gill } 992ef93eab3SJagpal Singh Gill 993de0c960cSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 994de0c960cSJagpal Singh Gill { 9959dae4deeSJagpal Singh Gill std::string applyTimeNewVal; 9969dae4deeSJagpal Singh Gill if (!convertApplyTime(asyncResp->res, *multipart->applyTime, 9979dae4deeSJagpal Singh Gill applyTimeNewVal)) 9989dae4deeSJagpal Singh Gill { 9999dae4deeSJagpal Singh Gill return; 10009dae4deeSJagpal Singh Gill } 10019dae4deeSJagpal Singh Gill task::Payload payload(req); 10029dae4deeSJagpal Singh Gill 10039dae4deeSJagpal Singh Gill processUpdateRequest(asyncResp, std::move(payload), 10049dae4deeSJagpal Singh Gill multipart->uploadData, applyTimeNewVal, 10059dae4deeSJagpal Singh Gill multipart->targets); 1006de0c960cSJagpal Singh Gill } 1007de0c960cSJagpal Singh Gill else 1008de0c960cSJagpal Singh Gill { 1009ef93eab3SJagpal Singh Gill setApplyTime(asyncResp, *multipart->applyTime); 10100ed80c8cSGeorge Liu 10116b54e4e0SEd Tanous // Setup callback for when new software detected 1012de0c960cSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 1013de0c960cSJagpal Singh Gill "/redfish/v1/UpdateService"); 10146b54e4e0SEd Tanous 1015ef93eab3SJagpal Singh Gill uploadImageFile(asyncResp->res, multipart->uploadData); 10160ed80c8cSGeorge Liu } 1017de0c960cSJagpal Singh Gill } 10180ed80c8cSGeorge Liu 10199dae4deeSJagpal Singh Gill inline void doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 10209dae4deeSJagpal Singh Gill const crow::Request& req) 10219dae4deeSJagpal Singh Gill { 10229dae4deeSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) 10239dae4deeSJagpal Singh Gill { 10249dae4deeSJagpal Singh Gill task::Payload payload(req); 10259dae4deeSJagpal Singh Gill // HTTP push only supports BMC updates (with ApplyTime as immediate) for 10269dae4deeSJagpal Singh Gill // backwards compatibility. Specific component updates will be handled 10279dae4deeSJagpal Singh Gill // through Multipart form HTTP push. 10289dae4deeSJagpal Singh Gill std::vector<std::string> targets; 10299dae4deeSJagpal Singh Gill targets.emplace_back(BMCWEB_REDFISH_MANAGER_URI_NAME); 10309dae4deeSJagpal Singh Gill 10319dae4deeSJagpal Singh Gill processUpdateRequest( 10329dae4deeSJagpal Singh Gill asyncResp, std::move(payload), req.body(), 10339dae4deeSJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate", 10349dae4deeSJagpal Singh Gill targets); 10359dae4deeSJagpal Singh Gill } 10369dae4deeSJagpal Singh Gill else 10379dae4deeSJagpal Singh Gill { 10389dae4deeSJagpal Singh Gill // Setup callback for when new software detected 10399dae4deeSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req, 10409dae4deeSJagpal Singh Gill "/redfish/v1/UpdateService"); 10419dae4deeSJagpal Singh Gill 10429dae4deeSJagpal Singh Gill uploadImageFile(asyncResp->res, req.body()); 10439dae4deeSJagpal Singh Gill } 10449dae4deeSJagpal Singh Gill } 10459dae4deeSJagpal Singh Gill 1046c2051d11SEd Tanous inline void 1047c2051d11SEd Tanous handleUpdateServicePost(App& app, const crow::Request& req, 1048c2051d11SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1049c2051d11SEd Tanous { 10503ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1051c2051d11SEd Tanous { 1052c2051d11SEd Tanous return; 1053c2051d11SEd Tanous } 1054b33a4327SNinad Palsule std::string_view contentType = req.getHeaderValue("Content-Type"); 1055b33a4327SNinad Palsule 105662598e31SEd Tanous BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType); 1057b33a4327SNinad Palsule 1058b33a4327SNinad Palsule // Make sure that content type is application/octet-stream or 1059b33a4327SNinad Palsule // multipart/form-data 106018f8f608SEd Tanous if (bmcweb::asciiIEquals(contentType, "application/octet-stream")) 1061b33a4327SNinad Palsule { 10629dae4deeSJagpal Singh Gill doHTTPUpdate(asyncResp, req); 1063b33a4327SNinad Palsule } 1064b33a4327SNinad Palsule else if (contentType.starts_with("multipart/form-data")) 1065b33a4327SNinad Palsule { 1066b33a4327SNinad Palsule MultipartParser parser; 1067c2051d11SEd Tanous 10680ed80c8cSGeorge Liu ParserError ec = parser.parse(req); 10690ed80c8cSGeorge Liu if (ec != ParserError::PARSER_SUCCESS) 10700ed80c8cSGeorge Liu { 10710ed80c8cSGeorge Liu // handle error 107262598e31SEd Tanous BMCWEB_LOG_ERROR("MIME parse failed, ec : {}", 107362598e31SEd Tanous static_cast<int>(ec)); 10740ed80c8cSGeorge Liu messages::internalError(asyncResp->res); 10750ed80c8cSGeorge Liu return; 10760ed80c8cSGeorge Liu } 10776b54e4e0SEd Tanous 1078ef93eab3SJagpal Singh Gill updateMultipartContext(asyncResp, req, std::move(parser)); 1079c2051d11SEd Tanous } 1080b33a4327SNinad Palsule else 1081b33a4327SNinad Palsule { 108262598e31SEd Tanous BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType); 1083b33a4327SNinad Palsule asyncResp->res.result(boost::beast::http::status::bad_request); 1084b33a4327SNinad Palsule } 1085b33a4327SNinad Palsule } 1086c2051d11SEd Tanous 1087f5139334SEd Tanous inline void 1088f5139334SEd Tanous handleUpdateServiceGet(App& app, const crow::Request& req, 1089f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 10901abe55efSEd Tanous { 10913ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 109245ca1b86SEd Tanous { 109345ca1b86SEd Tanous return; 109445ca1b86SEd Tanous } 10958d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 10960ed80c8cSGeorge Liu "#UpdateService.v1_11_1.UpdateService"; 10978d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService"; 10988d1b46d7Szhanghch05 asyncResp->res.jsonValue["Id"] = "UpdateService"; 1099002d39b4SEd Tanous asyncResp->res.jsonValue["Description"] = "Service for Software Update"; 11008d1b46d7Szhanghch05 asyncResp->res.jsonValue["Name"] = "Update Service"; 11014dc23f3fSEd Tanous 11027e860f15SJohn Edward Broadbent asyncResp->res.jsonValue["HttpPushUri"] = 11034dc23f3fSEd Tanous "/redfish/v1/UpdateService/update"; 11040ed80c8cSGeorge Liu asyncResp->res.jsonValue["MultipartHttpPushUri"] = 11050ed80c8cSGeorge Liu "/redfish/v1/UpdateService/update"; 11064dc23f3fSEd Tanous 11070f74e643SEd Tanous // UpdateService cannot be disabled 11088d1b46d7Szhanghch05 asyncResp->res.jsonValue["ServiceEnabled"] = true; 11091476687dSEd Tanous asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] = 11101476687dSEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1111d61e5194STejas Patil // Get the MaxImageSizeBytes 1112bd79bce8SPatrick Williams asyncResp->res.jsonValue["MaxImageSizeBytes"] = 1113bd79bce8SPatrick Williams BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024; 1114d61e5194STejas Patil 11150554c984SAndrew Geissler // Update Actions object. 11160554c984SAndrew Geissler nlohmann::json& updateSvcSimpleUpdate = 1117002d39b4SEd Tanous asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"]; 11180554c984SAndrew Geissler updateSvcSimpleUpdate["target"] = 11190554c984SAndrew Geissler "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate"; 1120757178a5SEd Tanous 1121757178a5SEd Tanous nlohmann::json::array_t allowed; 1122e5cf777eSEd Tanous allowed.emplace_back(update_service::TransferProtocolType::HTTPS); 1123757178a5SEd Tanous 112425b54dbaSEd Tanous if constexpr (BMCWEB_INSECURE_PUSH_STYLE_NOTIFICATION) 112525b54dbaSEd Tanous { 1126757178a5SEd Tanous allowed.emplace_back(update_service::TransferProtocolType::TFTP); 112725b54dbaSEd Tanous } 1128757178a5SEd Tanous 1129757178a5SEd Tanous updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = 1130757178a5SEd Tanous std::move(allowed); 1131757178a5SEd Tanous 1132539d8c6bSEd Tanous asyncResp->res 1133539d8c6bSEd Tanous .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] = 1134539d8c6bSEd Tanous update_service::ApplyTime::Immediate; 1135729dae72SJennifer Lee } 1136729dae72SJennifer Lee 1137f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryCollectionGet( 1138f5139334SEd Tanous App& app, const crow::Request& req, 1139f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 11401abe55efSEd Tanous { 11413ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 114245ca1b86SEd Tanous { 114345ca1b86SEd Tanous return; 114445ca1b86SEd Tanous } 11458d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 11460f74e643SEd Tanous "#SoftwareInventoryCollection.SoftwareInventoryCollection"; 11478d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = 11480f74e643SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory"; 1149002d39b4SEd Tanous asyncResp->res.jsonValue["Name"] = "Software Inventory Collection"; 115008d81adaSJohn Edward Broadbent const std::array<const std::string_view, 1> iface = { 1151e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 11526c4eb9deSJennifer Lee 115308d81adaSJohn Edward Broadbent redfish::collection_util::getCollectionMembers( 115408d81adaSJohn Edward Broadbent asyncResp, 1155f5139334SEd Tanous boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface, 1156f5139334SEd Tanous "/xyz/openbmc_project/software"); 1157729dae72SJennifer Lee } 1158f5139334SEd Tanous 115987d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */ 1160f5139334SEd Tanous inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 116187d84729SAndrew Geissler const std::string& purpose) 116287d84729SAndrew Geissler { 1163eee0013eSWilly Tu if (purpose == sw_util::bmcPurpose) 116487d84729SAndrew Geissler { 1165ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"]; 11661476687dSEd Tanous nlohmann::json::object_t item; 1167253f11b8SEd Tanous item["@odata.id"] = boost::urls::format( 1168253f11b8SEd Tanous "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); 1169b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item)); 1170ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] = 1171ac106bf6SEd Tanous relatedItem.size(); 117287d84729SAndrew Geissler } 1173eee0013eSWilly Tu else if (purpose == sw_util::biosPurpose) 117487d84729SAndrew Geissler { 1175ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"]; 11761476687dSEd Tanous nlohmann::json::object_t item; 1177253f11b8SEd Tanous item["@odata.id"] = std::format("/redfish/v1/Systems/{}/Bios", 1178253f11b8SEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME); 1179b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item)); 1180ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] = 1181ac106bf6SEd Tanous relatedItem.size(); 118287d84729SAndrew Geissler } 118387d84729SAndrew Geissler else 118487d84729SAndrew Geissler { 1185bf2ddedeSCarson Labrado BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose); 118687d84729SAndrew Geissler } 118787d84729SAndrew Geissler } 118887d84729SAndrew Geissler 1189af24660dSWilly Tu inline void 1190af24660dSWilly Tu getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1191af24660dSWilly Tu const std::string& service, const std::string& path, 1192af24660dSWilly Tu const std::string& swId) 1193af24660dSWilly Tu { 1194d1bde9e5SKrzysztof Grobelny sdbusplus::asio::getAllProperties( 1195d1bde9e5SKrzysztof Grobelny *crow::connections::systemBus, service, path, 1196d1bde9e5SKrzysztof Grobelny "xyz.openbmc_project.Software.Version", 1197af24660dSWilly Tu [asyncResp, 11988b24275dSEd Tanous swId](const boost::system::error_code& ec, 1199af24660dSWilly Tu const dbus::utility::DBusPropertiesMap& propertiesList) { 12008b24275dSEd Tanous if (ec) 1201af24660dSWilly Tu { 1202af24660dSWilly Tu messages::internalError(asyncResp->res); 1203af24660dSWilly Tu return; 1204af24660dSWilly Tu } 1205d1bde9e5SKrzysztof Grobelny 1206af24660dSWilly Tu const std::string* swInvPurpose = nullptr; 1207af24660dSWilly Tu const std::string* version = nullptr; 1208d1bde9e5SKrzysztof Grobelny 1209d1bde9e5SKrzysztof Grobelny const bool success = sdbusplus::unpackPropertiesNoThrow( 1210d1bde9e5SKrzysztof Grobelny dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose", 1211d1bde9e5SKrzysztof Grobelny swInvPurpose, "Version", version); 1212d1bde9e5SKrzysztof Grobelny 1213d1bde9e5SKrzysztof Grobelny if (!success) 1214af24660dSWilly Tu { 1215d1bde9e5SKrzysztof Grobelny messages::internalError(asyncResp->res); 1216d1bde9e5SKrzysztof Grobelny return; 1217af24660dSWilly Tu } 1218af24660dSWilly Tu 1219af24660dSWilly Tu if (swInvPurpose == nullptr) 1220af24660dSWilly Tu { 122162598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!"); 1222af24660dSWilly Tu messages::internalError(asyncResp->res); 1223af24660dSWilly Tu return; 1224af24660dSWilly Tu } 1225af24660dSWilly Tu 122662598e31SEd Tanous BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose); 1227af24660dSWilly Tu 1228af24660dSWilly Tu if (version == nullptr) 1229af24660dSWilly Tu { 123062598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Version\"!"); 1231af24660dSWilly Tu 1232af24660dSWilly Tu messages::internalError(asyncResp->res); 1233af24660dSWilly Tu 1234af24660dSWilly Tu return; 1235af24660dSWilly Tu } 1236af24660dSWilly Tu asyncResp->res.jsonValue["Version"] = *version; 1237af24660dSWilly Tu asyncResp->res.jsonValue["Id"] = swId; 1238af24660dSWilly Tu 1239af24660dSWilly Tu // swInvPurpose is of format: 1240af24660dSWilly Tu // xyz.openbmc_project.Software.Version.VersionPurpose.ABC 1241af24660dSWilly Tu // Translate this to "ABC image" 1242af24660dSWilly Tu size_t endDesc = swInvPurpose->rfind('.'); 1243af24660dSWilly Tu if (endDesc == std::string::npos) 1244af24660dSWilly Tu { 1245af24660dSWilly Tu messages::internalError(asyncResp->res); 1246af24660dSWilly Tu return; 1247af24660dSWilly Tu } 1248af24660dSWilly Tu endDesc++; 1249af24660dSWilly Tu if (endDesc >= swInvPurpose->size()) 1250af24660dSWilly Tu { 1251af24660dSWilly Tu messages::internalError(asyncResp->res); 1252af24660dSWilly Tu return; 1253af24660dSWilly Tu } 1254af24660dSWilly Tu 1255af24660dSWilly Tu std::string formatDesc = swInvPurpose->substr(endDesc); 1256af24660dSWilly Tu asyncResp->res.jsonValue["Description"] = formatDesc + " image"; 1257af24660dSWilly Tu getRelatedItems(asyncResp, *swInvPurpose); 1258d1bde9e5SKrzysztof Grobelny }); 1259af24660dSWilly Tu } 1260af24660dSWilly Tu 1261f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryGet( 1262f5139334SEd Tanous App& app, const crow::Request& req, 126345ca1b86SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1264f5139334SEd Tanous const std::string& param) 1265f5139334SEd Tanous { 12663ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 126745ca1b86SEd Tanous { 126845ca1b86SEd Tanous return; 126945ca1b86SEd Tanous } 1270f5139334SEd Tanous std::shared_ptr<std::string> swId = std::make_shared<std::string>(param); 1271c711bf86SEd Tanous 1272ef4c65b7SEd Tanous asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1273ef4c65b7SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId); 1274c711bf86SEd Tanous 1275e99073f5SGeorge Liu constexpr std::array<std::string_view, 1> interfaces = { 1276e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 1277e99073f5SGeorge Liu dbus::utility::getSubTree( 1278e99073f5SGeorge Liu "/", 0, interfaces, 1279b9d36b47SEd Tanous [asyncResp, 1280e99073f5SGeorge Liu swId](const boost::system::error_code& ec, 1281b9d36b47SEd Tanous const dbus::utility::MapperGetSubTreeResponse& subtree) { 128262598e31SEd Tanous BMCWEB_LOG_DEBUG("doGet callback..."); 12831abe55efSEd Tanous if (ec) 12841abe55efSEd Tanous { 1285f12894f8SJason M. Bills messages::internalError(asyncResp->res); 12866c4eb9deSJennifer Lee return; 12876c4eb9deSJennifer Lee } 12886c4eb9deSJennifer Lee 12896913228dSAndrew Geissler // Ensure we find our input swId, otherwise return an error 12906913228dSAndrew Geissler bool found = false; 1291bd79bce8SPatrick Williams for (const std::pair<std::string, 1292bd79bce8SPatrick Williams std::vector<std::pair< 1293bd79bce8SPatrick Williams std::string, std::vector<std::string>>>>& 1294002d39b4SEd Tanous obj : subtree) 12951abe55efSEd Tanous { 129611ba3979SEd Tanous if (!obj.first.ends_with(*swId)) 12971abe55efSEd Tanous { 1298acb7cfb4SJennifer Lee continue; 1299acb7cfb4SJennifer Lee } 1300acb7cfb4SJennifer Lee 130126f6976fSEd Tanous if (obj.second.empty()) 13021abe55efSEd Tanous { 1303acb7cfb4SJennifer Lee continue; 1304acb7cfb4SJennifer Lee } 13056c4eb9deSJennifer Lee 13066913228dSAndrew Geissler found = true; 1307eee0013eSWilly Tu sw_util::getSwStatus(asyncResp, swId, obj.second[0].first); 1308af24660dSWilly Tu getSoftwareVersion(asyncResp, obj.second[0].first, obj.first, 1309af24660dSWilly Tu *swId); 13106c4eb9deSJennifer Lee } 13116913228dSAndrew Geissler if (!found) 13126913228dSAndrew Geissler { 131362598e31SEd Tanous BMCWEB_LOG_WARNING("Input swID {} not found!", *swId); 13146913228dSAndrew Geissler messages::resourceMissingAtURI( 1315ef4c65b7SEd Tanous asyncResp->res, 1316ef4c65b7SEd Tanous boost::urls::format( 1317bd79bce8SPatrick Williams "/redfish/v1/UpdateService/FirmwareInventory/{}", 1318bd79bce8SPatrick Williams *swId)); 13196913228dSAndrew Geissler return; 13206913228dSAndrew Geissler } 13214e68c45bSAyushi Smriti asyncResp->res.jsonValue["@odata.type"] = 13224e68c45bSAyushi Smriti "#SoftwareInventory.v1_1_0.SoftwareInventory"; 13234e68c45bSAyushi Smriti asyncResp->res.jsonValue["Name"] = "Software Inventory"; 1324539d8c6bSEd Tanous asyncResp->res.jsonValue["Status"]["HealthRollup"] = 1325539d8c6bSEd Tanous resource::Health::OK; 13263f8a743aSAppaRao Puli 13273f8a743aSAppaRao Puli asyncResp->res.jsonValue["Updateable"] = false; 1328eee0013eSWilly Tu sw_util::getSwUpdatableStatus(asyncResp, swId); 1329e99073f5SGeorge Liu }); 1330f5139334SEd Tanous } 1331f5139334SEd Tanous 1332f5139334SEd Tanous inline void requestRoutesUpdateService(App& app) 1333f5139334SEd Tanous { 1334f5139334SEd Tanous BMCWEB_ROUTE( 1335f5139334SEd Tanous app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/") 1336f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1337f5139334SEd Tanous .methods(boost::beast::http::verb::post)(std::bind_front( 1338f5139334SEd Tanous handleUpdateServiceSimpleUpdateAction, std::ref(app))); 1339f5139334SEd Tanous 1340f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/") 1341f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventory) 1342f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1343f5139334SEd Tanous handleUpdateServiceFirmwareInventoryGet, std::ref(app))); 1344f5139334SEd Tanous 1345f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/") 1346f5139334SEd Tanous .privileges(redfish::privileges::getUpdateService) 1347f5139334SEd Tanous .methods(boost::beast::http::verb::get)( 1348f5139334SEd Tanous std::bind_front(handleUpdateServiceGet, std::ref(app))); 1349f5139334SEd Tanous 1350f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/") 1351f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService) 1352f5139334SEd Tanous .methods(boost::beast::http::verb::post)( 1353f5139334SEd Tanous std::bind_front(handleUpdateServicePost, std::ref(app))); 1354f5139334SEd Tanous 1355f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/") 1356f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventoryCollection) 1357f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1358f5139334SEd Tanous handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app))); 13596c4eb9deSJennifer Lee } 1360729dae72SJennifer Lee 1361729dae72SJennifer Lee } // namespace redfish 1362