xref: /openbmc/bmcweb/features/redfish/lib/update_service.hpp (revision 253f11b84347de6bff7c6b624bef270fefae5f5a)
1729dae72SJennifer Lee /*
2729dae72SJennifer Lee // Copyright (c) 2018 Intel Corporation
3729dae72SJennifer Lee //
4729dae72SJennifer Lee // Licensed under the Apache License, Version 2.0 (the "License");
5729dae72SJennifer Lee // you may not use this file except in compliance with the License.
6729dae72SJennifer Lee // You may obtain a copy of the License at
7729dae72SJennifer Lee //
8729dae72SJennifer Lee //      http://www.apache.org/licenses/LICENSE-2.0
9729dae72SJennifer Lee //
10729dae72SJennifer Lee // Unless required by applicable law or agreed to in writing, software
11729dae72SJennifer Lee // distributed under the License is distributed on an "AS IS" BASIS,
12729dae72SJennifer Lee // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13729dae72SJennifer Lee // See the License for the specific language governing permissions and
14729dae72SJennifer Lee // 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 
35e99073f5SGeorge Liu #include <boost/system/error_code.hpp>
36ef4c65b7SEd Tanous #include <boost/url/format.hpp>
371e1e598dSJonathan Doman #include <sdbusplus/asio/property.hpp>
383ccb3adbSEd Tanous #include <sdbusplus/bus/match.hpp>
39d1bde9e5SKrzysztof Grobelny #include <sdbusplus/unpack_properties.hpp>
401214b7e7SGunnar Mills 
412b73119cSGeorge Liu #include <array>
420ed80c8cSGeorge Liu #include <filesystem>
43c71b6c99SJagpal Singh Gill #include <functional>
44ef93eab3SJagpal Singh Gill #include <memory>
457cb59f65SEd Tanous #include <optional>
467cb59f65SEd Tanous #include <string>
472b73119cSGeorge Liu #include <string_view>
48ef93eab3SJagpal Singh Gill #include <vector>
492b73119cSGeorge Liu 
501abe55efSEd Tanous namespace redfish
511abe55efSEd Tanous {
5227826b5fSEd Tanous 
530e7de46fSAndrew Geissler // Match signals added on software path
54cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
5559d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
56cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
5759d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
580e7de46fSAndrew Geissler // Only allow one update at a time
59cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
600e7de46fSAndrew Geissler static bool fwUpdateInProgress = false;
6186adcd6dSAndrew Geissler // Timer for software available
62cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
63271584abSEd Tanous static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
6486adcd6dSAndrew Geissler 
65df254f2cSEd Tanous inline void cleanUp()
6686adcd6dSAndrew Geissler {
6786adcd6dSAndrew Geissler     fwUpdateInProgress = false;
6886adcd6dSAndrew Geissler     fwUpdateMatcher = nullptr;
694cde5d90SJames Feist     fwUpdateErrorMatcher = nullptr;
7086adcd6dSAndrew Geissler }
71df254f2cSEd Tanous 
72df254f2cSEd Tanous inline void activateImage(const std::string& objPath,
7386adcd6dSAndrew Geissler                           const std::string& service)
7486adcd6dSAndrew Geissler {
7562598e31SEd Tanous     BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
769ae226faSGeorge Liu     sdbusplus::asio::setProperty(
779ae226faSGeorge Liu         *crow::connections::systemBus, service, objPath,
789ae226faSGeorge Liu         "xyz.openbmc_project.Software.Activation", "RequestedActivation",
799ae226faSGeorge Liu         "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
808b24275dSEd Tanous         [](const boost::system::error_code& ec) {
818b24275dSEd Tanous         if (ec)
8286adcd6dSAndrew Geissler         {
8362598e31SEd Tanous             BMCWEB_LOG_DEBUG("error_code = {}", ec);
8462598e31SEd Tanous             BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
8586adcd6dSAndrew Geissler         }
869ae226faSGeorge Liu     });
8786adcd6dSAndrew Geissler }
880554c984SAndrew Geissler 
89c71b6c99SJagpal Singh Gill inline bool handleCreateTask(const boost::system::error_code& ec2,
90c71b6c99SJagpal Singh Gill                              sdbusplus::message_t& msg,
91c71b6c99SJagpal Singh Gill                              const std::shared_ptr<task::TaskData>& taskData)
92c71b6c99SJagpal Singh Gill {
93c71b6c99SJagpal Singh Gill     if (ec2)
94c71b6c99SJagpal Singh Gill     {
95c71b6c99SJagpal Singh Gill         return task::completed;
96c71b6c99SJagpal Singh Gill     }
97c71b6c99SJagpal Singh Gill 
98c71b6c99SJagpal Singh Gill     std::string iface;
99c71b6c99SJagpal Singh Gill     dbus::utility::DBusPropertiesMap values;
100c71b6c99SJagpal Singh Gill 
101c71b6c99SJagpal Singh Gill     std::string index = std::to_string(taskData->index);
102c71b6c99SJagpal Singh Gill     msg.read(iface, values);
103c71b6c99SJagpal Singh Gill 
104c71b6c99SJagpal Singh Gill     if (iface == "xyz.openbmc_project.Software.Activation")
105c71b6c99SJagpal Singh Gill     {
106c71b6c99SJagpal Singh Gill         const std::string* state = nullptr;
107c71b6c99SJagpal Singh Gill         for (const auto& property : values)
108c71b6c99SJagpal Singh Gill         {
109c71b6c99SJagpal Singh Gill             if (property.first == "Activation")
110c71b6c99SJagpal Singh Gill             {
111c71b6c99SJagpal Singh Gill                 state = std::get_if<std::string>(&property.second);
112c71b6c99SJagpal Singh Gill                 if (state == nullptr)
113c71b6c99SJagpal Singh Gill                 {
114c71b6c99SJagpal Singh Gill                     taskData->messages.emplace_back(messages::internalError());
115c71b6c99SJagpal Singh Gill                     return task::completed;
116c71b6c99SJagpal Singh Gill                 }
117c71b6c99SJagpal Singh Gill             }
118c71b6c99SJagpal Singh Gill         }
119c71b6c99SJagpal Singh Gill 
120c71b6c99SJagpal Singh Gill         if (state == nullptr)
121c71b6c99SJagpal Singh Gill         {
122c71b6c99SJagpal Singh Gill             return !task::completed;
123c71b6c99SJagpal Singh Gill         }
124c71b6c99SJagpal Singh Gill 
125c71b6c99SJagpal Singh Gill         if (state->ends_with("Invalid") || state->ends_with("Failed"))
126c71b6c99SJagpal Singh Gill         {
127c71b6c99SJagpal Singh Gill             taskData->state = "Exception";
128c71b6c99SJagpal Singh Gill             taskData->status = "Warning";
129c71b6c99SJagpal Singh Gill             taskData->messages.emplace_back(messages::taskAborted(index));
130c71b6c99SJagpal Singh Gill             return task::completed;
131c71b6c99SJagpal Singh Gill         }
132c71b6c99SJagpal Singh Gill 
133c71b6c99SJagpal Singh Gill         if (state->ends_with("Staged"))
134c71b6c99SJagpal Singh Gill         {
135c71b6c99SJagpal Singh Gill             taskData->state = "Stopping";
136c71b6c99SJagpal Singh Gill             taskData->messages.emplace_back(messages::taskPaused(index));
137c71b6c99SJagpal Singh Gill 
138c71b6c99SJagpal Singh Gill             // its staged, set a long timer to
139c71b6c99SJagpal Singh Gill             // allow them time to complete the
140c71b6c99SJagpal Singh Gill             // update (probably cycle the
141c71b6c99SJagpal Singh Gill             // system) if this expires then
142c71b6c99SJagpal Singh Gill             // task will be canceled
143c71b6c99SJagpal Singh Gill             taskData->extendTimer(std::chrono::hours(5));
144c71b6c99SJagpal Singh Gill             return !task::completed;
145c71b6c99SJagpal Singh Gill         }
146c71b6c99SJagpal Singh Gill 
147c71b6c99SJagpal Singh Gill         if (state->ends_with("Active"))
148c71b6c99SJagpal Singh Gill         {
149c71b6c99SJagpal Singh Gill             taskData->messages.emplace_back(messages::taskCompletedOK(index));
150c71b6c99SJagpal Singh Gill             taskData->state = "Completed";
151c71b6c99SJagpal Singh Gill             return task::completed;
152c71b6c99SJagpal Singh Gill         }
153c71b6c99SJagpal Singh Gill     }
154c71b6c99SJagpal Singh Gill     else if (iface == "xyz.openbmc_project.Software.ActivationProgress")
155c71b6c99SJagpal Singh Gill     {
156c71b6c99SJagpal Singh Gill         const uint8_t* progress = nullptr;
157c71b6c99SJagpal Singh Gill         for (const auto& property : values)
158c71b6c99SJagpal Singh Gill         {
159c71b6c99SJagpal Singh Gill             if (property.first == "Progress")
160c71b6c99SJagpal Singh Gill             {
161c71b6c99SJagpal Singh Gill                 progress = std::get_if<uint8_t>(&property.second);
162c71b6c99SJagpal Singh Gill                 if (progress == nullptr)
163c71b6c99SJagpal Singh Gill                 {
164c71b6c99SJagpal Singh Gill                     taskData->messages.emplace_back(messages::internalError());
165c71b6c99SJagpal Singh Gill                     return task::completed;
166c71b6c99SJagpal Singh Gill                 }
167c71b6c99SJagpal Singh Gill             }
168c71b6c99SJagpal Singh Gill         }
169c71b6c99SJagpal Singh Gill 
170c71b6c99SJagpal Singh Gill         if (progress == nullptr)
171c71b6c99SJagpal Singh Gill         {
172c71b6c99SJagpal Singh Gill             return !task::completed;
173c71b6c99SJagpal Singh Gill         }
174c71b6c99SJagpal Singh Gill         taskData->percentComplete = *progress;
175c71b6c99SJagpal Singh Gill         taskData->messages.emplace_back(
176c71b6c99SJagpal Singh Gill             messages::taskProgressChanged(index, *progress));
177c71b6c99SJagpal Singh Gill 
178c71b6c99SJagpal Singh Gill         // if we're getting status updates it's
179c71b6c99SJagpal Singh Gill         // still alive, update timer
180c71b6c99SJagpal Singh Gill         taskData->extendTimer(std::chrono::minutes(5));
181c71b6c99SJagpal Singh Gill     }
182c71b6c99SJagpal Singh Gill 
183c71b6c99SJagpal Singh Gill     // as firmware update often results in a
184c71b6c99SJagpal Singh Gill     // reboot, the task  may never "complete"
185c71b6c99SJagpal Singh Gill     // unless it is an error
186c71b6c99SJagpal Singh Gill 
187c71b6c99SJagpal Singh Gill     return !task::completed;
188c71b6c99SJagpal Singh Gill }
189c71b6c99SJagpal Singh Gill 
190c71b6c99SJagpal Singh Gill inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
191c71b6c99SJagpal Singh Gill                        task::Payload&& payload,
192c71b6c99SJagpal Singh Gill                        const sdbusplus::message::object_path& objPath)
193c71b6c99SJagpal Singh Gill {
194c71b6c99SJagpal Singh Gill     std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
195c71b6c99SJagpal Singh Gill         std::bind_front(handleCreateTask),
196c71b6c99SJagpal Singh Gill         "type='signal',interface='org.freedesktop.DBus.Properties',"
197c71b6c99SJagpal Singh Gill         "member='PropertiesChanged',path='" +
198c71b6c99SJagpal Singh Gill             objPath.str + "'");
199c71b6c99SJagpal Singh Gill     task->startTimer(std::chrono::minutes(5));
200c71b6c99SJagpal Singh Gill     task->populateResp(asyncResp->res);
201c71b6c99SJagpal Singh Gill     task->payload.emplace(std::move(payload));
202c71b6c99SJagpal Singh Gill }
203c71b6c99SJagpal Singh Gill 
2040554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
2050554c984SAndrew Geissler // then no asyncResp updates will occur
2068d1b46d7Szhanghch05 static void
2078d1b46d7Szhanghch05     softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
20859d494eeSPatrick Williams                            sdbusplus::message_t& m, task::Payload&& payload)
20986adcd6dSAndrew Geissler {
21080f79a40SMichael Shen     dbus::utility::DBusInterfacesMap interfacesProperties;
21186adcd6dSAndrew Geissler 
21286adcd6dSAndrew Geissler     sdbusplus::message::object_path objPath;
21386adcd6dSAndrew Geissler 
21486adcd6dSAndrew Geissler     m.read(objPath, interfacesProperties);
21586adcd6dSAndrew Geissler 
21662598e31SEd Tanous     BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
217e3eb3d63SEd Tanous     for (const auto& interface : interfacesProperties)
21886adcd6dSAndrew Geissler     {
21962598e31SEd Tanous         BMCWEB_LOG_DEBUG("interface = {}", interface.first);
22086adcd6dSAndrew Geissler 
22186adcd6dSAndrew Geissler         if (interface.first == "xyz.openbmc_project.Software.Activation")
22286adcd6dSAndrew Geissler         {
22386adcd6dSAndrew Geissler             // Retrieve service and activate
2242b73119cSGeorge Liu             constexpr std::array<std::string_view, 1> interfaces = {
2252b73119cSGeorge Liu                 "xyz.openbmc_project.Software.Activation"};
2262b73119cSGeorge Liu             dbus::utility::getDbusObject(
2272b73119cSGeorge Liu                 objPath.str, interfaces,
228a3e65892SEd Tanous                 [objPath, asyncResp, payload(std::move(payload))](
2298b24275dSEd Tanous                     const boost::system::error_code& ec,
230a3e65892SEd Tanous                     const std::vector<
231a3e65892SEd Tanous                         std::pair<std::string, std::vector<std::string>>>&
232a3e65892SEd Tanous                         objInfo) mutable {
2338b24275dSEd Tanous                 if (ec)
23486adcd6dSAndrew Geissler                 {
23562598e31SEd Tanous                     BMCWEB_LOG_DEBUG("error_code = {}", ec);
23662598e31SEd Tanous                     BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
2370554c984SAndrew Geissler                     if (asyncResp)
2380554c984SAndrew Geissler                     {
23986adcd6dSAndrew Geissler                         messages::internalError(asyncResp->res);
2400554c984SAndrew Geissler                     }
24186adcd6dSAndrew Geissler                     cleanUp();
24286adcd6dSAndrew Geissler                     return;
24386adcd6dSAndrew Geissler                 }
24486adcd6dSAndrew Geissler                 // Ensure we only got one service back
24586adcd6dSAndrew Geissler                 if (objInfo.size() != 1)
24686adcd6dSAndrew Geissler                 {
24762598e31SEd Tanous                     BMCWEB_LOG_ERROR("Invalid Object Size {}", objInfo.size());
2480554c984SAndrew Geissler                     if (asyncResp)
2490554c984SAndrew Geissler                     {
25086adcd6dSAndrew Geissler                         messages::internalError(asyncResp->res);
2510554c984SAndrew Geissler                     }
25286adcd6dSAndrew Geissler                     cleanUp();
25386adcd6dSAndrew Geissler                     return;
25486adcd6dSAndrew Geissler                 }
25586adcd6dSAndrew Geissler                 // cancel timer only when
25686adcd6dSAndrew Geissler                 // xyz.openbmc_project.Software.Activation interface
25786adcd6dSAndrew Geissler                 // is added
25886adcd6dSAndrew Geissler                 fwAvailableTimer = nullptr;
25986adcd6dSAndrew Geissler 
26086adcd6dSAndrew Geissler                 activateImage(objPath.str, objInfo[0].first);
2610554c984SAndrew Geissler                 if (asyncResp)
2620554c984SAndrew Geissler                 {
263c71b6c99SJagpal Singh Gill                     createTask(asyncResp, std::move(payload), objPath);
2640554c984SAndrew Geissler                 }
26586adcd6dSAndrew Geissler                 fwUpdateInProgress = false;
2662b73119cSGeorge Liu             });
26762bafc01SPatrick Williams 
26862bafc01SPatrick Williams             break;
26986adcd6dSAndrew Geissler         }
27086adcd6dSAndrew Geissler     }
27186adcd6dSAndrew Geissler }
27286adcd6dSAndrew Geissler 
2738549b951SMyung Bae inline void afterAvailbleTimerAsyncWait(
2748549b951SMyung Bae     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2758549b951SMyung Bae     const boost::system::error_code& ec)
2768549b951SMyung Bae {
2778549b951SMyung Bae     cleanUp();
2788549b951SMyung Bae     if (ec == boost::asio::error::operation_aborted)
2798549b951SMyung Bae     {
2808549b951SMyung Bae         // expected, we were canceled before the timer completed.
2818549b951SMyung Bae         return;
2828549b951SMyung Bae     }
2838549b951SMyung Bae     BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
2848549b951SMyung Bae     BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
2858549b951SMyung Bae     if (ec)
2868549b951SMyung Bae     {
2878549b951SMyung Bae         BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
2888549b951SMyung Bae         return;
2898549b951SMyung Bae     }
2908549b951SMyung Bae     if (asyncResp)
2918549b951SMyung Bae     {
2928549b951SMyung Bae         redfish::messages::internalError(asyncResp->res);
2938549b951SMyung Bae     }
2948549b951SMyung Bae }
2958549b951SMyung Bae 
2968549b951SMyung Bae inline void
2978549b951SMyung Bae     handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2988549b951SMyung Bae                           const std::string& url, const std::string& type)
2998549b951SMyung Bae {
3008549b951SMyung Bae     if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
3018549b951SMyung Bae     {
3028549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3038549b951SMyung Bae                                          "Invalid archive");
3048549b951SMyung Bae     }
3058549b951SMyung Bae     else if (type ==
3068549b951SMyung Bae              "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
3078549b951SMyung Bae     {
3088549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3098549b951SMyung Bae                                          "Invalid manifest");
3108549b951SMyung Bae     }
3118549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
3128549b951SMyung Bae     {
3138549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3148549b951SMyung Bae                                          "Invalid image format");
3158549b951SMyung Bae     }
3168549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
3178549b951SMyung Bae     {
3188549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3198549b951SMyung Bae                                          "Image version already exists");
3208549b951SMyung Bae 
3218549b951SMyung Bae         redfish::messages::resourceAlreadyExists(
3228549b951SMyung Bae             asyncResp->res, "UpdateService", "Version", "uploaded version");
3238549b951SMyung Bae     }
3248549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
3258549b951SMyung Bae     {
3268549b951SMyung Bae         redfish::messages::resourceExhaustion(asyncResp->res, url);
3278549b951SMyung Bae     }
3284034a652SMyung Bae     else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
3298549b951SMyung Bae     {
3304034a652SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3314034a652SMyung Bae                                          "Incompatible image version");
3324034a652SMyung Bae     }
3334034a652SMyung Bae     else if (type ==
3344034a652SMyung Bae              "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
3354034a652SMyung Bae     {
3364034a652SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3374034a652SMyung Bae                                          "Update Access Key Expired");
3384034a652SMyung Bae     }
3394034a652SMyung Bae     else if (type ==
3404034a652SMyung Bae              "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
3414034a652SMyung Bae     {
3424034a652SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3434034a652SMyung Bae                                          "Invalid image signature");
3444034a652SMyung Bae     }
3454034a652SMyung Bae     else if (type ==
3464034a652SMyung Bae                  "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
3474034a652SMyung Bae              type == "xyz.openbmc_project.Software.Version.Error.HostFile")
3484034a652SMyung Bae     {
3494034a652SMyung Bae         BMCWEB_LOG_ERROR("Software Image Error type={}", type);
3508549b951SMyung Bae         redfish::messages::internalError(asyncResp->res);
3518549b951SMyung Bae     }
3524034a652SMyung Bae     else
3534034a652SMyung Bae     {
3544034a652SMyung Bae         // Unrelated error types. Ignored
3554034a652SMyung Bae         BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
3564034a652SMyung Bae         return;
3574034a652SMyung Bae     }
3584034a652SMyung Bae     // Clear the timer
3594034a652SMyung Bae     fwAvailableTimer = nullptr;
3608549b951SMyung Bae }
3618549b951SMyung Bae 
3628549b951SMyung Bae inline void
3638549b951SMyung Bae     afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3648549b951SMyung Bae                             const std::string& url, sdbusplus::message_t& m)
3658549b951SMyung Bae {
36680f79a40SMichael Shen     dbus::utility::DBusInterfacesMap interfacesProperties;
3678549b951SMyung Bae     sdbusplus::message::object_path objPath;
3688549b951SMyung Bae     m.read(objPath, interfacesProperties);
3698549b951SMyung Bae     BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
3708549b951SMyung Bae     for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
3718549b951SMyung Bae              interface : interfacesProperties)
3728549b951SMyung Bae     {
3738549b951SMyung Bae         if (interface.first == "xyz.openbmc_project.Logging.Entry")
3748549b951SMyung Bae         {
3758549b951SMyung Bae             for (const std::pair<std::string, dbus::utility::DbusVariantType>&
3768549b951SMyung Bae                      value : interface.second)
3778549b951SMyung Bae             {
3788549b951SMyung Bae                 if (value.first != "Message")
3798549b951SMyung Bae                 {
3808549b951SMyung Bae                     continue;
3818549b951SMyung Bae                 }
3828549b951SMyung Bae                 const std::string* type =
3838549b951SMyung Bae                     std::get_if<std::string>(&value.second);
3848549b951SMyung Bae                 if (type == nullptr)
3858549b951SMyung Bae                 {
3868549b951SMyung Bae                     // if this was our message, timeout will cover it
3878549b951SMyung Bae                     return;
3888549b951SMyung Bae                 }
3898549b951SMyung Bae                 handleUpdateErrorType(asyncResp, url, *type);
3908549b951SMyung Bae             }
3918549b951SMyung Bae         }
3928549b951SMyung Bae     }
3938549b951SMyung Bae }
3948549b951SMyung Bae 
3950554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
3960554c984SAndrew Geissler // then no asyncResp updates will occur
397f5139334SEd Tanous inline void monitorForSoftwareAvailable(
3988d1b46d7Szhanghch05     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3998d1b46d7Szhanghch05     const crow::Request& req, const std::string& url,
4005d138943SGunnar Mills     int timeoutTimeSeconds = 25)
40186adcd6dSAndrew Geissler {
40286adcd6dSAndrew Geissler     // Only allow one FW update at a time
403e05aec50SEd Tanous     if (fwUpdateInProgress)
40486adcd6dSAndrew Geissler     {
4050554c984SAndrew Geissler         if (asyncResp)
4060554c984SAndrew Geissler         {
40786adcd6dSAndrew Geissler             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
4080554c984SAndrew Geissler         }
40986adcd6dSAndrew Geissler         return;
41086adcd6dSAndrew Geissler     }
41186adcd6dSAndrew Geissler 
4128e8245dbSEd Tanous     if (req.ioService == nullptr)
4138e8245dbSEd Tanous     {
4148e8245dbSEd Tanous         messages::internalError(asyncResp->res);
4158e8245dbSEd Tanous         return;
4168e8245dbSEd Tanous     }
4178e8245dbSEd Tanous 
4180554c984SAndrew Geissler     fwAvailableTimer =
419271584abSEd Tanous         std::make_unique<boost::asio::steady_timer>(*req.ioService);
42086adcd6dSAndrew Geissler 
421271584abSEd Tanous     fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
42286adcd6dSAndrew Geissler 
42386adcd6dSAndrew Geissler     fwAvailableTimer->async_wait(
4248549b951SMyung Bae         std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
4258549b951SMyung Bae 
426a3e65892SEd Tanous     task::Payload payload(req);
42759d494eeSPatrick Williams     auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
42862598e31SEd Tanous         BMCWEB_LOG_DEBUG("Match fired");
429a3e65892SEd Tanous         softwareInterfaceAdded(asyncResp, m, std::move(payload));
43086adcd6dSAndrew Geissler     };
43186adcd6dSAndrew Geissler 
43286adcd6dSAndrew Geissler     fwUpdateInProgress = true;
43386adcd6dSAndrew Geissler 
43459d494eeSPatrick Williams     fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
43586adcd6dSAndrew Geissler         *crow::connections::systemBus,
43686adcd6dSAndrew Geissler         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
43786adcd6dSAndrew Geissler         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
43886adcd6dSAndrew Geissler         callback);
4394cde5d90SJames Feist 
44059d494eeSPatrick Williams     fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
4414cde5d90SJames Feist         *crow::connections::systemBus,
442e1cc4828SBrian Ma         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
443e1cc4828SBrian Ma         "member='InterfacesAdded',"
444e1cc4828SBrian Ma         "path='/xyz/openbmc_project/logging'",
4458549b951SMyung Bae         std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
44686adcd6dSAndrew Geissler }
447729dae72SJennifer Lee 
448757178a5SEd Tanous inline std::optional<boost::urls::url>
449757178a5SEd Tanous     parseSimpleUpdateUrl(std::string imageURI,
450f86bcc87SEd Tanous                          std::optional<std::string> transferProtocol,
451f86bcc87SEd Tanous                          crow::Response& res)
452f86bcc87SEd Tanous {
453f86bcc87SEd Tanous     if (imageURI.find("://") == std::string::npos)
454f86bcc87SEd Tanous     {
455f86bcc87SEd Tanous         if (imageURI.starts_with("/"))
456f86bcc87SEd Tanous         {
457f86bcc87SEd Tanous             messages::actionParameterValueTypeError(
458f86bcc87SEd Tanous                 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
459f86bcc87SEd Tanous             return std::nullopt;
460f86bcc87SEd Tanous         }
461f86bcc87SEd Tanous         if (!transferProtocol)
462f86bcc87SEd Tanous         {
463f86bcc87SEd Tanous             messages::actionParameterValueTypeError(
464f86bcc87SEd Tanous                 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
465f86bcc87SEd Tanous             return std::nullopt;
466f86bcc87SEd Tanous         }
467e5cf777eSEd Tanous         // OpenBMC currently only supports TFTP or HTTPS
468757178a5SEd Tanous         if (*transferProtocol == "TFTP")
469757178a5SEd Tanous         {
470757178a5SEd Tanous             imageURI = "tftp://" + imageURI;
471757178a5SEd Tanous         }
472e5cf777eSEd Tanous         else if (*transferProtocol == "HTTPS")
473e5cf777eSEd Tanous         {
474e5cf777eSEd Tanous             imageURI = "https://" + imageURI;
475e5cf777eSEd Tanous         }
476757178a5SEd Tanous         else
477f86bcc87SEd Tanous         {
478f86bcc87SEd Tanous             messages::actionParameterNotSupported(res, "TransferProtocol",
479f86bcc87SEd Tanous                                                   *transferProtocol);
480f86bcc87SEd Tanous             BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
481f86bcc87SEd Tanous                              *transferProtocol);
482f86bcc87SEd Tanous             return std::nullopt;
483f86bcc87SEd Tanous         }
484f86bcc87SEd Tanous     }
485f86bcc87SEd Tanous 
486f86bcc87SEd Tanous     boost::system::result<boost::urls::url> url =
487f86bcc87SEd Tanous         boost::urls::parse_absolute_uri(imageURI);
488f86bcc87SEd Tanous     if (!url)
489f86bcc87SEd Tanous     {
490f86bcc87SEd Tanous         messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
491f86bcc87SEd Tanous                                                 "UpdateService.SimpleUpdate");
492f86bcc87SEd Tanous 
493f86bcc87SEd Tanous         return std::nullopt;
494f86bcc87SEd Tanous     }
495f86bcc87SEd Tanous     url->normalize();
496f86bcc87SEd Tanous 
497757178a5SEd Tanous     if (url->scheme() == "tftp")
498757178a5SEd Tanous     {
499757178a5SEd Tanous         if (url->encoded_path().size() < 2)
500757178a5SEd Tanous         {
501757178a5SEd Tanous             messages::actionParameterNotSupported(res, "ImageURI",
502757178a5SEd Tanous                                                   url->buffer());
503757178a5SEd Tanous             return std::nullopt;
504757178a5SEd Tanous         }
505757178a5SEd Tanous     }
506e5cf777eSEd Tanous     else if (url->scheme() == "https")
507e5cf777eSEd Tanous     {
508e5cf777eSEd Tanous         // Empty paths default to "/"
509e5cf777eSEd Tanous         if (url->encoded_path().empty())
510e5cf777eSEd Tanous         {
511e5cf777eSEd Tanous             url->set_encoded_path("/");
512e5cf777eSEd Tanous         }
513e5cf777eSEd Tanous     }
514757178a5SEd Tanous     else
515f86bcc87SEd Tanous     {
516f86bcc87SEd Tanous         messages::actionParameterNotSupported(res, "ImageURI", imageURI);
517f86bcc87SEd Tanous         return std::nullopt;
518f86bcc87SEd Tanous     }
519757178a5SEd Tanous 
520757178a5SEd Tanous     if (url->encoded_path().empty())
521f86bcc87SEd Tanous     {
522757178a5SEd Tanous         messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
523757178a5SEd Tanous                                                 "UpdateService.SimpleUpdate");
524f86bcc87SEd Tanous         return std::nullopt;
525f86bcc87SEd Tanous     }
526757178a5SEd Tanous 
527757178a5SEd Tanous     return *url;
528f86bcc87SEd Tanous }
529f86bcc87SEd Tanous 
530e5cf777eSEd Tanous inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
531e5cf777eSEd Tanous                           const boost::urls::url_view_base& url)
532e5cf777eSEd Tanous {
533e5cf777eSEd Tanous     messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
534e5cf777eSEd Tanous                                           url.buffer());
535e5cf777eSEd Tanous }
536e5cf777eSEd Tanous 
5376b0f66bdSEd Tanous inline void doTftpUpdate(const crow::Request& req,
5386b0f66bdSEd Tanous                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
539757178a5SEd Tanous                          const boost::urls::url_view_base& url)
5406b0f66bdSEd Tanous {
541c72503f3SEd Tanous     if (!BMCWEB_INSECURE_TFTP_UPDATE)
542c72503f3SEd Tanous     {
5436b0f66bdSEd Tanous         messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
544757178a5SEd Tanous                                               url.buffer());
5456b0f66bdSEd Tanous         return;
546c72503f3SEd Tanous     }
547757178a5SEd Tanous 
548757178a5SEd Tanous     std::string path(url.encoded_path());
549757178a5SEd Tanous     if (path.size() < 2)
550757178a5SEd Tanous     {
551757178a5SEd Tanous         messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
552757178a5SEd Tanous                                               url.buffer());
553757178a5SEd Tanous         return;
554757178a5SEd Tanous     }
555757178a5SEd Tanous     // TFTP expects a path without a /
556757178a5SEd Tanous     path.erase(0, 1);
557757178a5SEd Tanous     std::string host(url.encoded_host_and_port());
558757178a5SEd Tanous     BMCWEB_LOG_DEBUG("Server: {} File: {}", host, path);
5596b0f66bdSEd Tanous 
5606b0f66bdSEd Tanous     // Setup callback for when new software detected
5616b0f66bdSEd Tanous     // Give TFTP 10 minutes to complete
5626b0f66bdSEd Tanous     monitorForSoftwareAvailable(
5636b0f66bdSEd Tanous         asyncResp, req,
5646b0f66bdSEd Tanous         "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate", 600);
5656b0f66bdSEd Tanous 
5666b0f66bdSEd Tanous     // TFTP can take up to 10 minutes depending on image size and
5676b0f66bdSEd Tanous     // connection speed. Return to caller as soon as the TFTP operation
5686b0f66bdSEd Tanous     // has been started. The callback above will ensure the activate
5696b0f66bdSEd Tanous     // is started once the download has completed
5706b0f66bdSEd Tanous     redfish::messages::success(asyncResp->res);
5716b0f66bdSEd Tanous 
5726b0f66bdSEd Tanous     // Call TFTP service
5736b0f66bdSEd Tanous     crow::connections::systemBus->async_method_call(
5746b0f66bdSEd Tanous         [](const boost::system::error_code& ec) {
5756b0f66bdSEd Tanous         if (ec)
5766b0f66bdSEd Tanous         {
5776b0f66bdSEd Tanous             // messages::internalError(asyncResp->res);
5786b0f66bdSEd Tanous             cleanUp();
5796b0f66bdSEd Tanous             BMCWEB_LOG_DEBUG("error_code = {}", ec);
5806b0f66bdSEd Tanous             BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
5816b0f66bdSEd Tanous         }
5826b0f66bdSEd Tanous         else
5836b0f66bdSEd Tanous         {
5846b0f66bdSEd Tanous             BMCWEB_LOG_DEBUG("Call to DownloaViaTFTP Success");
5856b0f66bdSEd Tanous         }
5866b0f66bdSEd Tanous     },
5876b0f66bdSEd Tanous         "xyz.openbmc_project.Software.Download",
5886b0f66bdSEd Tanous         "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
589757178a5SEd Tanous         "DownloadViaTFTP", path, host);
5906b0f66bdSEd Tanous }
5916b0f66bdSEd Tanous 
592f5139334SEd Tanous inline void handleUpdateServiceSimpleUpdateAction(
593f5139334SEd Tanous     crow::App& app, const crow::Request& req,
594f5139334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
5950554c984SAndrew Geissler {
5963ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
59745ca1b86SEd Tanous     {
59845ca1b86SEd Tanous         return;
59945ca1b86SEd Tanous     }
60045ca1b86SEd Tanous 
6010554c984SAndrew Geissler     std::optional<std::string> transferProtocol;
6020554c984SAndrew Geissler     std::string imageURI;
6030554c984SAndrew Geissler 
60462598e31SEd Tanous     BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
6050554c984SAndrew Geissler 
6060554c984SAndrew Geissler     // User can pass in both TransferProtocol and ImageURI parameters or
6074e0453b1SGunnar Mills     // they can pass in just the ImageURI with the transfer protocol
6084e0453b1SGunnar Mills     // embedded within it.
6090554c984SAndrew Geissler     // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
6100554c984SAndrew Geissler     // 2) ImageURI:tftp://1.1.1.1/myfile.bin
6110554c984SAndrew Geissler 
612002d39b4SEd Tanous     if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
613002d39b4SEd Tanous                                    transferProtocol, "ImageURI", imageURI))
6140554c984SAndrew Geissler     {
61562598e31SEd Tanous         BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
6160554c984SAndrew Geissler         return;
6170554c984SAndrew Geissler     }
618f5139334SEd Tanous 
619757178a5SEd Tanous     std::optional<boost::urls::url> url =
620757178a5SEd Tanous         parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res);
621757178a5SEd Tanous     if (!url)
6220554c984SAndrew Geissler     {
6230554c984SAndrew Geissler         return;
6240554c984SAndrew Geissler     }
625757178a5SEd Tanous     if (url->scheme() == "tftp")
626757178a5SEd Tanous     {
627757178a5SEd Tanous         doTftpUpdate(req, asyncResp, *url);
628757178a5SEd Tanous     }
629e5cf777eSEd Tanous     else if (url->scheme() == "https")
630e5cf777eSEd Tanous     {
631e5cf777eSEd Tanous         doHttpsUpdate(asyncResp, *url);
632e5cf777eSEd Tanous     }
633757178a5SEd Tanous     else
634757178a5SEd Tanous     {
635757178a5SEd Tanous         messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
636757178a5SEd Tanous                                               url->buffer());
637757178a5SEd Tanous         return;
638757178a5SEd Tanous     }
6390554c984SAndrew Geissler 
64062598e31SEd Tanous     BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
641729dae72SJennifer Lee }
642729dae72SJennifer Lee 
6430ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body)
6440ed80c8cSGeorge Liu {
6452c6ffdb0SEd Tanous     std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
6462c6ffdb0SEd Tanous 
64762598e31SEd Tanous     BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
6480ed80c8cSGeorge Liu     std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
6490ed80c8cSGeorge Liu                                     std::ofstream::trunc);
6500ed80c8cSGeorge Liu     // set the permission of the file to 640
65189492a15SPatrick Williams     std::filesystem::perms permission = std::filesystem::perms::owner_read |
65289492a15SPatrick Williams                                         std::filesystem::perms::group_read;
6530ed80c8cSGeorge Liu     std::filesystem::permissions(filepath, permission);
6540ed80c8cSGeorge Liu     out << body;
6550ed80c8cSGeorge Liu 
6560ed80c8cSGeorge Liu     if (out.bad())
6570ed80c8cSGeorge Liu     {
6580ed80c8cSGeorge Liu         messages::internalError(res);
6590ed80c8cSGeorge Liu         cleanUp();
6600ed80c8cSGeorge Liu     }
6610ed80c8cSGeorge Liu }
6620ed80c8cSGeorge Liu 
6630ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
6640ed80c8cSGeorge Liu                          const std::string& applyTime)
6650ed80c8cSGeorge Liu {
6660ed80c8cSGeorge Liu     std::string applyTimeNewVal;
6670ed80c8cSGeorge Liu     if (applyTime == "Immediate")
6680ed80c8cSGeorge Liu     {
6690ed80c8cSGeorge Liu         applyTimeNewVal =
6700ed80c8cSGeorge Liu             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
6710ed80c8cSGeorge Liu     }
6720ed80c8cSGeorge Liu     else if (applyTime == "OnReset")
6730ed80c8cSGeorge Liu     {
6740ed80c8cSGeorge Liu         applyTimeNewVal =
6750ed80c8cSGeorge Liu             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
6760ed80c8cSGeorge Liu     }
6770ed80c8cSGeorge Liu     else
6780ed80c8cSGeorge Liu     {
67962598e31SEd Tanous         BMCWEB_LOG_INFO(
68062598e31SEd Tanous             "ApplyTime value is not in the list of acceptable values");
6810ed80c8cSGeorge Liu         messages::propertyValueNotInList(asyncResp->res, applyTime,
6820ed80c8cSGeorge Liu                                          "ApplyTime");
6830ed80c8cSGeorge Liu         return;
6840ed80c8cSGeorge Liu     }
6850ed80c8cSGeorge Liu 
686d02aad39SEd Tanous     setDbusProperty(asyncResp, "xyz.openbmc_project.Settings",
687d02aad39SEd Tanous                     sdbusplus::message::object_path(
688d02aad39SEd Tanous                         "/xyz/openbmc_project/software/apply_time"),
689d02aad39SEd Tanous                     "xyz.openbmc_project.Software.ApplyTime",
690d02aad39SEd Tanous                     "RequestedApplyTime", "ApplyTime", applyTimeNewVal);
6910ed80c8cSGeorge Liu }
6920ed80c8cSGeorge Liu 
693ef93eab3SJagpal Singh Gill struct MultiPartUpdateParameters
6940ed80c8cSGeorge Liu {
695ef93eab3SJagpal Singh Gill     std::optional<std::string> applyTime;
696ef93eab3SJagpal Singh Gill     std::string uploadData;
697ef93eab3SJagpal Singh Gill     std::vector<boost::urls::url> targets;
698ef93eab3SJagpal Singh Gill };
699ef93eab3SJagpal Singh Gill 
700ef93eab3SJagpal Singh Gill inline std::optional<MultiPartUpdateParameters>
701ef93eab3SJagpal Singh Gill     extractMultipartUpdateParameters(
702ef93eab3SJagpal Singh Gill         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
703ef93eab3SJagpal Singh Gill         MultipartParser parser)
704ef93eab3SJagpal Singh Gill {
705ef93eab3SJagpal Singh Gill     MultiPartUpdateParameters multiRet;
706ef93eab3SJagpal Singh Gill     for (FormPart& formpart : parser.mime_fields)
7070ed80c8cSGeorge Liu     {
7080ed80c8cSGeorge Liu         boost::beast::http::fields::const_iterator it =
7090ed80c8cSGeorge Liu             formpart.fields.find("Content-Disposition");
7100ed80c8cSGeorge Liu         if (it == formpart.fields.end())
7110ed80c8cSGeorge Liu         {
71262598e31SEd Tanous             BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
713ef93eab3SJagpal Singh Gill             return std::nullopt;
7140ed80c8cSGeorge Liu         }
71562598e31SEd Tanous         BMCWEB_LOG_INFO("Parsing value {}", it->value());
7160ed80c8cSGeorge Liu 
7170ed80c8cSGeorge Liu         // The construction parameters of param_list must start with `;`
7180ed80c8cSGeorge Liu         size_t index = it->value().find(';');
7190ed80c8cSGeorge Liu         if (index == std::string::npos)
7200ed80c8cSGeorge Liu         {
7210ed80c8cSGeorge Liu             continue;
7220ed80c8cSGeorge Liu         }
7230ed80c8cSGeorge Liu 
72489492a15SPatrick Williams         for (const auto& param :
7250ed80c8cSGeorge Liu              boost::beast::http::param_list{it->value().substr(index)})
7260ed80c8cSGeorge Liu         {
7270ed80c8cSGeorge Liu             if (param.first != "name" || param.second.empty())
7280ed80c8cSGeorge Liu             {
7290ed80c8cSGeorge Liu                 continue;
7300ed80c8cSGeorge Liu             }
7310ed80c8cSGeorge Liu 
7320ed80c8cSGeorge Liu             if (param.second == "UpdateParameters")
7330ed80c8cSGeorge Liu             {
734ef93eab3SJagpal Singh Gill                 std::vector<std::string> tempTargets;
7350ed80c8cSGeorge Liu                 nlohmann::json content =
7360ed80c8cSGeorge Liu                     nlohmann::json::parse(formpart.content);
7377cb59f65SEd Tanous                 nlohmann::json::object_t* obj =
7387cb59f65SEd Tanous                     content.get_ptr<nlohmann::json::object_t*>();
7397cb59f65SEd Tanous                 if (obj == nullptr)
7407cb59f65SEd Tanous                 {
741ef93eab3SJagpal Singh Gill                     messages::propertyValueTypeError(
742ef93eab3SJagpal Singh Gill                         asyncResp->res, formpart.content, "UpdateParameters");
743ef93eab3SJagpal Singh Gill                     return std::nullopt;
7447cb59f65SEd Tanous                 }
7457cb59f65SEd Tanous 
7467cb59f65SEd Tanous                 if (!json_util::readJsonObject(
747ef93eab3SJagpal Singh Gill                         *obj, asyncResp->res, "Targets", tempTargets,
748ef93eab3SJagpal Singh Gill                         "@Redfish.OperationApplyTime", multiRet.applyTime))
7490ed80c8cSGeorge Liu                 {
750ef93eab3SJagpal Singh Gill                     return std::nullopt;
7510ed80c8cSGeorge Liu                 }
752ef93eab3SJagpal Singh Gill 
753ef93eab3SJagpal Singh Gill                 for (size_t urlIndex = 0; urlIndex < tempTargets.size();
754ef93eab3SJagpal Singh Gill                      urlIndex++)
7550ed80c8cSGeorge Liu                 {
756ef93eab3SJagpal Singh Gill                     const std::string& target = tempTargets[urlIndex];
757ef93eab3SJagpal Singh Gill                     boost::system::result<boost::urls::url_view> url =
758ef93eab3SJagpal Singh Gill                         boost::urls::parse_origin_form(target);
759ef93eab3SJagpal Singh Gill                     if (!url)
7600ed80c8cSGeorge Liu                     {
761ef93eab3SJagpal Singh Gill                         messages::propertyValueFormatError(
762ef93eab3SJagpal Singh Gill                             asyncResp->res, target,
763ef93eab3SJagpal Singh Gill                             std::format("Targets/{}", urlIndex));
764ef93eab3SJagpal Singh Gill                         return std::nullopt;
7650ed80c8cSGeorge Liu                     }
766ef93eab3SJagpal Singh Gill                     multiRet.targets.emplace_back(*url);
767ef93eab3SJagpal Singh Gill                 }
768ef93eab3SJagpal Singh Gill                 if (multiRet.targets.size() != 1)
769ef93eab3SJagpal Singh Gill                 {
770ef93eab3SJagpal Singh Gill                     messages::propertyValueFormatError(
771ef93eab3SJagpal Singh Gill                         asyncResp->res, multiRet.targets, "Targets");
772ef93eab3SJagpal Singh Gill                     return std::nullopt;
773ef93eab3SJagpal Singh Gill                 }
774*253f11b8SEd Tanous                 if (multiRet.targets[0].path() !=
775*253f11b8SEd Tanous                     std::format("/redfish/v1/Managers/{}",
776*253f11b8SEd Tanous                                 BMCWEB_REDFISH_MANAGER_URI_NAME))
777ef93eab3SJagpal Singh Gill                 {
778ef93eab3SJagpal Singh Gill                     messages::propertyValueNotInList(
779ef93eab3SJagpal Singh Gill                         asyncResp->res, multiRet.targets[0], "Targets/0");
780ef93eab3SJagpal Singh Gill                     return std::nullopt;
781ef93eab3SJagpal Singh Gill                 }
7820ed80c8cSGeorge Liu             }
7830ed80c8cSGeorge Liu             else if (param.second == "UpdateFile")
7840ed80c8cSGeorge Liu             {
785ef93eab3SJagpal Singh Gill                 multiRet.uploadData = std::move(formpart.content);
7860ed80c8cSGeorge Liu             }
7870ed80c8cSGeorge Liu         }
7880ed80c8cSGeorge Liu     }
7890ed80c8cSGeorge Liu 
790ef93eab3SJagpal Singh Gill     if (multiRet.uploadData.empty())
7910ed80c8cSGeorge Liu     {
79262598e31SEd Tanous         BMCWEB_LOG_ERROR("Upload data is NULL");
7930ed80c8cSGeorge Liu         messages::propertyMissing(asyncResp->res, "UpdateFile");
794ef93eab3SJagpal Singh Gill         return std::nullopt;
7950ed80c8cSGeorge Liu     }
796ef93eab3SJagpal Singh Gill     if (multiRet.targets.empty())
7970ed80c8cSGeorge Liu     {
798ef93eab3SJagpal Singh Gill         messages::propertyMissing(asyncResp->res, "Targets");
799ef93eab3SJagpal Singh Gill         return std::nullopt;
800ef93eab3SJagpal Singh Gill     }
801ef93eab3SJagpal Singh Gill     return multiRet;
8020ed80c8cSGeorge Liu }
8030ed80c8cSGeorge Liu 
804ef93eab3SJagpal Singh Gill inline void
805ef93eab3SJagpal Singh Gill     updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
806ef93eab3SJagpal Singh Gill                            const crow::Request& req, MultipartParser&& parser)
807ef93eab3SJagpal Singh Gill {
808ef93eab3SJagpal Singh Gill     std::optional<MultiPartUpdateParameters> multipart =
809ef93eab3SJagpal Singh Gill         extractMultipartUpdateParameters(asyncResp, std::move(parser));
810ef93eab3SJagpal Singh Gill     if (!multipart)
811ef93eab3SJagpal Singh Gill     {
812ef93eab3SJagpal Singh Gill         return;
813ef93eab3SJagpal Singh Gill     }
814ef93eab3SJagpal Singh Gill     if (!multipart->applyTime)
815ef93eab3SJagpal Singh Gill     {
816ef93eab3SJagpal Singh Gill         multipart->applyTime = "OnReset";
817ef93eab3SJagpal Singh Gill     }
818ef93eab3SJagpal Singh Gill 
819ef93eab3SJagpal Singh Gill     setApplyTime(asyncResp, *multipart->applyTime);
8200ed80c8cSGeorge Liu 
8216b54e4e0SEd Tanous     // Setup callback for when new software detected
8226b54e4e0SEd Tanous     monitorForSoftwareAvailable(asyncResp, req, "/redfish/v1/UpdateService");
8236b54e4e0SEd Tanous 
824ef93eab3SJagpal Singh Gill     uploadImageFile(asyncResp->res, multipart->uploadData);
8250ed80c8cSGeorge Liu }
8260ed80c8cSGeorge Liu 
827c2051d11SEd Tanous inline void
828c2051d11SEd Tanous     handleUpdateServicePost(App& app, const crow::Request& req,
829c2051d11SEd Tanous                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
830c2051d11SEd Tanous {
8313ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
832c2051d11SEd Tanous     {
833c2051d11SEd Tanous         return;
834c2051d11SEd Tanous     }
835b33a4327SNinad Palsule     std::string_view contentType = req.getHeaderValue("Content-Type");
836b33a4327SNinad Palsule 
83762598e31SEd Tanous     BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
838b33a4327SNinad Palsule 
839b33a4327SNinad Palsule     // Make sure that content type is application/octet-stream or
840b33a4327SNinad Palsule     // multipart/form-data
84118f8f608SEd Tanous     if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
842b33a4327SNinad Palsule     {
843b33a4327SNinad Palsule         // Setup callback for when new software detected
844b33a4327SNinad Palsule         monitorForSoftwareAvailable(asyncResp, req,
845b33a4327SNinad Palsule                                     "/redfish/v1/UpdateService");
846b33a4327SNinad Palsule 
847b33a4327SNinad Palsule         uploadImageFile(asyncResp->res, req.body());
848b33a4327SNinad Palsule     }
849b33a4327SNinad Palsule     else if (contentType.starts_with("multipart/form-data"))
850b33a4327SNinad Palsule     {
851b33a4327SNinad Palsule         MultipartParser parser;
852c2051d11SEd Tanous 
8530ed80c8cSGeorge Liu         ParserError ec = parser.parse(req);
8540ed80c8cSGeorge Liu         if (ec != ParserError::PARSER_SUCCESS)
8550ed80c8cSGeorge Liu         {
8560ed80c8cSGeorge Liu             // handle error
85762598e31SEd Tanous             BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
85862598e31SEd Tanous                              static_cast<int>(ec));
8590ed80c8cSGeorge Liu             messages::internalError(asyncResp->res);
8600ed80c8cSGeorge Liu             return;
8610ed80c8cSGeorge Liu         }
8626b54e4e0SEd Tanous 
863ef93eab3SJagpal Singh Gill         updateMultipartContext(asyncResp, req, std::move(parser));
864c2051d11SEd Tanous     }
865b33a4327SNinad Palsule     else
866b33a4327SNinad Palsule     {
86762598e31SEd Tanous         BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
868b33a4327SNinad Palsule         asyncResp->res.result(boost::beast::http::status::bad_request);
869b33a4327SNinad Palsule     }
870b33a4327SNinad Palsule }
871c2051d11SEd Tanous 
872f5139334SEd Tanous inline void
873f5139334SEd Tanous     handleUpdateServiceGet(App& app, const crow::Request& req,
874f5139334SEd Tanous                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
8751abe55efSEd Tanous {
8763ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
87745ca1b86SEd Tanous     {
87845ca1b86SEd Tanous         return;
87945ca1b86SEd Tanous     }
8808d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.type"] =
8810ed80c8cSGeorge Liu         "#UpdateService.v1_11_1.UpdateService";
8828d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
8838d1b46d7Szhanghch05     asyncResp->res.jsonValue["Id"] = "UpdateService";
884002d39b4SEd Tanous     asyncResp->res.jsonValue["Description"] = "Service for Software Update";
8858d1b46d7Szhanghch05     asyncResp->res.jsonValue["Name"] = "Update Service";
8864dc23f3fSEd Tanous 
8877e860f15SJohn Edward Broadbent     asyncResp->res.jsonValue["HttpPushUri"] =
8884dc23f3fSEd Tanous         "/redfish/v1/UpdateService/update";
8890ed80c8cSGeorge Liu     asyncResp->res.jsonValue["MultipartHttpPushUri"] =
8900ed80c8cSGeorge Liu         "/redfish/v1/UpdateService/update";
8914dc23f3fSEd Tanous 
8920f74e643SEd Tanous     // UpdateService cannot be disabled
8938d1b46d7Szhanghch05     asyncResp->res.jsonValue["ServiceEnabled"] = true;
8941476687dSEd Tanous     asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
8951476687dSEd Tanous         "/redfish/v1/UpdateService/FirmwareInventory";
896d61e5194STejas Patil     // Get the MaxImageSizeBytes
89725b54dbaSEd Tanous     asyncResp->res.jsonValue["MaxImageSizeBytes"] = BMCWEB_HTTP_BODY_LIMIT *
898f5139334SEd Tanous                                                     1024 * 1024;
899d61e5194STejas Patil 
9000554c984SAndrew Geissler     // Update Actions object.
9010554c984SAndrew Geissler     nlohmann::json& updateSvcSimpleUpdate =
902002d39b4SEd Tanous         asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
9030554c984SAndrew Geissler     updateSvcSimpleUpdate["target"] =
9040554c984SAndrew Geissler         "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
905757178a5SEd Tanous 
906757178a5SEd Tanous     nlohmann::json::array_t allowed;
907e5cf777eSEd Tanous     allowed.emplace_back(update_service::TransferProtocolType::HTTPS);
908757178a5SEd Tanous 
90925b54dbaSEd Tanous     if constexpr (BMCWEB_INSECURE_PUSH_STYLE_NOTIFICATION)
91025b54dbaSEd Tanous     {
911757178a5SEd Tanous         allowed.emplace_back(update_service::TransferProtocolType::TFTP);
91225b54dbaSEd Tanous     }
913757178a5SEd Tanous 
914757178a5SEd Tanous     updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
915757178a5SEd Tanous         std::move(allowed);
916757178a5SEd Tanous 
917274dfe62SJayashankar Padath     // Get the current ApplyTime value
9181e1e598dSJonathan Doman     sdbusplus::asio::getProperty<std::string>(
9191e1e598dSJonathan Doman         *crow::connections::systemBus, "xyz.openbmc_project.Settings",
9201e1e598dSJonathan Doman         "/xyz/openbmc_project/software/apply_time",
9211e1e598dSJonathan Doman         "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
9225e7e2dc5SEd Tanous         [asyncResp](const boost::system::error_code& ec,
9231e1e598dSJonathan Doman                     const std::string& applyTime) {
924274dfe62SJayashankar Padath         if (ec)
925274dfe62SJayashankar Padath         {
92662598e31SEd Tanous             BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
9278d1b46d7Szhanghch05             messages::internalError(asyncResp->res);
928274dfe62SJayashankar Padath             return;
929274dfe62SJayashankar Padath         }
930274dfe62SJayashankar Padath 
931274dfe62SJayashankar Padath         // Store the ApplyTime Value
9321e1e598dSJonathan Doman         if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
9331e1e598dSJonathan Doman                          "RequestedApplyTimes.Immediate")
934274dfe62SJayashankar Padath         {
935002d39b4SEd Tanous             asyncResp->res.jsonValue["HttpPushUriOptions"]
9367e860f15SJohn Edward Broadbent                                     ["HttpPushUriApplyTime"]["ApplyTime"] =
9377e860f15SJohn Edward Broadbent                 "Immediate";
938274dfe62SJayashankar Padath         }
939002d39b4SEd Tanous         else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
9401e1e598dSJonathan Doman                               "RequestedApplyTimes.OnReset")
941274dfe62SJayashankar Padath         {
942002d39b4SEd Tanous             asyncResp->res.jsonValue["HttpPushUriOptions"]
9437e860f15SJohn Edward Broadbent                                     ["HttpPushUriApplyTime"]["ApplyTime"] =
9447e860f15SJohn Edward Broadbent                 "OnReset";
945274dfe62SJayashankar Padath         }
9461e1e598dSJonathan Doman     });
947f5139334SEd Tanous }
948f5139334SEd Tanous 
949f5139334SEd Tanous inline void handleUpdateServicePatch(
950f5139334SEd Tanous     App& app, const crow::Request& req,
951f5139334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
952f5139334SEd Tanous {
9533ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
95445ca1b86SEd Tanous     {
95545ca1b86SEd Tanous         return;
95645ca1b86SEd Tanous     }
95762598e31SEd Tanous     BMCWEB_LOG_DEBUG("doPatch...");
958fa1a5a38SJayashankar Padath 
959274dfe62SJayashankar Padath     std::optional<std::string> applyTime;
9607cb59f65SEd Tanous     if (!json_util::readJsonPatch(
9617cb59f65SEd Tanous             req, asyncResp->res,
9627cb59f65SEd Tanous             "HttpPushUriOptions/HttpPushUriApplyTime/ApplyTime", applyTime))
963274dfe62SJayashankar Padath     {
964274dfe62SJayashankar Padath         return;
965274dfe62SJayashankar Padath     }
966274dfe62SJayashankar Padath 
967274dfe62SJayashankar Padath     if (applyTime)
968fa1a5a38SJayashankar Padath     {
9690ed80c8cSGeorge Liu         setApplyTime(asyncResp, *applyTime);
970fa1a5a38SJayashankar Padath     }
971729dae72SJennifer Lee }
972729dae72SJennifer Lee 
973f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryCollectionGet(
974f5139334SEd Tanous     App& app, const crow::Request& req,
975f5139334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
9761abe55efSEd Tanous {
9773ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
97845ca1b86SEd Tanous     {
97945ca1b86SEd Tanous         return;
98045ca1b86SEd Tanous     }
9818d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.type"] =
9820f74e643SEd Tanous         "#SoftwareInventoryCollection.SoftwareInventoryCollection";
9838d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.id"] =
9840f74e643SEd Tanous         "/redfish/v1/UpdateService/FirmwareInventory";
985002d39b4SEd Tanous     asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
98608d81adaSJohn Edward Broadbent     const std::array<const std::string_view, 1> iface = {
987e99073f5SGeorge Liu         "xyz.openbmc_project.Software.Version"};
9886c4eb9deSJennifer Lee 
98908d81adaSJohn Edward Broadbent     redfish::collection_util::getCollectionMembers(
99008d81adaSJohn Edward Broadbent         asyncResp,
991f5139334SEd Tanous         boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface,
992f5139334SEd Tanous         "/xyz/openbmc_project/software");
993729dae72SJennifer Lee }
994f5139334SEd Tanous 
99587d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */
996f5139334SEd Tanous inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
99787d84729SAndrew Geissler                             const std::string& purpose)
99887d84729SAndrew Geissler {
999eee0013eSWilly Tu     if (purpose == sw_util::bmcPurpose)
100087d84729SAndrew Geissler     {
1001ac106bf6SEd Tanous         nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
10021476687dSEd Tanous         nlohmann::json::object_t item;
1003*253f11b8SEd Tanous         item["@odata.id"] = boost::urls::format(
1004*253f11b8SEd Tanous             "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME);
1005b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
1006ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1007ac106bf6SEd Tanous             relatedItem.size();
100887d84729SAndrew Geissler     }
1009eee0013eSWilly Tu     else if (purpose == sw_util::biosPurpose)
101087d84729SAndrew Geissler     {
1011ac106bf6SEd Tanous         nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
10121476687dSEd Tanous         nlohmann::json::object_t item;
1013*253f11b8SEd Tanous         item["@odata.id"] = std::format("/redfish/v1/Systems/{}/Bios",
1014*253f11b8SEd Tanous                                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
1015b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
1016ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1017ac106bf6SEd Tanous             relatedItem.size();
101887d84729SAndrew Geissler     }
101987d84729SAndrew Geissler     else
102087d84729SAndrew Geissler     {
1021bf2ddedeSCarson Labrado         BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
102287d84729SAndrew Geissler     }
102387d84729SAndrew Geissler }
102487d84729SAndrew Geissler 
1025af24660dSWilly Tu inline void
1026af24660dSWilly Tu     getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1027af24660dSWilly Tu                        const std::string& service, const std::string& path,
1028af24660dSWilly Tu                        const std::string& swId)
1029af24660dSWilly Tu {
1030d1bde9e5SKrzysztof Grobelny     sdbusplus::asio::getAllProperties(
1031d1bde9e5SKrzysztof Grobelny         *crow::connections::systemBus, service, path,
1032d1bde9e5SKrzysztof Grobelny         "xyz.openbmc_project.Software.Version",
1033af24660dSWilly Tu         [asyncResp,
10348b24275dSEd Tanous          swId](const boost::system::error_code& ec,
1035af24660dSWilly Tu                const dbus::utility::DBusPropertiesMap& propertiesList) {
10368b24275dSEd Tanous         if (ec)
1037af24660dSWilly Tu         {
1038af24660dSWilly Tu             messages::internalError(asyncResp->res);
1039af24660dSWilly Tu             return;
1040af24660dSWilly Tu         }
1041d1bde9e5SKrzysztof Grobelny 
1042af24660dSWilly Tu         const std::string* swInvPurpose = nullptr;
1043af24660dSWilly Tu         const std::string* version = nullptr;
1044d1bde9e5SKrzysztof Grobelny 
1045d1bde9e5SKrzysztof Grobelny         const bool success = sdbusplus::unpackPropertiesNoThrow(
1046d1bde9e5SKrzysztof Grobelny             dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
1047d1bde9e5SKrzysztof Grobelny             swInvPurpose, "Version", version);
1048d1bde9e5SKrzysztof Grobelny 
1049d1bde9e5SKrzysztof Grobelny         if (!success)
1050af24660dSWilly Tu         {
1051d1bde9e5SKrzysztof Grobelny             messages::internalError(asyncResp->res);
1052d1bde9e5SKrzysztof Grobelny             return;
1053af24660dSWilly Tu         }
1054af24660dSWilly Tu 
1055af24660dSWilly Tu         if (swInvPurpose == nullptr)
1056af24660dSWilly Tu         {
105762598e31SEd Tanous             BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
1058af24660dSWilly Tu             messages::internalError(asyncResp->res);
1059af24660dSWilly Tu             return;
1060af24660dSWilly Tu         }
1061af24660dSWilly Tu 
106262598e31SEd Tanous         BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
1063af24660dSWilly Tu 
1064af24660dSWilly Tu         if (version == nullptr)
1065af24660dSWilly Tu         {
106662598e31SEd Tanous             BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
1067af24660dSWilly Tu 
1068af24660dSWilly Tu             messages::internalError(asyncResp->res);
1069af24660dSWilly Tu 
1070af24660dSWilly Tu             return;
1071af24660dSWilly Tu         }
1072af24660dSWilly Tu         asyncResp->res.jsonValue["Version"] = *version;
1073af24660dSWilly Tu         asyncResp->res.jsonValue["Id"] = swId;
1074af24660dSWilly Tu 
1075af24660dSWilly Tu         // swInvPurpose is of format:
1076af24660dSWilly Tu         // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
1077af24660dSWilly Tu         // Translate this to "ABC image"
1078af24660dSWilly Tu         size_t endDesc = swInvPurpose->rfind('.');
1079af24660dSWilly Tu         if (endDesc == std::string::npos)
1080af24660dSWilly Tu         {
1081af24660dSWilly Tu             messages::internalError(asyncResp->res);
1082af24660dSWilly Tu             return;
1083af24660dSWilly Tu         }
1084af24660dSWilly Tu         endDesc++;
1085af24660dSWilly Tu         if (endDesc >= swInvPurpose->size())
1086af24660dSWilly Tu         {
1087af24660dSWilly Tu             messages::internalError(asyncResp->res);
1088af24660dSWilly Tu             return;
1089af24660dSWilly Tu         }
1090af24660dSWilly Tu 
1091af24660dSWilly Tu         std::string formatDesc = swInvPurpose->substr(endDesc);
1092af24660dSWilly Tu         asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1093af24660dSWilly Tu         getRelatedItems(asyncResp, *swInvPurpose);
1094d1bde9e5SKrzysztof Grobelny     });
1095af24660dSWilly Tu }
1096af24660dSWilly Tu 
1097f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryGet(
1098f5139334SEd Tanous     App& app, const crow::Request& req,
109945ca1b86SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1100f5139334SEd Tanous     const std::string& param)
1101f5139334SEd Tanous {
11023ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
110345ca1b86SEd Tanous     {
110445ca1b86SEd Tanous         return;
110545ca1b86SEd Tanous     }
1106f5139334SEd Tanous     std::shared_ptr<std::string> swId = std::make_shared<std::string>(param);
1107c711bf86SEd Tanous 
1108ef4c65b7SEd Tanous     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1109ef4c65b7SEd Tanous         "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
1110c711bf86SEd Tanous 
1111e99073f5SGeorge Liu     constexpr std::array<std::string_view, 1> interfaces = {
1112e99073f5SGeorge Liu         "xyz.openbmc_project.Software.Version"};
1113e99073f5SGeorge Liu     dbus::utility::getSubTree(
1114e99073f5SGeorge Liu         "/", 0, interfaces,
1115b9d36b47SEd Tanous         [asyncResp,
1116e99073f5SGeorge Liu          swId](const boost::system::error_code& ec,
1117b9d36b47SEd Tanous                const dbus::utility::MapperGetSubTreeResponse& subtree) {
111862598e31SEd Tanous         BMCWEB_LOG_DEBUG("doGet callback...");
11191abe55efSEd Tanous         if (ec)
11201abe55efSEd Tanous         {
1121f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
11226c4eb9deSJennifer Lee             return;
11236c4eb9deSJennifer Lee         }
11246c4eb9deSJennifer Lee 
11256913228dSAndrew Geissler         // Ensure we find our input swId, otherwise return an error
11266913228dSAndrew Geissler         bool found = false;
1127f5139334SEd Tanous         for (const std::pair<
1128f5139334SEd Tanous                  std::string,
1129f5139334SEd Tanous                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
1130002d39b4SEd Tanous                  obj : subtree)
11311abe55efSEd Tanous         {
113211ba3979SEd Tanous             if (!obj.first.ends_with(*swId))
11331abe55efSEd Tanous             {
1134acb7cfb4SJennifer Lee                 continue;
1135acb7cfb4SJennifer Lee             }
1136acb7cfb4SJennifer Lee 
113726f6976fSEd Tanous             if (obj.second.empty())
11381abe55efSEd Tanous             {
1139acb7cfb4SJennifer Lee                 continue;
1140acb7cfb4SJennifer Lee             }
11416c4eb9deSJennifer Lee 
11426913228dSAndrew Geissler             found = true;
1143eee0013eSWilly Tu             sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1144af24660dSWilly Tu             getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1145af24660dSWilly Tu                                *swId);
11466c4eb9deSJennifer Lee         }
11476913228dSAndrew Geissler         if (!found)
11486913228dSAndrew Geissler         {
114962598e31SEd Tanous             BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
11506913228dSAndrew Geissler             messages::resourceMissingAtURI(
1151ef4c65b7SEd Tanous                 asyncResp->res,
1152ef4c65b7SEd Tanous                 boost::urls::format(
1153f5139334SEd Tanous                     "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId));
11546913228dSAndrew Geissler             return;
11556913228dSAndrew Geissler         }
11564e68c45bSAyushi Smriti         asyncResp->res.jsonValue["@odata.type"] =
11574e68c45bSAyushi Smriti             "#SoftwareInventory.v1_1_0.SoftwareInventory";
11584e68c45bSAyushi Smriti         asyncResp->res.jsonValue["Name"] = "Software Inventory";
11594e68c45bSAyushi Smriti         asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
11603f8a743aSAppaRao Puli 
11613f8a743aSAppaRao Puli         asyncResp->res.jsonValue["Updateable"] = false;
1162eee0013eSWilly Tu         sw_util::getSwUpdatableStatus(asyncResp, swId);
1163e99073f5SGeorge Liu     });
1164f5139334SEd Tanous }
1165f5139334SEd Tanous 
1166f5139334SEd Tanous inline void requestRoutesUpdateService(App& app)
1167f5139334SEd Tanous {
1168f5139334SEd Tanous     BMCWEB_ROUTE(
1169f5139334SEd Tanous         app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
1170f5139334SEd Tanous         .privileges(redfish::privileges::postUpdateService)
1171f5139334SEd Tanous         .methods(boost::beast::http::verb::post)(std::bind_front(
1172f5139334SEd Tanous             handleUpdateServiceSimpleUpdateAction, std::ref(app)));
1173f5139334SEd Tanous 
1174f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1175f5139334SEd Tanous         .privileges(redfish::privileges::getSoftwareInventory)
1176f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
1177f5139334SEd Tanous             handleUpdateServiceFirmwareInventoryGet, std::ref(app)));
1178f5139334SEd Tanous 
1179f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1180f5139334SEd Tanous         .privileges(redfish::privileges::getUpdateService)
1181f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(
1182f5139334SEd Tanous             std::bind_front(handleUpdateServiceGet, std::ref(app)));
1183f5139334SEd Tanous 
1184f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1185f5139334SEd Tanous         .privileges(redfish::privileges::patchUpdateService)
1186f5139334SEd Tanous         .methods(boost::beast::http::verb::patch)(
1187f5139334SEd Tanous             std::bind_front(handleUpdateServicePatch, std::ref(app)));
1188f5139334SEd Tanous 
1189f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
1190f5139334SEd Tanous         .privileges(redfish::privileges::postUpdateService)
1191f5139334SEd Tanous         .methods(boost::beast::http::verb::post)(
1192f5139334SEd Tanous             std::bind_front(handleUpdateServicePost, std::ref(app)));
1193f5139334SEd Tanous 
1194f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
1195f5139334SEd Tanous         .privileges(redfish::privileges::getSoftwareInventoryCollection)
1196f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
1197f5139334SEd Tanous             handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app)));
11986c4eb9deSJennifer Lee }
1199729dae72SJennifer Lee 
1200729dae72SJennifer Lee } // namespace redfish
1201