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