blob: 81534f94dea82c76d82c80475b734c8ed4f4103f [file] [log] [blame]
Jennifer Lee729dae72018-04-24 15:59:34 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
Tejas Patild61e5192021-06-04 15:49:35 +053018#include "bmcweb_config.h"
19
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080020#include "app.hpp"
21#include "dbus_utility.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070022#include "error_messages.hpp"
Ed Tanous757178a2024-04-03 14:32:38 -070023#include "generated/enums/update_service.hpp"
George Liu0ed80c82020-05-12 16:06:27 +080024#include "multipart_parser.hpp"
Ed Tanous2c6ffdb2023-06-28 11:28:38 -070025#include "ossl_random.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080026#include "query.hpp"
27#include "registries/privilege_registry.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080028#include "task.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070029#include "task_messages.hpp"
John Edward Broadbent08d81ad2022-05-17 20:00:23 -070030#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080031#include "utils/dbus_utils.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070032#include "utils/json_utils.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080033#include "utils/sw_utils.hpp"
34
George Liue99073f2022-12-09 11:06:16 +080035#include <boost/system/error_code.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070036#include <boost/url/format.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070037#include <sdbusplus/asio/property.hpp>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080038#include <sdbusplus/bus/match.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020039#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050040
George Liu2b731192023-01-11 16:27:13 +080041#include <array>
George Liu0ed80c82020-05-12 16:06:27 +080042#include <filesystem>
Jagpal Singh Gillc71b6c92024-04-29 16:50:53 -070043#include <functional>
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -070044#include <memory>
Ed Tanous7cb59f62022-05-05 11:48:31 -070045#include <optional>
46#include <string>
George Liu2b731192023-01-11 16:27:13 +080047#include <string_view>
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -070048#include <vector>
George Liu2b731192023-01-11 16:27:13 +080049
Ed Tanous1abe55e2018-09-05 08:30:59 -070050namespace redfish
51{
Ed Tanous27826b52018-10-29 11:40:58 -070052
Andrew Geissler0e7de462019-03-04 19:11:54 -060053// Match signals added on software path
Ed Tanouscf9e4172022-12-21 09:30:16 -080054// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050055static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
Ed Tanouscf9e4172022-12-21 09:30:16 -080056// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050057static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060058// Only allow one update at a time
Ed Tanouscf9e4172022-12-21 09:30:16 -080059// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Andrew Geissler0e7de462019-03-04 19:11:54 -060060static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050061// Timer for software available
Ed Tanouscf9e4172022-12-21 09:30:16 -080062// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous271584a2019-07-09 16:24:22 -070063static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050064
Ed Tanousdf254f22024-04-01 13:25:46 -070065inline void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050066{
67 fwUpdateInProgress = false;
68 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070069 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050070}
Ed Tanousdf254f22024-04-01 13:25:46 -070071
72inline void activateImage(const std::string& objPath,
73 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050074{
Ed Tanous62598e32023-07-17 17:06:25 -070075 BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
George Liu9ae226f2023-06-21 17:56:46 +080076 sdbusplus::asio::setProperty(
77 *crow::connections::systemBus, service, objPath,
78 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
79 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
Ed Tanous8b242752023-06-27 17:17:13 -070080 [](const boost::system::error_code& ec) {
81 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -070082 {
Ed Tanous62598e32023-07-17 17:06:25 -070083 BMCWEB_LOG_DEBUG("error_code = {}", ec);
84 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -070085 }
Patrick Williams5a39f772023-10-20 11:20:21 -050086 });
Andrew Geissler86adcd62019-04-18 10:58:05 -050087}
Andrew Geissler0554c982019-04-23 14:40:12 -050088
Jagpal Singh Gillc71b6c92024-04-29 16:50:53 -070089inline bool handleCreateTask(const boost::system::error_code& ec2,
90 sdbusplus::message_t& msg,
91 const std::shared_ptr<task::TaskData>& taskData)
92{
93 if (ec2)
94 {
95 return task::completed;
96 }
97
98 std::string iface;
99 dbus::utility::DBusPropertiesMap values;
100
101 std::string index = std::to_string(taskData->index);
102 msg.read(iface, values);
103
104 if (iface == "xyz.openbmc_project.Software.Activation")
105 {
106 const std::string* state = nullptr;
107 for (const auto& property : values)
108 {
109 if (property.first == "Activation")
110 {
111 state = std::get_if<std::string>(&property.second);
112 if (state == nullptr)
113 {
114 taskData->messages.emplace_back(messages::internalError());
115 return task::completed;
116 }
117 }
118 }
119
120 if (state == nullptr)
121 {
122 return !task::completed;
123 }
124
125 if (state->ends_with("Invalid") || state->ends_with("Failed"))
126 {
127 taskData->state = "Exception";
128 taskData->status = "Warning";
129 taskData->messages.emplace_back(messages::taskAborted(index));
130 return task::completed;
131 }
132
133 if (state->ends_with("Staged"))
134 {
135 taskData->state = "Stopping";
136 taskData->messages.emplace_back(messages::taskPaused(index));
137
138 // its staged, set a long timer to
139 // allow them time to complete the
140 // update (probably cycle the
141 // system) if this expires then
142 // task will be canceled
143 taskData->extendTimer(std::chrono::hours(5));
144 return !task::completed;
145 }
146
147 if (state->ends_with("Active"))
148 {
149 taskData->messages.emplace_back(messages::taskCompletedOK(index));
150 taskData->state = "Completed";
151 return task::completed;
152 }
153 }
154 else if (iface == "xyz.openbmc_project.Software.ActivationProgress")
155 {
156 const uint8_t* progress = nullptr;
157 for (const auto& property : values)
158 {
159 if (property.first == "Progress")
160 {
161 progress = std::get_if<uint8_t>(&property.second);
162 if (progress == nullptr)
163 {
164 taskData->messages.emplace_back(messages::internalError());
165 return task::completed;
166 }
167 }
168 }
169
170 if (progress == nullptr)
171 {
172 return !task::completed;
173 }
174 taskData->percentComplete = *progress;
175 taskData->messages.emplace_back(
176 messages::taskProgressChanged(index, *progress));
177
178 // if we're getting status updates it's
179 // still alive, update timer
180 taskData->extendTimer(std::chrono::minutes(5));
181 }
182
183 // as firmware update often results in a
184 // reboot, the task may never "complete"
185 // unless it is an error
186
187 return !task::completed;
188}
189
190inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
191 task::Payload&& payload,
192 const sdbusplus::message::object_path& objPath)
193{
194 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
195 std::bind_front(handleCreateTask),
196 "type='signal',interface='org.freedesktop.DBus.Properties',"
197 "member='PropertiesChanged',path='" +
198 objPath.str + "'");
199 task->startTimer(std::chrono::minutes(5));
200 task->populateResp(asyncResp->res);
201 task->payload.emplace(std::move(payload));
202}
203
Andrew Geissler0554c982019-04-23 14:40:12 -0500204// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
205// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +0800206static void
207 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -0500208 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500209{
Michael Shen80f79a42023-08-24 13:41:53 +0000210 dbus::utility::DBusInterfacesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500211
212 sdbusplus::message::object_path objPath;
213
214 m.read(objPath, interfacesProperties);
215
Ed Tanous62598e32023-07-17 17:06:25 -0700216 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
Ed Tanouse3eb3d62022-12-21 11:56:07 -0800217 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500218 {
Ed Tanous62598e32023-07-17 17:06:25 -0700219 BMCWEB_LOG_DEBUG("interface = {}", interface.first);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500220
221 if (interface.first == "xyz.openbmc_project.Software.Activation")
222 {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500223 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +0800224 constexpr std::array<std::string_view, 1> interfaces = {
225 "xyz.openbmc_project.Software.Activation"};
226 dbus::utility::getDbusObject(
227 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700228 [objPath, asyncResp, payload(std::move(payload))](
Ed Tanous8b242752023-06-27 17:17:13 -0700229 const boost::system::error_code& ec,
Ed Tanousa3e65892021-09-16 14:13:20 -0700230 const std::vector<
231 std::pair<std::string, std::vector<std::string>>>&
232 objInfo) mutable {
Ed Tanous8b242752023-06-27 17:17:13 -0700233 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700234 {
Ed Tanous62598e32023-07-17 17:06:25 -0700235 BMCWEB_LOG_DEBUG("error_code = {}", ec);
236 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Andrew Geissler0554c982019-04-23 14:40:12 -0500237 if (asyncResp)
238 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700239 messages::internalError(asyncResp->res);
240 }
241 cleanUp();
242 return;
243 }
244 // Ensure we only got one service back
245 if (objInfo.size() != 1)
246 {
Ed Tanous62598e32023-07-17 17:06:25 -0700247 BMCWEB_LOG_ERROR("Invalid Object Size {}", objInfo.size());
Ed Tanous002d39b2022-05-31 08:59:27 -0700248 if (asyncResp)
249 {
250 messages::internalError(asyncResp->res);
251 }
252 cleanUp();
253 return;
254 }
255 // cancel timer only when
256 // xyz.openbmc_project.Software.Activation interface
257 // is added
258 fwAvailableTimer = nullptr;
259
260 activateImage(objPath.str, objInfo[0].first);
261 if (asyncResp)
262 {
Jagpal Singh Gillc71b6c92024-04-29 16:50:53 -0700263 createTask(asyncResp, std::move(payload), objPath);
Ed Tanous002d39b2022-05-31 08:59:27 -0700264 }
265 fwUpdateInProgress = false;
Patrick Williams5a39f772023-10-20 11:20:21 -0500266 });
Patrick Williams62bafc02022-09-08 17:35:35 -0500267
268 break;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500269 }
270 }
271}
272
Myung Bae8549b952023-08-16 15:18:19 -0400273inline void afterAvailbleTimerAsyncWait(
274 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
275 const boost::system::error_code& ec)
276{
277 cleanUp();
278 if (ec == boost::asio::error::operation_aborted)
279 {
280 // expected, we were canceled before the timer completed.
281 return;
282 }
283 BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
284 BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
285 if (ec)
286 {
287 BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
288 return;
289 }
290 if (asyncResp)
291 {
292 redfish::messages::internalError(asyncResp->res);
293 }
294}
295
296inline void
297 handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
298 const std::string& url, const std::string& type)
299{
300 if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
301 {
302 redfish::messages::invalidUpload(asyncResp->res, url,
303 "Invalid archive");
304 }
305 else if (type ==
306 "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
307 {
308 redfish::messages::invalidUpload(asyncResp->res, url,
309 "Invalid manifest");
310 }
311 else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
312 {
313 redfish::messages::invalidUpload(asyncResp->res, url,
314 "Invalid image format");
315 }
316 else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
317 {
318 redfish::messages::invalidUpload(asyncResp->res, url,
319 "Image version already exists");
320
321 redfish::messages::resourceAlreadyExists(
322 asyncResp->res, "UpdateService", "Version", "uploaded version");
323 }
324 else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
325 {
326 redfish::messages::resourceExhaustion(asyncResp->res, url);
327 }
Myung Bae4034a652023-08-17 08:47:35 -0400328 else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
Myung Bae8549b952023-08-16 15:18:19 -0400329 {
Myung Bae4034a652023-08-17 08:47:35 -0400330 redfish::messages::invalidUpload(asyncResp->res, url,
331 "Incompatible image version");
332 }
333 else if (type ==
334 "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
335 {
336 redfish::messages::invalidUpload(asyncResp->res, url,
337 "Update Access Key Expired");
338 }
339 else if (type ==
340 "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
341 {
342 redfish::messages::invalidUpload(asyncResp->res, url,
343 "Invalid image signature");
344 }
345 else if (type ==
346 "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
347 type == "xyz.openbmc_project.Software.Version.Error.HostFile")
348 {
349 BMCWEB_LOG_ERROR("Software Image Error type={}", type);
Myung Bae8549b952023-08-16 15:18:19 -0400350 redfish::messages::internalError(asyncResp->res);
351 }
Myung Bae4034a652023-08-17 08:47:35 -0400352 else
353 {
354 // Unrelated error types. Ignored
355 BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
356 return;
357 }
358 // Clear the timer
359 fwAvailableTimer = nullptr;
Myung Bae8549b952023-08-16 15:18:19 -0400360}
361
362inline void
363 afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
364 const std::string& url, sdbusplus::message_t& m)
365{
Michael Shen80f79a42023-08-24 13:41:53 +0000366 dbus::utility::DBusInterfacesMap interfacesProperties;
Myung Bae8549b952023-08-16 15:18:19 -0400367 sdbusplus::message::object_path objPath;
368 m.read(objPath, interfacesProperties);
369 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
370 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
371 interface : interfacesProperties)
372 {
373 if (interface.first == "xyz.openbmc_project.Logging.Entry")
374 {
375 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
376 value : interface.second)
377 {
378 if (value.first != "Message")
379 {
380 continue;
381 }
382 const std::string* type =
383 std::get_if<std::string>(&value.second);
384 if (type == nullptr)
385 {
386 // if this was our message, timeout will cover it
387 return;
388 }
Myung Bae8549b952023-08-16 15:18:19 -0400389 handleUpdateErrorType(asyncResp, url, *type);
390 }
391 }
392 }
393}
394
Andrew Geissler0554c982019-04-23 14:40:12 -0500395// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
396// then no asyncResp updates will occur
Ed Tanousf5139332024-04-03 13:25:04 -0700397inline void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800398 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
399 const crow::Request& req, const std::string& url,
Gunnar Mills5d138942022-09-07 10:26:21 -0500400 int timeoutTimeSeconds = 25)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500401{
402 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800403 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500404 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500405 if (asyncResp)
406 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500407 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
408 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500409 return;
410 }
411
Ed Tanous8e8245d2024-04-11 22:21:38 -0700412 if (req.ioService == nullptr)
413 {
414 messages::internalError(asyncResp->res);
415 return;
416 }
417
Andrew Geissler0554c982019-04-23 14:40:12 -0500418 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700419 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500420
Ed Tanous271584a2019-07-09 16:24:22 -0700421 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500422
423 fwAvailableTimer->async_wait(
Myung Bae8549b952023-08-16 15:18:19 -0400424 std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
425
Ed Tanousa3e65892021-09-16 14:13:20 -0700426 task::Payload payload(req);
Patrick Williams59d494e2022-07-22 19:26:55 -0500427 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
Ed Tanous62598e32023-07-17 17:06:25 -0700428 BMCWEB_LOG_DEBUG("Match fired");
Ed Tanousa3e65892021-09-16 14:13:20 -0700429 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500430 };
431
432 fwUpdateInProgress = true;
433
Patrick Williams59d494e2022-07-22 19:26:55 -0500434 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Andrew Geissler86adcd62019-04-18 10:58:05 -0500435 *crow::connections::systemBus,
436 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
437 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
438 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700439
Patrick Williams59d494e2022-07-22 19:26:55 -0500440 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
James Feist4cde5d92020-06-11 10:39:55 -0700441 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800442 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
443 "member='InterfacesAdded',"
444 "path='/xyz/openbmc_project/logging'",
Myung Bae8549b952023-08-16 15:18:19 -0400445 std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500446}
Jennifer Lee729dae72018-04-24 15:59:34 -0700447
Ed Tanous757178a2024-04-03 14:32:38 -0700448inline std::optional<boost::urls::url>
449 parseSimpleUpdateUrl(std::string imageURI,
450 std::optional<std::string> transferProtocol,
451 crow::Response& res)
Ed Tanousf86bcc82023-08-25 09:34:07 -0700452{
453 if (imageURI.find("://") == std::string::npos)
454 {
455 if (imageURI.starts_with("/"))
456 {
457 messages::actionParameterValueTypeError(
458 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
459 return std::nullopt;
460 }
461 if (!transferProtocol)
462 {
463 messages::actionParameterValueTypeError(
464 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
465 return std::nullopt;
466 }
Ed Tanouse5cf7772024-04-03 13:45:31 -0700467 // OpenBMC currently only supports TFTP or HTTPS
Ed Tanous757178a2024-04-03 14:32:38 -0700468 if (*transferProtocol == "TFTP")
469 {
470 imageURI = "tftp://" + imageURI;
471 }
Ed Tanouse5cf7772024-04-03 13:45:31 -0700472 else if (*transferProtocol == "HTTPS")
473 {
474 imageURI = "https://" + imageURI;
475 }
Ed Tanous757178a2024-04-03 14:32:38 -0700476 else
Ed Tanousf86bcc82023-08-25 09:34:07 -0700477 {
478 messages::actionParameterNotSupported(res, "TransferProtocol",
479 *transferProtocol);
480 BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
481 *transferProtocol);
482 return std::nullopt;
483 }
Ed Tanousf86bcc82023-08-25 09:34:07 -0700484 }
485
486 boost::system::result<boost::urls::url> url =
487 boost::urls::parse_absolute_uri(imageURI);
488 if (!url)
489 {
490 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
491 "UpdateService.SimpleUpdate");
492
493 return std::nullopt;
494 }
495 url->normalize();
496
Ed Tanous757178a2024-04-03 14:32:38 -0700497 if (url->scheme() == "tftp")
498 {
499 if (url->encoded_path().size() < 2)
500 {
501 messages::actionParameterNotSupported(res, "ImageURI",
502 url->buffer());
503 return std::nullopt;
504 }
505 }
Ed Tanouse5cf7772024-04-03 13:45:31 -0700506 else if (url->scheme() == "https")
507 {
508 // Empty paths default to "/"
509 if (url->encoded_path().empty())
510 {
511 url->set_encoded_path("/");
512 }
513 }
Ed Tanous757178a2024-04-03 14:32:38 -0700514 else
Ed Tanousf86bcc82023-08-25 09:34:07 -0700515 {
516 messages::actionParameterNotSupported(res, "ImageURI", imageURI);
517 return std::nullopt;
518 }
Ed Tanous757178a2024-04-03 14:32:38 -0700519
520 if (url->encoded_path().empty())
Ed Tanousf86bcc82023-08-25 09:34:07 -0700521 {
Ed Tanous757178a2024-04-03 14:32:38 -0700522 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
523 "UpdateService.SimpleUpdate");
Ed Tanousf86bcc82023-08-25 09:34:07 -0700524 return std::nullopt;
525 }
Ed Tanous757178a2024-04-03 14:32:38 -0700526
527 return *url;
Ed Tanousf86bcc82023-08-25 09:34:07 -0700528}
529
Ed Tanouse5cf7772024-04-03 13:45:31 -0700530inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
531 const boost::urls::url_view_base& url)
532{
533 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
534 url.buffer());
535}
536
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700537inline void doTftpUpdate(const crow::Request& req,
538 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous757178a2024-04-03 14:32:38 -0700539 const boost::urls::url_view_base& url)
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700540{
Ed Tanousc72503f2024-05-06 14:57:13 -0700541 if (!BMCWEB_INSECURE_TFTP_UPDATE)
542 {
543 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
544 url.buffer());
545 return;
546 }
Ed Tanous757178a2024-04-03 14:32:38 -0700547
548 std::string path(url.encoded_path());
549 if (path.size() < 2)
550 {
551 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
552 url.buffer());
553 return;
554 }
555 // TFTP expects a path without a /
556 path.erase(0, 1);
557 std::string host(url.encoded_host_and_port());
558 BMCWEB_LOG_DEBUG("Server: {} File: {}", host, path);
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700559
560 // Setup callback for when new software detected
561 // Give TFTP 10 minutes to complete
562 monitorForSoftwareAvailable(
563 asyncResp, req,
564 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate", 600);
565
566 // TFTP can take up to 10 minutes depending on image size and
567 // connection speed. Return to caller as soon as the TFTP operation
568 // has been started. The callback above will ensure the activate
569 // is started once the download has completed
570 redfish::messages::success(asyncResp->res);
571
572 // Call TFTP service
573 crow::connections::systemBus->async_method_call(
574 [](const boost::system::error_code& ec) {
575 if (ec)
576 {
577 // messages::internalError(asyncResp->res);
578 cleanUp();
579 BMCWEB_LOG_DEBUG("error_code = {}", ec);
580 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
581 }
582 else
583 {
584 BMCWEB_LOG_DEBUG("Call to DownloaViaTFTP Success");
585 }
586 },
587 "xyz.openbmc_project.Software.Download",
588 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
Ed Tanous757178a2024-04-03 14:32:38 -0700589 "DownloadViaTFTP", path, host);
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700590}
591
Ed Tanousf5139332024-04-03 13:25:04 -0700592inline void handleUpdateServiceSimpleUpdateAction(
593 crow::App& app, const crow::Request& req,
594 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Andrew Geissler0554c982019-04-23 14:40:12 -0500595{
Ed Tanousf5139332024-04-03 13:25:04 -0700596 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
597 {
598 return;
599 }
600
601 std::optional<std::string> transferProtocol;
602 std::string imageURI;
603
604 BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
605
606 // User can pass in both TransferProtocol and ImageURI parameters or
607 // they can pass in just the ImageURI with the transfer protocol
608 // embedded within it.
609 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
610 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
611
612 if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
613 transferProtocol, "ImageURI", imageURI))
614 {
615 BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
616 return;
617 }
618
Ed Tanous757178a2024-04-03 14:32:38 -0700619 std::optional<boost::urls::url> url =
620 parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res);
621 if (!url)
Ed Tanousf5139332024-04-03 13:25:04 -0700622 {
623 return;
624 }
Ed Tanous757178a2024-04-03 14:32:38 -0700625 if (url->scheme() == "tftp")
626 {
627 doTftpUpdate(req, asyncResp, *url);
628 }
Ed Tanouse5cf7772024-04-03 13:45:31 -0700629 else if (url->scheme() == "https")
630 {
631 doHttpsUpdate(asyncResp, *url);
632 }
Ed Tanous757178a2024-04-03 14:32:38 -0700633 else
634 {
635 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
636 url->buffer());
637 return;
638 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700639
Ed Tanousf5139332024-04-03 13:25:04 -0700640 BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700641}
642
George Liu0ed80c82020-05-12 16:06:27 +0800643inline void uploadImageFile(crow::Response& res, std::string_view body)
644{
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700645 std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
646
Ed Tanous62598e32023-07-17 17:06:25 -0700647 BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
George Liu0ed80c82020-05-12 16:06:27 +0800648 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
649 std::ofstream::trunc);
650 // set the permission of the file to 640
Patrick Williams89492a12023-05-10 07:51:34 -0500651 std::filesystem::perms permission = std::filesystem::perms::owner_read |
652 std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800653 std::filesystem::permissions(filepath, permission);
654 out << body;
655
656 if (out.bad())
657 {
658 messages::internalError(res);
659 cleanUp();
660 }
661}
662
663inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
664 const std::string& applyTime)
665{
666 std::string applyTimeNewVal;
667 if (applyTime == "Immediate")
668 {
669 applyTimeNewVal =
670 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
671 }
672 else if (applyTime == "OnReset")
673 {
674 applyTimeNewVal =
675 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
676 }
677 else
678 {
Ed Tanous62598e32023-07-17 17:06:25 -0700679 BMCWEB_LOG_INFO(
680 "ApplyTime value is not in the list of acceptable values");
George Liu0ed80c82020-05-12 16:06:27 +0800681 messages::propertyValueNotInList(asyncResp->res, applyTime,
682 "ApplyTime");
683 return;
684 }
685
Ed Tanousd02aad32024-02-13 14:43:34 -0800686 setDbusProperty(asyncResp, "xyz.openbmc_project.Settings",
687 sdbusplus::message::object_path(
688 "/xyz/openbmc_project/software/apply_time"),
689 "xyz.openbmc_project.Software.ApplyTime",
690 "RequestedApplyTime", "ApplyTime", applyTimeNewVal);
George Liu0ed80c82020-05-12 16:06:27 +0800691}
692
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700693struct MultiPartUpdateParameters
George Liu0ed80c82020-05-12 16:06:27 +0800694{
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700695 std::optional<std::string> applyTime;
696 std::string uploadData;
697 std::vector<boost::urls::url> targets;
698};
699
700inline std::optional<MultiPartUpdateParameters>
701 extractMultipartUpdateParameters(
702 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
703 MultipartParser parser)
704{
705 MultiPartUpdateParameters multiRet;
706 for (FormPart& formpart : parser.mime_fields)
George Liu0ed80c82020-05-12 16:06:27 +0800707 {
708 boost::beast::http::fields::const_iterator it =
709 formpart.fields.find("Content-Disposition");
710 if (it == formpart.fields.end())
711 {
Ed Tanous62598e32023-07-17 17:06:25 -0700712 BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700713 return std::nullopt;
George Liu0ed80c82020-05-12 16:06:27 +0800714 }
Ed Tanous62598e32023-07-17 17:06:25 -0700715 BMCWEB_LOG_INFO("Parsing value {}", it->value());
George Liu0ed80c82020-05-12 16:06:27 +0800716
717 // The construction parameters of param_list must start with `;`
718 size_t index = it->value().find(';');
719 if (index == std::string::npos)
720 {
721 continue;
722 }
723
Patrick Williams89492a12023-05-10 07:51:34 -0500724 for (const auto& param :
George Liu0ed80c82020-05-12 16:06:27 +0800725 boost::beast::http::param_list{it->value().substr(index)})
726 {
727 if (param.first != "name" || param.second.empty())
728 {
729 continue;
730 }
731
732 if (param.second == "UpdateParameters")
733 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700734 std::vector<std::string> tempTargets;
George Liu0ed80c82020-05-12 16:06:27 +0800735 nlohmann::json content =
736 nlohmann::json::parse(formpart.content);
Ed Tanous7cb59f62022-05-05 11:48:31 -0700737 nlohmann::json::object_t* obj =
738 content.get_ptr<nlohmann::json::object_t*>();
739 if (obj == nullptr)
740 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700741 messages::propertyValueTypeError(
742 asyncResp->res, formpart.content, "UpdateParameters");
743 return std::nullopt;
Ed Tanous7cb59f62022-05-05 11:48:31 -0700744 }
745
746 if (!json_util::readJsonObject(
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700747 *obj, asyncResp->res, "Targets", tempTargets,
748 "@Redfish.OperationApplyTime", multiRet.applyTime))
George Liu0ed80c82020-05-12 16:06:27 +0800749 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700750 return std::nullopt;
George Liu0ed80c82020-05-12 16:06:27 +0800751 }
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700752
753 for (size_t urlIndex = 0; urlIndex < tempTargets.size();
754 urlIndex++)
George Liu0ed80c82020-05-12 16:06:27 +0800755 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700756 const std::string& target = tempTargets[urlIndex];
757 boost::system::result<boost::urls::url_view> url =
758 boost::urls::parse_origin_form(target);
759 if (!url)
760 {
761 messages::propertyValueFormatError(
762 asyncResp->res, target,
763 std::format("Targets/{}", urlIndex));
764 return std::nullopt;
765 }
766 multiRet.targets.emplace_back(*url);
George Liu0ed80c82020-05-12 16:06:27 +0800767 }
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700768 if (multiRet.targets.size() != 1)
George Liu0ed80c82020-05-12 16:06:27 +0800769 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700770 messages::propertyValueFormatError(
771 asyncResp->res, multiRet.targets, "Targets");
772 return std::nullopt;
George Liu0ed80c82020-05-12 16:06:27 +0800773 }
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700774 if (multiRet.targets[0].path() != "/redfish/v1/Managers/bmc")
775 {
776 messages::propertyValueNotInList(
777 asyncResp->res, multiRet.targets[0], "Targets/0");
778 return std::nullopt;
779 }
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
802inline void
803 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
804 const crow::Request& req, MultipartParser&& parser)
805{
806 std::optional<MultiPartUpdateParameters> multipart =
807 extractMultipartUpdateParameters(asyncResp, std::move(parser));
808 if (!multipart)
809 {
George Liu0ed80c82020-05-12 16:06:27 +0800810 return;
811 }
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700812 if (!multipart->applyTime)
George Liu0ed80c82020-05-12 16:06:27 +0800813 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700814 multipart->applyTime = "OnReset";
George Liu0ed80c82020-05-12 16:06:27 +0800815 }
816
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700817 setApplyTime(asyncResp, *multipart->applyTime);
George Liu0ed80c82020-05-12 16:06:27 +0800818
Ed Tanous6b54e4e2024-04-10 08:58:48 -0700819 // Setup callback for when new software detected
820 monitorForSoftwareAvailable(asyncResp, req, "/redfish/v1/UpdateService");
821
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700822 uploadImageFile(asyncResp->res, multipart->uploadData);
George Liu0ed80c82020-05-12 16:06:27 +0800823}
824
Ed Tanousc2051d12022-05-11 12:21:55 -0700825inline void
826 handleUpdateServicePost(App& app, const crow::Request& req,
827 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
828{
Carson Labrado3ba00072022-06-06 19:40:56 +0000829 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700830 {
831 return;
832 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500833 std::string_view contentType = req.getHeaderValue("Content-Type");
Ed Tanousc2051d12022-05-11 12:21:55 -0700834
Ed Tanous62598e32023-07-17 17:06:25 -0700835 BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
Ed Tanousc2051d12022-05-11 12:21:55 -0700836
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500837 // Make sure that content type is application/octet-stream or
838 // multipart/form-data
Ed Tanous18f8f602023-07-18 10:07:23 -0700839 if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
George Liu0ed80c82020-05-12 16:06:27 +0800840 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500841 // Setup callback for when new software detected
842 monitorForSoftwareAvailable(asyncResp, req,
843 "/redfish/v1/UpdateService");
844
George Liu0ed80c82020-05-12 16:06:27 +0800845 uploadImageFile(asyncResp->res, req.body());
George Liu0ed80c82020-05-12 16:06:27 +0800846 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500847 else if (contentType.starts_with("multipart/form-data"))
George Liu0ed80c82020-05-12 16:06:27 +0800848 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500849 MultipartParser parser;
850
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500851 ParserError ec = parser.parse(req);
852 if (ec != ParserError::PARSER_SUCCESS)
853 {
854 // handle error
Ed Tanous62598e32023-07-17 17:06:25 -0700855 BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
856 static_cast<int>(ec));
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500857 messages::internalError(asyncResp->res);
858 return;
859 }
Ed Tanous6b54e4e2024-04-10 08:58:48 -0700860
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700861 updateMultipartContext(asyncResp, req, std::move(parser));
George Liu0ed80c82020-05-12 16:06:27 +0800862 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500863 else
864 {
Ed Tanous62598e32023-07-17 17:06:25 -0700865 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500866 asyncResp->res.result(boost::beast::http::status::bad_request);
867 }
Ed Tanousc2051d12022-05-11 12:21:55 -0700868}
869
Ed Tanousf5139332024-04-03 13:25:04 -0700870inline void
871 handleUpdateServiceGet(App& app, const crow::Request& req,
872 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700873{
Ed Tanousf5139332024-04-03 13:25:04 -0700874 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
875 {
876 return;
877 }
878 asyncResp->res.jsonValue["@odata.type"] =
879 "#UpdateService.v1_11_1.UpdateService";
880 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
881 asyncResp->res.jsonValue["Id"] = "UpdateService";
882 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
883 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700884
Ed Tanousf5139332024-04-03 13:25:04 -0700885 asyncResp->res.jsonValue["HttpPushUri"] =
886 "/redfish/v1/UpdateService/update";
887 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
888 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700889
Ed Tanousf5139332024-04-03 13:25:04 -0700890 // UpdateService cannot be disabled
891 asyncResp->res.jsonValue["ServiceEnabled"] = true;
892 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
893 "/redfish/v1/UpdateService/FirmwareInventory";
894 // Get the MaxImageSizeBytes
Ed Tanous25b54db2024-04-17 15:40:31 -0700895 asyncResp->res.jsonValue["MaxImageSizeBytes"] = BMCWEB_HTTP_BODY_LIMIT *
Ed Tanousf5139332024-04-03 13:25:04 -0700896 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530897
Ed Tanousf5139332024-04-03 13:25:04 -0700898 // Update Actions object.
899 nlohmann::json& updateSvcSimpleUpdate =
900 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
901 updateSvcSimpleUpdate["target"] =
902 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
Ed Tanous757178a2024-04-03 14:32:38 -0700903
904 nlohmann::json::array_t allowed;
Ed Tanouse5cf7772024-04-03 13:45:31 -0700905 allowed.emplace_back(update_service::TransferProtocolType::HTTPS);
Ed Tanous757178a2024-04-03 14:32:38 -0700906
Ed Tanous25b54db2024-04-17 15:40:31 -0700907 if constexpr (BMCWEB_INSECURE_PUSH_STYLE_NOTIFICATION)
908 {
909 allowed.emplace_back(update_service::TransferProtocolType::TFTP);
910 }
Ed Tanous757178a2024-04-03 14:32:38 -0700911
912 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
913 std::move(allowed);
914
Ed Tanousf5139332024-04-03 13:25:04 -0700915 // Get the current ApplyTime value
916 sdbusplus::asio::getProperty<std::string>(
917 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
918 "/xyz/openbmc_project/software/apply_time",
919 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
920 [asyncResp](const boost::system::error_code& ec,
921 const std::string& applyTime) {
922 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700923 {
Ed Tanousf5139332024-04-03 13:25:04 -0700924 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
925 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -0700926 return;
927 }
928
Ed Tanousf5139332024-04-03 13:25:04 -0700929 // Store the ApplyTime Value
930 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
931 "RequestedApplyTimes.Immediate")
Ed Tanous002d39b2022-05-31 08:59:27 -0700932 {
Ed Tanousf5139332024-04-03 13:25:04 -0700933 asyncResp->res.jsonValue["HttpPushUriOptions"]
934 ["HttpPushUriApplyTime"]["ApplyTime"] =
935 "Immediate";
936 }
937 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
938 "RequestedApplyTimes.OnReset")
939 {
940 asyncResp->res.jsonValue["HttpPushUriOptions"]
941 ["HttpPushUriApplyTime"]["ApplyTime"] =
942 "OnReset";
Ed Tanous002d39b2022-05-31 08:59:27 -0700943 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500944 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700945}
946
Ed Tanousf5139332024-04-03 13:25:04 -0700947inline void handleUpdateServicePatch(
948 App& app, const crow::Request& req,
949 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700950{
Ed Tanousf5139332024-04-03 13:25:04 -0700951 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
952 {
953 return;
954 }
955 BMCWEB_LOG_DEBUG("doPatch...");
Ed Tanous002d39b2022-05-31 08:59:27 -0700956
Ed Tanousf5139332024-04-03 13:25:04 -0700957 std::optional<std::string> applyTime;
958 if (!json_util::readJsonPatch(
959 req, asyncResp->res,
960 "HttpPushUriOptions/HttpPushUriApplyTime/ApplyTime", applyTime))
961 {
962 return;
963 }
964
965 if (applyTime)
966 {
967 setApplyTime(asyncResp, *applyTime);
968 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700969}
Ed Tanousf5139332024-04-03 13:25:04 -0700970
971inline void handleUpdateServiceFirmwareInventoryCollectionGet(
972 App& app, const crow::Request& req,
973 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
974{
975 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
976 {
977 return;
978 }
979 asyncResp->res.jsonValue["@odata.type"] =
980 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
981 asyncResp->res.jsonValue["@odata.id"] =
982 "/redfish/v1/UpdateService/FirmwareInventory";
983 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
984 const std::array<const std::string_view, 1> iface = {
985 "xyz.openbmc_project.Software.Version"};
986
987 redfish::collection_util::getCollectionMembers(
988 asyncResp,
989 boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface,
990 "/xyz/openbmc_project/software");
991}
992
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700993/* Fill related item links (i.e. bmc, bios) in for inventory */
Ed Tanousf5139332024-04-03 13:25:04 -0700994inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
995 const std::string& purpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700996{
Willy Tueee00132022-06-14 14:53:17 -0700997 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700998 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700999 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -07001000 nlohmann::json::object_t item;
1001 item["@odata.id"] = "/redfish/v1/Managers/bmc";
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001002 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -07001003 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1004 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001005 }
Willy Tueee00132022-06-14 14:53:17 -07001006 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001007 {
Ed Tanousac106bf2023-06-07 09:24:59 -07001008 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -07001009 nlohmann::json::object_t item;
1010 item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001011 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -07001012 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1013 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001014 }
1015 else
1016 {
Carson Labradobf2dded2023-08-10 00:37:06 +00001017 BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001018 }
1019}
1020
Willy Tuaf246602022-06-14 15:51:53 -07001021inline void
1022 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1023 const std::string& service, const std::string& path,
1024 const std::string& swId)
1025{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001026 sdbusplus::asio::getAllProperties(
1027 *crow::connections::systemBus, service, path,
1028 "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -07001029 [asyncResp,
Ed Tanous8b242752023-06-27 17:17:13 -07001030 swId](const boost::system::error_code& ec,
Willy Tuaf246602022-06-14 15:51:53 -07001031 const dbus::utility::DBusPropertiesMap& propertiesList) {
Ed Tanous8b242752023-06-27 17:17:13 -07001032 if (ec)
Willy Tuaf246602022-06-14 15:51:53 -07001033 {
1034 messages::internalError(asyncResp->res);
1035 return;
1036 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001037
Willy Tuaf246602022-06-14 15:51:53 -07001038 const std::string* swInvPurpose = nullptr;
1039 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001040
1041 const bool success = sdbusplus::unpackPropertiesNoThrow(
1042 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
1043 swInvPurpose, "Version", version);
1044
1045 if (!success)
Willy Tuaf246602022-06-14 15:51:53 -07001046 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001047 messages::internalError(asyncResp->res);
1048 return;
Willy Tuaf246602022-06-14 15:51:53 -07001049 }
1050
1051 if (swInvPurpose == nullptr)
1052 {
Ed Tanous62598e32023-07-17 17:06:25 -07001053 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
Willy Tuaf246602022-06-14 15:51:53 -07001054 messages::internalError(asyncResp->res);
1055 return;
1056 }
1057
Ed Tanous62598e32023-07-17 17:06:25 -07001058 BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
Willy Tuaf246602022-06-14 15:51:53 -07001059
1060 if (version == nullptr)
1061 {
Ed Tanous62598e32023-07-17 17:06:25 -07001062 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
Willy Tuaf246602022-06-14 15:51:53 -07001063
1064 messages::internalError(asyncResp->res);
1065
1066 return;
1067 }
1068 asyncResp->res.jsonValue["Version"] = *version;
1069 asyncResp->res.jsonValue["Id"] = swId;
1070
1071 // swInvPurpose is of format:
1072 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
1073 // Translate this to "ABC image"
1074 size_t endDesc = swInvPurpose->rfind('.');
1075 if (endDesc == std::string::npos)
1076 {
1077 messages::internalError(asyncResp->res);
1078 return;
1079 }
1080 endDesc++;
1081 if (endDesc >= swInvPurpose->size())
1082 {
1083 messages::internalError(asyncResp->res);
1084 return;
1085 }
1086
1087 std::string formatDesc = swInvPurpose->substr(endDesc);
1088 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1089 getRelatedItems(asyncResp, *swInvPurpose);
Patrick Williams5a39f772023-10-20 11:20:21 -05001090 });
Willy Tuaf246602022-06-14 15:51:53 -07001091}
1092
Ed Tanousf5139332024-04-03 13:25:04 -07001093inline void handleUpdateServiceFirmwareInventoryGet(
1094 App& app, const crow::Request& req,
1095 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1096 const std::string& param)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001097{
Ed Tanousf5139332024-04-03 13:25:04 -07001098 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1099 {
1100 return;
1101 }
1102 std::shared_ptr<std::string> swId = std::make_shared<std::string>(param);
1103
1104 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1105 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
1106
1107 constexpr std::array<std::string_view, 1> interfaces = {
1108 "xyz.openbmc_project.Software.Version"};
1109 dbus::utility::getSubTree(
1110 "/", 0, interfaces,
1111 [asyncResp,
1112 swId](const boost::system::error_code& ec,
1113 const dbus::utility::MapperGetSubTreeResponse& subtree) {
1114 BMCWEB_LOG_DEBUG("doGet callback...");
1115 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001116 {
Ed Tanousf5139332024-04-03 13:25:04 -07001117 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001118 return;
1119 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001120
Ed Tanousf5139332024-04-03 13:25:04 -07001121 // Ensure we find our input swId, otherwise return an error
1122 bool found = false;
1123 for (const std::pair<
1124 std::string,
1125 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1126 obj : subtree)
1127 {
1128 if (!obj.first.ends_with(*swId))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001129 {
Ed Tanousf5139332024-04-03 13:25:04 -07001130 continue;
Ed Tanous45ca1b82022-03-25 13:07:27 -07001131 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001132
Ed Tanousf5139332024-04-03 13:25:04 -07001133 if (obj.second.empty())
Ed Tanous002d39b2022-05-31 08:59:27 -07001134 {
Ed Tanousf5139332024-04-03 13:25:04 -07001135 continue;
Ed Tanous002d39b2022-05-31 08:59:27 -07001136 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001137
Ed Tanousf5139332024-04-03 13:25:04 -07001138 found = true;
1139 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1140 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1141 *swId);
1142 }
1143 if (!found)
1144 {
1145 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
1146 messages::resourceMissingAtURI(
1147 asyncResp->res,
1148 boost::urls::format(
1149 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId));
1150 return;
1151 }
1152 asyncResp->res.jsonValue["@odata.type"] =
1153 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1154 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1155 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
1156
1157 asyncResp->res.jsonValue["Updateable"] = false;
1158 sw_util::getSwUpdatableStatus(asyncResp, swId);
Patrick Williams5a39f772023-10-20 11:20:21 -05001159 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001160}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001161
Ed Tanousf5139332024-04-03 13:25:04 -07001162inline void requestRoutesUpdateService(App& app)
1163{
1164 BMCWEB_ROUTE(
1165 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
1166 .privileges(redfish::privileges::postUpdateService)
1167 .methods(boost::beast::http::verb::post)(std::bind_front(
1168 handleUpdateServiceSimpleUpdateAction, std::ref(app)));
1169
1170 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1171 .privileges(redfish::privileges::getSoftwareInventory)
1172 .methods(boost::beast::http::verb::get)(std::bind_front(
1173 handleUpdateServiceFirmwareInventoryGet, std::ref(app)));
1174
1175 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1176 .privileges(redfish::privileges::getUpdateService)
1177 .methods(boost::beast::http::verb::get)(
1178 std::bind_front(handleUpdateServiceGet, std::ref(app)));
1179
1180 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1181 .privileges(redfish::privileges::patchUpdateService)
1182 .methods(boost::beast::http::verb::patch)(
1183 std::bind_front(handleUpdateServicePatch, std::ref(app)));
1184
1185 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
1186 .privileges(redfish::privileges::postUpdateService)
1187 .methods(boost::beast::http::verb::post)(
1188 std::bind_front(handleUpdateServicePost, std::ref(app)));
1189
1190 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
1191 .privileges(redfish::privileges::getSoftwareInventoryCollection)
1192 .methods(boost::beast::http::verb::get)(std::bind_front(
1193 handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app)));
1194}
1195
Ed Tanous1abe55e2018-09-05 08:30:59 -07001196} // namespace redfish