blob: e76dc0c555bac8c6a08375fbb43635aa4ee710c4 [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
3// SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
Jennifer Lee729dae72018-04-24 15:59:34 -07004#pragma once
5
Tejas Patild61e5192021-06-04 15:49:35 +05306#include "bmcweb_config.h"
7
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08008#include "app.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08009#include "async_resp.hpp"
10#include "dbus_singleton.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080011#include "dbus_utility.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070012#include "error_messages.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080013#include "generated/enums/resource.hpp"
Ed Tanous757178a2024-04-03 14:32:38 -070014#include "generated/enums/update_service.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080015#include "http_request.hpp"
16#include "http_response.hpp"
Ed Tanousd98a2f92025-02-06 17:36:31 -080017#include "io_context_singleton.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080018#include "logging.hpp"
George Liu0ed80c82020-05-12 16:06:27 +080019#include "multipart_parser.hpp"
Ed Tanous2c6ffdb2023-06-28 11:28:38 -070020#include "ossl_random.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080021#include "query.hpp"
22#include "registries/privilege_registry.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080023#include "str_utility.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080024#include "task.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070025#include "task_messages.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080026#include "utility.hpp"
John Edward Broadbent08d81ad2022-05-17 20:00:23 -070027#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080028#include "utils/dbus_utils.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070029#include "utils/json_utils.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080030#include "utils/sw_utils.hpp"
31
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -070032#include <sys/mman.h>
Ed Tanousd7857202025-01-28 15:32:26 -080033#include <unistd.h>
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -070034
Ed Tanousd7857202025-01-28 15:32:26 -080035#include <boost/asio/error.hpp>
36#include <boost/asio/steady_timer.hpp>
37#include <boost/beast/http/fields.hpp>
38#include <boost/beast/http/status.hpp>
39#include <boost/beast/http/verb.hpp>
George Liue99073f2022-12-09 11:06:16 +080040#include <boost/system/error_code.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080041#include <boost/system/result.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070042#include <boost/url/format.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080043#include <boost/url/parse.hpp>
44#include <boost/url/url.hpp>
45#include <boost/url/url_view.hpp>
46#include <boost/url/url_view_base.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070047#include <sdbusplus/asio/property.hpp>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080048#include <sdbusplus/bus/match.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080049#include <sdbusplus/message.hpp>
50#include <sdbusplus/message/native_types.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020051#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050052
George Liu2b731192023-01-11 16:27:13 +080053#include <array>
Ed Tanousd7857202025-01-28 15:32:26 -080054#include <chrono>
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -070055#include <cstddef>
Ed Tanousd7857202025-01-28 15:32:26 -080056#include <cstdint>
57#include <cstdio>
George Liu0ed80c82020-05-12 16:06:27 +080058#include <filesystem>
Ed Tanousd7857202025-01-28 15:32:26 -080059#include <format>
60#include <fstream>
Jagpal Singh Gillc71b6c92024-04-29 16:50:53 -070061#include <functional>
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -070062#include <memory>
Ed Tanous7cb59f62022-05-05 11:48:31 -070063#include <optional>
64#include <string>
George Liu2b731192023-01-11 16:27:13 +080065#include <string_view>
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -070066#include <unordered_map>
Ed Tanousd7857202025-01-28 15:32:26 -080067#include <utility>
68#include <variant>
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -070069#include <vector>
George Liu2b731192023-01-11 16:27:13 +080070
Ed Tanous1abe55e2018-09-05 08:30:59 -070071namespace redfish
72{
Ed Tanous27826b52018-10-29 11:40:58 -070073
Andrew Geissler0e7de462019-03-04 19:11:54 -060074// Match signals added on software path
Ed Tanouscf9e4172022-12-21 09:30:16 -080075// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050076static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
Ed Tanouscf9e4172022-12-21 09:30:16 -080077// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050078static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060079// Only allow one update at a time
Ed Tanouscf9e4172022-12-21 09:30:16 -080080// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Andrew Geissler0e7de462019-03-04 19:11:54 -060081static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050082// Timer for software available
Ed Tanouscf9e4172022-12-21 09:30:16 -080083// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous271584a2019-07-09 16:24:22 -070084static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050085
rajeeranjan9fbf8532025-07-23 23:25:52 +053086/* @brief String that indicates the Software Update D-Bus interface */
87constexpr const char* updateInterface = "xyz.openbmc_project.Software.Update";
88
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -070089struct MemoryFileDescriptor
90{
91 int fd = -1;
92
93 explicit MemoryFileDescriptor(const std::string& filename) :
94 fd(memfd_create(filename.c_str(), 0))
95 {}
96
97 MemoryFileDescriptor(const MemoryFileDescriptor&) = default;
98 MemoryFileDescriptor(MemoryFileDescriptor&& other) noexcept : fd(other.fd)
99 {
100 other.fd = -1;
101 }
102 MemoryFileDescriptor& operator=(const MemoryFileDescriptor&) = delete;
103 MemoryFileDescriptor& operator=(MemoryFileDescriptor&&) = default;
104
105 ~MemoryFileDescriptor()
106 {
107 if (fd != -1)
108 {
109 close(fd);
110 }
111 }
112
113 bool rewind() const
114 {
115 if (lseek(fd, 0, SEEK_SET) == -1)
116 {
117 BMCWEB_LOG_ERROR("Failed to seek to beginning of image memfd");
118 return false;
119 }
120 return true;
121 }
122};
123
Ed Tanousdf254f22024-04-01 13:25:46 -0700124inline void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -0500125{
126 fwUpdateInProgress = false;
127 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -0700128 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500129}
Ed Tanousdf254f22024-04-01 13:25:46 -0700130
131inline void activateImage(const std::string& objPath,
132 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500133{
Ed Tanous62598e32023-07-17 17:06:25 -0700134 BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
George Liu9ae226f2023-06-21 17:56:46 +0800135 sdbusplus::asio::setProperty(
136 *crow::connections::systemBus, service, objPath,
137 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
138 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
Ed Tanous8b242752023-06-27 17:17:13 -0700139 [](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400140 if (ec)
141 {
142 BMCWEB_LOG_DEBUG("error_code = {}", ec);
143 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
144 }
145 });
Andrew Geissler86adcd62019-04-18 10:58:05 -0500146}
Andrew Geissler0554c982019-04-23 14:40:12 -0500147
Jagpal Singh Gillc71b6c92024-04-29 16:50:53 -0700148inline bool handleCreateTask(const boost::system::error_code& ec2,
149 sdbusplus::message_t& msg,
150 const std::shared_ptr<task::TaskData>& taskData)
151{
152 if (ec2)
153 {
154 return task::completed;
155 }
156
157 std::string iface;
158 dbus::utility::DBusPropertiesMap values;
159
160 std::string index = std::to_string(taskData->index);
161 msg.read(iface, values);
162
163 if (iface == "xyz.openbmc_project.Software.Activation")
164 {
165 const std::string* state = nullptr;
166 for (const auto& property : values)
167 {
168 if (property.first == "Activation")
169 {
170 state = std::get_if<std::string>(&property.second);
171 if (state == nullptr)
172 {
173 taskData->messages.emplace_back(messages::internalError());
174 return task::completed;
175 }
176 }
177 }
178
179 if (state == nullptr)
180 {
181 return !task::completed;
182 }
183
184 if (state->ends_with("Invalid") || state->ends_with("Failed"))
185 {
186 taskData->state = "Exception";
187 taskData->status = "Warning";
188 taskData->messages.emplace_back(messages::taskAborted(index));
189 return task::completed;
190 }
191
192 if (state->ends_with("Staged"))
193 {
194 taskData->state = "Stopping";
195 taskData->messages.emplace_back(messages::taskPaused(index));
196
197 // its staged, set a long timer to
198 // allow them time to complete the
199 // update (probably cycle the
200 // system) if this expires then
201 // task will be canceled
202 taskData->extendTimer(std::chrono::hours(5));
203 return !task::completed;
204 }
205
206 if (state->ends_with("Active"))
207 {
208 taskData->messages.emplace_back(messages::taskCompletedOK(index));
209 taskData->state = "Completed";
210 return task::completed;
211 }
212 }
213 else if (iface == "xyz.openbmc_project.Software.ActivationProgress")
214 {
215 const uint8_t* progress = nullptr;
216 for (const auto& property : values)
217 {
218 if (property.first == "Progress")
219 {
220 progress = std::get_if<uint8_t>(&property.second);
221 if (progress == nullptr)
222 {
223 taskData->messages.emplace_back(messages::internalError());
224 return task::completed;
225 }
226 }
227 }
228
229 if (progress == nullptr)
230 {
231 return !task::completed;
232 }
233 taskData->percentComplete = *progress;
234 taskData->messages.emplace_back(
235 messages::taskProgressChanged(index, *progress));
236
237 // if we're getting status updates it's
238 // still alive, update timer
239 taskData->extendTimer(std::chrono::minutes(5));
240 }
241
242 // as firmware update often results in a
243 // reboot, the task may never "complete"
244 // unless it is an error
245
246 return !task::completed;
247}
248
249inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
250 task::Payload&& payload,
251 const sdbusplus::message::object_path& objPath)
252{
253 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
254 std::bind_front(handleCreateTask),
255 "type='signal',interface='org.freedesktop.DBus.Properties',"
256 "member='PropertiesChanged',path='" +
257 objPath.str + "'");
258 task->startTimer(std::chrono::minutes(5));
Jagpal Singh Gillc71b6c92024-04-29 16:50:53 -0700259 task->payload.emplace(std::move(payload));
Chinmay Shripad Hegde29e2bdd2025-06-06 16:23:47 +0530260 task->populateResp(asyncResp->res);
Jagpal Singh Gillc71b6c92024-04-29 16:50:53 -0700261}
262
Andrew Geissler0554c982019-04-23 14:40:12 -0500263// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
264// then no asyncResp updates will occur
Patrick Williams504af5a2025-02-03 14:29:03 -0500265inline void softwareInterfaceAdded(
266 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
267 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500268{
Michael Shen80f79a42023-08-24 13:41:53 +0000269 dbus::utility::DBusInterfacesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500270
271 sdbusplus::message::object_path objPath;
272
273 m.read(objPath, interfacesProperties);
274
Ed Tanous62598e32023-07-17 17:06:25 -0700275 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
Ed Tanouse3eb3d62022-12-21 11:56:07 -0800276 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500277 {
Ed Tanous62598e32023-07-17 17:06:25 -0700278 BMCWEB_LOG_DEBUG("interface = {}", interface.first);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500279
280 if (interface.first == "xyz.openbmc_project.Software.Activation")
281 {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500282 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +0800283 constexpr std::array<std::string_view, 1> interfaces = {
284 "xyz.openbmc_project.Software.Activation"};
285 dbus::utility::getDbusObject(
286 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700287 [objPath, asyncResp, payload(std::move(payload))](
Ed Tanous8b242752023-06-27 17:17:13 -0700288 const boost::system::error_code& ec,
Ed Tanousa3e65892021-09-16 14:13:20 -0700289 const std::vector<
290 std::pair<std::string, std::vector<std::string>>>&
291 objInfo) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400292 if (ec)
Andrew Geissler0554c982019-04-23 14:40:12 -0500293 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400294 BMCWEB_LOG_DEBUG("error_code = {}", ec);
295 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
296 if (asyncResp)
297 {
298 messages::internalError(asyncResp->res);
299 }
300 cleanUp();
301 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700302 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400303 // Ensure we only got one service back
304 if (objInfo.size() != 1)
Ed Tanous002d39b2022-05-31 08:59:27 -0700305 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400306 BMCWEB_LOG_ERROR("Invalid Object Size {}",
307 objInfo.size());
308 if (asyncResp)
309 {
310 messages::internalError(asyncResp->res);
311 }
312 cleanUp();
313 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700314 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400315 // cancel timer only when
316 // xyz.openbmc_project.Software.Activation interface
317 // is added
318 fwAvailableTimer = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700319
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400320 activateImage(objPath.str, objInfo[0].first);
321 if (asyncResp)
322 {
323 createTask(asyncResp, std::move(payload), objPath);
324 }
325 fwUpdateInProgress = false;
326 });
Patrick Williams62bafc02022-09-08 17:35:35 -0500327
328 break;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500329 }
330 }
331}
332
Myung Bae8549b952023-08-16 15:18:19 -0400333inline void afterAvailbleTimerAsyncWait(
334 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
335 const boost::system::error_code& ec)
336{
337 cleanUp();
338 if (ec == boost::asio::error::operation_aborted)
339 {
340 // expected, we were canceled before the timer completed.
341 return;
342 }
343 BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
344 BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
345 if (ec)
346 {
347 BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
348 return;
349 }
350 if (asyncResp)
351 {
352 redfish::messages::internalError(asyncResp->res);
353 }
354}
355
Patrick Williams504af5a2025-02-03 14:29:03 -0500356inline void handleUpdateErrorType(
357 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& url,
358 const std::string& type)
Myung Bae8549b952023-08-16 15:18:19 -0400359{
Ed Tanousc87294a2024-11-16 22:17:12 -0800360 // NOLINTBEGIN(bugprone-branch-clone)
Myung Bae8549b952023-08-16 15:18:19 -0400361 if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
362 {
Ed Tanous48fb20b2024-11-17 11:51:13 -0800363 messages::missingOrMalformedPart(asyncResp->res);
Myung Bae8549b952023-08-16 15:18:19 -0400364 }
365 else if (type ==
366 "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
367 {
Ed Tanous48fb20b2024-11-17 11:51:13 -0800368 messages::missingOrMalformedPart(asyncResp->res);
Myung Bae8549b952023-08-16 15:18:19 -0400369 }
370 else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
371 {
Ed Tanous48fb20b2024-11-17 11:51:13 -0800372 messages::missingOrMalformedPart(asyncResp->res);
Myung Bae8549b952023-08-16 15:18:19 -0400373 }
374 else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
375 {
Ed Tanousc87294a2024-11-16 22:17:12 -0800376 messages::resourceAlreadyExists(asyncResp->res, "UpdateService",
377 "Version", "uploaded version");
Myung Bae8549b952023-08-16 15:18:19 -0400378 }
379 else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
380 {
Ed Tanous48fb20b2024-11-17 11:51:13 -0800381 messages::serviceTemporarilyUnavailable(asyncResp->res, url);
Myung Bae8549b952023-08-16 15:18:19 -0400382 }
Myung Bae4034a652023-08-17 08:47:35 -0400383 else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
Myung Bae8549b952023-08-16 15:18:19 -0400384 {
Ed Tanousc87294a2024-11-16 22:17:12 -0800385 messages::internalError(asyncResp->res);
Myung Bae4034a652023-08-17 08:47:35 -0400386 }
387 else if (type ==
388 "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
389 {
Ed Tanousc87294a2024-11-16 22:17:12 -0800390 messages::internalError(asyncResp->res);
Myung Bae4034a652023-08-17 08:47:35 -0400391 }
392 else if (type ==
393 "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
394 {
Ed Tanous48fb20b2024-11-17 11:51:13 -0800395 messages::missingOrMalformedPart(asyncResp->res);
Myung Bae4034a652023-08-17 08:47:35 -0400396 }
397 else if (type ==
398 "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
399 type == "xyz.openbmc_project.Software.Version.Error.HostFile")
400 {
401 BMCWEB_LOG_ERROR("Software Image Error type={}", type);
Ed Tanous48fb20b2024-11-17 11:51:13 -0800402 messages::internalError(asyncResp->res);
Myung Bae8549b952023-08-16 15:18:19 -0400403 }
Myung Bae4034a652023-08-17 08:47:35 -0400404 else
405 {
406 // Unrelated error types. Ignored
407 BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
408 return;
409 }
Ed Tanousc87294a2024-11-16 22:17:12 -0800410 // NOLINTEND(bugprone-branch-clone)
Myung Bae4034a652023-08-17 08:47:35 -0400411 // Clear the timer
412 fwAvailableTimer = nullptr;
Myung Bae8549b952023-08-16 15:18:19 -0400413}
414
Patrick Williams504af5a2025-02-03 14:29:03 -0500415inline void afterUpdateErrorMatcher(
416 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& url,
417 sdbusplus::message_t& m)
Myung Bae8549b952023-08-16 15:18:19 -0400418{
Michael Shen80f79a42023-08-24 13:41:53 +0000419 dbus::utility::DBusInterfacesMap interfacesProperties;
Myung Bae8549b952023-08-16 15:18:19 -0400420 sdbusplus::message::object_path objPath;
421 m.read(objPath, interfacesProperties);
422 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
423 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
424 interface : interfacesProperties)
425 {
426 if (interface.first == "xyz.openbmc_project.Logging.Entry")
427 {
428 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
429 value : interface.second)
430 {
431 if (value.first != "Message")
432 {
433 continue;
434 }
435 const std::string* type =
436 std::get_if<std::string>(&value.second);
437 if (type == nullptr)
438 {
439 // if this was our message, timeout will cover it
440 return;
441 }
Myung Bae8549b952023-08-16 15:18:19 -0400442 handleUpdateErrorType(asyncResp, url, *type);
443 }
444 }
445 }
446}
447
Andrew Geissler0554c982019-04-23 14:40:12 -0500448// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
449// then no asyncResp updates will occur
Ed Tanousf5139332024-04-03 13:25:04 -0700450inline void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800451 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
452 const crow::Request& req, const std::string& url,
Gunnar Mills19406772025-03-04 14:42:46 -0600453 int timeoutTimeSeconds = 50)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500454{
455 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800456 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500457 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500458 if (asyncResp)
459 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500460 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
461 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500462 return;
463 }
464
Andrew Geissler0554c982019-04-23 14:40:12 -0500465 fwAvailableTimer =
Ed Tanousd98a2f92025-02-06 17:36:31 -0800466 std::make_unique<boost::asio::steady_timer>(getIoContext());
Andrew Geissler86adcd62019-04-18 10:58:05 -0500467
Ed Tanous271584a2019-07-09 16:24:22 -0700468 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500469
470 fwAvailableTimer->async_wait(
Myung Bae8549b952023-08-16 15:18:19 -0400471 std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
472
Ed Tanousa3e65892021-09-16 14:13:20 -0700473 task::Payload payload(req);
Patrick Williams59d494e2022-07-22 19:26:55 -0500474 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
Ed Tanous62598e32023-07-17 17:06:25 -0700475 BMCWEB_LOG_DEBUG("Match fired");
Ed Tanousa3e65892021-09-16 14:13:20 -0700476 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500477 };
478
479 fwUpdateInProgress = true;
480
Patrick Williams59d494e2022-07-22 19:26:55 -0500481 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Andrew Geissler86adcd62019-04-18 10:58:05 -0500482 *crow::connections::systemBus,
483 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
484 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
485 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700486
Patrick Williams59d494e2022-07-22 19:26:55 -0500487 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
James Feist4cde5d92020-06-11 10:39:55 -0700488 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800489 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
490 "member='InterfacesAdded',"
491 "path='/xyz/openbmc_project/logging'",
Myung Bae8549b952023-08-16 15:18:19 -0400492 std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500493}
Jennifer Lee729dae72018-04-24 15:59:34 -0700494
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400495inline std::optional<boost::urls::url> parseSimpleUpdateUrl(
496 std::string imageURI, std::optional<std::string> transferProtocol,
497 crow::Response& res)
Ed Tanousf86bcc82023-08-25 09:34:07 -0700498{
499 if (imageURI.find("://") == std::string::npos)
500 {
501 if (imageURI.starts_with("/"))
502 {
503 messages::actionParameterValueTypeError(
504 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
505 return std::nullopt;
506 }
507 if (!transferProtocol)
508 {
509 messages::actionParameterValueTypeError(
510 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
511 return std::nullopt;
512 }
Ed Tanous6a371402024-12-03 14:01:25 -0800513 // OpenBMC currently only supports HTTPS
514 if (*transferProtocol == "HTTPS")
Ed Tanouse5cf7772024-04-03 13:45:31 -0700515 {
516 imageURI = "https://" + imageURI;
517 }
Ed Tanous757178a2024-04-03 14:32:38 -0700518 else
Ed Tanousf86bcc82023-08-25 09:34:07 -0700519 {
520 messages::actionParameterNotSupported(res, "TransferProtocol",
521 *transferProtocol);
522 BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
523 *transferProtocol);
524 return std::nullopt;
525 }
Ed Tanousf86bcc82023-08-25 09:34:07 -0700526 }
527
528 boost::system::result<boost::urls::url> url =
529 boost::urls::parse_absolute_uri(imageURI);
530 if (!url)
531 {
532 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
533 "UpdateService.SimpleUpdate");
534
535 return std::nullopt;
536 }
537 url->normalize();
538
Ed Tanous757178a2024-04-03 14:32:38 -0700539 if (url->scheme() == "tftp")
540 {
541 if (url->encoded_path().size() < 2)
542 {
543 messages::actionParameterNotSupported(res, "ImageURI",
544 url->buffer());
545 return std::nullopt;
546 }
547 }
Ed Tanouse5cf7772024-04-03 13:45:31 -0700548 else if (url->scheme() == "https")
549 {
550 // Empty paths default to "/"
551 if (url->encoded_path().empty())
552 {
553 url->set_encoded_path("/");
554 }
555 }
Ed Tanous757178a2024-04-03 14:32:38 -0700556 else
Ed Tanousf86bcc82023-08-25 09:34:07 -0700557 {
558 messages::actionParameterNotSupported(res, "ImageURI", imageURI);
559 return std::nullopt;
560 }
Ed Tanous757178a2024-04-03 14:32:38 -0700561
562 if (url->encoded_path().empty())
Ed Tanousf86bcc82023-08-25 09:34:07 -0700563 {
Ed Tanous757178a2024-04-03 14:32:38 -0700564 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
565 "UpdateService.SimpleUpdate");
Ed Tanousf86bcc82023-08-25 09:34:07 -0700566 return std::nullopt;
567 }
Ed Tanous757178a2024-04-03 14:32:38 -0700568
569 return *url;
Ed Tanousf86bcc82023-08-25 09:34:07 -0700570}
571
Ed Tanouse5cf7772024-04-03 13:45:31 -0700572inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
573 const boost::urls::url_view_base& url)
574{
575 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
576 url.buffer());
577}
578
Ed Tanousf5139332024-04-03 13:25:04 -0700579inline void handleUpdateServiceSimpleUpdateAction(
580 crow::App& app, const crow::Request& req,
581 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Andrew Geissler0554c982019-04-23 14:40:12 -0500582{
Ed Tanousf5139332024-04-03 13:25:04 -0700583 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
584 {
585 return;
586 }
587
588 std::optional<std::string> transferProtocol;
589 std::string imageURI;
590
591 BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
592
593 // User can pass in both TransferProtocol and ImageURI parameters or
594 // they can pass in just the ImageURI with the transfer protocol
595 // embedded within it.
596 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
597 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
598
Patrick Williams504af5a2025-02-03 14:29:03 -0500599 if (!json_util::readJsonAction( //
600 req, asyncResp->res, //
601 "ImageURI", imageURI, //
Myung Baeafc474a2024-10-09 00:53:29 -0700602 "TransferProtocol", transferProtocol //
603 ))
Ed Tanousf5139332024-04-03 13:25:04 -0700604 {
605 BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
606 return;
607 }
608
Ed Tanous757178a2024-04-03 14:32:38 -0700609 std::optional<boost::urls::url> url =
610 parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res);
611 if (!url)
Ed Tanousf5139332024-04-03 13:25:04 -0700612 {
613 return;
614 }
Jagpal Singh Gill4e338b22024-06-14 14:24:56 -0700615 if (url->scheme() == "https")
Ed Tanouse5cf7772024-04-03 13:45:31 -0700616 {
617 doHttpsUpdate(asyncResp, *url);
618 }
Ed Tanous757178a2024-04-03 14:32:38 -0700619 else
620 {
621 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
622 url->buffer());
623 return;
624 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700625
Ed Tanousf5139332024-04-03 13:25:04 -0700626 BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700627}
628
George Liu0ed80c82020-05-12 16:06:27 +0800629inline void uploadImageFile(crow::Response& res, std::string_view body)
630{
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700631 std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
632
Ed Tanous62598e32023-07-17 17:06:25 -0700633 BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
George Liu0ed80c82020-05-12 16:06:27 +0800634 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
635 std::ofstream::trunc);
636 // set the permission of the file to 640
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400637 std::filesystem::perms permission =
638 std::filesystem::perms::owner_read | std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800639 std::filesystem::permissions(filepath, permission);
640 out << body;
641
642 if (out.bad())
643 {
644 messages::internalError(res);
645 cleanUp();
646 }
647}
648
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700649// Convert the Request Apply Time to the D-Bus value
650inline bool convertApplyTime(crow::Response& res, const std::string& applyTime,
651 std::string& applyTimeNewVal)
652{
653 if (applyTime == "Immediate")
654 {
655 applyTimeNewVal =
Jagpal Singh Gill049079f2024-06-02 18:11:13 -0700656 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700657 }
658 else if (applyTime == "OnReset")
659 {
660 applyTimeNewVal =
Jagpal Singh Gill049079f2024-06-02 18:11:13 -0700661 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700662 }
663 else
664 {
665 BMCWEB_LOG_WARNING(
666 "ApplyTime value {} is not in the list of acceptable values",
667 applyTime);
668 messages::propertyValueNotInList(res, applyTime, "ApplyTime");
669 return false;
670 }
671 return true;
672}
673
George Liu0ed80c82020-05-12 16:06:27 +0800674inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
675 const std::string& applyTime)
676{
677 std::string applyTimeNewVal;
Jagpal Singh Gill049079f2024-06-02 18:11:13 -0700678 if (!convertApplyTime(asyncResp->res, applyTime, applyTimeNewVal))
George Liu0ed80c82020-05-12 16:06:27 +0800679 {
George Liu0ed80c82020-05-12 16:06:27 +0800680 return;
681 }
682
Ginu Georgee93abac2024-06-14 17:35:27 +0530683 setDbusProperty(asyncResp, "ApplyTime", "xyz.openbmc_project.Settings",
Ed Tanousd02aad32024-02-13 14:43:34 -0800684 sdbusplus::message::object_path(
685 "/xyz/openbmc_project/software/apply_time"),
686 "xyz.openbmc_project.Software.ApplyTime",
Ginu Georgee93abac2024-06-14 17:35:27 +0530687 "RequestedApplyTime", applyTimeNewVal);
George Liu0ed80c82020-05-12 16:06:27 +0800688}
689
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530690struct MultiPartUpdate
George Liu0ed80c82020-05-12 16:06:27 +0800691{
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700692 std::string uploadData;
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530693 struct UpdateParameters
694 {
695 std::optional<std::string> applyTime;
rajeeranjan9fbf8532025-07-23 23:25:52 +0530696 std::optional<std::vector<std::string>> targets;
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530697 } params;
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700698};
699
Patrick Williams504af5a2025-02-03 14:29:03 -0500700inline std::optional<std::string> processUrl(
701 boost::system::result<boost::urls::url_view>& url)
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700702{
703 if (!url)
704 {
705 return std::nullopt;
706 }
707 if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers",
708 BMCWEB_REDFISH_MANAGER_URI_NAME))
709 {
710 return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME));
711 }
712 if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
713 {
714 return std::nullopt;
715 }
716 std::string firmwareId;
717 if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService",
718 "FirmwareInventory",
719 std::ref(firmwareId)))
720 {
721 return std::nullopt;
722 }
723
724 return std::make_optional(firmwareId);
725}
726
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530727inline std::optional<std::string> parseFormPartName(
728 const boost::beast::http::fields::const_iterator& contentDisposition)
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700729{
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530730 size_t semicolonPos = contentDisposition->value().find(';');
731 if (semicolonPos == std::string::npos)
732 {
733 return std::nullopt;
734 }
735
736 for (const auto& param : boost::beast::http::param_list{
737 contentDisposition->value().substr(semicolonPos)})
738 {
739 if (param.first == "name" && !param.second.empty())
740 {
741 return std::string(param.second);
742 }
743 }
744 return std::nullopt;
745}
746
747inline std::optional<MultiPartUpdate::UpdateParameters> processUpdateParameters(
748 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
749 std::string_view content)
750{
751 MultiPartUpdate::UpdateParameters multiRet;
752 nlohmann::json jsonContent = nlohmann::json::parse(content, nullptr, false);
753 if (jsonContent.is_discarded())
754 {
755 return std::nullopt;
756 }
757 nlohmann::json::object_t* obj =
758 jsonContent.get_ptr<nlohmann::json::object_t*>();
759 if (obj == nullptr)
760 {
761 messages::propertyValueTypeError(asyncResp->res, content,
762 "UpdateParameters");
763 return std::nullopt;
764 }
765
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530766 if (!json_util::readJsonObject( //
767 *obj, asyncResp->res, //
768 "@Redfish.OperationApplyTime", multiRet.applyTime, //
rajeeranjan9fbf8532025-07-23 23:25:52 +0530769 "Targets", multiRet.targets //
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530770 ))
771 {
772 return std::nullopt;
773 }
774
rajeeranjan9fbf8532025-07-23 23:25:52 +0530775 if (multiRet.targets)
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530776 {
rajeeranjan9fbf8532025-07-23 23:25:52 +0530777 if (multiRet.targets->size() > 1)
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530778 {
rajeeranjan9fbf8532025-07-23 23:25:52 +0530779 messages::propertyValueFormatError(asyncResp->res,
780 *multiRet.targets, "Targets");
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530781 return std::nullopt;
782 }
rajeeranjan9fbf8532025-07-23 23:25:52 +0530783
784 for (auto& target : *multiRet.targets)
785 {
786 boost::system::result<boost::urls::url_view> url =
787 boost::urls::parse_origin_form(target);
788 auto res = processUrl(url);
789 if (!res.has_value())
790 {
791 messages::propertyValueFormatError(asyncResp->res, target,
792 "Targets");
793 return std::nullopt;
794 }
795 target = res.value();
796 }
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530797 }
rajeeranjan9fbf8532025-07-23 23:25:52 +0530798
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530799 return multiRet;
800}
801
802inline std::optional<MultiPartUpdate> extractMultipartUpdateParameters(
803 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, MultipartParser parser)
804{
805 MultiPartUpdate multiRet;
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700806 for (FormPart& formpart : parser.mime_fields)
George Liu0ed80c82020-05-12 16:06:27 +0800807 {
808 boost::beast::http::fields::const_iterator it =
809 formpart.fields.find("Content-Disposition");
810 if (it == formpart.fields.end())
811 {
Ed Tanous62598e32023-07-17 17:06:25 -0700812 BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700813 return std::nullopt;
George Liu0ed80c82020-05-12 16:06:27 +0800814 }
Ed Tanous62598e32023-07-17 17:06:25 -0700815 BMCWEB_LOG_INFO("Parsing value {}", it->value());
George Liu0ed80c82020-05-12 16:06:27 +0800816
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530817 auto formFieldNameOpt = parseFormPartName(it);
818 if (!formFieldNameOpt.has_value())
George Liu0ed80c82020-05-12 16:06:27 +0800819 {
820 continue;
821 }
822
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530823 const std::string& formFieldName = formFieldNameOpt.value();
824
825 if (formFieldName == "UpdateParameters")
George Liu0ed80c82020-05-12 16:06:27 +0800826 {
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530827 std::optional<MultiPartUpdate::UpdateParameters> params =
828 processUpdateParameters(asyncResp, formpart.content);
829 if (!params)
George Liu0ed80c82020-05-12 16:06:27 +0800830 {
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530831 return std::nullopt;
George Liu0ed80c82020-05-12 16:06:27 +0800832 }
rajeeranjan2c6f32a2025-07-21 11:34:50 +0530833 multiRet.params = std::move(*params);
834 }
835 else if (formFieldName == "UpdateFile")
836 {
837 multiRet.uploadData = std::move(formpart.content);
George Liu0ed80c82020-05-12 16:06:27 +0800838 }
839 }
840
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700841 if (multiRet.uploadData.empty())
George Liu0ed80c82020-05-12 16:06:27 +0800842 {
Ed Tanous62598e32023-07-17 17:06:25 -0700843 BMCWEB_LOG_ERROR("Upload data is NULL");
George Liu0ed80c82020-05-12 16:06:27 +0800844 messages::propertyMissing(asyncResp->res, "UpdateFile");
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700845 return std::nullopt;
846 }
rajeeranjan9fbf8532025-07-23 23:25:52 +0530847
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700848 return multiRet;
849}
850
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400851inline void handleStartUpdate(
852 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
853 const std::string& objectPath, const boost::system::error_code& ec,
854 const sdbusplus::message::object_path& retPath)
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700855{
856 if (ec)
857 {
858 BMCWEB_LOG_ERROR("error_code = {}", ec);
859 BMCWEB_LOG_ERROR("error msg = {}", ec.message());
860 messages::internalError(asyncResp->res);
861 return;
862 }
863
Jagpal Singh Gill587090c2024-08-12 00:24:16 -0700864 BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}",
865 objectPath, retPath.str);
866 createTask(asyncResp, std::move(payload), retPath);
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700867}
868
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400869inline void startUpdate(
870 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
871 const MemoryFileDescriptor& memfd, const std::string& applyTime,
872 const std::string& objectPath, const std::string& serviceName)
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700873{
Ed Tanous177612a2025-02-14 15:16:09 -0800874 dbus::utility::async_method_call(
875 asyncResp,
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700876 [asyncResp, payload = std::move(payload),
877 objectPath](const boost::system::error_code& ec1,
878 const sdbusplus::message::object_path& retPath) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400879 handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1,
880 retPath);
881 },
rajeeranjan9fbf8532025-07-23 23:25:52 +0530882 serviceName, objectPath, updateInterface, "StartUpdate",
883 sdbusplus::message::unix_fd(memfd.fd), applyTime);
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700884}
885
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700886inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
887 task::Payload payload, const MemoryFileDescriptor& memfd,
888 const std::string& applyTime, const std::string& target,
889 const boost::system::error_code& ec,
890 const dbus::utility::MapperGetSubTreeResponse& subtree)
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700891{
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700892 using SwInfoMap = std::unordered_map<
893 std::string, std::pair<sdbusplus::message::object_path, std::string>>;
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700894 SwInfoMap swInfoMap;
895
896 if (ec)
897 {
898 BMCWEB_LOG_ERROR("error_code = {}", ec);
899 BMCWEB_LOG_ERROR("error msg = {}", ec.message());
900 messages::internalError(asyncResp->res);
901 return;
902 }
903 BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size());
904
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700905 for (const auto& entry : subtree)
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700906 {
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700907 sdbusplus::message::object_path path(entry.first);
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700908 std::string swId = path.filename();
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700909 swInfoMap.emplace(swId, make_pair(path, entry.second[0].first));
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700910 }
911
912 auto swEntry = swInfoMap.find(target);
913 if (swEntry == swInfoMap.end())
914 {
915 BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target);
916 messages::propertyValueFormatError(asyncResp->res, target, "Targets");
917 return;
918 }
919
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700920 BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}",
921 swEntry->second.first.str, swEntry->second.second);
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700922
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700923 startUpdate(asyncResp, std::move(payload), memfd, applyTime,
924 swEntry->second.first.str, swEntry->second.second);
925}
926
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400927inline void handleBMCUpdate(
928 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
929 const MemoryFileDescriptor& memfd, const std::string& applyTime,
930 const boost::system::error_code& ec,
931 const dbus::utility::MapperEndPoints& functionalSoftware)
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700932{
933 if (ec)
934 {
935 BMCWEB_LOG_ERROR("error_code = {}", ec);
936 BMCWEB_LOG_ERROR("error msg = {}", ec.message());
937 messages::internalError(asyncResp->res);
938 return;
939 }
940 if (functionalSoftware.size() != 1)
941 {
942 BMCWEB_LOG_ERROR("Found {} functional software endpoints",
943 functionalSoftware.size());
944 messages::internalError(asyncResp->res);
945 return;
946 }
947
948 startUpdate(asyncResp, std::move(payload), memfd, applyTime,
949 functionalSoftware[0], "xyz.openbmc_project.Software.Manager");
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700950}
951
rajeeranjan9fbf8532025-07-23 23:25:52 +0530952inline void handleMultipartManagerUpdate(
953 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
954 const MemoryFileDescriptor& memfd, const std::string& applyTime,
955 const boost::system::error_code& ec,
956 const dbus::utility::MapperGetSubTreeResponse& subtree)
957{
958 if (ec)
959 {
960 BMCWEB_LOG_ERROR("error_code = {}", ec);
961 BMCWEB_LOG_ERROR("error msg = {}", ec.message());
962 messages::internalError(asyncResp->res);
963 return;
964 }
965 if (subtree.size() != 1)
966 {
967 BMCWEB_LOG_ERROR("Found {} MultipartUpdate objects, expected exactly 1",
968 subtree.size());
969 messages::internalError(asyncResp->res);
970 return;
971 }
972
973 const auto& [objectPath, services] = subtree[0];
974 for (const auto& [serviceName, ifaces] : services)
975 {
976 if (std::find(ifaces.begin(), ifaces.end(), updateInterface) !=
977 ifaces.end())
978 {
979 startUpdate(asyncResp, std::move(payload), memfd, applyTime,
980 objectPath, serviceName);
981 return;
982 }
983 }
984 BMCWEB_LOG_ERROR(
985 "MultipartUpdate object at {} does not implement {} interface",
986 objectPath, updateInterface);
987 messages::internalError(asyncResp->res);
988}
989
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400990inline void processUpdateRequest(
991 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
992 task::Payload&& payload, std::string_view body,
rajeeranjan9fbf8532025-07-23 23:25:52 +0530993 const std::string& applyTime, const std::vector<std::string>& targets)
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700994{
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700995 MemoryFileDescriptor memfd("update-image");
996 if (memfd.fd == -1)
997 {
998 BMCWEB_LOG_ERROR("Failed to create image memfd");
999 messages::internalError(asyncResp->res);
1000 return;
1001 }
1002 if (write(memfd.fd, body.data(), body.length()) !=
1003 static_cast<ssize_t>(body.length()))
1004 {
1005 BMCWEB_LOG_ERROR("Failed to write to image memfd");
1006 messages::internalError(asyncResp->res);
1007 return;
1008 }
1009 if (!memfd.rewind())
1010 {
1011 messages::internalError(asyncResp->res);
1012 return;
1013 }
1014
rajeeranjan9fbf8532025-07-23 23:25:52 +05301015 if (targets.empty())
1016 {
1017 constexpr std::array<std::string_view, 1> interfaces = {
1018 "xyz.openbmc_project.Software.MultipartUpdate"};
1019 dbus::utility::getSubTree(
1020 "/xyz/openbmc_project/software", 1, interfaces,
1021 [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
1022 applyTime](const boost::system::error_code& ec,
1023 const dbus::utility::MapperGetSubTreeResponse&
1024 subtree) mutable {
1025 handleMultipartManagerUpdate(asyncResp, std::move(payload),
1026 memfd, applyTime, ec, subtree);
1027 });
1028 return;
1029 }
1030
1031 if (targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME)
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -07001032 {
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -07001033 dbus::utility::getAssociationEndPoints(
Jagpal Singh Gill89449bb2024-08-12 16:17:58 -07001034 "/xyz/openbmc_project/software/bmc/updateable",
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -07001035 [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
1036 applyTime](
1037 const boost::system::error_code& ec,
1038 const dbus::utility::MapperEndPoints& objectPaths) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001039 handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime,
1040 ec, objectPaths);
1041 });
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -07001042 }
1043 else
1044 {
1045 constexpr std::array<std::string_view, 1> interfaces = {
1046 "xyz.openbmc_project.Software.Version"};
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -07001047 dbus::utility::getSubTree(
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -07001048 "/xyz/openbmc_project/software", 1, interfaces,
1049 [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -07001050 applyTime, targets](const boost::system::error_code& ec,
1051 const dbus::utility::MapperGetSubTreeResponse&
1052 subtree) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001053 getSwInfo(asyncResp, std::move(payload), memfd, applyTime,
1054 targets[0], ec, subtree);
1055 });
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -07001056 }
1057}
1058
Patrick Williams504af5a2025-02-03 14:29:03 -05001059inline void updateMultipartContext(
1060 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1061 const crow::Request& req, MultipartParser&& parser)
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -07001062{
rajeeranjan2c6f32a2025-07-21 11:34:50 +05301063 std::optional<MultiPartUpdate> multipart =
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -07001064 extractMultipartUpdateParameters(asyncResp, std::move(parser));
1065 if (!multipart)
1066 {
George Liu0ed80c82020-05-12 16:06:27 +08001067 return;
1068 }
rajeeranjan2c6f32a2025-07-21 11:34:50 +05301069 if (!multipart->params.applyTime)
George Liu0ed80c82020-05-12 16:06:27 +08001070 {
rajeeranjan2c6f32a2025-07-21 11:34:50 +05301071 multipart->params.applyTime = "OnReset";
George Liu0ed80c82020-05-12 16:06:27 +08001072 }
1073
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -07001074 if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
1075 {
Jagpal Singh Gill9dae4de2024-06-02 23:43:56 -07001076 std::string applyTimeNewVal;
rajeeranjan2c6f32a2025-07-21 11:34:50 +05301077 if (!convertApplyTime(asyncResp->res, *multipart->params.applyTime,
Jagpal Singh Gill9dae4de2024-06-02 23:43:56 -07001078 applyTimeNewVal))
1079 {
1080 return;
1081 }
1082 task::Payload payload(req);
1083
rajeeranjan9fbf8532025-07-23 23:25:52 +05301084 processUpdateRequest(
1085 asyncResp, std::move(payload), multipart->uploadData,
1086 applyTimeNewVal,
1087 multipart->params.targets.value_or(std::vector<std::string>{}));
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -07001088 }
1089 else
1090 {
rajeeranjan2c6f32a2025-07-21 11:34:50 +05301091 setApplyTime(asyncResp, *multipart->params.applyTime);
George Liu0ed80c82020-05-12 16:06:27 +08001092
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -07001093 // Setup callback for when new software detected
1094 monitorForSoftwareAvailable(asyncResp, req,
1095 "/redfish/v1/UpdateService");
Ed Tanous6b54e4e2024-04-10 08:58:48 -07001096
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -07001097 uploadImageFile(asyncResp->res, multipart->uploadData);
1098 }
George Liu0ed80c82020-05-12 16:06:27 +08001099}
1100
Jagpal Singh Gill9dae4de2024-06-02 23:43:56 -07001101inline void doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1102 const crow::Request& req)
1103{
1104 if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
1105 {
1106 task::Payload payload(req);
1107 // HTTP push only supports BMC updates (with ApplyTime as immediate) for
1108 // backwards compatibility. Specific component updates will be handled
1109 // through Multipart form HTTP push.
1110 std::vector<std::string> targets;
1111 targets.emplace_back(BMCWEB_REDFISH_MANAGER_URI_NAME);
1112
1113 processUpdateRequest(
1114 asyncResp, std::move(payload), req.body(),
1115 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate",
1116 targets);
1117 }
1118 else
1119 {
1120 // Setup callback for when new software detected
1121 monitorForSoftwareAvailable(asyncResp, req,
1122 "/redfish/v1/UpdateService");
1123
1124 uploadImageFile(asyncResp->res, req.body());
1125 }
1126}
1127
Patrick Williams504af5a2025-02-03 14:29:03 -05001128inline void handleUpdateServicePost(
1129 App& app, const crow::Request& req,
1130 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanousc2051d12022-05-11 12:21:55 -07001131{
Carson Labrado3ba00072022-06-06 19:40:56 +00001132 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -07001133 {
1134 return;
1135 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001136 std::string_view contentType = req.getHeaderValue("Content-Type");
Ed Tanousc2051d12022-05-11 12:21:55 -07001137
Ed Tanous62598e32023-07-17 17:06:25 -07001138 BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
Ed Tanousc2051d12022-05-11 12:21:55 -07001139
rajeeranjan0c814aa2025-03-25 13:31:49 +05301140 // Make sure that content type is application/octet-stream
Ed Tanous18f8f602023-07-18 10:07:23 -07001141 if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
George Liu0ed80c82020-05-12 16:06:27 +08001142 {
Jagpal Singh Gill9dae4de2024-06-02 23:43:56 -07001143 doHTTPUpdate(asyncResp, req);
George Liu0ed80c82020-05-12 16:06:27 +08001144 }
rajeeranjan0c814aa2025-03-25 13:31:49 +05301145 else
1146 {
1147 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
1148 asyncResp->res.result(boost::beast::http::status::bad_request);
1149 }
1150}
1151
1152inline void handleUpdateServiceMultipartUpdatePost(
1153 App& app, const crow::Request& req,
1154 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1155{
1156 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1157 {
1158 return;
1159 }
1160
1161 std::string_view contentType = req.getHeaderValue("Content-Type");
1162 // Make sure that content type is multipart/form-data
1163 if (contentType.starts_with("multipart/form-data"))
George Liu0ed80c82020-05-12 16:06:27 +08001164 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001165 MultipartParser parser;
1166
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001167 ParserError ec = parser.parse(req);
1168 if (ec != ParserError::PARSER_SUCCESS)
1169 {
1170 // handle error
Ed Tanous62598e32023-07-17 17:06:25 -07001171 BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
1172 static_cast<int>(ec));
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001173 messages::internalError(asyncResp->res);
1174 return;
1175 }
Ed Tanous6b54e4e2024-04-10 08:58:48 -07001176
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -07001177 updateMultipartContext(asyncResp, req, std::move(parser));
George Liu0ed80c82020-05-12 16:06:27 +08001178 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001179 else
1180 {
Ed Tanous62598e32023-07-17 17:06:25 -07001181 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
Sandeepde5259d2025-09-23 16:30:59 +05301182 asyncResp->res.result(
1183 boost::beast::http::status::unsupported_media_type);
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001184 }
Ed Tanousc2051d12022-05-11 12:21:55 -07001185}
1186
Patrick Williams504af5a2025-02-03 14:29:03 -05001187inline void handleUpdateServiceGet(
1188 App& app, const crow::Request& req,
1189 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001190{
Ed Tanousf5139332024-04-03 13:25:04 -07001191 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1192 {
1193 return;
1194 }
1195 asyncResp->res.jsonValue["@odata.type"] =
1196 "#UpdateService.v1_11_1.UpdateService";
1197 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
1198 asyncResp->res.jsonValue["Id"] = "UpdateService";
1199 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
1200 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -07001201
Ed Tanousf5139332024-04-03 13:25:04 -07001202 asyncResp->res.jsonValue["HttpPushUri"] =
1203 "/redfish/v1/UpdateService/update";
1204 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
rajeeranjan0c814aa2025-03-25 13:31:49 +05301205 "/redfish/v1/UpdateService/update-multipart";
Ed Tanous4dc23f32022-05-11 11:32:19 -07001206
Ed Tanousf5139332024-04-03 13:25:04 -07001207 // UpdateService cannot be disabled
1208 asyncResp->res.jsonValue["ServiceEnabled"] = true;
1209 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
1210 "/redfish/v1/UpdateService/FirmwareInventory";
1211 // Get the MaxImageSizeBytes
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001212 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
1213 BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +05301214
Ed Tanous6a371402024-12-03 14:01:25 -08001215 if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE)
Ed Tanous25b54db2024-04-17 15:40:31 -07001216 {
Ed Tanous6a371402024-12-03 14:01:25 -08001217 // Update Actions object.
1218 nlohmann::json& updateSvcSimpleUpdate =
1219 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
1220 updateSvcSimpleUpdate["target"] =
1221 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
Ed Tanous757178a2024-04-03 14:32:38 -07001222
Ed Tanous6a371402024-12-03 14:01:25 -08001223 nlohmann::json::array_t allowed;
1224 allowed.emplace_back(update_service::TransferProtocolType::HTTPS);
1225 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
1226 std::move(allowed);
1227 }
Ed Tanous757178a2024-04-03 14:32:38 -07001228
Ed Tanous539d8c62024-06-19 14:38:27 -07001229 asyncResp->res
1230 .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] =
1231 update_service::ApplyTime::Immediate;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001232}
Ed Tanousf5139332024-04-03 13:25:04 -07001233
1234inline void handleUpdateServiceFirmwareInventoryCollectionGet(
1235 App& app, const crow::Request& req,
1236 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1237{
1238 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1239 {
1240 return;
1241 }
1242 asyncResp->res.jsonValue["@odata.type"] =
1243 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
1244 asyncResp->res.jsonValue["@odata.id"] =
1245 "/redfish/v1/UpdateService/FirmwareInventory";
1246 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
1247 const std::array<const std::string_view, 1> iface = {
1248 "xyz.openbmc_project.Software.Version"};
1249
1250 redfish::collection_util::getCollectionMembers(
1251 asyncResp,
1252 boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface,
1253 "/xyz/openbmc_project/software");
1254}
1255
Alexander Hansen50ab50d2025-08-13 16:34:49 +02001256inline void addRelatedItem(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1257 const boost::urls::url& url)
1258{
1259 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
1260 nlohmann::json::object_t item;
1261 item["@odata.id"] = url;
1262 relatedItem.emplace_back(std::move(item));
1263 asyncResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
1264}
1265
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001266/* Fill related item links (i.e. bmc, bios) in for inventory */
Ed Tanousf5139332024-04-03 13:25:04 -07001267inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1268 const std::string& purpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001269{
Willy Tueee00132022-06-14 14:53:17 -07001270 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001271 {
Alexander Hansen50ab50d2025-08-13 16:34:49 +02001272 auto url = boost::urls::format("/redfish/v1/Managers/{}",
1273 BMCWEB_REDFISH_MANAGER_URI_NAME);
1274 addRelatedItem(asyncResp, url);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001275 }
Willy Tueee00132022-06-14 14:53:17 -07001276 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001277 {
Alexander Hansen50ab50d2025-08-13 16:34:49 +02001278 auto url = boost::urls::format("/redfish/v1/Systems/{}/Bios",
1279 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1280
1281 addRelatedItem(asyncResp, url);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001282 }
1283 else
1284 {
Carson Labradobf2dded2023-08-10 00:37:06 +00001285 BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001286 }
1287}
1288
Alexander Hansen78e69022025-08-13 16:20:29 +02001289inline void getSoftwareVersionCallback(
1290 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1291 const std::string& swId, const boost::system::error_code& ec,
1292 const dbus::utility::DBusPropertiesMap& propertiesList)
1293{
1294 if (ec)
1295 {
1296 messages::internalError(asyncResp->res);
1297 return;
1298 }
1299 const std::string* swInvPurpose = nullptr;
1300 const std::string* version = nullptr;
1301 const bool success = sdbusplus::unpackPropertiesNoThrow(
1302 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
1303 swInvPurpose, "Version", version);
1304 if (!success)
1305 {
1306 messages::internalError(asyncResp->res);
1307 return;
1308 }
1309 if (swInvPurpose == nullptr)
1310 {
1311 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
1312 messages::internalError(asyncResp->res);
1313 return;
1314 }
1315 BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
1316 if (version == nullptr)
1317 {
1318 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
1319 messages::internalError(asyncResp->res);
1320 return;
1321 }
1322 asyncResp->res.jsonValue["Version"] = *version;
1323 asyncResp->res.jsonValue["Id"] = swId;
1324 // swInvPurpose is of format:
1325 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
1326 // Translate this to "ABC image"
1327 size_t endDesc = swInvPurpose->rfind('.');
1328 if (endDesc == std::string::npos)
1329 {
1330 messages::internalError(asyncResp->res);
1331 return;
1332 }
1333 endDesc++;
1334 if (endDesc >= swInvPurpose->size())
1335 {
1336 messages::internalError(asyncResp->res);
1337 return;
1338 }
1339 std::string formatDesc = swInvPurpose->substr(endDesc);
1340 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1341 getRelatedItems(asyncResp, *swInvPurpose);
1342}
1343
Patrick Williams504af5a2025-02-03 14:29:03 -05001344inline void getSoftwareVersion(
1345 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1346 const std::string& service, const std::string& path,
1347 const std::string& swId)
Willy Tuaf246602022-06-14 15:51:53 -07001348{
Ed Tanousdeae6a72024-11-11 21:58:57 -08001349 dbus::utility::getAllProperties(
1350 service, path, "xyz.openbmc_project.Software.Version",
Alexander Hansen78e69022025-08-13 16:20:29 +02001351 std::bind_front(getSoftwareVersionCallback, asyncResp, swId));
Willy Tuaf246602022-06-14 15:51:53 -07001352}
1353
Alexander Hansen609b4ab2025-08-13 16:17:26 +02001354inline void handleUpdateServiceFirmwareInventoryGetCallback(
1355 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1356 const std::shared_ptr<std::string>& swId,
1357 const boost::system::error_code& ec,
1358 const dbus::utility::MapperGetSubTreeResponse& subtree)
1359{
1360 BMCWEB_LOG_DEBUG("doGet callback...");
1361 if (ec)
1362 {
1363 messages::internalError(asyncResp->res);
1364 return;
1365 }
1366 // Ensure we find our input swId, otherwise return an error
1367 bool found = false;
1368 for (const std::pair<
1369 std::string,
1370 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1371 obj : subtree)
1372 {
1373 sdbusplus::message::object_path path(obj.first);
1374 std::string id = path.filename();
1375 if (id.empty())
1376 {
1377 BMCWEB_LOG_DEBUG("Failed to find software id in {}", obj.first);
1378 continue;
1379 }
1380 if (id != *swId)
1381 {
1382 continue;
1383 }
1384 if (obj.second.empty())
1385 {
1386 continue;
1387 }
1388 found = true;
1389 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1390 sw_util::getSwMinimumVersion(asyncResp, swId, obj.second[0].first);
1391 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first, *swId);
1392 }
1393 if (!found)
1394 {
1395 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
1396 messages::resourceMissingAtURI(
1397 asyncResp->res,
1398 boost::urls::format(
1399 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId));
1400 return;
1401 }
1402 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1403 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
1404 asyncResp->res.jsonValue["@odata.type"] =
1405 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1406 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1407 asyncResp->res.jsonValue["Status"]["HealthRollup"] = resource::Health::OK;
1408 asyncResp->res.jsonValue["Updateable"] = false;
1409 sw_util::getSwUpdatableStatus(asyncResp, swId);
1410}
1411
Ed Tanousf5139332024-04-03 13:25:04 -07001412inline void handleUpdateServiceFirmwareInventoryGet(
1413 App& app, const crow::Request& req,
1414 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1415 const std::string& param)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001416{
Ed Tanousf5139332024-04-03 13:25:04 -07001417 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1418 {
1419 return;
1420 }
1421 std::shared_ptr<std::string> swId = std::make_shared<std::string>(param);
1422
Ed Tanousf5139332024-04-03 13:25:04 -07001423 constexpr std::array<std::string_view, 1> interfaces = {
1424 "xyz.openbmc_project.Software.Version"};
1425 dbus::utility::getSubTree(
Abiola Asojof90af522025-03-18 16:29:31 +00001426 "/xyz/openbmc_project/software/", 0, interfaces,
Alexander Hansen609b4ab2025-08-13 16:17:26 +02001427 std::bind_front(handleUpdateServiceFirmwareInventoryGetCallback,
1428 asyncResp, swId));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001429}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001430
Ed Tanousf5139332024-04-03 13:25:04 -07001431inline void requestRoutesUpdateService(App& app)
1432{
Ed Tanous6a371402024-12-03 14:01:25 -08001433 if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE)
1434 {
1435 BMCWEB_ROUTE(
1436 app,
1437 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
1438 .privileges(redfish::privileges::postUpdateService)
1439 .methods(boost::beast::http::verb::post)(std::bind_front(
1440 handleUpdateServiceSimpleUpdateAction, std::ref(app)));
1441 }
Ed Tanousf5139332024-04-03 13:25:04 -07001442 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1443 .privileges(redfish::privileges::getSoftwareInventory)
1444 .methods(boost::beast::http::verb::get)(std::bind_front(
1445 handleUpdateServiceFirmwareInventoryGet, std::ref(app)));
1446
1447 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1448 .privileges(redfish::privileges::getUpdateService)
1449 .methods(boost::beast::http::verb::get)(
1450 std::bind_front(handleUpdateServiceGet, std::ref(app)));
1451
Ed Tanousf5139332024-04-03 13:25:04 -07001452 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
1453 .privileges(redfish::privileges::postUpdateService)
1454 .methods(boost::beast::http::verb::post)(
1455 std::bind_front(handleUpdateServicePost, std::ref(app)));
1456
rajeeranjan0c814aa2025-03-25 13:31:49 +05301457 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update-multipart/")
1458 .privileges(redfish::privileges::postUpdateService)
1459 .methods(boost::beast::http::verb::post)(std::bind_front(
1460 handleUpdateServiceMultipartUpdatePost, std::ref(app)));
1461
Ed Tanousf5139332024-04-03 13:25:04 -07001462 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
1463 .privileges(redfish::privileges::getSoftwareInventoryCollection)
1464 .methods(boost::beast::http::verb::get)(std::bind_front(
1465 handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app)));
1466}
1467
Ed Tanous1abe55e2018-09-05 08:30:59 -07001468} // namespace redfish