xref: /openbmc/bmcweb/features/redfish/lib/update_service.hpp (revision ef93eab32e4e4157082ff16767408a610db2c190)
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>
43*ef93eab3SJagpal Singh Gill #include <memory>
447cb59f65SEd Tanous #include <optional>
457cb59f65SEd Tanous #include <string>
462b73119cSGeorge Liu #include <string_view>
47*ef93eab3SJagpal Singh Gill #include <vector>
482b73119cSGeorge Liu 
491abe55efSEd Tanous namespace redfish
501abe55efSEd Tanous {
5127826b5fSEd Tanous 
520e7de46fSAndrew Geissler // Match signals added on software path
53cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
5459d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
55cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
5659d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
570e7de46fSAndrew Geissler // Only allow one update at a time
58cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
590e7de46fSAndrew Geissler static bool fwUpdateInProgress = false;
6086adcd6dSAndrew Geissler // Timer for software available
61cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
62271584abSEd Tanous static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
6386adcd6dSAndrew Geissler 
64df254f2cSEd Tanous inline void cleanUp()
6586adcd6dSAndrew Geissler {
6686adcd6dSAndrew Geissler     fwUpdateInProgress = false;
6786adcd6dSAndrew Geissler     fwUpdateMatcher = nullptr;
684cde5d90SJames Feist     fwUpdateErrorMatcher = nullptr;
6986adcd6dSAndrew Geissler }
70df254f2cSEd Tanous 
71df254f2cSEd Tanous inline void activateImage(const std::string& objPath,
7286adcd6dSAndrew Geissler                           const std::string& service)
7386adcd6dSAndrew Geissler {
7462598e31SEd Tanous     BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
759ae226faSGeorge Liu     sdbusplus::asio::setProperty(
769ae226faSGeorge Liu         *crow::connections::systemBus, service, objPath,
779ae226faSGeorge Liu         "xyz.openbmc_project.Software.Activation", "RequestedActivation",
789ae226faSGeorge Liu         "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
798b24275dSEd Tanous         [](const boost::system::error_code& ec) {
808b24275dSEd Tanous         if (ec)
8186adcd6dSAndrew Geissler         {
8262598e31SEd Tanous             BMCWEB_LOG_DEBUG("error_code = {}", ec);
8362598e31SEd Tanous             BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
8486adcd6dSAndrew Geissler         }
859ae226faSGeorge Liu     });
8686adcd6dSAndrew Geissler }
870554c984SAndrew Geissler 
880554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
890554c984SAndrew Geissler // then no asyncResp updates will occur
908d1b46d7Szhanghch05 static void
918d1b46d7Szhanghch05     softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
9259d494eeSPatrick Williams                            sdbusplus::message_t& m, task::Payload&& payload)
9386adcd6dSAndrew Geissler {
9480f79a40SMichael Shen     dbus::utility::DBusInterfacesMap interfacesProperties;
9586adcd6dSAndrew Geissler 
9686adcd6dSAndrew Geissler     sdbusplus::message::object_path objPath;
9786adcd6dSAndrew Geissler 
9886adcd6dSAndrew Geissler     m.read(objPath, interfacesProperties);
9986adcd6dSAndrew Geissler 
10062598e31SEd Tanous     BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
101e3eb3d63SEd Tanous     for (const auto& interface : interfacesProperties)
10286adcd6dSAndrew Geissler     {
10362598e31SEd Tanous         BMCWEB_LOG_DEBUG("interface = {}", interface.first);
10486adcd6dSAndrew Geissler 
10586adcd6dSAndrew Geissler         if (interface.first == "xyz.openbmc_project.Software.Activation")
10686adcd6dSAndrew Geissler         {
10786adcd6dSAndrew Geissler             // Retrieve service and activate
1082b73119cSGeorge Liu             constexpr std::array<std::string_view, 1> interfaces = {
1092b73119cSGeorge Liu                 "xyz.openbmc_project.Software.Activation"};
1102b73119cSGeorge Liu             dbus::utility::getDbusObject(
1112b73119cSGeorge Liu                 objPath.str, interfaces,
112a3e65892SEd Tanous                 [objPath, asyncResp, payload(std::move(payload))](
1138b24275dSEd Tanous                     const boost::system::error_code& ec,
114a3e65892SEd Tanous                     const std::vector<
115a3e65892SEd Tanous                         std::pair<std::string, std::vector<std::string>>>&
116a3e65892SEd Tanous                         objInfo) mutable {
1178b24275dSEd Tanous                 if (ec)
11886adcd6dSAndrew Geissler                 {
11962598e31SEd Tanous                     BMCWEB_LOG_DEBUG("error_code = {}", ec);
12062598e31SEd Tanous                     BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
1210554c984SAndrew Geissler                     if (asyncResp)
1220554c984SAndrew Geissler                     {
12386adcd6dSAndrew Geissler                         messages::internalError(asyncResp->res);
1240554c984SAndrew Geissler                     }
12586adcd6dSAndrew Geissler                     cleanUp();
12686adcd6dSAndrew Geissler                     return;
12786adcd6dSAndrew Geissler                 }
12886adcd6dSAndrew Geissler                 // Ensure we only got one service back
12986adcd6dSAndrew Geissler                 if (objInfo.size() != 1)
13086adcd6dSAndrew Geissler                 {
13162598e31SEd Tanous                     BMCWEB_LOG_ERROR("Invalid Object Size {}", objInfo.size());
1320554c984SAndrew Geissler                     if (asyncResp)
1330554c984SAndrew Geissler                     {
13486adcd6dSAndrew Geissler                         messages::internalError(asyncResp->res);
1350554c984SAndrew Geissler                     }
13686adcd6dSAndrew Geissler                     cleanUp();
13786adcd6dSAndrew Geissler                     return;
13886adcd6dSAndrew Geissler                 }
13986adcd6dSAndrew Geissler                 // cancel timer only when
14086adcd6dSAndrew Geissler                 // xyz.openbmc_project.Software.Activation interface
14186adcd6dSAndrew Geissler                 // is added
14286adcd6dSAndrew Geissler                 fwAvailableTimer = nullptr;
14386adcd6dSAndrew Geissler 
14486adcd6dSAndrew Geissler                 activateImage(objPath.str, objInfo[0].first);
1450554c984SAndrew Geissler                 if (asyncResp)
1460554c984SAndrew Geissler                 {
14732898ceaSJames Feist                     std::shared_ptr<task::TaskData> task =
14832898ceaSJames Feist                         task::TaskData::createTask(
1498b24275dSEd Tanous                             [](const boost::system::error_code& ec2,
15059d494eeSPatrick Williams                                sdbusplus::message_t& msg,
1511214b7e7SGunnar Mills                                const std::shared_ptr<task::TaskData>&
1521214b7e7SGunnar Mills                                    taskData) {
1538b24275dSEd Tanous                         if (ec2)
15432898ceaSJames Feist                         {
15532898ceaSJames Feist                             return task::completed;
15632898ceaSJames Feist                         }
15732898ceaSJames Feist 
15832898ceaSJames Feist                         std::string iface;
159b9d36b47SEd Tanous                         dbus::utility::DBusPropertiesMap values;
160fd9ab9e1SJames Feist 
161002d39b4SEd Tanous                         std::string index = std::to_string(taskData->index);
16232898ceaSJames Feist                         msg.read(iface, values);
163fd9ab9e1SJames Feist 
164002d39b4SEd Tanous                         if (iface == "xyz.openbmc_project.Software.Activation")
165fd9ab9e1SJames Feist                         {
1660fb5b505SGayathri Leburu                             const std::string* state = nullptr;
167b9d36b47SEd Tanous                             for (const auto& property : values)
16832898ceaSJames Feist                             {
169b9d36b47SEd Tanous                                 if (property.first == "Activation")
170b9d36b47SEd Tanous                                 {
1710fb5b505SGayathri Leburu                                     state = std::get_if<std::string>(
172b9d36b47SEd Tanous                                         &property.second);
1730fb5b505SGayathri Leburu                                     if (state == nullptr)
174b9d36b47SEd Tanous                                     {
175002d39b4SEd Tanous                                         taskData->messages.emplace_back(
176002d39b4SEd Tanous                                             messages::internalError());
177b9d36b47SEd Tanous                                         return task::completed;
178b9d36b47SEd Tanous                                     }
179b9d36b47SEd Tanous                                 }
180b9d36b47SEd Tanous                             }
18132898ceaSJames Feist 
18232898ceaSJames Feist                             if (state == nullptr)
18332898ceaSJames Feist                             {
184b9d36b47SEd Tanous                                 return !task::completed;
18532898ceaSJames Feist                             }
18632898ceaSJames Feist 
18711ba3979SEd Tanous                             if (state->ends_with("Invalid") ||
18811ba3979SEd Tanous                                 state->ends_with("Failed"))
18932898ceaSJames Feist                             {
19032898ceaSJames Feist                                 taskData->state = "Exception";
19132898ceaSJames Feist                                 taskData->status = "Warning";
19232898ceaSJames Feist                                 taskData->messages.emplace_back(
193e5d5006bSJames Feist                                     messages::taskAborted(index));
19432898ceaSJames Feist                                 return task::completed;
19532898ceaSJames Feist                             }
19632898ceaSJames Feist 
19711ba3979SEd Tanous                             if (state->ends_with("Staged"))
19832898ceaSJames Feist                             {
199fd9ab9e1SJames Feist                                 taskData->state = "Stopping";
200fd9ab9e1SJames Feist                                 taskData->messages.emplace_back(
201fd9ab9e1SJames Feist                                     messages::taskPaused(index));
202fd9ab9e1SJames Feist 
203fd9ab9e1SJames Feist                                 // its staged, set a long timer to
204fd9ab9e1SJames Feist                                 // allow them time to complete the
205fd9ab9e1SJames Feist                                 // update (probably cycle the
206fd9ab9e1SJames Feist                                 // system) if this expires then
2078ece0e45SEd Tanous                                 // task will be canceled
208002d39b4SEd Tanous                                 taskData->extendTimer(std::chrono::hours(5));
20932898ceaSJames Feist                                 return !task::completed;
21032898ceaSJames Feist                             }
21132898ceaSJames Feist 
21211ba3979SEd Tanous                             if (state->ends_with("Active"))
21332898ceaSJames Feist                             {
21432898ceaSJames Feist                                 taskData->messages.emplace_back(
215002d39b4SEd Tanous                                     messages::taskCompletedOK(index));
21632898ceaSJames Feist                                 taskData->state = "Completed";
21732898ceaSJames Feist                                 return task::completed;
21832898ceaSJames Feist                             }
219fd9ab9e1SJames Feist                         }
2200fda0f12SGeorge Liu                         else if (
2210fda0f12SGeorge Liu                             iface ==
2220fda0f12SGeorge Liu                             "xyz.openbmc_project.Software.ActivationProgress")
223fd9ab9e1SJames Feist                         {
224b9d36b47SEd Tanous                             const uint8_t* progress = nullptr;
225b9d36b47SEd Tanous                             for (const auto& property : values)
226fd9ab9e1SJames Feist                             {
227b9d36b47SEd Tanous                                 if (property.first == "Progress")
228b9d36b47SEd Tanous                                 {
2290fb5b505SGayathri Leburu                                     progress =
2300fb5b505SGayathri Leburu                                         std::get_if<uint8_t>(&property.second);
2310fb5b505SGayathri Leburu                                     if (progress == nullptr)
232b9d36b47SEd Tanous                                     {
233002d39b4SEd Tanous                                         taskData->messages.emplace_back(
234002d39b4SEd Tanous                                             messages::internalError());
235b9d36b47SEd Tanous                                         return task::completed;
236fd9ab9e1SJames Feist                                     }
237b9d36b47SEd Tanous                                 }
238b9d36b47SEd Tanous                             }
239fd9ab9e1SJames Feist 
240fd9ab9e1SJames Feist                             if (progress == nullptr)
241fd9ab9e1SJames Feist                             {
242b9d36b47SEd Tanous                                 return !task::completed;
243fd9ab9e1SJames Feist                             }
2440fb5b505SGayathri Leburu                             taskData->percentComplete = *progress;
245fd9ab9e1SJames Feist                             taskData->messages.emplace_back(
2460fb5b505SGayathri Leburu                                 messages::taskProgressChanged(index,
2470fb5b505SGayathri Leburu                                                               *progress));
248fd9ab9e1SJames Feist 
249fd9ab9e1SJames Feist                             // if we're getting status updates it's
250fd9ab9e1SJames Feist                             // still alive, update timer
251002d39b4SEd Tanous                             taskData->extendTimer(std::chrono::minutes(5));
252fd9ab9e1SJames Feist                         }
25332898ceaSJames Feist 
25432898ceaSJames Feist                         // as firmware update often results in a
25532898ceaSJames Feist                         // reboot, the task  may never "complete"
25632898ceaSJames Feist                         // unless it is an error
25732898ceaSJames Feist 
25832898ceaSJames Feist                         return !task::completed;
25932898ceaSJames Feist                     },
2600fda0f12SGeorge Liu                             "type='signal',interface='org.freedesktop.DBus.Properties',"
261fd9ab9e1SJames Feist                             "member='PropertiesChanged',path='" +
26232898ceaSJames Feist                                 objPath.str + "'");
26332898ceaSJames Feist                     task->startTimer(std::chrono::minutes(5));
26432898ceaSJames Feist                     task->populateResp(asyncResp->res);
265a3e65892SEd Tanous                     task->payload.emplace(std::move(payload));
2660554c984SAndrew Geissler                 }
26786adcd6dSAndrew Geissler                 fwUpdateInProgress = false;
2682b73119cSGeorge Liu             });
26962bafc01SPatrick Williams 
27062bafc01SPatrick Williams             break;
27186adcd6dSAndrew Geissler         }
27286adcd6dSAndrew Geissler     }
27386adcd6dSAndrew Geissler }
27486adcd6dSAndrew Geissler 
2758549b951SMyung Bae inline void afterAvailbleTimerAsyncWait(
2768549b951SMyung Bae     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2778549b951SMyung Bae     const boost::system::error_code& ec)
2788549b951SMyung Bae {
2798549b951SMyung Bae     cleanUp();
2808549b951SMyung Bae     if (ec == boost::asio::error::operation_aborted)
2818549b951SMyung Bae     {
2828549b951SMyung Bae         // expected, we were canceled before the timer completed.
2838549b951SMyung Bae         return;
2848549b951SMyung Bae     }
2858549b951SMyung Bae     BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
2868549b951SMyung Bae     BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
2878549b951SMyung Bae     if (ec)
2888549b951SMyung Bae     {
2898549b951SMyung Bae         BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
2908549b951SMyung Bae         return;
2918549b951SMyung Bae     }
2928549b951SMyung Bae     if (asyncResp)
2938549b951SMyung Bae     {
2948549b951SMyung Bae         redfish::messages::internalError(asyncResp->res);
2958549b951SMyung Bae     }
2968549b951SMyung Bae }
2978549b951SMyung Bae 
2988549b951SMyung Bae inline void
2998549b951SMyung Bae     handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3008549b951SMyung Bae                           const std::string& url, const std::string& type)
3018549b951SMyung Bae {
3028549b951SMyung Bae     if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
3038549b951SMyung Bae     {
3048549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3058549b951SMyung Bae                                          "Invalid archive");
3068549b951SMyung Bae     }
3078549b951SMyung Bae     else if (type ==
3088549b951SMyung Bae              "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
3098549b951SMyung Bae     {
3108549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3118549b951SMyung Bae                                          "Invalid manifest");
3128549b951SMyung Bae     }
3138549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
3148549b951SMyung Bae     {
3158549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3168549b951SMyung Bae                                          "Invalid image format");
3178549b951SMyung Bae     }
3188549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
3198549b951SMyung Bae     {
3208549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3218549b951SMyung Bae                                          "Image version already exists");
3228549b951SMyung Bae 
3238549b951SMyung Bae         redfish::messages::resourceAlreadyExists(
3248549b951SMyung Bae             asyncResp->res, "UpdateService", "Version", "uploaded version");
3258549b951SMyung Bae     }
3268549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
3278549b951SMyung Bae     {
3288549b951SMyung Bae         redfish::messages::resourceExhaustion(asyncResp->res, url);
3298549b951SMyung Bae     }
3304034a652SMyung Bae     else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
3318549b951SMyung Bae     {
3324034a652SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3334034a652SMyung Bae                                          "Incompatible image version");
3344034a652SMyung Bae     }
3354034a652SMyung Bae     else if (type ==
3364034a652SMyung Bae              "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
3374034a652SMyung Bae     {
3384034a652SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3394034a652SMyung Bae                                          "Update Access Key Expired");
3404034a652SMyung Bae     }
3414034a652SMyung Bae     else if (type ==
3424034a652SMyung Bae              "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
3434034a652SMyung Bae     {
3444034a652SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3454034a652SMyung Bae                                          "Invalid image signature");
3464034a652SMyung Bae     }
3474034a652SMyung Bae     else if (type ==
3484034a652SMyung Bae                  "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
3494034a652SMyung Bae              type == "xyz.openbmc_project.Software.Version.Error.HostFile")
3504034a652SMyung Bae     {
3514034a652SMyung Bae         BMCWEB_LOG_ERROR("Software Image Error type={}", type);
3528549b951SMyung Bae         redfish::messages::internalError(asyncResp->res);
3538549b951SMyung Bae     }
3544034a652SMyung Bae     else
3554034a652SMyung Bae     {
3564034a652SMyung Bae         // Unrelated error types. Ignored
3574034a652SMyung Bae         BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
3584034a652SMyung Bae         return;
3594034a652SMyung Bae     }
3604034a652SMyung Bae     // Clear the timer
3614034a652SMyung Bae     fwAvailableTimer = nullptr;
3628549b951SMyung Bae }
3638549b951SMyung Bae 
3648549b951SMyung Bae inline void
3658549b951SMyung Bae     afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3668549b951SMyung Bae                             const std::string& url, sdbusplus::message_t& m)
3678549b951SMyung Bae {
36880f79a40SMichael Shen     dbus::utility::DBusInterfacesMap interfacesProperties;
3698549b951SMyung Bae     sdbusplus::message::object_path objPath;
3708549b951SMyung Bae     m.read(objPath, interfacesProperties);
3718549b951SMyung Bae     BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
3728549b951SMyung Bae     for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
3738549b951SMyung Bae              interface : interfacesProperties)
3748549b951SMyung Bae     {
3758549b951SMyung Bae         if (interface.first == "xyz.openbmc_project.Logging.Entry")
3768549b951SMyung Bae         {
3778549b951SMyung Bae             for (const std::pair<std::string, dbus::utility::DbusVariantType>&
3788549b951SMyung Bae                      value : interface.second)
3798549b951SMyung Bae             {
3808549b951SMyung Bae                 if (value.first != "Message")
3818549b951SMyung Bae                 {
3828549b951SMyung Bae                     continue;
3838549b951SMyung Bae                 }
3848549b951SMyung Bae                 const std::string* type =
3858549b951SMyung Bae                     std::get_if<std::string>(&value.second);
3868549b951SMyung Bae                 if (type == nullptr)
3878549b951SMyung Bae                 {
3888549b951SMyung Bae                     // if this was our message, timeout will cover it
3898549b951SMyung Bae                     return;
3908549b951SMyung Bae                 }
3918549b951SMyung Bae                 handleUpdateErrorType(asyncResp, url, *type);
3928549b951SMyung Bae             }
3938549b951SMyung Bae         }
3948549b951SMyung Bae     }
3958549b951SMyung Bae }
3968549b951SMyung Bae 
3970554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
3980554c984SAndrew Geissler // then no asyncResp updates will occur
399f5139334SEd Tanous inline void monitorForSoftwareAvailable(
4008d1b46d7Szhanghch05     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
4018d1b46d7Szhanghch05     const crow::Request& req, const std::string& url,
4025d138943SGunnar Mills     int timeoutTimeSeconds = 25)
40386adcd6dSAndrew Geissler {
40486adcd6dSAndrew Geissler     // Only allow one FW update at a time
405e05aec50SEd Tanous     if (fwUpdateInProgress)
40686adcd6dSAndrew Geissler     {
4070554c984SAndrew Geissler         if (asyncResp)
4080554c984SAndrew Geissler         {
40986adcd6dSAndrew Geissler             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
4100554c984SAndrew Geissler         }
41186adcd6dSAndrew Geissler         return;
41286adcd6dSAndrew Geissler     }
41386adcd6dSAndrew Geissler 
4148e8245dbSEd Tanous     if (req.ioService == nullptr)
4158e8245dbSEd Tanous     {
4168e8245dbSEd Tanous         messages::internalError(asyncResp->res);
4178e8245dbSEd Tanous         return;
4188e8245dbSEd Tanous     }
4198e8245dbSEd Tanous 
4200554c984SAndrew Geissler     fwAvailableTimer =
421271584abSEd Tanous         std::make_unique<boost::asio::steady_timer>(*req.ioService);
42286adcd6dSAndrew Geissler 
423271584abSEd Tanous     fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
42486adcd6dSAndrew Geissler 
42586adcd6dSAndrew Geissler     fwAvailableTimer->async_wait(
4268549b951SMyung Bae         std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
4278549b951SMyung Bae 
428a3e65892SEd Tanous     task::Payload payload(req);
42959d494eeSPatrick Williams     auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
43062598e31SEd Tanous         BMCWEB_LOG_DEBUG("Match fired");
431a3e65892SEd Tanous         softwareInterfaceAdded(asyncResp, m, std::move(payload));
43286adcd6dSAndrew Geissler     };
43386adcd6dSAndrew Geissler 
43486adcd6dSAndrew Geissler     fwUpdateInProgress = true;
43586adcd6dSAndrew Geissler 
43659d494eeSPatrick Williams     fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
43786adcd6dSAndrew Geissler         *crow::connections::systemBus,
43886adcd6dSAndrew Geissler         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
43986adcd6dSAndrew Geissler         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
44086adcd6dSAndrew Geissler         callback);
4414cde5d90SJames Feist 
44259d494eeSPatrick Williams     fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
4434cde5d90SJames Feist         *crow::connections::systemBus,
444e1cc4828SBrian Ma         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
445e1cc4828SBrian Ma         "member='InterfacesAdded',"
446e1cc4828SBrian Ma         "path='/xyz/openbmc_project/logging'",
4478549b951SMyung Bae         std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
44886adcd6dSAndrew Geissler }
449729dae72SJennifer Lee 
450757178a5SEd Tanous inline std::optional<boost::urls::url>
451757178a5SEd Tanous     parseSimpleUpdateUrl(std::string imageURI,
452f86bcc87SEd Tanous                          std::optional<std::string> transferProtocol,
453f86bcc87SEd Tanous                          crow::Response& res)
454f86bcc87SEd Tanous {
455f86bcc87SEd Tanous     if (imageURI.find("://") == std::string::npos)
456f86bcc87SEd Tanous     {
457f86bcc87SEd Tanous         if (imageURI.starts_with("/"))
458f86bcc87SEd Tanous         {
459f86bcc87SEd Tanous             messages::actionParameterValueTypeError(
460f86bcc87SEd Tanous                 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
461f86bcc87SEd Tanous             return std::nullopt;
462f86bcc87SEd Tanous         }
463f86bcc87SEd Tanous         if (!transferProtocol)
464f86bcc87SEd Tanous         {
465f86bcc87SEd Tanous             messages::actionParameterValueTypeError(
466f86bcc87SEd Tanous                 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
467f86bcc87SEd Tanous             return std::nullopt;
468f86bcc87SEd Tanous         }
469e5cf777eSEd Tanous         // OpenBMC currently only supports TFTP or HTTPS
470757178a5SEd Tanous         if (*transferProtocol == "TFTP")
471757178a5SEd Tanous         {
472757178a5SEd Tanous             imageURI = "tftp://" + imageURI;
473757178a5SEd Tanous         }
474e5cf777eSEd Tanous         else if (*transferProtocol == "HTTPS")
475e5cf777eSEd Tanous         {
476e5cf777eSEd Tanous             imageURI = "https://" + imageURI;
477e5cf777eSEd Tanous         }
478757178a5SEd Tanous         else
479f86bcc87SEd Tanous         {
480f86bcc87SEd Tanous             messages::actionParameterNotSupported(res, "TransferProtocol",
481f86bcc87SEd Tanous                                                   *transferProtocol);
482f86bcc87SEd Tanous             BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
483f86bcc87SEd Tanous                              *transferProtocol);
484f86bcc87SEd Tanous             return std::nullopt;
485f86bcc87SEd Tanous         }
486f86bcc87SEd Tanous     }
487f86bcc87SEd Tanous 
488f86bcc87SEd Tanous     boost::system::result<boost::urls::url> url =
489f86bcc87SEd Tanous         boost::urls::parse_absolute_uri(imageURI);
490f86bcc87SEd Tanous     if (!url)
491f86bcc87SEd Tanous     {
492f86bcc87SEd Tanous         messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
493f86bcc87SEd Tanous                                                 "UpdateService.SimpleUpdate");
494f86bcc87SEd Tanous 
495f86bcc87SEd Tanous         return std::nullopt;
496f86bcc87SEd Tanous     }
497f86bcc87SEd Tanous     url->normalize();
498f86bcc87SEd Tanous 
499757178a5SEd Tanous     if (url->scheme() == "tftp")
500757178a5SEd Tanous     {
501757178a5SEd Tanous         if (url->encoded_path().size() < 2)
502757178a5SEd Tanous         {
503757178a5SEd Tanous             messages::actionParameterNotSupported(res, "ImageURI",
504757178a5SEd Tanous                                                   url->buffer());
505757178a5SEd Tanous             return std::nullopt;
506757178a5SEd Tanous         }
507757178a5SEd Tanous     }
508e5cf777eSEd Tanous     else if (url->scheme() == "https")
509e5cf777eSEd Tanous     {
510e5cf777eSEd Tanous         // Empty paths default to "/"
511e5cf777eSEd Tanous         if (url->encoded_path().empty())
512e5cf777eSEd Tanous         {
513e5cf777eSEd Tanous             url->set_encoded_path("/");
514e5cf777eSEd Tanous         }
515e5cf777eSEd Tanous     }
516757178a5SEd Tanous     else
517f86bcc87SEd Tanous     {
518f86bcc87SEd Tanous         messages::actionParameterNotSupported(res, "ImageURI", imageURI);
519f86bcc87SEd Tanous         return std::nullopt;
520f86bcc87SEd Tanous     }
521757178a5SEd Tanous 
522757178a5SEd Tanous     if (url->encoded_path().empty())
523f86bcc87SEd Tanous     {
524757178a5SEd Tanous         messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
525757178a5SEd Tanous                                                 "UpdateService.SimpleUpdate");
526f86bcc87SEd Tanous         return std::nullopt;
527f86bcc87SEd Tanous     }
528757178a5SEd Tanous 
529757178a5SEd Tanous     return *url;
530f86bcc87SEd Tanous }
531f86bcc87SEd Tanous 
532e5cf777eSEd Tanous inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
533e5cf777eSEd Tanous                           const boost::urls::url_view_base& url)
534e5cf777eSEd Tanous {
535e5cf777eSEd Tanous     messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
536e5cf777eSEd Tanous                                           url.buffer());
537e5cf777eSEd Tanous }
538e5cf777eSEd Tanous 
5396b0f66bdSEd Tanous inline void doTftpUpdate(const crow::Request& req,
5406b0f66bdSEd Tanous                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
541757178a5SEd Tanous                          const boost::urls::url_view_base& url)
5426b0f66bdSEd Tanous {
543c72503f3SEd Tanous     if (!BMCWEB_INSECURE_TFTP_UPDATE)
544c72503f3SEd Tanous     {
5456b0f66bdSEd Tanous         messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
546757178a5SEd Tanous                                               url.buffer());
5476b0f66bdSEd Tanous         return;
548c72503f3SEd Tanous     }
549757178a5SEd Tanous 
550757178a5SEd Tanous     std::string path(url.encoded_path());
551757178a5SEd Tanous     if (path.size() < 2)
552757178a5SEd Tanous     {
553757178a5SEd Tanous         messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
554757178a5SEd Tanous                                               url.buffer());
555757178a5SEd Tanous         return;
556757178a5SEd Tanous     }
557757178a5SEd Tanous     // TFTP expects a path without a /
558757178a5SEd Tanous     path.erase(0, 1);
559757178a5SEd Tanous     std::string host(url.encoded_host_and_port());
560757178a5SEd Tanous     BMCWEB_LOG_DEBUG("Server: {} File: {}", host, path);
5616b0f66bdSEd Tanous 
5626b0f66bdSEd Tanous     // Setup callback for when new software detected
5636b0f66bdSEd Tanous     // Give TFTP 10 minutes to complete
5646b0f66bdSEd Tanous     monitorForSoftwareAvailable(
5656b0f66bdSEd Tanous         asyncResp, req,
5666b0f66bdSEd Tanous         "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate", 600);
5676b0f66bdSEd Tanous 
5686b0f66bdSEd Tanous     // TFTP can take up to 10 minutes depending on image size and
5696b0f66bdSEd Tanous     // connection speed. Return to caller as soon as the TFTP operation
5706b0f66bdSEd Tanous     // has been started. The callback above will ensure the activate
5716b0f66bdSEd Tanous     // is started once the download has completed
5726b0f66bdSEd Tanous     redfish::messages::success(asyncResp->res);
5736b0f66bdSEd Tanous 
5746b0f66bdSEd Tanous     // Call TFTP service
5756b0f66bdSEd Tanous     crow::connections::systemBus->async_method_call(
5766b0f66bdSEd Tanous         [](const boost::system::error_code& ec) {
5776b0f66bdSEd Tanous         if (ec)
5786b0f66bdSEd Tanous         {
5796b0f66bdSEd Tanous             // messages::internalError(asyncResp->res);
5806b0f66bdSEd Tanous             cleanUp();
5816b0f66bdSEd Tanous             BMCWEB_LOG_DEBUG("error_code = {}", ec);
5826b0f66bdSEd Tanous             BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
5836b0f66bdSEd Tanous         }
5846b0f66bdSEd Tanous         else
5856b0f66bdSEd Tanous         {
5866b0f66bdSEd Tanous             BMCWEB_LOG_DEBUG("Call to DownloaViaTFTP Success");
5876b0f66bdSEd Tanous         }
5886b0f66bdSEd Tanous     },
5896b0f66bdSEd Tanous         "xyz.openbmc_project.Software.Download",
5906b0f66bdSEd Tanous         "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
591757178a5SEd Tanous         "DownloadViaTFTP", path, host);
5926b0f66bdSEd Tanous }
5936b0f66bdSEd Tanous 
594f5139334SEd Tanous inline void handleUpdateServiceSimpleUpdateAction(
595f5139334SEd Tanous     crow::App& app, const crow::Request& req,
596f5139334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
5970554c984SAndrew Geissler {
5983ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
59945ca1b86SEd Tanous     {
60045ca1b86SEd Tanous         return;
60145ca1b86SEd Tanous     }
60245ca1b86SEd Tanous 
6030554c984SAndrew Geissler     std::optional<std::string> transferProtocol;
6040554c984SAndrew Geissler     std::string imageURI;
6050554c984SAndrew Geissler 
60662598e31SEd Tanous     BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
6070554c984SAndrew Geissler 
6080554c984SAndrew Geissler     // User can pass in both TransferProtocol and ImageURI parameters or
6094e0453b1SGunnar Mills     // they can pass in just the ImageURI with the transfer protocol
6104e0453b1SGunnar Mills     // embedded within it.
6110554c984SAndrew Geissler     // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
6120554c984SAndrew Geissler     // 2) ImageURI:tftp://1.1.1.1/myfile.bin
6130554c984SAndrew Geissler 
614002d39b4SEd Tanous     if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
615002d39b4SEd Tanous                                    transferProtocol, "ImageURI", imageURI))
6160554c984SAndrew Geissler     {
61762598e31SEd Tanous         BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
6180554c984SAndrew Geissler         return;
6190554c984SAndrew Geissler     }
620f5139334SEd Tanous 
621757178a5SEd Tanous     std::optional<boost::urls::url> url =
622757178a5SEd Tanous         parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res);
623757178a5SEd Tanous     if (!url)
6240554c984SAndrew Geissler     {
6250554c984SAndrew Geissler         return;
6260554c984SAndrew Geissler     }
627757178a5SEd Tanous     if (url->scheme() == "tftp")
628757178a5SEd Tanous     {
629757178a5SEd Tanous         doTftpUpdate(req, asyncResp, *url);
630757178a5SEd Tanous     }
631e5cf777eSEd Tanous     else if (url->scheme() == "https")
632e5cf777eSEd Tanous     {
633e5cf777eSEd Tanous         doHttpsUpdate(asyncResp, *url);
634e5cf777eSEd Tanous     }
635757178a5SEd Tanous     else
636757178a5SEd Tanous     {
637757178a5SEd Tanous         messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
638757178a5SEd Tanous                                               url->buffer());
639757178a5SEd Tanous         return;
640757178a5SEd Tanous     }
6410554c984SAndrew Geissler 
64262598e31SEd Tanous     BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
643729dae72SJennifer Lee }
644729dae72SJennifer Lee 
6450ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body)
6460ed80c8cSGeorge Liu {
6472c6ffdb0SEd Tanous     std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
6482c6ffdb0SEd Tanous 
64962598e31SEd Tanous     BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
6500ed80c8cSGeorge Liu     std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
6510ed80c8cSGeorge Liu                                     std::ofstream::trunc);
6520ed80c8cSGeorge Liu     // set the permission of the file to 640
65389492a15SPatrick Williams     std::filesystem::perms permission = std::filesystem::perms::owner_read |
65489492a15SPatrick Williams                                         std::filesystem::perms::group_read;
6550ed80c8cSGeorge Liu     std::filesystem::permissions(filepath, permission);
6560ed80c8cSGeorge Liu     out << body;
6570ed80c8cSGeorge Liu 
6580ed80c8cSGeorge Liu     if (out.bad())
6590ed80c8cSGeorge Liu     {
6600ed80c8cSGeorge Liu         messages::internalError(res);
6610ed80c8cSGeorge Liu         cleanUp();
6620ed80c8cSGeorge Liu     }
6630ed80c8cSGeorge Liu }
6640ed80c8cSGeorge Liu 
6650ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
6660ed80c8cSGeorge Liu                          const std::string& applyTime)
6670ed80c8cSGeorge Liu {
6680ed80c8cSGeorge Liu     std::string applyTimeNewVal;
6690ed80c8cSGeorge Liu     if (applyTime == "Immediate")
6700ed80c8cSGeorge Liu     {
6710ed80c8cSGeorge Liu         applyTimeNewVal =
6720ed80c8cSGeorge Liu             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
6730ed80c8cSGeorge Liu     }
6740ed80c8cSGeorge Liu     else if (applyTime == "OnReset")
6750ed80c8cSGeorge Liu     {
6760ed80c8cSGeorge Liu         applyTimeNewVal =
6770ed80c8cSGeorge Liu             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
6780ed80c8cSGeorge Liu     }
6790ed80c8cSGeorge Liu     else
6800ed80c8cSGeorge Liu     {
68162598e31SEd Tanous         BMCWEB_LOG_INFO(
68262598e31SEd Tanous             "ApplyTime value is not in the list of acceptable values");
6830ed80c8cSGeorge Liu         messages::propertyValueNotInList(asyncResp->res, applyTime,
6840ed80c8cSGeorge Liu                                          "ApplyTime");
6850ed80c8cSGeorge Liu         return;
6860ed80c8cSGeorge Liu     }
6870ed80c8cSGeorge Liu 
688d02aad39SEd Tanous     setDbusProperty(asyncResp, "xyz.openbmc_project.Settings",
689d02aad39SEd Tanous                     sdbusplus::message::object_path(
690d02aad39SEd Tanous                         "/xyz/openbmc_project/software/apply_time"),
691d02aad39SEd Tanous                     "xyz.openbmc_project.Software.ApplyTime",
692d02aad39SEd Tanous                     "RequestedApplyTime", "ApplyTime", applyTimeNewVal);
6930ed80c8cSGeorge Liu }
6940ed80c8cSGeorge Liu 
695*ef93eab3SJagpal Singh Gill struct MultiPartUpdateParameters
6960ed80c8cSGeorge Liu {
697*ef93eab3SJagpal Singh Gill     std::optional<std::string> applyTime;
698*ef93eab3SJagpal Singh Gill     std::string uploadData;
699*ef93eab3SJagpal Singh Gill     std::vector<boost::urls::url> targets;
700*ef93eab3SJagpal Singh Gill };
701*ef93eab3SJagpal Singh Gill 
702*ef93eab3SJagpal Singh Gill inline std::optional<MultiPartUpdateParameters>
703*ef93eab3SJagpal Singh Gill     extractMultipartUpdateParameters(
704*ef93eab3SJagpal Singh Gill         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
705*ef93eab3SJagpal Singh Gill         MultipartParser parser)
706*ef93eab3SJagpal Singh Gill {
707*ef93eab3SJagpal Singh Gill     MultiPartUpdateParameters multiRet;
708*ef93eab3SJagpal Singh Gill     for (FormPart& formpart : parser.mime_fields)
7090ed80c8cSGeorge Liu     {
7100ed80c8cSGeorge Liu         boost::beast::http::fields::const_iterator it =
7110ed80c8cSGeorge Liu             formpart.fields.find("Content-Disposition");
7120ed80c8cSGeorge Liu         if (it == formpart.fields.end())
7130ed80c8cSGeorge Liu         {
71462598e31SEd Tanous             BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
715*ef93eab3SJagpal Singh Gill             return std::nullopt;
7160ed80c8cSGeorge Liu         }
71762598e31SEd Tanous         BMCWEB_LOG_INFO("Parsing value {}", it->value());
7180ed80c8cSGeorge Liu 
7190ed80c8cSGeorge Liu         // The construction parameters of param_list must start with `;`
7200ed80c8cSGeorge Liu         size_t index = it->value().find(';');
7210ed80c8cSGeorge Liu         if (index == std::string::npos)
7220ed80c8cSGeorge Liu         {
7230ed80c8cSGeorge Liu             continue;
7240ed80c8cSGeorge Liu         }
7250ed80c8cSGeorge Liu 
72689492a15SPatrick Williams         for (const auto& param :
7270ed80c8cSGeorge Liu              boost::beast::http::param_list{it->value().substr(index)})
7280ed80c8cSGeorge Liu         {
7290ed80c8cSGeorge Liu             if (param.first != "name" || param.second.empty())
7300ed80c8cSGeorge Liu             {
7310ed80c8cSGeorge Liu                 continue;
7320ed80c8cSGeorge Liu             }
7330ed80c8cSGeorge Liu 
7340ed80c8cSGeorge Liu             if (param.second == "UpdateParameters")
7350ed80c8cSGeorge Liu             {
736*ef93eab3SJagpal Singh Gill                 std::vector<std::string> tempTargets;
7370ed80c8cSGeorge Liu                 nlohmann::json content =
7380ed80c8cSGeorge Liu                     nlohmann::json::parse(formpart.content);
7397cb59f65SEd Tanous                 nlohmann::json::object_t* obj =
7407cb59f65SEd Tanous                     content.get_ptr<nlohmann::json::object_t*>();
7417cb59f65SEd Tanous                 if (obj == nullptr)
7427cb59f65SEd Tanous                 {
743*ef93eab3SJagpal Singh Gill                     messages::propertyValueTypeError(
744*ef93eab3SJagpal Singh Gill                         asyncResp->res, formpart.content, "UpdateParameters");
745*ef93eab3SJagpal Singh Gill                     return std::nullopt;
7467cb59f65SEd Tanous                 }
7477cb59f65SEd Tanous 
7487cb59f65SEd Tanous                 if (!json_util::readJsonObject(
749*ef93eab3SJagpal Singh Gill                         *obj, asyncResp->res, "Targets", tempTargets,
750*ef93eab3SJagpal Singh Gill                         "@Redfish.OperationApplyTime", multiRet.applyTime))
7510ed80c8cSGeorge Liu                 {
752*ef93eab3SJagpal Singh Gill                     return std::nullopt;
7530ed80c8cSGeorge Liu                 }
754*ef93eab3SJagpal Singh Gill 
755*ef93eab3SJagpal Singh Gill                 for (size_t urlIndex = 0; urlIndex < tempTargets.size();
756*ef93eab3SJagpal Singh Gill                      urlIndex++)
7570ed80c8cSGeorge Liu                 {
758*ef93eab3SJagpal Singh Gill                     const std::string& target = tempTargets[urlIndex];
759*ef93eab3SJagpal Singh Gill                     boost::system::result<boost::urls::url_view> url =
760*ef93eab3SJagpal Singh Gill                         boost::urls::parse_origin_form(target);
761*ef93eab3SJagpal Singh Gill                     if (!url)
7620ed80c8cSGeorge Liu                     {
763*ef93eab3SJagpal Singh Gill                         messages::propertyValueFormatError(
764*ef93eab3SJagpal Singh Gill                             asyncResp->res, target,
765*ef93eab3SJagpal Singh Gill                             std::format("Targets/{}", urlIndex));
766*ef93eab3SJagpal Singh Gill                         return std::nullopt;
7670ed80c8cSGeorge Liu                     }
768*ef93eab3SJagpal Singh Gill                     multiRet.targets.emplace_back(*url);
769*ef93eab3SJagpal Singh Gill                 }
770*ef93eab3SJagpal Singh Gill                 if (multiRet.targets.size() != 1)
771*ef93eab3SJagpal Singh Gill                 {
772*ef93eab3SJagpal Singh Gill                     messages::propertyValueFormatError(
773*ef93eab3SJagpal Singh Gill                         asyncResp->res, multiRet.targets, "Targets");
774*ef93eab3SJagpal Singh Gill                     return std::nullopt;
775*ef93eab3SJagpal Singh Gill                 }
776*ef93eab3SJagpal Singh Gill                 if (multiRet.targets[0].path() != "/redfish/v1/Managers/bmc")
777*ef93eab3SJagpal Singh Gill                 {
778*ef93eab3SJagpal Singh Gill                     messages::propertyValueNotInList(
779*ef93eab3SJagpal Singh Gill                         asyncResp->res, multiRet.targets[0], "Targets/0");
780*ef93eab3SJagpal Singh Gill                     return std::nullopt;
781*ef93eab3SJagpal Singh Gill                 }
7820ed80c8cSGeorge Liu             }
7830ed80c8cSGeorge Liu             else if (param.second == "UpdateFile")
7840ed80c8cSGeorge Liu             {
785*ef93eab3SJagpal Singh Gill                 multiRet.uploadData = std::move(formpart.content);
7860ed80c8cSGeorge Liu             }
7870ed80c8cSGeorge Liu         }
7880ed80c8cSGeorge Liu     }
7890ed80c8cSGeorge Liu 
790*ef93eab3SJagpal Singh Gill     if (multiRet.uploadData.empty())
7910ed80c8cSGeorge Liu     {
79262598e31SEd Tanous         BMCWEB_LOG_ERROR("Upload data is NULL");
7930ed80c8cSGeorge Liu         messages::propertyMissing(asyncResp->res, "UpdateFile");
794*ef93eab3SJagpal Singh Gill         return std::nullopt;
7950ed80c8cSGeorge Liu     }
796*ef93eab3SJagpal Singh Gill     if (multiRet.targets.empty())
7970ed80c8cSGeorge Liu     {
798*ef93eab3SJagpal Singh Gill         messages::propertyMissing(asyncResp->res, "Targets");
799*ef93eab3SJagpal Singh Gill         return std::nullopt;
800*ef93eab3SJagpal Singh Gill     }
801*ef93eab3SJagpal Singh Gill     return multiRet;
8020ed80c8cSGeorge Liu }
8030ed80c8cSGeorge Liu 
804*ef93eab3SJagpal Singh Gill inline void
805*ef93eab3SJagpal Singh Gill     updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
806*ef93eab3SJagpal Singh Gill                            const crow::Request& req, MultipartParser&& parser)
807*ef93eab3SJagpal Singh Gill {
808*ef93eab3SJagpal Singh Gill     std::optional<MultiPartUpdateParameters> multipart =
809*ef93eab3SJagpal Singh Gill         extractMultipartUpdateParameters(asyncResp, std::move(parser));
810*ef93eab3SJagpal Singh Gill     if (!multipart)
811*ef93eab3SJagpal Singh Gill     {
812*ef93eab3SJagpal Singh Gill         return;
813*ef93eab3SJagpal Singh Gill     }
814*ef93eab3SJagpal Singh Gill     if (!multipart->applyTime)
815*ef93eab3SJagpal Singh Gill     {
816*ef93eab3SJagpal Singh Gill         multipart->applyTime = "OnReset";
817*ef93eab3SJagpal Singh Gill     }
818*ef93eab3SJagpal Singh Gill 
819*ef93eab3SJagpal 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 
824*ef93eab3SJagpal 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 
863*ef93eab3SJagpal 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;
10031476687dSEd Tanous         item["@odata.id"] = "/redfish/v1/Managers/bmc";
1004b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
1005ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1006ac106bf6SEd Tanous             relatedItem.size();
100787d84729SAndrew Geissler     }
1008eee0013eSWilly Tu     else if (purpose == sw_util::biosPurpose)
100987d84729SAndrew Geissler     {
1010ac106bf6SEd Tanous         nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
10111476687dSEd Tanous         nlohmann::json::object_t item;
10121476687dSEd Tanous         item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
1013b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
1014ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1015ac106bf6SEd Tanous             relatedItem.size();
101687d84729SAndrew Geissler     }
101787d84729SAndrew Geissler     else
101887d84729SAndrew Geissler     {
1019bf2ddedeSCarson Labrado         BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
102087d84729SAndrew Geissler     }
102187d84729SAndrew Geissler }
102287d84729SAndrew Geissler 
1023af24660dSWilly Tu inline void
1024af24660dSWilly Tu     getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1025af24660dSWilly Tu                        const std::string& service, const std::string& path,
1026af24660dSWilly Tu                        const std::string& swId)
1027af24660dSWilly Tu {
1028d1bde9e5SKrzysztof Grobelny     sdbusplus::asio::getAllProperties(
1029d1bde9e5SKrzysztof Grobelny         *crow::connections::systemBus, service, path,
1030d1bde9e5SKrzysztof Grobelny         "xyz.openbmc_project.Software.Version",
1031af24660dSWilly Tu         [asyncResp,
10328b24275dSEd Tanous          swId](const boost::system::error_code& ec,
1033af24660dSWilly Tu                const dbus::utility::DBusPropertiesMap& propertiesList) {
10348b24275dSEd Tanous         if (ec)
1035af24660dSWilly Tu         {
1036af24660dSWilly Tu             messages::internalError(asyncResp->res);
1037af24660dSWilly Tu             return;
1038af24660dSWilly Tu         }
1039d1bde9e5SKrzysztof Grobelny 
1040af24660dSWilly Tu         const std::string* swInvPurpose = nullptr;
1041af24660dSWilly Tu         const std::string* version = nullptr;
1042d1bde9e5SKrzysztof Grobelny 
1043d1bde9e5SKrzysztof Grobelny         const bool success = sdbusplus::unpackPropertiesNoThrow(
1044d1bde9e5SKrzysztof Grobelny             dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
1045d1bde9e5SKrzysztof Grobelny             swInvPurpose, "Version", version);
1046d1bde9e5SKrzysztof Grobelny 
1047d1bde9e5SKrzysztof Grobelny         if (!success)
1048af24660dSWilly Tu         {
1049d1bde9e5SKrzysztof Grobelny             messages::internalError(asyncResp->res);
1050d1bde9e5SKrzysztof Grobelny             return;
1051af24660dSWilly Tu         }
1052af24660dSWilly Tu 
1053af24660dSWilly Tu         if (swInvPurpose == nullptr)
1054af24660dSWilly Tu         {
105562598e31SEd Tanous             BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
1056af24660dSWilly Tu             messages::internalError(asyncResp->res);
1057af24660dSWilly Tu             return;
1058af24660dSWilly Tu         }
1059af24660dSWilly Tu 
106062598e31SEd Tanous         BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
1061af24660dSWilly Tu 
1062af24660dSWilly Tu         if (version == nullptr)
1063af24660dSWilly Tu         {
106462598e31SEd Tanous             BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
1065af24660dSWilly Tu 
1066af24660dSWilly Tu             messages::internalError(asyncResp->res);
1067af24660dSWilly Tu 
1068af24660dSWilly Tu             return;
1069af24660dSWilly Tu         }
1070af24660dSWilly Tu         asyncResp->res.jsonValue["Version"] = *version;
1071af24660dSWilly Tu         asyncResp->res.jsonValue["Id"] = swId;
1072af24660dSWilly Tu 
1073af24660dSWilly Tu         // swInvPurpose is of format:
1074af24660dSWilly Tu         // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
1075af24660dSWilly Tu         // Translate this to "ABC image"
1076af24660dSWilly Tu         size_t endDesc = swInvPurpose->rfind('.');
1077af24660dSWilly Tu         if (endDesc == std::string::npos)
1078af24660dSWilly Tu         {
1079af24660dSWilly Tu             messages::internalError(asyncResp->res);
1080af24660dSWilly Tu             return;
1081af24660dSWilly Tu         }
1082af24660dSWilly Tu         endDesc++;
1083af24660dSWilly Tu         if (endDesc >= swInvPurpose->size())
1084af24660dSWilly Tu         {
1085af24660dSWilly Tu             messages::internalError(asyncResp->res);
1086af24660dSWilly Tu             return;
1087af24660dSWilly Tu         }
1088af24660dSWilly Tu 
1089af24660dSWilly Tu         std::string formatDesc = swInvPurpose->substr(endDesc);
1090af24660dSWilly Tu         asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1091af24660dSWilly Tu         getRelatedItems(asyncResp, *swInvPurpose);
1092d1bde9e5SKrzysztof Grobelny     });
1093af24660dSWilly Tu }
1094af24660dSWilly Tu 
1095f5139334SEd Tanous inline void handleUpdateServiceFirmwareInventoryGet(
1096f5139334SEd Tanous     App& app, const crow::Request& req,
109745ca1b86SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1098f5139334SEd Tanous     const std::string& param)
1099f5139334SEd Tanous {
11003ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
110145ca1b86SEd Tanous     {
110245ca1b86SEd Tanous         return;
110345ca1b86SEd Tanous     }
1104f5139334SEd Tanous     std::shared_ptr<std::string> swId = std::make_shared<std::string>(param);
1105c711bf86SEd Tanous 
1106ef4c65b7SEd Tanous     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1107ef4c65b7SEd Tanous         "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
1108c711bf86SEd Tanous 
1109e99073f5SGeorge Liu     constexpr std::array<std::string_view, 1> interfaces = {
1110e99073f5SGeorge Liu         "xyz.openbmc_project.Software.Version"};
1111e99073f5SGeorge Liu     dbus::utility::getSubTree(
1112e99073f5SGeorge Liu         "/", 0, interfaces,
1113b9d36b47SEd Tanous         [asyncResp,
1114e99073f5SGeorge Liu          swId](const boost::system::error_code& ec,
1115b9d36b47SEd Tanous                const dbus::utility::MapperGetSubTreeResponse& subtree) {
111662598e31SEd Tanous         BMCWEB_LOG_DEBUG("doGet callback...");
11171abe55efSEd Tanous         if (ec)
11181abe55efSEd Tanous         {
1119f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
11206c4eb9deSJennifer Lee             return;
11216c4eb9deSJennifer Lee         }
11226c4eb9deSJennifer Lee 
11236913228dSAndrew Geissler         // Ensure we find our input swId, otherwise return an error
11246913228dSAndrew Geissler         bool found = false;
1125f5139334SEd Tanous         for (const std::pair<
1126f5139334SEd Tanous                  std::string,
1127f5139334SEd Tanous                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
1128002d39b4SEd Tanous                  obj : subtree)
11291abe55efSEd Tanous         {
113011ba3979SEd Tanous             if (!obj.first.ends_with(*swId))
11311abe55efSEd Tanous             {
1132acb7cfb4SJennifer Lee                 continue;
1133acb7cfb4SJennifer Lee             }
1134acb7cfb4SJennifer Lee 
113526f6976fSEd Tanous             if (obj.second.empty())
11361abe55efSEd Tanous             {
1137acb7cfb4SJennifer Lee                 continue;
1138acb7cfb4SJennifer Lee             }
11396c4eb9deSJennifer Lee 
11406913228dSAndrew Geissler             found = true;
1141eee0013eSWilly Tu             sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1142af24660dSWilly Tu             getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1143af24660dSWilly Tu                                *swId);
11446c4eb9deSJennifer Lee         }
11456913228dSAndrew Geissler         if (!found)
11466913228dSAndrew Geissler         {
114762598e31SEd Tanous             BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
11486913228dSAndrew Geissler             messages::resourceMissingAtURI(
1149ef4c65b7SEd Tanous                 asyncResp->res,
1150ef4c65b7SEd Tanous                 boost::urls::format(
1151f5139334SEd Tanous                     "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId));
11526913228dSAndrew Geissler             return;
11536913228dSAndrew Geissler         }
11544e68c45bSAyushi Smriti         asyncResp->res.jsonValue["@odata.type"] =
11554e68c45bSAyushi Smriti             "#SoftwareInventory.v1_1_0.SoftwareInventory";
11564e68c45bSAyushi Smriti         asyncResp->res.jsonValue["Name"] = "Software Inventory";
11574e68c45bSAyushi Smriti         asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
11583f8a743aSAppaRao Puli 
11593f8a743aSAppaRao Puli         asyncResp->res.jsonValue["Updateable"] = false;
1160eee0013eSWilly Tu         sw_util::getSwUpdatableStatus(asyncResp, swId);
1161e99073f5SGeorge Liu     });
1162f5139334SEd Tanous }
1163f5139334SEd Tanous 
1164f5139334SEd Tanous inline void requestRoutesUpdateService(App& app)
1165f5139334SEd Tanous {
1166f5139334SEd Tanous     BMCWEB_ROUTE(
1167f5139334SEd Tanous         app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
1168f5139334SEd Tanous         .privileges(redfish::privileges::postUpdateService)
1169f5139334SEd Tanous         .methods(boost::beast::http::verb::post)(std::bind_front(
1170f5139334SEd Tanous             handleUpdateServiceSimpleUpdateAction, std::ref(app)));
1171f5139334SEd Tanous 
1172f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1173f5139334SEd Tanous         .privileges(redfish::privileges::getSoftwareInventory)
1174f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
1175f5139334SEd Tanous             handleUpdateServiceFirmwareInventoryGet, std::ref(app)));
1176f5139334SEd Tanous 
1177f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1178f5139334SEd Tanous         .privileges(redfish::privileges::getUpdateService)
1179f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(
1180f5139334SEd Tanous             std::bind_front(handleUpdateServiceGet, std::ref(app)));
1181f5139334SEd Tanous 
1182f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1183f5139334SEd Tanous         .privileges(redfish::privileges::patchUpdateService)
1184f5139334SEd Tanous         .methods(boost::beast::http::verb::patch)(
1185f5139334SEd Tanous             std::bind_front(handleUpdateServicePatch, std::ref(app)));
1186f5139334SEd Tanous 
1187f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
1188f5139334SEd Tanous         .privileges(redfish::privileges::postUpdateService)
1189f5139334SEd Tanous         .methods(boost::beast::http::verb::post)(
1190f5139334SEd Tanous             std::bind_front(handleUpdateServicePost, std::ref(app)));
1191f5139334SEd Tanous 
1192f5139334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
1193f5139334SEd Tanous         .privileges(redfish::privileges::getSoftwareInventoryCollection)
1194f5139334SEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
1195f5139334SEd Tanous             handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app)));
11966c4eb9deSJennifer Lee }
1197729dae72SJennifer Lee 
1198729dae72SJennifer Lee } // namespace redfish
1199