blob: df4043f36982bab108d60a301d925c90603a3bf3 [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 Tanous757178a2024-04-03 14:32:38 -070022#include "generated/enums/update_service.hpp"
George Liu0ed80c82020-05-12 16:06:27 +080023#include "multipart_parser.hpp"
Ed Tanous2c6ffdb2023-06-28 11:28:38 -070024#include "ossl_random.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080025#include "query.hpp"
26#include "registries/privilege_registry.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080027#include "task.hpp"
John Edward Broadbent08d81ad2022-05-17 20:00:23 -070028#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080029#include "utils/dbus_utils.hpp"
30#include "utils/sw_utils.hpp"
31
George Liue99073f2022-12-09 11:06:16 +080032#include <boost/system/error_code.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070033#include <boost/url/format.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070034#include <sdbusplus/asio/property.hpp>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080035#include <sdbusplus/bus/match.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020036#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050037
George Liu2b731192023-01-11 16:27:13 +080038#include <array>
George Liu0ed80c82020-05-12 16:06:27 +080039#include <filesystem>
Ed Tanous7cb59f62022-05-05 11:48:31 -070040#include <optional>
41#include <string>
George Liu2b731192023-01-11 16:27:13 +080042#include <string_view>
43
Ed Tanous1abe55e2018-09-05 08:30:59 -070044namespace redfish
45{
Ed Tanous27826b52018-10-29 11:40:58 -070046
Andrew Geissler0e7de462019-03-04 19:11:54 -060047// Match signals added on software path
Ed Tanouscf9e4172022-12-21 09:30:16 -080048// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050049static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
Ed Tanouscf9e4172022-12-21 09:30:16 -080050// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050051static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060052// Only allow one update at a time
Ed Tanouscf9e4172022-12-21 09:30:16 -080053// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Andrew Geissler0e7de462019-03-04 19:11:54 -060054static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050055// Timer for software available
Ed Tanouscf9e4172022-12-21 09:30:16 -080056// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous271584a2019-07-09 16:24:22 -070057static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050058
Ed Tanousdf254f22024-04-01 13:25:46 -070059inline void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050060{
61 fwUpdateInProgress = false;
62 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070063 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050064}
Ed Tanousdf254f22024-04-01 13:25:46 -070065
66inline void activateImage(const std::string& objPath,
67 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050068{
Ed Tanous62598e32023-07-17 17:06:25 -070069 BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
George Liu9ae226f2023-06-21 17:56:46 +080070 sdbusplus::asio::setProperty(
71 *crow::connections::systemBus, service, objPath,
72 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
73 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
Ed Tanous8b242752023-06-27 17:17:13 -070074 [](const boost::system::error_code& ec) {
75 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -070076 {
Ed Tanous62598e32023-07-17 17:06:25 -070077 BMCWEB_LOG_DEBUG("error_code = {}", ec);
78 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -070079 }
Patrick Williams5a39f772023-10-20 11:20:21 -050080 });
Andrew Geissler86adcd62019-04-18 10:58:05 -050081}
Andrew Geissler0554c982019-04-23 14:40:12 -050082
83// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
84// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080085static void
86 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -050087 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050088{
Michael Shen80f79a42023-08-24 13:41:53 +000089 dbus::utility::DBusInterfacesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -050090
91 sdbusplus::message::object_path objPath;
92
93 m.read(objPath, interfacesProperties);
94
Ed Tanous62598e32023-07-17 17:06:25 -070095 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
Ed Tanouse3eb3d62022-12-21 11:56:07 -080096 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050097 {
Ed Tanous62598e32023-07-17 17:06:25 -070098 BMCWEB_LOG_DEBUG("interface = {}", interface.first);
Andrew Geissler86adcd62019-04-18 10:58:05 -050099
100 if (interface.first == "xyz.openbmc_project.Software.Activation")
101 {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500102 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +0800103 constexpr std::array<std::string_view, 1> interfaces = {
104 "xyz.openbmc_project.Software.Activation"};
105 dbus::utility::getDbusObject(
106 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700107 [objPath, asyncResp, payload(std::move(payload))](
Ed Tanous8b242752023-06-27 17:17:13 -0700108 const boost::system::error_code& ec,
Ed Tanousa3e65892021-09-16 14:13:20 -0700109 const std::vector<
110 std::pair<std::string, std::vector<std::string>>>&
111 objInfo) mutable {
Ed Tanous8b242752023-06-27 17:17:13 -0700112 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700113 {
Ed Tanous62598e32023-07-17 17:06:25 -0700114 BMCWEB_LOG_DEBUG("error_code = {}", ec);
115 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Andrew Geissler0554c982019-04-23 14:40:12 -0500116 if (asyncResp)
117 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700118 messages::internalError(asyncResp->res);
119 }
120 cleanUp();
121 return;
122 }
123 // Ensure we only got one service back
124 if (objInfo.size() != 1)
125 {
Ed Tanous62598e32023-07-17 17:06:25 -0700126 BMCWEB_LOG_ERROR("Invalid Object Size {}", objInfo.size());
Ed Tanous002d39b2022-05-31 08:59:27 -0700127 if (asyncResp)
128 {
129 messages::internalError(asyncResp->res);
130 }
131 cleanUp();
132 return;
133 }
134 // cancel timer only when
135 // xyz.openbmc_project.Software.Activation interface
136 // is added
137 fwAvailableTimer = nullptr;
138
139 activateImage(objPath.str, objInfo[0].first);
140 if (asyncResp)
141 {
142 std::shared_ptr<task::TaskData> task =
143 task::TaskData::createTask(
Ed Tanous8b242752023-06-27 17:17:13 -0700144 [](const boost::system::error_code& ec2,
Patrick Williams59d494e2022-07-22 19:26:55 -0500145 sdbusplus::message_t& msg,
Ed Tanous002d39b2022-05-31 08:59:27 -0700146 const std::shared_ptr<task::TaskData>&
147 taskData) {
Ed Tanous8b242752023-06-27 17:17:13 -0700148 if (ec2)
Ed Tanous002d39b2022-05-31 08:59:27 -0700149 {
150 return task::completed;
151 }
152
153 std::string iface;
154 dbus::utility::DBusPropertiesMap values;
155
156 std::string index = std::to_string(taskData->index);
157 msg.read(iface, values);
158
159 if (iface == "xyz.openbmc_project.Software.Activation")
160 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000161 const std::string* state = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700162 for (const auto& property : values)
163 {
164 if (property.first == "Activation")
165 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000166 state = std::get_if<std::string>(
167 &property.second);
168 if (state == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700169 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700170 taskData->messages.emplace_back(
171 messages::internalError());
James Feist32898ce2020-03-10 16:16:52 -0700172 return task::completed;
173 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700174 }
175 }
James Feist32898ce2020-03-10 16:16:52 -0700176
Ed Tanous002d39b2022-05-31 08:59:27 -0700177 if (state == nullptr)
178 {
179 return !task::completed;
180 }
James Feist32898ce2020-03-10 16:16:52 -0700181
Ed Tanous11ba3972022-07-11 09:50:41 -0700182 if (state->ends_with("Invalid") ||
183 state->ends_with("Failed"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700184 {
185 taskData->state = "Exception";
186 taskData->status = "Warning";
187 taskData->messages.emplace_back(
188 messages::taskAborted(index));
189 return task::completed;
190 }
James Feiste5d50062020-05-11 17:29:00 -0700191
Ed Tanous11ba3972022-07-11 09:50:41 -0700192 if (state->ends_with("Staged"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700193 {
194 taskData->state = "Stopping";
195 taskData->messages.emplace_back(
196 messages::taskPaused(index));
197
198 // its staged, set a long timer to
199 // allow them time to complete the
200 // update (probably cycle the
201 // system) if this expires then
Ed Tanous8ece0e42024-01-02 13:16:50 -0800202 // task will be canceled
Ed Tanous002d39b2022-05-31 08:59:27 -0700203 taskData->extendTimer(std::chrono::hours(5));
204 return !task::completed;
205 }
206
Ed Tanous11ba3972022-07-11 09:50:41 -0700207 if (state->ends_with("Active"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700208 {
209 taskData->messages.emplace_back(
210 messages::taskCompletedOK(index));
211 taskData->state = "Completed";
212 return task::completed;
213 }
214 }
215 else if (
216 iface ==
217 "xyz.openbmc_project.Software.ActivationProgress")
218 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700219 const uint8_t* progress = nullptr;
220 for (const auto& property : values)
221 {
222 if (property.first == "Progress")
223 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000224 progress =
225 std::get_if<uint8_t>(&property.second);
226 if (progress == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700227 {
James Feist32898ce2020-03-10 16:16:52 -0700228 taskData->messages.emplace_back(
Ed Tanous002d39b2022-05-31 08:59:27 -0700229 messages::internalError());
230 return task::completed;
James Feist32898ce2020-03-10 16:16:52 -0700231 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700232 }
233 }
James Feist32898ce2020-03-10 16:16:52 -0700234
Ed Tanous002d39b2022-05-31 08:59:27 -0700235 if (progress == nullptr)
236 {
237 return !task::completed;
238 }
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000239 taskData->percentComplete = *progress;
Ed Tanous002d39b2022-05-31 08:59:27 -0700240 taskData->messages.emplace_back(
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000241 messages::taskProgressChanged(index,
242 *progress));
James Feist32898ce2020-03-10 16:16:52 -0700243
Ed Tanous002d39b2022-05-31 08:59:27 -0700244 // if we're getting status updates it's
245 // still alive, update timer
246 taskData->extendTimer(std::chrono::minutes(5));
247 }
248
249 // as firmware update often results in a
250 // reboot, the task may never "complete"
251 // unless it is an error
252
253 return !task::completed;
Patrick Williams5a39f772023-10-20 11:20:21 -0500254 },
Ed Tanous002d39b2022-05-31 08:59:27 -0700255 "type='signal',interface='org.freedesktop.DBus.Properties',"
256 "member='PropertiesChanged',path='" +
257 objPath.str + "'");
258 task->startTimer(std::chrono::minutes(5));
259 task->populateResp(asyncResp->res);
260 task->payload.emplace(std::move(payload));
261 }
262 fwUpdateInProgress = false;
Patrick Williams5a39f772023-10-20 11:20:21 -0500263 });
Patrick Williams62bafc02022-09-08 17:35:35 -0500264
265 break;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500266 }
267 }
268}
269
Myung Bae8549b952023-08-16 15:18:19 -0400270inline void afterAvailbleTimerAsyncWait(
271 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
272 const boost::system::error_code& ec)
273{
274 cleanUp();
275 if (ec == boost::asio::error::operation_aborted)
276 {
277 // expected, we were canceled before the timer completed.
278 return;
279 }
280 BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
281 BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
282 if (ec)
283 {
284 BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
285 return;
286 }
287 if (asyncResp)
288 {
289 redfish::messages::internalError(asyncResp->res);
290 }
291}
292
293inline void
294 handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
295 const std::string& url, const std::string& type)
296{
297 if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
298 {
299 redfish::messages::invalidUpload(asyncResp->res, url,
300 "Invalid archive");
301 }
302 else if (type ==
303 "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
304 {
305 redfish::messages::invalidUpload(asyncResp->res, url,
306 "Invalid manifest");
307 }
308 else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
309 {
310 redfish::messages::invalidUpload(asyncResp->res, url,
311 "Invalid image format");
312 }
313 else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
314 {
315 redfish::messages::invalidUpload(asyncResp->res, url,
316 "Image version already exists");
317
318 redfish::messages::resourceAlreadyExists(
319 asyncResp->res, "UpdateService", "Version", "uploaded version");
320 }
321 else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
322 {
323 redfish::messages::resourceExhaustion(asyncResp->res, url);
324 }
Myung Bae4034a652023-08-17 08:47:35 -0400325 else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
Myung Bae8549b952023-08-16 15:18:19 -0400326 {
Myung Bae4034a652023-08-17 08:47:35 -0400327 redfish::messages::invalidUpload(asyncResp->res, url,
328 "Incompatible image version");
329 }
330 else if (type ==
331 "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
332 {
333 redfish::messages::invalidUpload(asyncResp->res, url,
334 "Update Access Key Expired");
335 }
336 else if (type ==
337 "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
338 {
339 redfish::messages::invalidUpload(asyncResp->res, url,
340 "Invalid image signature");
341 }
342 else if (type ==
343 "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
344 type == "xyz.openbmc_project.Software.Version.Error.HostFile")
345 {
346 BMCWEB_LOG_ERROR("Software Image Error type={}", type);
Myung Bae8549b952023-08-16 15:18:19 -0400347 redfish::messages::internalError(asyncResp->res);
348 }
Myung Bae4034a652023-08-17 08:47:35 -0400349 else
350 {
351 // Unrelated error types. Ignored
352 BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
353 return;
354 }
355 // Clear the timer
356 fwAvailableTimer = nullptr;
Myung Bae8549b952023-08-16 15:18:19 -0400357}
358
359inline void
360 afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
361 const std::string& url, sdbusplus::message_t& m)
362{
Michael Shen80f79a42023-08-24 13:41:53 +0000363 dbus::utility::DBusInterfacesMap interfacesProperties;
Myung Bae8549b952023-08-16 15:18:19 -0400364 sdbusplus::message::object_path objPath;
365 m.read(objPath, interfacesProperties);
366 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
367 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
368 interface : interfacesProperties)
369 {
370 if (interface.first == "xyz.openbmc_project.Logging.Entry")
371 {
372 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
373 value : interface.second)
374 {
375 if (value.first != "Message")
376 {
377 continue;
378 }
379 const std::string* type =
380 std::get_if<std::string>(&value.second);
381 if (type == nullptr)
382 {
383 // if this was our message, timeout will cover it
384 return;
385 }
Myung Bae8549b952023-08-16 15:18:19 -0400386 handleUpdateErrorType(asyncResp, url, *type);
387 }
388 }
389 }
390}
391
Andrew Geissler0554c982019-04-23 14:40:12 -0500392// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
393// then no asyncResp updates will occur
Ed Tanousf5139332024-04-03 13:25:04 -0700394inline void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800395 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
396 const crow::Request& req, const std::string& url,
Gunnar Mills5d138942022-09-07 10:26:21 -0500397 int timeoutTimeSeconds = 25)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500398{
399 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800400 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500401 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500402 if (asyncResp)
403 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500404 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
405 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500406 return;
407 }
408
Ed Tanous8e8245d2024-04-11 22:21:38 -0700409 if (req.ioService == nullptr)
410 {
411 messages::internalError(asyncResp->res);
412 return;
413 }
414
Andrew Geissler0554c982019-04-23 14:40:12 -0500415 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700416 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500417
Ed Tanous271584a2019-07-09 16:24:22 -0700418 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500419
420 fwAvailableTimer->async_wait(
Myung Bae8549b952023-08-16 15:18:19 -0400421 std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
422
Ed Tanousa3e65892021-09-16 14:13:20 -0700423 task::Payload payload(req);
Patrick Williams59d494e2022-07-22 19:26:55 -0500424 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
Ed Tanous62598e32023-07-17 17:06:25 -0700425 BMCWEB_LOG_DEBUG("Match fired");
Ed Tanousa3e65892021-09-16 14:13:20 -0700426 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500427 };
428
429 fwUpdateInProgress = true;
430
Patrick Williams59d494e2022-07-22 19:26:55 -0500431 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Andrew Geissler86adcd62019-04-18 10:58:05 -0500432 *crow::connections::systemBus,
433 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
434 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
435 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700436
Patrick Williams59d494e2022-07-22 19:26:55 -0500437 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
James Feist4cde5d92020-06-11 10:39:55 -0700438 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800439 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
440 "member='InterfacesAdded',"
441 "path='/xyz/openbmc_project/logging'",
Myung Bae8549b952023-08-16 15:18:19 -0400442 std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500443}
Jennifer Lee729dae72018-04-24 15:59:34 -0700444
Ed Tanous757178a2024-04-03 14:32:38 -0700445inline std::optional<boost::urls::url>
446 parseSimpleUpdateUrl(std::string imageURI,
447 std::optional<std::string> transferProtocol,
448 crow::Response& res)
Ed Tanousf86bcc82023-08-25 09:34:07 -0700449{
450 if (imageURI.find("://") == std::string::npos)
451 {
452 if (imageURI.starts_with("/"))
453 {
454 messages::actionParameterValueTypeError(
455 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
456 return std::nullopt;
457 }
458 if (!transferProtocol)
459 {
460 messages::actionParameterValueTypeError(
461 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
462 return std::nullopt;
463 }
464 // OpenBMC currently only supports TFTP
Ed Tanous757178a2024-04-03 14:32:38 -0700465 if (*transferProtocol == "TFTP")
466 {
467 imageURI = "tftp://" + imageURI;
468 }
469 else
Ed Tanousf86bcc82023-08-25 09:34:07 -0700470 {
471 messages::actionParameterNotSupported(res, "TransferProtocol",
472 *transferProtocol);
473 BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
474 *transferProtocol);
475 return std::nullopt;
476 }
Ed Tanousf86bcc82023-08-25 09:34:07 -0700477 }
478
479 boost::system::result<boost::urls::url> url =
480 boost::urls::parse_absolute_uri(imageURI);
481 if (!url)
482 {
483 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
484 "UpdateService.SimpleUpdate");
485
486 return std::nullopt;
487 }
488 url->normalize();
489
Ed Tanous757178a2024-04-03 14:32:38 -0700490 if (url->scheme() == "tftp")
491 {
492 if (url->encoded_path().size() < 2)
493 {
494 messages::actionParameterNotSupported(res, "ImageURI",
495 url->buffer());
496 return std::nullopt;
497 }
498 }
499 else
Ed Tanousf86bcc82023-08-25 09:34:07 -0700500 {
501 messages::actionParameterNotSupported(res, "ImageURI", imageURI);
502 return std::nullopt;
503 }
Ed Tanous757178a2024-04-03 14:32:38 -0700504
505 if (url->encoded_path().empty())
Ed Tanousf86bcc82023-08-25 09:34:07 -0700506 {
Ed Tanous757178a2024-04-03 14:32:38 -0700507 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
508 "UpdateService.SimpleUpdate");
Ed Tanousf86bcc82023-08-25 09:34:07 -0700509 return std::nullopt;
510 }
Ed Tanous757178a2024-04-03 14:32:38 -0700511
512 return *url;
Ed Tanousf86bcc82023-08-25 09:34:07 -0700513}
514
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700515inline void doTftpUpdate(const crow::Request& req,
516 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous757178a2024-04-03 14:32:38 -0700517 const boost::urls::url_view_base& url)
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700518{
519#ifndef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
520 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
Ed Tanous757178a2024-04-03 14:32:38 -0700521 url.buffer());
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700522 return;
523#endif
Ed Tanous757178a2024-04-03 14:32:38 -0700524
525 std::string path(url.encoded_path());
526 if (path.size() < 2)
527 {
528 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
529 url.buffer());
530 return;
531 }
532 // TFTP expects a path without a /
533 path.erase(0, 1);
534 std::string host(url.encoded_host_and_port());
535 BMCWEB_LOG_DEBUG("Server: {} File: {}", host, path);
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700536
537 // Setup callback for when new software detected
538 // Give TFTP 10 minutes to complete
539 monitorForSoftwareAvailable(
540 asyncResp, req,
541 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate", 600);
542
543 // TFTP can take up to 10 minutes depending on image size and
544 // connection speed. Return to caller as soon as the TFTP operation
545 // has been started. The callback above will ensure the activate
546 // is started once the download has completed
547 redfish::messages::success(asyncResp->res);
548
549 // Call TFTP service
550 crow::connections::systemBus->async_method_call(
551 [](const boost::system::error_code& ec) {
552 if (ec)
553 {
554 // messages::internalError(asyncResp->res);
555 cleanUp();
556 BMCWEB_LOG_DEBUG("error_code = {}", ec);
557 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
558 }
559 else
560 {
561 BMCWEB_LOG_DEBUG("Call to DownloaViaTFTP Success");
562 }
563 },
564 "xyz.openbmc_project.Software.Download",
565 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
Ed Tanous757178a2024-04-03 14:32:38 -0700566 "DownloadViaTFTP", path, host);
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700567}
568
Ed Tanousf5139332024-04-03 13:25:04 -0700569inline void handleUpdateServiceSimpleUpdateAction(
570 crow::App& app, const crow::Request& req,
571 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Andrew Geissler0554c982019-04-23 14:40:12 -0500572{
Ed Tanousf5139332024-04-03 13:25:04 -0700573 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
574 {
575 return;
576 }
577
578 std::optional<std::string> transferProtocol;
579 std::string imageURI;
580
581 BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
582
583 // User can pass in both TransferProtocol and ImageURI parameters or
584 // they can pass in just the ImageURI with the transfer protocol
585 // embedded within it.
586 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
587 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
588
589 if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
590 transferProtocol, "ImageURI", imageURI))
591 {
592 BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
593 return;
594 }
595
Ed Tanous757178a2024-04-03 14:32:38 -0700596 std::optional<boost::urls::url> url =
597 parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res);
598 if (!url)
Ed Tanousf5139332024-04-03 13:25:04 -0700599 {
600 return;
601 }
Ed Tanous757178a2024-04-03 14:32:38 -0700602 if (url->scheme() == "tftp")
603 {
604 doTftpUpdate(req, asyncResp, *url);
605 }
606 else
607 {
608 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
609 url->buffer());
610 return;
611 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700612
Ed Tanousf5139332024-04-03 13:25:04 -0700613 BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700614}
615
George Liu0ed80c82020-05-12 16:06:27 +0800616inline void uploadImageFile(crow::Response& res, std::string_view body)
617{
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700618 std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
619
Ed Tanous62598e32023-07-17 17:06:25 -0700620 BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
George Liu0ed80c82020-05-12 16:06:27 +0800621 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
622 std::ofstream::trunc);
623 // set the permission of the file to 640
Patrick Williams89492a12023-05-10 07:51:34 -0500624 std::filesystem::perms permission = std::filesystem::perms::owner_read |
625 std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800626 std::filesystem::permissions(filepath, permission);
627 out << body;
628
629 if (out.bad())
630 {
631 messages::internalError(res);
632 cleanUp();
633 }
634}
635
636inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
637 const std::string& applyTime)
638{
639 std::string applyTimeNewVal;
640 if (applyTime == "Immediate")
641 {
642 applyTimeNewVal =
643 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
644 }
645 else if (applyTime == "OnReset")
646 {
647 applyTimeNewVal =
648 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
649 }
650 else
651 {
Ed Tanous62598e32023-07-17 17:06:25 -0700652 BMCWEB_LOG_INFO(
653 "ApplyTime value is not in the list of acceptable values");
George Liu0ed80c82020-05-12 16:06:27 +0800654 messages::propertyValueNotInList(asyncResp->res, applyTime,
655 "ApplyTime");
656 return;
657 }
658
Ed Tanousd02aad32024-02-13 14:43:34 -0800659 setDbusProperty(asyncResp, "xyz.openbmc_project.Settings",
660 sdbusplus::message::object_path(
661 "/xyz/openbmc_project/software/apply_time"),
662 "xyz.openbmc_project.Software.ApplyTime",
663 "RequestedApplyTime", "ApplyTime", applyTimeNewVal);
George Liu0ed80c82020-05-12 16:06:27 +0800664}
665
666inline void
667 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
668 const MultipartParser& parser)
669{
670 const std::string* uploadData = nullptr;
671 std::optional<std::string> applyTime = "OnReset";
672 bool targetFound = false;
673 for (const FormPart& formpart : parser.mime_fields)
674 {
675 boost::beast::http::fields::const_iterator it =
676 formpart.fields.find("Content-Disposition");
677 if (it == formpart.fields.end())
678 {
Ed Tanous62598e32023-07-17 17:06:25 -0700679 BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
George Liu0ed80c82020-05-12 16:06:27 +0800680 return;
681 }
Ed Tanous62598e32023-07-17 17:06:25 -0700682 BMCWEB_LOG_INFO("Parsing value {}", it->value());
George Liu0ed80c82020-05-12 16:06:27 +0800683
684 // The construction parameters of param_list must start with `;`
685 size_t index = it->value().find(';');
686 if (index == std::string::npos)
687 {
688 continue;
689 }
690
Patrick Williams89492a12023-05-10 07:51:34 -0500691 for (const auto& param :
George Liu0ed80c82020-05-12 16:06:27 +0800692 boost::beast::http::param_list{it->value().substr(index)})
693 {
694 if (param.first != "name" || param.second.empty())
695 {
696 continue;
697 }
698
699 if (param.second == "UpdateParameters")
700 {
701 std::vector<std::string> targets;
702 nlohmann::json content =
703 nlohmann::json::parse(formpart.content);
Ed Tanous7cb59f62022-05-05 11:48:31 -0700704 nlohmann::json::object_t* obj =
705 content.get_ptr<nlohmann::json::object_t*>();
706 if (obj == nullptr)
707 {
708 messages::propertyValueFormatError(asyncResp->res, targets,
709 "UpdateParameters");
710 return;
711 }
712
713 if (!json_util::readJsonObject(
714 *obj, asyncResp->res, "Targets", targets,
715 "@Redfish.OperationApplyTime", applyTime))
George Liu0ed80c82020-05-12 16:06:27 +0800716 {
717 return;
718 }
719 if (targets.size() != 1)
720 {
Ed Tanous7cb59f62022-05-05 11:48:31 -0700721 messages::propertyValueFormatError(asyncResp->res, targets,
722 "Targets");
George Liu0ed80c82020-05-12 16:06:27 +0800723 return;
724 }
725 if (targets[0] != "/redfish/v1/Managers/bmc")
726 {
Ed Tanous7cb59f62022-05-05 11:48:31 -0700727 messages::propertyValueNotInList(asyncResp->res, targets[0],
728 "Targets/0");
George Liu0ed80c82020-05-12 16:06:27 +0800729 return;
730 }
731 targetFound = true;
732 }
733 else if (param.second == "UpdateFile")
734 {
735 uploadData = &(formpart.content);
736 }
737 }
738 }
739
740 if (uploadData == nullptr)
741 {
Ed Tanous62598e32023-07-17 17:06:25 -0700742 BMCWEB_LOG_ERROR("Upload data is NULL");
George Liu0ed80c82020-05-12 16:06:27 +0800743 messages::propertyMissing(asyncResp->res, "UpdateFile");
744 return;
745 }
746 if (!targetFound)
747 {
748 messages::propertyMissing(asyncResp->res, "targets");
749 return;
750 }
751
752 setApplyTime(asyncResp, *applyTime);
753
754 uploadImageFile(asyncResp->res, *uploadData);
755}
756
Ed Tanousc2051d12022-05-11 12:21:55 -0700757inline void
758 handleUpdateServicePost(App& app, const crow::Request& req,
759 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
760{
Carson Labrado3ba00072022-06-06 19:40:56 +0000761 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700762 {
763 return;
764 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500765 std::string_view contentType = req.getHeaderValue("Content-Type");
Ed Tanousc2051d12022-05-11 12:21:55 -0700766
Ed Tanous62598e32023-07-17 17:06:25 -0700767 BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
Ed Tanousc2051d12022-05-11 12:21:55 -0700768
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500769 // Make sure that content type is application/octet-stream or
770 // multipart/form-data
Ed Tanous18f8f602023-07-18 10:07:23 -0700771 if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
George Liu0ed80c82020-05-12 16:06:27 +0800772 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500773 // Setup callback for when new software detected
774 monitorForSoftwareAvailable(asyncResp, req,
775 "/redfish/v1/UpdateService");
776
George Liu0ed80c82020-05-12 16:06:27 +0800777 uploadImageFile(asyncResp->res, req.body());
George Liu0ed80c82020-05-12 16:06:27 +0800778 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500779 else if (contentType.starts_with("multipart/form-data"))
George Liu0ed80c82020-05-12 16:06:27 +0800780 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500781 MultipartParser parser;
782
783 // Setup callback for when new software detected
784 monitorForSoftwareAvailable(asyncResp, req,
785 "/redfish/v1/UpdateService");
786
787 ParserError ec = parser.parse(req);
788 if (ec != ParserError::PARSER_SUCCESS)
789 {
790 // handle error
Ed Tanous62598e32023-07-17 17:06:25 -0700791 BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
792 static_cast<int>(ec));
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500793 messages::internalError(asyncResp->res);
794 return;
795 }
796 updateMultipartContext(asyncResp, parser);
George Liu0ed80c82020-05-12 16:06:27 +0800797 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500798 else
799 {
Ed Tanous62598e32023-07-17 17:06:25 -0700800 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500801 asyncResp->res.result(boost::beast::http::status::bad_request);
802 }
Ed Tanousc2051d12022-05-11 12:21:55 -0700803}
804
Ed Tanousf5139332024-04-03 13:25:04 -0700805inline void
806 handleUpdateServiceGet(App& app, const crow::Request& req,
807 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700808{
Ed Tanousf5139332024-04-03 13:25:04 -0700809 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
810 {
811 return;
812 }
813 asyncResp->res.jsonValue["@odata.type"] =
814 "#UpdateService.v1_11_1.UpdateService";
815 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
816 asyncResp->res.jsonValue["Id"] = "UpdateService";
817 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
818 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700819
Ed Tanousf5139332024-04-03 13:25:04 -0700820 asyncResp->res.jsonValue["HttpPushUri"] =
821 "/redfish/v1/UpdateService/update";
822 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
823 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700824
Ed Tanousf5139332024-04-03 13:25:04 -0700825 // UpdateService cannot be disabled
826 asyncResp->res.jsonValue["ServiceEnabled"] = true;
827 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
828 "/redfish/v1/UpdateService/FirmwareInventory";
829 // Get the MaxImageSizeBytes
830 asyncResp->res.jsonValue["MaxImageSizeBytes"] = bmcwebHttpReqBodyLimitMb *
831 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530832
Ed Tanousf5139332024-04-03 13:25:04 -0700833 // Update Actions object.
834 nlohmann::json& updateSvcSimpleUpdate =
835 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
836 updateSvcSimpleUpdate["target"] =
837 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
Ed Tanous757178a2024-04-03 14:32:38 -0700838
839 nlohmann::json::array_t allowed;
840
841#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
842 allowed.emplace_back(update_service::TransferProtocolType::TFTP);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700843#endif
Ed Tanous757178a2024-04-03 14:32:38 -0700844
845 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
846 std::move(allowed);
847
Ed Tanousf5139332024-04-03 13:25:04 -0700848 // Get the current ApplyTime value
849 sdbusplus::asio::getProperty<std::string>(
850 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
851 "/xyz/openbmc_project/software/apply_time",
852 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
853 [asyncResp](const boost::system::error_code& ec,
854 const std::string& applyTime) {
855 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700856 {
Ed Tanousf5139332024-04-03 13:25:04 -0700857 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
858 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -0700859 return;
860 }
861
Ed Tanousf5139332024-04-03 13:25:04 -0700862 // Store the ApplyTime Value
863 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
864 "RequestedApplyTimes.Immediate")
Ed Tanous002d39b2022-05-31 08:59:27 -0700865 {
Ed Tanousf5139332024-04-03 13:25:04 -0700866 asyncResp->res.jsonValue["HttpPushUriOptions"]
867 ["HttpPushUriApplyTime"]["ApplyTime"] =
868 "Immediate";
869 }
870 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
871 "RequestedApplyTimes.OnReset")
872 {
873 asyncResp->res.jsonValue["HttpPushUriOptions"]
874 ["HttpPushUriApplyTime"]["ApplyTime"] =
875 "OnReset";
Ed Tanous002d39b2022-05-31 08:59:27 -0700876 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500877 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700878}
879
Ed Tanousf5139332024-04-03 13:25:04 -0700880inline void handleUpdateServicePatch(
881 App& app, const crow::Request& req,
882 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700883{
Ed Tanousf5139332024-04-03 13:25:04 -0700884 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
885 {
886 return;
887 }
888 BMCWEB_LOG_DEBUG("doPatch...");
Ed Tanous002d39b2022-05-31 08:59:27 -0700889
Ed Tanousf5139332024-04-03 13:25:04 -0700890 std::optional<std::string> applyTime;
891 if (!json_util::readJsonPatch(
892 req, asyncResp->res,
893 "HttpPushUriOptions/HttpPushUriApplyTime/ApplyTime", applyTime))
894 {
895 return;
896 }
897
898 if (applyTime)
899 {
900 setApplyTime(asyncResp, *applyTime);
901 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700902}
Ed Tanousf5139332024-04-03 13:25:04 -0700903
904inline void handleUpdateServiceFirmwareInventoryCollectionGet(
905 App& app, const crow::Request& req,
906 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
907{
908 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
909 {
910 return;
911 }
912 asyncResp->res.jsonValue["@odata.type"] =
913 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
914 asyncResp->res.jsonValue["@odata.id"] =
915 "/redfish/v1/UpdateService/FirmwareInventory";
916 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
917 const std::array<const std::string_view, 1> iface = {
918 "xyz.openbmc_project.Software.Version"};
919
920 redfish::collection_util::getCollectionMembers(
921 asyncResp,
922 boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface,
923 "/xyz/openbmc_project/software");
924}
925
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700926/* Fill related item links (i.e. bmc, bios) in for inventory */
Ed Tanousf5139332024-04-03 13:25:04 -0700927inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
928 const std::string& purpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700929{
Willy Tueee00132022-06-14 14:53:17 -0700930 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700931 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700932 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700933 nlohmann::json::object_t item;
934 item["@odata.id"] = "/redfish/v1/Managers/bmc";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500935 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700936 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
937 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700938 }
Willy Tueee00132022-06-14 14:53:17 -0700939 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700940 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700941 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700942 nlohmann::json::object_t item;
943 item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500944 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700945 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
946 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700947 }
948 else
949 {
Carson Labradobf2dded2023-08-10 00:37:06 +0000950 BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700951 }
952}
953
Willy Tuaf246602022-06-14 15:51:53 -0700954inline void
955 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
956 const std::string& service, const std::string& path,
957 const std::string& swId)
958{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200959 sdbusplus::asio::getAllProperties(
960 *crow::connections::systemBus, service, path,
961 "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -0700962 [asyncResp,
Ed Tanous8b242752023-06-27 17:17:13 -0700963 swId](const boost::system::error_code& ec,
Willy Tuaf246602022-06-14 15:51:53 -0700964 const dbus::utility::DBusPropertiesMap& propertiesList) {
Ed Tanous8b242752023-06-27 17:17:13 -0700965 if (ec)
Willy Tuaf246602022-06-14 15:51:53 -0700966 {
967 messages::internalError(asyncResp->res);
968 return;
969 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200970
Willy Tuaf246602022-06-14 15:51:53 -0700971 const std::string* swInvPurpose = nullptr;
972 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200973
974 const bool success = sdbusplus::unpackPropertiesNoThrow(
975 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
976 swInvPurpose, "Version", version);
977
978 if (!success)
Willy Tuaf246602022-06-14 15:51:53 -0700979 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200980 messages::internalError(asyncResp->res);
981 return;
Willy Tuaf246602022-06-14 15:51:53 -0700982 }
983
984 if (swInvPurpose == nullptr)
985 {
Ed Tanous62598e32023-07-17 17:06:25 -0700986 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
Willy Tuaf246602022-06-14 15:51:53 -0700987 messages::internalError(asyncResp->res);
988 return;
989 }
990
Ed Tanous62598e32023-07-17 17:06:25 -0700991 BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
Willy Tuaf246602022-06-14 15:51:53 -0700992
993 if (version == nullptr)
994 {
Ed Tanous62598e32023-07-17 17:06:25 -0700995 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
Willy Tuaf246602022-06-14 15:51:53 -0700996
997 messages::internalError(asyncResp->res);
998
999 return;
1000 }
1001 asyncResp->res.jsonValue["Version"] = *version;
1002 asyncResp->res.jsonValue["Id"] = swId;
1003
1004 // swInvPurpose is of format:
1005 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
1006 // Translate this to "ABC image"
1007 size_t endDesc = swInvPurpose->rfind('.');
1008 if (endDesc == std::string::npos)
1009 {
1010 messages::internalError(asyncResp->res);
1011 return;
1012 }
1013 endDesc++;
1014 if (endDesc >= swInvPurpose->size())
1015 {
1016 messages::internalError(asyncResp->res);
1017 return;
1018 }
1019
1020 std::string formatDesc = swInvPurpose->substr(endDesc);
1021 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1022 getRelatedItems(asyncResp, *swInvPurpose);
Patrick Williams5a39f772023-10-20 11:20:21 -05001023 });
Willy Tuaf246602022-06-14 15:51:53 -07001024}
1025
Ed Tanousf5139332024-04-03 13:25:04 -07001026inline void handleUpdateServiceFirmwareInventoryGet(
1027 App& app, const crow::Request& req,
1028 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1029 const std::string& param)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001030{
Ed Tanousf5139332024-04-03 13:25:04 -07001031 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1032 {
1033 return;
1034 }
1035 std::shared_ptr<std::string> swId = std::make_shared<std::string>(param);
1036
1037 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1038 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
1039
1040 constexpr std::array<std::string_view, 1> interfaces = {
1041 "xyz.openbmc_project.Software.Version"};
1042 dbus::utility::getSubTree(
1043 "/", 0, interfaces,
1044 [asyncResp,
1045 swId](const boost::system::error_code& ec,
1046 const dbus::utility::MapperGetSubTreeResponse& subtree) {
1047 BMCWEB_LOG_DEBUG("doGet callback...");
1048 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001049 {
Ed Tanousf5139332024-04-03 13:25:04 -07001050 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001051 return;
1052 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001053
Ed Tanousf5139332024-04-03 13:25:04 -07001054 // Ensure we find our input swId, otherwise return an error
1055 bool found = false;
1056 for (const std::pair<
1057 std::string,
1058 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1059 obj : subtree)
1060 {
1061 if (!obj.first.ends_with(*swId))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001062 {
Ed Tanousf5139332024-04-03 13:25:04 -07001063 continue;
Ed Tanous45ca1b82022-03-25 13:07:27 -07001064 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001065
Ed Tanousf5139332024-04-03 13:25:04 -07001066 if (obj.second.empty())
Ed Tanous002d39b2022-05-31 08:59:27 -07001067 {
Ed Tanousf5139332024-04-03 13:25:04 -07001068 continue;
Ed Tanous002d39b2022-05-31 08:59:27 -07001069 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001070
Ed Tanousf5139332024-04-03 13:25:04 -07001071 found = true;
1072 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1073 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1074 *swId);
1075 }
1076 if (!found)
1077 {
1078 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
1079 messages::resourceMissingAtURI(
1080 asyncResp->res,
1081 boost::urls::format(
1082 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId));
1083 return;
1084 }
1085 asyncResp->res.jsonValue["@odata.type"] =
1086 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1087 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1088 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
1089
1090 asyncResp->res.jsonValue["Updateable"] = false;
1091 sw_util::getSwUpdatableStatus(asyncResp, swId);
Patrick Williams5a39f772023-10-20 11:20:21 -05001092 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001093}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001094
Ed Tanousf5139332024-04-03 13:25:04 -07001095inline void requestRoutesUpdateService(App& app)
1096{
1097 BMCWEB_ROUTE(
1098 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
1099 .privileges(redfish::privileges::postUpdateService)
1100 .methods(boost::beast::http::verb::post)(std::bind_front(
1101 handleUpdateServiceSimpleUpdateAction, std::ref(app)));
1102
1103 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1104 .privileges(redfish::privileges::getSoftwareInventory)
1105 .methods(boost::beast::http::verb::get)(std::bind_front(
1106 handleUpdateServiceFirmwareInventoryGet, std::ref(app)));
1107
1108 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1109 .privileges(redfish::privileges::getUpdateService)
1110 .methods(boost::beast::http::verb::get)(
1111 std::bind_front(handleUpdateServiceGet, std::ref(app)));
1112
1113 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1114 .privileges(redfish::privileges::patchUpdateService)
1115 .methods(boost::beast::http::verb::patch)(
1116 std::bind_front(handleUpdateServicePatch, std::ref(app)));
1117
1118 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
1119 .privileges(redfish::privileges::postUpdateService)
1120 .methods(boost::beast::http::verb::post)(
1121 std::bind_front(handleUpdateServicePost, std::ref(app)));
1122
1123 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
1124 .privileges(redfish::privileges::getSoftwareInventoryCollection)
1125 .methods(boost::beast::http::verb::get)(std::bind_front(
1126 handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app)));
1127}
1128
Ed Tanous1abe55e2018-09-05 08:30:59 -07001129} // namespace redfish