xref: /openbmc/bmcweb/features/redfish/lib/update_service.hpp (revision b33a4327cb87c7e1a71e6e0d49823111a72181a3)
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"
233ccb3adbSEd Tanous #include "query.hpp"
243ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
25a8e884fcSEd Tanous #include "task.hpp"
2608d81adaSJohn Edward Broadbent #include "utils/collection.hpp"
273ccb3adbSEd Tanous #include "utils/dbus_utils.hpp"
283ccb3adbSEd Tanous #include "utils/sw_utils.hpp"
293ccb3adbSEd Tanous 
30d093c996SEd Tanous #include <boost/algorithm/string/case_conv.hpp>
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 {
6586adcd6dSAndrew Geissler     BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
6686adcd6dSAndrew Geissler     crow::connections::systemBus->async_method_call(
675e7e2dc5SEd Tanous         [](const boost::system::error_code& errorCode) {
6881ce609eSEd Tanous         if (errorCode)
6986adcd6dSAndrew Geissler         {
7081ce609eSEd Tanous             BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
7181ce609eSEd Tanous             BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
7286adcd6dSAndrew Geissler         }
7386adcd6dSAndrew Geissler         },
7486adcd6dSAndrew Geissler         service, objPath, "org.freedesktop.DBus.Properties", "Set",
7586adcd6dSAndrew Geissler         "xyz.openbmc_project.Software.Activation", "RequestedActivation",
76168e20c1SEd Tanous         dbus::utility::DbusVariantType(
770fda0f12SGeorge Liu             "xyz.openbmc_project.Software.Activation.RequestedActivations.Active"));
7886adcd6dSAndrew Geissler }
790554c984SAndrew Geissler 
800554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
810554c984SAndrew Geissler // then no asyncResp updates will occur
828d1b46d7Szhanghch05 static void
838d1b46d7Szhanghch05     softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
8459d494eeSPatrick Williams                            sdbusplus::message_t& m, task::Payload&& payload)
8586adcd6dSAndrew Geissler {
86b9d36b47SEd Tanous     dbus::utility::DBusInteracesMap interfacesProperties;
8786adcd6dSAndrew Geissler 
8886adcd6dSAndrew Geissler     sdbusplus::message::object_path objPath;
8986adcd6dSAndrew Geissler 
9086adcd6dSAndrew Geissler     m.read(objPath, interfacesProperties);
9186adcd6dSAndrew Geissler 
9286adcd6dSAndrew Geissler     BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
93e3eb3d63SEd Tanous     for (const auto& interface : interfacesProperties)
9486adcd6dSAndrew Geissler     {
9586adcd6dSAndrew Geissler         BMCWEB_LOG_DEBUG << "interface = " << interface.first;
9686adcd6dSAndrew Geissler 
9786adcd6dSAndrew Geissler         if (interface.first == "xyz.openbmc_project.Software.Activation")
9886adcd6dSAndrew Geissler         {
9986adcd6dSAndrew Geissler             // Retrieve service and activate
1002b73119cSGeorge Liu             constexpr std::array<std::string_view, 1> interfaces = {
1012b73119cSGeorge Liu                 "xyz.openbmc_project.Software.Activation"};
1022b73119cSGeorge Liu             dbus::utility::getDbusObject(
1032b73119cSGeorge Liu                 objPath.str, interfaces,
104a3e65892SEd Tanous                 [objPath, asyncResp, payload(std::move(payload))](
1052b73119cSGeorge Liu                     const boost::system::error_code& errorCode,
106a3e65892SEd Tanous                     const std::vector<
107a3e65892SEd Tanous                         std::pair<std::string, std::vector<std::string>>>&
108a3e65892SEd Tanous                         objInfo) mutable {
10981ce609eSEd Tanous                 if (errorCode)
11086adcd6dSAndrew Geissler                 {
11181ce609eSEd Tanous                     BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
112002d39b4SEd Tanous                     BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
1130554c984SAndrew Geissler                     if (asyncResp)
1140554c984SAndrew Geissler                     {
11586adcd6dSAndrew Geissler                         messages::internalError(asyncResp->res);
1160554c984SAndrew Geissler                     }
11786adcd6dSAndrew Geissler                     cleanUp();
11886adcd6dSAndrew Geissler                     return;
11986adcd6dSAndrew Geissler                 }
12086adcd6dSAndrew Geissler                 // Ensure we only got one service back
12186adcd6dSAndrew Geissler                 if (objInfo.size() != 1)
12286adcd6dSAndrew Geissler                 {
12386adcd6dSAndrew Geissler                     BMCWEB_LOG_ERROR << "Invalid Object Size "
12486adcd6dSAndrew Geissler                                      << objInfo.size();
1250554c984SAndrew Geissler                     if (asyncResp)
1260554c984SAndrew Geissler                     {
12786adcd6dSAndrew Geissler                         messages::internalError(asyncResp->res);
1280554c984SAndrew Geissler                     }
12986adcd6dSAndrew Geissler                     cleanUp();
13086adcd6dSAndrew Geissler                     return;
13186adcd6dSAndrew Geissler                 }
13286adcd6dSAndrew Geissler                 // cancel timer only when
13386adcd6dSAndrew Geissler                 // xyz.openbmc_project.Software.Activation interface
13486adcd6dSAndrew Geissler                 // is added
13586adcd6dSAndrew Geissler                 fwAvailableTimer = nullptr;
13686adcd6dSAndrew Geissler 
13786adcd6dSAndrew Geissler                 activateImage(objPath.str, objInfo[0].first);
1380554c984SAndrew Geissler                 if (asyncResp)
1390554c984SAndrew Geissler                 {
14032898ceaSJames Feist                     std::shared_ptr<task::TaskData> task =
14132898ceaSJames Feist                         task::TaskData::createTask(
1425e7e2dc5SEd Tanous                             [](const boost::system::error_code& ec,
14359d494eeSPatrick Williams                                sdbusplus::message_t& msg,
1441214b7e7SGunnar Mills                                const std::shared_ptr<task::TaskData>&
1451214b7e7SGunnar Mills                                    taskData) {
14632898ceaSJames Feist                         if (ec)
14732898ceaSJames Feist                         {
14832898ceaSJames Feist                             return task::completed;
14932898ceaSJames Feist                         }
15032898ceaSJames Feist 
15132898ceaSJames Feist                         std::string iface;
152b9d36b47SEd Tanous                         dbus::utility::DBusPropertiesMap values;
153fd9ab9e1SJames Feist 
154002d39b4SEd Tanous                         std::string index = std::to_string(taskData->index);
15532898ceaSJames Feist                         msg.read(iface, values);
156fd9ab9e1SJames Feist 
157002d39b4SEd Tanous                         if (iface == "xyz.openbmc_project.Software.Activation")
158fd9ab9e1SJames Feist                         {
1590fb5b505SGayathri Leburu                             const std::string* state = nullptr;
160b9d36b47SEd Tanous                             for (const auto& property : values)
16132898ceaSJames Feist                             {
162b9d36b47SEd Tanous                                 if (property.first == "Activation")
163b9d36b47SEd Tanous                                 {
1640fb5b505SGayathri Leburu                                     state = std::get_if<std::string>(
165b9d36b47SEd Tanous                                         &property.second);
1660fb5b505SGayathri Leburu                                     if (state == nullptr)
167b9d36b47SEd Tanous                                     {
168002d39b4SEd Tanous                                         taskData->messages.emplace_back(
169002d39b4SEd Tanous                                             messages::internalError());
170b9d36b47SEd Tanous                                         return task::completed;
171b9d36b47SEd Tanous                                     }
172b9d36b47SEd Tanous                                 }
173b9d36b47SEd Tanous                             }
17432898ceaSJames Feist 
17532898ceaSJames Feist                             if (state == nullptr)
17632898ceaSJames Feist                             {
177b9d36b47SEd Tanous                                 return !task::completed;
17832898ceaSJames Feist                             }
17932898ceaSJames Feist 
18011ba3979SEd Tanous                             if (state->ends_with("Invalid") ||
18111ba3979SEd Tanous                                 state->ends_with("Failed"))
18232898ceaSJames Feist                             {
18332898ceaSJames Feist                                 taskData->state = "Exception";
18432898ceaSJames Feist                                 taskData->status = "Warning";
18532898ceaSJames Feist                                 taskData->messages.emplace_back(
186e5d5006bSJames Feist                                     messages::taskAborted(index));
18732898ceaSJames Feist                                 return task::completed;
18832898ceaSJames Feist                             }
18932898ceaSJames Feist 
19011ba3979SEd Tanous                             if (state->ends_with("Staged"))
19132898ceaSJames Feist                             {
192fd9ab9e1SJames Feist                                 taskData->state = "Stopping";
193fd9ab9e1SJames Feist                                 taskData->messages.emplace_back(
194fd9ab9e1SJames Feist                                     messages::taskPaused(index));
195fd9ab9e1SJames Feist 
196fd9ab9e1SJames Feist                                 // its staged, set a long timer to
197fd9ab9e1SJames Feist                                 // allow them time to complete the
198fd9ab9e1SJames Feist                                 // update (probably cycle the
199fd9ab9e1SJames Feist                                 // system) if this expires then
200fd9ab9e1SJames Feist                                 // task will be cancelled
201002d39b4SEd Tanous                                 taskData->extendTimer(std::chrono::hours(5));
20232898ceaSJames Feist                                 return !task::completed;
20332898ceaSJames Feist                             }
20432898ceaSJames Feist 
20511ba3979SEd Tanous                             if (state->ends_with("Active"))
20632898ceaSJames Feist                             {
20732898ceaSJames Feist                                 taskData->messages.emplace_back(
208002d39b4SEd Tanous                                     messages::taskCompletedOK(index));
20932898ceaSJames Feist                                 taskData->state = "Completed";
21032898ceaSJames Feist                                 return task::completed;
21132898ceaSJames Feist                             }
212fd9ab9e1SJames Feist                         }
2130fda0f12SGeorge Liu                         else if (
2140fda0f12SGeorge Liu                             iface ==
2150fda0f12SGeorge Liu                             "xyz.openbmc_project.Software.ActivationProgress")
216fd9ab9e1SJames Feist                         {
217b9d36b47SEd Tanous                             const uint8_t* progress = nullptr;
218b9d36b47SEd Tanous                             for (const auto& property : values)
219fd9ab9e1SJames Feist                             {
220b9d36b47SEd Tanous                                 if (property.first == "Progress")
221b9d36b47SEd Tanous                                 {
2220fb5b505SGayathri Leburu                                     progress =
2230fb5b505SGayathri Leburu                                         std::get_if<uint8_t>(&property.second);
2240fb5b505SGayathri Leburu                                     if (progress == nullptr)
225b9d36b47SEd Tanous                                     {
226002d39b4SEd Tanous                                         taskData->messages.emplace_back(
227002d39b4SEd Tanous                                             messages::internalError());
228b9d36b47SEd Tanous                                         return task::completed;
229fd9ab9e1SJames Feist                                     }
230b9d36b47SEd Tanous                                 }
231b9d36b47SEd Tanous                             }
232fd9ab9e1SJames Feist 
233fd9ab9e1SJames Feist                             if (progress == nullptr)
234fd9ab9e1SJames Feist                             {
235b9d36b47SEd Tanous                                 return !task::completed;
236fd9ab9e1SJames Feist                             }
2370fb5b505SGayathri Leburu                             taskData->percentComplete = *progress;
238fd9ab9e1SJames Feist                             taskData->messages.emplace_back(
2390fb5b505SGayathri Leburu                                 messages::taskProgressChanged(index,
2400fb5b505SGayathri Leburu                                                               *progress));
241fd9ab9e1SJames Feist 
242fd9ab9e1SJames Feist                             // if we're getting status updates it's
243fd9ab9e1SJames Feist                             // still alive, update timer
244002d39b4SEd Tanous                             taskData->extendTimer(std::chrono::minutes(5));
245fd9ab9e1SJames Feist                         }
24632898ceaSJames Feist 
24732898ceaSJames Feist                         // as firmware update often results in a
24832898ceaSJames Feist                         // reboot, the task  may never "complete"
24932898ceaSJames Feist                         // unless it is an error
25032898ceaSJames Feist 
25132898ceaSJames Feist                         return !task::completed;
25232898ceaSJames Feist                             },
2530fda0f12SGeorge Liu                             "type='signal',interface='org.freedesktop.DBus.Properties',"
254fd9ab9e1SJames Feist                             "member='PropertiesChanged',path='" +
25532898ceaSJames Feist                                 objPath.str + "'");
25632898ceaSJames Feist                     task->startTimer(std::chrono::minutes(5));
25732898ceaSJames Feist                     task->populateResp(asyncResp->res);
258a3e65892SEd Tanous                     task->payload.emplace(std::move(payload));
2590554c984SAndrew Geissler                 }
26086adcd6dSAndrew Geissler                 fwUpdateInProgress = false;
2612b73119cSGeorge Liu                 });
26262bafc01SPatrick Williams 
26362bafc01SPatrick Williams             break;
26486adcd6dSAndrew Geissler         }
26586adcd6dSAndrew Geissler     }
26686adcd6dSAndrew Geissler }
26786adcd6dSAndrew Geissler 
2680554c984SAndrew Geissler // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
2690554c984SAndrew Geissler // then no asyncResp updates will occur
270b5a76932SEd Tanous static void monitorForSoftwareAvailable(
2718d1b46d7Szhanghch05     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2728d1b46d7Szhanghch05     const crow::Request& req, const std::string& url,
2735d138943SGunnar Mills     int timeoutTimeSeconds = 25)
27486adcd6dSAndrew Geissler {
27586adcd6dSAndrew Geissler     // Only allow one FW update at a time
276e05aec50SEd Tanous     if (fwUpdateInProgress)
27786adcd6dSAndrew Geissler     {
2780554c984SAndrew Geissler         if (asyncResp)
2790554c984SAndrew Geissler         {
28086adcd6dSAndrew Geissler             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
2810554c984SAndrew Geissler         }
28286adcd6dSAndrew Geissler         return;
28386adcd6dSAndrew Geissler     }
28486adcd6dSAndrew Geissler 
2850554c984SAndrew Geissler     fwAvailableTimer =
286271584abSEd Tanous         std::make_unique<boost::asio::steady_timer>(*req.ioService);
28786adcd6dSAndrew Geissler 
288271584abSEd Tanous     fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
28986adcd6dSAndrew Geissler 
29086adcd6dSAndrew Geissler     fwAvailableTimer->async_wait(
29186adcd6dSAndrew Geissler         [asyncResp](const boost::system::error_code& ec) {
29286adcd6dSAndrew Geissler         cleanUp();
29386adcd6dSAndrew Geissler         if (ec == boost::asio::error::operation_aborted)
29486adcd6dSAndrew Geissler         {
29586adcd6dSAndrew Geissler             // expected, we were canceled before the timer completed.
29686adcd6dSAndrew Geissler             return;
29786adcd6dSAndrew Geissler         }
29886adcd6dSAndrew Geissler         BMCWEB_LOG_ERROR
29986adcd6dSAndrew Geissler             << "Timed out waiting for firmware object being created";
300002d39b4SEd Tanous         BMCWEB_LOG_ERROR << "FW image may has already been uploaded to server";
30186adcd6dSAndrew Geissler         if (ec)
30286adcd6dSAndrew Geissler         {
30386adcd6dSAndrew Geissler             BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
30486adcd6dSAndrew Geissler             return;
30586adcd6dSAndrew Geissler         }
3060554c984SAndrew Geissler         if (asyncResp)
3070554c984SAndrew Geissler         {
30886adcd6dSAndrew Geissler             redfish::messages::internalError(asyncResp->res);
3090554c984SAndrew Geissler         }
31086adcd6dSAndrew Geissler     });
311a3e65892SEd Tanous     task::Payload payload(req);
31259d494eeSPatrick Williams     auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
31386adcd6dSAndrew Geissler         BMCWEB_LOG_DEBUG << "Match fired";
314a3e65892SEd Tanous         softwareInterfaceAdded(asyncResp, m, std::move(payload));
31586adcd6dSAndrew Geissler     };
31686adcd6dSAndrew Geissler 
31786adcd6dSAndrew Geissler     fwUpdateInProgress = true;
31886adcd6dSAndrew Geissler 
31959d494eeSPatrick Williams     fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
32086adcd6dSAndrew Geissler         *crow::connections::systemBus,
32186adcd6dSAndrew Geissler         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
32286adcd6dSAndrew Geissler         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
32386adcd6dSAndrew Geissler         callback);
3244cde5d90SJames Feist 
32559d494eeSPatrick Williams     fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
3264cde5d90SJames Feist         *crow::connections::systemBus,
327e1cc4828SBrian Ma         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
328e1cc4828SBrian Ma         "member='InterfacesAdded',"
329e1cc4828SBrian Ma         "path='/xyz/openbmc_project/logging'",
33059d494eeSPatrick Williams         [asyncResp, url](sdbusplus::message_t& m) {
331002d39b4SEd Tanous         std::vector<std::pair<std::string, dbus::utility::DBusPropertiesMap>>
332e1cc4828SBrian Ma             interfacesProperties;
333e1cc4828SBrian Ma         sdbusplus::message::object_path objPath;
334e1cc4828SBrian Ma         m.read(objPath, interfacesProperties);
335e1cc4828SBrian Ma         BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
336e1cc4828SBrian Ma         for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
337e1cc4828SBrian Ma                  interface : interfacesProperties)
338e1cc4828SBrian Ma         {
339e1cc4828SBrian Ma             if (interface.first == "xyz.openbmc_project.Logging.Entry")
340e1cc4828SBrian Ma             {
341711ac7a9SEd Tanous                 for (const std::pair<std::string,
342002d39b4SEd Tanous                                      dbus::utility::DbusVariantType>& value :
343002d39b4SEd Tanous                      interface.second)
3444cde5d90SJames Feist                 {
345711ac7a9SEd Tanous                     if (value.first != "Message")
346711ac7a9SEd Tanous                     {
347711ac7a9SEd Tanous                         continue;
3484cde5d90SJames Feist                     }
349e1cc4828SBrian Ma                     const std::string* type =
350711ac7a9SEd Tanous                         std::get_if<std::string>(&value.second);
3514cde5d90SJames Feist                     if (type == nullptr)
3524cde5d90SJames Feist                     {
353e1cc4828SBrian Ma                         // if this was our message, timeout will cover it
3544cde5d90SJames Feist                         return;
3554cde5d90SJames Feist                     }
356e1cc4828SBrian Ma                     fwAvailableTimer = nullptr;
3574cde5d90SJames Feist                     if (*type ==
3584cde5d90SJames Feist                         "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
3594cde5d90SJames Feist                     {
360002d39b4SEd Tanous                         redfish::messages::invalidUpload(asyncResp->res, url,
361002d39b4SEd Tanous                                                          "Invalid archive");
3624cde5d90SJames Feist                     }
363e1cc4828SBrian Ma                     else if (*type ==
364e1cc4828SBrian Ma                              "xyz.openbmc_project.Software.Image.Error."
365e1cc4828SBrian Ma                              "ManifestFileFailure")
3664cde5d90SJames Feist                     {
367002d39b4SEd Tanous                         redfish::messages::invalidUpload(asyncResp->res, url,
368002d39b4SEd Tanous                                                          "Invalid manifest");
3694cde5d90SJames Feist                     }
370e1cc4828SBrian Ma                     else if (
371e1cc4828SBrian Ma                         *type ==
3724cde5d90SJames Feist                         "xyz.openbmc_project.Software.Image.Error.ImageFailure")
3734cde5d90SJames Feist                     {
374e1cc4828SBrian Ma                         redfish::messages::invalidUpload(
375e1cc4828SBrian Ma                             asyncResp->res, url, "Invalid image format");
3764cde5d90SJames Feist                     }
377e1cc4828SBrian Ma                     else if (
378e1cc4828SBrian Ma                         *type ==
3790fda0f12SGeorge Liu                         "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
38088b3dd12SGunnar Mills                     {
38188b3dd12SGunnar Mills                         redfish::messages::invalidUpload(
382e1cc4828SBrian Ma                             asyncResp->res, url,
383e1cc4828SBrian Ma                             "Image version already exists");
38488b3dd12SGunnar Mills 
38588b3dd12SGunnar Mills                         redfish::messages::resourceAlreadyExists(
386d8a5d5d8SJiaqing Zhao                             asyncResp->res, "UpdateService", "Version",
387e1cc4828SBrian Ma                             "uploaded version");
38888b3dd12SGunnar Mills                     }
389e1cc4828SBrian Ma                     else if (
390e1cc4828SBrian Ma                         *type ==
3914cde5d90SJames Feist                         "xyz.openbmc_project.Software.Image.Error.BusyFailure")
3924cde5d90SJames Feist                     {
393002d39b4SEd Tanous                         redfish::messages::resourceExhaustion(asyncResp->res,
394002d39b4SEd Tanous                                                               url);
3954cde5d90SJames Feist                     }
3964cde5d90SJames Feist                     else
3974cde5d90SJames Feist                     {
3984cde5d90SJames Feist                         redfish::messages::internalError(asyncResp->res);
3994cde5d90SJames Feist                     }
400e1cc4828SBrian Ma                 }
401e1cc4828SBrian Ma             }
402711ac7a9SEd Tanous         }
4034cde5d90SJames Feist         });
40486adcd6dSAndrew Geissler }
405729dae72SJennifer Lee 
4060554c984SAndrew Geissler /**
4070554c984SAndrew Geissler  * UpdateServiceActionsSimpleUpdate class supports handle POST method for
4080554c984SAndrew Geissler  * SimpleUpdate action.
4090554c984SAndrew Geissler  */
4107e860f15SJohn Edward Broadbent inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
4110554c984SAndrew Geissler {
4127e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(
4137e860f15SJohn Edward Broadbent         app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
414ed398213SEd Tanous         .privileges(redfish::privileges::postUpdateService)
415002d39b4SEd Tanous         .methods(boost::beast::http::verb::post)(
416002d39b4SEd Tanous             [&app](const crow::Request& req,
4177e860f15SJohn Edward Broadbent                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
4183ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
41945ca1b86SEd Tanous         {
42045ca1b86SEd Tanous             return;
42145ca1b86SEd Tanous         }
42245ca1b86SEd Tanous 
4230554c984SAndrew Geissler         std::optional<std::string> transferProtocol;
4240554c984SAndrew Geissler         std::string imageURI;
4250554c984SAndrew Geissler 
4260554c984SAndrew Geissler         BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
4270554c984SAndrew Geissler 
4280554c984SAndrew Geissler         // User can pass in both TransferProtocol and ImageURI parameters or
4294e0453b1SGunnar Mills         // they can pass in just the ImageURI with the transfer protocol
4304e0453b1SGunnar Mills         // embedded within it.
4310554c984SAndrew Geissler         // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
4320554c984SAndrew Geissler         // 2) ImageURI:tftp://1.1.1.1/myfile.bin
4330554c984SAndrew Geissler 
434002d39b4SEd Tanous         if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
435002d39b4SEd Tanous                                        transferProtocol, "ImageURI", imageURI))
4360554c984SAndrew Geissler         {
4370554c984SAndrew Geissler             BMCWEB_LOG_DEBUG
4380554c984SAndrew Geissler                 << "Missing TransferProtocol or ImageURI parameter";
4390554c984SAndrew Geissler             return;
4400554c984SAndrew Geissler         }
4410554c984SAndrew Geissler         if (!transferProtocol)
4420554c984SAndrew Geissler         {
4430554c984SAndrew Geissler             // Must be option 2
4440554c984SAndrew Geissler             // Verify ImageURI has transfer protocol in it
445f23b7296SEd Tanous             size_t separator = imageURI.find(':');
4460554c984SAndrew Geissler             if ((separator == std::string::npos) ||
4470554c984SAndrew Geissler                 ((separator + 1) > imageURI.size()))
4480554c984SAndrew Geissler             {
4490554c984SAndrew Geissler                 messages::actionParameterValueTypeError(
4500554c984SAndrew Geissler                     asyncResp->res, imageURI, "ImageURI",
4510554c984SAndrew Geissler                     "UpdateService.SimpleUpdate");
4520554c984SAndrew Geissler                 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
4530554c984SAndrew Geissler                                  << imageURI;
4540554c984SAndrew Geissler                 return;
4550554c984SAndrew Geissler             }
4560554c984SAndrew Geissler             transferProtocol = imageURI.substr(0, separator);
4577e860f15SJohn Edward Broadbent             // Ensure protocol is upper case for a common comparison path
4587e860f15SJohn Edward Broadbent             // below
4590554c984SAndrew Geissler             boost::to_upper(*transferProtocol);
4600554c984SAndrew Geissler             BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
4610554c984SAndrew Geissler                              << *transferProtocol;
4620554c984SAndrew Geissler 
4630554c984SAndrew Geissler             // Adjust imageURI to not have the protocol on it for parsing
4640554c984SAndrew Geissler             // below
4650554c984SAndrew Geissler             // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
4660554c984SAndrew Geissler             imageURI = imageURI.substr(separator + 3);
4670554c984SAndrew Geissler             BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
4680554c984SAndrew Geissler         }
4690554c984SAndrew Geissler 
4700554c984SAndrew Geissler         // OpenBMC currently only supports TFTP
4710554c984SAndrew Geissler         if (*transferProtocol != "TFTP")
4720554c984SAndrew Geissler         {
473002d39b4SEd Tanous             messages::actionParameterNotSupported(asyncResp->res,
474002d39b4SEd Tanous                                                   "TransferProtocol",
4750554c984SAndrew Geissler                                                   "UpdateService.SimpleUpdate");
4760554c984SAndrew Geissler             BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
4770554c984SAndrew Geissler                              << *transferProtocol;
4780554c984SAndrew Geissler             return;
4790554c984SAndrew Geissler         }
4800554c984SAndrew Geissler 
4810554c984SAndrew Geissler         // Format should be <IP or Hostname>/<file> for imageURI
482f23b7296SEd Tanous         size_t separator = imageURI.find('/');
4830554c984SAndrew Geissler         if ((separator == std::string::npos) ||
4840554c984SAndrew Geissler             ((separator + 1) > imageURI.size()))
4850554c984SAndrew Geissler         {
4860554c984SAndrew Geissler             messages::actionParameterValueTypeError(
4870554c984SAndrew Geissler                 asyncResp->res, imageURI, "ImageURI",
4880554c984SAndrew Geissler                 "UpdateService.SimpleUpdate");
4890554c984SAndrew Geissler             BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
4900554c984SAndrew Geissler             return;
4910554c984SAndrew Geissler         }
4920554c984SAndrew Geissler 
4930554c984SAndrew Geissler         std::string tftpServer = imageURI.substr(0, separator);
4940554c984SAndrew Geissler         std::string fwFile = imageURI.substr(separator + 1);
4950554c984SAndrew Geissler         BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
4960554c984SAndrew Geissler 
4970554c984SAndrew Geissler         // Setup callback for when new software detected
4982618d5e3SGunnar Mills         // Give TFTP 10 minutes to complete
4994cde5d90SJames Feist         monitorForSoftwareAvailable(
500d7a596bdSAlbert Zhang             asyncResp, req,
5014cde5d90SJames Feist             "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
5022618d5e3SGunnar Mills             600);
5030554c984SAndrew Geissler 
5042618d5e3SGunnar Mills         // TFTP can take up to 10 minutes depending on image size and
5050554c984SAndrew Geissler         // connection speed. Return to caller as soon as the TFTP operation
5060554c984SAndrew Geissler         // has been started. The callback above will ensure the activate
5070554c984SAndrew Geissler         // is started once the download has completed
5080554c984SAndrew Geissler         redfish::messages::success(asyncResp->res);
5090554c984SAndrew Geissler 
5100554c984SAndrew Geissler         // Call TFTP service
5110554c984SAndrew Geissler         crow::connections::systemBus->async_method_call(
5125e7e2dc5SEd Tanous             [](const boost::system::error_code& ec) {
5130554c984SAndrew Geissler             if (ec)
5140554c984SAndrew Geissler             {
5150554c984SAndrew Geissler                 // messages::internalError(asyncResp->res);
5160554c984SAndrew Geissler                 cleanUp();
5170554c984SAndrew Geissler                 BMCWEB_LOG_DEBUG << "error_code = " << ec;
5180554c984SAndrew Geissler                 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
5190554c984SAndrew Geissler             }
5200554c984SAndrew Geissler             else
5210554c984SAndrew Geissler             {
5220554c984SAndrew Geissler                 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
5230554c984SAndrew Geissler             }
5240554c984SAndrew Geissler             },
5250554c984SAndrew Geissler             "xyz.openbmc_project.Software.Download",
526002d39b4SEd Tanous             "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
527002d39b4SEd Tanous             "DownloadViaTFTP", fwFile, tftpServer);
5280554c984SAndrew Geissler 
5290554c984SAndrew Geissler         BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
5307e860f15SJohn Edward Broadbent         });
531729dae72SJennifer Lee }
532729dae72SJennifer Lee 
5330ed80c8cSGeorge Liu inline void uploadImageFile(crow::Response& res, std::string_view body)
5340ed80c8cSGeorge Liu {
5350ed80c8cSGeorge Liu     std::filesystem::path filepath(
5360ed80c8cSGeorge Liu         "/tmp/images/" +
5370ed80c8cSGeorge Liu         boost::uuids::to_string(boost::uuids::random_generator()()));
5380ed80c8cSGeorge Liu     BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
5390ed80c8cSGeorge Liu     std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
5400ed80c8cSGeorge Liu                                     std::ofstream::trunc);
5410ed80c8cSGeorge Liu     // set the permission of the file to 640
54289492a15SPatrick Williams     std::filesystem::perms permission = std::filesystem::perms::owner_read |
54389492a15SPatrick Williams                                         std::filesystem::perms::group_read;
5440ed80c8cSGeorge Liu     std::filesystem::permissions(filepath, permission);
5450ed80c8cSGeorge Liu     out << body;
5460ed80c8cSGeorge Liu 
5470ed80c8cSGeorge Liu     if (out.bad())
5480ed80c8cSGeorge Liu     {
5490ed80c8cSGeorge Liu         messages::internalError(res);
5500ed80c8cSGeorge Liu         cleanUp();
5510ed80c8cSGeorge Liu     }
5520ed80c8cSGeorge Liu }
5530ed80c8cSGeorge Liu 
5540ed80c8cSGeorge Liu inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
5550ed80c8cSGeorge Liu                          const std::string& applyTime)
5560ed80c8cSGeorge Liu {
5570ed80c8cSGeorge Liu     std::string applyTimeNewVal;
5580ed80c8cSGeorge Liu     if (applyTime == "Immediate")
5590ed80c8cSGeorge Liu     {
5600ed80c8cSGeorge Liu         applyTimeNewVal =
5610ed80c8cSGeorge Liu             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
5620ed80c8cSGeorge Liu     }
5630ed80c8cSGeorge Liu     else if (applyTime == "OnReset")
5640ed80c8cSGeorge Liu     {
5650ed80c8cSGeorge Liu         applyTimeNewVal =
5660ed80c8cSGeorge Liu             "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
5670ed80c8cSGeorge Liu     }
5680ed80c8cSGeorge Liu     else
5690ed80c8cSGeorge Liu     {
5700ed80c8cSGeorge Liu         BMCWEB_LOG_INFO
5710ed80c8cSGeorge Liu             << "ApplyTime value is not in the list of acceptable values";
5720ed80c8cSGeorge Liu         messages::propertyValueNotInList(asyncResp->res, applyTime,
5730ed80c8cSGeorge Liu                                          "ApplyTime");
5740ed80c8cSGeorge Liu         return;
5750ed80c8cSGeorge Liu     }
5760ed80c8cSGeorge Liu 
5770ed80c8cSGeorge Liu     // Set the requested image apply time value
5780ed80c8cSGeorge Liu     crow::connections::systemBus->async_method_call(
57981c4e330SEd Tanous         [asyncResp](const boost::system::error_code& ec) {
5800ed80c8cSGeorge Liu         if (ec)
5810ed80c8cSGeorge Liu         {
5820ed80c8cSGeorge Liu             BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
5830ed80c8cSGeorge Liu             messages::internalError(asyncResp->res);
5840ed80c8cSGeorge Liu             return;
5850ed80c8cSGeorge Liu         }
5860ed80c8cSGeorge Liu         messages::success(asyncResp->res);
5870ed80c8cSGeorge Liu         },
5880ed80c8cSGeorge Liu         "xyz.openbmc_project.Settings",
5890ed80c8cSGeorge Liu         "/xyz/openbmc_project/software/apply_time",
5900ed80c8cSGeorge Liu         "org.freedesktop.DBus.Properties", "Set",
5910ed80c8cSGeorge Liu         "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
5920ed80c8cSGeorge Liu         dbus::utility::DbusVariantType{applyTimeNewVal});
5930ed80c8cSGeorge Liu }
5940ed80c8cSGeorge Liu 
5950ed80c8cSGeorge Liu inline void
5960ed80c8cSGeorge Liu     updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
5970ed80c8cSGeorge Liu                            const MultipartParser& parser)
5980ed80c8cSGeorge Liu {
5990ed80c8cSGeorge Liu     const std::string* uploadData = nullptr;
6000ed80c8cSGeorge Liu     std::optional<std::string> applyTime = "OnReset";
6010ed80c8cSGeorge Liu     bool targetFound = false;
6020ed80c8cSGeorge Liu     for (const FormPart& formpart : parser.mime_fields)
6030ed80c8cSGeorge Liu     {
6040ed80c8cSGeorge Liu         boost::beast::http::fields::const_iterator it =
6050ed80c8cSGeorge Liu             formpart.fields.find("Content-Disposition");
6060ed80c8cSGeorge Liu         if (it == formpart.fields.end())
6070ed80c8cSGeorge Liu         {
6080ed80c8cSGeorge Liu             BMCWEB_LOG_ERROR << "Couldn't find Content-Disposition";
6090ed80c8cSGeorge Liu             return;
6100ed80c8cSGeorge Liu         }
6110ed80c8cSGeorge Liu         BMCWEB_LOG_INFO << "Parsing value " << it->value();
6120ed80c8cSGeorge Liu 
6130ed80c8cSGeorge Liu         // The construction parameters of param_list must start with `;`
6140ed80c8cSGeorge Liu         size_t index = it->value().find(';');
6150ed80c8cSGeorge Liu         if (index == std::string::npos)
6160ed80c8cSGeorge Liu         {
6170ed80c8cSGeorge Liu             continue;
6180ed80c8cSGeorge Liu         }
6190ed80c8cSGeorge Liu 
62089492a15SPatrick Williams         for (const auto& param :
6210ed80c8cSGeorge Liu              boost::beast::http::param_list{it->value().substr(index)})
6220ed80c8cSGeorge Liu         {
6230ed80c8cSGeorge Liu             if (param.first != "name" || param.second.empty())
6240ed80c8cSGeorge Liu             {
6250ed80c8cSGeorge Liu                 continue;
6260ed80c8cSGeorge Liu             }
6270ed80c8cSGeorge Liu 
6280ed80c8cSGeorge Liu             if (param.second == "UpdateParameters")
6290ed80c8cSGeorge Liu             {
6300ed80c8cSGeorge Liu                 std::vector<std::string> targets;
6310ed80c8cSGeorge Liu                 nlohmann::json content =
6320ed80c8cSGeorge Liu                     nlohmann::json::parse(formpart.content);
6330ed80c8cSGeorge Liu                 if (!json_util::readJson(content, asyncResp->res, "Targets",
6340ed80c8cSGeorge Liu                                          targets, "@Redfish.OperationApplyTime",
6350ed80c8cSGeorge Liu                                          applyTime))
6360ed80c8cSGeorge Liu                 {
6370ed80c8cSGeorge Liu                     return;
6380ed80c8cSGeorge Liu                 }
6390ed80c8cSGeorge Liu                 if (targets.size() != 1)
6400ed80c8cSGeorge Liu                 {
6410ed80c8cSGeorge Liu                     messages::propertyValueFormatError(asyncResp->res,
6420ed80c8cSGeorge Liu                                                        "Targets", "");
6430ed80c8cSGeorge Liu                     return;
6440ed80c8cSGeorge Liu                 }
6450ed80c8cSGeorge Liu                 if (targets[0] != "/redfish/v1/Managers/bmc")
6460ed80c8cSGeorge Liu                 {
6470ed80c8cSGeorge Liu                     messages::propertyValueNotInList(asyncResp->res,
6480ed80c8cSGeorge Liu                                                      "Targets/0", targets[0]);
6490ed80c8cSGeorge Liu                     return;
6500ed80c8cSGeorge Liu                 }
6510ed80c8cSGeorge Liu                 targetFound = true;
6520ed80c8cSGeorge Liu             }
6530ed80c8cSGeorge Liu             else if (param.second == "UpdateFile")
6540ed80c8cSGeorge Liu             {
6550ed80c8cSGeorge Liu                 uploadData = &(formpart.content);
6560ed80c8cSGeorge Liu             }
6570ed80c8cSGeorge Liu         }
6580ed80c8cSGeorge Liu     }
6590ed80c8cSGeorge Liu 
6600ed80c8cSGeorge Liu     if (uploadData == nullptr)
6610ed80c8cSGeorge Liu     {
6620ed80c8cSGeorge Liu         BMCWEB_LOG_ERROR << "Upload data is NULL";
6630ed80c8cSGeorge Liu         messages::propertyMissing(asyncResp->res, "UpdateFile");
6640ed80c8cSGeorge Liu         return;
6650ed80c8cSGeorge Liu     }
6660ed80c8cSGeorge Liu     if (!targetFound)
6670ed80c8cSGeorge Liu     {
6680ed80c8cSGeorge Liu         messages::propertyMissing(asyncResp->res, "targets");
6690ed80c8cSGeorge Liu         return;
6700ed80c8cSGeorge Liu     }
6710ed80c8cSGeorge Liu 
6720ed80c8cSGeorge Liu     setApplyTime(asyncResp, *applyTime);
6730ed80c8cSGeorge Liu 
6740ed80c8cSGeorge Liu     uploadImageFile(asyncResp->res, *uploadData);
6750ed80c8cSGeorge Liu }
6760ed80c8cSGeorge Liu 
677c2051d11SEd Tanous inline void
678c2051d11SEd Tanous     handleUpdateServicePost(App& app, const crow::Request& req,
679c2051d11SEd Tanous                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
680c2051d11SEd Tanous {
6813ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
682c2051d11SEd Tanous     {
683c2051d11SEd Tanous         return;
684c2051d11SEd Tanous     }
685*b33a4327SNinad Palsule     std::string_view contentType = req.getHeaderValue("Content-Type");
686*b33a4327SNinad Palsule 
687*b33a4327SNinad Palsule     BMCWEB_LOG_DEBUG << "doPost: contentType=" << contentType;
688*b33a4327SNinad Palsule 
689*b33a4327SNinad Palsule     // Make sure that content type is application/octet-stream or
690*b33a4327SNinad Palsule     // multipart/form-data
691*b33a4327SNinad Palsule     if (boost::iequals(contentType, "application/octet-stream"))
692*b33a4327SNinad Palsule     {
693*b33a4327SNinad Palsule         // Setup callback for when new software detected
694*b33a4327SNinad Palsule         monitorForSoftwareAvailable(asyncResp, req,
695*b33a4327SNinad Palsule                                     "/redfish/v1/UpdateService");
696*b33a4327SNinad Palsule 
697*b33a4327SNinad Palsule         uploadImageFile(asyncResp->res, req.body());
698*b33a4327SNinad Palsule     }
699*b33a4327SNinad Palsule     else if (contentType.starts_with("multipart/form-data"))
700*b33a4327SNinad Palsule     {
701*b33a4327SNinad Palsule         MultipartParser parser;
702c2051d11SEd Tanous 
703c2051d11SEd Tanous         // Setup callback for when new software detected
704*b33a4327SNinad Palsule         monitorForSoftwareAvailable(asyncResp, req,
705*b33a4327SNinad Palsule                                     "/redfish/v1/UpdateService");
706c2051d11SEd Tanous 
7070ed80c8cSGeorge Liu         ParserError ec = parser.parse(req);
7080ed80c8cSGeorge Liu         if (ec != ParserError::PARSER_SUCCESS)
7090ed80c8cSGeorge Liu         {
7100ed80c8cSGeorge Liu             // handle error
711*b33a4327SNinad Palsule             BMCWEB_LOG_ERROR << "MIME parse failed, ec : "
712*b33a4327SNinad Palsule                              << static_cast<int>(ec);
7130ed80c8cSGeorge Liu             messages::internalError(asyncResp->res);
7140ed80c8cSGeorge Liu             return;
7150ed80c8cSGeorge Liu         }
7160ed80c8cSGeorge Liu         updateMultipartContext(asyncResp, parser);
717c2051d11SEd Tanous     }
718*b33a4327SNinad Palsule     else
719*b33a4327SNinad Palsule     {
720*b33a4327SNinad Palsule         BMCWEB_LOG_DEBUG << "Bad content type specified:" << contentType;
721*b33a4327SNinad Palsule         asyncResp->res.result(boost::beast::http::status::bad_request);
722*b33a4327SNinad Palsule     }
723*b33a4327SNinad Palsule }
724c2051d11SEd Tanous 
7257e860f15SJohn Edward Broadbent inline void requestRoutesUpdateService(App& app)
7261abe55efSEd Tanous {
7277e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
728ed398213SEd Tanous         .privileges(redfish::privileges::getUpdateService)
729002d39b4SEd Tanous         .methods(boost::beast::http::verb::get)(
730002d39b4SEd Tanous             [&app](const crow::Request& req,
731002d39b4SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
7323ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
73345ca1b86SEd Tanous         {
73445ca1b86SEd Tanous             return;
73545ca1b86SEd Tanous         }
7368d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.type"] =
7370ed80c8cSGeorge Liu             "#UpdateService.v1_11_1.UpdateService";
7388d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
7398d1b46d7Szhanghch05         asyncResp->res.jsonValue["Id"] = "UpdateService";
740002d39b4SEd Tanous         asyncResp->res.jsonValue["Description"] = "Service for Software Update";
7418d1b46d7Szhanghch05         asyncResp->res.jsonValue["Name"] = "Update Service";
7424dc23f3fSEd Tanous 
7437e860f15SJohn Edward Broadbent         asyncResp->res.jsonValue["HttpPushUri"] =
7444dc23f3fSEd Tanous             "/redfish/v1/UpdateService/update";
7450ed80c8cSGeorge Liu         asyncResp->res.jsonValue["MultipartHttpPushUri"] =
7460ed80c8cSGeorge Liu             "/redfish/v1/UpdateService/update";
7474dc23f3fSEd Tanous 
7480f74e643SEd Tanous         // UpdateService cannot be disabled
7498d1b46d7Szhanghch05         asyncResp->res.jsonValue["ServiceEnabled"] = true;
7501476687dSEd Tanous         asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
7511476687dSEd Tanous             "/redfish/v1/UpdateService/FirmwareInventory";
752d61e5194STejas Patil         // Get the MaxImageSizeBytes
753d61e5194STejas Patil         asyncResp->res.jsonValue["MaxImageSizeBytes"] =
754d61e5194STejas Patil             bmcwebHttpReqBodyLimitMb * 1024 * 1024;
755d61e5194STejas Patil 
7560554c984SAndrew Geissler #ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
7570554c984SAndrew Geissler         // Update Actions object.
7580554c984SAndrew Geissler         nlohmann::json& updateSvcSimpleUpdate =
759002d39b4SEd Tanous             asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
7600554c984SAndrew Geissler         updateSvcSimpleUpdate["target"] =
7610554c984SAndrew Geissler             "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
762002d39b4SEd Tanous         updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
763002d39b4SEd Tanous             "TFTP"};
7640554c984SAndrew Geissler #endif
765274dfe62SJayashankar Padath         // Get the current ApplyTime value
7661e1e598dSJonathan Doman         sdbusplus::asio::getProperty<std::string>(
7671e1e598dSJonathan Doman             *crow::connections::systemBus, "xyz.openbmc_project.Settings",
7681e1e598dSJonathan Doman             "/xyz/openbmc_project/software/apply_time",
7691e1e598dSJonathan Doman             "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
7705e7e2dc5SEd Tanous             [asyncResp](const boost::system::error_code& ec,
7711e1e598dSJonathan Doman                         const std::string& applyTime) {
772274dfe62SJayashankar Padath             if (ec)
773274dfe62SJayashankar Padath             {
774274dfe62SJayashankar Padath                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
7758d1b46d7Szhanghch05                 messages::internalError(asyncResp->res);
776274dfe62SJayashankar Padath                 return;
777274dfe62SJayashankar Padath             }
778274dfe62SJayashankar Padath 
779274dfe62SJayashankar Padath             // Store the ApplyTime Value
7801e1e598dSJonathan Doman             if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
7811e1e598dSJonathan Doman                              "RequestedApplyTimes.Immediate")
782274dfe62SJayashankar Padath             {
783002d39b4SEd Tanous                 asyncResp->res.jsonValue["HttpPushUriOptions"]
7847e860f15SJohn Edward Broadbent                                         ["HttpPushUriApplyTime"]["ApplyTime"] =
7857e860f15SJohn Edward Broadbent                     "Immediate";
786274dfe62SJayashankar Padath             }
787002d39b4SEd Tanous             else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
7881e1e598dSJonathan Doman                                   "RequestedApplyTimes.OnReset")
789274dfe62SJayashankar Padath             {
790002d39b4SEd Tanous                 asyncResp->res.jsonValue["HttpPushUriOptions"]
7917e860f15SJohn Edward Broadbent                                         ["HttpPushUriApplyTime"]["ApplyTime"] =
7927e860f15SJohn Edward Broadbent                     "OnReset";
793274dfe62SJayashankar Padath             }
7941e1e598dSJonathan Doman             });
7957e860f15SJohn Edward Broadbent         });
7967e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
797ed398213SEd Tanous         .privileges(redfish::privileges::patchUpdateService)
798002d39b4SEd Tanous         .methods(boost::beast::http::verb::patch)(
799002d39b4SEd Tanous             [&app](const crow::Request& req,
800002d39b4SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
8013ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
80245ca1b86SEd Tanous         {
80345ca1b86SEd Tanous             return;
80445ca1b86SEd Tanous         }
805fa1a5a38SJayashankar Padath         BMCWEB_LOG_DEBUG << "doPatch...";
806fa1a5a38SJayashankar Padath 
807274dfe62SJayashankar Padath         std::optional<nlohmann::json> pushUriOptions;
808002d39b4SEd Tanous         if (!json_util::readJsonPatch(req, asyncResp->res, "HttpPushUriOptions",
809002d39b4SEd Tanous                                       pushUriOptions))
810fa1a5a38SJayashankar Padath         {
811fa1a5a38SJayashankar Padath             return;
812fa1a5a38SJayashankar Padath         }
813fa1a5a38SJayashankar Padath 
814274dfe62SJayashankar Padath         if (pushUriOptions)
815274dfe62SJayashankar Padath         {
816274dfe62SJayashankar Padath             std::optional<nlohmann::json> pushUriApplyTime;
8178d1b46d7Szhanghch05             if (!json_util::readJson(*pushUriOptions, asyncResp->res,
818002d39b4SEd Tanous                                      "HttpPushUriApplyTime", pushUriApplyTime))
819274dfe62SJayashankar Padath             {
820274dfe62SJayashankar Padath                 return;
821274dfe62SJayashankar Padath             }
822274dfe62SJayashankar Padath 
823274dfe62SJayashankar Padath             if (pushUriApplyTime)
824274dfe62SJayashankar Padath             {
825274dfe62SJayashankar Padath                 std::optional<std::string> applyTime;
8260fda0f12SGeorge Liu                 if (!json_util::readJson(*pushUriApplyTime, asyncResp->res,
8270fda0f12SGeorge Liu                                          "ApplyTime", applyTime))
828274dfe62SJayashankar Padath                 {
829274dfe62SJayashankar Padath                     return;
830274dfe62SJayashankar Padath                 }
831274dfe62SJayashankar Padath 
832274dfe62SJayashankar Padath                 if (applyTime)
833fa1a5a38SJayashankar Padath                 {
8340ed80c8cSGeorge Liu                     setApplyTime(asyncResp, *applyTime);
835fa1a5a38SJayashankar Padath                 }
836274dfe62SJayashankar Padath             }
837fa1a5a38SJayashankar Padath         }
8387e860f15SJohn Edward Broadbent         });
839c2051d11SEd Tanous 
8404dc23f3fSEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
8414dc23f3fSEd Tanous         .privileges(redfish::privileges::postUpdateService)
8427e860f15SJohn Edward Broadbent         .methods(boost::beast::http::verb::post)(
843c2051d11SEd Tanous             std::bind_front(handleUpdateServicePost, std::ref(app)));
844729dae72SJennifer Lee }
845729dae72SJennifer Lee 
8467e860f15SJohn Edward Broadbent inline void requestRoutesSoftwareInventoryCollection(App& app)
8471abe55efSEd Tanous {
8487e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
849ed398213SEd Tanous         .privileges(redfish::privileges::getSoftwareInventoryCollection)
8501476687dSEd Tanous         .methods(boost::beast::http::verb::get)(
8511476687dSEd Tanous             [&app](const crow::Request& req,
8521476687dSEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
8533ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
85445ca1b86SEd Tanous         {
85545ca1b86SEd Tanous             return;
85645ca1b86SEd Tanous         }
8578d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.type"] =
8580f74e643SEd Tanous             "#SoftwareInventoryCollection.SoftwareInventoryCollection";
8598d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.id"] =
8600f74e643SEd Tanous             "/redfish/v1/UpdateService/FirmwareInventory";
861002d39b4SEd Tanous         asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
86208d81adaSJohn Edward Broadbent         const std::array<const std::string_view, 1> iface = {
863e99073f5SGeorge Liu             "xyz.openbmc_project.Software.Version"};
8646c4eb9deSJennifer Lee 
86508d81adaSJohn Edward Broadbent         redfish::collection_util::getCollectionMembers(
86608d81adaSJohn Edward Broadbent             asyncResp,
86708d81adaSJohn Edward Broadbent             boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"),
86808d81adaSJohn Edward Broadbent             iface, "/xyz/openbmc_project/software");
8697e860f15SJohn Edward Broadbent         });
870729dae72SJennifer Lee }
87187d84729SAndrew Geissler /* Fill related item links (i.e. bmc, bios) in for inventory */
8727e860f15SJohn Edward Broadbent inline static void
873ac106bf6SEd Tanous     getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
87487d84729SAndrew Geissler                     const std::string& purpose)
87587d84729SAndrew Geissler {
876eee0013eSWilly Tu     if (purpose == sw_util::bmcPurpose)
87787d84729SAndrew Geissler     {
878ac106bf6SEd Tanous         nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
8791476687dSEd Tanous         nlohmann::json::object_t item;
8801476687dSEd Tanous         item["@odata.id"] = "/redfish/v1/Managers/bmc";
881b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
882ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
883ac106bf6SEd Tanous             relatedItem.size();
88487d84729SAndrew Geissler     }
885eee0013eSWilly Tu     else if (purpose == sw_util::biosPurpose)
88687d84729SAndrew Geissler     {
887ac106bf6SEd Tanous         nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
8881476687dSEd Tanous         nlohmann::json::object_t item;
8891476687dSEd Tanous         item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
890b2ba3072SPatrick Williams         relatedItem.emplace_back(std::move(item));
891ac106bf6SEd Tanous         asyncResp->res.jsonValue["RelatedItem@odata.count"] =
892ac106bf6SEd Tanous             relatedItem.size();
89387d84729SAndrew Geissler     }
89487d84729SAndrew Geissler     else
89587d84729SAndrew Geissler     {
89687d84729SAndrew Geissler         BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
89787d84729SAndrew Geissler     }
89887d84729SAndrew Geissler }
89987d84729SAndrew Geissler 
900af24660dSWilly Tu inline void
901af24660dSWilly Tu     getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
902af24660dSWilly Tu                        const std::string& service, const std::string& path,
903af24660dSWilly Tu                        const std::string& swId)
904af24660dSWilly Tu {
905d1bde9e5SKrzysztof Grobelny     sdbusplus::asio::getAllProperties(
906d1bde9e5SKrzysztof Grobelny         *crow::connections::systemBus, service, path,
907d1bde9e5SKrzysztof Grobelny         "xyz.openbmc_project.Software.Version",
908af24660dSWilly Tu         [asyncResp,
9095e7e2dc5SEd Tanous          swId](const boost::system::error_code& errorCode,
910af24660dSWilly Tu                const dbus::utility::DBusPropertiesMap& propertiesList) {
911af24660dSWilly Tu         if (errorCode)
912af24660dSWilly Tu         {
913af24660dSWilly Tu             messages::internalError(asyncResp->res);
914af24660dSWilly Tu             return;
915af24660dSWilly Tu         }
916d1bde9e5SKrzysztof Grobelny 
917af24660dSWilly Tu         const std::string* swInvPurpose = nullptr;
918af24660dSWilly Tu         const std::string* version = nullptr;
919d1bde9e5SKrzysztof Grobelny 
920d1bde9e5SKrzysztof Grobelny         const bool success = sdbusplus::unpackPropertiesNoThrow(
921d1bde9e5SKrzysztof Grobelny             dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
922d1bde9e5SKrzysztof Grobelny             swInvPurpose, "Version", version);
923d1bde9e5SKrzysztof Grobelny 
924d1bde9e5SKrzysztof Grobelny         if (!success)
925af24660dSWilly Tu         {
926d1bde9e5SKrzysztof Grobelny             messages::internalError(asyncResp->res);
927d1bde9e5SKrzysztof Grobelny             return;
928af24660dSWilly Tu         }
929af24660dSWilly Tu 
930af24660dSWilly Tu         if (swInvPurpose == nullptr)
931af24660dSWilly Tu         {
932af24660dSWilly Tu             BMCWEB_LOG_DEBUG << "Can't find property \"Purpose\"!";
933af24660dSWilly Tu             messages::internalError(asyncResp->res);
934af24660dSWilly Tu             return;
935af24660dSWilly Tu         }
936af24660dSWilly Tu 
937af24660dSWilly Tu         BMCWEB_LOG_DEBUG << "swInvPurpose = " << *swInvPurpose;
938af24660dSWilly Tu 
939af24660dSWilly Tu         if (version == nullptr)
940af24660dSWilly Tu         {
941af24660dSWilly Tu             BMCWEB_LOG_DEBUG << "Can't find property \"Version\"!";
942af24660dSWilly Tu 
943af24660dSWilly Tu             messages::internalError(asyncResp->res);
944af24660dSWilly Tu 
945af24660dSWilly Tu             return;
946af24660dSWilly Tu         }
947af24660dSWilly Tu         asyncResp->res.jsonValue["Version"] = *version;
948af24660dSWilly Tu         asyncResp->res.jsonValue["Id"] = swId;
949af24660dSWilly Tu 
950af24660dSWilly Tu         // swInvPurpose is of format:
951af24660dSWilly Tu         // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
952af24660dSWilly Tu         // Translate this to "ABC image"
953af24660dSWilly Tu         size_t endDesc = swInvPurpose->rfind('.');
954af24660dSWilly Tu         if (endDesc == std::string::npos)
955af24660dSWilly Tu         {
956af24660dSWilly Tu             messages::internalError(asyncResp->res);
957af24660dSWilly Tu             return;
958af24660dSWilly Tu         }
959af24660dSWilly Tu         endDesc++;
960af24660dSWilly Tu         if (endDesc >= swInvPurpose->size())
961af24660dSWilly Tu         {
962af24660dSWilly Tu             messages::internalError(asyncResp->res);
963af24660dSWilly Tu             return;
964af24660dSWilly Tu         }
965af24660dSWilly Tu 
966af24660dSWilly Tu         std::string formatDesc = swInvPurpose->substr(endDesc);
967af24660dSWilly Tu         asyncResp->res.jsonValue["Description"] = formatDesc + " image";
968af24660dSWilly Tu         getRelatedItems(asyncResp, *swInvPurpose);
969d1bde9e5SKrzysztof Grobelny         });
970af24660dSWilly Tu }
971af24660dSWilly Tu 
9727e860f15SJohn Edward Broadbent inline void requestRoutesSoftwareInventory(App& app)
9731abe55efSEd Tanous {
9747e860f15SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
975ed398213SEd Tanous         .privileges(redfish::privileges::getSoftwareInventory)
976002d39b4SEd Tanous         .methods(boost::beast::http::verb::get)(
977002d39b4SEd Tanous             [&app](const crow::Request& req,
97845ca1b86SEd Tanous                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
9797e860f15SJohn Edward Broadbent                    const std::string& param) {
9803ba00073SCarson Labrado         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
98145ca1b86SEd Tanous         {
98245ca1b86SEd Tanous             return;
98345ca1b86SEd Tanous         }
9843ae837c9SEd Tanous         std::shared_ptr<std::string> swId =
9857e860f15SJohn Edward Broadbent             std::make_shared<std::string>(param);
986c711bf86SEd Tanous 
987ef4c65b7SEd Tanous         asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
988ef4c65b7SEd Tanous             "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
989c711bf86SEd Tanous 
990e99073f5SGeorge Liu         constexpr std::array<std::string_view, 1> interfaces = {
991e99073f5SGeorge Liu             "xyz.openbmc_project.Software.Version"};
992e99073f5SGeorge Liu         dbus::utility::getSubTree(
993e99073f5SGeorge Liu             "/", 0, interfaces,
994b9d36b47SEd Tanous             [asyncResp,
995e99073f5SGeorge Liu              swId](const boost::system::error_code& ec,
996b9d36b47SEd Tanous                    const dbus::utility::MapperGetSubTreeResponse& subtree) {
99755c7b7a2SEd Tanous             BMCWEB_LOG_DEBUG << "doGet callback...";
9981abe55efSEd Tanous             if (ec)
9991abe55efSEd Tanous             {
1000f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
10016c4eb9deSJennifer Lee                 return;
10026c4eb9deSJennifer Lee             }
10036c4eb9deSJennifer Lee 
10046913228dSAndrew Geissler             // Ensure we find our input swId, otherwise return an error
10056913228dSAndrew Geissler             bool found = false;
1006002d39b4SEd Tanous             for (const std::pair<std::string,
10077e860f15SJohn Edward Broadbent                                  std::vector<std::pair<
1008002d39b4SEd Tanous                                      std::string, std::vector<std::string>>>>&
1009002d39b4SEd Tanous                      obj : subtree)
10101abe55efSEd Tanous             {
101111ba3979SEd Tanous                 if (!obj.first.ends_with(*swId))
10121abe55efSEd Tanous                 {
1013acb7cfb4SJennifer Lee                     continue;
1014acb7cfb4SJennifer Lee                 }
1015acb7cfb4SJennifer Lee 
101626f6976fSEd Tanous                 if (obj.second.empty())
10171abe55efSEd Tanous                 {
1018acb7cfb4SJennifer Lee                     continue;
1019acb7cfb4SJennifer Lee                 }
10206c4eb9deSJennifer Lee 
10216913228dSAndrew Geissler                 found = true;
1022eee0013eSWilly Tu                 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1023af24660dSWilly Tu                 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1024af24660dSWilly Tu                                    *swId);
10256c4eb9deSJennifer Lee             }
10266913228dSAndrew Geissler             if (!found)
10276913228dSAndrew Geissler             {
1028b90d14f2SMyung Bae                 BMCWEB_LOG_WARNING << "Input swID " << *swId << " not found!";
10296913228dSAndrew Geissler                 messages::resourceMissingAtURI(
1030ef4c65b7SEd Tanous                     asyncResp->res,
1031ef4c65b7SEd Tanous                     boost::urls::format(
1032ef4c65b7SEd Tanous                         "/redfish/v1/UpdateService/FirmwareInventory/{}",
1033ef4c65b7SEd Tanous                         *swId));
10346913228dSAndrew Geissler                 return;
10356913228dSAndrew Geissler             }
10364e68c45bSAyushi Smriti             asyncResp->res.jsonValue["@odata.type"] =
10374e68c45bSAyushi Smriti                 "#SoftwareInventory.v1_1_0.SoftwareInventory";
10384e68c45bSAyushi Smriti             asyncResp->res.jsonValue["Name"] = "Software Inventory";
10394e68c45bSAyushi Smriti             asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
10403f8a743aSAppaRao Puli 
10413f8a743aSAppaRao Puli             asyncResp->res.jsonValue["Updateable"] = false;
1042eee0013eSWilly Tu             sw_util::getSwUpdatableStatus(asyncResp, swId);
1043e99073f5SGeorge Liu             });
10447e860f15SJohn Edward Broadbent         });
10456c4eb9deSJennifer Lee }
1046729dae72SJennifer Lee 
1047729dae72SJennifer Lee } // namespace redfish
1048