xref: /openbmc/bmcweb/features/redfish/lib/update_service.hpp (revision 89449bbec0335bfa2e224931068b79f8e5b4331c)
1729dae72SJennifer Lee /*
26be832e2SEd Tanous Copyright (c) 2018 Intel Corporation
36be832e2SEd Tanous 
46be832e2SEd Tanous Licensed under the Apache License, Version 2.0 (the "License");
56be832e2SEd Tanous you may not use this file except in compliance with the License.
66be832e2SEd Tanous You may obtain a copy of the License at
76be832e2SEd Tanous 
86be832e2SEd Tanous       http://www.apache.org/licenses/LICENSE-2.0
96be832e2SEd Tanous 
106be832e2SEd Tanous Unless required by applicable law or agreed to in writing, software
116be832e2SEd Tanous distributed under the License is distributed on an "AS IS" BASIS,
126be832e2SEd Tanous WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136be832e2SEd Tanous See the License for the specific language governing permissions and
146be832e2SEd Tanous limitations under the License.
15729dae72SJennifer Lee */
16729dae72SJennifer Lee #pragma once
17729dae72SJennifer Lee 
18d61e5194STejas Patil #include "bmcweb_config.h"
19d61e5194STejas Patil 
203ccb3adbSEd Tanous #include "app.hpp"
213ccb3adbSEd Tanous #include "dbus_utility.hpp"
225b90429aSEd Tanous #include "error_messages.hpp"
23757178a5SEd Tanous #include "generated/enums/update_service.hpp"
240ed80c8cSGeorge Liu #include "multipart_parser.hpp"
252c6ffdb0SEd Tanous #include "ossl_random.hpp"
263ccb3adbSEd Tanous #include "query.hpp"
273ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
28a8e884fcSEd Tanous #include "task.hpp"
295b90429aSEd Tanous #include "task_messages.hpp"
3008d81adaSJohn Edward Broadbent #include "utils/collection.hpp"
313ccb3adbSEd Tanous #include "utils/dbus_utils.hpp"
325b90429aSEd Tanous #include "utils/json_utils.hpp"
333ccb3adbSEd Tanous #include "utils/sw_utils.hpp"
343ccb3adbSEd Tanous 
35de0c960cSJagpal Singh Gill #include <sys/mman.h>
36de0c960cSJagpal Singh Gill 
37e99073f5SGeorge Liu #include <boost/system/error_code.hpp>
38ef4c65b7SEd Tanous #include <boost/url/format.hpp>
391e1e598dSJonathan Doman #include <sdbusplus/asio/property.hpp>
403ccb3adbSEd Tanous #include <sdbusplus/bus/match.hpp>
41d1bde9e5SKrzysztof Grobelny #include <sdbusplus/unpack_properties.hpp>
421214b7e7SGunnar Mills 
432b73119cSGeorge Liu #include <array>
44de0c960cSJagpal Singh Gill #include <cstddef>
450ed80c8cSGeorge Liu #include <filesystem>
46c71b6c99SJagpal Singh Gill #include <functional>
47de0c960cSJagpal Singh Gill #include <iterator>
48ef93eab3SJagpal Singh Gill #include <memory>
497cb59f65SEd Tanous #include <optional>
507cb59f65SEd Tanous #include <string>
512b73119cSGeorge Liu #include <string_view>
52de0c960cSJagpal Singh Gill #include <unordered_map>
53ef93eab3SJagpal Singh Gill #include <vector>
542b73119cSGeorge Liu 
551abe55efSEd Tanous namespace redfish
561abe55efSEd Tanous {
5727826b5fSEd Tanous 
580e7de46fSAndrew Geissler // Match signals added on software path
59cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
6059d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
61cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
6259d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
630e7de46fSAndrew Geissler // Only allow one update at a time
64cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
650e7de46fSAndrew Geissler static bool fwUpdateInProgress = false;
6686adcd6dSAndrew Geissler // Timer for software available
67cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
68271584abSEd Tanous static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
6986adcd6dSAndrew Geissler 
70de0c960cSJagpal Singh Gill struct MemoryFileDescriptor
71de0c960cSJagpal Singh Gill {
72de0c960cSJagpal Singh Gill     int fd = -1;
73de0c960cSJagpal Singh Gill 
74de0c960cSJagpal Singh Gill     explicit MemoryFileDescriptor(const std::string& filename) :
75de0c960cSJagpal Singh Gill         fd(memfd_create(filename.c_str(), 0))
76de0c960cSJagpal Singh Gill     {}
77de0c960cSJagpal Singh Gill 
78de0c960cSJagpal Singh Gill     MemoryFileDescriptor(const MemoryFileDescriptor&) = default;
79de0c960cSJagpal Singh Gill     MemoryFileDescriptor(MemoryFileDescriptor&& other) noexcept : fd(other.fd)
80de0c960cSJagpal Singh Gill     {
81de0c960cSJagpal Singh Gill         other.fd = -1;
82de0c960cSJagpal Singh Gill     }
83de0c960cSJagpal Singh Gill     MemoryFileDescriptor& operator=(const MemoryFileDescriptor&) = delete;
84de0c960cSJagpal Singh Gill     MemoryFileDescriptor& operator=(MemoryFileDescriptor&&) = default;
85de0c960cSJagpal Singh Gill 
86de0c960cSJagpal Singh Gill     ~MemoryFileDescriptor()
87de0c960cSJagpal Singh Gill     {
88de0c960cSJagpal Singh Gill         if (fd != -1)
89de0c960cSJagpal Singh Gill         {
90de0c960cSJagpal Singh Gill             close(fd);
91de0c960cSJagpal Singh Gill         }
92de0c960cSJagpal Singh Gill     }
93de0c960cSJagpal Singh Gill 
94de0c960cSJagpal Singh Gill     bool rewind() const
95de0c960cSJagpal Singh Gill     {
96de0c960cSJagpal Singh Gill         if (lseek(fd, 0, SEEK_SET) == -1)
97de0c960cSJagpal Singh Gill         {
98de0c960cSJagpal Singh Gill             BMCWEB_LOG_ERROR("Failed to seek to beginning of image memfd");
99de0c960cSJagpal Singh Gill             return false;
100de0c960cSJagpal Singh Gill         }
101de0c960cSJagpal Singh Gill         return true;
102de0c960cSJagpal Singh Gill     }
103de0c960cSJagpal Singh Gill };
104de0c960cSJagpal Singh Gill 
105df254f2cSEd Tanous inline void cleanUp()
10686adcd6dSAndrew Geissler {
10786adcd6dSAndrew Geissler     fwUpdateInProgress = false;
10886adcd6dSAndrew Geissler     fwUpdateMatcher = nullptr;
1094cde5d90SJames Feist     fwUpdateErrorMatcher = nullptr;
11086adcd6dSAndrew Geissler }
111df254f2cSEd Tanous 
112df254f2cSEd Tanous inline void activateImage(const std::string& objPath,
11386adcd6dSAndrew Geissler                           const std::string& service)
11486adcd6dSAndrew Geissler {
11562598e31SEd Tanous     BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
1169ae226faSGeorge Liu     sdbusplus::asio::setProperty(
1179ae226faSGeorge Liu         *crow::connections::systemBus, service, objPath,
1189ae226faSGeorge Liu         "xyz.openbmc_project.Software.Activation", "RequestedActivation",
1199ae226faSGeorge Liu         "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
1208b24275dSEd Tanous         [](const boost::system::error_code& ec) {
1218b24275dSEd Tanous             if (ec)
12286adcd6dSAndrew Geissler             {
12362598e31SEd Tanous                 BMCWEB_LOG_DEBUG("error_code = {}", ec);
12462598e31SEd Tanous                 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
12586adcd6dSAndrew Geissler             }
1269ae226faSGeorge Liu         });
12786adcd6dSAndrew Geissler }
1280554c984SAndrew Geissler 
129c71b6c99SJagpal Singh Gill inline bool handleCreateTask(const boost::system::error_code& ec2,
130c71b6c99SJagpal Singh Gill                              sdbusplus::message_t& msg,
131c71b6c99SJagpal Singh Gill                              const std::shared_ptr<task::TaskData>& taskData)
132c71b6c99SJagpal Singh Gill {
133c71b6c99SJagpal Singh Gill     if (ec2)
134c71b6c99SJagpal Singh Gill     {
135c71b6c99SJagpal Singh Gill         return task::completed;
136c71b6c99SJagpal Singh Gill     }
137c71b6c99SJagpal Singh Gill 
138c71b6c99SJagpal Singh Gill     std::string iface;
139c71b6c99SJagpal Singh Gill     dbus::utility::DBusPropertiesMap values;
140c71b6c99SJagpal Singh Gill 
141c71b6c99SJagpal Singh Gill     std::string index = std::to_string(taskData->index);
142c71b6c99SJagpal Singh Gill     msg.read(iface, values);
143c71b6c99SJagpal Singh Gill 
144c71b6c99SJagpal Singh Gill     if (iface == "xyz.openbmc_project.Software.Activation")
145c71b6c99SJagpal Singh Gill     {
146c71b6c99SJagpal Singh Gill         const std::string* state = nullptr;
147c71b6c99SJagpal Singh Gill         for (const auto& property : values)
148c71b6c99SJagpal Singh Gill         {
149c71b6c99SJagpal Singh Gill             if (property.first == "Activation")
150c71b6c99SJagpal Singh Gill             {
151c71b6c99SJagpal Singh Gill                 state = std::get_if<std::string>(&property.second);
152c71b6c99SJagpal Singh Gill                 if (state == nullptr)
153c71b6c99SJagpal Singh Gill                 {
154c71b6c99SJagpal Singh Gill                     taskData->messages.emplace_back(messages::internalError());
155c71b6c99SJagpal Singh Gill                     return task::completed;
156c71b6c99SJagpal Singh Gill                 }
157c71b6c99SJagpal Singh Gill             }
158c71b6c99SJagpal Singh Gill         }
159c71b6c99SJagpal Singh Gill 
160c71b6c99SJagpal Singh Gill         if (state == nullptr)
161c71b6c99SJagpal Singh Gill         {
162c71b6c99SJagpal Singh Gill             return !task::completed;
163c71b6c99SJagpal Singh Gill         }
164c71b6c99SJagpal Singh Gill 
165c71b6c99SJagpal Singh Gill         if (state->ends_with("Invalid") || state->ends_with("Failed"))
166c71b6c99SJagpal Singh Gill         {
167c71b6c99SJagpal Singh Gill             taskData->state = "Exception";
168c71b6c99SJagpal Singh Gill             taskData->status = "Warning";
169c71b6c99SJagpal Singh Gill             taskData->messages.emplace_back(messages::taskAborted(index));
170c71b6c99SJagpal Singh Gill             return task::completed;
171c71b6c99SJagpal Singh Gill         }
172c71b6c99SJagpal Singh Gill 
173c71b6c99SJagpal Singh Gill         if (state->ends_with("Staged"))
174c71b6c99SJagpal Singh Gill         {
175c71b6c99SJagpal Singh Gill             taskData->state = "Stopping";
176c71b6c99SJagpal Singh Gill             taskData->messages.emplace_back(messages::taskPaused(index));
177c71b6c99SJagpal Singh Gill 
178c71b6c99SJagpal Singh Gill             // its staged, set a long timer to
179c71b6c99SJagpal Singh Gill             // allow them time to complete the
180c71b6c99SJagpal Singh Gill             // update (probably cycle the
181c71b6c99SJagpal Singh Gill             // system) if this expires then
182c71b6c99SJagpal Singh Gill             // task will be canceled
183c71b6c99SJagpal Singh Gill             taskData->extendTimer(std::chrono::hours(5));
184c71b6c99SJagpal Singh Gill             return !task::completed;
185c71b6c99SJagpal Singh Gill         }
186c71b6c99SJagpal Singh Gill 
187c71b6c99SJagpal Singh Gill         if (state->ends_with("Active"))
188c71b6c99SJagpal Singh Gill         {
189c71b6c99SJagpal Singh Gill             taskData->messages.emplace_back(messages::taskCompletedOK(index));
190c71b6c99SJagpal Singh Gill             taskData->state = "Completed";
191c71b6c99SJagpal Singh Gill             return task::completed;
192c71b6c99SJagpal Singh Gill         }
193c71b6c99SJagpal Singh Gill     }
194c71b6c99SJagpal Singh Gill     else if (iface == "xyz.openbmc_project.Software.ActivationProgress")
195c71b6c99SJagpal Singh Gill     {
196c71b6c99SJagpal Singh Gill         const uint8_t* progress = nullptr;
197c71b6c99SJagpal Singh Gill         for (const auto& property : values)
198c71b6c99SJagpal Singh Gill         {
199c71b6c99SJagpal Singh Gill             if (property.first == "Progress")
200c71b6c99SJagpal Singh Gill             {
201c71b6c99SJagpal Singh Gill                 progress = std::get_if<uint8_t>(&property.second);
202c71b6c99SJagpal Singh Gill                 if (progress == nullptr)
203c71b6c99SJagpal Singh Gill                 {
204c71b6c99SJagpal Singh Gill                     taskData->messages.emplace_back(messages::internalError());
205c71b6c99SJagpal Singh Gill                     return task::completed;
206c71b6c99SJagpal Singh Gill                 }
207c71b6c99SJagpal Singh Gill             }
208c71b6c99SJagpal Singh Gill         }
209c71b6c99SJagpal Singh Gill 
210c71b6c99SJagpal Singh Gill         if (progress == nullptr)
211c71b6c99SJagpal Singh Gill         {
212c71b6c99SJagpal Singh Gill             return !task::completed;
213c71b6c99SJagpal Singh Gill         }
214c71b6c99SJagpal Singh Gill         taskData->percentComplete = *progress;
215c71b6c99SJagpal Singh Gill         taskData->messages.emplace_back(
216c71b6c99SJagpal Singh Gill             messages::taskProgressChanged(index, *progress));
217c71b6c99SJagpal Singh Gill 
218c71b6c99SJagpal Singh Gill         // if we're getting status updates it's
219c71b6c99SJagpal Singh Gill         // still alive, update timer
220c71b6c99SJagpal Singh Gill         taskData->extendTimer(std::chrono::minutes(5));
221c71b6c99SJagpal Singh Gill     }
222c71b6c99SJagpal Singh Gill 
223c71b6c99SJagpal Singh Gill     // as firmware update often results in a
224c71b6c99SJagpal Singh Gill     // reboot, the task  may never "complete"
225c71b6c99SJagpal Singh Gill     // unless it is an error
226c71b6c99SJagpal Singh Gill 
227c71b6c99SJagpal Singh Gill     return !task::completed;
228c71b6c99SJagpal Singh Gill }
229c71b6c99SJagpal Singh Gill 
230c71b6c99SJagpal Singh Gill inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
231c71b6c99SJagpal Singh Gill                        task::Payload&& payload,
232c71b6c99SJagpal Singh Gill                        const sdbusplus::message::object_path& objPath)
233c71b6c99SJagpal Singh Gill {
234c71b6c99SJagpal Singh Gill     std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
235c71b6c99SJagpal Singh Gill         std::bind_front(handleCreateTask),
236c71b6c99SJagpal Singh Gill         "type='signal',interface='org.freedesktop.DBus.Properties',"
237c71b6c99SJagpal Singh Gill         "member='PropertiesChanged',path='" +
238c71b6c99SJagpal Singh Gill             objPath.str + "'");
239c71b6c99SJagpal Singh Gill     task->startTimer(std::chrono::minutes(5));
240c71b6c99SJagpal Singh Gill     task->populateResp(asyncResp->res);
241c71b6c99SJagpal Singh Gill     task->payload.emplace(std::move(payload));
242c71b6c99SJagpal Singh Gill }
243c71b6c99SJagpal Singh Gill 
2440554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
2450554c984SAndrew Geissler // then no asyncResp updates will occur
2464ff0f1f4SEd Tanous inline void
2478d1b46d7Szhanghch05     softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
24859d494eeSPatrick Williams                            sdbusplus::message_t& m, task::Payload&& payload)
24986adcd6dSAndrew Geissler {
25080f79a40SMichael Shen     dbus::utility::DBusInterfacesMap interfacesProperties;
25186adcd6dSAndrew Geissler 
25286adcd6dSAndrew Geissler     sdbusplus::message::object_path objPath;
25386adcd6dSAndrew Geissler 
25486adcd6dSAndrew Geissler     m.read(objPath, interfacesProperties);
25586adcd6dSAndrew Geissler 
25662598e31SEd Tanous     BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
257e3eb3d63SEd Tanous     for (const auto& interface : interfacesProperties)
25886adcd6dSAndrew Geissler     {
25962598e31SEd Tanous         BMCWEB_LOG_DEBUG("interface = {}", interface.first);
26086adcd6dSAndrew Geissler 
26186adcd6dSAndrew Geissler         if (interface.first == "xyz.openbmc_project.Software.Activation")
26286adcd6dSAndrew Geissler         {
26386adcd6dSAndrew Geissler             // Retrieve service and activate
2642b73119cSGeorge Liu             constexpr std::array<std::string_view, 1> interfaces = {
2652b73119cSGeorge Liu                 "xyz.openbmc_project.Software.Activation"};
2662b73119cSGeorge Liu             dbus::utility::getDbusObject(
2672b73119cSGeorge Liu                 objPath.str, interfaces,
268a3e65892SEd Tanous                 [objPath, asyncResp, payload(std::move(payload))](
2698b24275dSEd Tanous                     const boost::system::error_code& ec,
270a3e65892SEd Tanous                     const std::vector<
271a3e65892SEd Tanous                         std::pair<std::string, std::vector<std::string>>>&
272a3e65892SEd Tanous                         objInfo) mutable {
2738b24275dSEd Tanous                     if (ec)
27486adcd6dSAndrew Geissler                     {
27562598e31SEd Tanous                         BMCWEB_LOG_DEBUG("error_code = {}", ec);
27662598e31SEd Tanous                         BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
2770554c984SAndrew Geissler                         if (asyncResp)
2780554c984SAndrew Geissler                         {
27986adcd6dSAndrew Geissler                             messages::internalError(asyncResp->res);
2800554c984SAndrew Geissler                         }
28186adcd6dSAndrew Geissler                         cleanUp();
28286adcd6dSAndrew Geissler                         return;
28386adcd6dSAndrew Geissler                     }
28486adcd6dSAndrew Geissler                     // Ensure we only got one service back
28586adcd6dSAndrew Geissler                     if (objInfo.size() != 1)
28686adcd6dSAndrew Geissler                     {
287bd79bce8SPatrick Williams                         BMCWEB_LOG_ERROR("Invalid Object Size {}",
288bd79bce8SPatrick Williams                                          objInfo.size());
2890554c984SAndrew Geissler                         if (asyncResp)
2900554c984SAndrew Geissler                         {
29186adcd6dSAndrew Geissler                             messages::internalError(asyncResp->res);
2920554c984SAndrew Geissler                         }
29386adcd6dSAndrew Geissler                         cleanUp();
29486adcd6dSAndrew Geissler                         return;
29586adcd6dSAndrew Geissler                     }
29686adcd6dSAndrew Geissler                     // cancel timer only when
29786adcd6dSAndrew Geissler                     // xyz.openbmc_project.Software.Activation interface
29886adcd6dSAndrew Geissler                     // is added
29986adcd6dSAndrew Geissler                     fwAvailableTimer = nullptr;
30086adcd6dSAndrew Geissler 
30186adcd6dSAndrew Geissler                     activateImage(objPath.str, objInfo[0].first);
3020554c984SAndrew Geissler                     if (asyncResp)
3030554c984SAndrew Geissler                     {
304c71b6c99SJagpal Singh Gill                         createTask(asyncResp, std::move(payload), objPath);
3050554c984SAndrew Geissler                     }
30686adcd6dSAndrew Geissler                     fwUpdateInProgress = false;
3072b73119cSGeorge Liu                 });
30862bafc01SPatrick Williams 
30962bafc01SPatrick Williams             break;
31086adcd6dSAndrew Geissler         }
31186adcd6dSAndrew Geissler     }
31286adcd6dSAndrew Geissler }
31386adcd6dSAndrew Geissler 
3148549b951SMyung Bae inline void afterAvailbleTimerAsyncWait(
3158549b951SMyung Bae     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3168549b951SMyung Bae     const boost::system::error_code& ec)
3178549b951SMyung Bae {
3188549b951SMyung Bae     cleanUp();
3198549b951SMyung Bae     if (ec == boost::asio::error::operation_aborted)
3208549b951SMyung Bae     {
3218549b951SMyung Bae         // expected, we were canceled before the timer completed.
3228549b951SMyung Bae         return;
3238549b951SMyung Bae     }
3248549b951SMyung Bae     BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
3258549b951SMyung Bae     BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
3268549b951SMyung Bae     if (ec)
3278549b951SMyung Bae     {
3288549b951SMyung Bae         BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
3298549b951SMyung Bae         return;
3308549b951SMyung Bae     }
3318549b951SMyung Bae     if (asyncResp)
3328549b951SMyung Bae     {
3338549b951SMyung Bae         redfish::messages::internalError(asyncResp->res);
3348549b951SMyung Bae     }
3358549b951SMyung Bae }
3368549b951SMyung Bae 
3378549b951SMyung Bae inline void
3388549b951SMyung Bae     handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3398549b951SMyung Bae                           const std::string& url, const std::string& type)
3408549b951SMyung Bae {
3418549b951SMyung Bae     if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
3428549b951SMyung Bae     {
3438549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3448549b951SMyung Bae                                          "Invalid archive");
3458549b951SMyung Bae     }
3468549b951SMyung Bae     else if (type ==
3478549b951SMyung Bae              "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
3488549b951SMyung Bae     {
3498549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3508549b951SMyung Bae                                          "Invalid manifest");
3518549b951SMyung Bae     }
3528549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
3538549b951SMyung Bae     {
3548549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3558549b951SMyung Bae                                          "Invalid image format");
3568549b951SMyung Bae     }
3578549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
3588549b951SMyung Bae     {
3598549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3608549b951SMyung Bae                                          "Image version already exists");
3618549b951SMyung Bae 
3628549b951SMyung Bae         redfish::messages::resourceAlreadyExists(
3638549b951SMyung Bae             asyncResp->res, "UpdateService", "Version", "uploaded version");
3648549b951SMyung Bae     }
3658549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
3668549b951SMyung Bae     {
3678549b951SMyung Bae         redfish::messages::resourceExhaustion(asyncResp->res, url);
3688549b951SMyung Bae     }
3694034a652SMyung Bae     else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
3708549b951SMyung Bae     {
3714034a652SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3724034a652SMyung Bae                                          "Incompatible image version");
3734034a652SMyung Bae     }
3744034a652SMyung Bae     else if (type ==
3754034a652SMyung Bae              "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
3764034a652SMyung Bae     {
3774034a652SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3784034a652SMyung Bae                                          "Update Access Key Expired");
3794034a652SMyung Bae     }
3804034a652SMyung Bae     else if (type ==
3814034a652SMyung Bae              "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
3824034a652SMyung Bae     {
3834034a652SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3844034a652SMyung Bae                                          "Invalid image signature");
3854034a652SMyung Bae     }
3864034a652SMyung Bae     else if (type ==
3874034a652SMyung Bae                  "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
3884034a652SMyung Bae              type == "xyz.openbmc_project.Software.Version.Error.HostFile")
3894034a652SMyung Bae     {
3904034a652SMyung Bae         BMCWEB_LOG_ERROR("Software Image Error type={}", type);
3918549b951SMyung Bae         redfish::messages::internalError(asyncResp->res);
3928549b951SMyung Bae     }
3934034a652SMyung Bae     else
3944034a652SMyung Bae     {
3954034a652SMyung Bae         // Unrelated error types. Ignored
3964034a652SMyung Bae         BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
3974034a652SMyung Bae         return;
3984034a652SMyung Bae     }
3994034a652SMyung Bae     // Clear the timer
4004034a652SMyung Bae     fwAvailableTimer = nullptr;
4018549b951SMyung Bae }
4028549b951SMyung Bae 
4038549b951SMyung Bae inline void
4048549b951SMyung Bae     afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
4058549b951SMyung Bae                             const std::string& url, sdbusplus::message_t& m)
4068549b951SMyung Bae {
40780f79a40SMichael Shen     dbus::utility::DBusInterfacesMap interfacesProperties;
4088549b951SMyung Bae     sdbusplus::message::object_path objPath;
4098549b951SMyung Bae     m.read(objPath, interfacesProperties);
4108549b951SMyung Bae     BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
4118549b951SMyung Bae     for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
4128549b951SMyung Bae              interface : interfacesProperties)
4138549b951SMyung Bae     {
4148549b951SMyung Bae         if (interface.first == "xyz.openbmc_project.Logging.Entry")
4158549b951SMyung Bae         {
4168549b951SMyung Bae             for (const std::pair<std::string, dbus::utility::DbusVariantType>&
4178549b951SMyung Bae                      value : interface.second)
4188549b951SMyung Bae             {
4198549b951SMyung Bae                 if (value.first != "Message")
4208549b951SMyung Bae                 {
4218549b951SMyung Bae                     continue;
4228549b951SMyung Bae                 }
4238549b951SMyung Bae                 const std::string* type =
4248549b951SMyung Bae                     std::get_if<std::string>(&value.second);
4258549b951SMyung Bae                 if (type == nullptr)
4268549b951SMyung Bae                 {
4278549b951SMyung Bae                     // if this was our message, timeout will cover it
4288549b951SMyung Bae                     return;
4298549b951SMyung Bae                 }
4308549b951SMyung Bae                 handleUpdateErrorType(asyncResp, url, *type);
4318549b951SMyung Bae             }
4328549b951SMyung Bae         }
4338549b951SMyung Bae     }
4348549b951SMyung Bae }
4358549b951SMyung Bae 
4360554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
4370554c984SAndrew Geissler // then no asyncResp updates will occur
438f5139334SEd Tanous inline void monitorForSoftwareAvailable(
4398d1b46d7Szhanghch05     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
4408d1b46d7Szhanghch05     const crow::Request& req, const std::string& url,
4415d138943SGunnar Mills     int timeoutTimeSeconds = 25)
44286adcd6dSAndrew Geissler {
44386adcd6dSAndrew Geissler     // Only allow one FW update at a time
444e05aec50SEd Tanous     if (fwUpdateInProgress)
44586adcd6dSAndrew Geissler     {
4460554c984SAndrew Geissler         if (asyncResp)
4470554c984SAndrew Geissler         {
44886adcd6dSAndrew Geissler             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
4490554c984SAndrew Geissler         }
45086adcd6dSAndrew Geissler         return;
45186adcd6dSAndrew Geissler     }
45286adcd6dSAndrew Geissler 
4538e8245dbSEd Tanous     if (req.ioService == nullptr)
4548e8245dbSEd Tanous     {
4558e8245dbSEd Tanous         messages::internalError(asyncResp->res);
4568e8245dbSEd Tanous         return;
4578e8245dbSEd Tanous     }
4588e8245dbSEd Tanous 
4590554c984SAndrew Geissler     fwAvailableTimer =
460271584abSEd Tanous         std::make_unique<boost::asio::steady_timer>(*req.ioService);
46186adcd6dSAndrew Geissler 
462271584abSEd Tanous     fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
46386adcd6dSAndrew Geissler 
46486adcd6dSAndrew Geissler     fwAvailableTimer->async_wait(
4658549b951SMyung Bae         std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
4668549b951SMyung Bae 
467a3e65892SEd Tanous     task::Payload payload(req);
46859d494eeSPatrick Williams     auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
46962598e31SEd Tanous         BMCWEB_LOG_DEBUG("Match fired");
470a3e65892SEd Tanous         softwareInterfaceAdded(asyncResp, m, std::move(payload));
47186adcd6dSAndrew Geissler     };
47286adcd6dSAndrew Geissler 
47386adcd6dSAndrew Geissler     fwUpdateInProgress = true;
47486adcd6dSAndrew Geissler 
47559d494eeSPatrick Williams     fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
47686adcd6dSAndrew Geissler         *crow::connections::systemBus,
47786adcd6dSAndrew Geissler         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
47886adcd6dSAndrew Geissler         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
47986adcd6dSAndrew Geissler         callback);
4804cde5d90SJames Feist 
48159d494eeSPatrick Williams     fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
4824cde5d90SJames Feist         *crow::connections::systemBus,
483e1cc4828SBrian Ma         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
484e1cc4828SBrian Ma         "member='InterfacesAdded',"
485e1cc4828SBrian Ma         "path='/xyz/openbmc_project/logging'",
4868549b951SMyung Bae         std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
48786adcd6dSAndrew Geissler }
488729dae72SJennifer Lee 
489bd79bce8SPatrick Williams inline std::optional<boost::urls::url> parseSimpleUpdateUrl(
490bd79bce8SPatrick Williams     std::string imageURI, std::optional<std::string> transferProtocol,
491f86bcc87SEd Tanous     crow::Response& res)
492f86bcc87SEd Tanous {
493f86bcc87SEd Tanous     if (imageURI.find("://") == std::string::npos)
494f86bcc87SEd Tanous     {
495f86bcc87SEd Tanous         if (imageURI.starts_with("/"))
496f86bcc87SEd Tanous         {
497f86bcc87SEd Tanous             messages::actionParameterValueTypeError(
498f86bcc87SEd Tanous                 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
499f86bcc87SEd Tanous             return std::nullopt;
500f86bcc87SEd Tanous         }
501f86bcc87SEd Tanous         if (!transferProtocol)
502f86bcc87SEd Tanous         {
503f86bcc87SEd Tanous             messages::actionParameterValueTypeError(
504f86bcc87SEd Tanous                 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
505f86bcc87SEd Tanous             return std::nullopt;
506f86bcc87SEd Tanous         }
507e5cf777eSEd Tanous         // OpenBMC currently only supports TFTP or HTTPS
508757178a5SEd Tanous         if (*transferProtocol == "TFTP")
509757178a5SEd Tanous         {
510757178a5SEd Tanous             imageURI = "tftp://" + imageURI;
511757178a5SEd Tanous         }
512e5cf777eSEd Tanous         else if (*transferProtocol == "HTTPS")
513e5cf777eSEd Tanous         {
514e5cf777eSEd Tanous             imageURI = "https://" + imageURI;
515e5cf777eSEd Tanous         }
516757178a5SEd Tanous         else
517f86bcc87SEd Tanous         {
518f86bcc87SEd Tanous             messages::actionParameterNotSupported(res, "TransferProtocol",
519f86bcc87SEd Tanous                                                   *transferProtocol);
520f86bcc87SEd Tanous             BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
521f86bcc87SEd Tanous                              *transferProtocol);
522f86bcc87SEd Tanous             return std::nullopt;
523f86bcc87SEd Tanous         }
524f86bcc87SEd Tanous     }
525f86bcc87SEd Tanous 
526f86bcc87SEd Tanous     boost::system::result<boost::urls::url> url =
527f86bcc87SEd Tanous         boost::urls::parse_absolute_uri(imageURI);
528f86bcc87SEd Tanous     if (!url)
529f86bcc87SEd Tanous     {
530f86bcc87SEd Tanous         messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
531f86bcc87SEd Tanous                                                 "UpdateService.SimpleUpdate");
532f86bcc87SEd Tanous 
533f86bcc87SEd Tanous         return std::nullopt;
534f86bcc87SEd Tanous     }
535f86bcc87SEd Tanous     url->normalize();
536f86bcc87SEd Tanous 
537757178a5SEd Tanous     if (url->scheme() == "tftp")
538757178a5SEd Tanous     {
539757178a5SEd Tanous         if (url->encoded_path().size() < 2)
540757178a5SEd Tanous         {
541757178a5SEd Tanous             messages::actionParameterNotSupported(res, "ImageURI",
542757178a5SEd Tanous                                                   url->buffer());
543757178a5SEd Tanous             return std::nullopt;
544757178a5SEd Tanous         }
545757178a5SEd Tanous     }
546e5cf777eSEd Tanous     else if (url->scheme() == "https")
547e5cf777eSEd Tanous     {
548e5cf777eSEd Tanous         // Empty paths default to "/"
549e5cf777eSEd Tanous         if (url->encoded_path().empty())
550e5cf777eSEd Tanous         {
551e5cf777eSEd Tanous             url->set_encoded_path("/");
552e5cf777eSEd Tanous         }
553e5cf777eSEd Tanous     }
554757178a5SEd Tanous     else
555f86bcc87SEd Tanous     {
556f86bcc87SEd Tanous         messages::actionParameterNotSupported(res, "ImageURI", imageURI);
557f86bcc87SEd Tanous         return std::nullopt;
558f86bcc87SEd Tanous     }
559757178a5SEd Tanous 
560757178a5SEd Tanous     if (url->encoded_path().empty())
561f86bcc87SEd Tanous     {
562757178a5SEd Tanous         messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
563757178a5SEd Tanous                                                 "UpdateService.SimpleUpdate");
564f86bcc87SEd Tanous         return std::nullopt;
565f86bcc87SEd Tanous     }
566757178a5SEd Tanous 
567757178a5SEd Tanous     return *url;
568f86bcc87SEd Tanous }
569f86bcc87SEd Tanous 
570e5cf777eSEd Tanous inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
571e5cf777eSEd Tanous                           const boost::urls::url_view_base& url)
572e5cf777eSEd Tanous {
573e5cf777eSEd Tanous     messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
574e5cf777eSEd Tanous                                           url.buffer());
575e5cf777eSEd Tanous }
576e5cf777eSEd Tanous 
577f5139334SEd Tanous inline void handleUpdateServiceSimpleUpdateAction(
578f5139334SEd Tanous     crow::App& app, const crow::Request& req,
579f5139334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
5800554c984SAndrew Geissler {
5813ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
58245ca1b86SEd Tanous     {
58345ca1b86SEd Tanous         return;
58445ca1b86SEd Tanous     }
58545ca1b86SEd Tanous 
5860554c984SAndrew Geissler     std::optional<std::string> transferProtocol;
5870554c984SAndrew Geissler     std::string imageURI;
5880554c984SAndrew Geissler 
58962598e31SEd Tanous     BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
5900554c984SAndrew Geissler 
5910554c984SAndrew Geissler     // User can pass in both TransferProtocol and ImageURI parameters or
5924e0453b1SGunnar Mills     // they can pass in just the ImageURI with the transfer protocol
5934e0453b1SGunnar Mills     // embedded within it.
5940554c984SAndrew Geissler     // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
5950554c984SAndrew Geissler     // 2) ImageURI:tftp://1.1.1.1/myfile.bin
5960554c984SAndrew Geissler 
597002d39b4SEd Tanous     if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
598002d39b4SEd Tanous                                    transferProtocol, "ImageURI", imageURI))
5990554c984SAndrew Geissler     {
60062598e31SEd Tanous         BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
6010554c984SAndrew Geissler         return;
6020554c984SAndrew Geissler     }
603f5139334SEd Tanous 
604757178a5SEd Tanous     std::optional<boost::urls::url> url =
605757178a5SEd Tanous         parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res);
606757178a5SEd Tanous     if (!url)
6070554c984SAndrew Geissler     {
6080554c984SAndrew Geissler         return;
6090554c984SAndrew Geissler     }
6104e338b23SJagpal Singh Gill     if (url->scheme() == "https")
611e5cf777eSEd Tanous     {
612e5cf777eSEd Tanous         doHttpsUpdate(asyncResp, *url);
613e5cf777eSEd Tanous     }
614757178a5SEd Tanous     else
615757178a5SEd Tanous     {
616757178a5SEd Tanous         messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
617757178a5SEd Tanous                                               url->buffer());
618757178a5SEd Tanous         return;
619757178a5SEd Tanous     }
6200554c984SAndrew Geissler 
62162598e31SEd Tanous     BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
622729dae72SJennifer Lee }
623729dae72SJennifer Lee 
6240ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body)
6250ed80c8cSGeorge Liu {
6262c6ffdb0SEd Tanous     std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
6272c6ffdb0SEd Tanous 
62862598e31SEd Tanous     BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
6290ed80c8cSGeorge Liu     std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
6300ed80c8cSGeorge Liu                                     std::ofstream::trunc);
6310ed80c8cSGeorge Liu     // set the permission of the file to 640
632bd79bce8SPatrick Williams     std::filesystem::perms permission =
633bd79bce8SPatrick Williams         std::filesystem::perms::owner_read | std::filesystem::perms::group_read;
6340ed80c8cSGeorge Liu     std::filesystem::permissions(filepath, permission);
6350ed80c8cSGeorge Liu     out << body;
6360ed80c8cSGeorge Liu 
6370ed80c8cSGeorge Liu     if (out.bad())
6380ed80c8cSGeorge Liu     {
6390ed80c8cSGeorge Liu         messages::internalError(res);
6400ed80c8cSGeorge Liu         cleanUp();
6410ed80c8cSGeorge Liu     }
6420ed80c8cSGeorge Liu }
6430ed80c8cSGeorge Liu 
644de0c960cSJagpal Singh Gill // Convert the Request Apply Time to the D-Bus value
645de0c960cSJagpal Singh Gill inline bool convertApplyTime(crow::Response& res, const std::string& applyTime,
646de0c960cSJagpal Singh Gill                              std::string& applyTimeNewVal)
647de0c960cSJagpal Singh Gill {
648de0c960cSJagpal Singh Gill     if (applyTime == "Immediate")
649de0c960cSJagpal Singh Gill     {
650de0c960cSJagpal Singh Gill         applyTimeNewVal =
651049079f6SJagpal Singh Gill             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
652de0c960cSJagpal Singh Gill     }
653de0c960cSJagpal Singh Gill     else if (applyTime == "OnReset")
654de0c960cSJagpal Singh Gill     {
655de0c960cSJagpal Singh Gill         applyTimeNewVal =
656049079f6SJagpal Singh Gill             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
657de0c960cSJagpal Singh Gill     }
658de0c960cSJagpal Singh Gill     else
659de0c960cSJagpal Singh Gill     {
660de0c960cSJagpal Singh Gill         BMCWEB_LOG_WARNING(
661de0c960cSJagpal Singh Gill             "ApplyTime value {} is not in the list of acceptable values",
662de0c960cSJagpal Singh Gill             applyTime);
663de0c960cSJagpal Singh Gill         messages::propertyValueNotInList(res, applyTime, "ApplyTime");
664de0c960cSJagpal Singh Gill         return false;
665de0c960cSJagpal Singh Gill     }
666de0c960cSJagpal Singh Gill     return true;
667de0c960cSJagpal Singh Gill }
668de0c960cSJagpal Singh Gill 
6690ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
6700ed80c8cSGeorge Liu                          const std::string& applyTime)
6710ed80c8cSGeorge Liu {
6720ed80c8cSGeorge Liu     std::string applyTimeNewVal;
673049079f6SJagpal Singh Gill     if (!convertApplyTime(asyncResp->res, applyTime, applyTimeNewVal))
6740ed80c8cSGeorge Liu     {
6750ed80c8cSGeorge Liu         return;
6760ed80c8cSGeorge Liu     }
6770ed80c8cSGeorge Liu 
678e93abac6SGinu George     setDbusProperty(asyncResp, "ApplyTime", "xyz.openbmc_project.Settings",
679d02aad39SEd Tanous                     sdbusplus::message::object_path(
680d02aad39SEd Tanous                         "/xyz/openbmc_project/software/apply_time"),
681d02aad39SEd Tanous                     "xyz.openbmc_project.Software.ApplyTime",
682e93abac6SGinu George                     "RequestedApplyTime", applyTimeNewVal);
6830ed80c8cSGeorge Liu }
6840ed80c8cSGeorge Liu 
685ef93eab3SJagpal Singh Gill struct MultiPartUpdateParameters
6860ed80c8cSGeorge Liu {
687ef93eab3SJagpal Singh Gill     std::optional<std::string> applyTime;
688ef93eab3SJagpal Singh Gill     std::string uploadData;
689de0c960cSJagpal Singh Gill     std::vector<std::string> targets;
690ef93eab3SJagpal Singh Gill };
691ef93eab3SJagpal Singh Gill 
692de0c960cSJagpal Singh Gill inline std::optional<std::string>
693de0c960cSJagpal Singh Gill     processUrl(boost::system::result<boost::urls::url_view>& url)
694de0c960cSJagpal Singh Gill {
695de0c960cSJagpal Singh Gill     if (!url)
696de0c960cSJagpal Singh Gill     {
697de0c960cSJagpal Singh Gill         return std::nullopt;
698de0c960cSJagpal Singh Gill     }
699de0c960cSJagpal Singh Gill     if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers",
700de0c960cSJagpal Singh Gill                                        BMCWEB_REDFISH_MANAGER_URI_NAME))
701de0c960cSJagpal Singh Gill     {
702de0c960cSJagpal Singh Gill         return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME));
703de0c960cSJagpal Singh Gill     }
704de0c960cSJagpal Singh Gill     if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
705de0c960cSJagpal Singh Gill     {
706de0c960cSJagpal Singh Gill         return std::nullopt;
707de0c960cSJagpal Singh Gill     }
708de0c960cSJagpal Singh Gill     std::string firmwareId;
709de0c960cSJagpal Singh Gill     if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService",
710de0c960cSJagpal Singh Gill                                         "FirmwareInventory",
711de0c960cSJagpal Singh Gill                                         std::ref(firmwareId)))
712de0c960cSJagpal Singh Gill     {
713de0c960cSJagpal Singh Gill         return std::nullopt;
714de0c960cSJagpal Singh Gill     }
715de0c960cSJagpal Singh Gill 
716de0c960cSJagpal Singh Gill     return std::make_optional(firmwareId);
717de0c960cSJagpal Singh Gill }
718de0c960cSJagpal Singh Gill 
719ef93eab3SJagpal Singh Gill inline std::optional<MultiPartUpdateParameters>
720ef93eab3SJagpal Singh Gill     extractMultipartUpdateParameters(
721ef93eab3SJagpal Singh Gill         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
722ef93eab3SJagpal Singh Gill         MultipartParser parser)
723ef93eab3SJagpal Singh Gill {
724ef93eab3SJagpal Singh Gill     MultiPartUpdateParameters multiRet;
725ef93eab3SJagpal Singh Gill     for (FormPart& formpart : parser.mime_fields)
7260ed80c8cSGeorge Liu     {
7270ed80c8cSGeorge Liu         boost::beast::http::fields::const_iterator it =
7280ed80c8cSGeorge Liu             formpart.fields.find("Content-Disposition");
7290ed80c8cSGeorge Liu         if (it == formpart.fields.end())
7300ed80c8cSGeorge Liu         {
73162598e31SEd Tanous             BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
732ef93eab3SJagpal Singh Gill             return std::nullopt;
7330ed80c8cSGeorge Liu         }
73462598e31SEd Tanous         BMCWEB_LOG_INFO("Parsing value {}", it->value());
7350ed80c8cSGeorge Liu 
7360ed80c8cSGeorge Liu         // The construction parameters of param_list must start with `;`
7370ed80c8cSGeorge Liu         size_t index = it->value().find(';');
7380ed80c8cSGeorge Liu         if (index == std::string::npos)
7390ed80c8cSGeorge Liu         {
7400ed80c8cSGeorge Liu             continue;
7410ed80c8cSGeorge Liu         }
7420ed80c8cSGeorge Liu 
74389492a15SPatrick Williams         for (const auto& param :
7440ed80c8cSGeorge Liu              boost::beast::http::param_list{it->value().substr(index)})
7450ed80c8cSGeorge Liu         {
7460ed80c8cSGeorge Liu             if (param.first != "name" || param.second.empty())
7470ed80c8cSGeorge Liu             {
7480ed80c8cSGeorge Liu                 continue;
7490ed80c8cSGeorge Liu             }
7500ed80c8cSGeorge Liu 
7510ed80c8cSGeorge Liu             if (param.second == "UpdateParameters")
7520ed80c8cSGeorge Liu             {
753ef93eab3SJagpal Singh Gill                 std::vector<std::string> tempTargets;
754bd79bce8SPatrick Williams                 nlohmann::json content =
755bd79bce8SPatrick Williams                     nlohmann::json::parse(formpart.content, nullptr, false);
756ac1e1246SEd Tanous                 if (content.is_discarded())
757ac1e1246SEd Tanous                 {
758ac1e1246SEd Tanous                     return std::nullopt;
759ac1e1246SEd Tanous                 }
7607cb59f65SEd Tanous                 nlohmann::json::object_t* obj =
7617cb59f65SEd Tanous                     content.get_ptr<nlohmann::json::object_t*>();
7627cb59f65SEd Tanous                 if (obj == nullptr)
7637cb59f65SEd Tanous                 {
764ef93eab3SJagpal Singh Gill                     messages::propertyValueTypeError(
765ef93eab3SJagpal Singh Gill                         asyncResp->res, formpart.content, "UpdateParameters");
766ef93eab3SJagpal Singh Gill                     return std::nullopt;
7677cb59f65SEd Tanous                 }
7687cb59f65SEd Tanous 
7697cb59f65SEd Tanous                 if (!json_util::readJsonObject(
770ef93eab3SJagpal Singh Gill                         *obj, asyncResp->res, "Targets", tempTargets,
771ef93eab3SJagpal Singh Gill                         "@Redfish.OperationApplyTime", multiRet.applyTime))
7720ed80c8cSGeorge Liu                 {
773ef93eab3SJagpal Singh Gill                     return std::nullopt;
7740ed80c8cSGeorge Liu                 }
775ef93eab3SJagpal Singh Gill 
776ef93eab3SJagpal Singh Gill                 for (size_t urlIndex = 0; urlIndex < tempTargets.size();
777ef93eab3SJagpal Singh Gill                      urlIndex++)
7780ed80c8cSGeorge Liu                 {
779ef93eab3SJagpal Singh Gill                     const std::string& target = tempTargets[urlIndex];
780ef93eab3SJagpal Singh Gill                     boost::system::result<boost::urls::url_view> url =
781ef93eab3SJagpal Singh Gill                         boost::urls::parse_origin_form(target);
782de0c960cSJagpal Singh Gill                     auto res = processUrl(url);
783de0c960cSJagpal Singh Gill                     if (!res.has_value())
7840ed80c8cSGeorge Liu                     {
785ef93eab3SJagpal Singh Gill                         messages::propertyValueFormatError(
786ef93eab3SJagpal Singh Gill                             asyncResp->res, target,
787ef93eab3SJagpal Singh Gill                             std::format("Targets/{}", urlIndex));
788ef93eab3SJagpal Singh Gill                         return std::nullopt;
7890ed80c8cSGeorge Liu                     }
790de0c960cSJagpal Singh Gill                     multiRet.targets.emplace_back(res.value());
791ef93eab3SJagpal Singh Gill                 }
792ef93eab3SJagpal Singh Gill                 if (multiRet.targets.size() != 1)
793ef93eab3SJagpal Singh Gill                 {
794ef93eab3SJagpal Singh Gill                     messages::propertyValueFormatError(
795ef93eab3SJagpal Singh Gill                         asyncResp->res, multiRet.targets, "Targets");
796ef93eab3SJagpal Singh Gill                     return std::nullopt;
797ef93eab3SJagpal Singh Gill                 }
7980ed80c8cSGeorge Liu             }
7990ed80c8cSGeorge Liu             else if (param.second == "UpdateFile")
8000ed80c8cSGeorge Liu             {
801ef93eab3SJagpal Singh Gill                 multiRet.uploadData = std::move(formpart.content);
8020ed80c8cSGeorge Liu             }
8030ed80c8cSGeorge Liu         }
8040ed80c8cSGeorge Liu     }
8050ed80c8cSGeorge Liu 
806ef93eab3SJagpal Singh Gill     if (multiRet.uploadData.empty())
8070ed80c8cSGeorge Liu     {
80862598e31SEd Tanous         BMCWEB_LOG_ERROR("Upload data is NULL");
8090ed80c8cSGeorge Liu         messages::propertyMissing(asyncResp->res, "UpdateFile");
810ef93eab3SJagpal Singh Gill         return std::nullopt;
8110ed80c8cSGeorge Liu     }
812ef93eab3SJagpal Singh Gill     if (multiRet.targets.empty())
8130ed80c8cSGeorge Liu     {
814ef93eab3SJagpal Singh Gill         messages::propertyMissing(asyncResp->res, "Targets");
815ef93eab3SJagpal Singh Gill         return std::nullopt;
816ef93eab3SJagpal Singh Gill     }
817ef93eab3SJagpal Singh Gill     return multiRet;
8180ed80c8cSGeorge Liu }
8190ed80c8cSGeorge Liu 
820bd79bce8SPatrick Williams inline void handleStartUpdate(
821bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
822bd79bce8SPatrick Williams     const std::string& objectPath, const boost::system::error_code& ec,
823de0c960cSJagpal Singh Gill     const sdbusplus::message::object_path& retPath)
824de0c960cSJagpal Singh Gill {
825de0c960cSJagpal Singh Gill     if (ec)
826de0c960cSJagpal Singh Gill     {
827de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("error_code = {}", ec);
828de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("error msg = {}", ec.message());
829de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
830de0c960cSJagpal Singh Gill         return;
831de0c960cSJagpal Singh Gill     }
832de0c960cSJagpal Singh Gill 
833587090cdSJagpal Singh Gill     BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}",
834587090cdSJagpal Singh Gill                     objectPath, retPath.str);
835587090cdSJagpal Singh Gill     createTask(asyncResp, std::move(payload), retPath);
836de0c960cSJagpal Singh Gill }
837de0c960cSJagpal Singh Gill 
838bd79bce8SPatrick Williams inline void startUpdate(
839bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
840bd79bce8SPatrick Williams     const MemoryFileDescriptor& memfd, const std::string& applyTime,
841bd79bce8SPatrick Williams     const std::string& objectPath, const std::string& serviceName)
842de0c960cSJagpal Singh Gill {
843de0c960cSJagpal Singh Gill     crow::connections::systemBus->async_method_call(
844de0c960cSJagpal Singh Gill         [asyncResp, payload = std::move(payload),
845de0c960cSJagpal Singh Gill          objectPath](const boost::system::error_code& ec1,
846de0c960cSJagpal Singh Gill                      const sdbusplus::message::object_path& retPath) mutable {
847de0c960cSJagpal Singh Gill             handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1,
848de0c960cSJagpal Singh Gill                               retPath);
849de0c960cSJagpal Singh Gill         },
850de0c960cSJagpal Singh Gill         serviceName, objectPath, "xyz.openbmc_project.Software.Update",
851de0c960cSJagpal Singh Gill         "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime);
852de0c960cSJagpal Singh Gill }
853de0c960cSJagpal Singh Gill 
85408f61d53SJagpal Singh Gill inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
85508f61d53SJagpal Singh Gill                       task::Payload payload, const MemoryFileDescriptor& memfd,
85608f61d53SJagpal Singh Gill                       const std::string& applyTime, const std::string& target,
857de0c960cSJagpal Singh Gill                       const boost::system::error_code& ec,
858de0c960cSJagpal Singh Gill                       const dbus::utility::MapperGetSubTreeResponse& subtree)
859de0c960cSJagpal Singh Gill {
86008f61d53SJagpal Singh Gill     using SwInfoMap = std::unordered_map<
86108f61d53SJagpal Singh Gill         std::string, std::pair<sdbusplus::message::object_path, std::string>>;
862de0c960cSJagpal Singh Gill     SwInfoMap swInfoMap;
863de0c960cSJagpal Singh Gill 
864de0c960cSJagpal Singh Gill     if (ec)
865de0c960cSJagpal Singh Gill     {
866de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("error_code = {}", ec);
867de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("error msg = {}", ec.message());
868de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
869de0c960cSJagpal Singh Gill         return;
870de0c960cSJagpal Singh Gill     }
871de0c960cSJagpal Singh Gill     BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size());
872de0c960cSJagpal Singh Gill 
87308f61d53SJagpal Singh Gill     for (const auto& entry : subtree)
874de0c960cSJagpal Singh Gill     {
87508f61d53SJagpal Singh Gill         sdbusplus::message::object_path path(entry.first);
876de0c960cSJagpal Singh Gill         std::string swId = path.filename();
87708f61d53SJagpal Singh Gill         swInfoMap.emplace(swId, make_pair(path, entry.second[0].first));
878de0c960cSJagpal Singh Gill     }
879de0c960cSJagpal Singh Gill 
880de0c960cSJagpal Singh Gill     auto swEntry = swInfoMap.find(target);
881de0c960cSJagpal Singh Gill     if (swEntry == swInfoMap.end())
882de0c960cSJagpal Singh Gill     {
883de0c960cSJagpal Singh Gill         BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target);
884de0c960cSJagpal Singh Gill         messages::propertyValueFormatError(asyncResp->res, target, "Targets");
885de0c960cSJagpal Singh Gill         return;
886de0c960cSJagpal Singh Gill     }
887de0c960cSJagpal Singh Gill 
88808f61d53SJagpal Singh Gill     BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}",
88908f61d53SJagpal Singh Gill                      swEntry->second.first.str, swEntry->second.second);
890de0c960cSJagpal Singh Gill 
89108f61d53SJagpal Singh Gill     startUpdate(asyncResp, std::move(payload), memfd, applyTime,
89208f61d53SJagpal Singh Gill                 swEntry->second.first.str, swEntry->second.second);
89308f61d53SJagpal Singh Gill }
89408f61d53SJagpal Singh Gill 
895bd79bce8SPatrick Williams inline void handleBMCUpdate(
896bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
897bd79bce8SPatrick Williams     const MemoryFileDescriptor& memfd, const std::string& applyTime,
89808f61d53SJagpal Singh Gill     const boost::system::error_code& ec,
89908f61d53SJagpal Singh Gill     const dbus::utility::MapperEndPoints& functionalSoftware)
90008f61d53SJagpal Singh Gill {
90108f61d53SJagpal Singh Gill     if (ec)
90208f61d53SJagpal Singh Gill     {
90308f61d53SJagpal Singh Gill         BMCWEB_LOG_ERROR("error_code = {}", ec);
90408f61d53SJagpal Singh Gill         BMCWEB_LOG_ERROR("error msg = {}", ec.message());
90508f61d53SJagpal Singh Gill         messages::internalError(asyncResp->res);
90608f61d53SJagpal Singh Gill         return;
90708f61d53SJagpal Singh Gill     }
90808f61d53SJagpal Singh Gill     if (functionalSoftware.size() != 1)
90908f61d53SJagpal Singh Gill     {
91008f61d53SJagpal Singh Gill         BMCWEB_LOG_ERROR("Found {} functional software endpoints",
91108f61d53SJagpal Singh Gill                          functionalSoftware.size());
91208f61d53SJagpal Singh Gill         messages::internalError(asyncResp->res);
91308f61d53SJagpal Singh Gill         return;
91408f61d53SJagpal Singh Gill     }
91508f61d53SJagpal Singh Gill 
91608f61d53SJagpal Singh Gill     startUpdate(asyncResp, std::move(payload), memfd, applyTime,
91708f61d53SJagpal Singh Gill                 functionalSoftware[0], "xyz.openbmc_project.Software.Manager");
918de0c960cSJagpal Singh Gill }
919de0c960cSJagpal Singh Gill 
920bd79bce8SPatrick Williams inline void processUpdateRequest(
921bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
9229dae4deeSJagpal Singh Gill     task::Payload&& payload, std::string_view body,
923bd79bce8SPatrick Williams     const std::string& applyTime, std::vector<std::string>& targets)
924de0c960cSJagpal Singh Gill {
925de0c960cSJagpal Singh Gill     MemoryFileDescriptor memfd("update-image");
926de0c960cSJagpal Singh Gill     if (memfd.fd == -1)
927de0c960cSJagpal Singh Gill     {
928de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("Failed to create image memfd");
929de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
930de0c960cSJagpal Singh Gill         return;
931de0c960cSJagpal Singh Gill     }
932de0c960cSJagpal Singh Gill     if (write(memfd.fd, body.data(), body.length()) !=
933de0c960cSJagpal Singh Gill         static_cast<ssize_t>(body.length()))
934de0c960cSJagpal Singh Gill     {
935de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("Failed to write to image memfd");
936de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
937de0c960cSJagpal Singh Gill         return;
938de0c960cSJagpal Singh Gill     }
939de0c960cSJagpal Singh Gill     if (!memfd.rewind())
940de0c960cSJagpal Singh Gill     {
941de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
942de0c960cSJagpal Singh Gill         return;
943de0c960cSJagpal Singh Gill     }
944de0c960cSJagpal Singh Gill 
945de0c960cSJagpal Singh Gill     if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME)
946de0c960cSJagpal Singh Gill     {
94708f61d53SJagpal Singh Gill         dbus::utility::getAssociationEndPoints(
948*89449bbeSJagpal Singh Gill             "/xyz/openbmc_project/software/bmc/updateable",
94908f61d53SJagpal Singh Gill             [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
95008f61d53SJagpal Singh Gill              applyTime](
95108f61d53SJagpal Singh Gill                 const boost::system::error_code& ec,
95208f61d53SJagpal Singh Gill                 const dbus::utility::MapperEndPoints& objectPaths) mutable {
953bd79bce8SPatrick Williams                 handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime,
954bd79bce8SPatrick Williams                                 ec, objectPaths);
95508f61d53SJagpal Singh Gill             });
956de0c960cSJagpal Singh Gill     }
957de0c960cSJagpal Singh Gill     else
958de0c960cSJagpal Singh Gill     {
959de0c960cSJagpal Singh Gill         constexpr std::array<std::string_view, 1> interfaces = {
960de0c960cSJagpal Singh Gill             "xyz.openbmc_project.Software.Version"};
96108f61d53SJagpal Singh Gill         dbus::utility::getSubTree(
962de0c960cSJagpal Singh Gill             "/xyz/openbmc_project/software", 1, interfaces,
963de0c960cSJagpal Singh Gill             [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
96408f61d53SJagpal Singh Gill              applyTime, targets](const boost::system::error_code& ec,
96508f61d53SJagpal Singh Gill                                  const dbus::utility::MapperGetSubTreeResponse&
966de0c960cSJagpal Singh Gill                                      subtree) mutable {
96708f61d53SJagpal Singh Gill                 getSwInfo(asyncResp, std::move(payload), memfd, applyTime,
96808f61d53SJagpal Singh Gill                           targets[0], ec, subtree);
969de0c960cSJagpal Singh Gill             });
970de0c960cSJagpal Singh Gill     }
971de0c960cSJagpal Singh Gill }
972de0c960cSJagpal Singh Gill 
973de0c960cSJagpal Singh Gill inline void
974ef93eab3SJagpal Singh Gill     updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
975ef93eab3SJagpal Singh Gill                            const crow::Request& req, MultipartParser&& parser)
976ef93eab3SJagpal Singh Gill {
977ef93eab3SJagpal Singh Gill     std::optional<MultiPartUpdateParameters> multipart =
978ef93eab3SJagpal Singh Gill         extractMultipartUpdateParameters(asyncResp, std::move(parser));
979ef93eab3SJagpal Singh Gill     if (!multipart)
980ef93eab3SJagpal Singh Gill     {
981ef93eab3SJagpal Singh Gill         return;
982ef93eab3SJagpal Singh Gill     }
983ef93eab3SJagpal Singh Gill     if (!multipart->applyTime)
984ef93eab3SJagpal Singh Gill     {
985ef93eab3SJagpal Singh Gill         multipart->applyTime = "OnReset";
986ef93eab3SJagpal Singh Gill     }
987ef93eab3SJagpal Singh Gill 
988de0c960cSJagpal Singh Gill     if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
989de0c960cSJagpal Singh Gill     {
9909dae4deeSJagpal Singh Gill         std::string applyTimeNewVal;
9919dae4deeSJagpal Singh Gill         if (!convertApplyTime(asyncResp->res, *multipart->applyTime,
9929dae4deeSJagpal Singh Gill                               applyTimeNewVal))
9939dae4deeSJagpal Singh Gill         {
9949dae4deeSJagpal Singh Gill             return;
9959dae4deeSJagpal Singh Gill         }
9969dae4deeSJagpal Singh Gill         task::Payload payload(req);
9979dae4deeSJagpal Singh Gill 
9989dae4deeSJagpal Singh Gill         processUpdateRequest(asyncResp, std::move(payload),
9999dae4deeSJagpal Singh Gill                              multipart->uploadData, applyTimeNewVal,
10009dae4deeSJagpal Singh Gill                              multipart->targets);
1001de0c960cSJagpal Singh Gill     }
1002de0c960cSJagpal Singh Gill     else
1003de0c960cSJagpal Singh Gill     {
1004ef93eab3SJagpal Singh Gill         setApplyTime(asyncResp, *multipart->applyTime);
10050ed80c8cSGeorge Liu 
10066b54e4e0SEd Tanous         // Setup callback for when new software detected
1007de0c960cSJagpal Singh Gill         monitorForSoftwareAvailable(asyncResp, req,
1008de0c960cSJagpal Singh Gill                                     "/redfish/v1/UpdateService");
10096b54e4e0SEd Tanous 
1010ef93eab3SJagpal Singh Gill         uploadImageFile(asyncResp->res, multipart->uploadData);
10110ed80c8cSGeorge Liu     }
1012de0c960cSJagpal Singh Gill }
10130ed80c8cSGeorge Liu 
10149dae4deeSJagpal Singh Gill inline void doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
10159dae4deeSJagpal Singh Gill                          const crow::Request& req)
10169dae4deeSJagpal Singh Gill {
10179dae4deeSJagpal Singh Gill     if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
10189dae4deeSJagpal Singh Gill     {
10199dae4deeSJagpal Singh Gill         task::Payload payload(req);
10209dae4deeSJagpal Singh Gill         // HTTP push only supports BMC updates (with ApplyTime as immediate) for
10219dae4deeSJagpal Singh Gill         // backwards compatibility. Specific component updates will be handled
10229dae4deeSJagpal Singh Gill         // through Multipart form HTTP push.
10239dae4deeSJagpal Singh Gill         std::vector<std::string> targets;
10249dae4deeSJagpal Singh Gill         targets.emplace_back(BMCWEB_REDFISH_MANAGER_URI_NAME);
10259dae4deeSJagpal Singh Gill 
10269dae4deeSJagpal Singh Gill         processUpdateRequest(
10279dae4deeSJagpal Singh Gill             asyncResp, std::move(payload), req.body(),
10289dae4deeSJagpal Singh Gill             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate",
10299dae4deeSJagpal Singh Gill             targets);
10309dae4deeSJagpal Singh Gill     }
10319dae4deeSJagpal Singh Gill     else
10329dae4deeSJagpal Singh Gill     {
10339dae4deeSJagpal Singh Gill         // Setup callback for when new software detected
10349dae4deeSJagpal Singh Gill         monitorForSoftwareAvailable(asyncResp, req,
10359dae4deeSJagpal Singh Gill                                     "/redfish/v1/UpdateService");
10369dae4deeSJagpal Singh Gill 
10379dae4deeSJagpal Singh Gill         uploadImageFile(asyncResp->res, req.body());
10389dae4deeSJagpal Singh Gill     }
10399dae4deeSJagpal Singh Gill }
10409dae4deeSJagpal Singh Gill 
1041c2051d11SEd Tanous inline void
1042c2051d11SEd Tanous     handleUpdateServicePost(App& app, const crow::Request& req,
1043c2051d11SEd Tanous                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1044c2051d11SEd Tanous {
10453ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1046c2051d11SEd Tanous     {
1047c2051d11SEd Tanous         return;
1048c2051d11SEd Tanous     }
1049b33a4327SNinad Palsule     std::string_view contentType = req.getHeaderValue("Content-Type");
1050b33a4327SNinad Palsule 
105162598e31SEd Tanous     BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
1052b33a4327SNinad Palsule 
1053b33a4327SNinad Palsule     // Make sure that content type is application/octet-stream or
1054b33a4327SNinad Palsule     // multipart/form-data
105518f8f608SEd Tanous     if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
1056b33a4327SNinad Palsule     {
10579dae4deeSJagpal Singh Gill         doHTTPUpdate(asyncResp, req);
1058b33a4327SNinad Palsule     }
1059b33a4327SNinad Palsule     else if (contentType.starts_with("multipart/form-data"))
1060b33a4327SNinad Palsule     {
1061b33a4327SNinad Palsule         MultipartParser parser;
1062c2051d11SEd Tanous 
10630ed80c8cSGeorge Liu         ParserError ec = parser.parse(req);
10640ed80c8cSGeorge Liu         if (ec != ParserError::PARSER_SUCCESS)
10650ed80c8cSGeorge Liu         {
10660ed80c8cSGeorge Liu             // handle error
106762598e31SEd Tanous             BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
106862598e31SEd Tanous                              static_cast<int>(ec));
10690ed80c8cSGeorge Liu             messages::internalError(asyncResp->res);
10700ed80c8cSGeorge Liu             return;
10710ed80c8cSGeorge Liu         }
10726b54e4e0SEd Tanous 
1073ef93eab3SJagpal Singh Gill         updateMultipartContext(asyncResp, req, std::move(parser));
1074c2051d11SEd Tanous     }
1075b33a4327SNinad Palsule     else
1076b33a4327SNinad Palsule     {
107762598e31SEd Tanous         BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
1078b33a4327SNinad Palsule         asyncResp->res.result(boost::beast::http::status::bad_request);
1079b33a4327SNinad Palsule     }
1080b33a4327SNinad Palsule }
1081c2051d11SEd Tanous 
1082f5139334SEd Tanous inline void
1083f5139334SEd Tanous     handleUpdateServiceGet(App& app, const crow::Request& req,
1084f5139334SEd Tanous                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
10851abe55efSEd Tanous {
10863ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
108745ca1b86SEd Tanous     {
108845ca1b86SEd Tanous         return;
108945ca1b86SEd Tanous     }
10908d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.type"] =
10910ed80c8cSGeorge Liu         "#UpdateService.v1_11_1.UpdateService";
10928d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
10938d1b46d7Szhanghch05     asyncResp->res.jsonValue["Id"] = "UpdateService";
1094002d39b4SEd Tanous     asyncResp->res.jsonValue["Description"] = "Service for Software Update";
10958d1b46d7Szhanghch05     asyncResp->res.jsonValue["Name"] = "Update Service";
10964dc23f3fSEd Tanous 
10977e860f15SJohn Edward Broadbent     asyncResp->res.jsonValue["HttpPushUri"] =
10984dc23f3fSEd Tanous         "/redfish/v1/UpdateService/update";
10990ed80c8cSGeorge Liu     asyncResp->res.jsonValue["MultipartHttpPushUri"] =
11000ed80c8cSGeorge Liu         "/redfish/v1/UpdateService/update";
11014dc23f3fSEd Tanous 
11020f74e643SEd Tanous     // UpdateService cannot be disabled
11038d1b46d7Szhanghch05     asyncResp->res.jsonValue["ServiceEnabled"] = true;
11041476687dSEd Tanous     asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
11051476687dSEd Tanous         "/redfish/v1/UpdateService/FirmwareInventory";
1106d61e5194STejas Patil     // Get the MaxImageSizeBytes
1107bd79bce8SPatrick Williams     asyncResp->res.jsonValue["MaxImageSizeBytes"] =
1108bd79bce8SPatrick Williams         BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024;
1109d61e5194STejas Patil 
11100554c984SAndrew Geissler     // Update Actions object.
11110554c984SAndrew Geissler     nlohmann::json& updateSvcSimpleUpdate =
1112002d39b4SEd Tanous         asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
11130554c984SAndrew Geissler     updateSvcSimpleUpdate["target"] =
11140554c984SAndrew Geissler         "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
1115757178a5SEd Tanous 
1116757178a5SEd Tanous     nlohmann::json::array_t allowed;
1117e5cf777eSEd Tanous     allowed.emplace_back(update_service::TransferProtocolType::HTTPS);
1118757178a5SEd Tanous 
111925b54dbaSEd Tanous     if constexpr (BMCWEB_INSECURE_PUSH_STYLE_NOTIFICATION)
112025b54dbaSEd Tanous     {
1121757178a5SEd Tanous         allowed.emplace_back(update_service::TransferProtocolType::TFTP);
112225b54dbaSEd Tanous     }
1123757178a5SEd Tanous 
1124757178a5SEd Tanous     updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
1125757178a5SEd Tanous         std::move(allowed);
1126757178a5SEd Tanous 
1127539d8c6bSEd Tanous     asyncResp->res
1128539d8c6bSEd Tanous         .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] =
1129539d8c6bSEd Tanous         update_service::ApplyTime::Immediate;
1130729dae72SJennifer Lee }
1131729dae72SJennifer Lee 
1132f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryCollectionGet(
1133f5139334SEd Tanous     App& app, const crow::Request& req,
1134f5139334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
11351abe55efSEd Tanous {
11363ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
113745ca1b86SEd Tanous     {
113845ca1b86SEd Tanous         return;
113945ca1b86SEd Tanous     }
11408d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.type"] =
11410f74e643SEd Tanous         "#SoftwareInventoryCollection.SoftwareInventoryCollection";
11428d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.id"] =
11430f74e643SEd Tanous         "/redfish/v1/UpdateService/FirmwareInventory";
1144002d39b4SEd Tanous     asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
114508d81adaSJohn Edward Broadbent     const std::array<const std::string_view, 1> iface = {
1146e99073f5SGeorge Liu         "xyz.openbmc_project.Software.Version"};
11476c4eb9deSJennifer Lee 
114808d81adaSJohn Edward Broadbent     redfish::collection_util::getCollectionMembers(
114908d81adaSJohn Edward Broadbent         asyncResp,
1150f5139334SEd Tanous         boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface,
1151f5139334SEd Tanous         "/xyz/openbmc_project/software");
1152729dae72SJennifer Lee }
1153f5139334SEd Tanous 
115487d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */
1155f5139334SEd Tanous inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
115687d84729SAndrew Geissler                             const std::string& purpose)
115787d84729SAndrew Geissler {
1158eee0013eSWilly Tu     if (purpose == sw_util::bmcPurpose)
115987d84729SAndrew Geissler     {
1160ac106bf6SEd Tanous         nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
11611476687dSEd Tanous         nlohmann::json::object_t item;
1162253f11b8SEd Tanous         item["@odata.id"] = boost::urls::format(
1163253f11b8SEd Tanous             "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME);
1164b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
1165ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1166ac106bf6SEd Tanous             relatedItem.size();
116787d84729SAndrew Geissler     }
1168eee0013eSWilly Tu     else if (purpose == sw_util::biosPurpose)
116987d84729SAndrew Geissler     {
1170ac106bf6SEd Tanous         nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
11711476687dSEd Tanous         nlohmann::json::object_t item;
1172253f11b8SEd Tanous         item["@odata.id"] = std::format("/redfish/v1/Systems/{}/Bios",
1173253f11b8SEd Tanous                                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
1174b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
1175ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1176ac106bf6SEd Tanous             relatedItem.size();
117787d84729SAndrew Geissler     }
117887d84729SAndrew Geissler     else
117987d84729SAndrew Geissler     {
1180bf2ddedeSCarson Labrado         BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
118187d84729SAndrew Geissler     }
118287d84729SAndrew Geissler }
118387d84729SAndrew Geissler 
1184af24660dSWilly Tu inline void
1185af24660dSWilly Tu     getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1186af24660dSWilly Tu                        const std::string& service, const std::string& path,
1187af24660dSWilly Tu                        const std::string& swId)
1188af24660dSWilly Tu {
1189d1bde9e5SKrzysztof Grobelny     sdbusplus::asio::getAllProperties(
1190d1bde9e5SKrzysztof Grobelny         *crow::connections::systemBus, service, path,
1191d1bde9e5SKrzysztof Grobelny         "xyz.openbmc_project.Software.Version",
1192af24660dSWilly Tu         [asyncResp,
11938b24275dSEd Tanous          swId](const boost::system::error_code& ec,
1194af24660dSWilly Tu                const dbus::utility::DBusPropertiesMap& propertiesList) {
11958b24275dSEd Tanous             if (ec)
1196af24660dSWilly Tu             {
1197af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1198af24660dSWilly Tu                 return;
1199af24660dSWilly Tu             }
1200d1bde9e5SKrzysztof Grobelny 
1201af24660dSWilly Tu             const std::string* swInvPurpose = nullptr;
1202af24660dSWilly Tu             const std::string* version = nullptr;
1203d1bde9e5SKrzysztof Grobelny 
1204d1bde9e5SKrzysztof Grobelny             const bool success = sdbusplus::unpackPropertiesNoThrow(
1205d1bde9e5SKrzysztof Grobelny                 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
1206d1bde9e5SKrzysztof Grobelny                 swInvPurpose, "Version", version);
1207d1bde9e5SKrzysztof Grobelny 
1208d1bde9e5SKrzysztof Grobelny             if (!success)
1209af24660dSWilly Tu             {
1210d1bde9e5SKrzysztof Grobelny                 messages::internalError(asyncResp->res);
1211d1bde9e5SKrzysztof Grobelny                 return;
1212af24660dSWilly Tu             }
1213af24660dSWilly Tu 
1214af24660dSWilly Tu             if (swInvPurpose == nullptr)
1215af24660dSWilly Tu             {
121662598e31SEd Tanous                 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
1217af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1218af24660dSWilly Tu                 return;
1219af24660dSWilly Tu             }
1220af24660dSWilly Tu 
122162598e31SEd Tanous             BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
1222af24660dSWilly Tu 
1223af24660dSWilly Tu             if (version == nullptr)
1224af24660dSWilly Tu             {
122562598e31SEd Tanous                 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
1226af24660dSWilly Tu 
1227af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1228af24660dSWilly Tu 
1229af24660dSWilly Tu                 return;
1230af24660dSWilly Tu             }
1231af24660dSWilly Tu             asyncResp->res.jsonValue["Version"] = *version;
1232af24660dSWilly Tu             asyncResp->res.jsonValue["Id"] = swId;
1233af24660dSWilly Tu 
1234af24660dSWilly Tu             // swInvPurpose is of format:
1235af24660dSWilly Tu             // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
1236af24660dSWilly Tu             // Translate this to "ABC image"
1237af24660dSWilly Tu             size_t endDesc = swInvPurpose->rfind('.');
1238af24660dSWilly Tu             if (endDesc == std::string::npos)
1239af24660dSWilly Tu             {
1240af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1241af24660dSWilly Tu                 return;
1242af24660dSWilly Tu             }
1243af24660dSWilly Tu             endDesc++;
1244af24660dSWilly Tu             if (endDesc >= swInvPurpose->size())
1245af24660dSWilly Tu             {
1246af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1247af24660dSWilly Tu                 return;
1248af24660dSWilly Tu             }
1249af24660dSWilly Tu 
1250af24660dSWilly Tu             std::string formatDesc = swInvPurpose->substr(endDesc);
1251af24660dSWilly Tu             asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1252af24660dSWilly Tu             getRelatedItems(asyncResp, *swInvPurpose);
1253d1bde9e5SKrzysztof Grobelny         });
1254af24660dSWilly Tu }
1255af24660dSWilly Tu 
1256f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryGet(
1257f5139334SEd Tanous     App& app, const crow::Request& req,
125845ca1b86SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1259f5139334SEd Tanous     const std::string& param)
1260f5139334SEd Tanous {
12613ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
126245ca1b86SEd Tanous     {
126345ca1b86SEd Tanous         return;
126445ca1b86SEd Tanous     }
1265f5139334SEd Tanous     std::shared_ptr<std::string> swId = std::make_shared<std::string>(param);
1266c711bf86SEd Tanous 
1267ef4c65b7SEd Tanous     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1268ef4c65b7SEd Tanous         "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
1269c711bf86SEd Tanous 
1270e99073f5SGeorge Liu     constexpr std::array<std::string_view, 1> interfaces = {
1271e99073f5SGeorge Liu         "xyz.openbmc_project.Software.Version"};
1272e99073f5SGeorge Liu     dbus::utility::getSubTree(
1273e99073f5SGeorge Liu         "/", 0, interfaces,
1274b9d36b47SEd Tanous         [asyncResp,
1275e99073f5SGeorge Liu          swId](const boost::system::error_code& ec,
1276b9d36b47SEd Tanous                const dbus::utility::MapperGetSubTreeResponse& subtree) {
127762598e31SEd Tanous             BMCWEB_LOG_DEBUG("doGet callback...");
12781abe55efSEd Tanous             if (ec)
12791abe55efSEd Tanous             {
1280f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
12816c4eb9deSJennifer Lee                 return;
12826c4eb9deSJennifer Lee             }
12836c4eb9deSJennifer Lee 
12846913228dSAndrew Geissler             // Ensure we find our input swId, otherwise return an error
12856913228dSAndrew Geissler             bool found = false;
1286bd79bce8SPatrick Williams             for (const std::pair<std::string,
1287bd79bce8SPatrick Williams                                  std::vector<std::pair<
1288bd79bce8SPatrick Williams                                      std::string, std::vector<std::string>>>>&
1289002d39b4SEd Tanous                      obj : subtree)
12901abe55efSEd Tanous             {
129111ba3979SEd Tanous                 if (!obj.first.ends_with(*swId))
12921abe55efSEd Tanous                 {
1293acb7cfb4SJennifer Lee                     continue;
1294acb7cfb4SJennifer Lee                 }
1295acb7cfb4SJennifer Lee 
129626f6976fSEd Tanous                 if (obj.second.empty())
12971abe55efSEd Tanous                 {
1298acb7cfb4SJennifer Lee                     continue;
1299acb7cfb4SJennifer Lee                 }
13006c4eb9deSJennifer Lee 
13016913228dSAndrew Geissler                 found = true;
1302eee0013eSWilly Tu                 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1303af24660dSWilly Tu                 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1304af24660dSWilly Tu                                    *swId);
13056c4eb9deSJennifer Lee             }
13066913228dSAndrew Geissler             if (!found)
13076913228dSAndrew Geissler             {
130862598e31SEd Tanous                 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
13096913228dSAndrew Geissler                 messages::resourceMissingAtURI(
1310ef4c65b7SEd Tanous                     asyncResp->res,
1311ef4c65b7SEd Tanous                     boost::urls::format(
1312bd79bce8SPatrick Williams                         "/redfish/v1/UpdateService/FirmwareInventory/{}",
1313bd79bce8SPatrick Williams                         *swId));
13146913228dSAndrew Geissler                 return;
13156913228dSAndrew Geissler             }
13164e68c45bSAyushi Smriti             asyncResp->res.jsonValue["@odata.type"] =
13174e68c45bSAyushi Smriti                 "#SoftwareInventory.v1_1_0.SoftwareInventory";
13184e68c45bSAyushi Smriti             asyncResp->res.jsonValue["Name"] = "Software Inventory";
1319539d8c6bSEd Tanous             asyncResp->res.jsonValue["Status"]["HealthRollup"] =
1320539d8c6bSEd Tanous                 resource::Health::OK;
13213f8a743aSAppaRao Puli 
13223f8a743aSAppaRao Puli             asyncResp->res.jsonValue["Updateable"] = false;
1323eee0013eSWilly Tu             sw_util::getSwUpdatableStatus(asyncResp, swId);
1324e99073f5SGeorge Liu         });
1325f5139334SEd Tanous }
1326f5139334SEd Tanous 
1327f5139334SEd Tanous inline void requestRoutesUpdateService(App& app)
1328f5139334SEd Tanous {
1329f5139334SEd Tanous     BMCWEB_ROUTE(
1330f5139334SEd Tanous         app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
1331f5139334SEd Tanous         .privileges(redfish::privileges::postUpdateService)
1332f5139334SEd Tanous         .methods(boost::beast::http::verb::post)(std::bind_front(
1333f5139334SEd Tanous             handleUpdateServiceSimpleUpdateAction, std::ref(app)));
1334f5139334SEd Tanous 
1335f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1336f5139334SEd Tanous         .privileges(redfish::privileges::getSoftwareInventory)
1337f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
1338f5139334SEd Tanous             handleUpdateServiceFirmwareInventoryGet, std::ref(app)));
1339f5139334SEd Tanous 
1340f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1341f5139334SEd Tanous         .privileges(redfish::privileges::getUpdateService)
1342f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(
1343f5139334SEd Tanous             std::bind_front(handleUpdateServiceGet, std::ref(app)));
1344f5139334SEd Tanous 
1345f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
1346f5139334SEd Tanous         .privileges(redfish::privileges::postUpdateService)
1347f5139334SEd Tanous         .methods(boost::beast::http::verb::post)(
1348f5139334SEd Tanous             std::bind_front(handleUpdateServicePost, std::ref(app)));
1349f5139334SEd Tanous 
1350f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
1351f5139334SEd Tanous         .privileges(redfish::privileges::getSoftwareInventoryCollection)
1352f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
1353f5139334SEd Tanous             handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app)));
13546c4eb9deSJennifer Lee }
1355729dae72SJennifer Lee 
1356729dae72SJennifer Lee } // namespace redfish
1357