140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
340e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4729dae72SJennifer Lee #pragma once
5729dae72SJennifer Lee
6d61e5194STejas Patil #include "bmcweb_config.h"
7d61e5194STejas Patil
83ccb3adbSEd Tanous #include "app.hpp"
9d7857201SEd Tanous #include "async_resp.hpp"
10d7857201SEd Tanous #include "dbus_singleton.hpp"
113ccb3adbSEd Tanous #include "dbus_utility.hpp"
125b90429aSEd Tanous #include "error_messages.hpp"
13d7857201SEd Tanous #include "generated/enums/resource.hpp"
14757178a5SEd Tanous #include "generated/enums/update_service.hpp"
15d7857201SEd Tanous #include "http_request.hpp"
16d7857201SEd Tanous #include "http_response.hpp"
17d98a2f93SEd Tanous #include "io_context_singleton.hpp"
18d7857201SEd Tanous #include "logging.hpp"
190ed80c8cSGeorge Liu #include "multipart_parser.hpp"
202c6ffdb0SEd Tanous #include "ossl_random.hpp"
213ccb3adbSEd Tanous #include "query.hpp"
223ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
23d7857201SEd Tanous #include "str_utility.hpp"
24a8e884fcSEd Tanous #include "task.hpp"
255b90429aSEd Tanous #include "task_messages.hpp"
26d7857201SEd Tanous #include "utility.hpp"
2708d81adaSJohn Edward Broadbent #include "utils/collection.hpp"
283ccb3adbSEd Tanous #include "utils/dbus_utils.hpp"
295b90429aSEd Tanous #include "utils/json_utils.hpp"
303ccb3adbSEd Tanous #include "utils/sw_utils.hpp"
313ccb3adbSEd Tanous
32de0c960cSJagpal Singh Gill #include <sys/mman.h>
33d7857201SEd Tanous #include <unistd.h>
34de0c960cSJagpal Singh Gill
35d7857201SEd Tanous #include <boost/asio/error.hpp>
36d7857201SEd Tanous #include <boost/asio/steady_timer.hpp>
37d7857201SEd Tanous #include <boost/beast/http/fields.hpp>
38d7857201SEd Tanous #include <boost/beast/http/status.hpp>
39d7857201SEd Tanous #include <boost/beast/http/verb.hpp>
40e99073f5SGeorge Liu #include <boost/system/error_code.hpp>
41d7857201SEd Tanous #include <boost/system/result.hpp>
42ef4c65b7SEd Tanous #include <boost/url/format.hpp>
43d7857201SEd Tanous #include <boost/url/parse.hpp>
44d7857201SEd Tanous #include <boost/url/url.hpp>
45d7857201SEd Tanous #include <boost/url/url_view.hpp>
46d7857201SEd Tanous #include <boost/url/url_view_base.hpp>
471e1e598dSJonathan Doman #include <sdbusplus/asio/property.hpp>
483ccb3adbSEd Tanous #include <sdbusplus/bus/match.hpp>
49d7857201SEd Tanous #include <sdbusplus/message.hpp>
50d7857201SEd Tanous #include <sdbusplus/message/native_types.hpp>
51d1bde9e5SKrzysztof Grobelny #include <sdbusplus/unpack_properties.hpp>
521214b7e7SGunnar Mills
532b73119cSGeorge Liu #include <array>
54d7857201SEd Tanous #include <chrono>
55de0c960cSJagpal Singh Gill #include <cstddef>
56d7857201SEd Tanous #include <cstdint>
57d7857201SEd Tanous #include <cstdio>
580ed80c8cSGeorge Liu #include <filesystem>
59d7857201SEd Tanous #include <format>
60d7857201SEd Tanous #include <fstream>
61c71b6c99SJagpal Singh Gill #include <functional>
62ef93eab3SJagpal Singh Gill #include <memory>
637cb59f65SEd Tanous #include <optional>
647cb59f65SEd Tanous #include <string>
652b73119cSGeorge Liu #include <string_view>
66de0c960cSJagpal Singh Gill #include <unordered_map>
67d7857201SEd Tanous #include <utility>
68d7857201SEd Tanous #include <variant>
69ef93eab3SJagpal Singh Gill #include <vector>
702b73119cSGeorge Liu
711abe55efSEd Tanous namespace redfish
721abe55efSEd Tanous {
7327826b5fSEd Tanous
740e7de46fSAndrew Geissler // Match signals added on software path
75cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
7659d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
77cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
7859d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
790e7de46fSAndrew Geissler // Only allow one update at a time
80cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
810e7de46fSAndrew Geissler static bool fwUpdateInProgress = false;
8286adcd6dSAndrew Geissler // Timer for software available
83cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
84271584abSEd Tanous static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
8586adcd6dSAndrew Geissler
86de0c960cSJagpal Singh Gill struct MemoryFileDescriptor
87de0c960cSJagpal Singh Gill {
88de0c960cSJagpal Singh Gill int fd = -1;
89de0c960cSJagpal Singh Gill
MemoryFileDescriptorredfish::MemoryFileDescriptor90de0c960cSJagpal Singh Gill explicit MemoryFileDescriptor(const std::string& filename) :
91de0c960cSJagpal Singh Gill fd(memfd_create(filename.c_str(), 0))
92de0c960cSJagpal Singh Gill {}
93de0c960cSJagpal Singh Gill
94de0c960cSJagpal Singh Gill MemoryFileDescriptor(const MemoryFileDescriptor&) = default;
MemoryFileDescriptorredfish::MemoryFileDescriptor95de0c960cSJagpal Singh Gill MemoryFileDescriptor(MemoryFileDescriptor&& other) noexcept : fd(other.fd)
96de0c960cSJagpal Singh Gill {
97de0c960cSJagpal Singh Gill other.fd = -1;
98de0c960cSJagpal Singh Gill }
99de0c960cSJagpal Singh Gill MemoryFileDescriptor& operator=(const MemoryFileDescriptor&) = delete;
100de0c960cSJagpal Singh Gill MemoryFileDescriptor& operator=(MemoryFileDescriptor&&) = default;
101de0c960cSJagpal Singh Gill
~MemoryFileDescriptorredfish::MemoryFileDescriptor102de0c960cSJagpal Singh Gill ~MemoryFileDescriptor()
103de0c960cSJagpal Singh Gill {
104de0c960cSJagpal Singh Gill if (fd != -1)
105de0c960cSJagpal Singh Gill {
106de0c960cSJagpal Singh Gill close(fd);
107de0c960cSJagpal Singh Gill }
108de0c960cSJagpal Singh Gill }
109de0c960cSJagpal Singh Gill
rewindredfish::MemoryFileDescriptor110de0c960cSJagpal Singh Gill bool rewind() const
111de0c960cSJagpal Singh Gill {
112de0c960cSJagpal Singh Gill if (lseek(fd, 0, SEEK_SET) == -1)
113de0c960cSJagpal Singh Gill {
114de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("Failed to seek to beginning of image memfd");
115de0c960cSJagpal Singh Gill return false;
116de0c960cSJagpal Singh Gill }
117de0c960cSJagpal Singh Gill return true;
118de0c960cSJagpal Singh Gill }
119de0c960cSJagpal Singh Gill };
120de0c960cSJagpal Singh Gill
cleanUp()121df254f2cSEd Tanous inline void cleanUp()
12286adcd6dSAndrew Geissler {
12386adcd6dSAndrew Geissler fwUpdateInProgress = false;
12486adcd6dSAndrew Geissler fwUpdateMatcher = nullptr;
1254cde5d90SJames Feist fwUpdateErrorMatcher = nullptr;
12686adcd6dSAndrew Geissler }
127df254f2cSEd Tanous
activateImage(const std::string & objPath,const std::string & service)128df254f2cSEd Tanous inline void activateImage(const std::string& objPath,
12986adcd6dSAndrew Geissler const std::string& service)
13086adcd6dSAndrew Geissler {
13162598e31SEd Tanous BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
1329ae226faSGeorge Liu sdbusplus::asio::setProperty(
1339ae226faSGeorge Liu *crow::connections::systemBus, service, objPath,
1349ae226faSGeorge Liu "xyz.openbmc_project.Software.Activation", "RequestedActivation",
1359ae226faSGeorge Liu "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
1368b24275dSEd Tanous [](const boost::system::error_code& ec) {
1378b24275dSEd Tanous if (ec)
13886adcd6dSAndrew Geissler {
13962598e31SEd Tanous BMCWEB_LOG_DEBUG("error_code = {}", ec);
14062598e31SEd Tanous BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
14186adcd6dSAndrew Geissler }
1429ae226faSGeorge Liu });
14386adcd6dSAndrew Geissler }
1440554c984SAndrew Geissler
handleCreateTask(const boost::system::error_code & ec2,sdbusplus::message_t & msg,const std::shared_ptr<task::TaskData> & taskData)145c71b6c99SJagpal Singh Gill inline bool handleCreateTask(const boost::system::error_code& ec2,
146c71b6c99SJagpal Singh Gill sdbusplus::message_t& msg,
147c71b6c99SJagpal Singh Gill const std::shared_ptr<task::TaskData>& taskData)
148c71b6c99SJagpal Singh Gill {
149c71b6c99SJagpal Singh Gill if (ec2)
150c71b6c99SJagpal Singh Gill {
151c71b6c99SJagpal Singh Gill return task::completed;
152c71b6c99SJagpal Singh Gill }
153c71b6c99SJagpal Singh Gill
154c71b6c99SJagpal Singh Gill std::string iface;
155c71b6c99SJagpal Singh Gill dbus::utility::DBusPropertiesMap values;
156c71b6c99SJagpal Singh Gill
157c71b6c99SJagpal Singh Gill std::string index = std::to_string(taskData->index);
158c71b6c99SJagpal Singh Gill msg.read(iface, values);
159c71b6c99SJagpal Singh Gill
160c71b6c99SJagpal Singh Gill if (iface == "xyz.openbmc_project.Software.Activation")
161c71b6c99SJagpal Singh Gill {
162c71b6c99SJagpal Singh Gill const std::string* state = nullptr;
163c71b6c99SJagpal Singh Gill for (const auto& property : values)
164c71b6c99SJagpal Singh Gill {
165c71b6c99SJagpal Singh Gill if (property.first == "Activation")
166c71b6c99SJagpal Singh Gill {
167c71b6c99SJagpal Singh Gill state = std::get_if<std::string>(&property.second);
168c71b6c99SJagpal Singh Gill if (state == nullptr)
169c71b6c99SJagpal Singh Gill {
170c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::internalError());
171c71b6c99SJagpal Singh Gill return task::completed;
172c71b6c99SJagpal Singh Gill }
173c71b6c99SJagpal Singh Gill }
174c71b6c99SJagpal Singh Gill }
175c71b6c99SJagpal Singh Gill
176c71b6c99SJagpal Singh Gill if (state == nullptr)
177c71b6c99SJagpal Singh Gill {
178c71b6c99SJagpal Singh Gill return !task::completed;
179c71b6c99SJagpal Singh Gill }
180c71b6c99SJagpal Singh Gill
181c71b6c99SJagpal Singh Gill if (state->ends_with("Invalid") || state->ends_with("Failed"))
182c71b6c99SJagpal Singh Gill {
183c71b6c99SJagpal Singh Gill taskData->state = "Exception";
184c71b6c99SJagpal Singh Gill taskData->status = "Warning";
185c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskAborted(index));
186c71b6c99SJagpal Singh Gill return task::completed;
187c71b6c99SJagpal Singh Gill }
188c71b6c99SJagpal Singh Gill
189c71b6c99SJagpal Singh Gill if (state->ends_with("Staged"))
190c71b6c99SJagpal Singh Gill {
191c71b6c99SJagpal Singh Gill taskData->state = "Stopping";
192c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskPaused(index));
193c71b6c99SJagpal Singh Gill
194c71b6c99SJagpal Singh Gill // its staged, set a long timer to
195c71b6c99SJagpal Singh Gill // allow them time to complete the
196c71b6c99SJagpal Singh Gill // update (probably cycle the
197c71b6c99SJagpal Singh Gill // system) if this expires then
198c71b6c99SJagpal Singh Gill // task will be canceled
199c71b6c99SJagpal Singh Gill taskData->extendTimer(std::chrono::hours(5));
200c71b6c99SJagpal Singh Gill return !task::completed;
201c71b6c99SJagpal Singh Gill }
202c71b6c99SJagpal Singh Gill
203c71b6c99SJagpal Singh Gill if (state->ends_with("Active"))
204c71b6c99SJagpal Singh Gill {
205c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::taskCompletedOK(index));
206c71b6c99SJagpal Singh Gill taskData->state = "Completed";
207c71b6c99SJagpal Singh Gill return task::completed;
208c71b6c99SJagpal Singh Gill }
209c71b6c99SJagpal Singh Gill }
210c71b6c99SJagpal Singh Gill else if (iface == "xyz.openbmc_project.Software.ActivationProgress")
211c71b6c99SJagpal Singh Gill {
212c71b6c99SJagpal Singh Gill const uint8_t* progress = nullptr;
213c71b6c99SJagpal Singh Gill for (const auto& property : values)
214c71b6c99SJagpal Singh Gill {
215c71b6c99SJagpal Singh Gill if (property.first == "Progress")
216c71b6c99SJagpal Singh Gill {
217c71b6c99SJagpal Singh Gill progress = std::get_if<uint8_t>(&property.second);
218c71b6c99SJagpal Singh Gill if (progress == nullptr)
219c71b6c99SJagpal Singh Gill {
220c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(messages::internalError());
221c71b6c99SJagpal Singh Gill return task::completed;
222c71b6c99SJagpal Singh Gill }
223c71b6c99SJagpal Singh Gill }
224c71b6c99SJagpal Singh Gill }
225c71b6c99SJagpal Singh Gill
226c71b6c99SJagpal Singh Gill if (progress == nullptr)
227c71b6c99SJagpal Singh Gill {
228c71b6c99SJagpal Singh Gill return !task::completed;
229c71b6c99SJagpal Singh Gill }
230c71b6c99SJagpal Singh Gill taskData->percentComplete = *progress;
231c71b6c99SJagpal Singh Gill taskData->messages.emplace_back(
232c71b6c99SJagpal Singh Gill messages::taskProgressChanged(index, *progress));
233c71b6c99SJagpal Singh Gill
234c71b6c99SJagpal Singh Gill // if we're getting status updates it's
235c71b6c99SJagpal Singh Gill // still alive, update timer
236c71b6c99SJagpal Singh Gill taskData->extendTimer(std::chrono::minutes(5));
237c71b6c99SJagpal Singh Gill }
238c71b6c99SJagpal Singh Gill
239c71b6c99SJagpal Singh Gill // as firmware update often results in a
240c71b6c99SJagpal Singh Gill // reboot, the task may never "complete"
241c71b6c99SJagpal Singh Gill // unless it is an error
242c71b6c99SJagpal Singh Gill
243c71b6c99SJagpal Singh Gill return !task::completed;
244c71b6c99SJagpal Singh Gill }
245c71b6c99SJagpal Singh Gill
createTask(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,task::Payload && payload,const sdbusplus::message::object_path & objPath)246c71b6c99SJagpal Singh Gill inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
247c71b6c99SJagpal Singh Gill task::Payload&& payload,
248c71b6c99SJagpal Singh Gill const sdbusplus::message::object_path& objPath)
249c71b6c99SJagpal Singh Gill {
250c71b6c99SJagpal Singh Gill std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
251c71b6c99SJagpal Singh Gill std::bind_front(handleCreateTask),
252c71b6c99SJagpal Singh Gill "type='signal',interface='org.freedesktop.DBus.Properties',"
253c71b6c99SJagpal Singh Gill "member='PropertiesChanged',path='" +
254c71b6c99SJagpal Singh Gill objPath.str + "'");
255c71b6c99SJagpal Singh Gill task->startTimer(std::chrono::minutes(5));
256c71b6c99SJagpal Singh Gill task->populateResp(asyncResp->res);
257c71b6c99SJagpal Singh Gill task->payload.emplace(std::move(payload));
258c71b6c99SJagpal Singh Gill }
259c71b6c99SJagpal Singh Gill
2600554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
2610554c984SAndrew Geissler // then no asyncResp updates will occur
softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,sdbusplus::message_t & m,task::Payload && payload)262504af5a0SPatrick Williams inline void softwareInterfaceAdded(
263504af5a0SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
26459d494eeSPatrick Williams sdbusplus::message_t& m, task::Payload&& payload)
26586adcd6dSAndrew Geissler {
26680f79a40SMichael Shen dbus::utility::DBusInterfacesMap interfacesProperties;
26786adcd6dSAndrew Geissler
26886adcd6dSAndrew Geissler sdbusplus::message::object_path objPath;
26986adcd6dSAndrew Geissler
27086adcd6dSAndrew Geissler m.read(objPath, interfacesProperties);
27186adcd6dSAndrew Geissler
27262598e31SEd Tanous BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
273e3eb3d63SEd Tanous for (const auto& interface : interfacesProperties)
27486adcd6dSAndrew Geissler {
27562598e31SEd Tanous BMCWEB_LOG_DEBUG("interface = {}", interface.first);
27686adcd6dSAndrew Geissler
27786adcd6dSAndrew Geissler if (interface.first == "xyz.openbmc_project.Software.Activation")
27886adcd6dSAndrew Geissler {
27986adcd6dSAndrew Geissler // Retrieve service and activate
2802b73119cSGeorge Liu constexpr std::array<std::string_view, 1> interfaces = {
2812b73119cSGeorge Liu "xyz.openbmc_project.Software.Activation"};
2822b73119cSGeorge Liu dbus::utility::getDbusObject(
2832b73119cSGeorge Liu objPath.str, interfaces,
284a3e65892SEd Tanous [objPath, asyncResp, payload(std::move(payload))](
2858b24275dSEd Tanous const boost::system::error_code& ec,
286a3e65892SEd Tanous const std::vector<
287a3e65892SEd Tanous std::pair<std::string, std::vector<std::string>>>&
288a3e65892SEd Tanous objInfo) mutable {
2898b24275dSEd Tanous if (ec)
29086adcd6dSAndrew Geissler {
29162598e31SEd Tanous BMCWEB_LOG_DEBUG("error_code = {}", ec);
29262598e31SEd Tanous BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
2930554c984SAndrew Geissler if (asyncResp)
2940554c984SAndrew Geissler {
29586adcd6dSAndrew Geissler messages::internalError(asyncResp->res);
2960554c984SAndrew Geissler }
29786adcd6dSAndrew Geissler cleanUp();
29886adcd6dSAndrew Geissler return;
29986adcd6dSAndrew Geissler }
30086adcd6dSAndrew Geissler // Ensure we only got one service back
30186adcd6dSAndrew Geissler if (objInfo.size() != 1)
30286adcd6dSAndrew Geissler {
303bd79bce8SPatrick Williams BMCWEB_LOG_ERROR("Invalid Object Size {}",
304bd79bce8SPatrick Williams objInfo.size());
3050554c984SAndrew Geissler if (asyncResp)
3060554c984SAndrew Geissler {
30786adcd6dSAndrew Geissler messages::internalError(asyncResp->res);
3080554c984SAndrew Geissler }
30986adcd6dSAndrew Geissler cleanUp();
31086adcd6dSAndrew Geissler return;
31186adcd6dSAndrew Geissler }
31286adcd6dSAndrew Geissler // cancel timer only when
31386adcd6dSAndrew Geissler // xyz.openbmc_project.Software.Activation interface
31486adcd6dSAndrew Geissler // is added
31586adcd6dSAndrew Geissler fwAvailableTimer = nullptr;
31686adcd6dSAndrew Geissler
31786adcd6dSAndrew Geissler activateImage(objPath.str, objInfo[0].first);
3180554c984SAndrew Geissler if (asyncResp)
3190554c984SAndrew Geissler {
320c71b6c99SJagpal Singh Gill createTask(asyncResp, std::move(payload), objPath);
3210554c984SAndrew Geissler }
32286adcd6dSAndrew Geissler fwUpdateInProgress = false;
3232b73119cSGeorge Liu });
32462bafc01SPatrick Williams
32562bafc01SPatrick Williams break;
32686adcd6dSAndrew Geissler }
32786adcd6dSAndrew Geissler }
32886adcd6dSAndrew Geissler }
32986adcd6dSAndrew Geissler
afterAvailbleTimerAsyncWait(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec)3308549b951SMyung Bae inline void afterAvailbleTimerAsyncWait(
3318549b951SMyung Bae const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3328549b951SMyung Bae const boost::system::error_code& ec)
3338549b951SMyung Bae {
3348549b951SMyung Bae cleanUp();
3358549b951SMyung Bae if (ec == boost::asio::error::operation_aborted)
3368549b951SMyung Bae {
3378549b951SMyung Bae // expected, we were canceled before the timer completed.
3388549b951SMyung Bae return;
3398549b951SMyung Bae }
3408549b951SMyung Bae BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
3418549b951SMyung Bae BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
3428549b951SMyung Bae if (ec)
3438549b951SMyung Bae {
3448549b951SMyung Bae BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
3458549b951SMyung Bae return;
3468549b951SMyung Bae }
3478549b951SMyung Bae if (asyncResp)
3488549b951SMyung Bae {
3498549b951SMyung Bae redfish::messages::internalError(asyncResp->res);
3508549b951SMyung Bae }
3518549b951SMyung Bae }
3528549b951SMyung Bae
handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & url,const std::string & type)353504af5a0SPatrick Williams inline void handleUpdateErrorType(
354504af5a0SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& url,
355504af5a0SPatrick Williams const std::string& type)
3568549b951SMyung Bae {
357c87294a6SEd Tanous // NOLINTBEGIN(bugprone-branch-clone)
3588549b951SMyung Bae if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
3598549b951SMyung Bae {
36048fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res);
3618549b951SMyung Bae }
3628549b951SMyung Bae else if (type ==
3638549b951SMyung Bae "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
3648549b951SMyung Bae {
36548fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res);
3668549b951SMyung Bae }
3678549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
3688549b951SMyung Bae {
36948fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res);
3708549b951SMyung Bae }
3718549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
3728549b951SMyung Bae {
373c87294a6SEd Tanous messages::resourceAlreadyExists(asyncResp->res, "UpdateService",
374c87294a6SEd Tanous "Version", "uploaded version");
3758549b951SMyung Bae }
3768549b951SMyung Bae else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
3778549b951SMyung Bae {
37848fb20b9SEd Tanous messages::serviceTemporarilyUnavailable(asyncResp->res, url);
3798549b951SMyung Bae }
3804034a652SMyung Bae else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
3818549b951SMyung Bae {
382c87294a6SEd Tanous messages::internalError(asyncResp->res);
3834034a652SMyung Bae }
3844034a652SMyung Bae else if (type ==
3854034a652SMyung Bae "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
3864034a652SMyung Bae {
387c87294a6SEd Tanous messages::internalError(asyncResp->res);
3884034a652SMyung Bae }
3894034a652SMyung Bae else if (type ==
3904034a652SMyung Bae "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
3914034a652SMyung Bae {
39248fb20b9SEd Tanous messages::missingOrMalformedPart(asyncResp->res);
3934034a652SMyung Bae }
3944034a652SMyung Bae else if (type ==
3954034a652SMyung Bae "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
3964034a652SMyung Bae type == "xyz.openbmc_project.Software.Version.Error.HostFile")
3974034a652SMyung Bae {
3984034a652SMyung Bae BMCWEB_LOG_ERROR("Software Image Error type={}", type);
39948fb20b9SEd Tanous messages::internalError(asyncResp->res);
4008549b951SMyung Bae }
4014034a652SMyung Bae else
4024034a652SMyung Bae {
4034034a652SMyung Bae // Unrelated error types. Ignored
4044034a652SMyung Bae BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
4054034a652SMyung Bae return;
4064034a652SMyung Bae }
407c87294a6SEd Tanous // NOLINTEND(bugprone-branch-clone)
4084034a652SMyung Bae // Clear the timer
4094034a652SMyung Bae fwAvailableTimer = nullptr;
4108549b951SMyung Bae }
4118549b951SMyung Bae
afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & url,sdbusplus::message_t & m)412504af5a0SPatrick Williams inline void afterUpdateErrorMatcher(
413504af5a0SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& url,
414504af5a0SPatrick Williams sdbusplus::message_t& m)
4158549b951SMyung Bae {
41680f79a40SMichael Shen dbus::utility::DBusInterfacesMap interfacesProperties;
4178549b951SMyung Bae sdbusplus::message::object_path objPath;
4188549b951SMyung Bae m.read(objPath, interfacesProperties);
4198549b951SMyung Bae BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
4208549b951SMyung Bae for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
4218549b951SMyung Bae interface : interfacesProperties)
4228549b951SMyung Bae {
4238549b951SMyung Bae if (interface.first == "xyz.openbmc_project.Logging.Entry")
4248549b951SMyung Bae {
4258549b951SMyung Bae for (const std::pair<std::string, dbus::utility::DbusVariantType>&
4268549b951SMyung Bae value : interface.second)
4278549b951SMyung Bae {
4288549b951SMyung Bae if (value.first != "Message")
4298549b951SMyung Bae {
4308549b951SMyung Bae continue;
4318549b951SMyung Bae }
4328549b951SMyung Bae const std::string* type =
4338549b951SMyung Bae std::get_if<std::string>(&value.second);
4348549b951SMyung Bae if (type == nullptr)
4358549b951SMyung Bae {
4368549b951SMyung Bae // if this was our message, timeout will cover it
4378549b951SMyung Bae return;
4388549b951SMyung Bae }
4398549b951SMyung Bae handleUpdateErrorType(asyncResp, url, *type);
4408549b951SMyung Bae }
4418549b951SMyung Bae }
4428549b951SMyung Bae }
4438549b951SMyung Bae }
4448549b951SMyung Bae
4450554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
4460554c984SAndrew Geissler // then no asyncResp updates will occur
monitorForSoftwareAvailable(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const crow::Request & req,const std::string & url,int timeoutTimeSeconds=50)447f5139334SEd Tanous inline void monitorForSoftwareAvailable(
4488d1b46d7Szhanghch05 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
4498d1b46d7Szhanghch05 const crow::Request& req, const std::string& url,
4501940677aSGunnar Mills int timeoutTimeSeconds = 50)
45186adcd6dSAndrew Geissler {
45286adcd6dSAndrew Geissler // Only allow one FW update at a time
453e05aec50SEd Tanous if (fwUpdateInProgress)
45486adcd6dSAndrew Geissler {
4550554c984SAndrew Geissler if (asyncResp)
4560554c984SAndrew Geissler {
45786adcd6dSAndrew Geissler messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
4580554c984SAndrew Geissler }
45986adcd6dSAndrew Geissler return;
46086adcd6dSAndrew Geissler }
46186adcd6dSAndrew Geissler
4620554c984SAndrew Geissler fwAvailableTimer =
463d98a2f93SEd Tanous std::make_unique<boost::asio::steady_timer>(getIoContext());
46486adcd6dSAndrew Geissler
465271584abSEd Tanous fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
46686adcd6dSAndrew Geissler
46786adcd6dSAndrew Geissler fwAvailableTimer->async_wait(
4688549b951SMyung Bae std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
4698549b951SMyung Bae
470a3e65892SEd Tanous task::Payload payload(req);
47159d494eeSPatrick Williams auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
47262598e31SEd Tanous BMCWEB_LOG_DEBUG("Match fired");
473a3e65892SEd Tanous softwareInterfaceAdded(asyncResp, m, std::move(payload));
47486adcd6dSAndrew Geissler };
47586adcd6dSAndrew Geissler
47686adcd6dSAndrew Geissler fwUpdateInProgress = true;
47786adcd6dSAndrew Geissler
47859d494eeSPatrick Williams fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
47986adcd6dSAndrew Geissler *crow::connections::systemBus,
48086adcd6dSAndrew Geissler "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
48186adcd6dSAndrew Geissler "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
48286adcd6dSAndrew Geissler callback);
4834cde5d90SJames Feist
48459d494eeSPatrick Williams fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
4854cde5d90SJames Feist *crow::connections::systemBus,
486e1cc4828SBrian Ma "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
487e1cc4828SBrian Ma "member='InterfacesAdded',"
488e1cc4828SBrian Ma "path='/xyz/openbmc_project/logging'",
4898549b951SMyung Bae std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
49086adcd6dSAndrew Geissler }
491729dae72SJennifer Lee
parseSimpleUpdateUrl(std::string imageURI,std::optional<std::string> transferProtocol,crow::Response & res)492bd79bce8SPatrick Williams inline std::optional<boost::urls::url> parseSimpleUpdateUrl(
493bd79bce8SPatrick Williams std::string imageURI, std::optional<std::string> transferProtocol,
494f86bcc87SEd Tanous crow::Response& res)
495f86bcc87SEd Tanous {
496f86bcc87SEd Tanous if (imageURI.find("://") == std::string::npos)
497f86bcc87SEd Tanous {
498f86bcc87SEd Tanous if (imageURI.starts_with("/"))
499f86bcc87SEd Tanous {
500f86bcc87SEd Tanous messages::actionParameterValueTypeError(
501f86bcc87SEd Tanous res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
502f86bcc87SEd Tanous return std::nullopt;
503f86bcc87SEd Tanous }
504f86bcc87SEd Tanous if (!transferProtocol)
505f86bcc87SEd Tanous {
506f86bcc87SEd Tanous messages::actionParameterValueTypeError(
507f86bcc87SEd Tanous res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
508f86bcc87SEd Tanous return std::nullopt;
509f86bcc87SEd Tanous }
5106a37140aSEd Tanous // OpenBMC currently only supports HTTPS
5116a37140aSEd Tanous if (*transferProtocol == "HTTPS")
512e5cf777eSEd Tanous {
513e5cf777eSEd Tanous imageURI = "https://" + imageURI;
514e5cf777eSEd Tanous }
515757178a5SEd Tanous else
516f86bcc87SEd Tanous {
517f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "TransferProtocol",
518f86bcc87SEd Tanous *transferProtocol);
519f86bcc87SEd Tanous BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
520f86bcc87SEd Tanous *transferProtocol);
521f86bcc87SEd Tanous return std::nullopt;
522f86bcc87SEd Tanous }
523f86bcc87SEd Tanous }
524f86bcc87SEd Tanous
525f86bcc87SEd Tanous boost::system::result<boost::urls::url> url =
526f86bcc87SEd Tanous boost::urls::parse_absolute_uri(imageURI);
527f86bcc87SEd Tanous if (!url)
528f86bcc87SEd Tanous {
529f86bcc87SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
530f86bcc87SEd Tanous "UpdateService.SimpleUpdate");
531f86bcc87SEd Tanous
532f86bcc87SEd Tanous return std::nullopt;
533f86bcc87SEd Tanous }
534f86bcc87SEd Tanous url->normalize();
535f86bcc87SEd Tanous
536757178a5SEd Tanous if (url->scheme() == "tftp")
537757178a5SEd Tanous {
538757178a5SEd Tanous if (url->encoded_path().size() < 2)
539757178a5SEd Tanous {
540757178a5SEd Tanous messages::actionParameterNotSupported(res, "ImageURI",
541757178a5SEd Tanous url->buffer());
542757178a5SEd Tanous return std::nullopt;
543757178a5SEd Tanous }
544757178a5SEd Tanous }
545e5cf777eSEd Tanous else if (url->scheme() == "https")
546e5cf777eSEd Tanous {
547e5cf777eSEd Tanous // Empty paths default to "/"
548e5cf777eSEd Tanous if (url->encoded_path().empty())
549e5cf777eSEd Tanous {
550e5cf777eSEd Tanous url->set_encoded_path("/");
551e5cf777eSEd Tanous }
552e5cf777eSEd Tanous }
553757178a5SEd Tanous else
554f86bcc87SEd Tanous {
555f86bcc87SEd Tanous messages::actionParameterNotSupported(res, "ImageURI", imageURI);
556f86bcc87SEd Tanous return std::nullopt;
557f86bcc87SEd Tanous }
558757178a5SEd Tanous
559757178a5SEd Tanous if (url->encoded_path().empty())
560f86bcc87SEd Tanous {
561757178a5SEd Tanous messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
562757178a5SEd Tanous "UpdateService.SimpleUpdate");
563f86bcc87SEd Tanous return std::nullopt;
564f86bcc87SEd Tanous }
565757178a5SEd Tanous
566757178a5SEd Tanous return *url;
567f86bcc87SEd Tanous }
568f86bcc87SEd Tanous
doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::urls::url_view_base & url)569e5cf777eSEd Tanous inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
570e5cf777eSEd Tanous const boost::urls::url_view_base& url)
571e5cf777eSEd Tanous {
572e5cf777eSEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
573e5cf777eSEd Tanous url.buffer());
574e5cf777eSEd Tanous }
575e5cf777eSEd Tanous
handleUpdateServiceSimpleUpdateAction(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)576f5139334SEd Tanous inline void handleUpdateServiceSimpleUpdateAction(
577f5139334SEd Tanous crow::App& app, const crow::Request& req,
578f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
5790554c984SAndrew Geissler {
5803ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp))
58145ca1b86SEd Tanous {
58245ca1b86SEd Tanous return;
58345ca1b86SEd Tanous }
58445ca1b86SEd Tanous
5850554c984SAndrew Geissler std::optional<std::string> transferProtocol;
5860554c984SAndrew Geissler std::string imageURI;
5870554c984SAndrew Geissler
58862598e31SEd Tanous BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
5890554c984SAndrew Geissler
5900554c984SAndrew Geissler // User can pass in both TransferProtocol and ImageURI parameters or
5914e0453b1SGunnar Mills // they can pass in just the ImageURI with the transfer protocol
5924e0453b1SGunnar Mills // embedded within it.
5930554c984SAndrew Geissler // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
5940554c984SAndrew Geissler // 2) ImageURI:tftp://1.1.1.1/myfile.bin
5950554c984SAndrew Geissler
596afc474aeSMyung Bae if (!json_util::readJsonAction( //
597afc474aeSMyung Bae req, asyncResp->res, //
598afc474aeSMyung Bae "ImageURI", imageURI, //
599afc474aeSMyung Bae "TransferProtocol", transferProtocol //
600afc474aeSMyung Bae ))
6010554c984SAndrew Geissler {
60262598e31SEd Tanous BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
6030554c984SAndrew Geissler return;
6040554c984SAndrew Geissler }
605f5139334SEd Tanous
606757178a5SEd Tanous std::optional<boost::urls::url> url =
607757178a5SEd Tanous parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res);
608757178a5SEd Tanous if (!url)
6090554c984SAndrew Geissler {
6100554c984SAndrew Geissler return;
6110554c984SAndrew Geissler }
6124e338b23SJagpal Singh Gill if (url->scheme() == "https")
613e5cf777eSEd Tanous {
614e5cf777eSEd Tanous doHttpsUpdate(asyncResp, *url);
615e5cf777eSEd Tanous }
616757178a5SEd Tanous else
617757178a5SEd Tanous {
618757178a5SEd Tanous messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
619757178a5SEd Tanous url->buffer());
620757178a5SEd Tanous return;
621757178a5SEd Tanous }
6220554c984SAndrew Geissler
62362598e31SEd Tanous BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
624729dae72SJennifer Lee }
625729dae72SJennifer Lee
uploadImageFile(crow::Response & res,std::string_view body)6260ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body)
6270ed80c8cSGeorge Liu {
6282c6ffdb0SEd Tanous std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
6292c6ffdb0SEd Tanous
63062598e31SEd Tanous BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
6310ed80c8cSGeorge Liu std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
6320ed80c8cSGeorge Liu std::ofstream::trunc);
6330ed80c8cSGeorge Liu // set the permission of the file to 640
634bd79bce8SPatrick Williams std::filesystem::perms permission =
635bd79bce8SPatrick Williams std::filesystem::perms::owner_read | std::filesystem::perms::group_read;
6360ed80c8cSGeorge Liu std::filesystem::permissions(filepath, permission);
6370ed80c8cSGeorge Liu out << body;
6380ed80c8cSGeorge Liu
6390ed80c8cSGeorge Liu if (out.bad())
6400ed80c8cSGeorge Liu {
6410ed80c8cSGeorge Liu messages::internalError(res);
6420ed80c8cSGeorge Liu cleanUp();
6430ed80c8cSGeorge Liu }
6440ed80c8cSGeorge Liu }
6450ed80c8cSGeorge Liu
646de0c960cSJagpal Singh Gill // Convert the Request Apply Time to the D-Bus value
convertApplyTime(crow::Response & res,const std::string & applyTime,std::string & applyTimeNewVal)647de0c960cSJagpal Singh Gill inline bool convertApplyTime(crow::Response& res, const std::string& applyTime,
648de0c960cSJagpal Singh Gill std::string& applyTimeNewVal)
649de0c960cSJagpal Singh Gill {
650de0c960cSJagpal Singh Gill if (applyTime == "Immediate")
651de0c960cSJagpal Singh Gill {
652de0c960cSJagpal Singh Gill applyTimeNewVal =
653049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
654de0c960cSJagpal Singh Gill }
655de0c960cSJagpal Singh Gill else if (applyTime == "OnReset")
656de0c960cSJagpal Singh Gill {
657de0c960cSJagpal Singh Gill applyTimeNewVal =
658049079f6SJagpal Singh Gill "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
659de0c960cSJagpal Singh Gill }
660de0c960cSJagpal Singh Gill else
661de0c960cSJagpal Singh Gill {
662de0c960cSJagpal Singh Gill BMCWEB_LOG_WARNING(
663de0c960cSJagpal Singh Gill "ApplyTime value {} is not in the list of acceptable values",
664de0c960cSJagpal Singh Gill applyTime);
665de0c960cSJagpal Singh Gill messages::propertyValueNotInList(res, applyTime, "ApplyTime");
666de0c960cSJagpal Singh Gill return false;
667de0c960cSJagpal Singh Gill }
668de0c960cSJagpal Singh Gill return true;
669de0c960cSJagpal Singh Gill }
670de0c960cSJagpal Singh Gill
setApplyTime(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & applyTime)6710ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
6720ed80c8cSGeorge Liu const std::string& applyTime)
6730ed80c8cSGeorge Liu {
6740ed80c8cSGeorge Liu std::string applyTimeNewVal;
675049079f6SJagpal Singh Gill if (!convertApplyTime(asyncResp->res, applyTime, applyTimeNewVal))
6760ed80c8cSGeorge Liu {
6770ed80c8cSGeorge Liu return;
6780ed80c8cSGeorge Liu }
6790ed80c8cSGeorge Liu
680e93abac6SGinu George setDbusProperty(asyncResp, "ApplyTime", "xyz.openbmc_project.Settings",
681d02aad39SEd Tanous sdbusplus::message::object_path(
682d02aad39SEd Tanous "/xyz/openbmc_project/software/apply_time"),
683d02aad39SEd Tanous "xyz.openbmc_project.Software.ApplyTime",
684e93abac6SGinu George "RequestedApplyTime", applyTimeNewVal);
6850ed80c8cSGeorge Liu }
6860ed80c8cSGeorge Liu
687ef93eab3SJagpal Singh Gill struct MultiPartUpdateParameters
6880ed80c8cSGeorge Liu {
689ef93eab3SJagpal Singh Gill std::optional<std::string> applyTime;
690ef93eab3SJagpal Singh Gill std::string uploadData;
691de0c960cSJagpal Singh Gill std::vector<std::string> targets;
692ef93eab3SJagpal Singh Gill };
693ef93eab3SJagpal Singh Gill
processUrl(boost::system::result<boost::urls::url_view> & url)694504af5a0SPatrick Williams inline std::optional<std::string> processUrl(
695504af5a0SPatrick Williams boost::system::result<boost::urls::url_view>& url)
696de0c960cSJagpal Singh Gill {
697de0c960cSJagpal Singh Gill if (!url)
698de0c960cSJagpal Singh Gill {
699de0c960cSJagpal Singh Gill return std::nullopt;
700de0c960cSJagpal Singh Gill }
701de0c960cSJagpal Singh Gill if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers",
702de0c960cSJagpal Singh Gill BMCWEB_REDFISH_MANAGER_URI_NAME))
703de0c960cSJagpal Singh Gill {
704de0c960cSJagpal Singh Gill return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME));
705de0c960cSJagpal Singh Gill }
706de0c960cSJagpal Singh Gill if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
707de0c960cSJagpal Singh Gill {
708de0c960cSJagpal Singh Gill return std::nullopt;
709de0c960cSJagpal Singh Gill }
710de0c960cSJagpal Singh Gill std::string firmwareId;
711de0c960cSJagpal Singh Gill if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService",
712de0c960cSJagpal Singh Gill "FirmwareInventory",
713de0c960cSJagpal Singh Gill std::ref(firmwareId)))
714de0c960cSJagpal Singh Gill {
715de0c960cSJagpal Singh Gill return std::nullopt;
716de0c960cSJagpal Singh Gill }
717de0c960cSJagpal Singh Gill
718de0c960cSJagpal Singh Gill return std::make_optional(firmwareId);
719de0c960cSJagpal Singh Gill }
720de0c960cSJagpal Singh Gill
721ef93eab3SJagpal Singh Gill inline std::optional<MultiPartUpdateParameters>
extractMultipartUpdateParameters(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,MultipartParser parser)722ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters(
723ef93eab3SJagpal Singh Gill const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
724ef93eab3SJagpal Singh Gill MultipartParser parser)
725ef93eab3SJagpal Singh Gill {
726ef93eab3SJagpal Singh Gill MultiPartUpdateParameters multiRet;
727ef93eab3SJagpal Singh Gill for (FormPart& formpart : parser.mime_fields)
7280ed80c8cSGeorge Liu {
7290ed80c8cSGeorge Liu boost::beast::http::fields::const_iterator it =
7300ed80c8cSGeorge Liu formpart.fields.find("Content-Disposition");
7310ed80c8cSGeorge Liu if (it == formpart.fields.end())
7320ed80c8cSGeorge Liu {
73362598e31SEd Tanous BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
734ef93eab3SJagpal Singh Gill return std::nullopt;
7350ed80c8cSGeorge Liu }
73662598e31SEd Tanous BMCWEB_LOG_INFO("Parsing value {}", it->value());
7370ed80c8cSGeorge Liu
7380ed80c8cSGeorge Liu // The construction parameters of param_list must start with `;`
7390ed80c8cSGeorge Liu size_t index = it->value().find(';');
7400ed80c8cSGeorge Liu if (index == std::string::npos)
7410ed80c8cSGeorge Liu {
7420ed80c8cSGeorge Liu continue;
7430ed80c8cSGeorge Liu }
7440ed80c8cSGeorge Liu
74589492a15SPatrick Williams for (const auto& param :
7460ed80c8cSGeorge Liu boost::beast::http::param_list{it->value().substr(index)})
7470ed80c8cSGeorge Liu {
7480ed80c8cSGeorge Liu if (param.first != "name" || param.second.empty())
7490ed80c8cSGeorge Liu {
7500ed80c8cSGeorge Liu continue;
7510ed80c8cSGeorge Liu }
7520ed80c8cSGeorge Liu
7530ed80c8cSGeorge Liu if (param.second == "UpdateParameters")
7540ed80c8cSGeorge Liu {
755ef93eab3SJagpal Singh Gill std::vector<std::string> tempTargets;
756bd79bce8SPatrick Williams nlohmann::json content =
757bd79bce8SPatrick Williams nlohmann::json::parse(formpart.content, nullptr, false);
758ac1e1246SEd Tanous if (content.is_discarded())
759ac1e1246SEd Tanous {
760ac1e1246SEd Tanous return std::nullopt;
761ac1e1246SEd Tanous }
7627cb59f65SEd Tanous nlohmann::json::object_t* obj =
7637cb59f65SEd Tanous content.get_ptr<nlohmann::json::object_t*>();
7647cb59f65SEd Tanous if (obj == nullptr)
7657cb59f65SEd Tanous {
766ef93eab3SJagpal Singh Gill messages::propertyValueTypeError(
767ef93eab3SJagpal Singh Gill asyncResp->res, formpart.content, "UpdateParameters");
768ef93eab3SJagpal Singh Gill return std::nullopt;
7697cb59f65SEd Tanous }
7707cb59f65SEd Tanous
771afc474aeSMyung Bae if (!json_util::readJsonObject( //
772afc474aeSMyung Bae *obj, asyncResp->res, //
773afc474aeSMyung Bae "@Redfish.OperationApplyTime", multiRet.applyTime, //
774afc474aeSMyung Bae "Targets", tempTargets //
775afc474aeSMyung Bae ))
7760ed80c8cSGeorge Liu {
777ef93eab3SJagpal Singh Gill return std::nullopt;
7780ed80c8cSGeorge Liu }
779ef93eab3SJagpal Singh Gill
780ef93eab3SJagpal Singh Gill for (size_t urlIndex = 0; urlIndex < tempTargets.size();
781ef93eab3SJagpal Singh Gill urlIndex++)
7820ed80c8cSGeorge Liu {
783ef93eab3SJagpal Singh Gill const std::string& target = tempTargets[urlIndex];
784ef93eab3SJagpal Singh Gill boost::system::result<boost::urls::url_view> url =
785ef93eab3SJagpal Singh Gill boost::urls::parse_origin_form(target);
786de0c960cSJagpal Singh Gill auto res = processUrl(url);
787de0c960cSJagpal Singh Gill if (!res.has_value())
7880ed80c8cSGeorge Liu {
789ef93eab3SJagpal Singh Gill messages::propertyValueFormatError(
790ef93eab3SJagpal Singh Gill asyncResp->res, target,
791ef93eab3SJagpal Singh Gill std::format("Targets/{}", urlIndex));
792ef93eab3SJagpal Singh Gill return std::nullopt;
7930ed80c8cSGeorge Liu }
794de0c960cSJagpal Singh Gill multiRet.targets.emplace_back(res.value());
795ef93eab3SJagpal Singh Gill }
796ef93eab3SJagpal Singh Gill if (multiRet.targets.size() != 1)
797ef93eab3SJagpal Singh Gill {
798ef93eab3SJagpal Singh Gill messages::propertyValueFormatError(
799ef93eab3SJagpal Singh Gill asyncResp->res, multiRet.targets, "Targets");
800ef93eab3SJagpal Singh Gill return std::nullopt;
801ef93eab3SJagpal Singh Gill }
8020ed80c8cSGeorge Liu }
8030ed80c8cSGeorge Liu else if (param.second == "UpdateFile")
8040ed80c8cSGeorge Liu {
805ef93eab3SJagpal Singh Gill multiRet.uploadData = std::move(formpart.content);
8060ed80c8cSGeorge Liu }
8070ed80c8cSGeorge Liu }
8080ed80c8cSGeorge Liu }
8090ed80c8cSGeorge Liu
810ef93eab3SJagpal Singh Gill if (multiRet.uploadData.empty())
8110ed80c8cSGeorge Liu {
81262598e31SEd Tanous BMCWEB_LOG_ERROR("Upload data is NULL");
8130ed80c8cSGeorge Liu messages::propertyMissing(asyncResp->res, "UpdateFile");
814ef93eab3SJagpal Singh Gill return std::nullopt;
8150ed80c8cSGeorge Liu }
816ef93eab3SJagpal Singh Gill if (multiRet.targets.empty())
8170ed80c8cSGeorge Liu {
818ef93eab3SJagpal Singh Gill messages::propertyMissing(asyncResp->res, "Targets");
819ef93eab3SJagpal Singh Gill return std::nullopt;
820ef93eab3SJagpal Singh Gill }
821ef93eab3SJagpal Singh Gill return multiRet;
8220ed80c8cSGeorge Liu }
8230ed80c8cSGeorge Liu
handleStartUpdate(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,task::Payload payload,const std::string & objectPath,const boost::system::error_code & ec,const sdbusplus::message::object_path & retPath)824bd79bce8SPatrick Williams inline void handleStartUpdate(
825bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
826bd79bce8SPatrick Williams const std::string& objectPath, const boost::system::error_code& ec,
827de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath)
828de0c960cSJagpal Singh Gill {
829de0c960cSJagpal Singh Gill if (ec)
830de0c960cSJagpal Singh Gill {
831de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error_code = {}", ec);
832de0c960cSJagpal Singh Gill BMCWEB_LOG_ERROR("error msg = {}", ec.message());
833de0c960cSJagpal Singh Gill messages::internalError(asyncResp->res);
834de0c960cSJagpal Singh Gill return;
835de0c960cSJagpal Singh Gill }
836de0c960cSJagpal Singh Gill
837587090cdSJagpal Singh Gill BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}",
838587090cdSJagpal Singh Gill objectPath, retPath.str);
839587090cdSJagpal Singh Gill createTask(asyncResp, std::move(payload), retPath);
840de0c960cSJagpal Singh Gill }
841de0c960cSJagpal Singh Gill
startUpdate(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,task::Payload payload,const MemoryFileDescriptor & memfd,const std::string & applyTime,const std::string & objectPath,const std::string & serviceName)842bd79bce8SPatrick Williams inline void startUpdate(
843bd79bce8SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
844bd79bce8SPatrick Williams const MemoryFileDescriptor& memfd, const std::string& applyTime,
845bd79bce8SPatrick Williams const std::string& objectPath, const std::string& serviceName)
846de0c960cSJagpal Singh Gill {
847*177612aaSEd Tanous dbus::utility::async_method_call(
848*177612aaSEd Tanous asyncResp,
849de0c960cSJagpal Singh Gill [asyncResp, payload = std::move(payload),
850de0c960cSJagpal Singh Gill objectPath](const boost::system::error_code& ec1,
851de0c960cSJagpal Singh Gill const sdbusplus::message::object_path& retPath) mutable {
852de0c960cSJagpal Singh Gill handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1,
853de0c960cSJagpal Singh Gill retPath);
854de0c960cSJagpal Singh Gill },
855de0c960cSJagpal Singh Gill serviceName, objectPath, "xyz.openbmc_project.Software.Update",
856de0c960cSJagpal Singh Gill "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime);
857de0c960cSJagpal Singh Gill }
858de0c960cSJagpal Singh Gill
getSwInfo(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,task::Payload payload,const MemoryFileDescriptor & memfd,const std::string & applyTime,const std::string & target,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)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
handleBMCUpdate(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,task::Payload payload,const MemoryFileDescriptor & memfd,const std::string & applyTime,const boost::system::error_code & ec,const dbus::utility::MapperEndPoints & functionalSoftware)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
processUpdateRequest(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,task::Payload && payload,std::string_view body,const std::string & applyTime,std::vector<std::string> & targets)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
updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const crow::Request & req,MultipartParser && parser)978504af5a0SPatrick Williams inline void updateMultipartContext(
979504af5a0SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
980ef93eab3SJagpal Singh Gill const crow::Request& req, MultipartParser&& parser)
981ef93eab3SJagpal Singh Gill {
982ef93eab3SJagpal Singh Gill std::optional<MultiPartUpdateParameters> multipart =
983ef93eab3SJagpal Singh Gill extractMultipartUpdateParameters(asyncResp, std::move(parser));
984ef93eab3SJagpal Singh Gill if (!multipart)
985ef93eab3SJagpal Singh Gill {
986ef93eab3SJagpal Singh Gill return;
987ef93eab3SJagpal Singh Gill }
988ef93eab3SJagpal Singh Gill if (!multipart->applyTime)
989ef93eab3SJagpal Singh Gill {
990ef93eab3SJagpal Singh Gill multipart->applyTime = "OnReset";
991ef93eab3SJagpal Singh Gill }
992ef93eab3SJagpal Singh Gill
993de0c960cSJagpal Singh Gill if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
994de0c960cSJagpal Singh Gill {
9959dae4deeSJagpal Singh Gill std::string applyTimeNewVal;
9969dae4deeSJagpal Singh Gill if (!convertApplyTime(asyncResp->res, *multipart->applyTime,
9979dae4deeSJagpal Singh Gill applyTimeNewVal))
9989dae4deeSJagpal Singh Gill {
9999dae4deeSJagpal Singh Gill return;
10009dae4deeSJagpal Singh Gill }
10019dae4deeSJagpal Singh Gill task::Payload payload(req);
10029dae4deeSJagpal Singh Gill
10039dae4deeSJagpal Singh Gill processUpdateRequest(asyncResp, std::move(payload),
10049dae4deeSJagpal Singh Gill multipart->uploadData, applyTimeNewVal,
10059dae4deeSJagpal Singh Gill multipart->targets);
1006de0c960cSJagpal Singh Gill }
1007de0c960cSJagpal Singh Gill else
1008de0c960cSJagpal Singh Gill {
1009ef93eab3SJagpal Singh Gill setApplyTime(asyncResp, *multipart->applyTime);
10100ed80c8cSGeorge Liu
10116b54e4e0SEd Tanous // Setup callback for when new software detected
1012de0c960cSJagpal Singh Gill monitorForSoftwareAvailable(asyncResp, req,
1013de0c960cSJagpal Singh Gill "/redfish/v1/UpdateService");
10146b54e4e0SEd Tanous
1015ef93eab3SJagpal Singh Gill uploadImageFile(asyncResp->res, multipart->uploadData);
10160ed80c8cSGeorge Liu }
1017de0c960cSJagpal Singh Gill }
10180ed80c8cSGeorge Liu
doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const crow::Request & req)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
handleUpdateServicePost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1046504af5a0SPatrick Williams inline void handleUpdateServicePost(
1047504af5a0SPatrick Williams App& app, const crow::Request& req,
1048c2051d11SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1049c2051d11SEd Tanous {
10503ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1051c2051d11SEd Tanous {
1052c2051d11SEd Tanous return;
1053c2051d11SEd Tanous }
1054b33a4327SNinad Palsule std::string_view contentType = req.getHeaderValue("Content-Type");
1055b33a4327SNinad Palsule
105662598e31SEd Tanous BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
1057b33a4327SNinad Palsule
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
handleUpdateServiceGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1087504af5a0SPatrick Williams inline void handleUpdateServiceGet(
1088504af5a0SPatrick Williams 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
11156a37140aSEd Tanous if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE)
11166a37140aSEd Tanous {
11170554c984SAndrew Geissler // Update Actions object.
11180554c984SAndrew Geissler nlohmann::json& updateSvcSimpleUpdate =
1119002d39b4SEd Tanous asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
11200554c984SAndrew Geissler updateSvcSimpleUpdate["target"] =
11210554c984SAndrew Geissler "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
1122757178a5SEd Tanous
1123757178a5SEd Tanous nlohmann::json::array_t allowed;
1124e5cf777eSEd Tanous allowed.emplace_back(update_service::TransferProtocolType::HTTPS);
1125757178a5SEd Tanous updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
1126757178a5SEd Tanous std::move(allowed);
11276a37140aSEd Tanous }
1128757178a5SEd Tanous
1129539d8c6bSEd Tanous asyncResp->res
1130539d8c6bSEd Tanous .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] =
1131539d8c6bSEd Tanous update_service::ApplyTime::Immediate;
1132729dae72SJennifer Lee }
1133729dae72SJennifer Lee
handleUpdateServiceFirmwareInventoryCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1134f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryCollectionGet(
1135f5139334SEd Tanous App& app, const crow::Request& req,
1136f5139334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
11371abe55efSEd Tanous {
11383ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp))
113945ca1b86SEd Tanous {
114045ca1b86SEd Tanous return;
114145ca1b86SEd Tanous }
11428d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] =
11430f74e643SEd Tanous "#SoftwareInventoryCollection.SoftwareInventoryCollection";
11448d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] =
11450f74e643SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory";
1146002d39b4SEd Tanous asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
114708d81adaSJohn Edward Broadbent const std::array<const std::string_view, 1> iface = {
1148e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"};
11496c4eb9deSJennifer Lee
115008d81adaSJohn Edward Broadbent redfish::collection_util::getCollectionMembers(
115108d81adaSJohn Edward Broadbent asyncResp,
1152f5139334SEd Tanous boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface,
1153f5139334SEd Tanous "/xyz/openbmc_project/software");
1154729dae72SJennifer Lee }
1155f5139334SEd Tanous
115687d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */
getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & purpose)1157f5139334SEd Tanous inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
115887d84729SAndrew Geissler const std::string& purpose)
115987d84729SAndrew Geissler {
1160eee0013eSWilly Tu if (purpose == sw_util::bmcPurpose)
116187d84729SAndrew Geissler {
1162ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
11631476687dSEd Tanous nlohmann::json::object_t item;
1164253f11b8SEd Tanous item["@odata.id"] = boost::urls::format(
1165253f11b8SEd Tanous "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME);
1166b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item));
1167ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1168ac106bf6SEd Tanous relatedItem.size();
116987d84729SAndrew Geissler }
1170eee0013eSWilly Tu else if (purpose == sw_util::biosPurpose)
117187d84729SAndrew Geissler {
1172ac106bf6SEd Tanous nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
11731476687dSEd Tanous nlohmann::json::object_t item;
1174253f11b8SEd Tanous item["@odata.id"] = std::format("/redfish/v1/Systems/{}/Bios",
1175253f11b8SEd Tanous BMCWEB_REDFISH_SYSTEM_URI_NAME);
1176b2ba3072SPatrick Williams relatedItem.emplace_back(std::move(item));
1177ac106bf6SEd Tanous asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1178ac106bf6SEd Tanous relatedItem.size();
117987d84729SAndrew Geissler }
118087d84729SAndrew Geissler else
118187d84729SAndrew Geissler {
1182bf2ddedeSCarson Labrado BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
118387d84729SAndrew Geissler }
118487d84729SAndrew Geissler }
118587d84729SAndrew Geissler
getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & path,const std::string & swId)1186504af5a0SPatrick Williams inline void getSoftwareVersion(
1187504af5a0SPatrick Williams const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1188af24660dSWilly Tu const std::string& service, const std::string& path,
1189af24660dSWilly Tu const std::string& swId)
1190af24660dSWilly Tu {
1191deae6a78SEd Tanous dbus::utility::getAllProperties(
1192deae6a78SEd Tanous service, path, "xyz.openbmc_project.Software.Version",
1193af24660dSWilly Tu [asyncResp,
11948b24275dSEd Tanous swId](const boost::system::error_code& ec,
1195af24660dSWilly Tu const dbus::utility::DBusPropertiesMap& propertiesList) {
11968b24275dSEd Tanous if (ec)
1197af24660dSWilly Tu {
1198af24660dSWilly Tu messages::internalError(asyncResp->res);
1199af24660dSWilly Tu return;
1200af24660dSWilly Tu }
1201d1bde9e5SKrzysztof Grobelny
1202af24660dSWilly Tu const std::string* swInvPurpose = nullptr;
1203af24660dSWilly Tu const std::string* version = nullptr;
1204d1bde9e5SKrzysztof Grobelny
1205d1bde9e5SKrzysztof Grobelny const bool success = sdbusplus::unpackPropertiesNoThrow(
1206d1bde9e5SKrzysztof Grobelny dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
1207d1bde9e5SKrzysztof Grobelny swInvPurpose, "Version", version);
1208d1bde9e5SKrzysztof Grobelny
1209d1bde9e5SKrzysztof Grobelny if (!success)
1210af24660dSWilly Tu {
1211d1bde9e5SKrzysztof Grobelny messages::internalError(asyncResp->res);
1212d1bde9e5SKrzysztof Grobelny return;
1213af24660dSWilly Tu }
1214af24660dSWilly Tu
1215af24660dSWilly Tu if (swInvPurpose == nullptr)
1216af24660dSWilly Tu {
121762598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
1218af24660dSWilly Tu messages::internalError(asyncResp->res);
1219af24660dSWilly Tu return;
1220af24660dSWilly Tu }
1221af24660dSWilly Tu
122262598e31SEd Tanous BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
1223af24660dSWilly Tu
1224af24660dSWilly Tu if (version == nullptr)
1225af24660dSWilly Tu {
122662598e31SEd Tanous BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
1227af24660dSWilly Tu
1228af24660dSWilly Tu messages::internalError(asyncResp->res);
1229af24660dSWilly Tu
1230af24660dSWilly Tu return;
1231af24660dSWilly Tu }
1232af24660dSWilly Tu asyncResp->res.jsonValue["Version"] = *version;
1233af24660dSWilly Tu asyncResp->res.jsonValue["Id"] = swId;
1234af24660dSWilly Tu
1235af24660dSWilly Tu // swInvPurpose is of format:
1236af24660dSWilly Tu // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
1237af24660dSWilly Tu // Translate this to "ABC image"
1238af24660dSWilly Tu size_t endDesc = swInvPurpose->rfind('.');
1239af24660dSWilly Tu if (endDesc == std::string::npos)
1240af24660dSWilly Tu {
1241af24660dSWilly Tu messages::internalError(asyncResp->res);
1242af24660dSWilly Tu return;
1243af24660dSWilly Tu }
1244af24660dSWilly Tu endDesc++;
1245af24660dSWilly Tu if (endDesc >= swInvPurpose->size())
1246af24660dSWilly Tu {
1247af24660dSWilly Tu messages::internalError(asyncResp->res);
1248af24660dSWilly Tu return;
1249af24660dSWilly Tu }
1250af24660dSWilly Tu
1251af24660dSWilly Tu std::string formatDesc = swInvPurpose->substr(endDesc);
1252af24660dSWilly Tu asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1253af24660dSWilly Tu getRelatedItems(asyncResp, *swInvPurpose);
1254d1bde9e5SKrzysztof Grobelny });
1255af24660dSWilly Tu }
1256af24660dSWilly Tu
handleUpdateServiceFirmwareInventoryGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & param)1257f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryGet(
1258f5139334SEd Tanous App& app, const crow::Request& req,
125945ca1b86SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1260f5139334SEd Tanous const std::string& param)
1261f5139334SEd Tanous {
12623ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp))
126345ca1b86SEd Tanous {
126445ca1b86SEd Tanous return;
126545ca1b86SEd Tanous }
1266f5139334SEd Tanous std::shared_ptr<std::string> swId = std::make_shared<std::string>(param);
1267c711bf86SEd Tanous
1268e99073f5SGeorge Liu constexpr std::array<std::string_view, 1> interfaces = {
1269e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"};
1270e99073f5SGeorge Liu dbus::utility::getSubTree(
1271f90af522SAbiola Asojo "/xyz/openbmc_project/software/", 0, interfaces,
1272b9d36b47SEd Tanous [asyncResp,
1273e99073f5SGeorge Liu swId](const boost::system::error_code& ec,
1274b9d36b47SEd Tanous const dbus::utility::MapperGetSubTreeResponse& subtree) {
127562598e31SEd Tanous BMCWEB_LOG_DEBUG("doGet callback...");
12761abe55efSEd Tanous if (ec)
12771abe55efSEd Tanous {
1278f12894f8SJason M. Bills messages::internalError(asyncResp->res);
12796c4eb9deSJennifer Lee return;
12806c4eb9deSJennifer Lee }
12816c4eb9deSJennifer Lee
12826913228dSAndrew Geissler // Ensure we find our input swId, otherwise return an error
12836913228dSAndrew Geissler bool found = false;
1284bd79bce8SPatrick Williams for (const std::pair<std::string,
1285bd79bce8SPatrick Williams std::vector<std::pair<
1286bd79bce8SPatrick Williams std::string, std::vector<std::string>>>>&
1287002d39b4SEd Tanous obj : subtree)
12881abe55efSEd Tanous {
1289f90af522SAbiola Asojo sdbusplus::message::object_path path(obj.first);
1290f90af522SAbiola Asojo std::string id = path.filename();
1291f90af522SAbiola Asojo if (id.empty())
1292f90af522SAbiola Asojo {
1293f90af522SAbiola Asojo BMCWEB_LOG_DEBUG("Failed to find software id in {}",
1294f90af522SAbiola Asojo obj.first);
1295f90af522SAbiola Asojo continue;
1296f90af522SAbiola Asojo }
1297f90af522SAbiola Asojo if (id != *swId)
12981abe55efSEd Tanous {
1299acb7cfb4SJennifer Lee continue;
1300acb7cfb4SJennifer Lee }
1301acb7cfb4SJennifer Lee
130226f6976fSEd Tanous if (obj.second.empty())
13031abe55efSEd Tanous {
1304acb7cfb4SJennifer Lee continue;
1305acb7cfb4SJennifer Lee }
13066c4eb9deSJennifer Lee
13076913228dSAndrew Geissler found = true;
1308eee0013eSWilly Tu sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1309af24660dSWilly Tu getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1310af24660dSWilly Tu *swId);
13116c4eb9deSJennifer Lee }
13126913228dSAndrew Geissler if (!found)
13136913228dSAndrew Geissler {
131462598e31SEd Tanous BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
13156913228dSAndrew Geissler messages::resourceMissingAtURI(
1316ef4c65b7SEd Tanous asyncResp->res,
1317ef4c65b7SEd Tanous boost::urls::format(
1318bd79bce8SPatrick Williams "/redfish/v1/UpdateService/FirmwareInventory/{}",
1319bd79bce8SPatrick Williams *swId));
13206913228dSAndrew Geissler return;
13216913228dSAndrew Geissler }
1322f90af522SAbiola Asojo asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1323f90af522SAbiola Asojo "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
13244e68c45bSAyushi Smriti asyncResp->res.jsonValue["@odata.type"] =
13254e68c45bSAyushi Smriti "#SoftwareInventory.v1_1_0.SoftwareInventory";
13264e68c45bSAyushi Smriti asyncResp->res.jsonValue["Name"] = "Software Inventory";
1327539d8c6bSEd Tanous asyncResp->res.jsonValue["Status"]["HealthRollup"] =
1328539d8c6bSEd Tanous resource::Health::OK;
13293f8a743aSAppaRao Puli
13303f8a743aSAppaRao Puli asyncResp->res.jsonValue["Updateable"] = false;
1331eee0013eSWilly Tu sw_util::getSwUpdatableStatus(asyncResp, swId);
1332e99073f5SGeorge Liu });
1333f5139334SEd Tanous }
1334f5139334SEd Tanous
requestRoutesUpdateService(App & app)1335f5139334SEd Tanous inline void requestRoutesUpdateService(App& app)
1336f5139334SEd Tanous {
13376a37140aSEd Tanous if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE)
13386a37140aSEd Tanous {
1339f5139334SEd Tanous BMCWEB_ROUTE(
13406a37140aSEd Tanous app,
13416a37140aSEd Tanous "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
1342f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService)
1343f5139334SEd Tanous .methods(boost::beast::http::verb::post)(std::bind_front(
1344f5139334SEd Tanous handleUpdateServiceSimpleUpdateAction, std::ref(app)));
13456a37140aSEd Tanous }
1346f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1347f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventory)
1348f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front(
1349f5139334SEd Tanous handleUpdateServiceFirmwareInventoryGet, std::ref(app)));
1350f5139334SEd Tanous
1351f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1352f5139334SEd Tanous .privileges(redfish::privileges::getUpdateService)
1353f5139334SEd Tanous .methods(boost::beast::http::verb::get)(
1354f5139334SEd Tanous std::bind_front(handleUpdateServiceGet, std::ref(app)));
1355f5139334SEd Tanous
1356f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
1357f5139334SEd Tanous .privileges(redfish::privileges::postUpdateService)
1358f5139334SEd Tanous .methods(boost::beast::http::verb::post)(
1359f5139334SEd Tanous std::bind_front(handleUpdateServicePost, std::ref(app)));
1360f5139334SEd Tanous
1361f5139334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
1362f5139334SEd Tanous .privileges(redfish::privileges::getSoftwareInventoryCollection)
1363f5139334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front(
1364f5139334SEd Tanous handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app)));
13656c4eb9deSJennifer Lee }
1366729dae72SJennifer Lee
1367729dae72SJennifer Lee } // namespace redfish
1368