blob: ddb46f6be08d68f6d22aac6e1cd5e72f8cbfc9c0 [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"
George Liu0ed80c82020-05-12 16:06:27 +080022#include "multipart_parser.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080023#include "query.hpp"
24#include "registries/privilege_registry.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080025#include "task.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080026#include "utils/dbus_utils.hpp"
27#include "utils/sw_utils.hpp"
28
Ed Tanousd093c992023-01-19 19:01:49 -080029#include <boost/algorithm/string/case_conv.hpp>
George Liue99073f2022-12-09 11:06:16 +080030#include <boost/system/error_code.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070031#include <boost/url/format.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070032#include <sdbusplus/asio/property.hpp>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080033#include <sdbusplus/bus/match.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020034#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050035
George Liu2b731192023-01-11 16:27:13 +080036#include <array>
George Liu0ed80c82020-05-12 16:06:27 +080037#include <filesystem>
George Liu2b731192023-01-11 16:27:13 +080038#include <string_view>
39
Ed Tanous1abe55e2018-09-05 08:30:59 -070040namespace redfish
41{
Ed Tanous27826b52018-10-29 11:40:58 -070042
Andrew Geissler0e7de462019-03-04 19:11:54 -060043// Match signals added on software path
Ed Tanouscf9e4172022-12-21 09:30:16 -080044// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050045static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
Ed Tanouscf9e4172022-12-21 09:30:16 -080046// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050047static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060048// Only allow one update at a time
Ed Tanouscf9e4172022-12-21 09:30:16 -080049// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Andrew Geissler0e7de462019-03-04 19:11:54 -060050static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050051// Timer for software available
Ed Tanouscf9e4172022-12-21 09:30:16 -080052// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous271584a2019-07-09 16:24:22 -070053static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050054
John Edward Broadbent7e860f12021-04-08 15:57:16 -070055inline static void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050056{
57 fwUpdateInProgress = false;
58 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070059 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050060}
John Edward Broadbent7e860f12021-04-08 15:57:16 -070061inline static void activateImage(const std::string& objPath,
62 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050063{
64 BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
65 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -080066 [](const boost::system::error_code& errorCode) {
Ed Tanous002d39b2022-05-31 08:59:27 -070067 if (errorCode)
68 {
69 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
70 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
71 }
Andrew Geissler86adcd62019-04-18 10:58:05 -050072 },
73 service, objPath, "org.freedesktop.DBus.Properties", "Set",
74 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
Ed Tanous168e20c2021-12-13 14:39:53 -080075 dbus::utility::DbusVariantType(
George Liu0fda0f12021-11-16 10:06:17 +080076 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active"));
Andrew Geissler86adcd62019-04-18 10:58:05 -050077}
Andrew Geissler0554c982019-04-23 14:40:12 -050078
79// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
80// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080081static void
82 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -050083 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050084{
Ed Tanousb9d36b42022-02-26 21:42:46 -080085 dbus::utility::DBusInteracesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -050086
87 sdbusplus::message::object_path objPath;
88
89 m.read(objPath, interfacesProperties);
90
91 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
Ed Tanouse3eb3d62022-12-21 11:56:07 -080092 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050093 {
94 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
95
96 if (interface.first == "xyz.openbmc_project.Software.Activation")
97 {
Andrew Geissler86adcd62019-04-18 10:58:05 -050098 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +080099 constexpr std::array<std::string_view, 1> interfaces = {
100 "xyz.openbmc_project.Software.Activation"};
101 dbus::utility::getDbusObject(
102 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700103 [objPath, asyncResp, payload(std::move(payload))](
George Liu2b731192023-01-11 16:27:13 +0800104 const boost::system::error_code& errorCode,
Ed Tanousa3e65892021-09-16 14:13:20 -0700105 const std::vector<
106 std::pair<std::string, std::vector<std::string>>>&
107 objInfo) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700108 if (errorCode)
109 {
110 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
111 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
Andrew Geissler0554c982019-04-23 14:40:12 -0500112 if (asyncResp)
113 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700114 messages::internalError(asyncResp->res);
115 }
116 cleanUp();
117 return;
118 }
119 // Ensure we only got one service back
120 if (objInfo.size() != 1)
121 {
122 BMCWEB_LOG_ERROR << "Invalid Object Size "
123 << objInfo.size();
124 if (asyncResp)
125 {
126 messages::internalError(asyncResp->res);
127 }
128 cleanUp();
129 return;
130 }
131 // cancel timer only when
132 // xyz.openbmc_project.Software.Activation interface
133 // is added
134 fwAvailableTimer = nullptr;
135
136 activateImage(objPath.str, objInfo[0].first);
137 if (asyncResp)
138 {
139 std::shared_ptr<task::TaskData> task =
140 task::TaskData::createTask(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800141 [](const boost::system::error_code& ec,
Patrick Williams59d494e2022-07-22 19:26:55 -0500142 sdbusplus::message_t& msg,
Ed Tanous002d39b2022-05-31 08:59:27 -0700143 const std::shared_ptr<task::TaskData>&
144 taskData) {
145 if (ec)
146 {
147 return task::completed;
148 }
149
150 std::string iface;
151 dbus::utility::DBusPropertiesMap values;
152
153 std::string index = std::to_string(taskData->index);
154 msg.read(iface, values);
155
156 if (iface == "xyz.openbmc_project.Software.Activation")
157 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000158 const std::string* state = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700159 for (const auto& property : values)
160 {
161 if (property.first == "Activation")
162 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000163 state = std::get_if<std::string>(
164 &property.second);
165 if (state == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700166 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700167 taskData->messages.emplace_back(
168 messages::internalError());
James Feist32898ce2020-03-10 16:16:52 -0700169 return task::completed;
170 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700171 }
172 }
James Feist32898ce2020-03-10 16:16:52 -0700173
Ed Tanous002d39b2022-05-31 08:59:27 -0700174 if (state == nullptr)
175 {
176 return !task::completed;
177 }
James Feist32898ce2020-03-10 16:16:52 -0700178
Ed Tanous11ba3972022-07-11 09:50:41 -0700179 if (state->ends_with("Invalid") ||
180 state->ends_with("Failed"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700181 {
182 taskData->state = "Exception";
183 taskData->status = "Warning";
184 taskData->messages.emplace_back(
185 messages::taskAborted(index));
186 return task::completed;
187 }
James Feiste5d50062020-05-11 17:29:00 -0700188
Ed Tanous11ba3972022-07-11 09:50:41 -0700189 if (state->ends_with("Staged"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700190 {
191 taskData->state = "Stopping";
192 taskData->messages.emplace_back(
193 messages::taskPaused(index));
194
195 // its staged, set a long timer to
196 // allow them time to complete the
197 // update (probably cycle the
198 // system) if this expires then
199 // task will be cancelled
200 taskData->extendTimer(std::chrono::hours(5));
201 return !task::completed;
202 }
203
Ed Tanous11ba3972022-07-11 09:50:41 -0700204 if (state->ends_with("Active"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700205 {
206 taskData->messages.emplace_back(
207 messages::taskCompletedOK(index));
208 taskData->state = "Completed";
209 return task::completed;
210 }
211 }
212 else if (
213 iface ==
214 "xyz.openbmc_project.Software.ActivationProgress")
215 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700216 const uint8_t* progress = nullptr;
217 for (const auto& property : values)
218 {
219 if (property.first == "Progress")
220 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000221 progress =
222 std::get_if<uint8_t>(&property.second);
223 if (progress == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700224 {
James Feist32898ce2020-03-10 16:16:52 -0700225 taskData->messages.emplace_back(
Ed Tanous002d39b2022-05-31 08:59:27 -0700226 messages::internalError());
227 return task::completed;
James Feist32898ce2020-03-10 16:16:52 -0700228 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700229 }
230 }
James Feist32898ce2020-03-10 16:16:52 -0700231
Ed Tanous002d39b2022-05-31 08:59:27 -0700232 if (progress == nullptr)
233 {
234 return !task::completed;
235 }
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000236 taskData->percentComplete = *progress;
Ed Tanous002d39b2022-05-31 08:59:27 -0700237 taskData->messages.emplace_back(
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000238 messages::taskProgressChanged(index,
239 *progress));
James Feist32898ce2020-03-10 16:16:52 -0700240
Ed Tanous002d39b2022-05-31 08:59:27 -0700241 // if we're getting status updates it's
242 // still alive, update timer
243 taskData->extendTimer(std::chrono::minutes(5));
244 }
245
246 // as firmware update often results in a
247 // reboot, the task may never "complete"
248 // unless it is an error
249
250 return !task::completed;
251 },
252 "type='signal',interface='org.freedesktop.DBus.Properties',"
253 "member='PropertiesChanged',path='" +
254 objPath.str + "'");
255 task->startTimer(std::chrono::minutes(5));
256 task->populateResp(asyncResp->res);
257 task->payload.emplace(std::move(payload));
258 }
259 fwUpdateInProgress = false;
George Liu2b731192023-01-11 16:27:13 +0800260 });
Patrick Williams62bafc02022-09-08 17:35:35 -0500261
262 break;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500263 }
264 }
265}
266
Andrew Geissler0554c982019-04-23 14:40:12 -0500267// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
268// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700269static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800270 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
271 const crow::Request& req, const std::string& url,
Gunnar Mills5d138942022-09-07 10:26:21 -0500272 int timeoutTimeSeconds = 25)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500273{
274 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800275 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500276 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500277 if (asyncResp)
278 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500279 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
280 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500281 return;
282 }
283
Andrew Geissler0554c982019-04-23 14:40:12 -0500284 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700285 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500286
Ed Tanous271584a2019-07-09 16:24:22 -0700287 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500288
289 fwAvailableTimer->async_wait(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500290 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700291 cleanUp();
292 if (ec == boost::asio::error::operation_aborted)
293 {
294 // expected, we were canceled before the timer completed.
295 return;
296 }
297 BMCWEB_LOG_ERROR
298 << "Timed out waiting for firmware object being created";
299 BMCWEB_LOG_ERROR << "FW image may has already been uploaded to server";
300 if (ec)
301 {
302 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
303 return;
304 }
305 if (asyncResp)
306 {
307 redfish::messages::internalError(asyncResp->res);
308 }
309 });
Ed Tanousa3e65892021-09-16 14:13:20 -0700310 task::Payload payload(req);
Patrick Williams59d494e2022-07-22 19:26:55 -0500311 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500312 BMCWEB_LOG_DEBUG << "Match fired";
Ed Tanousa3e65892021-09-16 14:13:20 -0700313 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500314 };
315
316 fwUpdateInProgress = true;
317
Patrick Williams59d494e2022-07-22 19:26:55 -0500318 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Andrew Geissler86adcd62019-04-18 10:58:05 -0500319 *crow::connections::systemBus,
320 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
321 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
322 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700323
Patrick Williams59d494e2022-07-22 19:26:55 -0500324 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
James Feist4cde5d92020-06-11 10:39:55 -0700325 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800326 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
327 "member='InterfacesAdded',"
328 "path='/xyz/openbmc_project/logging'",
Patrick Williams59d494e2022-07-22 19:26:55 -0500329 [asyncResp, url](sdbusplus::message_t& m) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700330 std::vector<std::pair<std::string, dbus::utility::DBusPropertiesMap>>
331 interfacesProperties;
332 sdbusplus::message::object_path objPath;
333 m.read(objPath, interfacesProperties);
334 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
335 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
336 interface : interfacesProperties)
337 {
338 if (interface.first == "xyz.openbmc_project.Logging.Entry")
James Feist4cde5d92020-06-11 10:39:55 -0700339 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700340 for (const std::pair<std::string,
341 dbus::utility::DbusVariantType>& value :
342 interface.second)
Brian Mae1cc4822021-12-01 17:05:54 +0800343 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700344 if (value.first != "Message")
Brian Mae1cc4822021-12-01 17:05:54 +0800345 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700346 continue;
347 }
348 const std::string* type =
349 std::get_if<std::string>(&value.second);
350 if (type == nullptr)
351 {
352 // if this was our message, timeout will cover it
353 return;
354 }
355 fwAvailableTimer = nullptr;
356 if (*type ==
357 "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
358 {
359 redfish::messages::invalidUpload(asyncResp->res, url,
360 "Invalid archive");
361 }
362 else if (*type ==
363 "xyz.openbmc_project.Software.Image.Error."
364 "ManifestFileFailure")
365 {
366 redfish::messages::invalidUpload(asyncResp->res, url,
367 "Invalid manifest");
368 }
369 else if (
370 *type ==
371 "xyz.openbmc_project.Software.Image.Error.ImageFailure")
372 {
373 redfish::messages::invalidUpload(
374 asyncResp->res, url, "Invalid image format");
375 }
376 else if (
377 *type ==
378 "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
379 {
380 redfish::messages::invalidUpload(
381 asyncResp->res, url,
382 "Image version already exists");
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600383
Ed Tanous002d39b2022-05-31 08:59:27 -0700384 redfish::messages::resourceAlreadyExists(
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800385 asyncResp->res, "UpdateService", "Version",
Ed Tanous002d39b2022-05-31 08:59:27 -0700386 "uploaded version");
387 }
388 else if (
389 *type ==
390 "xyz.openbmc_project.Software.Image.Error.BusyFailure")
391 {
392 redfish::messages::resourceExhaustion(asyncResp->res,
393 url);
394 }
395 else
396 {
397 redfish::messages::internalError(asyncResp->res);
Brian Mae1cc4822021-12-01 17:05:54 +0800398 }
399 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600400 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700401 }
James Feist4cde5d92020-06-11 10:39:55 -0700402 });
Andrew Geissler86adcd62019-04-18 10:58:05 -0500403}
Jennifer Lee729dae72018-04-24 15:59:34 -0700404
Andrew Geissler0554c982019-04-23 14:40:12 -0500405/**
406 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
407 * SimpleUpdate action.
408 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700409inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500410{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700411 BMCWEB_ROUTE(
412 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
Ed Tanoused398212021-06-09 17:05:54 -0700413 .privileges(redfish::privileges::postUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700414 .methods(boost::beast::http::verb::post)(
415 [&app](const crow::Request& req,
416 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000417 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700418 {
419 return;
420 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700421
Ed Tanous002d39b2022-05-31 08:59:27 -0700422 std::optional<std::string> transferProtocol;
423 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500424
Ed Tanous002d39b2022-05-31 08:59:27 -0700425 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
Andrew Geissler0554c982019-04-23 14:40:12 -0500426
Ed Tanous002d39b2022-05-31 08:59:27 -0700427 // User can pass in both TransferProtocol and ImageURI parameters or
428 // they can pass in just the ImageURI with the transfer protocol
429 // embedded within it.
430 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
431 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500432
Ed Tanous002d39b2022-05-31 08:59:27 -0700433 if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
434 transferProtocol, "ImageURI", imageURI))
435 {
436 BMCWEB_LOG_DEBUG
437 << "Missing TransferProtocol or ImageURI parameter";
438 return;
439 }
440 if (!transferProtocol)
441 {
442 // Must be option 2
443 // Verify ImageURI has transfer protocol in it
444 size_t separator = imageURI.find(':');
Andrew Geissler0554c982019-04-23 14:40:12 -0500445 if ((separator == std::string::npos) ||
446 ((separator + 1) > imageURI.size()))
447 {
448 messages::actionParameterValueTypeError(
449 asyncResp->res, imageURI, "ImageURI",
450 "UpdateService.SimpleUpdate");
Ed Tanous002d39b2022-05-31 08:59:27 -0700451 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
452 << imageURI;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530453 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530454 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700455 transferProtocol = imageURI.substr(0, separator);
456 // Ensure protocol is upper case for a common comparison path
457 // below
458 boost::to_upper(*transferProtocol);
459 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
460 << *transferProtocol;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530461
Ed Tanous002d39b2022-05-31 08:59:27 -0700462 // Adjust imageURI to not have the protocol on it for parsing
463 // below
464 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
465 imageURI = imageURI.substr(separator + 3);
466 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
467 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530468
Ed Tanous002d39b2022-05-31 08:59:27 -0700469 // OpenBMC currently only supports TFTP
470 if (*transferProtocol != "TFTP")
471 {
472 messages::actionParameterNotSupported(asyncResp->res,
473 "TransferProtocol",
474 "UpdateService.SimpleUpdate");
475 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
476 << *transferProtocol;
477 return;
478 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700479
Ed Tanous002d39b2022-05-31 08:59:27 -0700480 // Format should be <IP or Hostname>/<file> for imageURI
481 size_t separator = imageURI.find('/');
482 if ((separator == std::string::npos) ||
483 ((separator + 1) > imageURI.size()))
484 {
485 messages::actionParameterValueTypeError(
486 asyncResp->res, imageURI, "ImageURI",
487 "UpdateService.SimpleUpdate");
488 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
489 return;
490 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700491
Ed Tanous002d39b2022-05-31 08:59:27 -0700492 std::string tftpServer = imageURI.substr(0, separator);
493 std::string fwFile = imageURI.substr(separator + 1);
494 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700495
Ed Tanous002d39b2022-05-31 08:59:27 -0700496 // Setup callback for when new software detected
497 // Give TFTP 10 minutes to complete
498 monitorForSoftwareAvailable(
499 asyncResp, req,
500 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
501 600);
502
503 // TFTP can take up to 10 minutes depending on image size and
504 // connection speed. Return to caller as soon as the TFTP operation
505 // has been started. The callback above will ensure the activate
506 // is started once the download has completed
507 redfish::messages::success(asyncResp->res);
508
509 // Call TFTP service
510 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800511 [](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700512 if (ec)
513 {
514 // messages::internalError(asyncResp->res);
515 cleanUp();
516 BMCWEB_LOG_DEBUG << "error_code = " << ec;
517 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
518 }
519 else
520 {
521 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
522 }
523 },
524 "xyz.openbmc_project.Software.Download",
525 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
526 "DownloadViaTFTP", fwFile, tftpServer);
527
528 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700529 });
530}
531
George Liu0ed80c82020-05-12 16:06:27 +0800532inline void uploadImageFile(crow::Response& res, std::string_view body)
533{
534 std::filesystem::path filepath(
535 "/tmp/images/" +
536 boost::uuids::to_string(boost::uuids::random_generator()()));
537 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
538 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
539 std::ofstream::trunc);
540 // set the permission of the file to 640
Patrick Williams89492a12023-05-10 07:51:34 -0500541 std::filesystem::perms permission = std::filesystem::perms::owner_read |
542 std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800543 std::filesystem::permissions(filepath, permission);
544 out << body;
545
546 if (out.bad())
547 {
548 messages::internalError(res);
549 cleanUp();
550 }
551}
552
553inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
554 const std::string& applyTime)
555{
556 std::string applyTimeNewVal;
557 if (applyTime == "Immediate")
558 {
559 applyTimeNewVal =
560 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
561 }
562 else if (applyTime == "OnReset")
563 {
564 applyTimeNewVal =
565 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
566 }
567 else
568 {
569 BMCWEB_LOG_INFO
570 << "ApplyTime value is not in the list of acceptable values";
571 messages::propertyValueNotInList(asyncResp->res, applyTime,
572 "ApplyTime");
573 return;
574 }
575
576 // Set the requested image apply time value
577 crow::connections::systemBus->async_method_call(
Ed Tanous81c4e332023-05-18 10:30:34 -0700578 [asyncResp](const boost::system::error_code& ec) {
George Liu0ed80c82020-05-12 16:06:27 +0800579 if (ec)
580 {
581 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
582 messages::internalError(asyncResp->res);
583 return;
584 }
585 messages::success(asyncResp->res);
586 },
587 "xyz.openbmc_project.Settings",
588 "/xyz/openbmc_project/software/apply_time",
589 "org.freedesktop.DBus.Properties", "Set",
590 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
591 dbus::utility::DbusVariantType{applyTimeNewVal});
592}
593
594inline void
595 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
596 const MultipartParser& parser)
597{
598 const std::string* uploadData = nullptr;
599 std::optional<std::string> applyTime = "OnReset";
600 bool targetFound = false;
601 for (const FormPart& formpart : parser.mime_fields)
602 {
603 boost::beast::http::fields::const_iterator it =
604 formpart.fields.find("Content-Disposition");
605 if (it == formpart.fields.end())
606 {
607 BMCWEB_LOG_ERROR << "Couldn't find Content-Disposition";
608 return;
609 }
610 BMCWEB_LOG_INFO << "Parsing value " << it->value();
611
612 // The construction parameters of param_list must start with `;`
613 size_t index = it->value().find(';');
614 if (index == std::string::npos)
615 {
616 continue;
617 }
618
Patrick Williams89492a12023-05-10 07:51:34 -0500619 for (const auto& param :
George Liu0ed80c82020-05-12 16:06:27 +0800620 boost::beast::http::param_list{it->value().substr(index)})
621 {
622 if (param.first != "name" || param.second.empty())
623 {
624 continue;
625 }
626
627 if (param.second == "UpdateParameters")
628 {
629 std::vector<std::string> targets;
630 nlohmann::json content =
631 nlohmann::json::parse(formpart.content);
632 if (!json_util::readJson(content, asyncResp->res, "Targets",
633 targets, "@Redfish.OperationApplyTime",
634 applyTime))
635 {
636 return;
637 }
638 if (targets.size() != 1)
639 {
640 messages::propertyValueFormatError(asyncResp->res,
641 "Targets", "");
642 return;
643 }
644 if (targets[0] != "/redfish/v1/Managers/bmc")
645 {
646 messages::propertyValueNotInList(asyncResp->res,
647 "Targets/0", targets[0]);
648 return;
649 }
650 targetFound = true;
651 }
652 else if (param.second == "UpdateFile")
653 {
654 uploadData = &(formpart.content);
655 }
656 }
657 }
658
659 if (uploadData == nullptr)
660 {
661 BMCWEB_LOG_ERROR << "Upload data is NULL";
662 messages::propertyMissing(asyncResp->res, "UpdateFile");
663 return;
664 }
665 if (!targetFound)
666 {
667 messages::propertyMissing(asyncResp->res, "targets");
668 return;
669 }
670
671 setApplyTime(asyncResp, *applyTime);
672
673 uploadImageFile(asyncResp->res, *uploadData);
674}
675
Ed Tanousc2051d12022-05-11 12:21:55 -0700676inline void
677 handleUpdateServicePost(App& app, const crow::Request& req,
678 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
679{
Carson Labrado3ba00072022-06-06 19:40:56 +0000680 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700681 {
682 return;
683 }
684 BMCWEB_LOG_DEBUG << "doPost...";
685
686 // Setup callback for when new software detected
687 monitorForSoftwareAvailable(asyncResp, req, "/redfish/v1/UpdateService");
688
George Liu0ed80c82020-05-12 16:06:27 +0800689 MultipartParser parser;
690 ParserError ec = parser.parse(req);
691 if (ec == ParserError::ERROR_BOUNDARY_FORMAT)
692 {
693 // If the request didnt' contain boundary information, assume it was a
694 // POST binary payload.
695 uploadImageFile(asyncResp->res, req.body());
696 return;
697 }
698 if (ec != ParserError::PARSER_SUCCESS)
699 {
700 // handle error
701 BMCWEB_LOG_ERROR << "MIME parse failed, ec : " << static_cast<int>(ec);
702 messages::internalError(asyncResp->res);
703 return;
704 }
705 updateMultipartContext(asyncResp, parser);
Ed Tanousc2051d12022-05-11 12:21:55 -0700706}
707
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700708inline void requestRoutesUpdateService(App& app)
709{
710 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700711 .privileges(redfish::privileges::getUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700712 .methods(boost::beast::http::verb::get)(
713 [&app](const crow::Request& req,
714 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000715 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700716 {
717 return;
718 }
719 asyncResp->res.jsonValue["@odata.type"] =
George Liu0ed80c82020-05-12 16:06:27 +0800720 "#UpdateService.v1_11_1.UpdateService";
Ed Tanous002d39b2022-05-31 08:59:27 -0700721 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
722 asyncResp->res.jsonValue["Id"] = "UpdateService";
723 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
724 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700725
Ed Tanous002d39b2022-05-31 08:59:27 -0700726 asyncResp->res.jsonValue["HttpPushUri"] =
727 "/redfish/v1/UpdateService/update";
George Liu0ed80c82020-05-12 16:06:27 +0800728 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
729 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700730
Ed Tanous002d39b2022-05-31 08:59:27 -0700731 // UpdateService cannot be disabled
732 asyncResp->res.jsonValue["ServiceEnabled"] = true;
733 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
734 "/redfish/v1/UpdateService/FirmwareInventory";
735 // Get the MaxImageSizeBytes
736 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
737 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530738
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700739#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
Ed Tanous002d39b2022-05-31 08:59:27 -0700740 // Update Actions object.
741 nlohmann::json& updateSvcSimpleUpdate =
742 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
743 updateSvcSimpleUpdate["target"] =
744 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
745 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
746 "TFTP"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700747#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700748 // Get the current ApplyTime value
749 sdbusplus::asio::getProperty<std::string>(
750 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
751 "/xyz/openbmc_project/software/apply_time",
752 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800753 [asyncResp](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700754 const std::string& applyTime) {
755 if (ec)
756 {
757 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
758 messages::internalError(asyncResp->res);
759 return;
760 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530761
Ed Tanous002d39b2022-05-31 08:59:27 -0700762 // Store the ApplyTime Value
763 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
764 "RequestedApplyTimes.Immediate")
765 {
766 asyncResp->res.jsonValue["HttpPushUriOptions"]
767 ["HttpPushUriApplyTime"]["ApplyTime"] =
768 "Immediate";
769 }
770 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
771 "RequestedApplyTimes.OnReset")
772 {
773 asyncResp->res.jsonValue["HttpPushUriOptions"]
774 ["HttpPushUriApplyTime"]["ApplyTime"] =
775 "OnReset";
776 }
777 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700778 });
779 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700780 .privileges(redfish::privileges::patchUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700781 .methods(boost::beast::http::verb::patch)(
782 [&app](const crow::Request& req,
783 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000784 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700785 {
786 return;
787 }
788 BMCWEB_LOG_DEBUG << "doPatch...";
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530789
Ed Tanous002d39b2022-05-31 08:59:27 -0700790 std::optional<nlohmann::json> pushUriOptions;
791 if (!json_util::readJsonPatch(req, asyncResp->res, "HttpPushUriOptions",
792 pushUriOptions))
793 {
794 return;
795 }
796
797 if (pushUriOptions)
798 {
799 std::optional<nlohmann::json> pushUriApplyTime;
800 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
801 "HttpPushUriApplyTime", pushUriApplyTime))
George Liu0fda0f12021-11-16 10:06:17 +0800802 {
803 return;
804 }
805
Ed Tanous002d39b2022-05-31 08:59:27 -0700806 if (pushUriApplyTime)
George Liu0fda0f12021-11-16 10:06:17 +0800807 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700808 std::optional<std::string> applyTime;
809 if (!json_util::readJson(*pushUriApplyTime, asyncResp->res,
810 "ApplyTime", applyTime))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700811 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700812 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700813 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700814
Ed Tanous002d39b2022-05-31 08:59:27 -0700815 if (applyTime)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700816 {
George Liu0ed80c82020-05-12 16:06:27 +0800817 setApplyTime(asyncResp, *applyTime);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700818 }
George Liu0fda0f12021-11-16 10:06:17 +0800819 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700820 }
George Liu0fda0f12021-11-16 10:06:17 +0800821 });
Ed Tanousc2051d12022-05-11 12:21:55 -0700822
Ed Tanous4dc23f32022-05-11 11:32:19 -0700823 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
824 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700825 .methods(boost::beast::http::verb::post)(
Ed Tanousc2051d12022-05-11 12:21:55 -0700826 std::bind_front(handleUpdateServicePost, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700827}
828
829inline void requestRoutesSoftwareInventoryCollection(App& app)
830{
831 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700832 .privileges(redfish::privileges::getSoftwareInventoryCollection)
Ed Tanous14766872022-03-15 10:44:42 -0700833 .methods(boost::beast::http::verb::get)(
834 [&app](const crow::Request& req,
835 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000836 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700837 {
838 return;
839 }
840 asyncResp->res.jsonValue["@odata.type"] =
841 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
842 asyncResp->res.jsonValue["@odata.id"] =
843 "/redfish/v1/UpdateService/FirmwareInventory";
844 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
845
George Liue99073f2022-12-09 11:06:16 +0800846 // Note that only firmware levels associated with a device
847 // are stored under /xyz/openbmc_project/software therefore
848 // to ensure only real FirmwareInventory items are returned,
849 // this full object path must be used here as input to
850 // mapper
851 constexpr std::array<std::string_view, 1> interfaces = {
852 "xyz.openbmc_project.Software.Version"};
853 dbus::utility::getSubTree(
854 "/xyz/openbmc_project/software", 0, interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -0700855 [asyncResp](
George Liue99073f2022-12-09 11:06:16 +0800856 const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700857 const dbus::utility::MapperGetSubTreeResponse& subtree) {
858 if (ec)
859 {
860 messages::internalError(asyncResp->res);
861 return;
862 }
863 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
864 asyncResp->res.jsonValue["Members@odata.count"] = 0;
865
866 for (const auto& obj : subtree)
867 {
868 sdbusplus::message::object_path path(obj.first);
869 std::string swId = path.filename();
870 if (swId.empty())
Ed Tanous14766872022-03-15 10:44:42 -0700871 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700872 messages::internalError(asyncResp->res);
873 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
Ed Tanous14766872022-03-15 10:44:42 -0700874 return;
875 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700876
Ed Tanous002d39b2022-05-31 08:59:27 -0700877 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
878 nlohmann::json::object_t member;
Ed Tanousef4c65b2023-04-24 15:28:50 -0700879 member["@odata.id"] = boost::urls::format(
880 "/redfish/v1/UpdateService/FirmwareInventory/{}", swId);
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500881 members.emplace_back(std::move(member));
Ed Tanous002d39b2022-05-31 08:59:27 -0700882 asyncResp->res.jsonValue["Members@odata.count"] =
883 members.size();
884 }
George Liue99073f2022-12-09 11:06:16 +0800885 });
Ed Tanous002d39b2022-05-31 08:59:27 -0700886 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700887}
888/* Fill related item links (i.e. bmc, bios) in for inventory */
889inline static void
890 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
891 const std::string& purpose)
892{
Willy Tueee00132022-06-14 14:53:17 -0700893 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700894 {
895 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700896 nlohmann::json::object_t item;
897 item["@odata.id"] = "/redfish/v1/Managers/bmc";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500898 relatedItem.emplace_back(std::move(item));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700899 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
900 }
Willy Tueee00132022-06-14 14:53:17 -0700901 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700902 {
903 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700904 nlohmann::json::object_t item;
905 item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500906 relatedItem.emplace_back(std::move(item));
Jiaqing Zhao1a6e51a2022-01-19 19:20:24 +0800907 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700908 }
909 else
910 {
911 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
912 }
913}
914
Willy Tuaf246602022-06-14 15:51:53 -0700915inline void
916 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
917 const std::string& service, const std::string& path,
918 const std::string& swId)
919{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200920 sdbusplus::asio::getAllProperties(
921 *crow::connections::systemBus, service, path,
922 "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -0700923 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800924 swId](const boost::system::error_code& errorCode,
Willy Tuaf246602022-06-14 15:51:53 -0700925 const dbus::utility::DBusPropertiesMap& propertiesList) {
926 if (errorCode)
927 {
928 messages::internalError(asyncResp->res);
929 return;
930 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200931
Willy Tuaf246602022-06-14 15:51:53 -0700932 const std::string* swInvPurpose = nullptr;
933 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200934
935 const bool success = sdbusplus::unpackPropertiesNoThrow(
936 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
937 swInvPurpose, "Version", version);
938
939 if (!success)
Willy Tuaf246602022-06-14 15:51:53 -0700940 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200941 messages::internalError(asyncResp->res);
942 return;
Willy Tuaf246602022-06-14 15:51:53 -0700943 }
944
945 if (swInvPurpose == nullptr)
946 {
947 BMCWEB_LOG_DEBUG << "Can't find property \"Purpose\"!";
948 messages::internalError(asyncResp->res);
949 return;
950 }
951
952 BMCWEB_LOG_DEBUG << "swInvPurpose = " << *swInvPurpose;
953
954 if (version == nullptr)
955 {
956 BMCWEB_LOG_DEBUG << "Can't find property \"Version\"!";
957
958 messages::internalError(asyncResp->res);
959
960 return;
961 }
962 asyncResp->res.jsonValue["Version"] = *version;
963 asyncResp->res.jsonValue["Id"] = swId;
964
965 // swInvPurpose is of format:
966 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
967 // Translate this to "ABC image"
968 size_t endDesc = swInvPurpose->rfind('.');
969 if (endDesc == std::string::npos)
970 {
971 messages::internalError(asyncResp->res);
972 return;
973 }
974 endDesc++;
975 if (endDesc >= swInvPurpose->size())
976 {
977 messages::internalError(asyncResp->res);
978 return;
979 }
980
981 std::string formatDesc = swInvPurpose->substr(endDesc);
982 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
983 getRelatedItems(asyncResp, *swInvPurpose);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200984 });
Willy Tuaf246602022-06-14 15:51:53 -0700985}
986
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700987inline void requestRoutesSoftwareInventory(App& app)
988{
989 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700990 .privileges(redfish::privileges::getSoftwareInventory)
Ed Tanous002d39b2022-05-31 08:59:27 -0700991 .methods(boost::beast::http::verb::get)(
992 [&app](const crow::Request& req,
993 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
994 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000995 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700996 {
997 return;
998 }
999 std::shared_ptr<std::string> swId =
1000 std::make_shared<std::string>(param);
1001
Ed Tanousef4c65b2023-04-24 15:28:50 -07001002 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1003 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001004
George Liue99073f2022-12-09 11:06:16 +08001005 constexpr std::array<std::string_view, 1> interfaces = {
1006 "xyz.openbmc_project.Software.Version"};
1007 dbus::utility::getSubTree(
1008 "/", 0, interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -07001009 [asyncResp,
George Liue99073f2022-12-09 11:06:16 +08001010 swId](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -07001011 const dbus::utility::MapperGetSubTreeResponse& subtree) {
1012 BMCWEB_LOG_DEBUG << "doGet callback...";
1013 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001014 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001015 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001016 return;
1017 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001018
Ed Tanous002d39b2022-05-31 08:59:27 -07001019 // Ensure we find our input swId, otherwise return an error
1020 bool found = false;
1021 for (const std::pair<std::string,
1022 std::vector<std::pair<
1023 std::string, std::vector<std::string>>>>&
1024 obj : subtree)
1025 {
Ed Tanous11ba3972022-07-11 09:50:41 -07001026 if (!obj.first.ends_with(*swId))
Ed Tanous002d39b2022-05-31 08:59:27 -07001027 {
1028 continue;
1029 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001030
Ed Tanous002d39b2022-05-31 08:59:27 -07001031 if (obj.second.empty())
1032 {
1033 continue;
1034 }
1035
1036 found = true;
Willy Tueee00132022-06-14 14:53:17 -07001037 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
Willy Tuaf246602022-06-14 15:51:53 -07001038 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1039 *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001040 }
1041 if (!found)
1042 {
1043 BMCWEB_LOG_ERROR << "Input swID " << *swId << " not found!";
1044 messages::resourceMissingAtURI(
Ed Tanousef4c65b2023-04-24 15:28:50 -07001045 asyncResp->res,
1046 boost::urls::format(
1047 "/redfish/v1/UpdateService/FirmwareInventory/{}",
1048 *swId));
Ed Tanous002d39b2022-05-31 08:59:27 -07001049 return;
1050 }
1051 asyncResp->res.jsonValue["@odata.type"] =
1052 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1053 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1054 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001055
Ed Tanous002d39b2022-05-31 08:59:27 -07001056 asyncResp->res.jsonValue["Updateable"] = false;
Willy Tueee00132022-06-14 14:53:17 -07001057 sw_util::getSwUpdatableStatus(asyncResp, swId);
George Liue99073f2022-12-09 11:06:16 +08001058 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001059 });
1060}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001061
1062} // namespace redfish