blob: b6c0487dfc2fc031bacbf9569a908035a3a8ad0f [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>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070031#include <sdbusplus/asio/property.hpp>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080032#include <sdbusplus/bus/match.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020033#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050034
George Liu2b731192023-01-11 16:27:13 +080035#include <array>
George Liu0ed80c82020-05-12 16:06:27 +080036#include <filesystem>
George Liu2b731192023-01-11 16:27:13 +080037#include <string_view>
38
Ed Tanous1abe55e2018-09-05 08:30:59 -070039namespace redfish
40{
Ed Tanous27826b52018-10-29 11:40:58 -070041
Andrew Geissler0e7de462019-03-04 19:11:54 -060042// Match signals added on software path
Ed Tanouscf9e4172022-12-21 09:30:16 -080043// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050044static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
Ed Tanouscf9e4172022-12-21 09:30:16 -080045// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050046static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060047// Only allow one update at a time
Ed Tanouscf9e4172022-12-21 09:30:16 -080048// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Andrew Geissler0e7de462019-03-04 19:11:54 -060049static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050050// Timer for software available
Ed Tanouscf9e4172022-12-21 09:30:16 -080051// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous271584a2019-07-09 16:24:22 -070052static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050053
John Edward Broadbent7e860f12021-04-08 15:57:16 -070054inline static void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050055{
56 fwUpdateInProgress = false;
57 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070058 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050059}
John Edward Broadbent7e860f12021-04-08 15:57:16 -070060inline static void activateImage(const std::string& objPath,
61 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050062{
63 BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
64 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -080065 [](const boost::system::error_code& errorCode) {
Ed Tanous002d39b2022-05-31 08:59:27 -070066 if (errorCode)
67 {
68 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
69 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
70 }
Andrew Geissler86adcd62019-04-18 10:58:05 -050071 },
72 service, objPath, "org.freedesktop.DBus.Properties", "Set",
73 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
Ed Tanous168e20c2021-12-13 14:39:53 -080074 dbus::utility::DbusVariantType(
George Liu0fda0f12021-11-16 10:06:17 +080075 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active"));
Andrew Geissler86adcd62019-04-18 10:58:05 -050076}
Andrew Geissler0554c982019-04-23 14:40:12 -050077
78// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
79// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080080static void
81 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -050082 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050083{
Ed Tanousb9d36b42022-02-26 21:42:46 -080084 dbus::utility::DBusInteracesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -050085
86 sdbusplus::message::object_path objPath;
87
88 m.read(objPath, interfacesProperties);
89
90 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
Ed Tanouse3eb3d62022-12-21 11:56:07 -080091 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050092 {
93 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
94
95 if (interface.first == "xyz.openbmc_project.Software.Activation")
96 {
Andrew Geissler86adcd62019-04-18 10:58:05 -050097 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +080098 constexpr std::array<std::string_view, 1> interfaces = {
99 "xyz.openbmc_project.Software.Activation"};
100 dbus::utility::getDbusObject(
101 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700102 [objPath, asyncResp, payload(std::move(payload))](
George Liu2b731192023-01-11 16:27:13 +0800103 const boost::system::error_code& errorCode,
Ed Tanousa3e65892021-09-16 14:13:20 -0700104 const std::vector<
105 std::pair<std::string, std::vector<std::string>>>&
106 objInfo) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700107 if (errorCode)
108 {
109 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
110 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
Andrew Geissler0554c982019-04-23 14:40:12 -0500111 if (asyncResp)
112 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700113 messages::internalError(asyncResp->res);
114 }
115 cleanUp();
116 return;
117 }
118 // Ensure we only got one service back
119 if (objInfo.size() != 1)
120 {
121 BMCWEB_LOG_ERROR << "Invalid Object Size "
122 << objInfo.size();
123 if (asyncResp)
124 {
125 messages::internalError(asyncResp->res);
126 }
127 cleanUp();
128 return;
129 }
130 // cancel timer only when
131 // xyz.openbmc_project.Software.Activation interface
132 // is added
133 fwAvailableTimer = nullptr;
134
135 activateImage(objPath.str, objInfo[0].first);
136 if (asyncResp)
137 {
138 std::shared_ptr<task::TaskData> task =
139 task::TaskData::createTask(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800140 [](const boost::system::error_code& ec,
Patrick Williams59d494e2022-07-22 19:26:55 -0500141 sdbusplus::message_t& msg,
Ed Tanous002d39b2022-05-31 08:59:27 -0700142 const std::shared_ptr<task::TaskData>&
143 taskData) {
144 if (ec)
145 {
146 return task::completed;
147 }
148
149 std::string iface;
150 dbus::utility::DBusPropertiesMap values;
151
152 std::string index = std::to_string(taskData->index);
153 msg.read(iface, values);
154
155 if (iface == "xyz.openbmc_project.Software.Activation")
156 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000157 const std::string* state = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700158 for (const auto& property : values)
159 {
160 if (property.first == "Activation")
161 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000162 state = std::get_if<std::string>(
163 &property.second);
164 if (state == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700165 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700166 taskData->messages.emplace_back(
167 messages::internalError());
James Feist32898ce2020-03-10 16:16:52 -0700168 return task::completed;
169 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700170 }
171 }
James Feist32898ce2020-03-10 16:16:52 -0700172
Ed Tanous002d39b2022-05-31 08:59:27 -0700173 if (state == nullptr)
174 {
175 return !task::completed;
176 }
James Feist32898ce2020-03-10 16:16:52 -0700177
Ed Tanous11ba3972022-07-11 09:50:41 -0700178 if (state->ends_with("Invalid") ||
179 state->ends_with("Failed"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700180 {
181 taskData->state = "Exception";
182 taskData->status = "Warning";
183 taskData->messages.emplace_back(
184 messages::taskAborted(index));
185 return task::completed;
186 }
James Feiste5d50062020-05-11 17:29:00 -0700187
Ed Tanous11ba3972022-07-11 09:50:41 -0700188 if (state->ends_with("Staged"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700189 {
190 taskData->state = "Stopping";
191 taskData->messages.emplace_back(
192 messages::taskPaused(index));
193
194 // its staged, set a long timer to
195 // allow them time to complete the
196 // update (probably cycle the
197 // system) if this expires then
198 // task will be cancelled
199 taskData->extendTimer(std::chrono::hours(5));
200 return !task::completed;
201 }
202
Ed Tanous11ba3972022-07-11 09:50:41 -0700203 if (state->ends_with("Active"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700204 {
205 taskData->messages.emplace_back(
206 messages::taskCompletedOK(index));
207 taskData->state = "Completed";
208 return task::completed;
209 }
210 }
211 else if (
212 iface ==
213 "xyz.openbmc_project.Software.ActivationProgress")
214 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700215 const uint8_t* progress = nullptr;
216 for (const auto& property : values)
217 {
218 if (property.first == "Progress")
219 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000220 progress =
221 std::get_if<uint8_t>(&property.second);
222 if (progress == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700223 {
James Feist32898ce2020-03-10 16:16:52 -0700224 taskData->messages.emplace_back(
Ed Tanous002d39b2022-05-31 08:59:27 -0700225 messages::internalError());
226 return task::completed;
James Feist32898ce2020-03-10 16:16:52 -0700227 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700228 }
229 }
James Feist32898ce2020-03-10 16:16:52 -0700230
Ed Tanous002d39b2022-05-31 08:59:27 -0700231 if (progress == nullptr)
232 {
233 return !task::completed;
234 }
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000235 taskData->percentComplete = *progress;
Ed Tanous002d39b2022-05-31 08:59:27 -0700236 taskData->messages.emplace_back(
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000237 messages::taskProgressChanged(index,
238 *progress));
James Feist32898ce2020-03-10 16:16:52 -0700239
Ed Tanous002d39b2022-05-31 08:59:27 -0700240 // if we're getting status updates it's
241 // still alive, update timer
242 taskData->extendTimer(std::chrono::minutes(5));
243 }
244
245 // as firmware update often results in a
246 // reboot, the task may never "complete"
247 // unless it is an error
248
249 return !task::completed;
250 },
251 "type='signal',interface='org.freedesktop.DBus.Properties',"
252 "member='PropertiesChanged',path='" +
253 objPath.str + "'");
254 task->startTimer(std::chrono::minutes(5));
255 task->populateResp(asyncResp->res);
256 task->payload.emplace(std::move(payload));
257 }
258 fwUpdateInProgress = false;
George Liu2b731192023-01-11 16:27:13 +0800259 });
Patrick Williams62bafc02022-09-08 17:35:35 -0500260
261 break;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500262 }
263 }
264}
265
Andrew Geissler0554c982019-04-23 14:40:12 -0500266// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
267// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700268static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800269 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
270 const crow::Request& req, const std::string& url,
Gunnar Mills5d138942022-09-07 10:26:21 -0500271 int timeoutTimeSeconds = 25)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500272{
273 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800274 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500275 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500276 if (asyncResp)
277 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500278 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
279 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500280 return;
281 }
282
Andrew Geissler0554c982019-04-23 14:40:12 -0500283 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700284 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500285
Ed Tanous271584a2019-07-09 16:24:22 -0700286 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500287
288 fwAvailableTimer->async_wait(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500289 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700290 cleanUp();
291 if (ec == boost::asio::error::operation_aborted)
292 {
293 // expected, we were canceled before the timer completed.
294 return;
295 }
296 BMCWEB_LOG_ERROR
297 << "Timed out waiting for firmware object being created";
298 BMCWEB_LOG_ERROR << "FW image may has already been uploaded to server";
299 if (ec)
300 {
301 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
302 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 {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500311 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);
333 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
334 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 Tanous002d39b2022-05-31 08:59:27 -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 {
435 BMCWEB_LOG_DEBUG
436 << "Missing TransferProtocol or ImageURI parameter";
437 return;
438 }
439 if (!transferProtocol)
440 {
441 // Must be option 2
442 // Verify ImageURI has transfer protocol in it
443 size_t separator = imageURI.find(':');
Andrew Geissler0554c982019-04-23 14:40:12 -0500444 if ((separator == std::string::npos) ||
445 ((separator + 1) > imageURI.size()))
446 {
447 messages::actionParameterValueTypeError(
448 asyncResp->res, imageURI, "ImageURI",
449 "UpdateService.SimpleUpdate");
Ed Tanous002d39b2022-05-31 08:59:27 -0700450 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
451 << imageURI;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530452 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530453 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700454 transferProtocol = imageURI.substr(0, separator);
455 // Ensure protocol is upper case for a common comparison path
456 // below
457 boost::to_upper(*transferProtocol);
458 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
459 << *transferProtocol;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530460
Ed Tanous002d39b2022-05-31 08:59:27 -0700461 // Adjust imageURI to not have the protocol on it for parsing
462 // below
463 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
464 imageURI = imageURI.substr(separator + 3);
465 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
466 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530467
Ed Tanous002d39b2022-05-31 08:59:27 -0700468 // OpenBMC currently only supports TFTP
469 if (*transferProtocol != "TFTP")
470 {
471 messages::actionParameterNotSupported(asyncResp->res,
472 "TransferProtocol",
473 "UpdateService.SimpleUpdate");
474 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
475 << *transferProtocol;
476 return;
477 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700478
Ed Tanous002d39b2022-05-31 08:59:27 -0700479 // Format should be <IP or Hostname>/<file> for imageURI
480 size_t separator = imageURI.find('/');
481 if ((separator == std::string::npos) ||
482 ((separator + 1) > imageURI.size()))
483 {
484 messages::actionParameterValueTypeError(
485 asyncResp->res, imageURI, "ImageURI",
486 "UpdateService.SimpleUpdate");
487 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
488 return;
489 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700490
Ed Tanous002d39b2022-05-31 08:59:27 -0700491 std::string tftpServer = imageURI.substr(0, separator);
492 std::string fwFile = imageURI.substr(separator + 1);
493 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700494
Ed Tanous002d39b2022-05-31 08:59:27 -0700495 // Setup callback for when new software detected
496 // Give TFTP 10 minutes to complete
497 monitorForSoftwareAvailable(
498 asyncResp, req,
499 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
500 600);
501
502 // TFTP can take up to 10 minutes depending on image size and
503 // connection speed. Return to caller as soon as the TFTP operation
504 // has been started. The callback above will ensure the activate
505 // is started once the download has completed
506 redfish::messages::success(asyncResp->res);
507
508 // Call TFTP service
509 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800510 [](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700511 if (ec)
512 {
513 // messages::internalError(asyncResp->res);
514 cleanUp();
515 BMCWEB_LOG_DEBUG << "error_code = " << ec;
516 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
517 }
518 else
519 {
520 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
521 }
522 },
523 "xyz.openbmc_project.Software.Download",
524 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
525 "DownloadViaTFTP", fwFile, tftpServer);
526
527 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700528 });
529}
530
George Liu0ed80c82020-05-12 16:06:27 +0800531inline void uploadImageFile(crow::Response& res, std::string_view body)
532{
533 std::filesystem::path filepath(
534 "/tmp/images/" +
535 boost::uuids::to_string(boost::uuids::random_generator()()));
536 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
537 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
538 std::ofstream::trunc);
539 // set the permission of the file to 640
Patrick Williams89492a12023-05-10 07:51:34 -0500540 std::filesystem::perms permission = std::filesystem::perms::owner_read |
541 std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800542 std::filesystem::permissions(filepath, permission);
543 out << body;
544
545 if (out.bad())
546 {
547 messages::internalError(res);
548 cleanUp();
549 }
550}
551
552inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
553 const std::string& applyTime)
554{
555 std::string applyTimeNewVal;
556 if (applyTime == "Immediate")
557 {
558 applyTimeNewVal =
559 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
560 }
561 else if (applyTime == "OnReset")
562 {
563 applyTimeNewVal =
564 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
565 }
566 else
567 {
568 BMCWEB_LOG_INFO
569 << "ApplyTime value is not in the list of acceptable values";
570 messages::propertyValueNotInList(asyncResp->res, applyTime,
571 "ApplyTime");
572 return;
573 }
574
575 // Set the requested image apply time value
576 crow::connections::systemBus->async_method_call(
577 [asyncResp](const boost::system::error_code ec) {
578 if (ec)
579 {
580 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
581 messages::internalError(asyncResp->res);
582 return;
583 }
584 messages::success(asyncResp->res);
585 },
586 "xyz.openbmc_project.Settings",
587 "/xyz/openbmc_project/software/apply_time",
588 "org.freedesktop.DBus.Properties", "Set",
589 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
590 dbus::utility::DbusVariantType{applyTimeNewVal});
591}
592
593inline void
594 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
595 const MultipartParser& parser)
596{
597 const std::string* uploadData = nullptr;
598 std::optional<std::string> applyTime = "OnReset";
599 bool targetFound = false;
600 for (const FormPart& formpart : parser.mime_fields)
601 {
602 boost::beast::http::fields::const_iterator it =
603 formpart.fields.find("Content-Disposition");
604 if (it == formpart.fields.end())
605 {
606 BMCWEB_LOG_ERROR << "Couldn't find Content-Disposition";
607 return;
608 }
609 BMCWEB_LOG_INFO << "Parsing value " << it->value();
610
611 // The construction parameters of param_list must start with `;`
612 size_t index = it->value().find(';');
613 if (index == std::string::npos)
614 {
615 continue;
616 }
617
Patrick Williams89492a12023-05-10 07:51:34 -0500618 for (const auto& param :
George Liu0ed80c82020-05-12 16:06:27 +0800619 boost::beast::http::param_list{it->value().substr(index)})
620 {
621 if (param.first != "name" || param.second.empty())
622 {
623 continue;
624 }
625
626 if (param.second == "UpdateParameters")
627 {
628 std::vector<std::string> targets;
629 nlohmann::json content =
630 nlohmann::json::parse(formpart.content);
631 if (!json_util::readJson(content, asyncResp->res, "Targets",
632 targets, "@Redfish.OperationApplyTime",
633 applyTime))
634 {
635 return;
636 }
637 if (targets.size() != 1)
638 {
639 messages::propertyValueFormatError(asyncResp->res,
640 "Targets", "");
641 return;
642 }
643 if (targets[0] != "/redfish/v1/Managers/bmc")
644 {
645 messages::propertyValueNotInList(asyncResp->res,
646 "Targets/0", targets[0]);
647 return;
648 }
649 targetFound = true;
650 }
651 else if (param.second == "UpdateFile")
652 {
653 uploadData = &(formpart.content);
654 }
655 }
656 }
657
658 if (uploadData == nullptr)
659 {
660 BMCWEB_LOG_ERROR << "Upload data is NULL";
661 messages::propertyMissing(asyncResp->res, "UpdateFile");
662 return;
663 }
664 if (!targetFound)
665 {
666 messages::propertyMissing(asyncResp->res, "targets");
667 return;
668 }
669
670 setApplyTime(asyncResp, *applyTime);
671
672 uploadImageFile(asyncResp->res, *uploadData);
673}
674
Ed Tanousc2051d12022-05-11 12:21:55 -0700675inline void
676 handleUpdateServicePost(App& app, const crow::Request& req,
677 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
678{
Carson Labrado3ba00072022-06-06 19:40:56 +0000679 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700680 {
681 return;
682 }
683 BMCWEB_LOG_DEBUG << "doPost...";
684
685 // Setup callback for when new software detected
686 monitorForSoftwareAvailable(asyncResp, req, "/redfish/v1/UpdateService");
687
George Liu0ed80c82020-05-12 16:06:27 +0800688 MultipartParser parser;
689 ParserError ec = parser.parse(req);
690 if (ec == ParserError::ERROR_BOUNDARY_FORMAT)
691 {
692 // If the request didnt' contain boundary information, assume it was a
693 // POST binary payload.
694 uploadImageFile(asyncResp->res, req.body());
695 return;
696 }
697 if (ec != ParserError::PARSER_SUCCESS)
698 {
699 // handle error
700 BMCWEB_LOG_ERROR << "MIME parse failed, ec : " << static_cast<int>(ec);
701 messages::internalError(asyncResp->res);
702 return;
703 }
704 updateMultipartContext(asyncResp, parser);
Ed Tanousc2051d12022-05-11 12:21:55 -0700705}
706
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700707inline void requestRoutesUpdateService(App& app)
708{
709 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700710 .privileges(redfish::privileges::getUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700711 .methods(boost::beast::http::verb::get)(
712 [&app](const crow::Request& req,
713 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000714 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700715 {
716 return;
717 }
718 asyncResp->res.jsonValue["@odata.type"] =
George Liu0ed80c82020-05-12 16:06:27 +0800719 "#UpdateService.v1_11_1.UpdateService";
Ed Tanous002d39b2022-05-31 08:59:27 -0700720 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
721 asyncResp->res.jsonValue["Id"] = "UpdateService";
722 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
723 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700724
Ed Tanous32ca38a2022-05-11 12:36:59 -0700725#ifdef BMCWEB_ENABLE_REDFISH_UPDATESERVICE_OLD_POST_URL
Ed Tanous002d39b2022-05-31 08:59:27 -0700726 // See note about later on in this file about why this is neccesary
727 // This is "Wrong" per the standard, but is done temporarily to
728 // avoid noise in failing tests as people transition to having this
729 // option disabled
730 asyncResp->res.addHeader(boost::beast::http::field::allow,
731 "GET, PATCH, HEAD");
Ed Tanous32ca38a2022-05-11 12:36:59 -0700732#endif
733
Ed Tanous002d39b2022-05-31 08:59:27 -0700734 asyncResp->res.jsonValue["HttpPushUri"] =
735 "/redfish/v1/UpdateService/update";
George Liu0ed80c82020-05-12 16:06:27 +0800736 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
737 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700738
Ed Tanous002d39b2022-05-31 08:59:27 -0700739 // UpdateService cannot be disabled
740 asyncResp->res.jsonValue["ServiceEnabled"] = true;
741 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
742 "/redfish/v1/UpdateService/FirmwareInventory";
743 // Get the MaxImageSizeBytes
744 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
745 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530746
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700747#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
Ed Tanous002d39b2022-05-31 08:59:27 -0700748 // Update Actions object.
749 nlohmann::json& updateSvcSimpleUpdate =
750 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
751 updateSvcSimpleUpdate["target"] =
752 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
753 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
754 "TFTP"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700755#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700756 // Get the current ApplyTime value
757 sdbusplus::asio::getProperty<std::string>(
758 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
759 "/xyz/openbmc_project/software/apply_time",
760 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800761 [asyncResp](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700762 const std::string& applyTime) {
763 if (ec)
764 {
765 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
766 messages::internalError(asyncResp->res);
767 return;
768 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530769
Ed Tanous002d39b2022-05-31 08:59:27 -0700770 // Store the ApplyTime Value
771 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
772 "RequestedApplyTimes.Immediate")
773 {
774 asyncResp->res.jsonValue["HttpPushUriOptions"]
775 ["HttpPushUriApplyTime"]["ApplyTime"] =
776 "Immediate";
777 }
778 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
779 "RequestedApplyTimes.OnReset")
780 {
781 asyncResp->res.jsonValue["HttpPushUriOptions"]
782 ["HttpPushUriApplyTime"]["ApplyTime"] =
783 "OnReset";
784 }
785 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700786 });
787 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700788 .privileges(redfish::privileges::patchUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700789 .methods(boost::beast::http::verb::patch)(
790 [&app](const crow::Request& req,
791 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000792 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700793 {
794 return;
795 }
796 BMCWEB_LOG_DEBUG << "doPatch...";
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530797
Ed Tanous002d39b2022-05-31 08:59:27 -0700798 std::optional<nlohmann::json> pushUriOptions;
799 if (!json_util::readJsonPatch(req, asyncResp->res, "HttpPushUriOptions",
800 pushUriOptions))
801 {
802 return;
803 }
804
805 if (pushUriOptions)
806 {
807 std::optional<nlohmann::json> pushUriApplyTime;
808 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
809 "HttpPushUriApplyTime", pushUriApplyTime))
George Liu0fda0f12021-11-16 10:06:17 +0800810 {
811 return;
812 }
813
Ed Tanous002d39b2022-05-31 08:59:27 -0700814 if (pushUriApplyTime)
George Liu0fda0f12021-11-16 10:06:17 +0800815 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700816 std::optional<std::string> applyTime;
817 if (!json_util::readJson(*pushUriApplyTime, asyncResp->res,
818 "ApplyTime", applyTime))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700819 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700820 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700822
Ed Tanous002d39b2022-05-31 08:59:27 -0700823 if (applyTime)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700824 {
George Liu0ed80c82020-05-12 16:06:27 +0800825 setApplyTime(asyncResp, *applyTime);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700826 }
George Liu0fda0f12021-11-16 10:06:17 +0800827 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700828 }
George Liu0fda0f12021-11-16 10:06:17 +0800829 });
Ed Tanousc2051d12022-05-11 12:21:55 -0700830
Ed Tanous4dc23f32022-05-11 11:32:19 -0700831// The "old" behavior of the update service URI causes redfish-service validator
832// failures when the Allow header is supported, given that in the spec,
833// UpdateService does not allow POST. in openbmc, we unfortunately reused that
834// resource as our HttpPushUri as well. A number of services, including the
835// openbmc tests, and documentation have hardcoded that erroneous API, instead
836// of relying on HttpPushUri as the spec requires. This option will exist
837// temporarily to allow the old behavior until Q4 2022, at which time it will be
838// removed.
839#ifdef BMCWEB_ENABLE_REDFISH_UPDATESERVICE_OLD_POST_URL
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700840 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700841 .privileges(redfish::privileges::postUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700842 .methods(boost::beast::http::verb::post)(
843 [&app](const crow::Request& req,
844 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
845 asyncResp->res.addHeader(
846 boost::beast::http::field::warning,
847 "299 - \"POST to /redfish/v1/UpdateService is deprecated. Use "
848 "the value contained within HttpPushUri.\"");
849 handleUpdateServicePost(app, req, asyncResp);
Ed Tanous4dc23f32022-05-11 11:32:19 -0700850 });
851#endif
852 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
853 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700854 .methods(boost::beast::http::verb::post)(
Ed Tanousc2051d12022-05-11 12:21:55 -0700855 std::bind_front(handleUpdateServicePost, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700856}
857
858inline void requestRoutesSoftwareInventoryCollection(App& app)
859{
860 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700861 .privileges(redfish::privileges::getSoftwareInventoryCollection)
Ed Tanous14766872022-03-15 10:44:42 -0700862 .methods(boost::beast::http::verb::get)(
863 [&app](const crow::Request& req,
864 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000865 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700866 {
867 return;
868 }
869 asyncResp->res.jsonValue["@odata.type"] =
870 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
871 asyncResp->res.jsonValue["@odata.id"] =
872 "/redfish/v1/UpdateService/FirmwareInventory";
873 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
874
George Liue99073f2022-12-09 11:06:16 +0800875 // Note that only firmware levels associated with a device
876 // are stored under /xyz/openbmc_project/software therefore
877 // to ensure only real FirmwareInventory items are returned,
878 // this full object path must be used here as input to
879 // mapper
880 constexpr std::array<std::string_view, 1> interfaces = {
881 "xyz.openbmc_project.Software.Version"};
882 dbus::utility::getSubTree(
883 "/xyz/openbmc_project/software", 0, interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -0700884 [asyncResp](
George Liue99073f2022-12-09 11:06:16 +0800885 const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700886 const dbus::utility::MapperGetSubTreeResponse& subtree) {
887 if (ec)
888 {
889 messages::internalError(asyncResp->res);
890 return;
891 }
892 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
893 asyncResp->res.jsonValue["Members@odata.count"] = 0;
894
895 for (const auto& obj : subtree)
896 {
897 sdbusplus::message::object_path path(obj.first);
898 std::string swId = path.filename();
899 if (swId.empty())
Ed Tanous14766872022-03-15 10:44:42 -0700900 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700901 messages::internalError(asyncResp->res);
902 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
Ed Tanous14766872022-03-15 10:44:42 -0700903 return;
904 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700905
Ed Tanous002d39b2022-05-31 08:59:27 -0700906 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
907 nlohmann::json::object_t member;
Willy Tueddfc432022-09-26 16:46:38 +0000908 member["@odata.id"] = crow::utility::urlFromPieces(
909 "redfish", "v1", "UpdateService", "FirmwareInventory",
910 swId);
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500911 members.emplace_back(std::move(member));
Ed Tanous002d39b2022-05-31 08:59:27 -0700912 asyncResp->res.jsonValue["Members@odata.count"] =
913 members.size();
914 }
George Liue99073f2022-12-09 11:06:16 +0800915 });
Ed Tanous002d39b2022-05-31 08:59:27 -0700916 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700917}
918/* Fill related item links (i.e. bmc, bios) in for inventory */
919inline static void
920 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
921 const std::string& purpose)
922{
Willy Tueee00132022-06-14 14:53:17 -0700923 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700924 {
925 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700926 nlohmann::json::object_t item;
927 item["@odata.id"] = "/redfish/v1/Managers/bmc";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500928 relatedItem.emplace_back(std::move(item));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700929 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
930 }
Willy Tueee00132022-06-14 14:53:17 -0700931 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700932 {
933 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700934 nlohmann::json::object_t item;
935 item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500936 relatedItem.emplace_back(std::move(item));
Jiaqing Zhao1a6e51a2022-01-19 19:20:24 +0800937 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700938 }
939 else
940 {
941 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
942 }
943}
944
Willy Tuaf246602022-06-14 15:51:53 -0700945inline void
946 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
947 const std::string& service, const std::string& path,
948 const std::string& swId)
949{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200950 sdbusplus::asio::getAllProperties(
951 *crow::connections::systemBus, service, path,
952 "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -0700953 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800954 swId](const boost::system::error_code& errorCode,
Willy Tuaf246602022-06-14 15:51:53 -0700955 const dbus::utility::DBusPropertiesMap& propertiesList) {
956 if (errorCode)
957 {
958 messages::internalError(asyncResp->res);
959 return;
960 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200961
Willy Tuaf246602022-06-14 15:51:53 -0700962 const std::string* swInvPurpose = nullptr;
963 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200964
965 const bool success = sdbusplus::unpackPropertiesNoThrow(
966 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
967 swInvPurpose, "Version", version);
968
969 if (!success)
Willy Tuaf246602022-06-14 15:51:53 -0700970 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200971 messages::internalError(asyncResp->res);
972 return;
Willy Tuaf246602022-06-14 15:51:53 -0700973 }
974
975 if (swInvPurpose == nullptr)
976 {
977 BMCWEB_LOG_DEBUG << "Can't find property \"Purpose\"!";
978 messages::internalError(asyncResp->res);
979 return;
980 }
981
982 BMCWEB_LOG_DEBUG << "swInvPurpose = " << *swInvPurpose;
983
984 if (version == nullptr)
985 {
986 BMCWEB_LOG_DEBUG << "Can't find property \"Version\"!";
987
988 messages::internalError(asyncResp->res);
989
990 return;
991 }
992 asyncResp->res.jsonValue["Version"] = *version;
993 asyncResp->res.jsonValue["Id"] = swId;
994
995 // swInvPurpose is of format:
996 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
997 // Translate this to "ABC image"
998 size_t endDesc = swInvPurpose->rfind('.');
999 if (endDesc == std::string::npos)
1000 {
1001 messages::internalError(asyncResp->res);
1002 return;
1003 }
1004 endDesc++;
1005 if (endDesc >= swInvPurpose->size())
1006 {
1007 messages::internalError(asyncResp->res);
1008 return;
1009 }
1010
1011 std::string formatDesc = swInvPurpose->substr(endDesc);
1012 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1013 getRelatedItems(asyncResp, *swInvPurpose);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001014 });
Willy Tuaf246602022-06-14 15:51:53 -07001015}
1016
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001017inline void requestRoutesSoftwareInventory(App& app)
1018{
1019 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001020 .privileges(redfish::privileges::getSoftwareInventory)
Ed Tanous002d39b2022-05-31 08:59:27 -07001021 .methods(boost::beast::http::verb::get)(
1022 [&app](const crow::Request& req,
1023 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1024 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001025 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001026 {
1027 return;
1028 }
1029 std::shared_ptr<std::string> swId =
1030 std::make_shared<std::string>(param);
1031
Willy Tueddfc432022-09-26 16:46:38 +00001032 asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
1033 "redfish", "v1", "UpdateService", "FirmwareInventory", *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001034
George Liue99073f2022-12-09 11:06:16 +08001035 constexpr std::array<std::string_view, 1> interfaces = {
1036 "xyz.openbmc_project.Software.Version"};
1037 dbus::utility::getSubTree(
1038 "/", 0, interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -07001039 [asyncResp,
George Liue99073f2022-12-09 11:06:16 +08001040 swId](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -07001041 const dbus::utility::MapperGetSubTreeResponse& subtree) {
1042 BMCWEB_LOG_DEBUG << "doGet callback...";
1043 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001044 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001045 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001046 return;
1047 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001048
Ed Tanous002d39b2022-05-31 08:59:27 -07001049 // Ensure we find our input swId, otherwise return an error
1050 bool found = false;
1051 for (const std::pair<std::string,
1052 std::vector<std::pair<
1053 std::string, std::vector<std::string>>>>&
1054 obj : subtree)
1055 {
Ed Tanous11ba3972022-07-11 09:50:41 -07001056 if (!obj.first.ends_with(*swId))
Ed Tanous002d39b2022-05-31 08:59:27 -07001057 {
1058 continue;
1059 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001060
Ed Tanous002d39b2022-05-31 08:59:27 -07001061 if (obj.second.empty())
1062 {
1063 continue;
1064 }
1065
1066 found = true;
Willy Tueee00132022-06-14 14:53:17 -07001067 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
Willy Tuaf246602022-06-14 15:51:53 -07001068 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1069 *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001070 }
1071 if (!found)
1072 {
1073 BMCWEB_LOG_ERROR << "Input swID " << *swId << " not found!";
1074 messages::resourceMissingAtURI(
1075 asyncResp->res, crow::utility::urlFromPieces(
1076 "redfish", "v1", "UpdateService",
1077 "FirmwareInventory", *swId));
1078 return;
1079 }
1080 asyncResp->res.jsonValue["@odata.type"] =
1081 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1082 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1083 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001084
Ed Tanous002d39b2022-05-31 08:59:27 -07001085 asyncResp->res.jsonValue["Updateable"] = false;
Willy Tueee00132022-06-14 14:53:17 -07001086 sw_util::getSwUpdatableStatus(asyncResp, swId);
George Liue99073f2022-12-09 11:06:16 +08001087 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001088 });
1089}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001090
1091} // namespace redfish