xref: /openbmc/bmcweb/features/redfish/lib/update_service.hpp (revision 6a37140a87a5f3185f6c68bd405701c6111a7b20)
1729dae72SJennifer Lee /*
26be832e2SEd Tanous Copyright (c) 2018 Intel Corporation
36be832e2SEd Tanous 
46be832e2SEd Tanous Licensed under the Apache License, Version 2.0 (the "License");
56be832e2SEd Tanous you may not use this file except in compliance with the License.
66be832e2SEd Tanous You may obtain a copy of the License at
76be832e2SEd Tanous 
86be832e2SEd Tanous       http://www.apache.org/licenses/LICENSE-2.0
96be832e2SEd Tanous 
106be832e2SEd Tanous Unless required by applicable law or agreed to in writing, software
116be832e2SEd Tanous distributed under the License is distributed on an "AS IS" BASIS,
126be832e2SEd Tanous WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136be832e2SEd Tanous See the License for the specific language governing permissions and
146be832e2SEd Tanous limitations under the License.
15729dae72SJennifer Lee */
16729dae72SJennifer Lee #pragma once
17729dae72SJennifer Lee 
18d61e5194STejas Patil #include "bmcweb_config.h"
19d61e5194STejas Patil 
203ccb3adbSEd Tanous #include "app.hpp"
213ccb3adbSEd Tanous #include "dbus_utility.hpp"
225b90429aSEd Tanous #include "error_messages.hpp"
23757178a5SEd Tanous #include "generated/enums/update_service.hpp"
240ed80c8cSGeorge Liu #include "multipart_parser.hpp"
252c6ffdb0SEd Tanous #include "ossl_random.hpp"
263ccb3adbSEd Tanous #include "query.hpp"
273ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
28a8e884fcSEd Tanous #include "task.hpp"
295b90429aSEd Tanous #include "task_messages.hpp"
3008d81adaSJohn Edward Broadbent #include "utils/collection.hpp"
313ccb3adbSEd Tanous #include "utils/dbus_utils.hpp"
325b90429aSEd Tanous #include "utils/json_utils.hpp"
333ccb3adbSEd Tanous #include "utils/sw_utils.hpp"
343ccb3adbSEd Tanous 
35de0c960cSJagpal Singh Gill #include <sys/mman.h>
36de0c960cSJagpal Singh Gill 
37e99073f5SGeorge Liu #include <boost/system/error_code.hpp>
38ef4c65b7SEd Tanous #include <boost/url/format.hpp>
391e1e598dSJonathan Doman #include <sdbusplus/asio/property.hpp>
403ccb3adbSEd Tanous #include <sdbusplus/bus/match.hpp>
41d1bde9e5SKrzysztof Grobelny #include <sdbusplus/unpack_properties.hpp>
421214b7e7SGunnar Mills 
432b73119cSGeorge Liu #include <array>
44de0c960cSJagpal Singh Gill #include <cstddef>
450ed80c8cSGeorge Liu #include <filesystem>
46c71b6c99SJagpal Singh Gill #include <functional>
47de0c960cSJagpal Singh Gill #include <iterator>
48ef93eab3SJagpal Singh Gill #include <memory>
497cb59f65SEd Tanous #include <optional>
507cb59f65SEd Tanous #include <string>
512b73119cSGeorge Liu #include <string_view>
52de0c960cSJagpal Singh Gill #include <unordered_map>
53ef93eab3SJagpal Singh Gill #include <vector>
542b73119cSGeorge Liu 
551abe55efSEd Tanous namespace redfish
561abe55efSEd Tanous {
5727826b5fSEd Tanous 
580e7de46fSAndrew Geissler // Match signals added on software path
59cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
6059d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
61cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
6259d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
630e7de46fSAndrew Geissler // Only allow one update at a time
64cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
650e7de46fSAndrew Geissler static bool fwUpdateInProgress = false;
6686adcd6dSAndrew Geissler // Timer for software available
67cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
68271584abSEd Tanous static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
6986adcd6dSAndrew Geissler 
70de0c960cSJagpal Singh Gill struct MemoryFileDescriptor
71de0c960cSJagpal Singh Gill {
72de0c960cSJagpal Singh Gill     int fd = -1;
73de0c960cSJagpal Singh Gill 
74de0c960cSJagpal Singh Gill     explicit MemoryFileDescriptor(const std::string& filename) :
75de0c960cSJagpal Singh Gill         fd(memfd_create(filename.c_str(), 0))
76de0c960cSJagpal Singh Gill     {}
77de0c960cSJagpal Singh Gill 
78de0c960cSJagpal Singh Gill     MemoryFileDescriptor(const MemoryFileDescriptor&) = default;
79de0c960cSJagpal Singh Gill     MemoryFileDescriptor(MemoryFileDescriptor&& other) noexcept : fd(other.fd)
80de0c960cSJagpal Singh Gill     {
81de0c960cSJagpal Singh Gill         other.fd = -1;
82de0c960cSJagpal Singh Gill     }
83de0c960cSJagpal Singh Gill     MemoryFileDescriptor& operator=(const MemoryFileDescriptor&) = delete;
84de0c960cSJagpal Singh Gill     MemoryFileDescriptor& operator=(MemoryFileDescriptor&&) = default;
85de0c960cSJagpal Singh Gill 
86de0c960cSJagpal Singh Gill     ~MemoryFileDescriptor()
87de0c960cSJagpal Singh Gill     {
88de0c960cSJagpal Singh Gill         if (fd != -1)
89de0c960cSJagpal Singh Gill         {
90de0c960cSJagpal Singh Gill             close(fd);
91de0c960cSJagpal Singh Gill         }
92de0c960cSJagpal Singh Gill     }
93de0c960cSJagpal Singh Gill 
94de0c960cSJagpal Singh Gill     bool rewind() const
95de0c960cSJagpal Singh Gill     {
96de0c960cSJagpal Singh Gill         if (lseek(fd, 0, SEEK_SET) == -1)
97de0c960cSJagpal Singh Gill         {
98de0c960cSJagpal Singh Gill             BMCWEB_LOG_ERROR("Failed to seek to beginning of image memfd");
99de0c960cSJagpal Singh Gill             return false;
100de0c960cSJagpal Singh Gill         }
101de0c960cSJagpal Singh Gill         return true;
102de0c960cSJagpal Singh Gill     }
103de0c960cSJagpal Singh Gill };
104de0c960cSJagpal Singh Gill 
105df254f2cSEd Tanous inline void cleanUp()
10686adcd6dSAndrew Geissler {
10786adcd6dSAndrew Geissler     fwUpdateInProgress = false;
10886adcd6dSAndrew Geissler     fwUpdateMatcher = nullptr;
1094cde5d90SJames Feist     fwUpdateErrorMatcher = nullptr;
11086adcd6dSAndrew Geissler }
111df254f2cSEd Tanous 
112df254f2cSEd Tanous inline void activateImage(const std::string& objPath,
11386adcd6dSAndrew Geissler                           const std::string& service)
11486adcd6dSAndrew Geissler {
11562598e31SEd Tanous     BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
1169ae226faSGeorge Liu     sdbusplus::asio::setProperty(
1179ae226faSGeorge Liu         *crow::connections::systemBus, service, objPath,
1189ae226faSGeorge Liu         "xyz.openbmc_project.Software.Activation", "RequestedActivation",
1199ae226faSGeorge Liu         "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
1208b24275dSEd Tanous         [](const boost::system::error_code& ec) {
1218b24275dSEd Tanous             if (ec)
12286adcd6dSAndrew Geissler             {
12362598e31SEd Tanous                 BMCWEB_LOG_DEBUG("error_code = {}", ec);
12462598e31SEd Tanous                 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
12586adcd6dSAndrew Geissler             }
1269ae226faSGeorge Liu         });
12786adcd6dSAndrew Geissler }
1280554c984SAndrew Geissler 
129c71b6c99SJagpal Singh Gill inline bool handleCreateTask(const boost::system::error_code& ec2,
130c71b6c99SJagpal Singh Gill                              sdbusplus::message_t& msg,
131c71b6c99SJagpal Singh Gill                              const std::shared_ptr<task::TaskData>& taskData)
132c71b6c99SJagpal Singh Gill {
133c71b6c99SJagpal Singh Gill     if (ec2)
134c71b6c99SJagpal Singh Gill     {
135c71b6c99SJagpal Singh Gill         return task::completed;
136c71b6c99SJagpal Singh Gill     }
137c71b6c99SJagpal Singh Gill 
138c71b6c99SJagpal Singh Gill     std::string iface;
139c71b6c99SJagpal Singh Gill     dbus::utility::DBusPropertiesMap values;
140c71b6c99SJagpal Singh Gill 
141c71b6c99SJagpal Singh Gill     std::string index = std::to_string(taskData->index);
142c71b6c99SJagpal Singh Gill     msg.read(iface, values);
143c71b6c99SJagpal Singh Gill 
144c71b6c99SJagpal Singh Gill     if (iface == "xyz.openbmc_project.Software.Activation")
145c71b6c99SJagpal Singh Gill     {
146c71b6c99SJagpal Singh Gill         const std::string* state = nullptr;
147c71b6c99SJagpal Singh Gill         for (const auto& property : values)
148c71b6c99SJagpal Singh Gill         {
149c71b6c99SJagpal Singh Gill             if (property.first == "Activation")
150c71b6c99SJagpal Singh Gill             {
151c71b6c99SJagpal Singh Gill                 state = std::get_if<std::string>(&property.second);
152c71b6c99SJagpal Singh Gill                 if (state == nullptr)
153c71b6c99SJagpal Singh Gill                 {
154c71b6c99SJagpal Singh Gill                     taskData->messages.emplace_back(messages::internalError());
155c71b6c99SJagpal Singh Gill                     return task::completed;
156c71b6c99SJagpal Singh Gill                 }
157c71b6c99SJagpal Singh Gill             }
158c71b6c99SJagpal Singh Gill         }
159c71b6c99SJagpal Singh Gill 
160c71b6c99SJagpal Singh Gill         if (state == nullptr)
161c71b6c99SJagpal Singh Gill         {
162c71b6c99SJagpal Singh Gill             return !task::completed;
163c71b6c99SJagpal Singh Gill         }
164c71b6c99SJagpal Singh Gill 
165c71b6c99SJagpal Singh Gill         if (state->ends_with("Invalid") || state->ends_with("Failed"))
166c71b6c99SJagpal Singh Gill         {
167c71b6c99SJagpal Singh Gill             taskData->state = "Exception";
168c71b6c99SJagpal Singh Gill             taskData->status = "Warning";
169c71b6c99SJagpal Singh Gill             taskData->messages.emplace_back(messages::taskAborted(index));
170c71b6c99SJagpal Singh Gill             return task::completed;
171c71b6c99SJagpal Singh Gill         }
172c71b6c99SJagpal Singh Gill 
173c71b6c99SJagpal Singh Gill         if (state->ends_with("Staged"))
174c71b6c99SJagpal Singh Gill         {
175c71b6c99SJagpal Singh Gill             taskData->state = "Stopping";
176c71b6c99SJagpal Singh Gill             taskData->messages.emplace_back(messages::taskPaused(index));
177c71b6c99SJagpal Singh Gill 
178c71b6c99SJagpal Singh Gill             // its staged, set a long timer to
179c71b6c99SJagpal Singh Gill             // allow them time to complete the
180c71b6c99SJagpal Singh Gill             // update (probably cycle the
181c71b6c99SJagpal Singh Gill             // system) if this expires then
182c71b6c99SJagpal Singh Gill             // task will be canceled
183c71b6c99SJagpal Singh Gill             taskData->extendTimer(std::chrono::hours(5));
184c71b6c99SJagpal Singh Gill             return !task::completed;
185c71b6c99SJagpal Singh Gill         }
186c71b6c99SJagpal Singh Gill 
187c71b6c99SJagpal Singh Gill         if (state->ends_with("Active"))
188c71b6c99SJagpal Singh Gill         {
189c71b6c99SJagpal Singh Gill             taskData->messages.emplace_back(messages::taskCompletedOK(index));
190c71b6c99SJagpal Singh Gill             taskData->state = "Completed";
191c71b6c99SJagpal Singh Gill             return task::completed;
192c71b6c99SJagpal Singh Gill         }
193c71b6c99SJagpal Singh Gill     }
194c71b6c99SJagpal Singh Gill     else if (iface == "xyz.openbmc_project.Software.ActivationProgress")
195c71b6c99SJagpal Singh Gill     {
196c71b6c99SJagpal Singh Gill         const uint8_t* progress = nullptr;
197c71b6c99SJagpal Singh Gill         for (const auto& property : values)
198c71b6c99SJagpal Singh Gill         {
199c71b6c99SJagpal Singh Gill             if (property.first == "Progress")
200c71b6c99SJagpal Singh Gill             {
201c71b6c99SJagpal Singh Gill                 progress = std::get_if<uint8_t>(&property.second);
202c71b6c99SJagpal Singh Gill                 if (progress == nullptr)
203c71b6c99SJagpal Singh Gill                 {
204c71b6c99SJagpal Singh Gill                     taskData->messages.emplace_back(messages::internalError());
205c71b6c99SJagpal Singh Gill                     return task::completed;
206c71b6c99SJagpal Singh Gill                 }
207c71b6c99SJagpal Singh Gill             }
208c71b6c99SJagpal Singh Gill         }
209c71b6c99SJagpal Singh Gill 
210c71b6c99SJagpal Singh Gill         if (progress == nullptr)
211c71b6c99SJagpal Singh Gill         {
212c71b6c99SJagpal Singh Gill             return !task::completed;
213c71b6c99SJagpal Singh Gill         }
214c71b6c99SJagpal Singh Gill         taskData->percentComplete = *progress;
215c71b6c99SJagpal Singh Gill         taskData->messages.emplace_back(
216c71b6c99SJagpal Singh Gill             messages::taskProgressChanged(index, *progress));
217c71b6c99SJagpal Singh Gill 
218c71b6c99SJagpal Singh Gill         // if we're getting status updates it's
219c71b6c99SJagpal Singh Gill         // still alive, update timer
220c71b6c99SJagpal Singh Gill         taskData->extendTimer(std::chrono::minutes(5));
221c71b6c99SJagpal Singh Gill     }
222c71b6c99SJagpal Singh Gill 
223c71b6c99SJagpal Singh Gill     // as firmware update often results in a
224c71b6c99SJagpal Singh Gill     // reboot, the task  may never "complete"
225c71b6c99SJagpal Singh Gill     // unless it is an error
226c71b6c99SJagpal Singh Gill 
227c71b6c99SJagpal Singh Gill     return !task::completed;
228c71b6c99SJagpal Singh Gill }
229c71b6c99SJagpal Singh Gill 
230c71b6c99SJagpal Singh Gill inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
231c71b6c99SJagpal Singh Gill                        task::Payload&& payload,
232c71b6c99SJagpal Singh Gill                        const sdbusplus::message::object_path& objPath)
233c71b6c99SJagpal Singh Gill {
234c71b6c99SJagpal Singh Gill     std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
235c71b6c99SJagpal Singh Gill         std::bind_front(handleCreateTask),
236c71b6c99SJagpal Singh Gill         "type='signal',interface='org.freedesktop.DBus.Properties',"
237c71b6c99SJagpal Singh Gill         "member='PropertiesChanged',path='" +
238c71b6c99SJagpal Singh Gill             objPath.str + "'");
239c71b6c99SJagpal Singh Gill     task->startTimer(std::chrono::minutes(5));
240c71b6c99SJagpal Singh Gill     task->populateResp(asyncResp->res);
241c71b6c99SJagpal Singh Gill     task->payload.emplace(std::move(payload));
242c71b6c99SJagpal Singh Gill }
243c71b6c99SJagpal Singh Gill 
2440554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
2450554c984SAndrew Geissler // then no asyncResp updates will occur
2464ff0f1f4SEd Tanous inline void
2478d1b46d7Szhanghch05     softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
24859d494eeSPatrick Williams                            sdbusplus::message_t& m, task::Payload&& payload)
24986adcd6dSAndrew Geissler {
25080f79a40SMichael Shen     dbus::utility::DBusInterfacesMap interfacesProperties;
25186adcd6dSAndrew Geissler 
25286adcd6dSAndrew Geissler     sdbusplus::message::object_path objPath;
25386adcd6dSAndrew Geissler 
25486adcd6dSAndrew Geissler     m.read(objPath, interfacesProperties);
25586adcd6dSAndrew Geissler 
25662598e31SEd Tanous     BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
257e3eb3d63SEd Tanous     for (const auto& interface : interfacesProperties)
25886adcd6dSAndrew Geissler     {
25962598e31SEd Tanous         BMCWEB_LOG_DEBUG("interface = {}", interface.first);
26086adcd6dSAndrew Geissler 
26186adcd6dSAndrew Geissler         if (interface.first == "xyz.openbmc_project.Software.Activation")
26286adcd6dSAndrew Geissler         {
26386adcd6dSAndrew Geissler             // Retrieve service and activate
2642b73119cSGeorge Liu             constexpr std::array<std::string_view, 1> interfaces = {
2652b73119cSGeorge Liu                 "xyz.openbmc_project.Software.Activation"};
2662b73119cSGeorge Liu             dbus::utility::getDbusObject(
2672b73119cSGeorge Liu                 objPath.str, interfaces,
268a3e65892SEd Tanous                 [objPath, asyncResp, payload(std::move(payload))](
2698b24275dSEd Tanous                     const boost::system::error_code& ec,
270a3e65892SEd Tanous                     const std::vector<
271a3e65892SEd Tanous                         std::pair<std::string, std::vector<std::string>>>&
272a3e65892SEd Tanous                         objInfo) mutable {
2738b24275dSEd Tanous                     if (ec)
27486adcd6dSAndrew Geissler                     {
27562598e31SEd Tanous                         BMCWEB_LOG_DEBUG("error_code = {}", ec);
27662598e31SEd Tanous                         BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
2770554c984SAndrew Geissler                         if (asyncResp)
2780554c984SAndrew Geissler                         {
27986adcd6dSAndrew Geissler                             messages::internalError(asyncResp->res);
2800554c984SAndrew Geissler                         }
28186adcd6dSAndrew Geissler                         cleanUp();
28286adcd6dSAndrew Geissler                         return;
28386adcd6dSAndrew Geissler                     }
28486adcd6dSAndrew Geissler                     // Ensure we only got one service back
28586adcd6dSAndrew Geissler                     if (objInfo.size() != 1)
28686adcd6dSAndrew Geissler                     {
287bd79bce8SPatrick Williams                         BMCWEB_LOG_ERROR("Invalid Object Size {}",
288bd79bce8SPatrick Williams                                          objInfo.size());
2890554c984SAndrew Geissler                         if (asyncResp)
2900554c984SAndrew Geissler                         {
29186adcd6dSAndrew Geissler                             messages::internalError(asyncResp->res);
2920554c984SAndrew Geissler                         }
29386adcd6dSAndrew Geissler                         cleanUp();
29486adcd6dSAndrew Geissler                         return;
29586adcd6dSAndrew Geissler                     }
29686adcd6dSAndrew Geissler                     // cancel timer only when
29786adcd6dSAndrew Geissler                     // xyz.openbmc_project.Software.Activation interface
29886adcd6dSAndrew Geissler                     // is added
29986adcd6dSAndrew Geissler                     fwAvailableTimer = nullptr;
30086adcd6dSAndrew Geissler 
30186adcd6dSAndrew Geissler                     activateImage(objPath.str, objInfo[0].first);
3020554c984SAndrew Geissler                     if (asyncResp)
3030554c984SAndrew Geissler                     {
304c71b6c99SJagpal Singh Gill                         createTask(asyncResp, std::move(payload), objPath);
3050554c984SAndrew Geissler                     }
30686adcd6dSAndrew Geissler                     fwUpdateInProgress = false;
3072b73119cSGeorge Liu                 });
30862bafc01SPatrick Williams 
30962bafc01SPatrick Williams             break;
31086adcd6dSAndrew Geissler         }
31186adcd6dSAndrew Geissler     }
31286adcd6dSAndrew Geissler }
31386adcd6dSAndrew Geissler 
3148549b951SMyung Bae inline void afterAvailbleTimerAsyncWait(
3158549b951SMyung Bae     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3168549b951SMyung Bae     const boost::system::error_code& ec)
3178549b951SMyung Bae {
3188549b951SMyung Bae     cleanUp();
3198549b951SMyung Bae     if (ec == boost::asio::error::operation_aborted)
3208549b951SMyung Bae     {
3218549b951SMyung Bae         // expected, we were canceled before the timer completed.
3228549b951SMyung Bae         return;
3238549b951SMyung Bae     }
3248549b951SMyung Bae     BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
3258549b951SMyung Bae     BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
3268549b951SMyung Bae     if (ec)
3278549b951SMyung Bae     {
3288549b951SMyung Bae         BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
3298549b951SMyung Bae         return;
3308549b951SMyung Bae     }
3318549b951SMyung Bae     if (asyncResp)
3328549b951SMyung Bae     {
3338549b951SMyung Bae         redfish::messages::internalError(asyncResp->res);
3348549b951SMyung Bae     }
3358549b951SMyung Bae }
3368549b951SMyung Bae 
3378549b951SMyung Bae inline void
3388549b951SMyung Bae     handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
33948fb20b9SEd Tanous                           const std::string& url, const std::string& type)
3408549b951SMyung Bae {
341c87294a6SEd Tanous     // NOLINTBEGIN(bugprone-branch-clone)
3428549b951SMyung Bae     if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
3438549b951SMyung Bae     {
34448fb20b9SEd Tanous         messages::missingOrMalformedPart(asyncResp->res);
3458549b951SMyung Bae     }
3468549b951SMyung Bae     else if (type ==
3478549b951SMyung Bae              "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
3488549b951SMyung Bae     {
34948fb20b9SEd Tanous         messages::missingOrMalformedPart(asyncResp->res);
3508549b951SMyung Bae     }
3518549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
3528549b951SMyung Bae     {
35348fb20b9SEd Tanous         messages::missingOrMalformedPart(asyncResp->res);
3548549b951SMyung Bae     }
3558549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
3568549b951SMyung Bae     {
357c87294a6SEd Tanous         messages::resourceAlreadyExists(asyncResp->res, "UpdateService",
358c87294a6SEd Tanous                                         "Version", "uploaded version");
3598549b951SMyung Bae     }
3608549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
3618549b951SMyung Bae     {
36248fb20b9SEd Tanous         messages::serviceTemporarilyUnavailable(asyncResp->res, url);
3638549b951SMyung Bae     }
3644034a652SMyung Bae     else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
3658549b951SMyung Bae     {
366c87294a6SEd Tanous         messages::internalError(asyncResp->res);
3674034a652SMyung Bae     }
3684034a652SMyung Bae     else if (type ==
3694034a652SMyung Bae              "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
3704034a652SMyung Bae     {
371c87294a6SEd Tanous         messages::internalError(asyncResp->res);
3724034a652SMyung Bae     }
3734034a652SMyung Bae     else if (type ==
3744034a652SMyung Bae              "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
3754034a652SMyung Bae     {
37648fb20b9SEd Tanous         messages::missingOrMalformedPart(asyncResp->res);
3774034a652SMyung Bae     }
3784034a652SMyung Bae     else if (type ==
3794034a652SMyung Bae                  "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
3804034a652SMyung Bae              type == "xyz.openbmc_project.Software.Version.Error.HostFile")
3814034a652SMyung Bae     {
3824034a652SMyung Bae         BMCWEB_LOG_ERROR("Software Image Error type={}", type);
38348fb20b9SEd Tanous         messages::internalError(asyncResp->res);
3848549b951SMyung Bae     }
3854034a652SMyung Bae     else
3864034a652SMyung Bae     {
3874034a652SMyung Bae         // Unrelated error types. Ignored
3884034a652SMyung Bae         BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
3894034a652SMyung Bae         return;
3904034a652SMyung Bae     }
391c87294a6SEd Tanous     // NOLINTEND(bugprone-branch-clone)
3924034a652SMyung Bae     // Clear the timer
3934034a652SMyung Bae     fwAvailableTimer = nullptr;
3948549b951SMyung Bae }
3958549b951SMyung Bae 
3968549b951SMyung Bae inline void
3978549b951SMyung Bae     afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3988549b951SMyung Bae                             const std::string& url, sdbusplus::message_t& m)
3998549b951SMyung Bae {
40080f79a40SMichael Shen     dbus::utility::DBusInterfacesMap interfacesProperties;
4018549b951SMyung Bae     sdbusplus::message::object_path objPath;
4028549b951SMyung Bae     m.read(objPath, interfacesProperties);
4038549b951SMyung Bae     BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
4048549b951SMyung Bae     for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
4058549b951SMyung Bae              interface : interfacesProperties)
4068549b951SMyung Bae     {
4078549b951SMyung Bae         if (interface.first == "xyz.openbmc_project.Logging.Entry")
4088549b951SMyung Bae         {
4098549b951SMyung Bae             for (const std::pair<std::string, dbus::utility::DbusVariantType>&
4108549b951SMyung Bae                      value : interface.second)
4118549b951SMyung Bae             {
4128549b951SMyung Bae                 if (value.first != "Message")
4138549b951SMyung Bae                 {
4148549b951SMyung Bae                     continue;
4158549b951SMyung Bae                 }
4168549b951SMyung Bae                 const std::string* type =
4178549b951SMyung Bae                     std::get_if<std::string>(&value.second);
4188549b951SMyung Bae                 if (type == nullptr)
4198549b951SMyung Bae                 {
4208549b951SMyung Bae                     // if this was our message, timeout will cover it
4218549b951SMyung Bae                     return;
4228549b951SMyung Bae                 }
4238549b951SMyung Bae                 handleUpdateErrorType(asyncResp, url, *type);
4248549b951SMyung Bae             }
4258549b951SMyung Bae         }
4268549b951SMyung Bae     }
4278549b951SMyung Bae }
4288549b951SMyung Bae 
4290554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
4300554c984SAndrew Geissler // then no asyncResp updates will occur
431f5139334SEd Tanous inline void monitorForSoftwareAvailable(
4328d1b46d7Szhanghch05     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
4338d1b46d7Szhanghch05     const crow::Request& req, const std::string& url,
4345d138943SGunnar Mills     int timeoutTimeSeconds = 25)
43586adcd6dSAndrew Geissler {
43686adcd6dSAndrew Geissler     // Only allow one FW update at a time
437e05aec50SEd Tanous     if (fwUpdateInProgress)
43886adcd6dSAndrew Geissler     {
4390554c984SAndrew Geissler         if (asyncResp)
4400554c984SAndrew Geissler         {
44186adcd6dSAndrew Geissler             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
4420554c984SAndrew Geissler         }
44386adcd6dSAndrew Geissler         return;
44486adcd6dSAndrew Geissler     }
44586adcd6dSAndrew Geissler 
4468e8245dbSEd Tanous     if (req.ioService == nullptr)
4478e8245dbSEd Tanous     {
4488e8245dbSEd Tanous         messages::internalError(asyncResp->res);
4498e8245dbSEd Tanous         return;
4508e8245dbSEd Tanous     }
4518e8245dbSEd Tanous 
4520554c984SAndrew Geissler     fwAvailableTimer =
453271584abSEd Tanous         std::make_unique<boost::asio::steady_timer>(*req.ioService);
45486adcd6dSAndrew Geissler 
455271584abSEd Tanous     fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
45686adcd6dSAndrew Geissler 
45786adcd6dSAndrew Geissler     fwAvailableTimer->async_wait(
4588549b951SMyung Bae         std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
4598549b951SMyung Bae 
460a3e65892SEd Tanous     task::Payload payload(req);
46159d494eeSPatrick Williams     auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
46262598e31SEd Tanous         BMCWEB_LOG_DEBUG("Match fired");
463a3e65892SEd Tanous         softwareInterfaceAdded(asyncResp, m, std::move(payload));
46486adcd6dSAndrew Geissler     };
46586adcd6dSAndrew Geissler 
46686adcd6dSAndrew Geissler     fwUpdateInProgress = true;
46786adcd6dSAndrew Geissler 
46859d494eeSPatrick Williams     fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
46986adcd6dSAndrew Geissler         *crow::connections::systemBus,
47086adcd6dSAndrew Geissler         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
47186adcd6dSAndrew Geissler         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
47286adcd6dSAndrew Geissler         callback);
4734cde5d90SJames Feist 
47459d494eeSPatrick Williams     fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
4754cde5d90SJames Feist         *crow::connections::systemBus,
476e1cc4828SBrian Ma         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
477e1cc4828SBrian Ma         "member='InterfacesAdded',"
478e1cc4828SBrian Ma         "path='/xyz/openbmc_project/logging'",
4798549b951SMyung Bae         std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
48086adcd6dSAndrew Geissler }
481729dae72SJennifer Lee 
482bd79bce8SPatrick Williams inline std::optional<boost::urls::url> parseSimpleUpdateUrl(
483bd79bce8SPatrick Williams     std::string imageURI, std::optional<std::string> transferProtocol,
484f86bcc87SEd Tanous     crow::Response& res)
485f86bcc87SEd Tanous {
486f86bcc87SEd Tanous     if (imageURI.find("://") == std::string::npos)
487f86bcc87SEd Tanous     {
488f86bcc87SEd Tanous         if (imageURI.starts_with("/"))
489f86bcc87SEd Tanous         {
490f86bcc87SEd Tanous             messages::actionParameterValueTypeError(
491f86bcc87SEd Tanous                 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
492f86bcc87SEd Tanous             return std::nullopt;
493f86bcc87SEd Tanous         }
494f86bcc87SEd Tanous         if (!transferProtocol)
495f86bcc87SEd Tanous         {
496f86bcc87SEd Tanous             messages::actionParameterValueTypeError(
497f86bcc87SEd Tanous                 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
498f86bcc87SEd Tanous             return std::nullopt;
499f86bcc87SEd Tanous         }
500*6a37140aSEd Tanous         // OpenBMC currently only supports HTTPS
501*6a37140aSEd Tanous         if (*transferProtocol == "HTTPS")
502e5cf777eSEd Tanous         {
503e5cf777eSEd Tanous             imageURI = "https://" + imageURI;
504e5cf777eSEd Tanous         }
505757178a5SEd Tanous         else
506f86bcc87SEd Tanous         {
507f86bcc87SEd Tanous             messages::actionParameterNotSupported(res, "TransferProtocol",
508f86bcc87SEd Tanous                                                   *transferProtocol);
509f86bcc87SEd Tanous             BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
510f86bcc87SEd Tanous                              *transferProtocol);
511f86bcc87SEd Tanous             return std::nullopt;
512f86bcc87SEd Tanous         }
513f86bcc87SEd Tanous     }
514f86bcc87SEd Tanous 
515f86bcc87SEd Tanous     boost::system::result<boost::urls::url> url =
516f86bcc87SEd Tanous         boost::urls::parse_absolute_uri(imageURI);
517f86bcc87SEd Tanous     if (!url)
518f86bcc87SEd Tanous     {
519f86bcc87SEd Tanous         messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
520f86bcc87SEd Tanous                                                 "UpdateService.SimpleUpdate");
521f86bcc87SEd Tanous 
522f86bcc87SEd Tanous         return std::nullopt;
523f86bcc87SEd Tanous     }
524f86bcc87SEd Tanous     url->normalize();
525f86bcc87SEd Tanous 
526757178a5SEd Tanous     if (url->scheme() == "tftp")
527757178a5SEd Tanous     {
528757178a5SEd Tanous         if (url->encoded_path().size() < 2)
529757178a5SEd Tanous         {
530757178a5SEd Tanous             messages::actionParameterNotSupported(res, "ImageURI",
531757178a5SEd Tanous                                                   url->buffer());
532757178a5SEd Tanous             return std::nullopt;
533757178a5SEd Tanous         }
534757178a5SEd Tanous     }
535e5cf777eSEd Tanous     else if (url->scheme() == "https")
536e5cf777eSEd Tanous     {
537e5cf777eSEd Tanous         // Empty paths default to "/"
538e5cf777eSEd Tanous         if (url->encoded_path().empty())
539e5cf777eSEd Tanous         {
540e5cf777eSEd Tanous             url->set_encoded_path("/");
541e5cf777eSEd Tanous         }
542e5cf777eSEd Tanous     }
543757178a5SEd Tanous     else
544f86bcc87SEd Tanous     {
545f86bcc87SEd Tanous         messages::actionParameterNotSupported(res, "ImageURI", imageURI);
546f86bcc87SEd Tanous         return std::nullopt;
547f86bcc87SEd Tanous     }
548757178a5SEd Tanous 
549757178a5SEd Tanous     if (url->encoded_path().empty())
550f86bcc87SEd Tanous     {
551757178a5SEd Tanous         messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
552757178a5SEd Tanous                                                 "UpdateService.SimpleUpdate");
553f86bcc87SEd Tanous         return std::nullopt;
554f86bcc87SEd Tanous     }
555757178a5SEd Tanous 
556757178a5SEd Tanous     return *url;
557f86bcc87SEd Tanous }
558f86bcc87SEd Tanous 
559e5cf777eSEd Tanous inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
560e5cf777eSEd Tanous                           const boost::urls::url_view_base& url)
561e5cf777eSEd Tanous {
562e5cf777eSEd Tanous     messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
563e5cf777eSEd Tanous                                           url.buffer());
564e5cf777eSEd Tanous }
565e5cf777eSEd Tanous 
566f5139334SEd Tanous inline void handleUpdateServiceSimpleUpdateAction(
567f5139334SEd Tanous     crow::App& app, const crow::Request& req,
568f5139334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
5690554c984SAndrew Geissler {
5703ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
57145ca1b86SEd Tanous     {
57245ca1b86SEd Tanous         return;
57345ca1b86SEd Tanous     }
57445ca1b86SEd Tanous 
5750554c984SAndrew Geissler     std::optional<std::string> transferProtocol;
5760554c984SAndrew Geissler     std::string imageURI;
5770554c984SAndrew Geissler 
57862598e31SEd Tanous     BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
5790554c984SAndrew Geissler 
5800554c984SAndrew Geissler     // User can pass in both TransferProtocol and ImageURI parameters or
5814e0453b1SGunnar Mills     // they can pass in just the ImageURI with the transfer protocol
5824e0453b1SGunnar Mills     // embedded within it.
5830554c984SAndrew Geissler     // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
5840554c984SAndrew Geissler     // 2) ImageURI:tftp://1.1.1.1/myfile.bin
5850554c984SAndrew Geissler 
586afc474aeSMyung Bae     if (!json_util::readJsonAction( //
587afc474aeSMyung Bae             req, asyncResp->res, //
588afc474aeSMyung Bae             "ImageURI", imageURI, //
589afc474aeSMyung Bae             "TransferProtocol", transferProtocol //
590afc474aeSMyung Bae             ))
5910554c984SAndrew Geissler     {
59262598e31SEd Tanous         BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
5930554c984SAndrew Geissler         return;
5940554c984SAndrew Geissler     }
595f5139334SEd Tanous 
596757178a5SEd Tanous     std::optional<boost::urls::url> url =
597757178a5SEd Tanous         parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res);
598757178a5SEd Tanous     if (!url)
5990554c984SAndrew Geissler     {
6000554c984SAndrew Geissler         return;
6010554c984SAndrew Geissler     }
6024e338b23SJagpal Singh Gill     if (url->scheme() == "https")
603e5cf777eSEd Tanous     {
604e5cf777eSEd Tanous         doHttpsUpdate(asyncResp, *url);
605e5cf777eSEd Tanous     }
606757178a5SEd Tanous     else
607757178a5SEd Tanous     {
608757178a5SEd Tanous         messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
609757178a5SEd Tanous                                               url->buffer());
610757178a5SEd Tanous         return;
611757178a5SEd Tanous     }
6120554c984SAndrew Geissler 
61362598e31SEd Tanous     BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
614729dae72SJennifer Lee }
615729dae72SJennifer Lee 
6160ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body)
6170ed80c8cSGeorge Liu {
6182c6ffdb0SEd Tanous     std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
6192c6ffdb0SEd Tanous 
62062598e31SEd Tanous     BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
6210ed80c8cSGeorge Liu     std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
6220ed80c8cSGeorge Liu                                     std::ofstream::trunc);
6230ed80c8cSGeorge Liu     // set the permission of the file to 640
624bd79bce8SPatrick Williams     std::filesystem::perms permission =
625bd79bce8SPatrick Williams         std::filesystem::perms::owner_read | std::filesystem::perms::group_read;
6260ed80c8cSGeorge Liu     std::filesystem::permissions(filepath, permission);
6270ed80c8cSGeorge Liu     out << body;
6280ed80c8cSGeorge Liu 
6290ed80c8cSGeorge Liu     if (out.bad())
6300ed80c8cSGeorge Liu     {
6310ed80c8cSGeorge Liu         messages::internalError(res);
6320ed80c8cSGeorge Liu         cleanUp();
6330ed80c8cSGeorge Liu     }
6340ed80c8cSGeorge Liu }
6350ed80c8cSGeorge Liu 
636de0c960cSJagpal Singh Gill // Convert the Request Apply Time to the D-Bus value
637de0c960cSJagpal Singh Gill inline bool convertApplyTime(crow::Response& res, const std::string& applyTime,
638de0c960cSJagpal Singh Gill                              std::string& applyTimeNewVal)
639de0c960cSJagpal Singh Gill {
640de0c960cSJagpal Singh Gill     if (applyTime == "Immediate")
641de0c960cSJagpal Singh Gill     {
642de0c960cSJagpal Singh Gill         applyTimeNewVal =
643049079f6SJagpal Singh Gill             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
644de0c960cSJagpal Singh Gill     }
645de0c960cSJagpal Singh Gill     else if (applyTime == "OnReset")
646de0c960cSJagpal Singh Gill     {
647de0c960cSJagpal Singh Gill         applyTimeNewVal =
648049079f6SJagpal Singh Gill             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
649de0c960cSJagpal Singh Gill     }
650de0c960cSJagpal Singh Gill     else
651de0c960cSJagpal Singh Gill     {
652de0c960cSJagpal Singh Gill         BMCWEB_LOG_WARNING(
653de0c960cSJagpal Singh Gill             "ApplyTime value {} is not in the list of acceptable values",
654de0c960cSJagpal Singh Gill             applyTime);
655de0c960cSJagpal Singh Gill         messages::propertyValueNotInList(res, applyTime, "ApplyTime");
656de0c960cSJagpal Singh Gill         return false;
657de0c960cSJagpal Singh Gill     }
658de0c960cSJagpal Singh Gill     return true;
659de0c960cSJagpal Singh Gill }
660de0c960cSJagpal Singh Gill 
6610ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
6620ed80c8cSGeorge Liu                          const std::string& applyTime)
6630ed80c8cSGeorge Liu {
6640ed80c8cSGeorge Liu     std::string applyTimeNewVal;
665049079f6SJagpal Singh Gill     if (!convertApplyTime(asyncResp->res, applyTime, applyTimeNewVal))
6660ed80c8cSGeorge Liu     {
6670ed80c8cSGeorge Liu         return;
6680ed80c8cSGeorge Liu     }
6690ed80c8cSGeorge Liu 
670e93abac6SGinu George     setDbusProperty(asyncResp, "ApplyTime", "xyz.openbmc_project.Settings",
671d02aad39SEd Tanous                     sdbusplus::message::object_path(
672d02aad39SEd Tanous                         "/xyz/openbmc_project/software/apply_time"),
673d02aad39SEd Tanous                     "xyz.openbmc_project.Software.ApplyTime",
674e93abac6SGinu George                     "RequestedApplyTime", applyTimeNewVal);
6750ed80c8cSGeorge Liu }
6760ed80c8cSGeorge Liu 
677ef93eab3SJagpal Singh Gill struct MultiPartUpdateParameters
6780ed80c8cSGeorge Liu {
679ef93eab3SJagpal Singh Gill     std::optional<std::string> applyTime;
680ef93eab3SJagpal Singh Gill     std::string uploadData;
681de0c960cSJagpal Singh Gill     std::vector<std::string> targets;
682ef93eab3SJagpal Singh Gill };
683ef93eab3SJagpal Singh Gill 
684de0c960cSJagpal Singh Gill inline std::optional<std::string>
685de0c960cSJagpal Singh Gill     processUrl(boost::system::result<boost::urls::url_view>& url)
686de0c960cSJagpal Singh Gill {
687de0c960cSJagpal Singh Gill     if (!url)
688de0c960cSJagpal Singh Gill     {
689de0c960cSJagpal Singh Gill         return std::nullopt;
690de0c960cSJagpal Singh Gill     }
691de0c960cSJagpal Singh Gill     if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers",
692de0c960cSJagpal Singh Gill                                        BMCWEB_REDFISH_MANAGER_URI_NAME))
693de0c960cSJagpal Singh Gill     {
694de0c960cSJagpal Singh Gill         return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME));
695de0c960cSJagpal Singh Gill     }
696de0c960cSJagpal Singh Gill     if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
697de0c960cSJagpal Singh Gill     {
698de0c960cSJagpal Singh Gill         return std::nullopt;
699de0c960cSJagpal Singh Gill     }
700de0c960cSJagpal Singh Gill     std::string firmwareId;
701de0c960cSJagpal Singh Gill     if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService",
702de0c960cSJagpal Singh Gill                                         "FirmwareInventory",
703de0c960cSJagpal Singh Gill                                         std::ref(firmwareId)))
704de0c960cSJagpal Singh Gill     {
705de0c960cSJagpal Singh Gill         return std::nullopt;
706de0c960cSJagpal Singh Gill     }
707de0c960cSJagpal Singh Gill 
708de0c960cSJagpal Singh Gill     return std::make_optional(firmwareId);
709de0c960cSJagpal Singh Gill }
710de0c960cSJagpal Singh Gill 
711ef93eab3SJagpal Singh Gill inline std::optional<MultiPartUpdateParameters>
712ef93eab3SJagpal Singh Gill     extractMultipartUpdateParameters(
713ef93eab3SJagpal Singh Gill         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
714ef93eab3SJagpal Singh Gill         MultipartParser parser)
715ef93eab3SJagpal Singh Gill {
716ef93eab3SJagpal Singh Gill     MultiPartUpdateParameters multiRet;
717ef93eab3SJagpal Singh Gill     for (FormPart& formpart : parser.mime_fields)
7180ed80c8cSGeorge Liu     {
7190ed80c8cSGeorge Liu         boost::beast::http::fields::const_iterator it =
7200ed80c8cSGeorge Liu             formpart.fields.find("Content-Disposition");
7210ed80c8cSGeorge Liu         if (it == formpart.fields.end())
7220ed80c8cSGeorge Liu         {
72362598e31SEd Tanous             BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
724ef93eab3SJagpal Singh Gill             return std::nullopt;
7250ed80c8cSGeorge Liu         }
72662598e31SEd Tanous         BMCWEB_LOG_INFO("Parsing value {}", it->value());
7270ed80c8cSGeorge Liu 
7280ed80c8cSGeorge Liu         // The construction parameters of param_list must start with `;`
7290ed80c8cSGeorge Liu         size_t index = it->value().find(';');
7300ed80c8cSGeorge Liu         if (index == std::string::npos)
7310ed80c8cSGeorge Liu         {
7320ed80c8cSGeorge Liu             continue;
7330ed80c8cSGeorge Liu         }
7340ed80c8cSGeorge Liu 
73589492a15SPatrick Williams         for (const auto& param :
7360ed80c8cSGeorge Liu              boost::beast::http::param_list{it->value().substr(index)})
7370ed80c8cSGeorge Liu         {
7380ed80c8cSGeorge Liu             if (param.first != "name" || param.second.empty())
7390ed80c8cSGeorge Liu             {
7400ed80c8cSGeorge Liu                 continue;
7410ed80c8cSGeorge Liu             }
7420ed80c8cSGeorge Liu 
7430ed80c8cSGeorge Liu             if (param.second == "UpdateParameters")
7440ed80c8cSGeorge Liu             {
745ef93eab3SJagpal Singh Gill                 std::vector<std::string> tempTargets;
746bd79bce8SPatrick Williams                 nlohmann::json content =
747bd79bce8SPatrick Williams                     nlohmann::json::parse(formpart.content, nullptr, false);
748ac1e1246SEd Tanous                 if (content.is_discarded())
749ac1e1246SEd Tanous                 {
750ac1e1246SEd Tanous                     return std::nullopt;
751ac1e1246SEd Tanous                 }
7527cb59f65SEd Tanous                 nlohmann::json::object_t* obj =
7537cb59f65SEd Tanous                     content.get_ptr<nlohmann::json::object_t*>();
7547cb59f65SEd Tanous                 if (obj == nullptr)
7557cb59f65SEd Tanous                 {
756ef93eab3SJagpal Singh Gill                     messages::propertyValueTypeError(
757ef93eab3SJagpal Singh Gill                         asyncResp->res, formpart.content, "UpdateParameters");
758ef93eab3SJagpal Singh Gill                     return std::nullopt;
7597cb59f65SEd Tanous                 }
7607cb59f65SEd Tanous 
761afc474aeSMyung Bae                 if (!json_util::readJsonObject( //
762afc474aeSMyung Bae                         *obj, asyncResp->res, //
763afc474aeSMyung Bae                         "@Redfish.OperationApplyTime", multiRet.applyTime, //
764afc474aeSMyung Bae                         "Targets", tempTargets //
765afc474aeSMyung Bae                         ))
7660ed80c8cSGeorge Liu                 {
767ef93eab3SJagpal Singh Gill                     return std::nullopt;
7680ed80c8cSGeorge Liu                 }
769ef93eab3SJagpal Singh Gill 
770ef93eab3SJagpal Singh Gill                 for (size_t urlIndex = 0; urlIndex < tempTargets.size();
771ef93eab3SJagpal Singh Gill                      urlIndex++)
7720ed80c8cSGeorge Liu                 {
773ef93eab3SJagpal Singh Gill                     const std::string& target = tempTargets[urlIndex];
774ef93eab3SJagpal Singh Gill                     boost::system::result<boost::urls::url_view> url =
775ef93eab3SJagpal Singh Gill                         boost::urls::parse_origin_form(target);
776de0c960cSJagpal Singh Gill                     auto res = processUrl(url);
777de0c960cSJagpal Singh Gill                     if (!res.has_value())
7780ed80c8cSGeorge Liu                     {
779ef93eab3SJagpal Singh Gill                         messages::propertyValueFormatError(
780ef93eab3SJagpal Singh Gill                             asyncResp->res, target,
781ef93eab3SJagpal Singh Gill                             std::format("Targets/{}", urlIndex));
782ef93eab3SJagpal Singh Gill                         return std::nullopt;
7830ed80c8cSGeorge Liu                     }
784de0c960cSJagpal Singh Gill                     multiRet.targets.emplace_back(res.value());
785ef93eab3SJagpal Singh Gill                 }
786ef93eab3SJagpal Singh Gill                 if (multiRet.targets.size() != 1)
787ef93eab3SJagpal Singh Gill                 {
788ef93eab3SJagpal Singh Gill                     messages::propertyValueFormatError(
789ef93eab3SJagpal Singh Gill                         asyncResp->res, multiRet.targets, "Targets");
790ef93eab3SJagpal Singh Gill                     return std::nullopt;
791ef93eab3SJagpal Singh Gill                 }
7920ed80c8cSGeorge Liu             }
7930ed80c8cSGeorge Liu             else if (param.second == "UpdateFile")
7940ed80c8cSGeorge Liu             {
795ef93eab3SJagpal Singh Gill                 multiRet.uploadData = std::move(formpart.content);
7960ed80c8cSGeorge Liu             }
7970ed80c8cSGeorge Liu         }
7980ed80c8cSGeorge Liu     }
7990ed80c8cSGeorge Liu 
800ef93eab3SJagpal Singh Gill     if (multiRet.uploadData.empty())
8010ed80c8cSGeorge Liu     {
80262598e31SEd Tanous         BMCWEB_LOG_ERROR("Upload data is NULL");
8030ed80c8cSGeorge Liu         messages::propertyMissing(asyncResp->res, "UpdateFile");
804ef93eab3SJagpal Singh Gill         return std::nullopt;
8050ed80c8cSGeorge Liu     }
806ef93eab3SJagpal Singh Gill     if (multiRet.targets.empty())
8070ed80c8cSGeorge Liu     {
808ef93eab3SJagpal Singh Gill         messages::propertyMissing(asyncResp->res, "Targets");
809ef93eab3SJagpal Singh Gill         return std::nullopt;
810ef93eab3SJagpal Singh Gill     }
811ef93eab3SJagpal Singh Gill     return multiRet;
8120ed80c8cSGeorge Liu }
8130ed80c8cSGeorge Liu 
814bd79bce8SPatrick Williams inline void handleStartUpdate(
815bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
816bd79bce8SPatrick Williams     const std::string& objectPath, const boost::system::error_code& ec,
817de0c960cSJagpal Singh Gill     const sdbusplus::message::object_path& retPath)
818de0c960cSJagpal Singh Gill {
819de0c960cSJagpal Singh Gill     if (ec)
820de0c960cSJagpal Singh Gill     {
821de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("error_code = {}", ec);
822de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("error msg = {}", ec.message());
823de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
824de0c960cSJagpal Singh Gill         return;
825de0c960cSJagpal Singh Gill     }
826de0c960cSJagpal Singh Gill 
827587090cdSJagpal Singh Gill     BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}",
828587090cdSJagpal Singh Gill                     objectPath, retPath.str);
829587090cdSJagpal Singh Gill     createTask(asyncResp, std::move(payload), retPath);
830de0c960cSJagpal Singh Gill }
831de0c960cSJagpal Singh Gill 
832bd79bce8SPatrick Williams inline void startUpdate(
833bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
834bd79bce8SPatrick Williams     const MemoryFileDescriptor& memfd, const std::string& applyTime,
835bd79bce8SPatrick Williams     const std::string& objectPath, const std::string& serviceName)
836de0c960cSJagpal Singh Gill {
837de0c960cSJagpal Singh Gill     crow::connections::systemBus->async_method_call(
838de0c960cSJagpal Singh Gill         [asyncResp, payload = std::move(payload),
839de0c960cSJagpal Singh Gill          objectPath](const boost::system::error_code& ec1,
840de0c960cSJagpal Singh Gill                      const sdbusplus::message::object_path& retPath) mutable {
841de0c960cSJagpal Singh Gill             handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1,
842de0c960cSJagpal Singh Gill                               retPath);
843de0c960cSJagpal Singh Gill         },
844de0c960cSJagpal Singh Gill         serviceName, objectPath, "xyz.openbmc_project.Software.Update",
845de0c960cSJagpal Singh Gill         "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime);
846de0c960cSJagpal Singh Gill }
847de0c960cSJagpal Singh Gill 
84808f61d53SJagpal Singh Gill inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
84908f61d53SJagpal Singh Gill                       task::Payload payload, const MemoryFileDescriptor& memfd,
85008f61d53SJagpal Singh Gill                       const std::string& applyTime, const std::string& target,
851de0c960cSJagpal Singh Gill                       const boost::system::error_code& ec,
852de0c960cSJagpal Singh Gill                       const dbus::utility::MapperGetSubTreeResponse& subtree)
853de0c960cSJagpal Singh Gill {
85408f61d53SJagpal Singh Gill     using SwInfoMap = std::unordered_map<
85508f61d53SJagpal Singh Gill         std::string, std::pair<sdbusplus::message::object_path, std::string>>;
856de0c960cSJagpal Singh Gill     SwInfoMap swInfoMap;
857de0c960cSJagpal Singh Gill 
858de0c960cSJagpal Singh Gill     if (ec)
859de0c960cSJagpal Singh Gill     {
860de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("error_code = {}", ec);
861de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("error msg = {}", ec.message());
862de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
863de0c960cSJagpal Singh Gill         return;
864de0c960cSJagpal Singh Gill     }
865de0c960cSJagpal Singh Gill     BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size());
866de0c960cSJagpal Singh Gill 
86708f61d53SJagpal Singh Gill     for (const auto& entry : subtree)
868de0c960cSJagpal Singh Gill     {
86908f61d53SJagpal Singh Gill         sdbusplus::message::object_path path(entry.first);
870de0c960cSJagpal Singh Gill         std::string swId = path.filename();
87108f61d53SJagpal Singh Gill         swInfoMap.emplace(swId, make_pair(path, entry.second[0].first));
872de0c960cSJagpal Singh Gill     }
873de0c960cSJagpal Singh Gill 
874de0c960cSJagpal Singh Gill     auto swEntry = swInfoMap.find(target);
875de0c960cSJagpal Singh Gill     if (swEntry == swInfoMap.end())
876de0c960cSJagpal Singh Gill     {
877de0c960cSJagpal Singh Gill         BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target);
878de0c960cSJagpal Singh Gill         messages::propertyValueFormatError(asyncResp->res, target, "Targets");
879de0c960cSJagpal Singh Gill         return;
880de0c960cSJagpal Singh Gill     }
881de0c960cSJagpal Singh Gill 
88208f61d53SJagpal Singh Gill     BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}",
88308f61d53SJagpal Singh Gill                      swEntry->second.first.str, swEntry->second.second);
884de0c960cSJagpal Singh Gill 
88508f61d53SJagpal Singh Gill     startUpdate(asyncResp, std::move(payload), memfd, applyTime,
88608f61d53SJagpal Singh Gill                 swEntry->second.first.str, swEntry->second.second);
88708f61d53SJagpal Singh Gill }
88808f61d53SJagpal Singh Gill 
889bd79bce8SPatrick Williams inline void handleBMCUpdate(
890bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
891bd79bce8SPatrick Williams     const MemoryFileDescriptor& memfd, const std::string& applyTime,
89208f61d53SJagpal Singh Gill     const boost::system::error_code& ec,
89308f61d53SJagpal Singh Gill     const dbus::utility::MapperEndPoints& functionalSoftware)
89408f61d53SJagpal Singh Gill {
89508f61d53SJagpal Singh Gill     if (ec)
89608f61d53SJagpal Singh Gill     {
89708f61d53SJagpal Singh Gill         BMCWEB_LOG_ERROR("error_code = {}", ec);
89808f61d53SJagpal Singh Gill         BMCWEB_LOG_ERROR("error msg = {}", ec.message());
89908f61d53SJagpal Singh Gill         messages::internalError(asyncResp->res);
90008f61d53SJagpal Singh Gill         return;
90108f61d53SJagpal Singh Gill     }
90208f61d53SJagpal Singh Gill     if (functionalSoftware.size() != 1)
90308f61d53SJagpal Singh Gill     {
90408f61d53SJagpal Singh Gill         BMCWEB_LOG_ERROR("Found {} functional software endpoints",
90508f61d53SJagpal Singh Gill                          functionalSoftware.size());
90608f61d53SJagpal Singh Gill         messages::internalError(asyncResp->res);
90708f61d53SJagpal Singh Gill         return;
90808f61d53SJagpal Singh Gill     }
90908f61d53SJagpal Singh Gill 
91008f61d53SJagpal Singh Gill     startUpdate(asyncResp, std::move(payload), memfd, applyTime,
91108f61d53SJagpal Singh Gill                 functionalSoftware[0], "xyz.openbmc_project.Software.Manager");
912de0c960cSJagpal Singh Gill }
913de0c960cSJagpal Singh Gill 
914bd79bce8SPatrick Williams inline void processUpdateRequest(
915bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
9169dae4deeSJagpal Singh Gill     task::Payload&& payload, std::string_view body,
917bd79bce8SPatrick Williams     const std::string& applyTime, std::vector<std::string>& targets)
918de0c960cSJagpal Singh Gill {
919de0c960cSJagpal Singh Gill     MemoryFileDescriptor memfd("update-image");
920de0c960cSJagpal Singh Gill     if (memfd.fd == -1)
921de0c960cSJagpal Singh Gill     {
922de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("Failed to create image memfd");
923de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
924de0c960cSJagpal Singh Gill         return;
925de0c960cSJagpal Singh Gill     }
926de0c960cSJagpal Singh Gill     if (write(memfd.fd, body.data(), body.length()) !=
927de0c960cSJagpal Singh Gill         static_cast<ssize_t>(body.length()))
928de0c960cSJagpal Singh Gill     {
929de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("Failed to write to image memfd");
930de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
931de0c960cSJagpal Singh Gill         return;
932de0c960cSJagpal Singh Gill     }
933de0c960cSJagpal Singh Gill     if (!memfd.rewind())
934de0c960cSJagpal Singh Gill     {
935de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
936de0c960cSJagpal Singh Gill         return;
937de0c960cSJagpal Singh Gill     }
938de0c960cSJagpal Singh Gill 
939de0c960cSJagpal Singh Gill     if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME)
940de0c960cSJagpal Singh Gill     {
94108f61d53SJagpal Singh Gill         dbus::utility::getAssociationEndPoints(
94289449bbeSJagpal Singh Gill             "/xyz/openbmc_project/software/bmc/updateable",
94308f61d53SJagpal Singh Gill             [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
94408f61d53SJagpal Singh Gill              applyTime](
94508f61d53SJagpal Singh Gill                 const boost::system::error_code& ec,
94608f61d53SJagpal Singh Gill                 const dbus::utility::MapperEndPoints& objectPaths) mutable {
947bd79bce8SPatrick Williams                 handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime,
948bd79bce8SPatrick Williams                                 ec, objectPaths);
94908f61d53SJagpal Singh Gill             });
950de0c960cSJagpal Singh Gill     }
951de0c960cSJagpal Singh Gill     else
952de0c960cSJagpal Singh Gill     {
953de0c960cSJagpal Singh Gill         constexpr std::array<std::string_view, 1> interfaces = {
954de0c960cSJagpal Singh Gill             "xyz.openbmc_project.Software.Version"};
95508f61d53SJagpal Singh Gill         dbus::utility::getSubTree(
956de0c960cSJagpal Singh Gill             "/xyz/openbmc_project/software", 1, interfaces,
957de0c960cSJagpal Singh Gill             [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
95808f61d53SJagpal Singh Gill              applyTime, targets](const boost::system::error_code& ec,
95908f61d53SJagpal Singh Gill                                  const dbus::utility::MapperGetSubTreeResponse&
960de0c960cSJagpal Singh Gill                                      subtree) mutable {
96108f61d53SJagpal Singh Gill                 getSwInfo(asyncResp, std::move(payload), memfd, applyTime,
96208f61d53SJagpal Singh Gill                           targets[0], ec, subtree);
963de0c960cSJagpal Singh Gill             });
964de0c960cSJagpal Singh Gill     }
965de0c960cSJagpal Singh Gill }
966de0c960cSJagpal Singh Gill 
967de0c960cSJagpal Singh Gill inline void
968ef93eab3SJagpal Singh Gill     updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
969ef93eab3SJagpal Singh Gill                            const crow::Request& req, MultipartParser&& parser)
970ef93eab3SJagpal Singh Gill {
971ef93eab3SJagpal Singh Gill     std::optional<MultiPartUpdateParameters> multipart =
972ef93eab3SJagpal Singh Gill         extractMultipartUpdateParameters(asyncResp, std::move(parser));
973ef93eab3SJagpal Singh Gill     if (!multipart)
974ef93eab3SJagpal Singh Gill     {
975ef93eab3SJagpal Singh Gill         return;
976ef93eab3SJagpal Singh Gill     }
977ef93eab3SJagpal Singh Gill     if (!multipart->applyTime)
978ef93eab3SJagpal Singh Gill     {
979ef93eab3SJagpal Singh Gill         multipart->applyTime = "OnReset";
980ef93eab3SJagpal Singh Gill     }
981ef93eab3SJagpal Singh Gill 
982de0c960cSJagpal Singh Gill     if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
983de0c960cSJagpal Singh Gill     {
9849dae4deeSJagpal Singh Gill         std::string applyTimeNewVal;
9859dae4deeSJagpal Singh Gill         if (!convertApplyTime(asyncResp->res, *multipart->applyTime,
9869dae4deeSJagpal Singh Gill                               applyTimeNewVal))
9879dae4deeSJagpal Singh Gill         {
9889dae4deeSJagpal Singh Gill             return;
9899dae4deeSJagpal Singh Gill         }
9909dae4deeSJagpal Singh Gill         task::Payload payload(req);
9919dae4deeSJagpal Singh Gill 
9929dae4deeSJagpal Singh Gill         processUpdateRequest(asyncResp, std::move(payload),
9939dae4deeSJagpal Singh Gill                              multipart->uploadData, applyTimeNewVal,
9949dae4deeSJagpal Singh Gill                              multipart->targets);
995de0c960cSJagpal Singh Gill     }
996de0c960cSJagpal Singh Gill     else
997de0c960cSJagpal Singh Gill     {
998ef93eab3SJagpal Singh Gill         setApplyTime(asyncResp, *multipart->applyTime);
9990ed80c8cSGeorge Liu 
10006b54e4e0SEd Tanous         // Setup callback for when new software detected
1001de0c960cSJagpal Singh Gill         monitorForSoftwareAvailable(asyncResp, req,
1002de0c960cSJagpal Singh Gill                                     "/redfish/v1/UpdateService");
10036b54e4e0SEd Tanous 
1004ef93eab3SJagpal Singh Gill         uploadImageFile(asyncResp->res, multipart->uploadData);
10050ed80c8cSGeorge Liu     }
1006de0c960cSJagpal Singh Gill }
10070ed80c8cSGeorge Liu 
10089dae4deeSJagpal Singh Gill inline void doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
10099dae4deeSJagpal Singh Gill                          const crow::Request& req)
10109dae4deeSJagpal Singh Gill {
10119dae4deeSJagpal Singh Gill     if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
10129dae4deeSJagpal Singh Gill     {
10139dae4deeSJagpal Singh Gill         task::Payload payload(req);
10149dae4deeSJagpal Singh Gill         // HTTP push only supports BMC updates (with ApplyTime as immediate) for
10159dae4deeSJagpal Singh Gill         // backwards compatibility. Specific component updates will be handled
10169dae4deeSJagpal Singh Gill         // through Multipart form HTTP push.
10179dae4deeSJagpal Singh Gill         std::vector<std::string> targets;
10189dae4deeSJagpal Singh Gill         targets.emplace_back(BMCWEB_REDFISH_MANAGER_URI_NAME);
10199dae4deeSJagpal Singh Gill 
10209dae4deeSJagpal Singh Gill         processUpdateRequest(
10219dae4deeSJagpal Singh Gill             asyncResp, std::move(payload), req.body(),
10229dae4deeSJagpal Singh Gill             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate",
10239dae4deeSJagpal Singh Gill             targets);
10249dae4deeSJagpal Singh Gill     }
10259dae4deeSJagpal Singh Gill     else
10269dae4deeSJagpal Singh Gill     {
10279dae4deeSJagpal Singh Gill         // Setup callback for when new software detected
10289dae4deeSJagpal Singh Gill         monitorForSoftwareAvailable(asyncResp, req,
10299dae4deeSJagpal Singh Gill                                     "/redfish/v1/UpdateService");
10309dae4deeSJagpal Singh Gill 
10319dae4deeSJagpal Singh Gill         uploadImageFile(asyncResp->res, req.body());
10329dae4deeSJagpal Singh Gill     }
10339dae4deeSJagpal Singh Gill }
10349dae4deeSJagpal Singh Gill 
1035c2051d11SEd Tanous inline void
1036c2051d11SEd Tanous     handleUpdateServicePost(App& app, const crow::Request& req,
1037c2051d11SEd Tanous                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1038c2051d11SEd Tanous {
10393ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1040c2051d11SEd Tanous     {
1041c2051d11SEd Tanous         return;
1042c2051d11SEd Tanous     }
1043b33a4327SNinad Palsule     std::string_view contentType = req.getHeaderValue("Content-Type");
1044b33a4327SNinad Palsule 
104562598e31SEd Tanous     BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
1046b33a4327SNinad Palsule 
1047b33a4327SNinad Palsule     // Make sure that content type is application/octet-stream or
1048b33a4327SNinad Palsule     // multipart/form-data
104918f8f608SEd Tanous     if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
1050b33a4327SNinad Palsule     {
10519dae4deeSJagpal Singh Gill         doHTTPUpdate(asyncResp, req);
1052b33a4327SNinad Palsule     }
1053b33a4327SNinad Palsule     else if (contentType.starts_with("multipart/form-data"))
1054b33a4327SNinad Palsule     {
1055b33a4327SNinad Palsule         MultipartParser parser;
1056c2051d11SEd Tanous 
10570ed80c8cSGeorge Liu         ParserError ec = parser.parse(req);
10580ed80c8cSGeorge Liu         if (ec != ParserError::PARSER_SUCCESS)
10590ed80c8cSGeorge Liu         {
10600ed80c8cSGeorge Liu             // handle error
106162598e31SEd Tanous             BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
106262598e31SEd Tanous                              static_cast<int>(ec));
10630ed80c8cSGeorge Liu             messages::internalError(asyncResp->res);
10640ed80c8cSGeorge Liu             return;
10650ed80c8cSGeorge Liu         }
10666b54e4e0SEd Tanous 
1067ef93eab3SJagpal Singh Gill         updateMultipartContext(asyncResp, req, std::move(parser));
1068c2051d11SEd Tanous     }
1069b33a4327SNinad Palsule     else
1070b33a4327SNinad Palsule     {
107162598e31SEd Tanous         BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
1072b33a4327SNinad Palsule         asyncResp->res.result(boost::beast::http::status::bad_request);
1073b33a4327SNinad Palsule     }
1074b33a4327SNinad Palsule }
1075c2051d11SEd Tanous 
1076f5139334SEd Tanous inline void
1077f5139334SEd Tanous     handleUpdateServiceGet(App& app, const crow::Request& req,
1078f5139334SEd Tanous                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
10791abe55efSEd Tanous {
10803ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
108145ca1b86SEd Tanous     {
108245ca1b86SEd Tanous         return;
108345ca1b86SEd Tanous     }
10848d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.type"] =
10850ed80c8cSGeorge Liu         "#UpdateService.v1_11_1.UpdateService";
10868d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
10878d1b46d7Szhanghch05     asyncResp->res.jsonValue["Id"] = "UpdateService";
1088002d39b4SEd Tanous     asyncResp->res.jsonValue["Description"] = "Service for Software Update";
10898d1b46d7Szhanghch05     asyncResp->res.jsonValue["Name"] = "Update Service";
10904dc23f3fSEd Tanous 
10917e860f15SJohn Edward Broadbent     asyncResp->res.jsonValue["HttpPushUri"] =
10924dc23f3fSEd Tanous         "/redfish/v1/UpdateService/update";
10930ed80c8cSGeorge Liu     asyncResp->res.jsonValue["MultipartHttpPushUri"] =
10940ed80c8cSGeorge Liu         "/redfish/v1/UpdateService/update";
10954dc23f3fSEd Tanous 
10960f74e643SEd Tanous     // UpdateService cannot be disabled
10978d1b46d7Szhanghch05     asyncResp->res.jsonValue["ServiceEnabled"] = true;
10981476687dSEd Tanous     asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
10991476687dSEd Tanous         "/redfish/v1/UpdateService/FirmwareInventory";
1100d61e5194STejas Patil     // Get the MaxImageSizeBytes
1101bd79bce8SPatrick Williams     asyncResp->res.jsonValue["MaxImageSizeBytes"] =
1102bd79bce8SPatrick Williams         BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024;
1103d61e5194STejas Patil 
1104*6a37140aSEd Tanous     if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE)
1105*6a37140aSEd Tanous     {
11060554c984SAndrew Geissler         // Update Actions object.
11070554c984SAndrew Geissler         nlohmann::json& updateSvcSimpleUpdate =
1108002d39b4SEd Tanous             asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
11090554c984SAndrew Geissler         updateSvcSimpleUpdate["target"] =
11100554c984SAndrew Geissler             "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
1111757178a5SEd Tanous 
1112757178a5SEd Tanous         nlohmann::json::array_t allowed;
1113e5cf777eSEd Tanous         allowed.emplace_back(update_service::TransferProtocolType::HTTPS);
1114757178a5SEd Tanous         updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
1115757178a5SEd Tanous             std::move(allowed);
1116*6a37140aSEd Tanous     }
1117757178a5SEd Tanous 
1118539d8c6bSEd Tanous     asyncResp->res
1119539d8c6bSEd Tanous         .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] =
1120539d8c6bSEd Tanous         update_service::ApplyTime::Immediate;
1121729dae72SJennifer Lee }
1122729dae72SJennifer Lee 
1123f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryCollectionGet(
1124f5139334SEd Tanous     App& app, const crow::Request& req,
1125f5139334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
11261abe55efSEd Tanous {
11273ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
112845ca1b86SEd Tanous     {
112945ca1b86SEd Tanous         return;
113045ca1b86SEd Tanous     }
11318d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.type"] =
11320f74e643SEd Tanous         "#SoftwareInventoryCollection.SoftwareInventoryCollection";
11338d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.id"] =
11340f74e643SEd Tanous         "/redfish/v1/UpdateService/FirmwareInventory";
1135002d39b4SEd Tanous     asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
113608d81adaSJohn Edward Broadbent     const std::array<const std::string_view, 1> iface = {
1137e99073f5SGeorge Liu         "xyz.openbmc_project.Software.Version"};
11386c4eb9deSJennifer Lee 
113908d81adaSJohn Edward Broadbent     redfish::collection_util::getCollectionMembers(
114008d81adaSJohn Edward Broadbent         asyncResp,
1141f5139334SEd Tanous         boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface,
1142f5139334SEd Tanous         "/xyz/openbmc_project/software");
1143729dae72SJennifer Lee }
1144f5139334SEd Tanous 
114587d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */
1146f5139334SEd Tanous inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
114787d84729SAndrew Geissler                             const std::string& purpose)
114887d84729SAndrew Geissler {
1149eee0013eSWilly Tu     if (purpose == sw_util::bmcPurpose)
115087d84729SAndrew Geissler     {
1151ac106bf6SEd Tanous         nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
11521476687dSEd Tanous         nlohmann::json::object_t item;
1153253f11b8SEd Tanous         item["@odata.id"] = boost::urls::format(
1154253f11b8SEd Tanous             "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME);
1155b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
1156ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1157ac106bf6SEd Tanous             relatedItem.size();
115887d84729SAndrew Geissler     }
1159eee0013eSWilly Tu     else if (purpose == sw_util::biosPurpose)
116087d84729SAndrew Geissler     {
1161ac106bf6SEd Tanous         nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
11621476687dSEd Tanous         nlohmann::json::object_t item;
1163253f11b8SEd Tanous         item["@odata.id"] = std::format("/redfish/v1/Systems/{}/Bios",
1164253f11b8SEd Tanous                                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
1165b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
1166ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1167ac106bf6SEd Tanous             relatedItem.size();
116887d84729SAndrew Geissler     }
116987d84729SAndrew Geissler     else
117087d84729SAndrew Geissler     {
1171bf2ddedeSCarson Labrado         BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
117287d84729SAndrew Geissler     }
117387d84729SAndrew Geissler }
117487d84729SAndrew Geissler 
1175af24660dSWilly Tu inline void
1176af24660dSWilly Tu     getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1177af24660dSWilly Tu                        const std::string& service, const std::string& path,
1178af24660dSWilly Tu                        const std::string& swId)
1179af24660dSWilly Tu {
1180d1bde9e5SKrzysztof Grobelny     sdbusplus::asio::getAllProperties(
1181d1bde9e5SKrzysztof Grobelny         *crow::connections::systemBus, service, path,
1182d1bde9e5SKrzysztof Grobelny         "xyz.openbmc_project.Software.Version",
1183af24660dSWilly Tu         [asyncResp,
11848b24275dSEd Tanous          swId](const boost::system::error_code& ec,
1185af24660dSWilly Tu                const dbus::utility::DBusPropertiesMap& propertiesList) {
11868b24275dSEd Tanous             if (ec)
1187af24660dSWilly Tu             {
1188af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1189af24660dSWilly Tu                 return;
1190af24660dSWilly Tu             }
1191d1bde9e5SKrzysztof Grobelny 
1192af24660dSWilly Tu             const std::string* swInvPurpose = nullptr;
1193af24660dSWilly Tu             const std::string* version = nullptr;
1194d1bde9e5SKrzysztof Grobelny 
1195d1bde9e5SKrzysztof Grobelny             const bool success = sdbusplus::unpackPropertiesNoThrow(
1196d1bde9e5SKrzysztof Grobelny                 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
1197d1bde9e5SKrzysztof Grobelny                 swInvPurpose, "Version", version);
1198d1bde9e5SKrzysztof Grobelny 
1199d1bde9e5SKrzysztof Grobelny             if (!success)
1200af24660dSWilly Tu             {
1201d1bde9e5SKrzysztof Grobelny                 messages::internalError(asyncResp->res);
1202d1bde9e5SKrzysztof Grobelny                 return;
1203af24660dSWilly Tu             }
1204af24660dSWilly Tu 
1205af24660dSWilly Tu             if (swInvPurpose == nullptr)
1206af24660dSWilly Tu             {
120762598e31SEd Tanous                 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
1208af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1209af24660dSWilly Tu                 return;
1210af24660dSWilly Tu             }
1211af24660dSWilly Tu 
121262598e31SEd Tanous             BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
1213af24660dSWilly Tu 
1214af24660dSWilly Tu             if (version == nullptr)
1215af24660dSWilly Tu             {
121662598e31SEd Tanous                 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
1217af24660dSWilly Tu 
1218af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1219af24660dSWilly Tu 
1220af24660dSWilly Tu                 return;
1221af24660dSWilly Tu             }
1222af24660dSWilly Tu             asyncResp->res.jsonValue["Version"] = *version;
1223af24660dSWilly Tu             asyncResp->res.jsonValue["Id"] = swId;
1224af24660dSWilly Tu 
1225af24660dSWilly Tu             // swInvPurpose is of format:
1226af24660dSWilly Tu             // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
1227af24660dSWilly Tu             // Translate this to "ABC image"
1228af24660dSWilly Tu             size_t endDesc = swInvPurpose->rfind('.');
1229af24660dSWilly Tu             if (endDesc == std::string::npos)
1230af24660dSWilly Tu             {
1231af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1232af24660dSWilly Tu                 return;
1233af24660dSWilly Tu             }
1234af24660dSWilly Tu             endDesc++;
1235af24660dSWilly Tu             if (endDesc >= swInvPurpose->size())
1236af24660dSWilly Tu             {
1237af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1238af24660dSWilly Tu                 return;
1239af24660dSWilly Tu             }
1240af24660dSWilly Tu 
1241af24660dSWilly Tu             std::string formatDesc = swInvPurpose->substr(endDesc);
1242af24660dSWilly Tu             asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1243af24660dSWilly Tu             getRelatedItems(asyncResp, *swInvPurpose);
1244d1bde9e5SKrzysztof Grobelny         });
1245af24660dSWilly Tu }
1246af24660dSWilly Tu 
1247f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryGet(
1248f5139334SEd Tanous     App& app, const crow::Request& req,
124945ca1b86SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1250f5139334SEd Tanous     const std::string& param)
1251f5139334SEd Tanous {
12523ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
125345ca1b86SEd Tanous     {
125445ca1b86SEd Tanous         return;
125545ca1b86SEd Tanous     }
1256f5139334SEd Tanous     std::shared_ptr<std::string> swId = std::make_shared<std::string>(param);
1257c711bf86SEd Tanous 
1258ef4c65b7SEd Tanous     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1259ef4c65b7SEd Tanous         "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
1260c711bf86SEd Tanous 
1261e99073f5SGeorge Liu     constexpr std::array<std::string_view, 1> interfaces = {
1262e99073f5SGeorge Liu         "xyz.openbmc_project.Software.Version"};
1263e99073f5SGeorge Liu     dbus::utility::getSubTree(
1264e99073f5SGeorge Liu         "/", 0, interfaces,
1265b9d36b47SEd Tanous         [asyncResp,
1266e99073f5SGeorge Liu          swId](const boost::system::error_code& ec,
1267b9d36b47SEd Tanous                const dbus::utility::MapperGetSubTreeResponse& subtree) {
126862598e31SEd Tanous             BMCWEB_LOG_DEBUG("doGet callback...");
12691abe55efSEd Tanous             if (ec)
12701abe55efSEd Tanous             {
1271f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
12726c4eb9deSJennifer Lee                 return;
12736c4eb9deSJennifer Lee             }
12746c4eb9deSJennifer Lee 
12756913228dSAndrew Geissler             // Ensure we find our input swId, otherwise return an error
12766913228dSAndrew Geissler             bool found = false;
1277bd79bce8SPatrick Williams             for (const std::pair<std::string,
1278bd79bce8SPatrick Williams                                  std::vector<std::pair<
1279bd79bce8SPatrick Williams                                      std::string, std::vector<std::string>>>>&
1280002d39b4SEd Tanous                      obj : subtree)
12811abe55efSEd Tanous             {
128211ba3979SEd Tanous                 if (!obj.first.ends_with(*swId))
12831abe55efSEd Tanous                 {
1284acb7cfb4SJennifer Lee                     continue;
1285acb7cfb4SJennifer Lee                 }
1286acb7cfb4SJennifer Lee 
128726f6976fSEd Tanous                 if (obj.second.empty())
12881abe55efSEd Tanous                 {
1289acb7cfb4SJennifer Lee                     continue;
1290acb7cfb4SJennifer Lee                 }
12916c4eb9deSJennifer Lee 
12926913228dSAndrew Geissler                 found = true;
1293eee0013eSWilly Tu                 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1294af24660dSWilly Tu                 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1295af24660dSWilly Tu                                    *swId);
12966c4eb9deSJennifer Lee             }
12976913228dSAndrew Geissler             if (!found)
12986913228dSAndrew Geissler             {
129962598e31SEd Tanous                 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
13006913228dSAndrew Geissler                 messages::resourceMissingAtURI(
1301ef4c65b7SEd Tanous                     asyncResp->res,
1302ef4c65b7SEd Tanous                     boost::urls::format(
1303bd79bce8SPatrick Williams                         "/redfish/v1/UpdateService/FirmwareInventory/{}",
1304bd79bce8SPatrick Williams                         *swId));
13056913228dSAndrew Geissler                 return;
13066913228dSAndrew Geissler             }
13074e68c45bSAyushi Smriti             asyncResp->res.jsonValue["@odata.type"] =
13084e68c45bSAyushi Smriti                 "#SoftwareInventory.v1_1_0.SoftwareInventory";
13094e68c45bSAyushi Smriti             asyncResp->res.jsonValue["Name"] = "Software Inventory";
1310539d8c6bSEd Tanous             asyncResp->res.jsonValue["Status"]["HealthRollup"] =
1311539d8c6bSEd Tanous                 resource::Health::OK;
13123f8a743aSAppaRao Puli 
13133f8a743aSAppaRao Puli             asyncResp->res.jsonValue["Updateable"] = false;
1314eee0013eSWilly Tu             sw_util::getSwUpdatableStatus(asyncResp, swId);
1315e99073f5SGeorge Liu         });
1316f5139334SEd Tanous }
1317f5139334SEd Tanous 
1318f5139334SEd Tanous inline void requestRoutesUpdateService(App& app)
1319f5139334SEd Tanous {
1320*6a37140aSEd Tanous     if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE)
1321*6a37140aSEd Tanous     {
1322f5139334SEd Tanous         BMCWEB_ROUTE(
1323*6a37140aSEd Tanous             app,
1324*6a37140aSEd Tanous             "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
1325f5139334SEd Tanous             .privileges(redfish::privileges::postUpdateService)
1326f5139334SEd Tanous             .methods(boost::beast::http::verb::post)(std::bind_front(
1327f5139334SEd Tanous                 handleUpdateServiceSimpleUpdateAction, std::ref(app)));
1328*6a37140aSEd Tanous     }
1329f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1330f5139334SEd Tanous         .privileges(redfish::privileges::getSoftwareInventory)
1331f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
1332f5139334SEd Tanous             handleUpdateServiceFirmwareInventoryGet, std::ref(app)));
1333f5139334SEd Tanous 
1334f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1335f5139334SEd Tanous         .privileges(redfish::privileges::getUpdateService)
1336f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(
1337f5139334SEd Tanous             std::bind_front(handleUpdateServiceGet, std::ref(app)));
1338f5139334SEd Tanous 
1339f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
1340f5139334SEd Tanous         .privileges(redfish::privileges::postUpdateService)
1341f5139334SEd Tanous         .methods(boost::beast::http::verb::post)(
1342f5139334SEd Tanous             std::bind_front(handleUpdateServicePost, std::ref(app)));
1343f5139334SEd Tanous 
1344f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
1345f5139334SEd Tanous         .privileges(redfish::privileges::getSoftwareInventoryCollection)
1346f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
1347f5139334SEd Tanous             handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app)));
13486c4eb9deSJennifer Lee }
1349729dae72SJennifer Lee 
1350729dae72SJennifer Lee } // namespace redfish
1351