blob: 3e75425ee5b1797cac2913b97bc332fb7d6bd2f3 [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 Tanous2c6ffdb2023-06-28 11:28:38 -070023#include "ossl_random.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080024#include "query.hpp"
25#include "registries/privilege_registry.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080026#include "task.hpp"
John Edward Broadbent08d81ad2022-05-17 20:00:23 -070027#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080028#include "utils/dbus_utils.hpp"
29#include "utils/sw_utils.hpp"
30
Ed Tanousd093c992023-01-19 19:01:49 -080031#include <boost/algorithm/string/case_conv.hpp>
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>
George Liu2b731192023-01-11 16:27:13 +080040#include <string_view>
41
Ed Tanous1abe55e2018-09-05 08:30:59 -070042namespace redfish
43{
Ed Tanous27826b52018-10-29 11:40:58 -070044
Andrew Geissler0e7de462019-03-04 19:11:54 -060045// Match signals added on software path
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> fwUpdateMatcher;
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> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060050// Only allow one update at a time
Ed Tanouscf9e4172022-12-21 09:30:16 -080051// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Andrew Geissler0e7de462019-03-04 19:11:54 -060052static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050053// Timer for software available
Ed Tanouscf9e4172022-12-21 09:30:16 -080054// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous271584a2019-07-09 16:24:22 -070055static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050056
John Edward Broadbent7e860f12021-04-08 15:57:16 -070057inline static void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050058{
59 fwUpdateInProgress = false;
60 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070061 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050062}
John Edward Broadbent7e860f12021-04-08 15:57:16 -070063inline static void activateImage(const std::string& objPath,
64 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050065{
Ed Tanous62598e32023-07-17 17:06:25 -070066 BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
George Liu9ae226f2023-06-21 17:56:46 +080067 sdbusplus::asio::setProperty(
68 *crow::connections::systemBus, service, objPath,
69 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
70 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
Ed Tanous8b242752023-06-27 17:17:13 -070071 [](const boost::system::error_code& ec) {
72 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -070073 {
Ed Tanous62598e32023-07-17 17:06:25 -070074 BMCWEB_LOG_DEBUG("error_code = {}", ec);
75 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -070076 }
George Liu9ae226f2023-06-21 17:56:46 +080077 });
Andrew Geissler86adcd62019-04-18 10:58:05 -050078}
Andrew Geissler0554c982019-04-23 14:40:12 -050079
80// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
81// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080082static void
83 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -050084 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050085{
Ed Tanousb9d36b42022-02-26 21:42:46 -080086 dbus::utility::DBusInteracesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -050087
88 sdbusplus::message::object_path objPath;
89
90 m.read(objPath, interfacesProperties);
91
Ed Tanous62598e32023-07-17 17:06:25 -070092 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
Ed Tanouse3eb3d62022-12-21 11:56:07 -080093 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050094 {
Ed Tanous62598e32023-07-17 17:06:25 -070095 BMCWEB_LOG_DEBUG("interface = {}", interface.first);
Andrew Geissler86adcd62019-04-18 10:58:05 -050096
97 if (interface.first == "xyz.openbmc_project.Software.Activation")
98 {
Andrew Geissler86adcd62019-04-18 10:58:05 -050099 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +0800100 constexpr std::array<std::string_view, 1> interfaces = {
101 "xyz.openbmc_project.Software.Activation"};
102 dbus::utility::getDbusObject(
103 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700104 [objPath, asyncResp, payload(std::move(payload))](
Ed Tanous8b242752023-06-27 17:17:13 -0700105 const boost::system::error_code& ec,
Ed Tanousa3e65892021-09-16 14:13:20 -0700106 const std::vector<
107 std::pair<std::string, std::vector<std::string>>>&
108 objInfo) mutable {
Ed Tanous8b242752023-06-27 17:17:13 -0700109 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700110 {
Ed Tanous62598e32023-07-17 17:06:25 -0700111 BMCWEB_LOG_DEBUG("error_code = {}", ec);
112 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Andrew Geissler0554c982019-04-23 14:40:12 -0500113 if (asyncResp)
114 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700115 messages::internalError(asyncResp->res);
116 }
117 cleanUp();
118 return;
119 }
120 // Ensure we only got one service back
121 if (objInfo.size() != 1)
122 {
Ed Tanous62598e32023-07-17 17:06:25 -0700123 BMCWEB_LOG_ERROR("Invalid Object Size {}", objInfo.size());
Ed Tanous002d39b2022-05-31 08:59:27 -0700124 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 Tanous8b242752023-06-27 17:17:13 -0700141 [](const boost::system::error_code& ec2,
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) {
Ed Tanous8b242752023-06-27 17:17:13 -0700145 if (ec2)
Ed Tanous002d39b2022-05-31 08:59:27 -0700146 {
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 }
Ed Tanous62598e32023-07-17 17:06:25 -0700297 BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
298 BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
Ed Tanous002d39b2022-05-31 08:59:27 -0700299 if (ec)
300 {
Ed Tanous62598e32023-07-17 17:06:25 -0700301 BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -0700302 return;
303 }
304 if (asyncResp)
305 {
306 redfish::messages::internalError(asyncResp->res);
307 }
308 });
Ed Tanousa3e65892021-09-16 14:13:20 -0700309 task::Payload payload(req);
Patrick Williams59d494e2022-07-22 19:26:55 -0500310 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
Ed Tanous62598e32023-07-17 17:06:25 -0700311 BMCWEB_LOG_DEBUG("Match fired");
Ed Tanousa3e65892021-09-16 14:13:20 -0700312 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500313 };
314
315 fwUpdateInProgress = true;
316
Patrick Williams59d494e2022-07-22 19:26:55 -0500317 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Andrew Geissler86adcd62019-04-18 10:58:05 -0500318 *crow::connections::systemBus,
319 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
320 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
321 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700322
Patrick Williams59d494e2022-07-22 19:26:55 -0500323 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
James Feist4cde5d92020-06-11 10:39:55 -0700324 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800325 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
326 "member='InterfacesAdded',"
327 "path='/xyz/openbmc_project/logging'",
Patrick Williams59d494e2022-07-22 19:26:55 -0500328 [asyncResp, url](sdbusplus::message_t& m) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700329 std::vector<std::pair<std::string, dbus::utility::DBusPropertiesMap>>
330 interfacesProperties;
331 sdbusplus::message::object_path objPath;
332 m.read(objPath, interfacesProperties);
Ed Tanous62598e32023-07-17 17:06:25 -0700333 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
Ed Tanous002d39b2022-05-31 08:59:27 -0700334 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
335 interface : interfacesProperties)
336 {
337 if (interface.first == "xyz.openbmc_project.Logging.Entry")
James Feist4cde5d92020-06-11 10:39:55 -0700338 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700339 for (const std::pair<std::string,
340 dbus::utility::DbusVariantType>& value :
341 interface.second)
Brian Mae1cc4822021-12-01 17:05:54 +0800342 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700343 if (value.first != "Message")
Brian Mae1cc4822021-12-01 17:05:54 +0800344 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700345 continue;
346 }
347 const std::string* type =
348 std::get_if<std::string>(&value.second);
349 if (type == nullptr)
350 {
351 // if this was our message, timeout will cover it
352 return;
353 }
354 fwAvailableTimer = nullptr;
355 if (*type ==
356 "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
357 {
358 redfish::messages::invalidUpload(asyncResp->res, url,
359 "Invalid archive");
360 }
361 else if (*type ==
362 "xyz.openbmc_project.Software.Image.Error."
363 "ManifestFileFailure")
364 {
365 redfish::messages::invalidUpload(asyncResp->res, url,
366 "Invalid manifest");
367 }
368 else if (
369 *type ==
370 "xyz.openbmc_project.Software.Image.Error.ImageFailure")
371 {
372 redfish::messages::invalidUpload(
373 asyncResp->res, url, "Invalid image format");
374 }
375 else if (
376 *type ==
377 "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
378 {
379 redfish::messages::invalidUpload(
380 asyncResp->res, url,
381 "Image version already exists");
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600382
Ed Tanous002d39b2022-05-31 08:59:27 -0700383 redfish::messages::resourceAlreadyExists(
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800384 asyncResp->res, "UpdateService", "Version",
Ed Tanous002d39b2022-05-31 08:59:27 -0700385 "uploaded version");
386 }
387 else if (
388 *type ==
389 "xyz.openbmc_project.Software.Image.Error.BusyFailure")
390 {
391 redfish::messages::resourceExhaustion(asyncResp->res,
392 url);
393 }
394 else
395 {
396 redfish::messages::internalError(asyncResp->res);
Brian Mae1cc4822021-12-01 17:05:54 +0800397 }
398 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600399 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700400 }
James Feist4cde5d92020-06-11 10:39:55 -0700401 });
Andrew Geissler86adcd62019-04-18 10:58:05 -0500402}
Jennifer Lee729dae72018-04-24 15:59:34 -0700403
Andrew Geissler0554c982019-04-23 14:40:12 -0500404/**
405 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
406 * SimpleUpdate action.
407 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700408inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500409{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700410 BMCWEB_ROUTE(
411 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
Ed Tanoused398212021-06-09 17:05:54 -0700412 .privileges(redfish::privileges::postUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700413 .methods(boost::beast::http::verb::post)(
414 [&app](const crow::Request& req,
415 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000416 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700417 {
418 return;
419 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700420
Ed Tanous002d39b2022-05-31 08:59:27 -0700421 std::optional<std::string> transferProtocol;
422 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500423
Ed Tanous62598e32023-07-17 17:06:25 -0700424 BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
Andrew Geissler0554c982019-04-23 14:40:12 -0500425
Ed Tanous002d39b2022-05-31 08:59:27 -0700426 // User can pass in both TransferProtocol and ImageURI parameters or
427 // they can pass in just the ImageURI with the transfer protocol
428 // embedded within it.
429 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
430 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500431
Ed Tanous002d39b2022-05-31 08:59:27 -0700432 if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
433 transferProtocol, "ImageURI", imageURI))
434 {
Ed Tanous62598e32023-07-17 17:06:25 -0700435 BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
Ed Tanous002d39b2022-05-31 08:59:27 -0700436 return;
437 }
438 if (!transferProtocol)
439 {
440 // Must be option 2
441 // Verify ImageURI has transfer protocol in it
442 size_t separator = imageURI.find(':');
Andrew Geissler0554c982019-04-23 14:40:12 -0500443 if ((separator == std::string::npos) ||
444 ((separator + 1) > imageURI.size()))
445 {
446 messages::actionParameterValueTypeError(
447 asyncResp->res, imageURI, "ImageURI",
448 "UpdateService.SimpleUpdate");
Ed Tanous62598e32023-07-17 17:06:25 -0700449 BMCWEB_LOG_ERROR("ImageURI missing transfer protocol: {}",
450 imageURI);
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530451 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530452 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700453 transferProtocol = imageURI.substr(0, separator);
454 // Ensure protocol is upper case for a common comparison path
455 // below
456 boost::to_upper(*transferProtocol);
Ed Tanous62598e32023-07-17 17:06:25 -0700457 BMCWEB_LOG_DEBUG("Encoded transfer protocol {}", *transferProtocol);
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530458
Ed Tanous002d39b2022-05-31 08:59:27 -0700459 // Adjust imageURI to not have the protocol on it for parsing
460 // below
461 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
462 imageURI = imageURI.substr(separator + 3);
Ed Tanous62598e32023-07-17 17:06:25 -0700463 BMCWEB_LOG_DEBUG("Adjusted imageUri {}", imageURI);
Ed Tanous002d39b2022-05-31 08:59:27 -0700464 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530465
Ed Tanous002d39b2022-05-31 08:59:27 -0700466 // OpenBMC currently only supports TFTP
467 if (*transferProtocol != "TFTP")
468 {
469 messages::actionParameterNotSupported(asyncResp->res,
470 "TransferProtocol",
471 "UpdateService.SimpleUpdate");
Ed Tanous62598e32023-07-17 17:06:25 -0700472 BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
473 *transferProtocol);
Ed Tanous002d39b2022-05-31 08:59:27 -0700474 return;
475 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700476
Ed Tanous002d39b2022-05-31 08:59:27 -0700477 // Format should be <IP or Hostname>/<file> for imageURI
478 size_t separator = imageURI.find('/');
479 if ((separator == std::string::npos) ||
480 ((separator + 1) > imageURI.size()))
481 {
482 messages::actionParameterValueTypeError(
483 asyncResp->res, imageURI, "ImageURI",
484 "UpdateService.SimpleUpdate");
Ed Tanous62598e32023-07-17 17:06:25 -0700485 BMCWEB_LOG_ERROR("Invalid ImageURI: {}", imageURI);
Ed Tanous002d39b2022-05-31 08:59:27 -0700486 return;
487 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700488
Ed Tanous002d39b2022-05-31 08:59:27 -0700489 std::string tftpServer = imageURI.substr(0, separator);
490 std::string fwFile = imageURI.substr(separator + 1);
Ed Tanous62598e32023-07-17 17:06:25 -0700491 BMCWEB_LOG_DEBUG("Server: {}{}", tftpServer + " File: ", fwFile);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700492
Ed Tanous002d39b2022-05-31 08:59:27 -0700493 // Setup callback for when new software detected
494 // Give TFTP 10 minutes to complete
495 monitorForSoftwareAvailable(
496 asyncResp, req,
497 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
498 600);
499
500 // TFTP can take up to 10 minutes depending on image size and
501 // connection speed. Return to caller as soon as the TFTP operation
502 // has been started. The callback above will ensure the activate
503 // is started once the download has completed
504 redfish::messages::success(asyncResp->res);
505
506 // Call TFTP service
507 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800508 [](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700509 if (ec)
510 {
511 // messages::internalError(asyncResp->res);
512 cleanUp();
Ed Tanous62598e32023-07-17 17:06:25 -0700513 BMCWEB_LOG_DEBUG("error_code = {}", ec);
514 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -0700515 }
516 else
517 {
Ed Tanous62598e32023-07-17 17:06:25 -0700518 BMCWEB_LOG_DEBUG("Call to DownloaViaTFTP Success");
Ed Tanous002d39b2022-05-31 08:59:27 -0700519 }
520 },
521 "xyz.openbmc_project.Software.Download",
522 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
523 "DownloadViaTFTP", fwFile, tftpServer);
524
Ed Tanous62598e32023-07-17 17:06:25 -0700525 BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700526 });
527}
528
George Liu0ed80c82020-05-12 16:06:27 +0800529inline void uploadImageFile(crow::Response& res, std::string_view body)
530{
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700531 std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
532
Ed Tanous62598e32023-07-17 17:06:25 -0700533 BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
George Liu0ed80c82020-05-12 16:06:27 +0800534 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
535 std::ofstream::trunc);
536 // set the permission of the file to 640
Patrick Williams89492a12023-05-10 07:51:34 -0500537 std::filesystem::perms permission = std::filesystem::perms::owner_read |
538 std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800539 std::filesystem::permissions(filepath, permission);
540 out << body;
541
542 if (out.bad())
543 {
544 messages::internalError(res);
545 cleanUp();
546 }
547}
548
549inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
550 const std::string& applyTime)
551{
552 std::string applyTimeNewVal;
553 if (applyTime == "Immediate")
554 {
555 applyTimeNewVal =
556 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
557 }
558 else if (applyTime == "OnReset")
559 {
560 applyTimeNewVal =
561 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
562 }
563 else
564 {
Ed Tanous62598e32023-07-17 17:06:25 -0700565 BMCWEB_LOG_INFO(
566 "ApplyTime value is not in the list of acceptable values");
George Liu0ed80c82020-05-12 16:06:27 +0800567 messages::propertyValueNotInList(asyncResp->res, applyTime,
568 "ApplyTime");
569 return;
570 }
571
572 // Set the requested image apply time value
George Liu9ae226f2023-06-21 17:56:46 +0800573 sdbusplus::asio::setProperty(
574 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
George Liu0ed80c82020-05-12 16:06:27 +0800575 "/xyz/openbmc_project/software/apply_time",
George Liu0ed80c82020-05-12 16:06:27 +0800576 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
George Liu9ae226f2023-06-21 17:56:46 +0800577 applyTimeNewVal, [asyncResp](const boost::system::error_code& ec) {
578 if (ec)
579 {
Ed Tanous62598e32023-07-17 17:06:25 -0700580 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec);
George Liu9ae226f2023-06-21 17:56:46 +0800581 messages::internalError(asyncResp->res);
582 return;
583 }
584 messages::success(asyncResp->res);
585 });
George Liu0ed80c82020-05-12 16:06:27 +0800586}
587
588inline void
589 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
590 const MultipartParser& parser)
591{
592 const std::string* uploadData = nullptr;
593 std::optional<std::string> applyTime = "OnReset";
594 bool targetFound = false;
595 for (const FormPart& formpart : parser.mime_fields)
596 {
597 boost::beast::http::fields::const_iterator it =
598 formpart.fields.find("Content-Disposition");
599 if (it == formpart.fields.end())
600 {
Ed Tanous62598e32023-07-17 17:06:25 -0700601 BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
George Liu0ed80c82020-05-12 16:06:27 +0800602 return;
603 }
Ed Tanous62598e32023-07-17 17:06:25 -0700604 BMCWEB_LOG_INFO("Parsing value {}", it->value());
George Liu0ed80c82020-05-12 16:06:27 +0800605
606 // The construction parameters of param_list must start with `;`
607 size_t index = it->value().find(';');
608 if (index == std::string::npos)
609 {
610 continue;
611 }
612
Patrick Williams89492a12023-05-10 07:51:34 -0500613 for (const auto& param :
George Liu0ed80c82020-05-12 16:06:27 +0800614 boost::beast::http::param_list{it->value().substr(index)})
615 {
616 if (param.first != "name" || param.second.empty())
617 {
618 continue;
619 }
620
621 if (param.second == "UpdateParameters")
622 {
623 std::vector<std::string> targets;
624 nlohmann::json content =
625 nlohmann::json::parse(formpart.content);
626 if (!json_util::readJson(content, asyncResp->res, "Targets",
627 targets, "@Redfish.OperationApplyTime",
628 applyTime))
629 {
630 return;
631 }
632 if (targets.size() != 1)
633 {
634 messages::propertyValueFormatError(asyncResp->res,
635 "Targets", "");
636 return;
637 }
638 if (targets[0] != "/redfish/v1/Managers/bmc")
639 {
640 messages::propertyValueNotInList(asyncResp->res,
641 "Targets/0", targets[0]);
642 return;
643 }
644 targetFound = true;
645 }
646 else if (param.second == "UpdateFile")
647 {
648 uploadData = &(formpart.content);
649 }
650 }
651 }
652
653 if (uploadData == nullptr)
654 {
Ed Tanous62598e32023-07-17 17:06:25 -0700655 BMCWEB_LOG_ERROR("Upload data is NULL");
George Liu0ed80c82020-05-12 16:06:27 +0800656 messages::propertyMissing(asyncResp->res, "UpdateFile");
657 return;
658 }
659 if (!targetFound)
660 {
661 messages::propertyMissing(asyncResp->res, "targets");
662 return;
663 }
664
665 setApplyTime(asyncResp, *applyTime);
666
667 uploadImageFile(asyncResp->res, *uploadData);
668}
669
Ed Tanousc2051d12022-05-11 12:21:55 -0700670inline void
671 handleUpdateServicePost(App& app, const crow::Request& req,
672 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
673{
Carson Labrado3ba00072022-06-06 19:40:56 +0000674 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700675 {
676 return;
677 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500678 std::string_view contentType = req.getHeaderValue("Content-Type");
Ed Tanousc2051d12022-05-11 12:21:55 -0700679
Ed Tanous62598e32023-07-17 17:06:25 -0700680 BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
Ed Tanousc2051d12022-05-11 12:21:55 -0700681
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500682 // Make sure that content type is application/octet-stream or
683 // multipart/form-data
684 if (boost::iequals(contentType, "application/octet-stream"))
George Liu0ed80c82020-05-12 16:06:27 +0800685 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500686 // Setup callback for when new software detected
687 monitorForSoftwareAvailable(asyncResp, req,
688 "/redfish/v1/UpdateService");
689
George Liu0ed80c82020-05-12 16:06:27 +0800690 uploadImageFile(asyncResp->res, req.body());
George Liu0ed80c82020-05-12 16:06:27 +0800691 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500692 else if (contentType.starts_with("multipart/form-data"))
George Liu0ed80c82020-05-12 16:06:27 +0800693 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500694 MultipartParser parser;
695
696 // Setup callback for when new software detected
697 monitorForSoftwareAvailable(asyncResp, req,
698 "/redfish/v1/UpdateService");
699
700 ParserError ec = parser.parse(req);
701 if (ec != ParserError::PARSER_SUCCESS)
702 {
703 // handle error
Ed Tanous62598e32023-07-17 17:06:25 -0700704 BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
705 static_cast<int>(ec));
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500706 messages::internalError(asyncResp->res);
707 return;
708 }
709 updateMultipartContext(asyncResp, parser);
George Liu0ed80c82020-05-12 16:06:27 +0800710 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500711 else
712 {
Ed Tanous62598e32023-07-17 17:06:25 -0700713 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500714 asyncResp->res.result(boost::beast::http::status::bad_request);
715 }
Ed Tanousc2051d12022-05-11 12:21:55 -0700716}
717
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700718inline void requestRoutesUpdateService(App& app)
719{
720 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700721 .privileges(redfish::privileges::getUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700722 .methods(boost::beast::http::verb::get)(
723 [&app](const crow::Request& req,
724 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000725 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700726 {
727 return;
728 }
729 asyncResp->res.jsonValue["@odata.type"] =
George Liu0ed80c82020-05-12 16:06:27 +0800730 "#UpdateService.v1_11_1.UpdateService";
Ed Tanous002d39b2022-05-31 08:59:27 -0700731 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
732 asyncResp->res.jsonValue["Id"] = "UpdateService";
733 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
734 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700735
Ed Tanous002d39b2022-05-31 08:59:27 -0700736 asyncResp->res.jsonValue["HttpPushUri"] =
737 "/redfish/v1/UpdateService/update";
George Liu0ed80c82020-05-12 16:06:27 +0800738 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
739 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700740
Ed Tanous002d39b2022-05-31 08:59:27 -0700741 // UpdateService cannot be disabled
742 asyncResp->res.jsonValue["ServiceEnabled"] = true;
743 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
744 "/redfish/v1/UpdateService/FirmwareInventory";
745 // Get the MaxImageSizeBytes
746 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
747 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530748
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700749#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
Ed Tanous002d39b2022-05-31 08:59:27 -0700750 // Update Actions object.
751 nlohmann::json& updateSvcSimpleUpdate =
752 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
753 updateSvcSimpleUpdate["target"] =
754 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
755 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
756 "TFTP"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700757#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700758 // Get the current ApplyTime value
759 sdbusplus::asio::getProperty<std::string>(
760 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
761 "/xyz/openbmc_project/software/apply_time",
762 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800763 [asyncResp](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700764 const std::string& applyTime) {
765 if (ec)
766 {
Ed Tanous62598e32023-07-17 17:06:25 -0700767 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -0700768 messages::internalError(asyncResp->res);
769 return;
770 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530771
Ed Tanous002d39b2022-05-31 08:59:27 -0700772 // Store the ApplyTime Value
773 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
774 "RequestedApplyTimes.Immediate")
775 {
776 asyncResp->res.jsonValue["HttpPushUriOptions"]
777 ["HttpPushUriApplyTime"]["ApplyTime"] =
778 "Immediate";
779 }
780 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
781 "RequestedApplyTimes.OnReset")
782 {
783 asyncResp->res.jsonValue["HttpPushUriOptions"]
784 ["HttpPushUriApplyTime"]["ApplyTime"] =
785 "OnReset";
786 }
787 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700788 });
789 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700790 .privileges(redfish::privileges::patchUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700791 .methods(boost::beast::http::verb::patch)(
792 [&app](const crow::Request& req,
793 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000794 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700795 {
796 return;
797 }
Ed Tanous62598e32023-07-17 17:06:25 -0700798 BMCWEB_LOG_DEBUG("doPatch...");
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530799
Ed Tanous002d39b2022-05-31 08:59:27 -0700800 std::optional<nlohmann::json> pushUriOptions;
801 if (!json_util::readJsonPatch(req, asyncResp->res, "HttpPushUriOptions",
802 pushUriOptions))
803 {
804 return;
805 }
806
807 if (pushUriOptions)
808 {
809 std::optional<nlohmann::json> pushUriApplyTime;
810 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
811 "HttpPushUriApplyTime", pushUriApplyTime))
George Liu0fda0f12021-11-16 10:06:17 +0800812 {
813 return;
814 }
815
Ed Tanous002d39b2022-05-31 08:59:27 -0700816 if (pushUriApplyTime)
George Liu0fda0f12021-11-16 10:06:17 +0800817 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700818 std::optional<std::string> applyTime;
819 if (!json_util::readJson(*pushUriApplyTime, asyncResp->res,
820 "ApplyTime", applyTime))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700822 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700824
Ed Tanous002d39b2022-05-31 08:59:27 -0700825 if (applyTime)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700826 {
George Liu0ed80c82020-05-12 16:06:27 +0800827 setApplyTime(asyncResp, *applyTime);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700828 }
George Liu0fda0f12021-11-16 10:06:17 +0800829 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700830 }
George Liu0fda0f12021-11-16 10:06:17 +0800831 });
Ed Tanousc2051d12022-05-11 12:21:55 -0700832
Ed Tanous4dc23f32022-05-11 11:32:19 -0700833 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
834 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700835 .methods(boost::beast::http::verb::post)(
Ed Tanousc2051d12022-05-11 12:21:55 -0700836 std::bind_front(handleUpdateServicePost, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700837}
838
839inline void requestRoutesSoftwareInventoryCollection(App& app)
840{
841 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700842 .privileges(redfish::privileges::getSoftwareInventoryCollection)
Ed Tanous14766872022-03-15 10:44:42 -0700843 .methods(boost::beast::http::verb::get)(
844 [&app](const crow::Request& req,
845 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000846 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700847 {
848 return;
849 }
850 asyncResp->res.jsonValue["@odata.type"] =
851 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
852 asyncResp->res.jsonValue["@odata.id"] =
853 "/redfish/v1/UpdateService/FirmwareInventory";
854 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
John Edward Broadbent08d81ad2022-05-17 20:00:23 -0700855 const std::array<const std::string_view, 1> iface = {
George Liue99073f2022-12-09 11:06:16 +0800856 "xyz.openbmc_project.Software.Version"};
Ed Tanous002d39b2022-05-31 08:59:27 -0700857
John Edward Broadbent08d81ad2022-05-17 20:00:23 -0700858 redfish::collection_util::getCollectionMembers(
859 asyncResp,
860 boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"),
861 iface, "/xyz/openbmc_project/software");
Ed Tanous002d39b2022-05-31 08:59:27 -0700862 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700863}
864/* Fill related item links (i.e. bmc, bios) in for inventory */
865inline static void
Ed Tanousac106bf2023-06-07 09:24:59 -0700866 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700867 const std::string& purpose)
868{
Willy Tueee00132022-06-14 14:53:17 -0700869 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700870 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700871 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700872 nlohmann::json::object_t item;
873 item["@odata.id"] = "/redfish/v1/Managers/bmc";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500874 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700875 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
876 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700877 }
Willy Tueee00132022-06-14 14:53:17 -0700878 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700879 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700880 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700881 nlohmann::json::object_t item;
882 item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500883 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700884 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
885 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700886 }
887 else
888 {
Ed Tanous62598e32023-07-17 17:06:25 -0700889 BMCWEB_LOG_ERROR("Unknown software purpose {}", purpose);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700890 }
891}
892
Willy Tuaf246602022-06-14 15:51:53 -0700893inline void
894 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
895 const std::string& service, const std::string& path,
896 const std::string& swId)
897{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200898 sdbusplus::asio::getAllProperties(
899 *crow::connections::systemBus, service, path,
900 "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -0700901 [asyncResp,
Ed Tanous8b242752023-06-27 17:17:13 -0700902 swId](const boost::system::error_code& ec,
Willy Tuaf246602022-06-14 15:51:53 -0700903 const dbus::utility::DBusPropertiesMap& propertiesList) {
Ed Tanous8b242752023-06-27 17:17:13 -0700904 if (ec)
Willy Tuaf246602022-06-14 15:51:53 -0700905 {
906 messages::internalError(asyncResp->res);
907 return;
908 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200909
Willy Tuaf246602022-06-14 15:51:53 -0700910 const std::string* swInvPurpose = nullptr;
911 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200912
913 const bool success = sdbusplus::unpackPropertiesNoThrow(
914 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
915 swInvPurpose, "Version", version);
916
917 if (!success)
Willy Tuaf246602022-06-14 15:51:53 -0700918 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200919 messages::internalError(asyncResp->res);
920 return;
Willy Tuaf246602022-06-14 15:51:53 -0700921 }
922
923 if (swInvPurpose == nullptr)
924 {
Ed Tanous62598e32023-07-17 17:06:25 -0700925 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
Willy Tuaf246602022-06-14 15:51:53 -0700926 messages::internalError(asyncResp->res);
927 return;
928 }
929
Ed Tanous62598e32023-07-17 17:06:25 -0700930 BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
Willy Tuaf246602022-06-14 15:51:53 -0700931
932 if (version == nullptr)
933 {
Ed Tanous62598e32023-07-17 17:06:25 -0700934 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
Willy Tuaf246602022-06-14 15:51:53 -0700935
936 messages::internalError(asyncResp->res);
937
938 return;
939 }
940 asyncResp->res.jsonValue["Version"] = *version;
941 asyncResp->res.jsonValue["Id"] = swId;
942
943 // swInvPurpose is of format:
944 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
945 // Translate this to "ABC image"
946 size_t endDesc = swInvPurpose->rfind('.');
947 if (endDesc == std::string::npos)
948 {
949 messages::internalError(asyncResp->res);
950 return;
951 }
952 endDesc++;
953 if (endDesc >= swInvPurpose->size())
954 {
955 messages::internalError(asyncResp->res);
956 return;
957 }
958
959 std::string formatDesc = swInvPurpose->substr(endDesc);
960 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
961 getRelatedItems(asyncResp, *swInvPurpose);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200962 });
Willy Tuaf246602022-06-14 15:51:53 -0700963}
964
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700965inline void requestRoutesSoftwareInventory(App& app)
966{
967 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700968 .privileges(redfish::privileges::getSoftwareInventory)
Ed Tanous002d39b2022-05-31 08:59:27 -0700969 .methods(boost::beast::http::verb::get)(
970 [&app](const crow::Request& req,
971 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
972 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000973 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700974 {
975 return;
976 }
977 std::shared_ptr<std::string> swId =
978 std::make_shared<std::string>(param);
979
Ed Tanousef4c65b2023-04-24 15:28:50 -0700980 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
981 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700982
George Liue99073f2022-12-09 11:06:16 +0800983 constexpr std::array<std::string_view, 1> interfaces = {
984 "xyz.openbmc_project.Software.Version"};
985 dbus::utility::getSubTree(
986 "/", 0, interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -0700987 [asyncResp,
George Liue99073f2022-12-09 11:06:16 +0800988 swId](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700989 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Ed Tanous62598e32023-07-17 17:06:25 -0700990 BMCWEB_LOG_DEBUG("doGet callback...");
Ed Tanous002d39b2022-05-31 08:59:27 -0700991 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700992 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700993 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700994 return;
995 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700996
Ed Tanous002d39b2022-05-31 08:59:27 -0700997 // Ensure we find our input swId, otherwise return an error
998 bool found = false;
999 for (const std::pair<std::string,
1000 std::vector<std::pair<
1001 std::string, std::vector<std::string>>>>&
1002 obj : subtree)
1003 {
Ed Tanous11ba3972022-07-11 09:50:41 -07001004 if (!obj.first.ends_with(*swId))
Ed Tanous002d39b2022-05-31 08:59:27 -07001005 {
1006 continue;
1007 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001008
Ed Tanous002d39b2022-05-31 08:59:27 -07001009 if (obj.second.empty())
1010 {
1011 continue;
1012 }
1013
1014 found = true;
Willy Tueee00132022-06-14 14:53:17 -07001015 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
Willy Tuaf246602022-06-14 15:51:53 -07001016 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1017 *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001018 }
1019 if (!found)
1020 {
Ed Tanous62598e32023-07-17 17:06:25 -07001021 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001022 messages::resourceMissingAtURI(
Ed Tanousef4c65b2023-04-24 15:28:50 -07001023 asyncResp->res,
1024 boost::urls::format(
1025 "/redfish/v1/UpdateService/FirmwareInventory/{}",
1026 *swId));
Ed Tanous002d39b2022-05-31 08:59:27 -07001027 return;
1028 }
1029 asyncResp->res.jsonValue["@odata.type"] =
1030 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1031 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1032 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001033
Ed Tanous002d39b2022-05-31 08:59:27 -07001034 asyncResp->res.jsonValue["Updateable"] = false;
Willy Tueee00132022-06-14 14:53:17 -07001035 sw_util::getSwUpdatableStatus(asyncResp, swId);
George Liue99073f2022-12-09 11:06:16 +08001036 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001037 });
1038}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001039
1040} // namespace redfish