blob: 3d804d7416fc27f56e27b33f614b41985f0c5be0 [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"
9#include "dbus_utility.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070010#include "error_messages.hpp"
Ed Tanous757178a2024-04-03 14:32:38 -070011#include "generated/enums/update_service.hpp"
George Liu0ed80c82020-05-12 16:06:27 +080012#include "multipart_parser.hpp"
Ed Tanous2c6ffdb2023-06-28 11:28:38 -070013#include "ossl_random.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080014#include "query.hpp"
15#include "registries/privilege_registry.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080016#include "task.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070017#include "task_messages.hpp"
John Edward Broadbent08d81ad2022-05-17 20:00:23 -070018#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080019#include "utils/dbus_utils.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070020#include "utils/json_utils.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080021#include "utils/sw_utils.hpp"
22
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -070023#include <sys/mman.h>
24
George Liue99073f2022-12-09 11:06:16 +080025#include <boost/system/error_code.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070026#include <boost/url/format.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070027#include <sdbusplus/asio/property.hpp>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080028#include <sdbusplus/bus/match.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020029#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050030
George Liu2b731192023-01-11 16:27:13 +080031#include <array>
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -070032#include <cstddef>
George Liu0ed80c82020-05-12 16:06:27 +080033#include <filesystem>
Jagpal Singh Gillc71b6c92024-04-29 16:50:53 -070034#include <functional>
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -070035#include <iterator>
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -070036#include <memory>
Ed Tanous7cb59f62022-05-05 11:48:31 -070037#include <optional>
38#include <string>
George Liu2b731192023-01-11 16:27:13 +080039#include <string_view>
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -070040#include <unordered_map>
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -070041#include <vector>
George Liu2b731192023-01-11 16:27:13 +080042
Ed Tanous1abe55e2018-09-05 08:30:59 -070043namespace redfish
44{
Ed Tanous27826b52018-10-29 11:40:58 -070045
Andrew Geissler0e7de462019-03-04 19:11:54 -060046// Match signals added on software path
Ed Tanouscf9e4172022-12-21 09:30:16 -080047// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050048static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
Ed Tanouscf9e4172022-12-21 09:30:16 -080049// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050050static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060051// Only allow one update at a time
Ed Tanouscf9e4172022-12-21 09:30:16 -080052// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Andrew Geissler0e7de462019-03-04 19:11:54 -060053static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050054// Timer for software available
Ed Tanouscf9e4172022-12-21 09:30:16 -080055// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous271584a2019-07-09 16:24:22 -070056static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050057
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -070058struct MemoryFileDescriptor
59{
60 int fd = -1;
61
62 explicit MemoryFileDescriptor(const std::string& filename) :
63 fd(memfd_create(filename.c_str(), 0))
64 {}
65
66 MemoryFileDescriptor(const MemoryFileDescriptor&) = default;
67 MemoryFileDescriptor(MemoryFileDescriptor&& other) noexcept : fd(other.fd)
68 {
69 other.fd = -1;
70 }
71 MemoryFileDescriptor& operator=(const MemoryFileDescriptor&) = delete;
72 MemoryFileDescriptor& operator=(MemoryFileDescriptor&&) = default;
73
74 ~MemoryFileDescriptor()
75 {
76 if (fd != -1)
77 {
78 close(fd);
79 }
80 }
81
82 bool rewind() const
83 {
84 if (lseek(fd, 0, SEEK_SET) == -1)
85 {
86 BMCWEB_LOG_ERROR("Failed to seek to beginning of image memfd");
87 return false;
88 }
89 return true;
90 }
91};
92
Ed Tanousdf254f22024-04-01 13:25:46 -070093inline void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050094{
95 fwUpdateInProgress = false;
96 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070097 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050098}
Ed Tanousdf254f22024-04-01 13:25:46 -070099
100inline void activateImage(const std::string& objPath,
101 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500102{
Ed Tanous62598e32023-07-17 17:06:25 -0700103 BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
George Liu9ae226f2023-06-21 17:56:46 +0800104 sdbusplus::asio::setProperty(
105 *crow::connections::systemBus, service, objPath,
106 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
107 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
Ed Tanous8b242752023-06-27 17:17:13 -0700108 [](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400109 if (ec)
110 {
111 BMCWEB_LOG_DEBUG("error_code = {}", ec);
112 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
113 }
114 });
Andrew Geissler86adcd62019-04-18 10:58:05 -0500115}
Andrew Geissler0554c982019-04-23 14:40:12 -0500116
Jagpal Singh Gillc71b6c92024-04-29 16:50:53 -0700117inline bool handleCreateTask(const boost::system::error_code& ec2,
118 sdbusplus::message_t& msg,
119 const std::shared_ptr<task::TaskData>& taskData)
120{
121 if (ec2)
122 {
123 return task::completed;
124 }
125
126 std::string iface;
127 dbus::utility::DBusPropertiesMap values;
128
129 std::string index = std::to_string(taskData->index);
130 msg.read(iface, values);
131
132 if (iface == "xyz.openbmc_project.Software.Activation")
133 {
134 const std::string* state = nullptr;
135 for (const auto& property : values)
136 {
137 if (property.first == "Activation")
138 {
139 state = std::get_if<std::string>(&property.second);
140 if (state == nullptr)
141 {
142 taskData->messages.emplace_back(messages::internalError());
143 return task::completed;
144 }
145 }
146 }
147
148 if (state == nullptr)
149 {
150 return !task::completed;
151 }
152
153 if (state->ends_with("Invalid") || state->ends_with("Failed"))
154 {
155 taskData->state = "Exception";
156 taskData->status = "Warning";
157 taskData->messages.emplace_back(messages::taskAborted(index));
158 return task::completed;
159 }
160
161 if (state->ends_with("Staged"))
162 {
163 taskData->state = "Stopping";
164 taskData->messages.emplace_back(messages::taskPaused(index));
165
166 // its staged, set a long timer to
167 // allow them time to complete the
168 // update (probably cycle the
169 // system) if this expires then
170 // task will be canceled
171 taskData->extendTimer(std::chrono::hours(5));
172 return !task::completed;
173 }
174
175 if (state->ends_with("Active"))
176 {
177 taskData->messages.emplace_back(messages::taskCompletedOK(index));
178 taskData->state = "Completed";
179 return task::completed;
180 }
181 }
182 else if (iface == "xyz.openbmc_project.Software.ActivationProgress")
183 {
184 const uint8_t* progress = nullptr;
185 for (const auto& property : values)
186 {
187 if (property.first == "Progress")
188 {
189 progress = std::get_if<uint8_t>(&property.second);
190 if (progress == nullptr)
191 {
192 taskData->messages.emplace_back(messages::internalError());
193 return task::completed;
194 }
195 }
196 }
197
198 if (progress == nullptr)
199 {
200 return !task::completed;
201 }
202 taskData->percentComplete = *progress;
203 taskData->messages.emplace_back(
204 messages::taskProgressChanged(index, *progress));
205
206 // if we're getting status updates it's
207 // still alive, update timer
208 taskData->extendTimer(std::chrono::minutes(5));
209 }
210
211 // as firmware update often results in a
212 // reboot, the task may never "complete"
213 // unless it is an error
214
215 return !task::completed;
216}
217
218inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
219 task::Payload&& payload,
220 const sdbusplus::message::object_path& objPath)
221{
222 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
223 std::bind_front(handleCreateTask),
224 "type='signal',interface='org.freedesktop.DBus.Properties',"
225 "member='PropertiesChanged',path='" +
226 objPath.str + "'");
227 task->startTimer(std::chrono::minutes(5));
228 task->populateResp(asyncResp->res);
229 task->payload.emplace(std::move(payload));
230}
231
Andrew Geissler0554c982019-04-23 14:40:12 -0500232// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
233// then no asyncResp updates will occur
Ed Tanous4ff0f1f2024-09-04 17:27:37 -0700234inline void
zhanghch058d1b46d2021-04-01 11:18:24 +0800235 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -0500236 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500237{
Michael Shen80f79a42023-08-24 13:41:53 +0000238 dbus::utility::DBusInterfacesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500239
240 sdbusplus::message::object_path objPath;
241
242 m.read(objPath, interfacesProperties);
243
Ed Tanous62598e32023-07-17 17:06:25 -0700244 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
Ed Tanouse3eb3d62022-12-21 11:56:07 -0800245 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500246 {
Ed Tanous62598e32023-07-17 17:06:25 -0700247 BMCWEB_LOG_DEBUG("interface = {}", interface.first);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500248
249 if (interface.first == "xyz.openbmc_project.Software.Activation")
250 {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500251 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +0800252 constexpr std::array<std::string_view, 1> interfaces = {
253 "xyz.openbmc_project.Software.Activation"};
254 dbus::utility::getDbusObject(
255 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700256 [objPath, asyncResp, payload(std::move(payload))](
Ed Tanous8b242752023-06-27 17:17:13 -0700257 const boost::system::error_code& ec,
Ed Tanousa3e65892021-09-16 14:13:20 -0700258 const std::vector<
259 std::pair<std::string, std::vector<std::string>>>&
260 objInfo) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400261 if (ec)
Andrew Geissler0554c982019-04-23 14:40:12 -0500262 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400263 BMCWEB_LOG_DEBUG("error_code = {}", ec);
264 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
265 if (asyncResp)
266 {
267 messages::internalError(asyncResp->res);
268 }
269 cleanUp();
270 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700271 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400272 // Ensure we only got one service back
273 if (objInfo.size() != 1)
Ed Tanous002d39b2022-05-31 08:59:27 -0700274 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400275 BMCWEB_LOG_ERROR("Invalid Object Size {}",
276 objInfo.size());
277 if (asyncResp)
278 {
279 messages::internalError(asyncResp->res);
280 }
281 cleanUp();
282 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700283 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400284 // cancel timer only when
285 // xyz.openbmc_project.Software.Activation interface
286 // is added
287 fwAvailableTimer = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700288
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400289 activateImage(objPath.str, objInfo[0].first);
290 if (asyncResp)
291 {
292 createTask(asyncResp, std::move(payload), objPath);
293 }
294 fwUpdateInProgress = false;
295 });
Patrick Williams62bafc02022-09-08 17:35:35 -0500296
297 break;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500298 }
299 }
300}
301
Myung Bae8549b952023-08-16 15:18:19 -0400302inline void afterAvailbleTimerAsyncWait(
303 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
304 const boost::system::error_code& ec)
305{
306 cleanUp();
307 if (ec == boost::asio::error::operation_aborted)
308 {
309 // expected, we were canceled before the timer completed.
310 return;
311 }
312 BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
313 BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
314 if (ec)
315 {
316 BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
317 return;
318 }
319 if (asyncResp)
320 {
321 redfish::messages::internalError(asyncResp->res);
322 }
323}
324
325inline void
326 handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous48fb20b2024-11-17 11:51:13 -0800327 const std::string& url, const std::string& type)
Myung Bae8549b952023-08-16 15:18:19 -0400328{
Ed Tanousc87294a2024-11-16 22:17:12 -0800329 // NOLINTBEGIN(bugprone-branch-clone)
Myung Bae8549b952023-08-16 15:18:19 -0400330 if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
331 {
Ed Tanous48fb20b2024-11-17 11:51:13 -0800332 messages::missingOrMalformedPart(asyncResp->res);
Myung Bae8549b952023-08-16 15:18:19 -0400333 }
334 else if (type ==
335 "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
336 {
Ed Tanous48fb20b2024-11-17 11:51:13 -0800337 messages::missingOrMalformedPart(asyncResp->res);
Myung Bae8549b952023-08-16 15:18:19 -0400338 }
339 else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
340 {
Ed Tanous48fb20b2024-11-17 11:51:13 -0800341 messages::missingOrMalformedPart(asyncResp->res);
Myung Bae8549b952023-08-16 15:18:19 -0400342 }
343 else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
344 {
Ed Tanousc87294a2024-11-16 22:17:12 -0800345 messages::resourceAlreadyExists(asyncResp->res, "UpdateService",
346 "Version", "uploaded version");
Myung Bae8549b952023-08-16 15:18:19 -0400347 }
348 else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
349 {
Ed Tanous48fb20b2024-11-17 11:51:13 -0800350 messages::serviceTemporarilyUnavailable(asyncResp->res, url);
Myung Bae8549b952023-08-16 15:18:19 -0400351 }
Myung Bae4034a652023-08-17 08:47:35 -0400352 else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
Myung Bae8549b952023-08-16 15:18:19 -0400353 {
Ed Tanousc87294a2024-11-16 22:17:12 -0800354 messages::internalError(asyncResp->res);
Myung Bae4034a652023-08-17 08:47:35 -0400355 }
356 else if (type ==
357 "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
358 {
Ed Tanousc87294a2024-11-16 22:17:12 -0800359 messages::internalError(asyncResp->res);
Myung Bae4034a652023-08-17 08:47:35 -0400360 }
361 else if (type ==
362 "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
363 {
Ed Tanous48fb20b2024-11-17 11:51:13 -0800364 messages::missingOrMalformedPart(asyncResp->res);
Myung Bae4034a652023-08-17 08:47:35 -0400365 }
366 else if (type ==
367 "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
368 type == "xyz.openbmc_project.Software.Version.Error.HostFile")
369 {
370 BMCWEB_LOG_ERROR("Software Image Error type={}", type);
Ed Tanous48fb20b2024-11-17 11:51:13 -0800371 messages::internalError(asyncResp->res);
Myung Bae8549b952023-08-16 15:18:19 -0400372 }
Myung Bae4034a652023-08-17 08:47:35 -0400373 else
374 {
375 // Unrelated error types. Ignored
376 BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
377 return;
378 }
Ed Tanousc87294a2024-11-16 22:17:12 -0800379 // NOLINTEND(bugprone-branch-clone)
Myung Bae4034a652023-08-17 08:47:35 -0400380 // Clear the timer
381 fwAvailableTimer = nullptr;
Myung Bae8549b952023-08-16 15:18:19 -0400382}
383
384inline void
385 afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
386 const std::string& url, sdbusplus::message_t& m)
387{
Michael Shen80f79a42023-08-24 13:41:53 +0000388 dbus::utility::DBusInterfacesMap interfacesProperties;
Myung Bae8549b952023-08-16 15:18:19 -0400389 sdbusplus::message::object_path objPath;
390 m.read(objPath, interfacesProperties);
391 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
392 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
393 interface : interfacesProperties)
394 {
395 if (interface.first == "xyz.openbmc_project.Logging.Entry")
396 {
397 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
398 value : interface.second)
399 {
400 if (value.first != "Message")
401 {
402 continue;
403 }
404 const std::string* type =
405 std::get_if<std::string>(&value.second);
406 if (type == nullptr)
407 {
408 // if this was our message, timeout will cover it
409 return;
410 }
Myung Bae8549b952023-08-16 15:18:19 -0400411 handleUpdateErrorType(asyncResp, url, *type);
412 }
413 }
414 }
415}
416
Andrew Geissler0554c982019-04-23 14:40:12 -0500417// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
418// then no asyncResp updates will occur
Ed Tanousf5139332024-04-03 13:25:04 -0700419inline void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800420 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
421 const crow::Request& req, const std::string& url,
Gunnar Mills5d138942022-09-07 10:26:21 -0500422 int timeoutTimeSeconds = 25)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500423{
424 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800425 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500426 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500427 if (asyncResp)
428 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500429 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
430 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500431 return;
432 }
433
Ed Tanous8e8245d2024-04-11 22:21:38 -0700434 if (req.ioService == nullptr)
435 {
436 messages::internalError(asyncResp->res);
437 return;
438 }
439
Andrew Geissler0554c982019-04-23 14:40:12 -0500440 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700441 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500442
Ed Tanous271584a2019-07-09 16:24:22 -0700443 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500444
445 fwAvailableTimer->async_wait(
Myung Bae8549b952023-08-16 15:18:19 -0400446 std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
447
Ed Tanousa3e65892021-09-16 14:13:20 -0700448 task::Payload payload(req);
Patrick Williams59d494e2022-07-22 19:26:55 -0500449 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
Ed Tanous62598e32023-07-17 17:06:25 -0700450 BMCWEB_LOG_DEBUG("Match fired");
Ed Tanousa3e65892021-09-16 14:13:20 -0700451 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500452 };
453
454 fwUpdateInProgress = true;
455
Patrick Williams59d494e2022-07-22 19:26:55 -0500456 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Andrew Geissler86adcd62019-04-18 10:58:05 -0500457 *crow::connections::systemBus,
458 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
459 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
460 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700461
Patrick Williams59d494e2022-07-22 19:26:55 -0500462 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
James Feist4cde5d92020-06-11 10:39:55 -0700463 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800464 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
465 "member='InterfacesAdded',"
466 "path='/xyz/openbmc_project/logging'",
Myung Bae8549b952023-08-16 15:18:19 -0400467 std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500468}
Jennifer Lee729dae72018-04-24 15:59:34 -0700469
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400470inline std::optional<boost::urls::url> parseSimpleUpdateUrl(
471 std::string imageURI, std::optional<std::string> transferProtocol,
472 crow::Response& res)
Ed Tanousf86bcc82023-08-25 09:34:07 -0700473{
474 if (imageURI.find("://") == std::string::npos)
475 {
476 if (imageURI.starts_with("/"))
477 {
478 messages::actionParameterValueTypeError(
479 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
480 return std::nullopt;
481 }
482 if (!transferProtocol)
483 {
484 messages::actionParameterValueTypeError(
485 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
486 return std::nullopt;
487 }
Ed Tanous6a371402024-12-03 14:01:25 -0800488 // OpenBMC currently only supports HTTPS
489 if (*transferProtocol == "HTTPS")
Ed Tanouse5cf7772024-04-03 13:45:31 -0700490 {
491 imageURI = "https://" + imageURI;
492 }
Ed Tanous757178a2024-04-03 14:32:38 -0700493 else
Ed Tanousf86bcc82023-08-25 09:34:07 -0700494 {
495 messages::actionParameterNotSupported(res, "TransferProtocol",
496 *transferProtocol);
497 BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
498 *transferProtocol);
499 return std::nullopt;
500 }
Ed Tanousf86bcc82023-08-25 09:34:07 -0700501 }
502
503 boost::system::result<boost::urls::url> url =
504 boost::urls::parse_absolute_uri(imageURI);
505 if (!url)
506 {
507 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
508 "UpdateService.SimpleUpdate");
509
510 return std::nullopt;
511 }
512 url->normalize();
513
Ed Tanous757178a2024-04-03 14:32:38 -0700514 if (url->scheme() == "tftp")
515 {
516 if (url->encoded_path().size() < 2)
517 {
518 messages::actionParameterNotSupported(res, "ImageURI",
519 url->buffer());
520 return std::nullopt;
521 }
522 }
Ed Tanouse5cf7772024-04-03 13:45:31 -0700523 else if (url->scheme() == "https")
524 {
525 // Empty paths default to "/"
526 if (url->encoded_path().empty())
527 {
528 url->set_encoded_path("/");
529 }
530 }
Ed Tanous757178a2024-04-03 14:32:38 -0700531 else
Ed Tanousf86bcc82023-08-25 09:34:07 -0700532 {
533 messages::actionParameterNotSupported(res, "ImageURI", imageURI);
534 return std::nullopt;
535 }
Ed Tanous757178a2024-04-03 14:32:38 -0700536
537 if (url->encoded_path().empty())
Ed Tanousf86bcc82023-08-25 09:34:07 -0700538 {
Ed Tanous757178a2024-04-03 14:32:38 -0700539 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
540 "UpdateService.SimpleUpdate");
Ed Tanousf86bcc82023-08-25 09:34:07 -0700541 return std::nullopt;
542 }
Ed Tanous757178a2024-04-03 14:32:38 -0700543
544 return *url;
Ed Tanousf86bcc82023-08-25 09:34:07 -0700545}
546
Ed Tanouse5cf7772024-04-03 13:45:31 -0700547inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
548 const boost::urls::url_view_base& url)
549{
550 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
551 url.buffer());
552}
553
Ed Tanousf5139332024-04-03 13:25:04 -0700554inline void handleUpdateServiceSimpleUpdateAction(
555 crow::App& app, const crow::Request& req,
556 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Andrew Geissler0554c982019-04-23 14:40:12 -0500557{
Ed Tanousf5139332024-04-03 13:25:04 -0700558 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
559 {
560 return;
561 }
562
563 std::optional<std::string> transferProtocol;
564 std::string imageURI;
565
566 BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
567
568 // User can pass in both TransferProtocol and ImageURI parameters or
569 // they can pass in just the ImageURI with the transfer protocol
570 // embedded within it.
571 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
572 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
573
Myung Baeafc474a2024-10-09 00:53:29 -0700574 if (!json_util::readJsonAction( //
575 req, asyncResp->res, //
576 "ImageURI", imageURI, //
577 "TransferProtocol", transferProtocol //
578 ))
Ed Tanousf5139332024-04-03 13:25:04 -0700579 {
580 BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
581 return;
582 }
583
Ed Tanous757178a2024-04-03 14:32:38 -0700584 std::optional<boost::urls::url> url =
585 parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res);
586 if (!url)
Ed Tanousf5139332024-04-03 13:25:04 -0700587 {
588 return;
589 }
Jagpal Singh Gill4e338b22024-06-14 14:24:56 -0700590 if (url->scheme() == "https")
Ed Tanouse5cf7772024-04-03 13:45:31 -0700591 {
592 doHttpsUpdate(asyncResp, *url);
593 }
Ed Tanous757178a2024-04-03 14:32:38 -0700594 else
595 {
596 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
597 url->buffer());
598 return;
599 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700600
Ed Tanousf5139332024-04-03 13:25:04 -0700601 BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700602}
603
George Liu0ed80c82020-05-12 16:06:27 +0800604inline void uploadImageFile(crow::Response& res, std::string_view body)
605{
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700606 std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
607
Ed Tanous62598e32023-07-17 17:06:25 -0700608 BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
George Liu0ed80c82020-05-12 16:06:27 +0800609 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
610 std::ofstream::trunc);
611 // set the permission of the file to 640
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400612 std::filesystem::perms permission =
613 std::filesystem::perms::owner_read | std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800614 std::filesystem::permissions(filepath, permission);
615 out << body;
616
617 if (out.bad())
618 {
619 messages::internalError(res);
620 cleanUp();
621 }
622}
623
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700624// Convert the Request Apply Time to the D-Bus value
625inline bool convertApplyTime(crow::Response& res, const std::string& applyTime,
626 std::string& applyTimeNewVal)
627{
628 if (applyTime == "Immediate")
629 {
630 applyTimeNewVal =
Jagpal Singh Gill049079f2024-06-02 18:11:13 -0700631 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700632 }
633 else if (applyTime == "OnReset")
634 {
635 applyTimeNewVal =
Jagpal Singh Gill049079f2024-06-02 18:11:13 -0700636 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700637 }
638 else
639 {
640 BMCWEB_LOG_WARNING(
641 "ApplyTime value {} is not in the list of acceptable values",
642 applyTime);
643 messages::propertyValueNotInList(res, applyTime, "ApplyTime");
644 return false;
645 }
646 return true;
647}
648
George Liu0ed80c82020-05-12 16:06:27 +0800649inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
650 const std::string& applyTime)
651{
652 std::string applyTimeNewVal;
Jagpal Singh Gill049079f2024-06-02 18:11:13 -0700653 if (!convertApplyTime(asyncResp->res, applyTime, applyTimeNewVal))
George Liu0ed80c82020-05-12 16:06:27 +0800654 {
George Liu0ed80c82020-05-12 16:06:27 +0800655 return;
656 }
657
Ginu Georgee93abac2024-06-14 17:35:27 +0530658 setDbusProperty(asyncResp, "ApplyTime", "xyz.openbmc_project.Settings",
Ed Tanousd02aad32024-02-13 14:43:34 -0800659 sdbusplus::message::object_path(
660 "/xyz/openbmc_project/software/apply_time"),
661 "xyz.openbmc_project.Software.ApplyTime",
Ginu Georgee93abac2024-06-14 17:35:27 +0530662 "RequestedApplyTime", applyTimeNewVal);
George Liu0ed80c82020-05-12 16:06:27 +0800663}
664
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700665struct MultiPartUpdateParameters
George Liu0ed80c82020-05-12 16:06:27 +0800666{
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700667 std::optional<std::string> applyTime;
668 std::string uploadData;
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700669 std::vector<std::string> targets;
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700670};
671
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700672inline std::optional<std::string>
673 processUrl(boost::system::result<boost::urls::url_view>& url)
674{
675 if (!url)
676 {
677 return std::nullopt;
678 }
679 if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers",
680 BMCWEB_REDFISH_MANAGER_URI_NAME))
681 {
682 return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME));
683 }
684 if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
685 {
686 return std::nullopt;
687 }
688 std::string firmwareId;
689 if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService",
690 "FirmwareInventory",
691 std::ref(firmwareId)))
692 {
693 return std::nullopt;
694 }
695
696 return std::make_optional(firmwareId);
697}
698
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700699inline std::optional<MultiPartUpdateParameters>
700 extractMultipartUpdateParameters(
701 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
702 MultipartParser parser)
703{
704 MultiPartUpdateParameters multiRet;
705 for (FormPart& formpart : parser.mime_fields)
George Liu0ed80c82020-05-12 16:06:27 +0800706 {
707 boost::beast::http::fields::const_iterator it =
708 formpart.fields.find("Content-Disposition");
709 if (it == formpart.fields.end())
710 {
Ed Tanous62598e32023-07-17 17:06:25 -0700711 BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700712 return std::nullopt;
George Liu0ed80c82020-05-12 16:06:27 +0800713 }
Ed Tanous62598e32023-07-17 17:06:25 -0700714 BMCWEB_LOG_INFO("Parsing value {}", it->value());
George Liu0ed80c82020-05-12 16:06:27 +0800715
716 // The construction parameters of param_list must start with `;`
717 size_t index = it->value().find(';');
718 if (index == std::string::npos)
719 {
720 continue;
721 }
722
Patrick Williams89492a12023-05-10 07:51:34 -0500723 for (const auto& param :
George Liu0ed80c82020-05-12 16:06:27 +0800724 boost::beast::http::param_list{it->value().substr(index)})
725 {
726 if (param.first != "name" || param.second.empty())
727 {
728 continue;
729 }
730
731 if (param.second == "UpdateParameters")
732 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700733 std::vector<std::string> tempTargets;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400734 nlohmann::json content =
735 nlohmann::json::parse(formpart.content, nullptr, false);
Ed Tanousac1e1242024-07-10 22:10:14 -0700736 if (content.is_discarded())
737 {
738 return std::nullopt;
739 }
Ed Tanous7cb59f62022-05-05 11:48:31 -0700740 nlohmann::json::object_t* obj =
741 content.get_ptr<nlohmann::json::object_t*>();
742 if (obj == nullptr)
743 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700744 messages::propertyValueTypeError(
745 asyncResp->res, formpart.content, "UpdateParameters");
746 return std::nullopt;
Ed Tanous7cb59f62022-05-05 11:48:31 -0700747 }
748
Myung Baeafc474a2024-10-09 00:53:29 -0700749 if (!json_util::readJsonObject( //
750 *obj, asyncResp->res, //
751 "@Redfish.OperationApplyTime", multiRet.applyTime, //
752 "Targets", tempTargets //
753 ))
George Liu0ed80c82020-05-12 16:06:27 +0800754 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700755 return std::nullopt;
George Liu0ed80c82020-05-12 16:06:27 +0800756 }
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700757
758 for (size_t urlIndex = 0; urlIndex < tempTargets.size();
759 urlIndex++)
George Liu0ed80c82020-05-12 16:06:27 +0800760 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700761 const std::string& target = tempTargets[urlIndex];
762 boost::system::result<boost::urls::url_view> url =
763 boost::urls::parse_origin_form(target);
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700764 auto res = processUrl(url);
765 if (!res.has_value())
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700766 {
767 messages::propertyValueFormatError(
768 asyncResp->res, target,
769 std::format("Targets/{}", urlIndex));
770 return std::nullopt;
771 }
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700772 multiRet.targets.emplace_back(res.value());
George Liu0ed80c82020-05-12 16:06:27 +0800773 }
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700774 if (multiRet.targets.size() != 1)
George Liu0ed80c82020-05-12 16:06:27 +0800775 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700776 messages::propertyValueFormatError(
777 asyncResp->res, multiRet.targets, "Targets");
778 return std::nullopt;
George Liu0ed80c82020-05-12 16:06:27 +0800779 }
George Liu0ed80c82020-05-12 16:06:27 +0800780 }
781 else if (param.second == "UpdateFile")
782 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700783 multiRet.uploadData = std::move(formpart.content);
George Liu0ed80c82020-05-12 16:06:27 +0800784 }
785 }
786 }
787
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700788 if (multiRet.uploadData.empty())
George Liu0ed80c82020-05-12 16:06:27 +0800789 {
Ed Tanous62598e32023-07-17 17:06:25 -0700790 BMCWEB_LOG_ERROR("Upload data is NULL");
George Liu0ed80c82020-05-12 16:06:27 +0800791 messages::propertyMissing(asyncResp->res, "UpdateFile");
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700792 return std::nullopt;
793 }
794 if (multiRet.targets.empty())
795 {
796 messages::propertyMissing(asyncResp->res, "Targets");
797 return std::nullopt;
798 }
799 return multiRet;
800}
801
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400802inline void handleStartUpdate(
803 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
804 const std::string& objectPath, const boost::system::error_code& ec,
805 const sdbusplus::message::object_path& retPath)
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700806{
807 if (ec)
808 {
809 BMCWEB_LOG_ERROR("error_code = {}", ec);
810 BMCWEB_LOG_ERROR("error msg = {}", ec.message());
811 messages::internalError(asyncResp->res);
812 return;
813 }
814
Jagpal Singh Gill587090c2024-08-12 00:24:16 -0700815 BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}",
816 objectPath, retPath.str);
817 createTask(asyncResp, std::move(payload), retPath);
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700818}
819
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400820inline void startUpdate(
821 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
822 const MemoryFileDescriptor& memfd, const std::string& applyTime,
823 const std::string& objectPath, const std::string& serviceName)
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700824{
825 crow::connections::systemBus->async_method_call(
826 [asyncResp, payload = std::move(payload),
827 objectPath](const boost::system::error_code& ec1,
828 const sdbusplus::message::object_path& retPath) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400829 handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1,
830 retPath);
831 },
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700832 serviceName, objectPath, "xyz.openbmc_project.Software.Update",
833 "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime);
834}
835
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700836inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
837 task::Payload payload, const MemoryFileDescriptor& memfd,
838 const std::string& applyTime, const std::string& target,
839 const boost::system::error_code& ec,
840 const dbus::utility::MapperGetSubTreeResponse& subtree)
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700841{
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700842 using SwInfoMap = std::unordered_map<
843 std::string, std::pair<sdbusplus::message::object_path, std::string>>;
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700844 SwInfoMap swInfoMap;
845
846 if (ec)
847 {
848 BMCWEB_LOG_ERROR("error_code = {}", ec);
849 BMCWEB_LOG_ERROR("error msg = {}", ec.message());
850 messages::internalError(asyncResp->res);
851 return;
852 }
853 BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size());
854
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700855 for (const auto& entry : subtree)
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700856 {
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700857 sdbusplus::message::object_path path(entry.first);
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700858 std::string swId = path.filename();
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700859 swInfoMap.emplace(swId, make_pair(path, entry.second[0].first));
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700860 }
861
862 auto swEntry = swInfoMap.find(target);
863 if (swEntry == swInfoMap.end())
864 {
865 BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target);
866 messages::propertyValueFormatError(asyncResp->res, target, "Targets");
867 return;
868 }
869
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700870 BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}",
871 swEntry->second.first.str, swEntry->second.second);
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700872
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700873 startUpdate(asyncResp, std::move(payload), memfd, applyTime,
874 swEntry->second.first.str, swEntry->second.second);
875}
876
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400877inline void handleBMCUpdate(
878 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
879 const MemoryFileDescriptor& memfd, const std::string& applyTime,
880 const boost::system::error_code& ec,
881 const dbus::utility::MapperEndPoints& functionalSoftware)
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700882{
883 if (ec)
884 {
885 BMCWEB_LOG_ERROR("error_code = {}", ec);
886 BMCWEB_LOG_ERROR("error msg = {}", ec.message());
887 messages::internalError(asyncResp->res);
888 return;
889 }
890 if (functionalSoftware.size() != 1)
891 {
892 BMCWEB_LOG_ERROR("Found {} functional software endpoints",
893 functionalSoftware.size());
894 messages::internalError(asyncResp->res);
895 return;
896 }
897
898 startUpdate(asyncResp, std::move(payload), memfd, applyTime,
899 functionalSoftware[0], "xyz.openbmc_project.Software.Manager");
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700900}
901
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400902inline void processUpdateRequest(
903 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
904 task::Payload&& payload, std::string_view body,
905 const std::string& applyTime, std::vector<std::string>& targets)
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700906{
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700907 MemoryFileDescriptor memfd("update-image");
908 if (memfd.fd == -1)
909 {
910 BMCWEB_LOG_ERROR("Failed to create image memfd");
911 messages::internalError(asyncResp->res);
912 return;
913 }
914 if (write(memfd.fd, body.data(), body.length()) !=
915 static_cast<ssize_t>(body.length()))
916 {
917 BMCWEB_LOG_ERROR("Failed to write to image memfd");
918 messages::internalError(asyncResp->res);
919 return;
920 }
921 if (!memfd.rewind())
922 {
923 messages::internalError(asyncResp->res);
924 return;
925 }
926
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700927 if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME)
928 {
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700929 dbus::utility::getAssociationEndPoints(
Jagpal Singh Gill89449bb2024-08-12 16:17:58 -0700930 "/xyz/openbmc_project/software/bmc/updateable",
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700931 [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
932 applyTime](
933 const boost::system::error_code& ec,
934 const dbus::utility::MapperEndPoints& objectPaths) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400935 handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime,
936 ec, objectPaths);
937 });
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700938 }
939 else
940 {
941 constexpr std::array<std::string_view, 1> interfaces = {
942 "xyz.openbmc_project.Software.Version"};
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700943 dbus::utility::getSubTree(
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700944 "/xyz/openbmc_project/software", 1, interfaces,
945 [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
Jagpal Singh Gill08f61d52024-07-17 15:17:22 -0700946 applyTime, targets](const boost::system::error_code& ec,
947 const dbus::utility::MapperGetSubTreeResponse&
948 subtree) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400949 getSwInfo(asyncResp, std::move(payload), memfd, applyTime,
950 targets[0], ec, subtree);
951 });
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700952 }
953}
954
955inline void
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700956 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
957 const crow::Request& req, MultipartParser&& parser)
958{
959 std::optional<MultiPartUpdateParameters> multipart =
960 extractMultipartUpdateParameters(asyncResp, std::move(parser));
961 if (!multipart)
962 {
George Liu0ed80c82020-05-12 16:06:27 +0800963 return;
964 }
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700965 if (!multipart->applyTime)
George Liu0ed80c82020-05-12 16:06:27 +0800966 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700967 multipart->applyTime = "OnReset";
George Liu0ed80c82020-05-12 16:06:27 +0800968 }
969
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700970 if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
971 {
Jagpal Singh Gill9dae4de2024-06-02 23:43:56 -0700972 std::string applyTimeNewVal;
973 if (!convertApplyTime(asyncResp->res, *multipart->applyTime,
974 applyTimeNewVal))
975 {
976 return;
977 }
978 task::Payload payload(req);
979
980 processUpdateRequest(asyncResp, std::move(payload),
981 multipart->uploadData, applyTimeNewVal,
982 multipart->targets);
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700983 }
984 else
985 {
986 setApplyTime(asyncResp, *multipart->applyTime);
George Liu0ed80c82020-05-12 16:06:27 +0800987
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700988 // Setup callback for when new software detected
989 monitorForSoftwareAvailable(asyncResp, req,
990 "/redfish/v1/UpdateService");
Ed Tanous6b54e4e2024-04-10 08:58:48 -0700991
Jagpal Singh Gillde0c9602024-04-29 17:30:21 -0700992 uploadImageFile(asyncResp->res, multipart->uploadData);
993 }
George Liu0ed80c82020-05-12 16:06:27 +0800994}
995
Jagpal Singh Gill9dae4de2024-06-02 23:43:56 -0700996inline void doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
997 const crow::Request& req)
998{
999 if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
1000 {
1001 task::Payload payload(req);
1002 // HTTP push only supports BMC updates (with ApplyTime as immediate) for
1003 // backwards compatibility. Specific component updates will be handled
1004 // through Multipart form HTTP push.
1005 std::vector<std::string> targets;
1006 targets.emplace_back(BMCWEB_REDFISH_MANAGER_URI_NAME);
1007
1008 processUpdateRequest(
1009 asyncResp, std::move(payload), req.body(),
1010 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate",
1011 targets);
1012 }
1013 else
1014 {
1015 // Setup callback for when new software detected
1016 monitorForSoftwareAvailable(asyncResp, req,
1017 "/redfish/v1/UpdateService");
1018
1019 uploadImageFile(asyncResp->res, req.body());
1020 }
1021}
1022
Ed Tanousc2051d12022-05-11 12:21:55 -07001023inline void
1024 handleUpdateServicePost(App& app, const crow::Request& req,
1025 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1026{
Carson Labrado3ba00072022-06-06 19:40:56 +00001027 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -07001028 {
1029 return;
1030 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001031 std::string_view contentType = req.getHeaderValue("Content-Type");
Ed Tanousc2051d12022-05-11 12:21:55 -07001032
Ed Tanous62598e32023-07-17 17:06:25 -07001033 BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
Ed Tanousc2051d12022-05-11 12:21:55 -07001034
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001035 // Make sure that content type is application/octet-stream or
1036 // multipart/form-data
Ed Tanous18f8f602023-07-18 10:07:23 -07001037 if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
George Liu0ed80c82020-05-12 16:06:27 +08001038 {
Jagpal Singh Gill9dae4de2024-06-02 23:43:56 -07001039 doHTTPUpdate(asyncResp, req);
George Liu0ed80c82020-05-12 16:06:27 +08001040 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001041 else if (contentType.starts_with("multipart/form-data"))
George Liu0ed80c82020-05-12 16:06:27 +08001042 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001043 MultipartParser parser;
1044
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001045 ParserError ec = parser.parse(req);
1046 if (ec != ParserError::PARSER_SUCCESS)
1047 {
1048 // handle error
Ed Tanous62598e32023-07-17 17:06:25 -07001049 BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
1050 static_cast<int>(ec));
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001051 messages::internalError(asyncResp->res);
1052 return;
1053 }
Ed Tanous6b54e4e2024-04-10 08:58:48 -07001054
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -07001055 updateMultipartContext(asyncResp, req, std::move(parser));
George Liu0ed80c82020-05-12 16:06:27 +08001056 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001057 else
1058 {
Ed Tanous62598e32023-07-17 17:06:25 -07001059 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
Ninad Palsuleb33a4322023-06-09 09:19:18 -05001060 asyncResp->res.result(boost::beast::http::status::bad_request);
1061 }
Ed Tanousc2051d12022-05-11 12:21:55 -07001062}
1063
Ed Tanousf5139332024-04-03 13:25:04 -07001064inline void
1065 handleUpdateServiceGet(App& app, const crow::Request& req,
1066 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001067{
Ed Tanousf5139332024-04-03 13:25:04 -07001068 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1069 {
1070 return;
1071 }
1072 asyncResp->res.jsonValue["@odata.type"] =
1073 "#UpdateService.v1_11_1.UpdateService";
1074 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
1075 asyncResp->res.jsonValue["Id"] = "UpdateService";
1076 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
1077 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -07001078
Ed Tanousf5139332024-04-03 13:25:04 -07001079 asyncResp->res.jsonValue["HttpPushUri"] =
1080 "/redfish/v1/UpdateService/update";
1081 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
1082 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -07001083
Ed Tanousf5139332024-04-03 13:25:04 -07001084 // UpdateService cannot be disabled
1085 asyncResp->res.jsonValue["ServiceEnabled"] = true;
1086 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
1087 "/redfish/v1/UpdateService/FirmwareInventory";
1088 // Get the MaxImageSizeBytes
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001089 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
1090 BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +05301091
Ed Tanous6a371402024-12-03 14:01:25 -08001092 if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE)
Ed Tanous25b54db2024-04-17 15:40:31 -07001093 {
Ed Tanous6a371402024-12-03 14:01:25 -08001094 // Update Actions object.
1095 nlohmann::json& updateSvcSimpleUpdate =
1096 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
1097 updateSvcSimpleUpdate["target"] =
1098 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
Ed Tanous757178a2024-04-03 14:32:38 -07001099
Ed Tanous6a371402024-12-03 14:01:25 -08001100 nlohmann::json::array_t allowed;
1101 allowed.emplace_back(update_service::TransferProtocolType::HTTPS);
1102 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
1103 std::move(allowed);
1104 }
Ed Tanous757178a2024-04-03 14:32:38 -07001105
Ed Tanous539d8c62024-06-19 14:38:27 -07001106 asyncResp->res
1107 .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] =
1108 update_service::ApplyTime::Immediate;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001109}
Ed Tanousf5139332024-04-03 13:25:04 -07001110
1111inline void handleUpdateServiceFirmwareInventoryCollectionGet(
1112 App& app, const crow::Request& req,
1113 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1114{
1115 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1116 {
1117 return;
1118 }
1119 asyncResp->res.jsonValue["@odata.type"] =
1120 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
1121 asyncResp->res.jsonValue["@odata.id"] =
1122 "/redfish/v1/UpdateService/FirmwareInventory";
1123 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
1124 const std::array<const std::string_view, 1> iface = {
1125 "xyz.openbmc_project.Software.Version"};
1126
1127 redfish::collection_util::getCollectionMembers(
1128 asyncResp,
1129 boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface,
1130 "/xyz/openbmc_project/software");
1131}
1132
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001133/* Fill related item links (i.e. bmc, bios) in for inventory */
Ed Tanousf5139332024-04-03 13:25:04 -07001134inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1135 const std::string& purpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001136{
Willy Tueee00132022-06-14 14:53:17 -07001137 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001138 {
Ed Tanousac106bf2023-06-07 09:24:59 -07001139 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -07001140 nlohmann::json::object_t item;
Ed Tanous253f11b2024-05-16 09:38:31 -07001141 item["@odata.id"] = boost::urls::format(
1142 "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME);
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001143 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -07001144 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1145 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001146 }
Willy Tueee00132022-06-14 14:53:17 -07001147 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001148 {
Ed Tanousac106bf2023-06-07 09:24:59 -07001149 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -07001150 nlohmann::json::object_t item;
Ed Tanous253f11b2024-05-16 09:38:31 -07001151 item["@odata.id"] = std::format("/redfish/v1/Systems/{}/Bios",
1152 BMCWEB_REDFISH_SYSTEM_URI_NAME);
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001153 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -07001154 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1155 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001156 }
1157 else
1158 {
Carson Labradobf2dded2023-08-10 00:37:06 +00001159 BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001160 }
1161}
1162
Willy Tuaf246602022-06-14 15:51:53 -07001163inline void
1164 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1165 const std::string& service, const std::string& path,
1166 const std::string& swId)
1167{
Ed Tanousdeae6a72024-11-11 21:58:57 -08001168 dbus::utility::getAllProperties(
1169 service, path, "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -07001170 [asyncResp,
Ed Tanous8b242752023-06-27 17:17:13 -07001171 swId](const boost::system::error_code& ec,
Willy Tuaf246602022-06-14 15:51:53 -07001172 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001173 if (ec)
1174 {
1175 messages::internalError(asyncResp->res);
1176 return;
1177 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001178
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001179 const std::string* swInvPurpose = nullptr;
1180 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001181
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001182 const bool success = sdbusplus::unpackPropertiesNoThrow(
1183 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
1184 swInvPurpose, "Version", version);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001185
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001186 if (!success)
1187 {
1188 messages::internalError(asyncResp->res);
1189 return;
1190 }
Willy Tuaf246602022-06-14 15:51:53 -07001191
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001192 if (swInvPurpose == nullptr)
1193 {
1194 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
1195 messages::internalError(asyncResp->res);
1196 return;
1197 }
Willy Tuaf246602022-06-14 15:51:53 -07001198
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001199 BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
Willy Tuaf246602022-06-14 15:51:53 -07001200
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001201 if (version == nullptr)
1202 {
1203 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
Willy Tuaf246602022-06-14 15:51:53 -07001204
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001205 messages::internalError(asyncResp->res);
Willy Tuaf246602022-06-14 15:51:53 -07001206
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001207 return;
1208 }
1209 asyncResp->res.jsonValue["Version"] = *version;
1210 asyncResp->res.jsonValue["Id"] = swId;
Willy Tuaf246602022-06-14 15:51:53 -07001211
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001212 // swInvPurpose is of format:
1213 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
1214 // Translate this to "ABC image"
1215 size_t endDesc = swInvPurpose->rfind('.');
1216 if (endDesc == std::string::npos)
1217 {
1218 messages::internalError(asyncResp->res);
1219 return;
1220 }
1221 endDesc++;
1222 if (endDesc >= swInvPurpose->size())
1223 {
1224 messages::internalError(asyncResp->res);
1225 return;
1226 }
Willy Tuaf246602022-06-14 15:51:53 -07001227
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001228 std::string formatDesc = swInvPurpose->substr(endDesc);
1229 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1230 getRelatedItems(asyncResp, *swInvPurpose);
1231 });
Willy Tuaf246602022-06-14 15:51:53 -07001232}
1233
Ed Tanousf5139332024-04-03 13:25:04 -07001234inline void handleUpdateServiceFirmwareInventoryGet(
1235 App& app, const crow::Request& req,
1236 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1237 const std::string& param)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001238{
Ed Tanousf5139332024-04-03 13:25:04 -07001239 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1240 {
1241 return;
1242 }
1243 std::shared_ptr<std::string> swId = std::make_shared<std::string>(param);
1244
1245 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1246 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
1247
1248 constexpr std::array<std::string_view, 1> interfaces = {
1249 "xyz.openbmc_project.Software.Version"};
1250 dbus::utility::getSubTree(
1251 "/", 0, interfaces,
1252 [asyncResp,
1253 swId](const boost::system::error_code& ec,
1254 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001255 BMCWEB_LOG_DEBUG("doGet callback...");
1256 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001257 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001258 messages::internalError(asyncResp->res);
1259 return;
Ed Tanous45ca1b82022-03-25 13:07:27 -07001260 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001261
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001262 // Ensure we find our input swId, otherwise return an error
1263 bool found = false;
1264 for (const std::pair<std::string,
1265 std::vector<std::pair<
1266 std::string, std::vector<std::string>>>>&
1267 obj : subtree)
Ed Tanous002d39b2022-05-31 08:59:27 -07001268 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001269 if (!obj.first.ends_with(*swId))
1270 {
1271 continue;
1272 }
1273
1274 if (obj.second.empty())
1275 {
1276 continue;
1277 }
1278
1279 found = true;
1280 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1281 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1282 *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001283 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001284 if (!found)
1285 {
1286 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
1287 messages::resourceMissingAtURI(
1288 asyncResp->res,
1289 boost::urls::format(
1290 "/redfish/v1/UpdateService/FirmwareInventory/{}",
1291 *swId));
1292 return;
1293 }
1294 asyncResp->res.jsonValue["@odata.type"] =
1295 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1296 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1297 asyncResp->res.jsonValue["Status"]["HealthRollup"] =
1298 resource::Health::OK;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001299
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001300 asyncResp->res.jsonValue["Updateable"] = false;
1301 sw_util::getSwUpdatableStatus(asyncResp, swId);
1302 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001303}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001304
Ed Tanousf5139332024-04-03 13:25:04 -07001305inline void requestRoutesUpdateService(App& app)
1306{
Ed Tanous6a371402024-12-03 14:01:25 -08001307 if constexpr (BMCWEB_REDFISH_ALLOW_SIMPLE_UPDATE)
1308 {
1309 BMCWEB_ROUTE(
1310 app,
1311 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
1312 .privileges(redfish::privileges::postUpdateService)
1313 .methods(boost::beast::http::verb::post)(std::bind_front(
1314 handleUpdateServiceSimpleUpdateAction, std::ref(app)));
1315 }
Ed Tanousf5139332024-04-03 13:25:04 -07001316 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1317 .privileges(redfish::privileges::getSoftwareInventory)
1318 .methods(boost::beast::http::verb::get)(std::bind_front(
1319 handleUpdateServiceFirmwareInventoryGet, std::ref(app)));
1320
1321 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1322 .privileges(redfish::privileges::getUpdateService)
1323 .methods(boost::beast::http::verb::get)(
1324 std::bind_front(handleUpdateServiceGet, std::ref(app)));
1325
Ed Tanousf5139332024-04-03 13:25:04 -07001326 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
1327 .privileges(redfish::privileges::postUpdateService)
1328 .methods(boost::beast::http::verb::post)(
1329 std::bind_front(handleUpdateServicePost, std::ref(app)));
1330
1331 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
1332 .privileges(redfish::privileges::getSoftwareInventoryCollection)
1333 .methods(boost::beast::http::verb::get)(std::bind_front(
1334 handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app)));
1335}
1336
Ed Tanous1abe55e2018-09-05 08:30:59 -07001337} // namespace redfish