blob: 6895cb49499f371af88cdbfa3e0e6b2621088a68 [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>
Ed Tanous7cb59f62022-05-05 11:48:31 -070043#include <optional>
44#include <string>
George Liu2b731192023-01-11 16:27:13 +080045#include <string_view>
46
Ed Tanous1abe55e2018-09-05 08:30:59 -070047namespace redfish
48{
Ed Tanous27826b52018-10-29 11:40:58 -070049
Andrew Geissler0e7de462019-03-04 19:11:54 -060050// Match signals added on software path
Ed Tanouscf9e4172022-12-21 09:30:16 -080051// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050052static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
Ed Tanouscf9e4172022-12-21 09:30:16 -080053// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050054static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060055// Only allow one update at a time
Ed Tanouscf9e4172022-12-21 09:30:16 -080056// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Andrew Geissler0e7de462019-03-04 19:11:54 -060057static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050058// Timer for software available
Ed Tanouscf9e4172022-12-21 09:30:16 -080059// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous271584a2019-07-09 16:24:22 -070060static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050061
Ed Tanousdf254f22024-04-01 13:25:46 -070062inline void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050063{
64 fwUpdateInProgress = false;
65 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070066 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050067}
Ed Tanousdf254f22024-04-01 13:25:46 -070068
69inline void activateImage(const std::string& objPath,
70 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050071{
Ed Tanous62598e32023-07-17 17:06:25 -070072 BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
George Liu9ae226f2023-06-21 17:56:46 +080073 sdbusplus::asio::setProperty(
74 *crow::connections::systemBus, service, objPath,
75 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
76 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
Ed Tanous8b242752023-06-27 17:17:13 -070077 [](const boost::system::error_code& ec) {
78 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -070079 {
Ed Tanous62598e32023-07-17 17:06:25 -070080 BMCWEB_LOG_DEBUG("error_code = {}", ec);
81 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -070082 }
Patrick Williams5a39f772023-10-20 11:20:21 -050083 });
Andrew Geissler86adcd62019-04-18 10:58:05 -050084}
Andrew Geissler0554c982019-04-23 14:40:12 -050085
86// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
87// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080088static void
89 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -050090 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050091{
Michael Shen80f79a42023-08-24 13:41:53 +000092 dbus::utility::DBusInterfacesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -050093
94 sdbusplus::message::object_path objPath;
95
96 m.read(objPath, interfacesProperties);
97
Ed Tanous62598e32023-07-17 17:06:25 -070098 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
Ed Tanouse3eb3d62022-12-21 11:56:07 -080099 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500100 {
Ed Tanous62598e32023-07-17 17:06:25 -0700101 BMCWEB_LOG_DEBUG("interface = {}", interface.first);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500102
103 if (interface.first == "xyz.openbmc_project.Software.Activation")
104 {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500105 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +0800106 constexpr std::array<std::string_view, 1> interfaces = {
107 "xyz.openbmc_project.Software.Activation"};
108 dbus::utility::getDbusObject(
109 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700110 [objPath, asyncResp, payload(std::move(payload))](
Ed Tanous8b242752023-06-27 17:17:13 -0700111 const boost::system::error_code& ec,
Ed Tanousa3e65892021-09-16 14:13:20 -0700112 const std::vector<
113 std::pair<std::string, std::vector<std::string>>>&
114 objInfo) mutable {
Ed Tanous8b242752023-06-27 17:17:13 -0700115 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700116 {
Ed Tanous62598e32023-07-17 17:06:25 -0700117 BMCWEB_LOG_DEBUG("error_code = {}", ec);
118 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Andrew Geissler0554c982019-04-23 14:40:12 -0500119 if (asyncResp)
120 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700121 messages::internalError(asyncResp->res);
122 }
123 cleanUp();
124 return;
125 }
126 // Ensure we only got one service back
127 if (objInfo.size() != 1)
128 {
Ed Tanous62598e32023-07-17 17:06:25 -0700129 BMCWEB_LOG_ERROR("Invalid Object Size {}", objInfo.size());
Ed Tanous002d39b2022-05-31 08:59:27 -0700130 if (asyncResp)
131 {
132 messages::internalError(asyncResp->res);
133 }
134 cleanUp();
135 return;
136 }
137 // cancel timer only when
138 // xyz.openbmc_project.Software.Activation interface
139 // is added
140 fwAvailableTimer = nullptr;
141
142 activateImage(objPath.str, objInfo[0].first);
143 if (asyncResp)
144 {
145 std::shared_ptr<task::TaskData> task =
146 task::TaskData::createTask(
Ed Tanous8b242752023-06-27 17:17:13 -0700147 [](const boost::system::error_code& ec2,
Patrick Williams59d494e2022-07-22 19:26:55 -0500148 sdbusplus::message_t& msg,
Ed Tanous002d39b2022-05-31 08:59:27 -0700149 const std::shared_ptr<task::TaskData>&
150 taskData) {
Ed Tanous8b242752023-06-27 17:17:13 -0700151 if (ec2)
Ed Tanous002d39b2022-05-31 08:59:27 -0700152 {
153 return task::completed;
154 }
155
156 std::string iface;
157 dbus::utility::DBusPropertiesMap values;
158
159 std::string index = std::to_string(taskData->index);
160 msg.read(iface, values);
161
162 if (iface == "xyz.openbmc_project.Software.Activation")
163 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000164 const std::string* state = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700165 for (const auto& property : values)
166 {
167 if (property.first == "Activation")
168 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000169 state = std::get_if<std::string>(
170 &property.second);
171 if (state == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700172 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700173 taskData->messages.emplace_back(
174 messages::internalError());
James Feist32898ce2020-03-10 16:16:52 -0700175 return task::completed;
176 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700177 }
178 }
James Feist32898ce2020-03-10 16:16:52 -0700179
Ed Tanous002d39b2022-05-31 08:59:27 -0700180 if (state == nullptr)
181 {
182 return !task::completed;
183 }
James Feist32898ce2020-03-10 16:16:52 -0700184
Ed Tanous11ba3972022-07-11 09:50:41 -0700185 if (state->ends_with("Invalid") ||
186 state->ends_with("Failed"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700187 {
188 taskData->state = "Exception";
189 taskData->status = "Warning";
190 taskData->messages.emplace_back(
191 messages::taskAborted(index));
192 return task::completed;
193 }
James Feiste5d50062020-05-11 17:29:00 -0700194
Ed Tanous11ba3972022-07-11 09:50:41 -0700195 if (state->ends_with("Staged"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700196 {
197 taskData->state = "Stopping";
198 taskData->messages.emplace_back(
199 messages::taskPaused(index));
200
201 // its staged, set a long timer to
202 // allow them time to complete the
203 // update (probably cycle the
204 // system) if this expires then
Ed Tanous8ece0e42024-01-02 13:16:50 -0800205 // task will be canceled
Ed Tanous002d39b2022-05-31 08:59:27 -0700206 taskData->extendTimer(std::chrono::hours(5));
207 return !task::completed;
208 }
209
Ed Tanous11ba3972022-07-11 09:50:41 -0700210 if (state->ends_with("Active"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700211 {
212 taskData->messages.emplace_back(
213 messages::taskCompletedOK(index));
214 taskData->state = "Completed";
215 return task::completed;
216 }
217 }
218 else if (
219 iface ==
220 "xyz.openbmc_project.Software.ActivationProgress")
221 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700222 const uint8_t* progress = nullptr;
223 for (const auto& property : values)
224 {
225 if (property.first == "Progress")
226 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000227 progress =
228 std::get_if<uint8_t>(&property.second);
229 if (progress == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700230 {
James Feist32898ce2020-03-10 16:16:52 -0700231 taskData->messages.emplace_back(
Ed Tanous002d39b2022-05-31 08:59:27 -0700232 messages::internalError());
233 return task::completed;
James Feist32898ce2020-03-10 16:16:52 -0700234 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700235 }
236 }
James Feist32898ce2020-03-10 16:16:52 -0700237
Ed Tanous002d39b2022-05-31 08:59:27 -0700238 if (progress == nullptr)
239 {
240 return !task::completed;
241 }
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000242 taskData->percentComplete = *progress;
Ed Tanous002d39b2022-05-31 08:59:27 -0700243 taskData->messages.emplace_back(
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000244 messages::taskProgressChanged(index,
245 *progress));
James Feist32898ce2020-03-10 16:16:52 -0700246
Ed Tanous002d39b2022-05-31 08:59:27 -0700247 // if we're getting status updates it's
248 // still alive, update timer
249 taskData->extendTimer(std::chrono::minutes(5));
250 }
251
252 // as firmware update often results in a
253 // reboot, the task may never "complete"
254 // unless it is an error
255
256 return !task::completed;
Patrick Williams5a39f772023-10-20 11:20:21 -0500257 },
Ed Tanous002d39b2022-05-31 08:59:27 -0700258 "type='signal',interface='org.freedesktop.DBus.Properties',"
259 "member='PropertiesChanged',path='" +
260 objPath.str + "'");
261 task->startTimer(std::chrono::minutes(5));
262 task->populateResp(asyncResp->res);
263 task->payload.emplace(std::move(payload));
264 }
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 }
467 // OpenBMC currently only supports TFTP
Ed Tanous757178a2024-04-03 14:32:38 -0700468 if (*transferProtocol == "TFTP")
469 {
470 imageURI = "tftp://" + imageURI;
471 }
472 else
Ed Tanousf86bcc82023-08-25 09:34:07 -0700473 {
474 messages::actionParameterNotSupported(res, "TransferProtocol",
475 *transferProtocol);
476 BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
477 *transferProtocol);
478 return std::nullopt;
479 }
Ed Tanousf86bcc82023-08-25 09:34:07 -0700480 }
481
482 boost::system::result<boost::urls::url> url =
483 boost::urls::parse_absolute_uri(imageURI);
484 if (!url)
485 {
486 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
487 "UpdateService.SimpleUpdate");
488
489 return std::nullopt;
490 }
491 url->normalize();
492
Ed Tanous757178a2024-04-03 14:32:38 -0700493 if (url->scheme() == "tftp")
494 {
495 if (url->encoded_path().size() < 2)
496 {
497 messages::actionParameterNotSupported(res, "ImageURI",
498 url->buffer());
499 return std::nullopt;
500 }
501 }
502 else
Ed Tanousf86bcc82023-08-25 09:34:07 -0700503 {
504 messages::actionParameterNotSupported(res, "ImageURI", imageURI);
505 return std::nullopt;
506 }
Ed Tanous757178a2024-04-03 14:32:38 -0700507
508 if (url->encoded_path().empty())
Ed Tanousf86bcc82023-08-25 09:34:07 -0700509 {
Ed Tanous757178a2024-04-03 14:32:38 -0700510 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
511 "UpdateService.SimpleUpdate");
Ed Tanousf86bcc82023-08-25 09:34:07 -0700512 return std::nullopt;
513 }
Ed Tanous757178a2024-04-03 14:32:38 -0700514
515 return *url;
Ed Tanousf86bcc82023-08-25 09:34:07 -0700516}
517
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700518inline void doTftpUpdate(const crow::Request& req,
519 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous757178a2024-04-03 14:32:38 -0700520 const boost::urls::url_view_base& url)
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700521{
522#ifndef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
523 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
Ed Tanous757178a2024-04-03 14:32:38 -0700524 url.buffer());
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700525 return;
526#endif
Ed Tanous757178a2024-04-03 14:32:38 -0700527
528 std::string path(url.encoded_path());
529 if (path.size() < 2)
530 {
531 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
532 url.buffer());
533 return;
534 }
535 // TFTP expects a path without a /
536 path.erase(0, 1);
537 std::string host(url.encoded_host_and_port());
538 BMCWEB_LOG_DEBUG("Server: {} File: {}", host, path);
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700539
540 // Setup callback for when new software detected
541 // Give TFTP 10 minutes to complete
542 monitorForSoftwareAvailable(
543 asyncResp, req,
544 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate", 600);
545
546 // TFTP can take up to 10 minutes depending on image size and
547 // connection speed. Return to caller as soon as the TFTP operation
548 // has been started. The callback above will ensure the activate
549 // is started once the download has completed
550 redfish::messages::success(asyncResp->res);
551
552 // Call TFTP service
553 crow::connections::systemBus->async_method_call(
554 [](const boost::system::error_code& ec) {
555 if (ec)
556 {
557 // messages::internalError(asyncResp->res);
558 cleanUp();
559 BMCWEB_LOG_DEBUG("error_code = {}", ec);
560 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
561 }
562 else
563 {
564 BMCWEB_LOG_DEBUG("Call to DownloaViaTFTP Success");
565 }
566 },
567 "xyz.openbmc_project.Software.Download",
568 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
Ed Tanous757178a2024-04-03 14:32:38 -0700569 "DownloadViaTFTP", path, host);
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700570}
571
Ed Tanousf5139332024-04-03 13:25:04 -0700572inline void handleUpdateServiceSimpleUpdateAction(
573 crow::App& app, const crow::Request& req,
574 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Andrew Geissler0554c982019-04-23 14:40:12 -0500575{
Ed Tanousf5139332024-04-03 13:25:04 -0700576 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
577 {
578 return;
579 }
580
581 std::optional<std::string> transferProtocol;
582 std::string imageURI;
583
584 BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
585
586 // User can pass in both TransferProtocol and ImageURI parameters or
587 // they can pass in just the ImageURI with the transfer protocol
588 // embedded within it.
589 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
590 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
591
592 if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
593 transferProtocol, "ImageURI", imageURI))
594 {
595 BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
596 return;
597 }
598
Ed Tanous757178a2024-04-03 14:32:38 -0700599 std::optional<boost::urls::url> url =
600 parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res);
601 if (!url)
Ed Tanousf5139332024-04-03 13:25:04 -0700602 {
603 return;
604 }
Ed Tanous757178a2024-04-03 14:32:38 -0700605 if (url->scheme() == "tftp")
606 {
607 doTftpUpdate(req, asyncResp, *url);
608 }
609 else
610 {
611 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
612 url->buffer());
613 return;
614 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700615
Ed Tanousf5139332024-04-03 13:25:04 -0700616 BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700617}
618
George Liu0ed80c82020-05-12 16:06:27 +0800619inline void uploadImageFile(crow::Response& res, std::string_view body)
620{
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700621 std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
622
Ed Tanous62598e32023-07-17 17:06:25 -0700623 BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
George Liu0ed80c82020-05-12 16:06:27 +0800624 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
625 std::ofstream::trunc);
626 // set the permission of the file to 640
Patrick Williams89492a12023-05-10 07:51:34 -0500627 std::filesystem::perms permission = std::filesystem::perms::owner_read |
628 std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800629 std::filesystem::permissions(filepath, permission);
630 out << body;
631
632 if (out.bad())
633 {
634 messages::internalError(res);
635 cleanUp();
636 }
637}
638
639inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
640 const std::string& applyTime)
641{
642 std::string applyTimeNewVal;
643 if (applyTime == "Immediate")
644 {
645 applyTimeNewVal =
646 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
647 }
648 else if (applyTime == "OnReset")
649 {
650 applyTimeNewVal =
651 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
652 }
653 else
654 {
Ed Tanous62598e32023-07-17 17:06:25 -0700655 BMCWEB_LOG_INFO(
656 "ApplyTime value is not in the list of acceptable values");
George Liu0ed80c82020-05-12 16:06:27 +0800657 messages::propertyValueNotInList(asyncResp->res, applyTime,
658 "ApplyTime");
659 return;
660 }
661
Ed Tanousd02aad32024-02-13 14:43:34 -0800662 setDbusProperty(asyncResp, "xyz.openbmc_project.Settings",
663 sdbusplus::message::object_path(
664 "/xyz/openbmc_project/software/apply_time"),
665 "xyz.openbmc_project.Software.ApplyTime",
666 "RequestedApplyTime", "ApplyTime", applyTimeNewVal);
George Liu0ed80c82020-05-12 16:06:27 +0800667}
668
669inline void
670 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous6b54e4e2024-04-10 08:58:48 -0700671 const crow::Request& req,
George Liu0ed80c82020-05-12 16:06:27 +0800672 const MultipartParser& parser)
673{
674 const std::string* uploadData = nullptr;
675 std::optional<std::string> applyTime = "OnReset";
676 bool targetFound = false;
677 for (const FormPart& formpart : parser.mime_fields)
678 {
679 boost::beast::http::fields::const_iterator it =
680 formpart.fields.find("Content-Disposition");
681 if (it == formpart.fields.end())
682 {
Ed Tanous62598e32023-07-17 17:06:25 -0700683 BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
George Liu0ed80c82020-05-12 16:06:27 +0800684 return;
685 }
Ed Tanous62598e32023-07-17 17:06:25 -0700686 BMCWEB_LOG_INFO("Parsing value {}", it->value());
George Liu0ed80c82020-05-12 16:06:27 +0800687
688 // The construction parameters of param_list must start with `;`
689 size_t index = it->value().find(';');
690 if (index == std::string::npos)
691 {
692 continue;
693 }
694
Patrick Williams89492a12023-05-10 07:51:34 -0500695 for (const auto& param :
George Liu0ed80c82020-05-12 16:06:27 +0800696 boost::beast::http::param_list{it->value().substr(index)})
697 {
698 if (param.first != "name" || param.second.empty())
699 {
700 continue;
701 }
702
703 if (param.second == "UpdateParameters")
704 {
705 std::vector<std::string> targets;
706 nlohmann::json content =
707 nlohmann::json::parse(formpart.content);
Ed Tanous7cb59f62022-05-05 11:48:31 -0700708 nlohmann::json::object_t* obj =
709 content.get_ptr<nlohmann::json::object_t*>();
710 if (obj == nullptr)
711 {
712 messages::propertyValueFormatError(asyncResp->res, targets,
713 "UpdateParameters");
714 return;
715 }
716
717 if (!json_util::readJsonObject(
718 *obj, asyncResp->res, "Targets", targets,
719 "@Redfish.OperationApplyTime", applyTime))
George Liu0ed80c82020-05-12 16:06:27 +0800720 {
721 return;
722 }
723 if (targets.size() != 1)
724 {
Ed Tanous7cb59f62022-05-05 11:48:31 -0700725 messages::propertyValueFormatError(asyncResp->res, targets,
726 "Targets");
George Liu0ed80c82020-05-12 16:06:27 +0800727 return;
728 }
729 if (targets[0] != "/redfish/v1/Managers/bmc")
730 {
Ed Tanous7cb59f62022-05-05 11:48:31 -0700731 messages::propertyValueNotInList(asyncResp->res, targets[0],
732 "Targets/0");
George Liu0ed80c82020-05-12 16:06:27 +0800733 return;
734 }
735 targetFound = true;
736 }
737 else if (param.second == "UpdateFile")
738 {
739 uploadData = &(formpart.content);
740 }
741 }
742 }
743
744 if (uploadData == nullptr)
745 {
Ed Tanous62598e32023-07-17 17:06:25 -0700746 BMCWEB_LOG_ERROR("Upload data is NULL");
George Liu0ed80c82020-05-12 16:06:27 +0800747 messages::propertyMissing(asyncResp->res, "UpdateFile");
748 return;
749 }
750 if (!targetFound)
751 {
752 messages::propertyMissing(asyncResp->res, "targets");
753 return;
754 }
755
756 setApplyTime(asyncResp, *applyTime);
757
Ed Tanous6b54e4e2024-04-10 08:58:48 -0700758 // Setup callback for when new software detected
759 monitorForSoftwareAvailable(asyncResp, req, "/redfish/v1/UpdateService");
760
George Liu0ed80c82020-05-12 16:06:27 +0800761 uploadImageFile(asyncResp->res, *uploadData);
762}
763
Ed Tanousc2051d12022-05-11 12:21:55 -0700764inline void
765 handleUpdateServicePost(App& app, const crow::Request& req,
766 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
767{
Carson Labrado3ba00072022-06-06 19:40:56 +0000768 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700769 {
770 return;
771 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500772 std::string_view contentType = req.getHeaderValue("Content-Type");
Ed Tanousc2051d12022-05-11 12:21:55 -0700773
Ed Tanous62598e32023-07-17 17:06:25 -0700774 BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
Ed Tanousc2051d12022-05-11 12:21:55 -0700775
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500776 // Make sure that content type is application/octet-stream or
777 // multipart/form-data
Ed Tanous18f8f602023-07-18 10:07:23 -0700778 if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
George Liu0ed80c82020-05-12 16:06:27 +0800779 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500780 // Setup callback for when new software detected
781 monitorForSoftwareAvailable(asyncResp, req,
782 "/redfish/v1/UpdateService");
783
George Liu0ed80c82020-05-12 16:06:27 +0800784 uploadImageFile(asyncResp->res, req.body());
George Liu0ed80c82020-05-12 16:06:27 +0800785 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500786 else if (contentType.starts_with("multipart/form-data"))
George Liu0ed80c82020-05-12 16:06:27 +0800787 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500788 MultipartParser parser;
789
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500790 ParserError ec = parser.parse(req);
791 if (ec != ParserError::PARSER_SUCCESS)
792 {
793 // handle error
Ed Tanous62598e32023-07-17 17:06:25 -0700794 BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
795 static_cast<int>(ec));
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500796 messages::internalError(asyncResp->res);
797 return;
798 }
Ed Tanous6b54e4e2024-04-10 08:58:48 -0700799
800 updateMultipartContext(asyncResp, req, parser);
George Liu0ed80c82020-05-12 16:06:27 +0800801 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500802 else
803 {
Ed Tanous62598e32023-07-17 17:06:25 -0700804 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500805 asyncResp->res.result(boost::beast::http::status::bad_request);
806 }
Ed Tanousc2051d12022-05-11 12:21:55 -0700807}
808
Ed Tanousf5139332024-04-03 13:25:04 -0700809inline void
810 handleUpdateServiceGet(App& app, const crow::Request& req,
811 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700812{
Ed Tanousf5139332024-04-03 13:25:04 -0700813 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
814 {
815 return;
816 }
817 asyncResp->res.jsonValue["@odata.type"] =
818 "#UpdateService.v1_11_1.UpdateService";
819 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
820 asyncResp->res.jsonValue["Id"] = "UpdateService";
821 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
822 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700823
Ed Tanousf5139332024-04-03 13:25:04 -0700824 asyncResp->res.jsonValue["HttpPushUri"] =
825 "/redfish/v1/UpdateService/update";
826 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
827 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700828
Ed Tanousf5139332024-04-03 13:25:04 -0700829 // UpdateService cannot be disabled
830 asyncResp->res.jsonValue["ServiceEnabled"] = true;
831 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
832 "/redfish/v1/UpdateService/FirmwareInventory";
833 // Get the MaxImageSizeBytes
Ed Tanous25b54db2024-04-17 15:40:31 -0700834 asyncResp->res.jsonValue["MaxImageSizeBytes"] = BMCWEB_HTTP_BODY_LIMIT *
Ed Tanousf5139332024-04-03 13:25:04 -0700835 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530836
Ed Tanousf5139332024-04-03 13:25:04 -0700837 // Update Actions object.
838 nlohmann::json& updateSvcSimpleUpdate =
839 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
840 updateSvcSimpleUpdate["target"] =
841 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
Ed Tanous757178a2024-04-03 14:32:38 -0700842
843 nlohmann::json::array_t allowed;
844
Ed Tanous25b54db2024-04-17 15:40:31 -0700845 if constexpr (BMCWEB_INSECURE_PUSH_STYLE_NOTIFICATION)
846 {
847 allowed.emplace_back(update_service::TransferProtocolType::TFTP);
848 }
Ed Tanous757178a2024-04-03 14:32:38 -0700849
850 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
851 std::move(allowed);
852
Ed Tanousf5139332024-04-03 13:25:04 -0700853 // Get the current ApplyTime value
854 sdbusplus::asio::getProperty<std::string>(
855 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
856 "/xyz/openbmc_project/software/apply_time",
857 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
858 [asyncResp](const boost::system::error_code& ec,
859 const std::string& applyTime) {
860 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700861 {
Ed Tanousf5139332024-04-03 13:25:04 -0700862 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
863 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -0700864 return;
865 }
866
Ed Tanousf5139332024-04-03 13:25:04 -0700867 // Store the ApplyTime Value
868 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
869 "RequestedApplyTimes.Immediate")
Ed Tanous002d39b2022-05-31 08:59:27 -0700870 {
Ed Tanousf5139332024-04-03 13:25:04 -0700871 asyncResp->res.jsonValue["HttpPushUriOptions"]
872 ["HttpPushUriApplyTime"]["ApplyTime"] =
873 "Immediate";
874 }
875 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
876 "RequestedApplyTimes.OnReset")
877 {
878 asyncResp->res.jsonValue["HttpPushUriOptions"]
879 ["HttpPushUriApplyTime"]["ApplyTime"] =
880 "OnReset";
Ed Tanous002d39b2022-05-31 08:59:27 -0700881 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500882 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700883}
884
Ed Tanousf5139332024-04-03 13:25:04 -0700885inline void handleUpdateServicePatch(
886 App& app, const crow::Request& req,
887 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700888{
Ed Tanousf5139332024-04-03 13:25:04 -0700889 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
890 {
891 return;
892 }
893 BMCWEB_LOG_DEBUG("doPatch...");
Ed Tanous002d39b2022-05-31 08:59:27 -0700894
Ed Tanousf5139332024-04-03 13:25:04 -0700895 std::optional<std::string> applyTime;
896 if (!json_util::readJsonPatch(
897 req, asyncResp->res,
898 "HttpPushUriOptions/HttpPushUriApplyTime/ApplyTime", applyTime))
899 {
900 return;
901 }
902
903 if (applyTime)
904 {
905 setApplyTime(asyncResp, *applyTime);
906 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700907}
Ed Tanousf5139332024-04-03 13:25:04 -0700908
909inline void handleUpdateServiceFirmwareInventoryCollectionGet(
910 App& app, const crow::Request& req,
911 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
912{
913 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
914 {
915 return;
916 }
917 asyncResp->res.jsonValue["@odata.type"] =
918 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
919 asyncResp->res.jsonValue["@odata.id"] =
920 "/redfish/v1/UpdateService/FirmwareInventory";
921 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
922 const std::array<const std::string_view, 1> iface = {
923 "xyz.openbmc_project.Software.Version"};
924
925 redfish::collection_util::getCollectionMembers(
926 asyncResp,
927 boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface,
928 "/xyz/openbmc_project/software");
929}
930
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700931/* Fill related item links (i.e. bmc, bios) in for inventory */
Ed Tanousf5139332024-04-03 13:25:04 -0700932inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
933 const std::string& purpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700934{
Willy Tueee00132022-06-14 14:53:17 -0700935 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700936 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700937 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700938 nlohmann::json::object_t item;
939 item["@odata.id"] = "/redfish/v1/Managers/bmc";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500940 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700941 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
942 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700943 }
Willy Tueee00132022-06-14 14:53:17 -0700944 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700945 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700946 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700947 nlohmann::json::object_t item;
948 item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500949 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700950 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
951 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700952 }
953 else
954 {
Carson Labradobf2dded2023-08-10 00:37:06 +0000955 BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700956 }
957}
958
Willy Tuaf246602022-06-14 15:51:53 -0700959inline void
960 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
961 const std::string& service, const std::string& path,
962 const std::string& swId)
963{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200964 sdbusplus::asio::getAllProperties(
965 *crow::connections::systemBus, service, path,
966 "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -0700967 [asyncResp,
Ed Tanous8b242752023-06-27 17:17:13 -0700968 swId](const boost::system::error_code& ec,
Willy Tuaf246602022-06-14 15:51:53 -0700969 const dbus::utility::DBusPropertiesMap& propertiesList) {
Ed Tanous8b242752023-06-27 17:17:13 -0700970 if (ec)
Willy Tuaf246602022-06-14 15:51:53 -0700971 {
972 messages::internalError(asyncResp->res);
973 return;
974 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200975
Willy Tuaf246602022-06-14 15:51:53 -0700976 const std::string* swInvPurpose = nullptr;
977 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200978
979 const bool success = sdbusplus::unpackPropertiesNoThrow(
980 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
981 swInvPurpose, "Version", version);
982
983 if (!success)
Willy Tuaf246602022-06-14 15:51:53 -0700984 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200985 messages::internalError(asyncResp->res);
986 return;
Willy Tuaf246602022-06-14 15:51:53 -0700987 }
988
989 if (swInvPurpose == nullptr)
990 {
Ed Tanous62598e32023-07-17 17:06:25 -0700991 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
Willy Tuaf246602022-06-14 15:51:53 -0700992 messages::internalError(asyncResp->res);
993 return;
994 }
995
Ed Tanous62598e32023-07-17 17:06:25 -0700996 BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
Willy Tuaf246602022-06-14 15:51:53 -0700997
998 if (version == nullptr)
999 {
Ed Tanous62598e32023-07-17 17:06:25 -07001000 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
Willy Tuaf246602022-06-14 15:51:53 -07001001
1002 messages::internalError(asyncResp->res);
1003
1004 return;
1005 }
1006 asyncResp->res.jsonValue["Version"] = *version;
1007 asyncResp->res.jsonValue["Id"] = swId;
1008
1009 // swInvPurpose is of format:
1010 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
1011 // Translate this to "ABC image"
1012 size_t endDesc = swInvPurpose->rfind('.');
1013 if (endDesc == std::string::npos)
1014 {
1015 messages::internalError(asyncResp->res);
1016 return;
1017 }
1018 endDesc++;
1019 if (endDesc >= swInvPurpose->size())
1020 {
1021 messages::internalError(asyncResp->res);
1022 return;
1023 }
1024
1025 std::string formatDesc = swInvPurpose->substr(endDesc);
1026 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1027 getRelatedItems(asyncResp, *swInvPurpose);
Patrick Williams5a39f772023-10-20 11:20:21 -05001028 });
Willy Tuaf246602022-06-14 15:51:53 -07001029}
1030
Ed Tanousf5139332024-04-03 13:25:04 -07001031inline void handleUpdateServiceFirmwareInventoryGet(
1032 App& app, const crow::Request& req,
1033 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1034 const std::string& param)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001035{
Ed Tanousf5139332024-04-03 13:25:04 -07001036 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1037 {
1038 return;
1039 }
1040 std::shared_ptr<std::string> swId = std::make_shared<std::string>(param);
1041
1042 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1043 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
1044
1045 constexpr std::array<std::string_view, 1> interfaces = {
1046 "xyz.openbmc_project.Software.Version"};
1047 dbus::utility::getSubTree(
1048 "/", 0, interfaces,
1049 [asyncResp,
1050 swId](const boost::system::error_code& ec,
1051 const dbus::utility::MapperGetSubTreeResponse& subtree) {
1052 BMCWEB_LOG_DEBUG("doGet callback...");
1053 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001054 {
Ed Tanousf5139332024-04-03 13:25:04 -07001055 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001056 return;
1057 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001058
Ed Tanousf5139332024-04-03 13:25:04 -07001059 // Ensure we find our input swId, otherwise return an error
1060 bool found = false;
1061 for (const std::pair<
1062 std::string,
1063 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1064 obj : subtree)
1065 {
1066 if (!obj.first.ends_with(*swId))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001067 {
Ed Tanousf5139332024-04-03 13:25:04 -07001068 continue;
Ed Tanous45ca1b82022-03-25 13:07:27 -07001069 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001070
Ed Tanousf5139332024-04-03 13:25:04 -07001071 if (obj.second.empty())
Ed Tanous002d39b2022-05-31 08:59:27 -07001072 {
Ed Tanousf5139332024-04-03 13:25:04 -07001073 continue;
Ed Tanous002d39b2022-05-31 08:59:27 -07001074 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001075
Ed Tanousf5139332024-04-03 13:25:04 -07001076 found = true;
1077 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1078 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1079 *swId);
1080 }
1081 if (!found)
1082 {
1083 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
1084 messages::resourceMissingAtURI(
1085 asyncResp->res,
1086 boost::urls::format(
1087 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId));
1088 return;
1089 }
1090 asyncResp->res.jsonValue["@odata.type"] =
1091 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1092 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1093 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
1094
1095 asyncResp->res.jsonValue["Updateable"] = false;
1096 sw_util::getSwUpdatableStatus(asyncResp, swId);
Patrick Williams5a39f772023-10-20 11:20:21 -05001097 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001098}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001099
Ed Tanousf5139332024-04-03 13:25:04 -07001100inline void requestRoutesUpdateService(App& app)
1101{
1102 BMCWEB_ROUTE(
1103 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
1104 .privileges(redfish::privileges::postUpdateService)
1105 .methods(boost::beast::http::verb::post)(std::bind_front(
1106 handleUpdateServiceSimpleUpdateAction, std::ref(app)));
1107
1108 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1109 .privileges(redfish::privileges::getSoftwareInventory)
1110 .methods(boost::beast::http::verb::get)(std::bind_front(
1111 handleUpdateServiceFirmwareInventoryGet, std::ref(app)));
1112
1113 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1114 .privileges(redfish::privileges::getUpdateService)
1115 .methods(boost::beast::http::verb::get)(
1116 std::bind_front(handleUpdateServiceGet, std::ref(app)));
1117
1118 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1119 .privileges(redfish::privileges::patchUpdateService)
1120 .methods(boost::beast::http::verb::patch)(
1121 std::bind_front(handleUpdateServicePatch, std::ref(app)));
1122
1123 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
1124 .privileges(redfish::privileges::postUpdateService)
1125 .methods(boost::beast::http::verb::post)(
1126 std::bind_front(handleUpdateServicePost, std::ref(app)));
1127
1128 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
1129 .privileges(redfish::privileges::getSoftwareInventoryCollection)
1130 .methods(boost::beast::http::verb::get)(std::bind_front(
1131 handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app)));
1132}
1133
Ed Tanous1abe55e2018-09-05 08:30:59 -07001134} // namespace redfish