xref: /openbmc/bmcweb/features/redfish/lib/update_service.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
1*40e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
2*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4729dae72SJennifer Lee #pragma once
5729dae72SJennifer Lee 
6d61e5194STejas Patil #include "bmcweb_config.h"
7d61e5194STejas Patil 
83ccb3adbSEd Tanous #include "app.hpp"
93ccb3adbSEd Tanous #include "dbus_utility.hpp"
105b90429aSEd Tanous #include "error_messages.hpp"
11757178a5SEd Tanous #include "generated/enums/update_service.hpp"
120ed80c8cSGeorge Liu #include "multipart_parser.hpp"
132c6ffdb0SEd Tanous #include "ossl_random.hpp"
143ccb3adbSEd Tanous #include "query.hpp"
153ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
16a8e884fcSEd Tanous #include "task.hpp"
175b90429aSEd Tanous #include "task_messages.hpp"
1808d81adaSJohn Edward Broadbent #include "utils/collection.hpp"
193ccb3adbSEd Tanous #include "utils/dbus_utils.hpp"
205b90429aSEd Tanous #include "utils/json_utils.hpp"
213ccb3adbSEd Tanous #include "utils/sw_utils.hpp"
223ccb3adbSEd Tanous 
23de0c960cSJagpal Singh Gill #include <sys/mman.h>
24de0c960cSJagpal Singh Gill 
25e99073f5SGeorge Liu #include <boost/system/error_code.hpp>
26ef4c65b7SEd Tanous #include <boost/url/format.hpp>
271e1e598dSJonathan Doman #include <sdbusplus/asio/property.hpp>
283ccb3adbSEd Tanous #include <sdbusplus/bus/match.hpp>
29d1bde9e5SKrzysztof Grobelny #include <sdbusplus/unpack_properties.hpp>
301214b7e7SGunnar Mills 
312b73119cSGeorge Liu #include <array>
32de0c960cSJagpal Singh Gill #include <cstddef>
330ed80c8cSGeorge Liu #include <filesystem>
34c71b6c99SJagpal Singh Gill #include <functional>
35de0c960cSJagpal Singh Gill #include <iterator>
36ef93eab3SJagpal Singh Gill #include <memory>
377cb59f65SEd Tanous #include <optional>
387cb59f65SEd Tanous #include <string>
392b73119cSGeorge Liu #include <string_view>
40de0c960cSJagpal Singh Gill #include <unordered_map>
41ef93eab3SJagpal Singh Gill #include <vector>
422b73119cSGeorge Liu 
431abe55efSEd Tanous namespace redfish
441abe55efSEd Tanous {
4527826b5fSEd Tanous 
460e7de46fSAndrew Geissler // Match signals added on software path
47cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
4859d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
49cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
5059d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
510e7de46fSAndrew Geissler // Only allow one update at a time
52cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
530e7de46fSAndrew Geissler static bool fwUpdateInProgress = false;
5486adcd6dSAndrew Geissler // Timer for software available
55cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
56271584abSEd Tanous static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
5786adcd6dSAndrew Geissler 
58de0c960cSJagpal Singh Gill struct MemoryFileDescriptor
59de0c960cSJagpal Singh Gill {
60de0c960cSJagpal Singh Gill     int fd = -1;
61de0c960cSJagpal Singh Gill 
62de0c960cSJagpal Singh Gill     explicit MemoryFileDescriptor(const std::string& filename) :
63de0c960cSJagpal Singh Gill         fd(memfd_create(filename.c_str(), 0))
64de0c960cSJagpal Singh Gill     {}
65de0c960cSJagpal Singh Gill 
66de0c960cSJagpal Singh Gill     MemoryFileDescriptor(const MemoryFileDescriptor&) = default;
67de0c960cSJagpal Singh Gill     MemoryFileDescriptor(MemoryFileDescriptor&& other) noexcept : fd(other.fd)
68de0c960cSJagpal Singh Gill     {
69de0c960cSJagpal Singh Gill         other.fd = -1;
70de0c960cSJagpal Singh Gill     }
71de0c960cSJagpal Singh Gill     MemoryFileDescriptor& operator=(const MemoryFileDescriptor&) = delete;
72de0c960cSJagpal Singh Gill     MemoryFileDescriptor& operator=(MemoryFileDescriptor&&) = default;
73de0c960cSJagpal Singh Gill 
74de0c960cSJagpal Singh Gill     ~MemoryFileDescriptor()
75de0c960cSJagpal Singh Gill     {
76de0c960cSJagpal Singh Gill         if (fd != -1)
77de0c960cSJagpal Singh Gill         {
78de0c960cSJagpal Singh Gill             close(fd);
79de0c960cSJagpal Singh Gill         }
80de0c960cSJagpal Singh Gill     }
81de0c960cSJagpal Singh Gill 
82de0c960cSJagpal Singh Gill     bool rewind() const
83de0c960cSJagpal Singh Gill     {
84de0c960cSJagpal Singh Gill         if (lseek(fd, 0, SEEK_SET) == -1)
85de0c960cSJagpal Singh Gill         {
86de0c960cSJagpal Singh Gill             BMCWEB_LOG_ERROR("Failed to seek to beginning of image memfd");
87de0c960cSJagpal Singh Gill             return false;
88de0c960cSJagpal Singh Gill         }
89de0c960cSJagpal Singh Gill         return true;
90de0c960cSJagpal Singh Gill     }
91de0c960cSJagpal Singh Gill };
92de0c960cSJagpal Singh Gill 
93df254f2cSEd Tanous inline void cleanUp()
9486adcd6dSAndrew Geissler {
9586adcd6dSAndrew Geissler     fwUpdateInProgress = false;
9686adcd6dSAndrew Geissler     fwUpdateMatcher = nullptr;
974cde5d90SJames Feist     fwUpdateErrorMatcher = nullptr;
9886adcd6dSAndrew Geissler }
99df254f2cSEd Tanous 
100df254f2cSEd Tanous inline void activateImage(const std::string& objPath,
10186adcd6dSAndrew Geissler                           const std::string& service)
10286adcd6dSAndrew Geissler {
10362598e31SEd Tanous     BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
1049ae226faSGeorge Liu     sdbusplus::asio::setProperty(
1059ae226faSGeorge Liu         *crow::connections::systemBus, service, objPath,
1069ae226faSGeorge Liu         "xyz.openbmc_project.Software.Activation", "RequestedActivation",
1079ae226faSGeorge Liu         "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
1088b24275dSEd Tanous         [](const boost::system::error_code& ec) {
1098b24275dSEd Tanous             if (ec)
11086adcd6dSAndrew Geissler             {
11162598e31SEd Tanous                 BMCWEB_LOG_DEBUG("error_code = {}", ec);
11262598e31SEd Tanous                 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
11386adcd6dSAndrew Geissler             }
1149ae226faSGeorge Liu         });
11586adcd6dSAndrew Geissler }
1160554c984SAndrew Geissler 
117c71b6c99SJagpal Singh Gill inline bool handleCreateTask(const boost::system::error_code& ec2,
118c71b6c99SJagpal Singh Gill                              sdbusplus::message_t& msg,
119c71b6c99SJagpal Singh Gill                              const std::shared_ptr<task::TaskData>& taskData)
120c71b6c99SJagpal Singh Gill {
121c71b6c99SJagpal Singh Gill     if (ec2)
122c71b6c99SJagpal Singh Gill     {
123c71b6c99SJagpal Singh Gill         return task::completed;
124c71b6c99SJagpal Singh Gill     }
125c71b6c99SJagpal Singh Gill 
126c71b6c99SJagpal Singh Gill     std::string iface;
127c71b6c99SJagpal Singh Gill     dbus::utility::DBusPropertiesMap values;
128c71b6c99SJagpal Singh Gill 
129c71b6c99SJagpal Singh Gill     std::string index = std::to_string(taskData->index);
130c71b6c99SJagpal Singh Gill     msg.read(iface, values);
131c71b6c99SJagpal Singh Gill 
132c71b6c99SJagpal Singh Gill     if (iface == "xyz.openbmc_project.Software.Activation")
133c71b6c99SJagpal Singh Gill     {
134c71b6c99SJagpal Singh Gill         const std::string* state = nullptr;
135c71b6c99SJagpal Singh Gill         for (const auto& property : values)
136c71b6c99SJagpal Singh Gill         {
137c71b6c99SJagpal Singh Gill             if (property.first == "Activation")
138c71b6c99SJagpal Singh Gill             {
139c71b6c99SJagpal Singh Gill                 state = std::get_if<std::string>(&property.second);
140c71b6c99SJagpal Singh Gill                 if (state == nullptr)
141c71b6c99SJagpal Singh Gill                 {
142c71b6c99SJagpal Singh Gill                     taskData->messages.emplace_back(messages::internalError());
143c71b6c99SJagpal Singh Gill                     return task::completed;
144c71b6c99SJagpal Singh Gill                 }
145c71b6c99SJagpal Singh Gill             }
146c71b6c99SJagpal Singh Gill         }
147c71b6c99SJagpal Singh Gill 
148c71b6c99SJagpal Singh Gill         if (state == nullptr)
149c71b6c99SJagpal Singh Gill         {
150c71b6c99SJagpal Singh Gill             return !task::completed;
151c71b6c99SJagpal Singh Gill         }
152c71b6c99SJagpal Singh Gill 
153c71b6c99SJagpal Singh Gill         if (state->ends_with("Invalid") || state->ends_with("Failed"))
154c71b6c99SJagpal Singh Gill         {
155c71b6c99SJagpal Singh Gill             taskData->state = "Exception";
156c71b6c99SJagpal Singh Gill             taskData->status = "Warning";
157c71b6c99SJagpal Singh Gill             taskData->messages.emplace_back(messages::taskAborted(index));
158c71b6c99SJagpal Singh Gill             return task::completed;
159c71b6c99SJagpal Singh Gill         }
160c71b6c99SJagpal Singh Gill 
161c71b6c99SJagpal Singh Gill         if (state->ends_with("Staged"))
162c71b6c99SJagpal Singh Gill         {
163c71b6c99SJagpal Singh Gill             taskData->state = "Stopping";
164c71b6c99SJagpal Singh Gill             taskData->messages.emplace_back(messages::taskPaused(index));
165c71b6c99SJagpal Singh Gill 
166c71b6c99SJagpal Singh Gill             // its staged, set a long timer to
167c71b6c99SJagpal Singh Gill             // allow them time to complete the
168c71b6c99SJagpal Singh Gill             // update (probably cycle the
169c71b6c99SJagpal Singh Gill             // system) if this expires then
170c71b6c99SJagpal Singh Gill             // task will be canceled
171c71b6c99SJagpal Singh Gill             taskData->extendTimer(std::chrono::hours(5));
172c71b6c99SJagpal Singh Gill             return !task::completed;
173c71b6c99SJagpal Singh Gill         }
174c71b6c99SJagpal Singh Gill 
175c71b6c99SJagpal Singh Gill         if (state->ends_with("Active"))
176c71b6c99SJagpal Singh Gill         {
177c71b6c99SJagpal Singh Gill             taskData->messages.emplace_back(messages::taskCompletedOK(index));
178c71b6c99SJagpal Singh Gill             taskData->state = "Completed";
179c71b6c99SJagpal Singh Gill             return task::completed;
180c71b6c99SJagpal Singh Gill         }
181c71b6c99SJagpal Singh Gill     }
182c71b6c99SJagpal Singh Gill     else if (iface == "xyz.openbmc_project.Software.ActivationProgress")
183c71b6c99SJagpal Singh Gill     {
184c71b6c99SJagpal Singh Gill         const uint8_t* progress = nullptr;
185c71b6c99SJagpal Singh Gill         for (const auto& property : values)
186c71b6c99SJagpal Singh Gill         {
187c71b6c99SJagpal Singh Gill             if (property.first == "Progress")
188c71b6c99SJagpal Singh Gill             {
189c71b6c99SJagpal Singh Gill                 progress = std::get_if<uint8_t>(&property.second);
190c71b6c99SJagpal Singh Gill                 if (progress == nullptr)
191c71b6c99SJagpal Singh Gill                 {
192c71b6c99SJagpal Singh Gill                     taskData->messages.emplace_back(messages::internalError());
193c71b6c99SJagpal Singh Gill                     return task::completed;
194c71b6c99SJagpal Singh Gill                 }
195c71b6c99SJagpal Singh Gill             }
196c71b6c99SJagpal Singh Gill         }
197c71b6c99SJagpal Singh Gill 
198c71b6c99SJagpal Singh Gill         if (progress == nullptr)
199c71b6c99SJagpal Singh Gill         {
200c71b6c99SJagpal Singh Gill             return !task::completed;
201c71b6c99SJagpal Singh Gill         }
202c71b6c99SJagpal Singh Gill         taskData->percentComplete = *progress;
203c71b6c99SJagpal Singh Gill         taskData->messages.emplace_back(
204c71b6c99SJagpal Singh Gill             messages::taskProgressChanged(index, *progress));
205c71b6c99SJagpal Singh Gill 
206c71b6c99SJagpal Singh Gill         // if we're getting status updates it's
207c71b6c99SJagpal Singh Gill         // still alive, update timer
208c71b6c99SJagpal Singh Gill         taskData->extendTimer(std::chrono::minutes(5));
209c71b6c99SJagpal Singh Gill     }
210c71b6c99SJagpal Singh Gill 
211c71b6c99SJagpal Singh Gill     // as firmware update often results in a
212c71b6c99SJagpal Singh Gill     // reboot, the task  may never "complete"
213c71b6c99SJagpal Singh Gill     // unless it is an error
214c71b6c99SJagpal Singh Gill 
215c71b6c99SJagpal Singh Gill     return !task::completed;
216c71b6c99SJagpal Singh Gill }
217c71b6c99SJagpal Singh Gill 
218c71b6c99SJagpal Singh Gill inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
219c71b6c99SJagpal Singh Gill                        task::Payload&& payload,
220c71b6c99SJagpal Singh Gill                        const sdbusplus::message::object_path& objPath)
221c71b6c99SJagpal Singh Gill {
222c71b6c99SJagpal Singh Gill     std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
223c71b6c99SJagpal Singh Gill         std::bind_front(handleCreateTask),
224c71b6c99SJagpal Singh Gill         "type='signal',interface='org.freedesktop.DBus.Properties',"
225c71b6c99SJagpal Singh Gill         "member='PropertiesChanged',path='" +
226c71b6c99SJagpal Singh Gill             objPath.str + "'");
227c71b6c99SJagpal Singh Gill     task->startTimer(std::chrono::minutes(5));
228c71b6c99SJagpal Singh Gill     task->populateResp(asyncResp->res);
229c71b6c99SJagpal Singh Gill     task->payload.emplace(std::move(payload));
230c71b6c99SJagpal Singh Gill }
231c71b6c99SJagpal Singh Gill 
2320554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
2330554c984SAndrew Geissler // then no asyncResp updates will occur
2344ff0f1f4SEd Tanous inline void
2358d1b46d7Szhanghch05     softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
23659d494eeSPatrick Williams                            sdbusplus::message_t& m, task::Payload&& payload)
23786adcd6dSAndrew Geissler {
23880f79a40SMichael Shen     dbus::utility::DBusInterfacesMap interfacesProperties;
23986adcd6dSAndrew Geissler 
24086adcd6dSAndrew Geissler     sdbusplus::message::object_path objPath;
24186adcd6dSAndrew Geissler 
24286adcd6dSAndrew Geissler     m.read(objPath, interfacesProperties);
24386adcd6dSAndrew Geissler 
24462598e31SEd Tanous     BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
245e3eb3d63SEd Tanous     for (const auto& interface : interfacesProperties)
24686adcd6dSAndrew Geissler     {
24762598e31SEd Tanous         BMCWEB_LOG_DEBUG("interface = {}", interface.first);
24886adcd6dSAndrew Geissler 
24986adcd6dSAndrew Geissler         if (interface.first == "xyz.openbmc_project.Software.Activation")
25086adcd6dSAndrew Geissler         {
25186adcd6dSAndrew Geissler             // Retrieve service and activate
2522b73119cSGeorge Liu             constexpr std::array<std::string_view, 1> interfaces = {
2532b73119cSGeorge Liu                 "xyz.openbmc_project.Software.Activation"};
2542b73119cSGeorge Liu             dbus::utility::getDbusObject(
2552b73119cSGeorge Liu                 objPath.str, interfaces,
256a3e65892SEd Tanous                 [objPath, asyncResp, payload(std::move(payload))](
2578b24275dSEd Tanous                     const boost::system::error_code& ec,
258a3e65892SEd Tanous                     const std::vector<
259a3e65892SEd Tanous                         std::pair<std::string, std::vector<std::string>>>&
260a3e65892SEd Tanous                         objInfo) mutable {
2618b24275dSEd Tanous                     if (ec)
26286adcd6dSAndrew Geissler                     {
26362598e31SEd Tanous                         BMCWEB_LOG_DEBUG("error_code = {}", ec);
26462598e31SEd Tanous                         BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
2650554c984SAndrew Geissler                         if (asyncResp)
2660554c984SAndrew Geissler                         {
26786adcd6dSAndrew Geissler                             messages::internalError(asyncResp->res);
2680554c984SAndrew Geissler                         }
26986adcd6dSAndrew Geissler                         cleanUp();
27086adcd6dSAndrew Geissler                         return;
27186adcd6dSAndrew Geissler                     }
27286adcd6dSAndrew Geissler                     // Ensure we only got one service back
27386adcd6dSAndrew Geissler                     if (objInfo.size() != 1)
27486adcd6dSAndrew Geissler                     {
275bd79bce8SPatrick Williams                         BMCWEB_LOG_ERROR("Invalid Object Size {}",
276bd79bce8SPatrick Williams                                          objInfo.size());
2770554c984SAndrew Geissler                         if (asyncResp)
2780554c984SAndrew Geissler                         {
27986adcd6dSAndrew Geissler                             messages::internalError(asyncResp->res);
2800554c984SAndrew Geissler                         }
28186adcd6dSAndrew Geissler                         cleanUp();
28286adcd6dSAndrew Geissler                         return;
28386adcd6dSAndrew Geissler                     }
28486adcd6dSAndrew Geissler                     // cancel timer only when
28586adcd6dSAndrew Geissler                     // xyz.openbmc_project.Software.Activation interface
28686adcd6dSAndrew Geissler                     // is added
28786adcd6dSAndrew Geissler                     fwAvailableTimer = nullptr;
28886adcd6dSAndrew Geissler 
28986adcd6dSAndrew Geissler                     activateImage(objPath.str, objInfo[0].first);
2900554c984SAndrew Geissler                     if (asyncResp)
2910554c984SAndrew Geissler                     {
292c71b6c99SJagpal Singh Gill                         createTask(asyncResp, std::move(payload), objPath);
2930554c984SAndrew Geissler                     }
29486adcd6dSAndrew Geissler                     fwUpdateInProgress = false;
2952b73119cSGeorge Liu                 });
29662bafc01SPatrick Williams 
29762bafc01SPatrick Williams             break;
29886adcd6dSAndrew Geissler         }
29986adcd6dSAndrew Geissler     }
30086adcd6dSAndrew Geissler }
30186adcd6dSAndrew Geissler 
3028549b951SMyung Bae inline void afterAvailbleTimerAsyncWait(
3038549b951SMyung Bae     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3048549b951SMyung Bae     const boost::system::error_code& ec)
3058549b951SMyung Bae {
3068549b951SMyung Bae     cleanUp();
3078549b951SMyung Bae     if (ec == boost::asio::error::operation_aborted)
3088549b951SMyung Bae     {
3098549b951SMyung Bae         // expected, we were canceled before the timer completed.
3108549b951SMyung Bae         return;
3118549b951SMyung Bae     }
3128549b951SMyung Bae     BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
3138549b951SMyung Bae     BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
3148549b951SMyung Bae     if (ec)
3158549b951SMyung Bae     {
3168549b951SMyung Bae         BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
3178549b951SMyung Bae         return;
3188549b951SMyung Bae     }
3198549b951SMyung Bae     if (asyncResp)
3208549b951SMyung Bae     {
3218549b951SMyung Bae         redfish::messages::internalError(asyncResp->res);
3228549b951SMyung Bae     }
3238549b951SMyung Bae }
3248549b951SMyung Bae 
3258549b951SMyung Bae inline void
3268549b951SMyung Bae     handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
32748fb20b9SEd Tanous                           const std::string& url, const std::string& type)
3288549b951SMyung Bae {
329c87294a6SEd Tanous     // NOLINTBEGIN(bugprone-branch-clone)
3308549b951SMyung Bae     if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
3318549b951SMyung Bae     {
33248fb20b9SEd Tanous         messages::missingOrMalformedPart(asyncResp->res);
3338549b951SMyung Bae     }
3348549b951SMyung Bae     else if (type ==
3358549b951SMyung Bae              "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
3368549b951SMyung Bae     {
33748fb20b9SEd Tanous         messages::missingOrMalformedPart(asyncResp->res);
3388549b951SMyung Bae     }
3398549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
3408549b951SMyung Bae     {
34148fb20b9SEd Tanous         messages::missingOrMalformedPart(asyncResp->res);
3428549b951SMyung Bae     }
3438549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
3448549b951SMyung Bae     {
345c87294a6SEd Tanous         messages::resourceAlreadyExists(asyncResp->res, "UpdateService",
346c87294a6SEd Tanous                                         "Version", "uploaded version");
3478549b951SMyung Bae     }
3488549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
3498549b951SMyung Bae     {
35048fb20b9SEd Tanous         messages::serviceTemporarilyUnavailable(asyncResp->res, url);
3518549b951SMyung Bae     }
3524034a652SMyung Bae     else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
3538549b951SMyung Bae     {
354c87294a6SEd Tanous         messages::internalError(asyncResp->res);
3554034a652SMyung Bae     }
3564034a652SMyung Bae     else if (type ==
3574034a652SMyung Bae              "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
3584034a652SMyung Bae     {
359c87294a6SEd Tanous         messages::internalError(asyncResp->res);
3604034a652SMyung Bae     }
3614034a652SMyung Bae     else if (type ==
3624034a652SMyung Bae              "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
3634034a652SMyung Bae     {
36448fb20b9SEd Tanous         messages::missingOrMalformedPart(asyncResp->res);
3654034a652SMyung Bae     }
3664034a652SMyung Bae     else if (type ==
3674034a652SMyung Bae                  "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
3684034a652SMyung Bae              type == "xyz.openbmc_project.Software.Version.Error.HostFile")
3694034a652SMyung Bae     {
3704034a652SMyung Bae         BMCWEB_LOG_ERROR("Software Image Error type={}", type);
37148fb20b9SEd Tanous         messages::internalError(asyncResp->res);
3728549b951SMyung Bae     }
3734034a652SMyung Bae     else
3744034a652SMyung Bae     {
3754034a652SMyung Bae         // Unrelated error types. Ignored
3764034a652SMyung Bae         BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
3774034a652SMyung Bae         return;
3784034a652SMyung Bae     }
379c87294a6SEd Tanous     // NOLINTEND(bugprone-branch-clone)
3804034a652SMyung Bae     // Clear the timer
3814034a652SMyung Bae     fwAvailableTimer = nullptr;
3828549b951SMyung Bae }
3838549b951SMyung Bae 
3848549b951SMyung Bae inline void
3858549b951SMyung Bae     afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3868549b951SMyung Bae                             const std::string& url, sdbusplus::message_t& m)
3878549b951SMyung Bae {
38880f79a40SMichael Shen     dbus::utility::DBusInterfacesMap interfacesProperties;
3898549b951SMyung Bae     sdbusplus::message::object_path objPath;
3908549b951SMyung Bae     m.read(objPath, interfacesProperties);
3918549b951SMyung Bae     BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
3928549b951SMyung Bae     for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
3938549b951SMyung Bae              interface : interfacesProperties)
3948549b951SMyung Bae     {
3958549b951SMyung Bae         if (interface.first == "xyz.openbmc_project.Logging.Entry")
3968549b951SMyung Bae         {
3978549b951SMyung Bae             for (const std::pair<std::string, dbus::utility::DbusVariantType>&
3988549b951SMyung Bae                      value : interface.second)
3998549b951SMyung Bae             {
4008549b951SMyung Bae                 if (value.first != "Message")
4018549b951SMyung Bae                 {
4028549b951SMyung Bae                     continue;
4038549b951SMyung Bae                 }
4048549b951SMyung Bae                 const std::string* type =
4058549b951SMyung Bae                     std::get_if<std::string>(&value.second);
4068549b951SMyung Bae                 if (type == nullptr)
4078549b951SMyung Bae                 {
4088549b951SMyung Bae                     // if this was our message, timeout will cover it
4098549b951SMyung Bae                     return;
4108549b951SMyung Bae                 }
4118549b951SMyung Bae                 handleUpdateErrorType(asyncResp, url, *type);
4128549b951SMyung Bae             }
4138549b951SMyung Bae         }
4148549b951SMyung Bae     }
4158549b951SMyung Bae }
4168549b951SMyung Bae 
4170554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
4180554c984SAndrew Geissler // then no asyncResp updates will occur
419f5139334SEd Tanous inline void monitorForSoftwareAvailable(
4208d1b46d7Szhanghch05     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
4218d1b46d7Szhanghch05     const crow::Request& req, const std::string& url,
4225d138943SGunnar Mills     int timeoutTimeSeconds = 25)
42386adcd6dSAndrew Geissler {
42486adcd6dSAndrew Geissler     // Only allow one FW update at a time
425e05aec50SEd Tanous     if (fwUpdateInProgress)
42686adcd6dSAndrew Geissler     {
4270554c984SAndrew Geissler         if (asyncResp)
4280554c984SAndrew Geissler         {
42986adcd6dSAndrew Geissler             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
4300554c984SAndrew Geissler         }
43186adcd6dSAndrew Geissler         return;
43286adcd6dSAndrew Geissler     }
43386adcd6dSAndrew Geissler 
4348e8245dbSEd Tanous     if (req.ioService == nullptr)
4358e8245dbSEd Tanous     {
4368e8245dbSEd Tanous         messages::internalError(asyncResp->res);
4378e8245dbSEd Tanous         return;
4388e8245dbSEd Tanous     }
4398e8245dbSEd Tanous 
4400554c984SAndrew Geissler     fwAvailableTimer =
441271584abSEd Tanous         std::make_unique<boost::asio::steady_timer>(*req.ioService);
44286adcd6dSAndrew Geissler 
443271584abSEd Tanous     fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
44486adcd6dSAndrew Geissler 
44586adcd6dSAndrew Geissler     fwAvailableTimer->async_wait(
4468549b951SMyung Bae         std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
4478549b951SMyung Bae 
448a3e65892SEd Tanous     task::Payload payload(req);
44959d494eeSPatrick Williams     auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
45062598e31SEd Tanous         BMCWEB_LOG_DEBUG("Match fired");
451a3e65892SEd Tanous         softwareInterfaceAdded(asyncResp, m, std::move(payload));
45286adcd6dSAndrew Geissler     };
45386adcd6dSAndrew Geissler 
45486adcd6dSAndrew Geissler     fwUpdateInProgress = true;
45586adcd6dSAndrew Geissler 
45659d494eeSPatrick Williams     fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
45786adcd6dSAndrew Geissler         *crow::connections::systemBus,
45886adcd6dSAndrew Geissler         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
45986adcd6dSAndrew Geissler         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
46086adcd6dSAndrew Geissler         callback);
4614cde5d90SJames Feist 
46259d494eeSPatrick Williams     fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
4634cde5d90SJames Feist         *crow::connections::systemBus,
464e1cc4828SBrian Ma         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
465e1cc4828SBrian Ma         "member='InterfacesAdded',"
466e1cc4828SBrian Ma         "path='/xyz/openbmc_project/logging'",
4678549b951SMyung Bae         std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
46886adcd6dSAndrew Geissler }
469729dae72SJennifer Lee 
470bd79bce8SPatrick Williams inline std::optional<boost::urls::url> parseSimpleUpdateUrl(
471bd79bce8SPatrick Williams     std::string imageURI, std::optional<std::string> transferProtocol,
472f86bcc87SEd Tanous     crow::Response& res)
473f86bcc87SEd Tanous {
474f86bcc87SEd Tanous     if (imageURI.find("://") == std::string::npos)
475f86bcc87SEd Tanous     {
476f86bcc87SEd Tanous         if (imageURI.starts_with("/"))
477f86bcc87SEd Tanous         {
478f86bcc87SEd Tanous             messages::actionParameterValueTypeError(
479f86bcc87SEd Tanous                 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
480f86bcc87SEd Tanous             return std::nullopt;
481f86bcc87SEd Tanous         }
482f86bcc87SEd Tanous         if (!transferProtocol)
483f86bcc87SEd Tanous         {
484f86bcc87SEd Tanous             messages::actionParameterValueTypeError(
485f86bcc87SEd Tanous                 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
486f86bcc87SEd Tanous             return std::nullopt;
487f86bcc87SEd Tanous         }
4886a37140aSEd Tanous         // OpenBMC currently only supports HTTPS
4896a37140aSEd Tanous         if (*transferProtocol == "HTTPS")
490e5cf777eSEd Tanous         {
491e5cf777eSEd Tanous             imageURI = "https://" + imageURI;
492e5cf777eSEd Tanous         }
493757178a5SEd Tanous         else
494f86bcc87SEd Tanous         {
495f86bcc87SEd Tanous             messages::actionParameterNotSupported(res, "TransferProtocol",
496f86bcc87SEd Tanous                                                   *transferProtocol);
497f86bcc87SEd Tanous             BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
498f86bcc87SEd Tanous                              *transferProtocol);
499f86bcc87SEd Tanous             return std::nullopt;
500f86bcc87SEd Tanous         }
501f86bcc87SEd Tanous     }
502f86bcc87SEd Tanous 
503f86bcc87SEd Tanous     boost::system::result<boost::urls::url> url =
504f86bcc87SEd Tanous         boost::urls::parse_absolute_uri(imageURI);
505f86bcc87SEd Tanous     if (!url)
506f86bcc87SEd Tanous     {
507f86bcc87SEd Tanous         messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
508f86bcc87SEd Tanous                                                 "UpdateService.SimpleUpdate");
509f86bcc87SEd Tanous 
510f86bcc87SEd Tanous         return std::nullopt;
511f86bcc87SEd Tanous     }
512f86bcc87SEd Tanous     url->normalize();
513f86bcc87SEd Tanous 
514757178a5SEd Tanous     if (url->scheme() == "tftp")
515757178a5SEd Tanous     {
516757178a5SEd Tanous         if (url->encoded_path().size() < 2)
517757178a5SEd Tanous         {
518757178a5SEd Tanous             messages::actionParameterNotSupported(res, "ImageURI",
519757178a5SEd Tanous                                                   url->buffer());
520757178a5SEd Tanous             return std::nullopt;
521757178a5SEd Tanous         }
522757178a5SEd Tanous     }
523e5cf777eSEd Tanous     else if (url->scheme() == "https")
524e5cf777eSEd Tanous     {
525e5cf777eSEd Tanous         // Empty paths default to "/"
526e5cf777eSEd Tanous         if (url->encoded_path().empty())
527e5cf777eSEd Tanous         {
528e5cf777eSEd Tanous             url->set_encoded_path("/");
529e5cf777eSEd Tanous         }
530e5cf777eSEd Tanous     }
531757178a5SEd Tanous     else
532f86bcc87SEd Tanous     {
533f86bcc87SEd Tanous         messages::actionParameterNotSupported(res, "ImageURI", imageURI);
534f86bcc87SEd Tanous         return std::nullopt;
535f86bcc87SEd Tanous     }
536757178a5SEd Tanous 
537757178a5SEd Tanous     if (url->encoded_path().empty())
538f86bcc87SEd Tanous     {
539757178a5SEd Tanous         messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
540757178a5SEd Tanous                                                 "UpdateService.SimpleUpdate");
541f86bcc87SEd Tanous         return std::nullopt;
542f86bcc87SEd Tanous     }
543757178a5SEd Tanous 
544757178a5SEd Tanous     return *url;
545f86bcc87SEd Tanous }
546f86bcc87SEd Tanous 
547e5cf777eSEd Tanous inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
548e5cf777eSEd Tanous                           const boost::urls::url_view_base& url)
549e5cf777eSEd Tanous {
550e5cf777eSEd Tanous     messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
551e5cf777eSEd Tanous                                           url.buffer());
552e5cf777eSEd Tanous }
553e5cf777eSEd Tanous 
554f5139334SEd Tanous inline void handleUpdateServiceSimpleUpdateAction(
555f5139334SEd Tanous     crow::App& app, const crow::Request& req,
556f5139334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
5570554c984SAndrew Geissler {
5583ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
55945ca1b86SEd Tanous     {
56045ca1b86SEd Tanous         return;
56145ca1b86SEd Tanous     }
56245ca1b86SEd Tanous 
5630554c984SAndrew Geissler     std::optional<std::string> transferProtocol;
5640554c984SAndrew Geissler     std::string imageURI;
5650554c984SAndrew Geissler 
56662598e31SEd Tanous     BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
5670554c984SAndrew Geissler 
5680554c984SAndrew Geissler     // User can pass in both TransferProtocol and ImageURI parameters or
5694e0453b1SGunnar Mills     // they can pass in just the ImageURI with the transfer protocol
5704e0453b1SGunnar Mills     // embedded within it.
5710554c984SAndrew Geissler     // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
5720554c984SAndrew Geissler     // 2) ImageURI:tftp://1.1.1.1/myfile.bin
5730554c984SAndrew Geissler 
574afc474aeSMyung Bae     if (!json_util::readJsonAction( //
575afc474aeSMyung Bae             req, asyncResp->res, //
576afc474aeSMyung Bae             "ImageURI", imageURI, //
577afc474aeSMyung Bae             "TransferProtocol", transferProtocol //
578afc474aeSMyung Bae             ))
5790554c984SAndrew Geissler     {
58062598e31SEd Tanous         BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
5810554c984SAndrew Geissler         return;
5820554c984SAndrew Geissler     }
583f5139334SEd Tanous 
584757178a5SEd Tanous     std::optional<boost::urls::url> url =
585757178a5SEd Tanous         parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res);
586757178a5SEd Tanous     if (!url)
5870554c984SAndrew Geissler     {
5880554c984SAndrew Geissler         return;
5890554c984SAndrew Geissler     }
5904e338b23SJagpal Singh Gill     if (url->scheme() == "https")
591e5cf777eSEd Tanous     {
592e5cf777eSEd Tanous         doHttpsUpdate(asyncResp, *url);
593e5cf777eSEd Tanous     }
594757178a5SEd Tanous     else
595757178a5SEd Tanous     {
596757178a5SEd Tanous         messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
597757178a5SEd Tanous                                               url->buffer());
598757178a5SEd Tanous         return;
599757178a5SEd Tanous     }
6000554c984SAndrew Geissler 
60162598e31SEd Tanous     BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
602729dae72SJennifer Lee }
603729dae72SJennifer Lee 
6040ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body)
6050ed80c8cSGeorge Liu {
6062c6ffdb0SEd Tanous     std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
6072c6ffdb0SEd Tanous 
60862598e31SEd Tanous     BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
6090ed80c8cSGeorge Liu     std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
6100ed80c8cSGeorge Liu                                     std::ofstream::trunc);
6110ed80c8cSGeorge Liu     // set the permission of the file to 640
612bd79bce8SPatrick Williams     std::filesystem::perms permission =
613bd79bce8SPatrick Williams         std::filesystem::perms::owner_read | std::filesystem::perms::group_read;
6140ed80c8cSGeorge Liu     std::filesystem::permissions(filepath, permission);
6150ed80c8cSGeorge Liu     out << body;
6160ed80c8cSGeorge Liu 
6170ed80c8cSGeorge Liu     if (out.bad())
6180ed80c8cSGeorge Liu     {
6190ed80c8cSGeorge Liu         messages::internalError(res);
6200ed80c8cSGeorge Liu         cleanUp();
6210ed80c8cSGeorge Liu     }
6220ed80c8cSGeorge Liu }
6230ed80c8cSGeorge Liu 
624de0c960cSJagpal Singh Gill // Convert the Request Apply Time to the D-Bus value
625de0c960cSJagpal Singh Gill inline bool convertApplyTime(crow::Response& res, const std::string& applyTime,
626de0c960cSJagpal Singh Gill                              std::string& applyTimeNewVal)
627de0c960cSJagpal Singh Gill {
628de0c960cSJagpal Singh Gill     if (applyTime == "Immediate")
629de0c960cSJagpal Singh Gill     {
630de0c960cSJagpal Singh Gill         applyTimeNewVal =
631049079f6SJagpal Singh Gill             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
632de0c960cSJagpal Singh Gill     }
633de0c960cSJagpal Singh Gill     else if (applyTime == "OnReset")
634de0c960cSJagpal Singh Gill     {
635de0c960cSJagpal Singh Gill         applyTimeNewVal =
636049079f6SJagpal Singh Gill             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
637de0c960cSJagpal Singh Gill     }
638de0c960cSJagpal Singh Gill     else
639de0c960cSJagpal Singh Gill     {
640de0c960cSJagpal Singh Gill         BMCWEB_LOG_WARNING(
641de0c960cSJagpal Singh Gill             "ApplyTime value {} is not in the list of acceptable values",
642de0c960cSJagpal Singh Gill             applyTime);
643de0c960cSJagpal Singh Gill         messages::propertyValueNotInList(res, applyTime, "ApplyTime");
644de0c960cSJagpal Singh Gill         return false;
645de0c960cSJagpal Singh Gill     }
646de0c960cSJagpal Singh Gill     return true;
647de0c960cSJagpal Singh Gill }
648de0c960cSJagpal Singh Gill 
6490ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
6500ed80c8cSGeorge Liu                          const std::string& applyTime)
6510ed80c8cSGeorge Liu {
6520ed80c8cSGeorge Liu     std::string applyTimeNewVal;
653049079f6SJagpal Singh Gill     if (!convertApplyTime(asyncResp->res, applyTime, applyTimeNewVal))
6540ed80c8cSGeorge Liu     {
6550ed80c8cSGeorge Liu         return;
6560ed80c8cSGeorge Liu     }
6570ed80c8cSGeorge Liu 
658e93abac6SGinu George     setDbusProperty(asyncResp, "ApplyTime", "xyz.openbmc_project.Settings",
659d02aad39SEd Tanous                     sdbusplus::message::object_path(
660d02aad39SEd Tanous                         "/xyz/openbmc_project/software/apply_time"),
661d02aad39SEd Tanous                     "xyz.openbmc_project.Software.ApplyTime",
662e93abac6SGinu George                     "RequestedApplyTime", applyTimeNewVal);
6630ed80c8cSGeorge Liu }
6640ed80c8cSGeorge Liu 
665ef93eab3SJagpal Singh Gill struct MultiPartUpdateParameters
6660ed80c8cSGeorge Liu {
667ef93eab3SJagpal Singh Gill     std::optional<std::string> applyTime;
668ef93eab3SJagpal Singh Gill     std::string uploadData;
669de0c960cSJagpal Singh Gill     std::vector<std::string> targets;
670ef93eab3SJagpal Singh Gill };
671ef93eab3SJagpal Singh Gill 
672de0c960cSJagpal Singh Gill inline std::optional<std::string>
673de0c960cSJagpal Singh Gill     processUrl(boost::system::result<boost::urls::url_view>& url)
674de0c960cSJagpal Singh Gill {
675de0c960cSJagpal Singh Gill     if (!url)
676de0c960cSJagpal Singh Gill     {
677de0c960cSJagpal Singh Gill         return std::nullopt;
678de0c960cSJagpal Singh Gill     }
679de0c960cSJagpal Singh Gill     if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers",
680de0c960cSJagpal Singh Gill                                        BMCWEB_REDFISH_MANAGER_URI_NAME))
681de0c960cSJagpal Singh Gill     {
682de0c960cSJagpal Singh Gill         return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME));
683de0c960cSJagpal Singh Gill     }
684de0c960cSJagpal Singh Gill     if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
685de0c960cSJagpal Singh Gill     {
686de0c960cSJagpal Singh Gill         return std::nullopt;
687de0c960cSJagpal Singh Gill     }
688de0c960cSJagpal Singh Gill     std::string firmwareId;
689de0c960cSJagpal Singh Gill     if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService",
690de0c960cSJagpal Singh Gill                                         "FirmwareInventory",
691de0c960cSJagpal Singh Gill                                         std::ref(firmwareId)))
692de0c960cSJagpal Singh Gill     {
693de0c960cSJagpal Singh Gill         return std::nullopt;
694de0c960cSJagpal Singh Gill     }
695de0c960cSJagpal Singh Gill 
696de0c960cSJagpal Singh Gill     return std::make_optional(firmwareId);
697de0c960cSJagpal Singh Gill }
698de0c960cSJagpal Singh Gill 
699ef93eab3SJagpal Singh Gill inline std::optional<MultiPartUpdateParameters>
700ef93eab3SJagpal Singh Gill     extractMultipartUpdateParameters(
701ef93eab3SJagpal Singh Gill         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
702ef93eab3SJagpal Singh Gill         MultipartParser parser)
703ef93eab3SJagpal Singh Gill {
704ef93eab3SJagpal Singh Gill     MultiPartUpdateParameters multiRet;
705ef93eab3SJagpal Singh Gill     for (FormPart& formpart : parser.mime_fields)
7060ed80c8cSGeorge Liu     {
7070ed80c8cSGeorge Liu         boost::beast::http::fields::const_iterator it =
7080ed80c8cSGeorge Liu             formpart.fields.find("Content-Disposition");
7090ed80c8cSGeorge Liu         if (it == formpart.fields.end())
7100ed80c8cSGeorge Liu         {
71162598e31SEd Tanous             BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
712ef93eab3SJagpal Singh Gill             return std::nullopt;
7130ed80c8cSGeorge Liu         }
71462598e31SEd Tanous         BMCWEB_LOG_INFO("Parsing value {}", it->value());
7150ed80c8cSGeorge Liu 
7160ed80c8cSGeorge Liu         // The construction parameters of param_list must start with `;`
7170ed80c8cSGeorge Liu         size_t index = it->value().find(';');
7180ed80c8cSGeorge Liu         if (index == std::string::npos)
7190ed80c8cSGeorge Liu         {
7200ed80c8cSGeorge Liu             continue;
7210ed80c8cSGeorge Liu         }
7220ed80c8cSGeorge Liu 
72389492a15SPatrick Williams         for (const auto& param :
7240ed80c8cSGeorge Liu              boost::beast::http::param_list{it->value().substr(index)})
7250ed80c8cSGeorge Liu         {
7260ed80c8cSGeorge Liu             if (param.first != "name" || param.second.empty())
7270ed80c8cSGeorge Liu             {
7280ed80c8cSGeorge Liu                 continue;
7290ed80c8cSGeorge Liu             }
7300ed80c8cSGeorge Liu 
7310ed80c8cSGeorge Liu             if (param.second == "UpdateParameters")
7320ed80c8cSGeorge Liu             {
733ef93eab3SJagpal Singh Gill                 std::vector<std::string> tempTargets;
734bd79bce8SPatrick Williams                 nlohmann::json content =
735bd79bce8SPatrick Williams                     nlohmann::json::parse(formpart.content, nullptr, false);
736ac1e1246SEd Tanous                 if (content.is_discarded())
737ac1e1246SEd Tanous                 {
738ac1e1246SEd Tanous                     return std::nullopt;
739ac1e1246SEd Tanous                 }
7407cb59f65SEd Tanous                 nlohmann::json::object_t* obj =
7417cb59f65SEd Tanous                     content.get_ptr<nlohmann::json::object_t*>();
7427cb59f65SEd Tanous                 if (obj == nullptr)
7437cb59f65SEd Tanous                 {
744ef93eab3SJagpal Singh Gill                     messages::propertyValueTypeError(
745ef93eab3SJagpal Singh Gill                         asyncResp->res, formpart.content, "UpdateParameters");
746ef93eab3SJagpal Singh Gill                     return std::nullopt;
7477cb59f65SEd Tanous                 }
7487cb59f65SEd Tanous 
749afc474aeSMyung Bae                 if (!json_util::readJsonObject( //
750afc474aeSMyung Bae                         *obj, asyncResp->res, //
751afc474aeSMyung Bae                         "@Redfish.OperationApplyTime", multiRet.applyTime, //
752afc474aeSMyung Bae                         "Targets", tempTargets //
753afc474aeSMyung Bae                         ))
7540ed80c8cSGeorge Liu                 {
755ef93eab3SJagpal Singh Gill                     return std::nullopt;
7560ed80c8cSGeorge Liu                 }
757ef93eab3SJagpal Singh Gill 
758ef93eab3SJagpal Singh Gill                 for (size_t urlIndex = 0; urlIndex < tempTargets.size();
759ef93eab3SJagpal Singh Gill                      urlIndex++)
7600ed80c8cSGeorge Liu                 {
761ef93eab3SJagpal Singh Gill                     const std::string& target = tempTargets[urlIndex];
762ef93eab3SJagpal Singh Gill                     boost::system::result<boost::urls::url_view> url =
763ef93eab3SJagpal Singh Gill                         boost::urls::parse_origin_form(target);
764de0c960cSJagpal Singh Gill                     auto res = processUrl(url);
765de0c960cSJagpal Singh Gill                     if (!res.has_value())
7660ed80c8cSGeorge Liu                     {
767ef93eab3SJagpal Singh Gill                         messages::propertyValueFormatError(
768ef93eab3SJagpal Singh Gill                             asyncResp->res, target,
769ef93eab3SJagpal Singh Gill                             std::format("Targets/{}", urlIndex));
770ef93eab3SJagpal Singh Gill                         return std::nullopt;
7710ed80c8cSGeorge Liu                     }
772de0c960cSJagpal Singh Gill                     multiRet.targets.emplace_back(res.value());
773ef93eab3SJagpal Singh Gill                 }
774ef93eab3SJagpal Singh Gill                 if (multiRet.targets.size() != 1)
775ef93eab3SJagpal Singh Gill                 {
776ef93eab3SJagpal Singh Gill                     messages::propertyValueFormatError(
777ef93eab3SJagpal Singh Gill                         asyncResp->res, multiRet.targets, "Targets");
778ef93eab3SJagpal Singh Gill                     return std::nullopt;
779ef93eab3SJagpal Singh Gill                 }
7800ed80c8cSGeorge Liu             }
7810ed80c8cSGeorge Liu             else if (param.second == "UpdateFile")
7820ed80c8cSGeorge Liu             {
783ef93eab3SJagpal Singh Gill                 multiRet.uploadData = std::move(formpart.content);
7840ed80c8cSGeorge Liu             }
7850ed80c8cSGeorge Liu         }
7860ed80c8cSGeorge Liu     }
7870ed80c8cSGeorge Liu 
788ef93eab3SJagpal Singh Gill     if (multiRet.uploadData.empty())
7890ed80c8cSGeorge Liu     {
79062598e31SEd Tanous         BMCWEB_LOG_ERROR("Upload data is NULL");
7910ed80c8cSGeorge Liu         messages::propertyMissing(asyncResp->res, "UpdateFile");
792ef93eab3SJagpal Singh Gill         return std::nullopt;
7930ed80c8cSGeorge Liu     }
794ef93eab3SJagpal Singh Gill     if (multiRet.targets.empty())
7950ed80c8cSGeorge Liu     {
796ef93eab3SJagpal Singh Gill         messages::propertyMissing(asyncResp->res, "Targets");
797ef93eab3SJagpal Singh Gill         return std::nullopt;
798ef93eab3SJagpal Singh Gill     }
799ef93eab3SJagpal Singh Gill     return multiRet;
8000ed80c8cSGeorge Liu }
8010ed80c8cSGeorge Liu 
802bd79bce8SPatrick Williams inline void handleStartUpdate(
803bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
804bd79bce8SPatrick Williams     const std::string& objectPath, const boost::system::error_code& ec,
805de0c960cSJagpal Singh Gill     const sdbusplus::message::object_path& retPath)
806de0c960cSJagpal Singh Gill {
807de0c960cSJagpal Singh Gill     if (ec)
808de0c960cSJagpal Singh Gill     {
809de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("error_code = {}", ec);
810de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("error msg = {}", ec.message());
811de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
812de0c960cSJagpal Singh Gill         return;
813de0c960cSJagpal Singh Gill     }
814de0c960cSJagpal Singh Gill 
815587090cdSJagpal Singh Gill     BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}",
816587090cdSJagpal Singh Gill                     objectPath, retPath.str);
817587090cdSJagpal Singh Gill     createTask(asyncResp, std::move(payload), retPath);
818de0c960cSJagpal Singh Gill }
819de0c960cSJagpal Singh Gill 
820bd79bce8SPatrick Williams inline void startUpdate(
821bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
822bd79bce8SPatrick Williams     const MemoryFileDescriptor& memfd, const std::string& applyTime,
823bd79bce8SPatrick Williams     const std::string& objectPath, const std::string& serviceName)
824de0c960cSJagpal Singh Gill {
825de0c960cSJagpal Singh Gill     crow::connections::systemBus->async_method_call(
826de0c960cSJagpal Singh Gill         [asyncResp, payload = std::move(payload),
827de0c960cSJagpal Singh Gill          objectPath](const boost::system::error_code& ec1,
828de0c960cSJagpal Singh Gill                      const sdbusplus::message::object_path& retPath) mutable {
829de0c960cSJagpal Singh Gill             handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1,
830de0c960cSJagpal Singh Gill                               retPath);
831de0c960cSJagpal Singh Gill         },
832de0c960cSJagpal Singh Gill         serviceName, objectPath, "xyz.openbmc_project.Software.Update",
833de0c960cSJagpal Singh Gill         "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime);
834de0c960cSJagpal Singh Gill }
835de0c960cSJagpal Singh Gill 
83608f61d53SJagpal Singh Gill inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
83708f61d53SJagpal Singh Gill                       task::Payload payload, const MemoryFileDescriptor& memfd,
83808f61d53SJagpal Singh Gill                       const std::string& applyTime, const std::string& target,
839de0c960cSJagpal Singh Gill                       const boost::system::error_code& ec,
840de0c960cSJagpal Singh Gill                       const dbus::utility::MapperGetSubTreeResponse& subtree)
841de0c960cSJagpal Singh Gill {
84208f61d53SJagpal Singh Gill     using SwInfoMap = std::unordered_map<
84308f61d53SJagpal Singh Gill         std::string, std::pair<sdbusplus::message::object_path, std::string>>;
844de0c960cSJagpal Singh Gill     SwInfoMap swInfoMap;
845de0c960cSJagpal Singh Gill 
846de0c960cSJagpal Singh Gill     if (ec)
847de0c960cSJagpal Singh Gill     {
848de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("error_code = {}", ec);
849de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("error msg = {}", ec.message());
850de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
851de0c960cSJagpal Singh Gill         return;
852de0c960cSJagpal Singh Gill     }
853de0c960cSJagpal Singh Gill     BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size());
854de0c960cSJagpal Singh Gill 
85508f61d53SJagpal Singh Gill     for (const auto& entry : subtree)
856de0c960cSJagpal Singh Gill     {
85708f61d53SJagpal Singh Gill         sdbusplus::message::object_path path(entry.first);
858de0c960cSJagpal Singh Gill         std::string swId = path.filename();
85908f61d53SJagpal Singh Gill         swInfoMap.emplace(swId, make_pair(path, entry.second[0].first));
860de0c960cSJagpal Singh Gill     }
861de0c960cSJagpal Singh Gill 
862de0c960cSJagpal Singh Gill     auto swEntry = swInfoMap.find(target);
863de0c960cSJagpal Singh Gill     if (swEntry == swInfoMap.end())
864de0c960cSJagpal Singh Gill     {
865de0c960cSJagpal Singh Gill         BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target);
866de0c960cSJagpal Singh Gill         messages::propertyValueFormatError(asyncResp->res, target, "Targets");
867de0c960cSJagpal Singh Gill         return;
868de0c960cSJagpal Singh Gill     }
869de0c960cSJagpal Singh Gill 
87008f61d53SJagpal Singh Gill     BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}",
87108f61d53SJagpal Singh Gill                      swEntry->second.first.str, swEntry->second.second);
872de0c960cSJagpal Singh Gill 
87308f61d53SJagpal Singh Gill     startUpdate(asyncResp, std::move(payload), memfd, applyTime,
87408f61d53SJagpal Singh Gill                 swEntry->second.first.str, swEntry->second.second);
87508f61d53SJagpal Singh Gill }
87608f61d53SJagpal Singh Gill 
877bd79bce8SPatrick Williams inline void handleBMCUpdate(
878bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
879bd79bce8SPatrick Williams     const MemoryFileDescriptor& memfd, const std::string& applyTime,
88008f61d53SJagpal Singh Gill     const boost::system::error_code& ec,
88108f61d53SJagpal Singh Gill     const dbus::utility::MapperEndPoints& functionalSoftware)
88208f61d53SJagpal Singh Gill {
88308f61d53SJagpal Singh Gill     if (ec)
88408f61d53SJagpal Singh Gill     {
88508f61d53SJagpal Singh Gill         BMCWEB_LOG_ERROR("error_code = {}", ec);
88608f61d53SJagpal Singh Gill         BMCWEB_LOG_ERROR("error msg = {}", ec.message());
88708f61d53SJagpal Singh Gill         messages::internalError(asyncResp->res);
88808f61d53SJagpal Singh Gill         return;
88908f61d53SJagpal Singh Gill     }
89008f61d53SJagpal Singh Gill     if (functionalSoftware.size() != 1)
89108f61d53SJagpal Singh Gill     {
89208f61d53SJagpal Singh Gill         BMCWEB_LOG_ERROR("Found {} functional software endpoints",
89308f61d53SJagpal Singh Gill                          functionalSoftware.size());
89408f61d53SJagpal Singh Gill         messages::internalError(asyncResp->res);
89508f61d53SJagpal Singh Gill         return;
89608f61d53SJagpal Singh Gill     }
89708f61d53SJagpal Singh Gill 
89808f61d53SJagpal Singh Gill     startUpdate(asyncResp, std::move(payload), memfd, applyTime,
89908f61d53SJagpal Singh Gill                 functionalSoftware[0], "xyz.openbmc_project.Software.Manager");
900de0c960cSJagpal Singh Gill }
901de0c960cSJagpal Singh Gill 
902bd79bce8SPatrick Williams inline void processUpdateRequest(
903bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
9049dae4deeSJagpal Singh Gill     task::Payload&& payload, std::string_view body,
905bd79bce8SPatrick Williams     const std::string& applyTime, std::vector<std::string>& targets)
906de0c960cSJagpal Singh Gill {
907de0c960cSJagpal Singh Gill     MemoryFileDescriptor memfd("update-image");
908de0c960cSJagpal Singh Gill     if (memfd.fd == -1)
909de0c960cSJagpal Singh Gill     {
910de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("Failed to create image memfd");
911de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
912de0c960cSJagpal Singh Gill         return;
913de0c960cSJagpal Singh Gill     }
914de0c960cSJagpal Singh Gill     if (write(memfd.fd, body.data(), body.length()) !=
915de0c960cSJagpal Singh Gill         static_cast<ssize_t>(body.length()))
916de0c960cSJagpal Singh Gill     {
917de0c960cSJagpal Singh Gill         BMCWEB_LOG_ERROR("Failed to write to image memfd");
918de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
919de0c960cSJagpal Singh Gill         return;
920de0c960cSJagpal Singh Gill     }
921de0c960cSJagpal Singh Gill     if (!memfd.rewind())
922de0c960cSJagpal Singh Gill     {
923de0c960cSJagpal Singh Gill         messages::internalError(asyncResp->res);
924de0c960cSJagpal Singh Gill         return;
925de0c960cSJagpal Singh Gill     }
926de0c960cSJagpal Singh Gill 
927de0c960cSJagpal Singh Gill     if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME)
928de0c960cSJagpal Singh Gill     {
92908f61d53SJagpal Singh Gill         dbus::utility::getAssociationEndPoints(
93089449bbeSJagpal Singh Gill             "/xyz/openbmc_project/software/bmc/updateable",
93108f61d53SJagpal Singh Gill             [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
93208f61d53SJagpal Singh Gill              applyTime](
93308f61d53SJagpal Singh Gill                 const boost::system::error_code& ec,
93408f61d53SJagpal Singh Gill                 const dbus::utility::MapperEndPoints& objectPaths) mutable {
935bd79bce8SPatrick Williams                 handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime,
936bd79bce8SPatrick Williams                                 ec, objectPaths);
93708f61d53SJagpal Singh Gill             });
938de0c960cSJagpal Singh Gill     }
939de0c960cSJagpal Singh Gill     else
940de0c960cSJagpal Singh Gill     {
941de0c960cSJagpal Singh Gill         constexpr std::array<std::string_view, 1> interfaces = {
942de0c960cSJagpal Singh Gill             "xyz.openbmc_project.Software.Version"};
94308f61d53SJagpal Singh Gill         dbus::utility::getSubTree(
944de0c960cSJagpal Singh Gill             "/xyz/openbmc_project/software", 1, interfaces,
945de0c960cSJagpal Singh Gill             [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
94608f61d53SJagpal Singh Gill              applyTime, targets](const boost::system::error_code& ec,
94708f61d53SJagpal Singh Gill                                  const dbus::utility::MapperGetSubTreeResponse&
948de0c960cSJagpal Singh Gill                                      subtree) mutable {
94908f61d53SJagpal Singh Gill                 getSwInfo(asyncResp, std::move(payload), memfd, applyTime,
95008f61d53SJagpal Singh Gill                           targets[0], ec, subtree);
951de0c960cSJagpal Singh Gill             });
952de0c960cSJagpal Singh Gill     }
953de0c960cSJagpal Singh Gill }
954de0c960cSJagpal Singh Gill 
955de0c960cSJagpal Singh Gill inline void
956ef93eab3SJagpal Singh Gill     updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
957ef93eab3SJagpal Singh Gill                            const crow::Request& req, MultipartParser&& parser)
958ef93eab3SJagpal Singh Gill {
959ef93eab3SJagpal Singh Gill     std::optional<MultiPartUpdateParameters> multipart =
960ef93eab3SJagpal Singh Gill         extractMultipartUpdateParameters(asyncResp, std::move(parser));
961ef93eab3SJagpal Singh Gill     if (!multipart)
962ef93eab3SJagpal Singh Gill     {
963ef93eab3SJagpal Singh Gill         return;
964ef93eab3SJagpal Singh Gill     }
965ef93eab3SJagpal Singh Gill     if (!multipart->applyTime)
966ef93eab3SJagpal Singh Gill     {
967ef93eab3SJagpal Singh Gill         multipart->applyTime = "OnReset";
968ef93eab3SJagpal Singh Gill     }
969ef93eab3SJagpal Singh Gill 
970de0c960cSJagpal Singh Gill     if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
971de0c960cSJagpal Singh Gill     {
9729dae4deeSJagpal Singh Gill         std::string applyTimeNewVal;
9739dae4deeSJagpal Singh Gill         if (!convertApplyTime(asyncResp->res, *multipart->applyTime,
9749dae4deeSJagpal Singh Gill                               applyTimeNewVal))
9759dae4deeSJagpal Singh Gill         {
9769dae4deeSJagpal Singh Gill             return;
9779dae4deeSJagpal Singh Gill         }
9789dae4deeSJagpal Singh Gill         task::Payload payload(req);
9799dae4deeSJagpal Singh Gill 
9809dae4deeSJagpal Singh Gill         processUpdateRequest(asyncResp, std::move(payload),
9819dae4deeSJagpal Singh Gill                              multipart->uploadData, applyTimeNewVal,
9829dae4deeSJagpal Singh Gill                              multipart->targets);
983de0c960cSJagpal Singh Gill     }
984de0c960cSJagpal Singh Gill     else
985de0c960cSJagpal Singh Gill     {
986ef93eab3SJagpal Singh Gill         setApplyTime(asyncResp, *multipart->applyTime);
9870ed80c8cSGeorge Liu 
9886b54e4e0SEd Tanous         // Setup callback for when new software detected
989de0c960cSJagpal Singh Gill         monitorForSoftwareAvailable(asyncResp, req,
990de0c960cSJagpal Singh Gill                                     "/redfish/v1/UpdateService");
9916b54e4e0SEd Tanous 
992ef93eab3SJagpal Singh Gill         uploadImageFile(asyncResp->res, multipart->uploadData);
9930ed80c8cSGeorge Liu     }
994de0c960cSJagpal Singh Gill }
9950ed80c8cSGeorge Liu 
9969dae4deeSJagpal Singh Gill inline void doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
9979dae4deeSJagpal Singh Gill                          const crow::Request& req)
9989dae4deeSJagpal Singh Gill {
9999dae4deeSJagpal Singh Gill     if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
10009dae4deeSJagpal Singh Gill     {
10019dae4deeSJagpal Singh Gill         task::Payload payload(req);
10029dae4deeSJagpal Singh Gill         // HTTP push only supports BMC updates (with ApplyTime as immediate) for
10039dae4deeSJagpal Singh Gill         // backwards compatibility. Specific component updates will be handled
10049dae4deeSJagpal Singh Gill         // through Multipart form HTTP push.
10059dae4deeSJagpal Singh Gill         std::vector<std::string> targets;
10069dae4deeSJagpal Singh Gill         targets.emplace_back(BMCWEB_REDFISH_MANAGER_URI_NAME);
10079dae4deeSJagpal Singh Gill 
10089dae4deeSJagpal Singh Gill         processUpdateRequest(
10099dae4deeSJagpal Singh Gill             asyncResp, std::move(payload), req.body(),
10109dae4deeSJagpal Singh Gill             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate",
10119dae4deeSJagpal Singh Gill             targets);
10129dae4deeSJagpal Singh Gill     }
10139dae4deeSJagpal Singh Gill     else
10149dae4deeSJagpal Singh Gill     {
10159dae4deeSJagpal Singh Gill         // Setup callback for when new software detected
10169dae4deeSJagpal Singh Gill         monitorForSoftwareAvailable(asyncResp, req,
10179dae4deeSJagpal Singh Gill                                     "/redfish/v1/UpdateService");
10189dae4deeSJagpal Singh Gill 
10199dae4deeSJagpal Singh Gill         uploadImageFile(asyncResp->res, req.body());
10209dae4deeSJagpal Singh Gill     }
10219dae4deeSJagpal Singh Gill }
10229dae4deeSJagpal Singh Gill 
1023c2051d11SEd Tanous inline void
1024c2051d11SEd Tanous     handleUpdateServicePost(App& app, const crow::Request& req,
1025c2051d11SEd Tanous                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1026c2051d11SEd Tanous {
10273ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1028c2051d11SEd Tanous     {
1029c2051d11SEd Tanous         return;
1030c2051d11SEd Tanous     }
1031b33a4327SNinad Palsule     std::string_view contentType = req.getHeaderValue("Content-Type");
1032b33a4327SNinad Palsule 
103362598e31SEd Tanous     BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
1034b33a4327SNinad Palsule 
1035b33a4327SNinad Palsule     // Make sure that content type is application/octet-stream or
1036b33a4327SNinad Palsule     // multipart/form-data
103718f8f608SEd Tanous     if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
1038b33a4327SNinad Palsule     {
10399dae4deeSJagpal Singh Gill         doHTTPUpdate(asyncResp, req);
1040b33a4327SNinad Palsule     }
1041b33a4327SNinad Palsule     else if (contentType.starts_with("multipart/form-data"))
1042b33a4327SNinad Palsule     {
1043b33a4327SNinad Palsule         MultipartParser parser;
1044c2051d11SEd Tanous 
10450ed80c8cSGeorge Liu         ParserError ec = parser.parse(req);
10460ed80c8cSGeorge Liu         if (ec != ParserError::PARSER_SUCCESS)
10470ed80c8cSGeorge Liu         {
10480ed80c8cSGeorge Liu             // handle error
104962598e31SEd Tanous             BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
105062598e31SEd Tanous                              static_cast<int>(ec));
10510ed80c8cSGeorge Liu             messages::internalError(asyncResp->res);
10520ed80c8cSGeorge Liu             return;
10530ed80c8cSGeorge Liu         }
10546b54e4e0SEd Tanous 
1055ef93eab3SJagpal Singh Gill         updateMultipartContext(asyncResp, req, std::move(parser));
1056c2051d11SEd Tanous     }
1057b33a4327SNinad Palsule     else
1058b33a4327SNinad Palsule     {
105962598e31SEd Tanous         BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
1060b33a4327SNinad Palsule         asyncResp->res.result(boost::beast::http::status::bad_request);
1061b33a4327SNinad Palsule     }
1062b33a4327SNinad Palsule }
1063c2051d11SEd Tanous 
1064f5139334SEd Tanous inline void
1065f5139334SEd Tanous     handleUpdateServiceGet(App& app, const crow::Request& req,
1066f5139334SEd Tanous                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
10671abe55efSEd Tanous {
10683ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
106945ca1b86SEd Tanous     {
107045ca1b86SEd Tanous         return;
107145ca1b86SEd Tanous     }
10728d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.type"] =
10730ed80c8cSGeorge Liu         "#UpdateService.v1_11_1.UpdateService";
10748d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
10758d1b46d7Szhanghch05     asyncResp->res.jsonValue["Id"] = "UpdateService";
1076002d39b4SEd Tanous     asyncResp->res.jsonValue["Description"] = "Service for Software Update";
10778d1b46d7Szhanghch05     asyncResp->res.jsonValue["Name"] = "Update Service";
10784dc23f3fSEd Tanous 
10797e860f15SJohn Edward Broadbent     asyncResp->res.jsonValue["HttpPushUri"] =
10804dc23f3fSEd Tanous         "/redfish/v1/UpdateService/update";
10810ed80c8cSGeorge Liu     asyncResp->res.jsonValue["MultipartHttpPushUri"] =
10820ed80c8cSGeorge Liu         "/redfish/v1/UpdateService/update";
10834dc23f3fSEd Tanous 
10840f74e643SEd Tanous     // UpdateService cannot be disabled
10858d1b46d7Szhanghch05     asyncResp->res.jsonValue["ServiceEnabled"] = true;
10861476687dSEd Tanous     asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
10871476687dSEd Tanous         "/redfish/v1/UpdateService/FirmwareInventory";
1088d61e5194STejas Patil     // Get the MaxImageSizeBytes
1089bd79bce8SPatrick Williams     asyncResp->res.jsonValue["MaxImageSizeBytes"] =
1090bd79bce8SPatrick Williams         BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024;
1091d61e5194STejas Patil 
10926a37140aSEd Tanous     if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE)
10936a37140aSEd Tanous     {
10940554c984SAndrew Geissler         // Update Actions object.
10950554c984SAndrew Geissler         nlohmann::json& updateSvcSimpleUpdate =
1096002d39b4SEd Tanous             asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
10970554c984SAndrew Geissler         updateSvcSimpleUpdate["target"] =
10980554c984SAndrew Geissler             "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
1099757178a5SEd Tanous 
1100757178a5SEd Tanous         nlohmann::json::array_t allowed;
1101e5cf777eSEd Tanous         allowed.emplace_back(update_service::TransferProtocolType::HTTPS);
1102757178a5SEd Tanous         updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
1103757178a5SEd Tanous             std::move(allowed);
11046a37140aSEd Tanous     }
1105757178a5SEd Tanous 
1106539d8c6bSEd Tanous     asyncResp->res
1107539d8c6bSEd Tanous         .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] =
1108539d8c6bSEd Tanous         update_service::ApplyTime::Immediate;
1109729dae72SJennifer Lee }
1110729dae72SJennifer Lee 
1111f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryCollectionGet(
1112f5139334SEd Tanous     App& app, const crow::Request& req,
1113f5139334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
11141abe55efSEd Tanous {
11153ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
111645ca1b86SEd Tanous     {
111745ca1b86SEd Tanous         return;
111845ca1b86SEd Tanous     }
11198d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.type"] =
11200f74e643SEd Tanous         "#SoftwareInventoryCollection.SoftwareInventoryCollection";
11218d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.id"] =
11220f74e643SEd Tanous         "/redfish/v1/UpdateService/FirmwareInventory";
1123002d39b4SEd Tanous     asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
112408d81adaSJohn Edward Broadbent     const std::array<const std::string_view, 1> iface = {
1125e99073f5SGeorge Liu         "xyz.openbmc_project.Software.Version"};
11266c4eb9deSJennifer Lee 
112708d81adaSJohn Edward Broadbent     redfish::collection_util::getCollectionMembers(
112808d81adaSJohn Edward Broadbent         asyncResp,
1129f5139334SEd Tanous         boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface,
1130f5139334SEd Tanous         "/xyz/openbmc_project/software");
1131729dae72SJennifer Lee }
1132f5139334SEd Tanous 
113387d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */
1134f5139334SEd Tanous inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
113587d84729SAndrew Geissler                             const std::string& purpose)
113687d84729SAndrew Geissler {
1137eee0013eSWilly Tu     if (purpose == sw_util::bmcPurpose)
113887d84729SAndrew Geissler     {
1139ac106bf6SEd Tanous         nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
11401476687dSEd Tanous         nlohmann::json::object_t item;
1141253f11b8SEd Tanous         item["@odata.id"] = boost::urls::format(
1142253f11b8SEd Tanous             "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME);
1143b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
1144ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1145ac106bf6SEd Tanous             relatedItem.size();
114687d84729SAndrew Geissler     }
1147eee0013eSWilly Tu     else if (purpose == sw_util::biosPurpose)
114887d84729SAndrew Geissler     {
1149ac106bf6SEd Tanous         nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
11501476687dSEd Tanous         nlohmann::json::object_t item;
1151253f11b8SEd Tanous         item["@odata.id"] = std::format("/redfish/v1/Systems/{}/Bios",
1152253f11b8SEd Tanous                                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
1153b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
1154ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1155ac106bf6SEd Tanous             relatedItem.size();
115687d84729SAndrew Geissler     }
115787d84729SAndrew Geissler     else
115887d84729SAndrew Geissler     {
1159bf2ddedeSCarson Labrado         BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
116087d84729SAndrew Geissler     }
116187d84729SAndrew Geissler }
116287d84729SAndrew Geissler 
1163af24660dSWilly Tu inline void
1164af24660dSWilly Tu     getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1165af24660dSWilly Tu                        const std::string& service, const std::string& path,
1166af24660dSWilly Tu                        const std::string& swId)
1167af24660dSWilly Tu {
1168deae6a78SEd Tanous     dbus::utility::getAllProperties(
1169deae6a78SEd Tanous         service, path, "xyz.openbmc_project.Software.Version",
1170af24660dSWilly Tu         [asyncResp,
11718b24275dSEd Tanous          swId](const boost::system::error_code& ec,
1172af24660dSWilly Tu                const dbus::utility::DBusPropertiesMap& propertiesList) {
11738b24275dSEd Tanous             if (ec)
1174af24660dSWilly Tu             {
1175af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1176af24660dSWilly Tu                 return;
1177af24660dSWilly Tu             }
1178d1bde9e5SKrzysztof Grobelny 
1179af24660dSWilly Tu             const std::string* swInvPurpose = nullptr;
1180af24660dSWilly Tu             const std::string* version = nullptr;
1181d1bde9e5SKrzysztof Grobelny 
1182d1bde9e5SKrzysztof Grobelny             const bool success = sdbusplus::unpackPropertiesNoThrow(
1183d1bde9e5SKrzysztof Grobelny                 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
1184d1bde9e5SKrzysztof Grobelny                 swInvPurpose, "Version", version);
1185d1bde9e5SKrzysztof Grobelny 
1186d1bde9e5SKrzysztof Grobelny             if (!success)
1187af24660dSWilly Tu             {
1188d1bde9e5SKrzysztof Grobelny                 messages::internalError(asyncResp->res);
1189d1bde9e5SKrzysztof Grobelny                 return;
1190af24660dSWilly Tu             }
1191af24660dSWilly Tu 
1192af24660dSWilly Tu             if (swInvPurpose == nullptr)
1193af24660dSWilly Tu             {
119462598e31SEd Tanous                 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
1195af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1196af24660dSWilly Tu                 return;
1197af24660dSWilly Tu             }
1198af24660dSWilly Tu 
119962598e31SEd Tanous             BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
1200af24660dSWilly Tu 
1201af24660dSWilly Tu             if (version == nullptr)
1202af24660dSWilly Tu             {
120362598e31SEd Tanous                 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
1204af24660dSWilly Tu 
1205af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1206af24660dSWilly Tu 
1207af24660dSWilly Tu                 return;
1208af24660dSWilly Tu             }
1209af24660dSWilly Tu             asyncResp->res.jsonValue["Version"] = *version;
1210af24660dSWilly Tu             asyncResp->res.jsonValue["Id"] = swId;
1211af24660dSWilly Tu 
1212af24660dSWilly Tu             // swInvPurpose is of format:
1213af24660dSWilly Tu             // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
1214af24660dSWilly Tu             // Translate this to "ABC image"
1215af24660dSWilly Tu             size_t endDesc = swInvPurpose->rfind('.');
1216af24660dSWilly Tu             if (endDesc == std::string::npos)
1217af24660dSWilly Tu             {
1218af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1219af24660dSWilly Tu                 return;
1220af24660dSWilly Tu             }
1221af24660dSWilly Tu             endDesc++;
1222af24660dSWilly Tu             if (endDesc >= swInvPurpose->size())
1223af24660dSWilly Tu             {
1224af24660dSWilly Tu                 messages::internalError(asyncResp->res);
1225af24660dSWilly Tu                 return;
1226af24660dSWilly Tu             }
1227af24660dSWilly Tu 
1228af24660dSWilly Tu             std::string formatDesc = swInvPurpose->substr(endDesc);
1229af24660dSWilly Tu             asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1230af24660dSWilly Tu             getRelatedItems(asyncResp, *swInvPurpose);
1231d1bde9e5SKrzysztof Grobelny         });
1232af24660dSWilly Tu }
1233af24660dSWilly Tu 
1234f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryGet(
1235f5139334SEd Tanous     App& app, const crow::Request& req,
123645ca1b86SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1237f5139334SEd Tanous     const std::string& param)
1238f5139334SEd Tanous {
12393ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
124045ca1b86SEd Tanous     {
124145ca1b86SEd Tanous         return;
124245ca1b86SEd Tanous     }
1243f5139334SEd Tanous     std::shared_ptr<std::string> swId = std::make_shared<std::string>(param);
1244c711bf86SEd Tanous 
1245ef4c65b7SEd Tanous     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1246ef4c65b7SEd Tanous         "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
1247c711bf86SEd Tanous 
1248e99073f5SGeorge Liu     constexpr std::array<std::string_view, 1> interfaces = {
1249e99073f5SGeorge Liu         "xyz.openbmc_project.Software.Version"};
1250e99073f5SGeorge Liu     dbus::utility::getSubTree(
1251e99073f5SGeorge Liu         "/", 0, interfaces,
1252b9d36b47SEd Tanous         [asyncResp,
1253e99073f5SGeorge Liu          swId](const boost::system::error_code& ec,
1254b9d36b47SEd Tanous                const dbus::utility::MapperGetSubTreeResponse& subtree) {
125562598e31SEd Tanous             BMCWEB_LOG_DEBUG("doGet callback...");
12561abe55efSEd Tanous             if (ec)
12571abe55efSEd Tanous             {
1258f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
12596c4eb9deSJennifer Lee                 return;
12606c4eb9deSJennifer Lee             }
12616c4eb9deSJennifer Lee 
12626913228dSAndrew Geissler             // Ensure we find our input swId, otherwise return an error
12636913228dSAndrew Geissler             bool found = false;
1264bd79bce8SPatrick Williams             for (const std::pair<std::string,
1265bd79bce8SPatrick Williams                                  std::vector<std::pair<
1266bd79bce8SPatrick Williams                                      std::string, std::vector<std::string>>>>&
1267002d39b4SEd Tanous                      obj : subtree)
12681abe55efSEd Tanous             {
126911ba3979SEd Tanous                 if (!obj.first.ends_with(*swId))
12701abe55efSEd Tanous                 {
1271acb7cfb4SJennifer Lee                     continue;
1272acb7cfb4SJennifer Lee                 }
1273acb7cfb4SJennifer Lee 
127426f6976fSEd Tanous                 if (obj.second.empty())
12751abe55efSEd Tanous                 {
1276acb7cfb4SJennifer Lee                     continue;
1277acb7cfb4SJennifer Lee                 }
12786c4eb9deSJennifer Lee 
12796913228dSAndrew Geissler                 found = true;
1280eee0013eSWilly Tu                 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1281af24660dSWilly Tu                 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1282af24660dSWilly Tu                                    *swId);
12836c4eb9deSJennifer Lee             }
12846913228dSAndrew Geissler             if (!found)
12856913228dSAndrew Geissler             {
128662598e31SEd Tanous                 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
12876913228dSAndrew Geissler                 messages::resourceMissingAtURI(
1288ef4c65b7SEd Tanous                     asyncResp->res,
1289ef4c65b7SEd Tanous                     boost::urls::format(
1290bd79bce8SPatrick Williams                         "/redfish/v1/UpdateService/FirmwareInventory/{}",
1291bd79bce8SPatrick Williams                         *swId));
12926913228dSAndrew Geissler                 return;
12936913228dSAndrew Geissler             }
12944e68c45bSAyushi Smriti             asyncResp->res.jsonValue["@odata.type"] =
12954e68c45bSAyushi Smriti                 "#SoftwareInventory.v1_1_0.SoftwareInventory";
12964e68c45bSAyushi Smriti             asyncResp->res.jsonValue["Name"] = "Software Inventory";
1297539d8c6bSEd Tanous             asyncResp->res.jsonValue["Status"]["HealthRollup"] =
1298539d8c6bSEd Tanous                 resource::Health::OK;
12993f8a743aSAppaRao Puli 
13003f8a743aSAppaRao Puli             asyncResp->res.jsonValue["Updateable"] = false;
1301eee0013eSWilly Tu             sw_util::getSwUpdatableStatus(asyncResp, swId);
1302e99073f5SGeorge Liu         });
1303f5139334SEd Tanous }
1304f5139334SEd Tanous 
1305f5139334SEd Tanous inline void requestRoutesUpdateService(App& app)
1306f5139334SEd Tanous {
13076a37140aSEd Tanous     if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE)
13086a37140aSEd Tanous     {
1309f5139334SEd Tanous         BMCWEB_ROUTE(
13106a37140aSEd Tanous             app,
13116a37140aSEd Tanous             "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
1312f5139334SEd Tanous             .privileges(redfish::privileges::postUpdateService)
1313f5139334SEd Tanous             .methods(boost::beast::http::verb::post)(std::bind_front(
1314f5139334SEd Tanous                 handleUpdateServiceSimpleUpdateAction, std::ref(app)));
13156a37140aSEd Tanous     }
1316f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1317f5139334SEd Tanous         .privileges(redfish::privileges::getSoftwareInventory)
1318f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
1319f5139334SEd Tanous             handleUpdateServiceFirmwareInventoryGet, std::ref(app)));
1320f5139334SEd Tanous 
1321f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1322f5139334SEd Tanous         .privileges(redfish::privileges::getUpdateService)
1323f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(
1324f5139334SEd Tanous             std::bind_front(handleUpdateServiceGet, std::ref(app)));
1325f5139334SEd Tanous 
1326f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
1327f5139334SEd Tanous         .privileges(redfish::privileges::postUpdateService)
1328f5139334SEd Tanous         .methods(boost::beast::http::verb::post)(
1329f5139334SEd Tanous             std::bind_front(handleUpdateServicePost, std::ref(app)));
1330f5139334SEd Tanous 
1331f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
1332f5139334SEd Tanous         .privileges(redfish::privileges::getSoftwareInventoryCollection)
1333f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
1334f5139334SEd Tanous             handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app)));
13356c4eb9deSJennifer Lee }
1336729dae72SJennifer Lee 
1337729dae72SJennifer Lee } // namespace redfish
1338