xref: /openbmc/bmcweb/features/redfish/lib/update_service.hpp (revision 18f8f608b966c802b3e2a389e3c1ec5a1fd9407b)
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"
220ed80c8cSGeorge Liu #include "multipart_parser.hpp"
232c6ffdb0SEd Tanous #include "ossl_random.hpp"
243ccb3adbSEd Tanous #include "query.hpp"
253ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
26a8e884fcSEd Tanous #include "task.hpp"
2708d81adaSJohn Edward Broadbent #include "utils/collection.hpp"
283ccb3adbSEd Tanous #include "utils/dbus_utils.hpp"
293ccb3adbSEd Tanous #include "utils/sw_utils.hpp"
303ccb3adbSEd Tanous 
31e99073f5SGeorge Liu #include <boost/system/error_code.hpp>
32ef4c65b7SEd Tanous #include <boost/url/format.hpp>
331e1e598dSJonathan Doman #include <sdbusplus/asio/property.hpp>
343ccb3adbSEd Tanous #include <sdbusplus/bus/match.hpp>
35d1bde9e5SKrzysztof Grobelny #include <sdbusplus/unpack_properties.hpp>
361214b7e7SGunnar Mills 
372b73119cSGeorge Liu #include <array>
380ed80c8cSGeorge Liu #include <filesystem>
392b73119cSGeorge Liu #include <string_view>
402b73119cSGeorge Liu 
411abe55efSEd Tanous namespace redfish
421abe55efSEd Tanous {
4327826b5fSEd Tanous 
440e7de46fSAndrew Geissler // Match signals added on software path
45cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
4659d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
47cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
4859d494eeSPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
490e7de46fSAndrew Geissler // Only allow one update at a time
50cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
510e7de46fSAndrew Geissler static bool fwUpdateInProgress = false;
5286adcd6dSAndrew Geissler // Timer for software available
53cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
54271584abSEd Tanous static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
5586adcd6dSAndrew Geissler 
567e860f15SJohn Edward Broadbent inline static void cleanUp()
5786adcd6dSAndrew Geissler {
5886adcd6dSAndrew Geissler     fwUpdateInProgress = false;
5986adcd6dSAndrew Geissler     fwUpdateMatcher = nullptr;
604cde5d90SJames Feist     fwUpdateErrorMatcher = nullptr;
6186adcd6dSAndrew Geissler }
627e860f15SJohn Edward Broadbent inline static void activateImage(const std::string& objPath,
6386adcd6dSAndrew Geissler                                  const std::string& service)
6486adcd6dSAndrew Geissler {
6562598e31SEd Tanous     BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
669ae226faSGeorge Liu     sdbusplus::asio::setProperty(
679ae226faSGeorge Liu         *crow::connections::systemBus, service, objPath,
689ae226faSGeorge Liu         "xyz.openbmc_project.Software.Activation", "RequestedActivation",
699ae226faSGeorge Liu         "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
708b24275dSEd Tanous         [](const boost::system::error_code& ec) {
718b24275dSEd Tanous         if (ec)
7286adcd6dSAndrew Geissler         {
7362598e31SEd Tanous             BMCWEB_LOG_DEBUG("error_code = {}", ec);
7462598e31SEd Tanous             BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
7586adcd6dSAndrew Geissler         }
769ae226faSGeorge Liu     });
7786adcd6dSAndrew Geissler }
780554c984SAndrew Geissler 
790554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
800554c984SAndrew Geissler // then no asyncResp updates will occur
818d1b46d7Szhanghch05 static void
828d1b46d7Szhanghch05     softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
8359d494eeSPatrick Williams                            sdbusplus::message_t& m, task::Payload&& payload)
8486adcd6dSAndrew Geissler {
8580f79a40SMichael Shen     dbus::utility::DBusInterfacesMap interfacesProperties;
8686adcd6dSAndrew Geissler 
8786adcd6dSAndrew Geissler     sdbusplus::message::object_path objPath;
8886adcd6dSAndrew Geissler 
8986adcd6dSAndrew Geissler     m.read(objPath, interfacesProperties);
9086adcd6dSAndrew Geissler 
9162598e31SEd Tanous     BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
92e3eb3d63SEd Tanous     for (const auto& interface : interfacesProperties)
9386adcd6dSAndrew Geissler     {
9462598e31SEd Tanous         BMCWEB_LOG_DEBUG("interface = {}", interface.first);
9586adcd6dSAndrew Geissler 
9686adcd6dSAndrew Geissler         if (interface.first == "xyz.openbmc_project.Software.Activation")
9786adcd6dSAndrew Geissler         {
9886adcd6dSAndrew Geissler             // Retrieve service and activate
992b73119cSGeorge Liu             constexpr std::array<std::string_view, 1> interfaces = {
1002b73119cSGeorge Liu                 "xyz.openbmc_project.Software.Activation"};
1012b73119cSGeorge Liu             dbus::utility::getDbusObject(
1022b73119cSGeorge Liu                 objPath.str, interfaces,
103a3e65892SEd Tanous                 [objPath, asyncResp, payload(std::move(payload))](
1048b24275dSEd Tanous                     const boost::system::error_code& ec,
105a3e65892SEd Tanous                     const std::vector<
106a3e65892SEd Tanous                         std::pair<std::string, std::vector<std::string>>>&
107a3e65892SEd Tanous                         objInfo) mutable {
1088b24275dSEd Tanous                 if (ec)
10986adcd6dSAndrew Geissler                 {
11062598e31SEd Tanous                     BMCWEB_LOG_DEBUG("error_code = {}", ec);
11162598e31SEd Tanous                     BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
1120554c984SAndrew Geissler                     if (asyncResp)
1130554c984SAndrew Geissler                     {
11486adcd6dSAndrew Geissler                         messages::internalError(asyncResp->res);
1150554c984SAndrew Geissler                     }
11686adcd6dSAndrew Geissler                     cleanUp();
11786adcd6dSAndrew Geissler                     return;
11886adcd6dSAndrew Geissler                 }
11986adcd6dSAndrew Geissler                 // Ensure we only got one service back
12086adcd6dSAndrew Geissler                 if (objInfo.size() != 1)
12186adcd6dSAndrew Geissler                 {
12262598e31SEd Tanous                     BMCWEB_LOG_ERROR("Invalid Object Size {}", objInfo.size());
1230554c984SAndrew Geissler                     if (asyncResp)
1240554c984SAndrew Geissler                     {
12586adcd6dSAndrew Geissler                         messages::internalError(asyncResp->res);
1260554c984SAndrew Geissler                     }
12786adcd6dSAndrew Geissler                     cleanUp();
12886adcd6dSAndrew Geissler                     return;
12986adcd6dSAndrew Geissler                 }
13086adcd6dSAndrew Geissler                 // cancel timer only when
13186adcd6dSAndrew Geissler                 // xyz.openbmc_project.Software.Activation interface
13286adcd6dSAndrew Geissler                 // is added
13386adcd6dSAndrew Geissler                 fwAvailableTimer = nullptr;
13486adcd6dSAndrew Geissler 
13586adcd6dSAndrew Geissler                 activateImage(objPath.str, objInfo[0].first);
1360554c984SAndrew Geissler                 if (asyncResp)
1370554c984SAndrew Geissler                 {
13832898ceaSJames Feist                     std::shared_ptr<task::TaskData> task =
13932898ceaSJames Feist                         task::TaskData::createTask(
1408b24275dSEd Tanous                             [](const boost::system::error_code& ec2,
14159d494eeSPatrick Williams                                sdbusplus::message_t& msg,
1421214b7e7SGunnar Mills                                const std::shared_ptr<task::TaskData>&
1431214b7e7SGunnar Mills                                    taskData) {
1448b24275dSEd Tanous                         if (ec2)
14532898ceaSJames Feist                         {
14632898ceaSJames Feist                             return task::completed;
14732898ceaSJames Feist                         }
14832898ceaSJames Feist 
14932898ceaSJames Feist                         std::string iface;
150b9d36b47SEd Tanous                         dbus::utility::DBusPropertiesMap values;
151fd9ab9e1SJames Feist 
152002d39b4SEd Tanous                         std::string index = std::to_string(taskData->index);
15332898ceaSJames Feist                         msg.read(iface, values);
154fd9ab9e1SJames Feist 
155002d39b4SEd Tanous                         if (iface == "xyz.openbmc_project.Software.Activation")
156fd9ab9e1SJames Feist                         {
1570fb5b505SGayathri Leburu                             const std::string* state = nullptr;
158b9d36b47SEd Tanous                             for (const auto& property : values)
15932898ceaSJames Feist                             {
160b9d36b47SEd Tanous                                 if (property.first == "Activation")
161b9d36b47SEd Tanous                                 {
1620fb5b505SGayathri Leburu                                     state = std::get_if<std::string>(
163b9d36b47SEd Tanous                                         &property.second);
1640fb5b505SGayathri Leburu                                     if (state == nullptr)
165b9d36b47SEd Tanous                                     {
166002d39b4SEd Tanous                                         taskData->messages.emplace_back(
167002d39b4SEd Tanous                                             messages::internalError());
168b9d36b47SEd Tanous                                         return task::completed;
169b9d36b47SEd Tanous                                     }
170b9d36b47SEd Tanous                                 }
171b9d36b47SEd Tanous                             }
17232898ceaSJames Feist 
17332898ceaSJames Feist                             if (state == nullptr)
17432898ceaSJames Feist                             {
175b9d36b47SEd Tanous                                 return !task::completed;
17632898ceaSJames Feist                             }
17732898ceaSJames Feist 
17811ba3979SEd Tanous                             if (state->ends_with("Invalid") ||
17911ba3979SEd Tanous                                 state->ends_with("Failed"))
18032898ceaSJames Feist                             {
18132898ceaSJames Feist                                 taskData->state = "Exception";
18232898ceaSJames Feist                                 taskData->status = "Warning";
18332898ceaSJames Feist                                 taskData->messages.emplace_back(
184e5d5006bSJames Feist                                     messages::taskAborted(index));
18532898ceaSJames Feist                                 return task::completed;
18632898ceaSJames Feist                             }
18732898ceaSJames Feist 
18811ba3979SEd Tanous                             if (state->ends_with("Staged"))
18932898ceaSJames Feist                             {
190fd9ab9e1SJames Feist                                 taskData->state = "Stopping";
191fd9ab9e1SJames Feist                                 taskData->messages.emplace_back(
192fd9ab9e1SJames Feist                                     messages::taskPaused(index));
193fd9ab9e1SJames Feist 
194fd9ab9e1SJames Feist                                 // its staged, set a long timer to
195fd9ab9e1SJames Feist                                 // allow them time to complete the
196fd9ab9e1SJames Feist                                 // update (probably cycle the
197fd9ab9e1SJames Feist                                 // system) if this expires then
1988ece0e45SEd Tanous                                 // task will be canceled
199002d39b4SEd Tanous                                 taskData->extendTimer(std::chrono::hours(5));
20032898ceaSJames Feist                                 return !task::completed;
20132898ceaSJames Feist                             }
20232898ceaSJames Feist 
20311ba3979SEd Tanous                             if (state->ends_with("Active"))
20432898ceaSJames Feist                             {
20532898ceaSJames Feist                                 taskData->messages.emplace_back(
206002d39b4SEd Tanous                                     messages::taskCompletedOK(index));
20732898ceaSJames Feist                                 taskData->state = "Completed";
20832898ceaSJames Feist                                 return task::completed;
20932898ceaSJames Feist                             }
210fd9ab9e1SJames Feist                         }
2110fda0f12SGeorge Liu                         else if (
2120fda0f12SGeorge Liu                             iface ==
2130fda0f12SGeorge Liu                             "xyz.openbmc_project.Software.ActivationProgress")
214fd9ab9e1SJames Feist                         {
215b9d36b47SEd Tanous                             const uint8_t* progress = nullptr;
216b9d36b47SEd Tanous                             for (const auto& property : values)
217fd9ab9e1SJames Feist                             {
218b9d36b47SEd Tanous                                 if (property.first == "Progress")
219b9d36b47SEd Tanous                                 {
2200fb5b505SGayathri Leburu                                     progress =
2210fb5b505SGayathri Leburu                                         std::get_if<uint8_t>(&property.second);
2220fb5b505SGayathri Leburu                                     if (progress == nullptr)
223b9d36b47SEd Tanous                                     {
224002d39b4SEd Tanous                                         taskData->messages.emplace_back(
225002d39b4SEd Tanous                                             messages::internalError());
226b9d36b47SEd Tanous                                         return task::completed;
227fd9ab9e1SJames Feist                                     }
228b9d36b47SEd Tanous                                 }
229b9d36b47SEd Tanous                             }
230fd9ab9e1SJames Feist 
231fd9ab9e1SJames Feist                             if (progress == nullptr)
232fd9ab9e1SJames Feist                             {
233b9d36b47SEd Tanous                                 return !task::completed;
234fd9ab9e1SJames Feist                             }
2350fb5b505SGayathri Leburu                             taskData->percentComplete = *progress;
236fd9ab9e1SJames Feist                             taskData->messages.emplace_back(
2370fb5b505SGayathri Leburu                                 messages::taskProgressChanged(index,
2380fb5b505SGayathri Leburu                                                               *progress));
239fd9ab9e1SJames Feist 
240fd9ab9e1SJames Feist                             // if we're getting status updates it's
241fd9ab9e1SJames Feist                             // still alive, update timer
242002d39b4SEd Tanous                             taskData->extendTimer(std::chrono::minutes(5));
243fd9ab9e1SJames Feist                         }
24432898ceaSJames Feist 
24532898ceaSJames Feist                         // as firmware update often results in a
24632898ceaSJames Feist                         // reboot, the task  may never "complete"
24732898ceaSJames Feist                         // unless it is an error
24832898ceaSJames Feist 
24932898ceaSJames Feist                         return !task::completed;
25032898ceaSJames Feist                     },
2510fda0f12SGeorge Liu                             "type='signal',interface='org.freedesktop.DBus.Properties',"
252fd9ab9e1SJames Feist                             "member='PropertiesChanged',path='" +
25332898ceaSJames Feist                                 objPath.str + "'");
25432898ceaSJames Feist                     task->startTimer(std::chrono::minutes(5));
25532898ceaSJames Feist                     task->populateResp(asyncResp->res);
256a3e65892SEd Tanous                     task->payload.emplace(std::move(payload));
2570554c984SAndrew Geissler                 }
25886adcd6dSAndrew Geissler                 fwUpdateInProgress = false;
2592b73119cSGeorge Liu             });
26062bafc01SPatrick Williams 
26162bafc01SPatrick Williams             break;
26286adcd6dSAndrew Geissler         }
26386adcd6dSAndrew Geissler     }
26486adcd6dSAndrew Geissler }
26586adcd6dSAndrew Geissler 
2668549b951SMyung Bae inline void afterAvailbleTimerAsyncWait(
2678549b951SMyung Bae     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2688549b951SMyung Bae     const boost::system::error_code& ec)
2698549b951SMyung Bae {
2708549b951SMyung Bae     cleanUp();
2718549b951SMyung Bae     if (ec == boost::asio::error::operation_aborted)
2728549b951SMyung Bae     {
2738549b951SMyung Bae         // expected, we were canceled before the timer completed.
2748549b951SMyung Bae         return;
2758549b951SMyung Bae     }
2768549b951SMyung Bae     BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
2778549b951SMyung Bae     BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
2788549b951SMyung Bae     if (ec)
2798549b951SMyung Bae     {
2808549b951SMyung Bae         BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
2818549b951SMyung Bae         return;
2828549b951SMyung Bae     }
2838549b951SMyung Bae     if (asyncResp)
2848549b951SMyung Bae     {
2858549b951SMyung Bae         redfish::messages::internalError(asyncResp->res);
2868549b951SMyung Bae     }
2878549b951SMyung Bae }
2888549b951SMyung Bae 
2898549b951SMyung Bae inline void
2908549b951SMyung Bae     handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2918549b951SMyung Bae                           const std::string& url, const std::string& type)
2928549b951SMyung Bae {
2938549b951SMyung Bae     if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
2948549b951SMyung Bae     {
2958549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
2968549b951SMyung Bae                                          "Invalid archive");
2978549b951SMyung Bae     }
2988549b951SMyung Bae     else if (type ==
2998549b951SMyung Bae              "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
3008549b951SMyung Bae     {
3018549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3028549b951SMyung Bae                                          "Invalid manifest");
3038549b951SMyung Bae     }
3048549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
3058549b951SMyung Bae     {
3068549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3078549b951SMyung Bae                                          "Invalid image format");
3088549b951SMyung Bae     }
3098549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
3108549b951SMyung Bae     {
3118549b951SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3128549b951SMyung Bae                                          "Image version already exists");
3138549b951SMyung Bae 
3148549b951SMyung Bae         redfish::messages::resourceAlreadyExists(
3158549b951SMyung Bae             asyncResp->res, "UpdateService", "Version", "uploaded version");
3168549b951SMyung Bae     }
3178549b951SMyung Bae     else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
3188549b951SMyung Bae     {
3198549b951SMyung Bae         redfish::messages::resourceExhaustion(asyncResp->res, url);
3208549b951SMyung Bae     }
3214034a652SMyung Bae     else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
3228549b951SMyung Bae     {
3234034a652SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3244034a652SMyung Bae                                          "Incompatible image version");
3254034a652SMyung Bae     }
3264034a652SMyung Bae     else if (type ==
3274034a652SMyung Bae              "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
3284034a652SMyung Bae     {
3294034a652SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3304034a652SMyung Bae                                          "Update Access Key Expired");
3314034a652SMyung Bae     }
3324034a652SMyung Bae     else if (type ==
3334034a652SMyung Bae              "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
3344034a652SMyung Bae     {
3354034a652SMyung Bae         redfish::messages::invalidUpload(asyncResp->res, url,
3364034a652SMyung Bae                                          "Invalid image signature");
3374034a652SMyung Bae     }
3384034a652SMyung Bae     else if (type ==
3394034a652SMyung Bae                  "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
3404034a652SMyung Bae              type == "xyz.openbmc_project.Software.Version.Error.HostFile")
3414034a652SMyung Bae     {
3424034a652SMyung Bae         BMCWEB_LOG_ERROR("Software Image Error type={}", type);
3438549b951SMyung Bae         redfish::messages::internalError(asyncResp->res);
3448549b951SMyung Bae     }
3454034a652SMyung Bae     else
3464034a652SMyung Bae     {
3474034a652SMyung Bae         // Unrelated error types. Ignored
3484034a652SMyung Bae         BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
3494034a652SMyung Bae         return;
3504034a652SMyung Bae     }
3514034a652SMyung Bae     // Clear the timer
3524034a652SMyung Bae     fwAvailableTimer = nullptr;
3538549b951SMyung Bae }
3548549b951SMyung Bae 
3558549b951SMyung Bae inline void
3568549b951SMyung Bae     afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3578549b951SMyung Bae                             const std::string& url, sdbusplus::message_t& m)
3588549b951SMyung Bae {
35980f79a40SMichael Shen     dbus::utility::DBusInterfacesMap interfacesProperties;
3608549b951SMyung Bae     sdbusplus::message::object_path objPath;
3618549b951SMyung Bae     m.read(objPath, interfacesProperties);
3628549b951SMyung Bae     BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
3638549b951SMyung Bae     for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
3648549b951SMyung Bae              interface : interfacesProperties)
3658549b951SMyung Bae     {
3668549b951SMyung Bae         if (interface.first == "xyz.openbmc_project.Logging.Entry")
3678549b951SMyung Bae         {
3688549b951SMyung Bae             for (const std::pair<std::string, dbus::utility::DbusVariantType>&
3698549b951SMyung Bae                      value : interface.second)
3708549b951SMyung Bae             {
3718549b951SMyung Bae                 if (value.first != "Message")
3728549b951SMyung Bae                 {
3738549b951SMyung Bae                     continue;
3748549b951SMyung Bae                 }
3758549b951SMyung Bae                 const std::string* type =
3768549b951SMyung Bae                     std::get_if<std::string>(&value.second);
3778549b951SMyung Bae                 if (type == nullptr)
3788549b951SMyung Bae                 {
3798549b951SMyung Bae                     // if this was our message, timeout will cover it
3808549b951SMyung Bae                     return;
3818549b951SMyung Bae                 }
3828549b951SMyung Bae                 handleUpdateErrorType(asyncResp, url, *type);
3838549b951SMyung Bae             }
3848549b951SMyung Bae         }
3858549b951SMyung Bae     }
3868549b951SMyung Bae }
3878549b951SMyung Bae 
3880554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
3890554c984SAndrew Geissler // then no asyncResp updates will occur
390b5a76932SEd Tanous static void monitorForSoftwareAvailable(
3918d1b46d7Szhanghch05     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3928d1b46d7Szhanghch05     const crow::Request& req, const std::string& url,
3935d138943SGunnar Mills     int timeoutTimeSeconds = 25)
39486adcd6dSAndrew Geissler {
39586adcd6dSAndrew Geissler     // Only allow one FW update at a time
396e05aec50SEd Tanous     if (fwUpdateInProgress)
39786adcd6dSAndrew Geissler     {
3980554c984SAndrew Geissler         if (asyncResp)
3990554c984SAndrew Geissler         {
40086adcd6dSAndrew Geissler             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
4010554c984SAndrew Geissler         }
40286adcd6dSAndrew Geissler         return;
40386adcd6dSAndrew Geissler     }
40486adcd6dSAndrew Geissler 
4050554c984SAndrew Geissler     fwAvailableTimer =
406271584abSEd Tanous         std::make_unique<boost::asio::steady_timer>(*req.ioService);
40786adcd6dSAndrew Geissler 
408271584abSEd Tanous     fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
40986adcd6dSAndrew Geissler 
41086adcd6dSAndrew Geissler     fwAvailableTimer->async_wait(
4118549b951SMyung Bae         std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
4128549b951SMyung Bae 
413a3e65892SEd Tanous     task::Payload payload(req);
41459d494eeSPatrick Williams     auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
41562598e31SEd Tanous         BMCWEB_LOG_DEBUG("Match fired");
416a3e65892SEd Tanous         softwareInterfaceAdded(asyncResp, m, std::move(payload));
41786adcd6dSAndrew Geissler     };
41886adcd6dSAndrew Geissler 
41986adcd6dSAndrew Geissler     fwUpdateInProgress = true;
42086adcd6dSAndrew Geissler 
42159d494eeSPatrick Williams     fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
42286adcd6dSAndrew Geissler         *crow::connections::systemBus,
42386adcd6dSAndrew Geissler         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
42486adcd6dSAndrew Geissler         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
42586adcd6dSAndrew Geissler         callback);
4264cde5d90SJames Feist 
42759d494eeSPatrick Williams     fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
4284cde5d90SJames Feist         *crow::connections::systemBus,
429e1cc4828SBrian Ma         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
430e1cc4828SBrian Ma         "member='InterfacesAdded',"
431e1cc4828SBrian Ma         "path='/xyz/openbmc_project/logging'",
4328549b951SMyung Bae         std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
43386adcd6dSAndrew Geissler }
434729dae72SJennifer Lee 
435f86bcc87SEd Tanous struct TftpUrl
436f86bcc87SEd Tanous {
437f86bcc87SEd Tanous     std::string fwFile;
438f86bcc87SEd Tanous     std::string tftpServer;
439f86bcc87SEd Tanous };
440f86bcc87SEd Tanous 
441f86bcc87SEd Tanous inline std::optional<TftpUrl>
442f86bcc87SEd Tanous     parseTftpUrl(std::string imageURI,
443f86bcc87SEd Tanous                  std::optional<std::string> transferProtocol,
444f86bcc87SEd Tanous                  crow::Response& res)
445f86bcc87SEd Tanous {
446f86bcc87SEd Tanous     if (imageURI.find("://") == std::string::npos)
447f86bcc87SEd Tanous     {
448f86bcc87SEd Tanous         if (imageURI.starts_with("/"))
449f86bcc87SEd Tanous         {
450f86bcc87SEd Tanous             messages::actionParameterValueTypeError(
451f86bcc87SEd Tanous                 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
452f86bcc87SEd Tanous             return std::nullopt;
453f86bcc87SEd Tanous         }
454f86bcc87SEd Tanous         if (!transferProtocol)
455f86bcc87SEd Tanous         {
456f86bcc87SEd Tanous             messages::actionParameterValueTypeError(
457f86bcc87SEd Tanous                 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
458f86bcc87SEd Tanous             return std::nullopt;
459f86bcc87SEd Tanous         }
460f86bcc87SEd Tanous         // OpenBMC currently only supports TFTP
461f86bcc87SEd Tanous         if (*transferProtocol != "TFTP")
462f86bcc87SEd Tanous         {
463f86bcc87SEd Tanous             messages::actionParameterNotSupported(res, "TransferProtocol",
464f86bcc87SEd Tanous                                                   *transferProtocol);
465f86bcc87SEd Tanous             BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
466f86bcc87SEd Tanous                              *transferProtocol);
467f86bcc87SEd Tanous             return std::nullopt;
468f86bcc87SEd Tanous         }
469f86bcc87SEd Tanous         imageURI = "tftp://" + imageURI;
470f86bcc87SEd Tanous     }
471f86bcc87SEd Tanous 
472f86bcc87SEd Tanous     boost::system::result<boost::urls::url> url =
473f86bcc87SEd Tanous         boost::urls::parse_absolute_uri(imageURI);
474f86bcc87SEd Tanous     if (!url)
475f86bcc87SEd Tanous     {
476f86bcc87SEd Tanous         messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
477f86bcc87SEd Tanous                                                 "UpdateService.SimpleUpdate");
478f86bcc87SEd Tanous 
479f86bcc87SEd Tanous         return std::nullopt;
480f86bcc87SEd Tanous     }
481f86bcc87SEd Tanous     url->normalize();
482f86bcc87SEd Tanous 
483f86bcc87SEd Tanous     if (url->scheme() != "tftp")
484f86bcc87SEd Tanous     {
485f86bcc87SEd Tanous         messages::actionParameterNotSupported(res, "ImageURI", imageURI);
486f86bcc87SEd Tanous         return std::nullopt;
487f86bcc87SEd Tanous     }
488f86bcc87SEd Tanous     std::string path(url->encoded_path());
489f86bcc87SEd Tanous     if (path.size() < 2)
490f86bcc87SEd Tanous     {
491f86bcc87SEd Tanous         messages::actionParameterNotSupported(res, "ImageURI", imageURI);
492f86bcc87SEd Tanous         return std::nullopt;
493f86bcc87SEd Tanous     }
494f86bcc87SEd Tanous     path.erase(0, 1);
495f86bcc87SEd Tanous     std::string host(url->encoded_host_and_port());
496f86bcc87SEd Tanous     return TftpUrl{path, host};
497f86bcc87SEd Tanous }
498f86bcc87SEd Tanous 
4990554c984SAndrew Geissler /**
5000554c984SAndrew Geissler  * UpdateServiceActionsSimpleUpdate class supports handle POST method for
5010554c984SAndrew Geissler  * SimpleUpdate action.
5020554c984SAndrew Geissler  */
5037e860f15SJohn Edward Broadbent inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
5040554c984SAndrew Geissler {
5057e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(
5067e860f15SJohn Edward Broadbent         app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
507ed398213SEd Tanous         .privileges(redfish::privileges::postUpdateService)
508002d39b4SEd Tanous         .methods(boost::beast::http::verb::post)(
509002d39b4SEd Tanous             [&app](const crow::Request& req,
5107e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
5113ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
51245ca1b86SEd Tanous         {
51345ca1b86SEd Tanous             return;
51445ca1b86SEd Tanous         }
51545ca1b86SEd Tanous 
5160554c984SAndrew Geissler         std::optional<std::string> transferProtocol;
5170554c984SAndrew Geissler         std::string imageURI;
5180554c984SAndrew Geissler 
51962598e31SEd Tanous         BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
5200554c984SAndrew Geissler 
5210554c984SAndrew Geissler         // User can pass in both TransferProtocol and ImageURI parameters or
5224e0453b1SGunnar Mills         // they can pass in just the ImageURI with the transfer protocol
5234e0453b1SGunnar Mills         // embedded within it.
5240554c984SAndrew Geissler         // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
5250554c984SAndrew Geissler         // 2) ImageURI:tftp://1.1.1.1/myfile.bin
5260554c984SAndrew Geissler 
527002d39b4SEd Tanous         if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
528002d39b4SEd Tanous                                        transferProtocol, "ImageURI", imageURI))
5290554c984SAndrew Geissler         {
53062598e31SEd Tanous             BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
5310554c984SAndrew Geissler             return;
5320554c984SAndrew Geissler         }
533f86bcc87SEd Tanous         std::optional<TftpUrl> ret = parseTftpUrl(imageURI, transferProtocol,
534f86bcc87SEd Tanous                                                   asyncResp->res);
535f86bcc87SEd Tanous         if (!ret)
5360554c984SAndrew Geissler         {
5370554c984SAndrew Geissler             return;
5380554c984SAndrew Geissler         }
5390554c984SAndrew Geissler 
540f86bcc87SEd Tanous         BMCWEB_LOG_DEBUG("Server: {} File: {}", ret->tftpServer, ret->fwFile);
5410554c984SAndrew Geissler 
5420554c984SAndrew Geissler         // Setup callback for when new software detected
5432618d5e3SGunnar Mills         // Give TFTP 10 minutes to complete
5444cde5d90SJames Feist         monitorForSoftwareAvailable(
545d7a596bdSAlbert Zhang             asyncResp, req,
5464cde5d90SJames Feist             "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
5472618d5e3SGunnar Mills             600);
5480554c984SAndrew Geissler 
5492618d5e3SGunnar Mills         // TFTP can take up to 10 minutes depending on image size and
5500554c984SAndrew Geissler         // connection speed. Return to caller as soon as the TFTP operation
5510554c984SAndrew Geissler         // has been started. The callback above will ensure the activate
5520554c984SAndrew Geissler         // is started once the download has completed
5530554c984SAndrew Geissler         redfish::messages::success(asyncResp->res);
5540554c984SAndrew Geissler 
5550554c984SAndrew Geissler         // Call TFTP service
5560554c984SAndrew Geissler         crow::connections::systemBus->async_method_call(
5575e7e2dc5SEd Tanous             [](const boost::system::error_code& ec) {
5580554c984SAndrew Geissler             if (ec)
5590554c984SAndrew Geissler             {
5600554c984SAndrew Geissler                 // messages::internalError(asyncResp->res);
5610554c984SAndrew Geissler                 cleanUp();
56262598e31SEd Tanous                 BMCWEB_LOG_DEBUG("error_code = {}", ec);
56362598e31SEd Tanous                 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
5640554c984SAndrew Geissler             }
5650554c984SAndrew Geissler             else
5660554c984SAndrew Geissler             {
56762598e31SEd Tanous                 BMCWEB_LOG_DEBUG("Call to DownloaViaTFTP Success");
5680554c984SAndrew Geissler             }
5690554c984SAndrew Geissler         },
5700554c984SAndrew Geissler             "xyz.openbmc_project.Software.Download",
571002d39b4SEd Tanous             "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
572f86bcc87SEd Tanous             "DownloadViaTFTP", ret->fwFile, ret->tftpServer);
5730554c984SAndrew Geissler 
57462598e31SEd Tanous         BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
5757e860f15SJohn Edward Broadbent     });
576729dae72SJennifer Lee }
577729dae72SJennifer Lee 
5780ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body)
5790ed80c8cSGeorge Liu {
5802c6ffdb0SEd Tanous     std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
5812c6ffdb0SEd Tanous 
58262598e31SEd Tanous     BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
5830ed80c8cSGeorge Liu     std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
5840ed80c8cSGeorge Liu                                     std::ofstream::trunc);
5850ed80c8cSGeorge Liu     // set the permission of the file to 640
58689492a15SPatrick Williams     std::filesystem::perms permission = std::filesystem::perms::owner_read |
58789492a15SPatrick Williams                                         std::filesystem::perms::group_read;
5880ed80c8cSGeorge Liu     std::filesystem::permissions(filepath, permission);
5890ed80c8cSGeorge Liu     out << body;
5900ed80c8cSGeorge Liu 
5910ed80c8cSGeorge Liu     if (out.bad())
5920ed80c8cSGeorge Liu     {
5930ed80c8cSGeorge Liu         messages::internalError(res);
5940ed80c8cSGeorge Liu         cleanUp();
5950ed80c8cSGeorge Liu     }
5960ed80c8cSGeorge Liu }
5970ed80c8cSGeorge Liu 
5980ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
5990ed80c8cSGeorge Liu                          const std::string& applyTime)
6000ed80c8cSGeorge Liu {
6010ed80c8cSGeorge Liu     std::string applyTimeNewVal;
6020ed80c8cSGeorge Liu     if (applyTime == "Immediate")
6030ed80c8cSGeorge Liu     {
6040ed80c8cSGeorge Liu         applyTimeNewVal =
6050ed80c8cSGeorge Liu             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
6060ed80c8cSGeorge Liu     }
6070ed80c8cSGeorge Liu     else if (applyTime == "OnReset")
6080ed80c8cSGeorge Liu     {
6090ed80c8cSGeorge Liu         applyTimeNewVal =
6100ed80c8cSGeorge Liu             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
6110ed80c8cSGeorge Liu     }
6120ed80c8cSGeorge Liu     else
6130ed80c8cSGeorge Liu     {
61462598e31SEd Tanous         BMCWEB_LOG_INFO(
61562598e31SEd Tanous             "ApplyTime value is not in the list of acceptable values");
6160ed80c8cSGeorge Liu         messages::propertyValueNotInList(asyncResp->res, applyTime,
6170ed80c8cSGeorge Liu                                          "ApplyTime");
6180ed80c8cSGeorge Liu         return;
6190ed80c8cSGeorge Liu     }
6200ed80c8cSGeorge Liu 
6210ed80c8cSGeorge Liu     // Set the requested image apply time value
6229ae226faSGeorge Liu     sdbusplus::asio::setProperty(
6239ae226faSGeorge Liu         *crow::connections::systemBus, "xyz.openbmc_project.Settings",
6249ae226faSGeorge Liu         "/xyz/openbmc_project/software/apply_time",
6259ae226faSGeorge Liu         "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
6269ae226faSGeorge Liu         applyTimeNewVal, [asyncResp](const boost::system::error_code& ec) {
6270ed80c8cSGeorge Liu         if (ec)
6280ed80c8cSGeorge Liu         {
62962598e31SEd Tanous             BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec);
6300ed80c8cSGeorge Liu             messages::internalError(asyncResp->res);
6310ed80c8cSGeorge Liu             return;
6320ed80c8cSGeorge Liu         }
6330ed80c8cSGeorge Liu         messages::success(asyncResp->res);
6349ae226faSGeorge Liu     });
6350ed80c8cSGeorge Liu }
6360ed80c8cSGeorge Liu 
6370ed80c8cSGeorge Liu inline void
6380ed80c8cSGeorge Liu     updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
6390ed80c8cSGeorge Liu                            const MultipartParser& parser)
6400ed80c8cSGeorge Liu {
6410ed80c8cSGeorge Liu     const std::string* uploadData = nullptr;
6420ed80c8cSGeorge Liu     std::optional<std::string> applyTime = "OnReset";
6430ed80c8cSGeorge Liu     bool targetFound = false;
6440ed80c8cSGeorge Liu     for (const FormPart& formpart : parser.mime_fields)
6450ed80c8cSGeorge Liu     {
6460ed80c8cSGeorge Liu         boost::beast::http::fields::const_iterator it =
6470ed80c8cSGeorge Liu             formpart.fields.find("Content-Disposition");
6480ed80c8cSGeorge Liu         if (it == formpart.fields.end())
6490ed80c8cSGeorge Liu         {
65062598e31SEd Tanous             BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
6510ed80c8cSGeorge Liu             return;
6520ed80c8cSGeorge Liu         }
65362598e31SEd Tanous         BMCWEB_LOG_INFO("Parsing value {}", it->value());
6540ed80c8cSGeorge Liu 
6550ed80c8cSGeorge Liu         // The construction parameters of param_list must start with `;`
6560ed80c8cSGeorge Liu         size_t index = it->value().find(';');
6570ed80c8cSGeorge Liu         if (index == std::string::npos)
6580ed80c8cSGeorge Liu         {
6590ed80c8cSGeorge Liu             continue;
6600ed80c8cSGeorge Liu         }
6610ed80c8cSGeorge Liu 
66289492a15SPatrick Williams         for (const auto& param :
6630ed80c8cSGeorge Liu              boost::beast::http::param_list{it->value().substr(index)})
6640ed80c8cSGeorge Liu         {
6650ed80c8cSGeorge Liu             if (param.first != "name" || param.second.empty())
6660ed80c8cSGeorge Liu             {
6670ed80c8cSGeorge Liu                 continue;
6680ed80c8cSGeorge Liu             }
6690ed80c8cSGeorge Liu 
6700ed80c8cSGeorge Liu             if (param.second == "UpdateParameters")
6710ed80c8cSGeorge Liu             {
6720ed80c8cSGeorge Liu                 std::vector<std::string> targets;
6730ed80c8cSGeorge Liu                 nlohmann::json content =
6740ed80c8cSGeorge Liu                     nlohmann::json::parse(formpart.content);
6750ed80c8cSGeorge Liu                 if (!json_util::readJson(content, asyncResp->res, "Targets",
6760ed80c8cSGeorge Liu                                          targets, "@Redfish.OperationApplyTime",
6770ed80c8cSGeorge Liu                                          applyTime))
6780ed80c8cSGeorge Liu                 {
6790ed80c8cSGeorge Liu                     return;
6800ed80c8cSGeorge Liu                 }
6810ed80c8cSGeorge Liu                 if (targets.size() != 1)
6820ed80c8cSGeorge Liu                 {
6830ed80c8cSGeorge Liu                     messages::propertyValueFormatError(asyncResp->res,
6840ed80c8cSGeorge Liu                                                        "Targets", "");
6850ed80c8cSGeorge Liu                     return;
6860ed80c8cSGeorge Liu                 }
6870ed80c8cSGeorge Liu                 if (targets[0] != "/redfish/v1/Managers/bmc")
6880ed80c8cSGeorge Liu                 {
6890ed80c8cSGeorge Liu                     messages::propertyValueNotInList(asyncResp->res,
6900ed80c8cSGeorge Liu                                                      "Targets/0", targets[0]);
6910ed80c8cSGeorge Liu                     return;
6920ed80c8cSGeorge Liu                 }
6930ed80c8cSGeorge Liu                 targetFound = true;
6940ed80c8cSGeorge Liu             }
6950ed80c8cSGeorge Liu             else if (param.second == "UpdateFile")
6960ed80c8cSGeorge Liu             {
6970ed80c8cSGeorge Liu                 uploadData = &(formpart.content);
6980ed80c8cSGeorge Liu             }
6990ed80c8cSGeorge Liu         }
7000ed80c8cSGeorge Liu     }
7010ed80c8cSGeorge Liu 
7020ed80c8cSGeorge Liu     if (uploadData == nullptr)
7030ed80c8cSGeorge Liu     {
70462598e31SEd Tanous         BMCWEB_LOG_ERROR("Upload data is NULL");
7050ed80c8cSGeorge Liu         messages::propertyMissing(asyncResp->res, "UpdateFile");
7060ed80c8cSGeorge Liu         return;
7070ed80c8cSGeorge Liu     }
7080ed80c8cSGeorge Liu     if (!targetFound)
7090ed80c8cSGeorge Liu     {
7100ed80c8cSGeorge Liu         messages::propertyMissing(asyncResp->res, "targets");
7110ed80c8cSGeorge Liu         return;
7120ed80c8cSGeorge Liu     }
7130ed80c8cSGeorge Liu 
7140ed80c8cSGeorge Liu     setApplyTime(asyncResp, *applyTime);
7150ed80c8cSGeorge Liu 
7160ed80c8cSGeorge Liu     uploadImageFile(asyncResp->res, *uploadData);
7170ed80c8cSGeorge Liu }
7180ed80c8cSGeorge Liu 
719c2051d11SEd Tanous inline void
720c2051d11SEd Tanous     handleUpdateServicePost(App& app, const crow::Request& req,
721c2051d11SEd Tanous                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
722c2051d11SEd Tanous {
7233ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
724c2051d11SEd Tanous     {
725c2051d11SEd Tanous         return;
726c2051d11SEd Tanous     }
727b33a4327SNinad Palsule     std::string_view contentType = req.getHeaderValue("Content-Type");
728b33a4327SNinad Palsule 
72962598e31SEd Tanous     BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
730b33a4327SNinad Palsule 
731b33a4327SNinad Palsule     // Make sure that content type is application/octet-stream or
732b33a4327SNinad Palsule     // multipart/form-data
733*18f8f608SEd Tanous     if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
734b33a4327SNinad Palsule     {
735b33a4327SNinad Palsule         // Setup callback for when new software detected
736b33a4327SNinad Palsule         monitorForSoftwareAvailable(asyncResp, req,
737b33a4327SNinad Palsule                                     "/redfish/v1/UpdateService");
738b33a4327SNinad Palsule 
739b33a4327SNinad Palsule         uploadImageFile(asyncResp->res, req.body());
740b33a4327SNinad Palsule     }
741b33a4327SNinad Palsule     else if (contentType.starts_with("multipart/form-data"))
742b33a4327SNinad Palsule     {
743b33a4327SNinad Palsule         MultipartParser parser;
744c2051d11SEd Tanous 
745c2051d11SEd Tanous         // Setup callback for when new software detected
746b33a4327SNinad Palsule         monitorForSoftwareAvailable(asyncResp, req,
747b33a4327SNinad Palsule                                     "/redfish/v1/UpdateService");
748c2051d11SEd Tanous 
7490ed80c8cSGeorge Liu         ParserError ec = parser.parse(req);
7500ed80c8cSGeorge Liu         if (ec != ParserError::PARSER_SUCCESS)
7510ed80c8cSGeorge Liu         {
7520ed80c8cSGeorge Liu             // handle error
75362598e31SEd Tanous             BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
75462598e31SEd Tanous                              static_cast<int>(ec));
7550ed80c8cSGeorge Liu             messages::internalError(asyncResp->res);
7560ed80c8cSGeorge Liu             return;
7570ed80c8cSGeorge Liu         }
7580ed80c8cSGeorge Liu         updateMultipartContext(asyncResp, parser);
759c2051d11SEd Tanous     }
760b33a4327SNinad Palsule     else
761b33a4327SNinad Palsule     {
76262598e31SEd Tanous         BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
763b33a4327SNinad Palsule         asyncResp->res.result(boost::beast::http::status::bad_request);
764b33a4327SNinad Palsule     }
765b33a4327SNinad Palsule }
766c2051d11SEd Tanous 
7677e860f15SJohn Edward Broadbent inline void requestRoutesUpdateService(App& app)
7681abe55efSEd Tanous {
7697e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
770ed398213SEd Tanous         .privileges(redfish::privileges::getUpdateService)
771002d39b4SEd Tanous         .methods(boost::beast::http::verb::get)(
772002d39b4SEd Tanous             [&app](const crow::Request& req,
773002d39b4SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
7743ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
77545ca1b86SEd Tanous         {
77645ca1b86SEd Tanous             return;
77745ca1b86SEd Tanous         }
7788d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.type"] =
7790ed80c8cSGeorge Liu             "#UpdateService.v1_11_1.UpdateService";
7808d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
7818d1b46d7Szhanghch05         asyncResp->res.jsonValue["Id"] = "UpdateService";
782002d39b4SEd Tanous         asyncResp->res.jsonValue["Description"] = "Service for Software Update";
7838d1b46d7Szhanghch05         asyncResp->res.jsonValue["Name"] = "Update Service";
7844dc23f3fSEd Tanous 
7857e860f15SJohn Edward Broadbent         asyncResp->res.jsonValue["HttpPushUri"] =
7864dc23f3fSEd Tanous             "/redfish/v1/UpdateService/update";
7870ed80c8cSGeorge Liu         asyncResp->res.jsonValue["MultipartHttpPushUri"] =
7880ed80c8cSGeorge Liu             "/redfish/v1/UpdateService/update";
7894dc23f3fSEd Tanous 
7900f74e643SEd Tanous         // UpdateService cannot be disabled
7918d1b46d7Szhanghch05         asyncResp->res.jsonValue["ServiceEnabled"] = true;
7921476687dSEd Tanous         asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
7931476687dSEd Tanous             "/redfish/v1/UpdateService/FirmwareInventory";
794d61e5194STejas Patil         // Get the MaxImageSizeBytes
795d61e5194STejas Patil         asyncResp->res.jsonValue["MaxImageSizeBytes"] =
796d61e5194STejas Patil             bmcwebHttpReqBodyLimitMb * 1024 * 1024;
797d61e5194STejas Patil 
7980554c984SAndrew Geissler #ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
7990554c984SAndrew Geissler         // Update Actions object.
8000554c984SAndrew Geissler         nlohmann::json& updateSvcSimpleUpdate =
801002d39b4SEd Tanous             asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
8020554c984SAndrew Geissler         updateSvcSimpleUpdate["target"] =
8030554c984SAndrew Geissler             "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
804002d39b4SEd Tanous         updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
805002d39b4SEd Tanous             "TFTP"};
8060554c984SAndrew Geissler #endif
807274dfe62SJayashankar Padath         // Get the current ApplyTime value
8081e1e598dSJonathan Doman         sdbusplus::asio::getProperty<std::string>(
8091e1e598dSJonathan Doman             *crow::connections::systemBus, "xyz.openbmc_project.Settings",
8101e1e598dSJonathan Doman             "/xyz/openbmc_project/software/apply_time",
8111e1e598dSJonathan Doman             "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
8125e7e2dc5SEd Tanous             [asyncResp](const boost::system::error_code& ec,
8131e1e598dSJonathan Doman                         const std::string& applyTime) {
814274dfe62SJayashankar Padath             if (ec)
815274dfe62SJayashankar Padath             {
81662598e31SEd Tanous                 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
8178d1b46d7Szhanghch05                 messages::internalError(asyncResp->res);
818274dfe62SJayashankar Padath                 return;
819274dfe62SJayashankar Padath             }
820274dfe62SJayashankar Padath 
821274dfe62SJayashankar Padath             // Store the ApplyTime Value
8221e1e598dSJonathan Doman             if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
8231e1e598dSJonathan Doman                              "RequestedApplyTimes.Immediate")
824274dfe62SJayashankar Padath             {
825002d39b4SEd Tanous                 asyncResp->res.jsonValue["HttpPushUriOptions"]
8267e860f15SJohn Edward Broadbent                                         ["HttpPushUriApplyTime"]["ApplyTime"] =
8277e860f15SJohn Edward Broadbent                     "Immediate";
828274dfe62SJayashankar Padath             }
829002d39b4SEd Tanous             else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
8301e1e598dSJonathan Doman                                   "RequestedApplyTimes.OnReset")
831274dfe62SJayashankar Padath             {
832002d39b4SEd Tanous                 asyncResp->res.jsonValue["HttpPushUriOptions"]
8337e860f15SJohn Edward Broadbent                                         ["HttpPushUriApplyTime"]["ApplyTime"] =
8347e860f15SJohn Edward Broadbent                     "OnReset";
835274dfe62SJayashankar Padath             }
8361e1e598dSJonathan Doman         });
8377e860f15SJohn Edward Broadbent     });
8387e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
839ed398213SEd Tanous         .privileges(redfish::privileges::patchUpdateService)
840002d39b4SEd Tanous         .methods(boost::beast::http::verb::patch)(
841002d39b4SEd Tanous             [&app](const crow::Request& req,
842002d39b4SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
8433ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
84445ca1b86SEd Tanous         {
84545ca1b86SEd Tanous             return;
84645ca1b86SEd Tanous         }
84762598e31SEd Tanous         BMCWEB_LOG_DEBUG("doPatch...");
848fa1a5a38SJayashankar Padath 
849274dfe62SJayashankar Padath         std::optional<nlohmann::json> pushUriOptions;
850002d39b4SEd Tanous         if (!json_util::readJsonPatch(req, asyncResp->res, "HttpPushUriOptions",
851002d39b4SEd Tanous                                       pushUriOptions))
852fa1a5a38SJayashankar Padath         {
853fa1a5a38SJayashankar Padath             return;
854fa1a5a38SJayashankar Padath         }
855fa1a5a38SJayashankar Padath 
856274dfe62SJayashankar Padath         if (pushUriOptions)
857274dfe62SJayashankar Padath         {
858274dfe62SJayashankar Padath             std::optional<nlohmann::json> pushUriApplyTime;
8598d1b46d7Szhanghch05             if (!json_util::readJson(*pushUriOptions, asyncResp->res,
860002d39b4SEd Tanous                                      "HttpPushUriApplyTime", pushUriApplyTime))
861274dfe62SJayashankar Padath             {
862274dfe62SJayashankar Padath                 return;
863274dfe62SJayashankar Padath             }
864274dfe62SJayashankar Padath 
865274dfe62SJayashankar Padath             if (pushUriApplyTime)
866274dfe62SJayashankar Padath             {
867274dfe62SJayashankar Padath                 std::optional<std::string> applyTime;
8680fda0f12SGeorge Liu                 if (!json_util::readJson(*pushUriApplyTime, asyncResp->res,
8690fda0f12SGeorge Liu                                          "ApplyTime", applyTime))
870274dfe62SJayashankar Padath                 {
871274dfe62SJayashankar Padath                     return;
872274dfe62SJayashankar Padath                 }
873274dfe62SJayashankar Padath 
874274dfe62SJayashankar Padath                 if (applyTime)
875fa1a5a38SJayashankar Padath                 {
8760ed80c8cSGeorge Liu                     setApplyTime(asyncResp, *applyTime);
877fa1a5a38SJayashankar Padath                 }
878274dfe62SJayashankar Padath             }
879fa1a5a38SJayashankar Padath         }
8807e860f15SJohn Edward Broadbent     });
881c2051d11SEd Tanous 
8824dc23f3fSEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
8834dc23f3fSEd Tanous         .privileges(redfish::privileges::postUpdateService)
8847e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::post)(
885c2051d11SEd Tanous             std::bind_front(handleUpdateServicePost, std::ref(app)));
886729dae72SJennifer Lee }
887729dae72SJennifer Lee 
8887e860f15SJohn Edward Broadbent inline void requestRoutesSoftwareInventoryCollection(App& app)
8891abe55efSEd Tanous {
8907e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
891ed398213SEd Tanous         .privileges(redfish::privileges::getSoftwareInventoryCollection)
8921476687dSEd Tanous         .methods(boost::beast::http::verb::get)(
8931476687dSEd Tanous             [&app](const crow::Request& req,
8941476687dSEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
8953ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
89645ca1b86SEd Tanous         {
89745ca1b86SEd Tanous             return;
89845ca1b86SEd Tanous         }
8998d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.type"] =
9000f74e643SEd Tanous             "#SoftwareInventoryCollection.SoftwareInventoryCollection";
9018d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.id"] =
9020f74e643SEd Tanous             "/redfish/v1/UpdateService/FirmwareInventory";
903002d39b4SEd Tanous         asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
90408d81adaSJohn Edward Broadbent         const std::array<const std::string_view, 1> iface = {
905e99073f5SGeorge Liu             "xyz.openbmc_project.Software.Version"};
9066c4eb9deSJennifer Lee 
90708d81adaSJohn Edward Broadbent         redfish::collection_util::getCollectionMembers(
90808d81adaSJohn Edward Broadbent             asyncResp,
90908d81adaSJohn Edward Broadbent             boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"),
91008d81adaSJohn Edward Broadbent             iface, "/xyz/openbmc_project/software");
9117e860f15SJohn Edward Broadbent     });
912729dae72SJennifer Lee }
91387d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */
9147e860f15SJohn Edward Broadbent inline static void
915ac106bf6SEd Tanous     getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
91687d84729SAndrew Geissler                     const std::string& purpose)
91787d84729SAndrew Geissler {
918eee0013eSWilly Tu     if (purpose == sw_util::bmcPurpose)
91987d84729SAndrew Geissler     {
920ac106bf6SEd Tanous         nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
9211476687dSEd Tanous         nlohmann::json::object_t item;
9221476687dSEd Tanous         item["@odata.id"] = "/redfish/v1/Managers/bmc";
923b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
924ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
925ac106bf6SEd Tanous             relatedItem.size();
92687d84729SAndrew Geissler     }
927eee0013eSWilly Tu     else if (purpose == sw_util::biosPurpose)
92887d84729SAndrew Geissler     {
929ac106bf6SEd Tanous         nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
9301476687dSEd Tanous         nlohmann::json::object_t item;
9311476687dSEd Tanous         item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
932b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
933ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
934ac106bf6SEd Tanous             relatedItem.size();
93587d84729SAndrew Geissler     }
93687d84729SAndrew Geissler     else
93787d84729SAndrew Geissler     {
938bf2ddedeSCarson Labrado         BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
93987d84729SAndrew Geissler     }
94087d84729SAndrew Geissler }
94187d84729SAndrew Geissler 
942af24660dSWilly Tu inline void
943af24660dSWilly Tu     getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
944af24660dSWilly Tu                        const std::string& service, const std::string& path,
945af24660dSWilly Tu                        const std::string& swId)
946af24660dSWilly Tu {
947d1bde9e5SKrzysztof Grobelny     sdbusplus::asio::getAllProperties(
948d1bde9e5SKrzysztof Grobelny         *crow::connections::systemBus, service, path,
949d1bde9e5SKrzysztof Grobelny         "xyz.openbmc_project.Software.Version",
950af24660dSWilly Tu         [asyncResp,
9518b24275dSEd Tanous          swId](const boost::system::error_code& ec,
952af24660dSWilly Tu                const dbus::utility::DBusPropertiesMap& propertiesList) {
9538b24275dSEd Tanous         if (ec)
954af24660dSWilly Tu         {
955af24660dSWilly Tu             messages::internalError(asyncResp->res);
956af24660dSWilly Tu             return;
957af24660dSWilly Tu         }
958d1bde9e5SKrzysztof Grobelny 
959af24660dSWilly Tu         const std::string* swInvPurpose = nullptr;
960af24660dSWilly Tu         const std::string* version = nullptr;
961d1bde9e5SKrzysztof Grobelny 
962d1bde9e5SKrzysztof Grobelny         const bool success = sdbusplus::unpackPropertiesNoThrow(
963d1bde9e5SKrzysztof Grobelny             dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
964d1bde9e5SKrzysztof Grobelny             swInvPurpose, "Version", version);
965d1bde9e5SKrzysztof Grobelny 
966d1bde9e5SKrzysztof Grobelny         if (!success)
967af24660dSWilly Tu         {
968d1bde9e5SKrzysztof Grobelny             messages::internalError(asyncResp->res);
969d1bde9e5SKrzysztof Grobelny             return;
970af24660dSWilly Tu         }
971af24660dSWilly Tu 
972af24660dSWilly Tu         if (swInvPurpose == nullptr)
973af24660dSWilly Tu         {
97462598e31SEd Tanous             BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
975af24660dSWilly Tu             messages::internalError(asyncResp->res);
976af24660dSWilly Tu             return;
977af24660dSWilly Tu         }
978af24660dSWilly Tu 
97962598e31SEd Tanous         BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
980af24660dSWilly Tu 
981af24660dSWilly Tu         if (version == nullptr)
982af24660dSWilly Tu         {
98362598e31SEd Tanous             BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
984af24660dSWilly Tu 
985af24660dSWilly Tu             messages::internalError(asyncResp->res);
986af24660dSWilly Tu 
987af24660dSWilly Tu             return;
988af24660dSWilly Tu         }
989af24660dSWilly Tu         asyncResp->res.jsonValue["Version"] = *version;
990af24660dSWilly Tu         asyncResp->res.jsonValue["Id"] = swId;
991af24660dSWilly Tu 
992af24660dSWilly Tu         // swInvPurpose is of format:
993af24660dSWilly Tu         // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
994af24660dSWilly Tu         // Translate this to "ABC image"
995af24660dSWilly Tu         size_t endDesc = swInvPurpose->rfind('.');
996af24660dSWilly Tu         if (endDesc == std::string::npos)
997af24660dSWilly Tu         {
998af24660dSWilly Tu             messages::internalError(asyncResp->res);
999af24660dSWilly Tu             return;
1000af24660dSWilly Tu         }
1001af24660dSWilly Tu         endDesc++;
1002af24660dSWilly Tu         if (endDesc >= swInvPurpose->size())
1003af24660dSWilly Tu         {
1004af24660dSWilly Tu             messages::internalError(asyncResp->res);
1005af24660dSWilly Tu             return;
1006af24660dSWilly Tu         }
1007af24660dSWilly Tu 
1008af24660dSWilly Tu         std::string formatDesc = swInvPurpose->substr(endDesc);
1009af24660dSWilly Tu         asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1010af24660dSWilly Tu         getRelatedItems(asyncResp, *swInvPurpose);
1011d1bde9e5SKrzysztof Grobelny     });
1012af24660dSWilly Tu }
1013af24660dSWilly Tu 
10147e860f15SJohn Edward Broadbent inline void requestRoutesSoftwareInventory(App& app)
10151abe55efSEd Tanous {
10167e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1017ed398213SEd Tanous         .privileges(redfish::privileges::getSoftwareInventory)
1018002d39b4SEd Tanous         .methods(boost::beast::http::verb::get)(
1019002d39b4SEd Tanous             [&app](const crow::Request& req,
102045ca1b86SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
10217e860f15SJohn Edward Broadbent                    const std::string& param) {
10223ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
102345ca1b86SEd Tanous         {
102445ca1b86SEd Tanous             return;
102545ca1b86SEd Tanous         }
10263ae837c9SEd Tanous         std::shared_ptr<std::string> swId =
10277e860f15SJohn Edward Broadbent             std::make_shared<std::string>(param);
1028c711bf86SEd Tanous 
1029ef4c65b7SEd Tanous         asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1030ef4c65b7SEd Tanous             "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
1031c711bf86SEd Tanous 
1032e99073f5SGeorge Liu         constexpr std::array<std::string_view, 1> interfaces = {
1033e99073f5SGeorge Liu             "xyz.openbmc_project.Software.Version"};
1034e99073f5SGeorge Liu         dbus::utility::getSubTree(
1035e99073f5SGeorge Liu             "/", 0, interfaces,
1036b9d36b47SEd Tanous             [asyncResp,
1037e99073f5SGeorge Liu              swId](const boost::system::error_code& ec,
1038b9d36b47SEd Tanous                    const dbus::utility::MapperGetSubTreeResponse& subtree) {
103962598e31SEd Tanous             BMCWEB_LOG_DEBUG("doGet callback...");
10401abe55efSEd Tanous             if (ec)
10411abe55efSEd Tanous             {
1042f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
10436c4eb9deSJennifer Lee                 return;
10446c4eb9deSJennifer Lee             }
10456c4eb9deSJennifer Lee 
10466913228dSAndrew Geissler             // Ensure we find our input swId, otherwise return an error
10476913228dSAndrew Geissler             bool found = false;
1048002d39b4SEd Tanous             for (const std::pair<std::string,
10497e860f15SJohn Edward Broadbent                                  std::vector<std::pair<
1050002d39b4SEd Tanous                                      std::string, std::vector<std::string>>>>&
1051002d39b4SEd Tanous                      obj : subtree)
10521abe55efSEd Tanous             {
105311ba3979SEd Tanous                 if (!obj.first.ends_with(*swId))
10541abe55efSEd Tanous                 {
1055acb7cfb4SJennifer Lee                     continue;
1056acb7cfb4SJennifer Lee                 }
1057acb7cfb4SJennifer Lee 
105826f6976fSEd Tanous                 if (obj.second.empty())
10591abe55efSEd Tanous                 {
1060acb7cfb4SJennifer Lee                     continue;
1061acb7cfb4SJennifer Lee                 }
10626c4eb9deSJennifer Lee 
10636913228dSAndrew Geissler                 found = true;
1064eee0013eSWilly Tu                 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1065af24660dSWilly Tu                 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1066af24660dSWilly Tu                                    *swId);
10676c4eb9deSJennifer Lee             }
10686913228dSAndrew Geissler             if (!found)
10696913228dSAndrew Geissler             {
107062598e31SEd Tanous                 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
10716913228dSAndrew Geissler                 messages::resourceMissingAtURI(
1072ef4c65b7SEd Tanous                     asyncResp->res,
1073ef4c65b7SEd Tanous                     boost::urls::format(
1074ef4c65b7SEd Tanous                         "/redfish/v1/UpdateService/FirmwareInventory/{}",
1075ef4c65b7SEd Tanous                         *swId));
10766913228dSAndrew Geissler                 return;
10776913228dSAndrew Geissler             }
10784e68c45bSAyushi Smriti             asyncResp->res.jsonValue["@odata.type"] =
10794e68c45bSAyushi Smriti                 "#SoftwareInventory.v1_1_0.SoftwareInventory";
10804e68c45bSAyushi Smriti             asyncResp->res.jsonValue["Name"] = "Software Inventory";
10814e68c45bSAyushi Smriti             asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
10823f8a743aSAppaRao Puli 
10833f8a743aSAppaRao Puli             asyncResp->res.jsonValue["Updateable"] = false;
1084eee0013eSWilly Tu             sw_util::getSwUpdatableStatus(asyncResp, swId);
1085e99073f5SGeorge Liu         });
10867e860f15SJohn Edward Broadbent     });
10876c4eb9deSJennifer Lee }
1088729dae72SJennifer Lee 
1089729dae72SJennifer Lee } // namespace redfish
1090