blob: 4ae7cdfea5a99f4aedf0d6a6502d995311ed707a [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
John Edward Broadbent7e860f12021-04-08 15:57:16 -070020#include <app.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -080021#include <dbus_utility.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -070022#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070023#include <registries/privilege_registry.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070024#include <sdbusplus/asio/property.hpp>
Andrew Geissler87d84722019-02-28 14:28:39 -060025#include <utils/fw_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace redfish
28{
Ed Tanous27826b52018-10-29 11:40:58 -070029
Andrew Geissler0e7de462019-03-04 19:11:54 -060030// Match signals added on software path
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070031static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
James Feist4cde5d92020-06-11 10:39:55 -070032static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060033// Only allow one update at a time
34static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050035// Timer for software available
Ed Tanous271584a2019-07-09 16:24:22 -070036static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050037
John Edward Broadbent7e860f12021-04-08 15:57:16 -070038inline static void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050039{
40 fwUpdateInProgress = false;
41 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070042 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050043}
John Edward Broadbent7e860f12021-04-08 15:57:16 -070044inline static void activateImage(const std::string& objPath,
45 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050046{
47 BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
48 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +000049 [](const boost::system::error_code errorCode) {
Ed Tanous002d39b2022-05-31 08:59:27 -070050 if (errorCode)
51 {
52 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
53 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
54 }
Andrew Geissler86adcd62019-04-18 10:58:05 -050055 },
56 service, objPath, "org.freedesktop.DBus.Properties", "Set",
57 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
Ed Tanous168e20c2021-12-13 14:39:53 -080058 dbus::utility::DbusVariantType(
George Liu0fda0f12021-11-16 10:06:17 +080059 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active"));
Andrew Geissler86adcd62019-04-18 10:58:05 -050060}
Andrew Geissler0554c982019-04-23 14:40:12 -050061
62// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
63// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080064static void
65 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
66 sdbusplus::message::message& m,
Ed Tanousa3e65892021-09-16 14:13:20 -070067 task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050068{
Ed Tanousb9d36b42022-02-26 21:42:46 -080069 dbus::utility::DBusInteracesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -050070
71 sdbusplus::message::object_path objPath;
72
73 m.read(objPath, interfacesProperties);
74
75 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050076 for (auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050077 {
78 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
79
80 if (interface.first == "xyz.openbmc_project.Software.Activation")
81 {
Andrew Geissler86adcd62019-04-18 10:58:05 -050082 // Retrieve service and activate
83 crow::connections::systemBus->async_method_call(
Ed Tanousa3e65892021-09-16 14:13:20 -070084 [objPath, asyncResp, payload(std::move(payload))](
85 const boost::system::error_code errorCode,
86 const std::vector<
87 std::pair<std::string, std::vector<std::string>>>&
88 objInfo) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -070089 if (errorCode)
90 {
91 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
92 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
Andrew Geissler0554c982019-04-23 14:40:12 -050093 if (asyncResp)
94 {
Ed Tanous002d39b2022-05-31 08:59:27 -070095 messages::internalError(asyncResp->res);
96 }
97 cleanUp();
98 return;
99 }
100 // Ensure we only got one service back
101 if (objInfo.size() != 1)
102 {
103 BMCWEB_LOG_ERROR << "Invalid Object Size "
104 << objInfo.size();
105 if (asyncResp)
106 {
107 messages::internalError(asyncResp->res);
108 }
109 cleanUp();
110 return;
111 }
112 // cancel timer only when
113 // xyz.openbmc_project.Software.Activation interface
114 // is added
115 fwAvailableTimer = nullptr;
116
117 activateImage(objPath.str, objInfo[0].first);
118 if (asyncResp)
119 {
120 std::shared_ptr<task::TaskData> task =
121 task::TaskData::createTask(
122 [](boost::system::error_code ec,
123 sdbusplus::message::message& msg,
124 const std::shared_ptr<task::TaskData>&
125 taskData) {
126 if (ec)
127 {
128 return task::completed;
129 }
130
131 std::string iface;
132 dbus::utility::DBusPropertiesMap values;
133
134 std::string index = std::to_string(taskData->index);
135 msg.read(iface, values);
136
137 if (iface == "xyz.openbmc_project.Software.Activation")
138 {
139 std::string* state = nullptr;
140 for (const auto& property : values)
141 {
142 if (property.first == "Activation")
143 {
144 const std::string* state =
145 std::get_if<std::string>(
146 &property.second);
147 if (state == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700148 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700149 taskData->messages.emplace_back(
150 messages::internalError());
James Feist32898ce2020-03-10 16:16:52 -0700151 return task::completed;
152 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700153 }
154 }
James Feist32898ce2020-03-10 16:16:52 -0700155
Ed Tanous002d39b2022-05-31 08:59:27 -0700156 if (state == nullptr)
157 {
158 return !task::completed;
159 }
James Feist32898ce2020-03-10 16:16:52 -0700160
Ed Tanous002d39b2022-05-31 08:59:27 -0700161 if (boost::ends_with(*state, "Invalid") ||
162 boost::ends_with(*state, "Failed"))
163 {
164 taskData->state = "Exception";
165 taskData->status = "Warning";
166 taskData->messages.emplace_back(
167 messages::taskAborted(index));
168 return task::completed;
169 }
James Feiste5d50062020-05-11 17:29:00 -0700170
Ed Tanous002d39b2022-05-31 08:59:27 -0700171 if (boost::ends_with(*state, "Staged"))
172 {
173 taskData->state = "Stopping";
174 taskData->messages.emplace_back(
175 messages::taskPaused(index));
176
177 // its staged, set a long timer to
178 // allow them time to complete the
179 // update (probably cycle the
180 // system) if this expires then
181 // task will be cancelled
182 taskData->extendTimer(std::chrono::hours(5));
183 return !task::completed;
184 }
185
186 if (boost::ends_with(*state, "Active"))
187 {
188 taskData->messages.emplace_back(
189 messages::taskCompletedOK(index));
190 taskData->state = "Completed";
191 return task::completed;
192 }
193 }
194 else if (
195 iface ==
196 "xyz.openbmc_project.Software.ActivationProgress")
197 {
198
199 const uint8_t* progress = nullptr;
200 for (const auto& property : values)
201 {
202 if (property.first == "Progress")
203 {
204 const std::string* progress =
205 std::get_if<std::string>(
206 &property.second);
207 if (progress == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700208 {
James Feist32898ce2020-03-10 16:16:52 -0700209 taskData->messages.emplace_back(
Ed Tanous002d39b2022-05-31 08:59:27 -0700210 messages::internalError());
211 return task::completed;
James Feist32898ce2020-03-10 16:16:52 -0700212 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700213 }
214 }
James Feist32898ce2020-03-10 16:16:52 -0700215
Ed Tanous002d39b2022-05-31 08:59:27 -0700216 if (progress == nullptr)
217 {
218 return !task::completed;
219 }
220 taskData->percentComplete =
221 static_cast<int>(*progress);
222 taskData->messages.emplace_back(
223 messages::taskProgressChanged(
224 index, static_cast<size_t>(*progress)));
James Feist32898ce2020-03-10 16:16:52 -0700225
Ed Tanous002d39b2022-05-31 08:59:27 -0700226 // if we're getting status updates it's
227 // still alive, update timer
228 taskData->extendTimer(std::chrono::minutes(5));
229 }
230
231 // as firmware update often results in a
232 // reboot, the task may never "complete"
233 // unless it is an error
234
235 return !task::completed;
236 },
237 "type='signal',interface='org.freedesktop.DBus.Properties',"
238 "member='PropertiesChanged',path='" +
239 objPath.str + "'");
240 task->startTimer(std::chrono::minutes(5));
241 task->populateResp(asyncResp->res);
242 task->payload.emplace(std::move(payload));
243 }
244 fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500245 },
246 "xyz.openbmc_project.ObjectMapper",
247 "/xyz/openbmc_project/object_mapper",
248 "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500249 std::array<const char*, 1>{
Andrew Geissler86adcd62019-04-18 10:58:05 -0500250 "xyz.openbmc_project.Software.Activation"});
251 }
252 }
253}
254
Andrew Geissler0554c982019-04-23 14:40:12 -0500255// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
256// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700257static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800258 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
259 const crow::Request& req, const std::string& url,
260 int timeoutTimeSeconds = 10)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500261{
262 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800263 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500264 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500265 if (asyncResp)
266 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500267 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
268 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500269 return;
270 }
271
Andrew Geissler0554c982019-04-23 14:40:12 -0500272 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700273 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500274
Ed Tanous271584a2019-07-09 16:24:22 -0700275 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500276
277 fwAvailableTimer->async_wait(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500278 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700279 cleanUp();
280 if (ec == boost::asio::error::operation_aborted)
281 {
282 // expected, we were canceled before the timer completed.
283 return;
284 }
285 BMCWEB_LOG_ERROR
286 << "Timed out waiting for firmware object being created";
287 BMCWEB_LOG_ERROR << "FW image may has already been uploaded to server";
288 if (ec)
289 {
290 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
291 return;
292 }
293 if (asyncResp)
294 {
295 redfish::messages::internalError(asyncResp->res);
296 }
297 });
Ed Tanousa3e65892021-09-16 14:13:20 -0700298 task::Payload payload(req);
Ed Tanous002d39b2022-05-31 08:59:27 -0700299 auto callback =
300 [asyncResp, payload](sdbusplus::message::message& m) mutable {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500301 BMCWEB_LOG_DEBUG << "Match fired";
Ed Tanousa3e65892021-09-16 14:13:20 -0700302 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500303 };
304
305 fwUpdateInProgress = true;
306
307 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
308 *crow::connections::systemBus,
309 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
310 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
311 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700312
313 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match::match>(
314 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800315 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
316 "member='InterfacesAdded',"
317 "path='/xyz/openbmc_project/logging'",
James Feist4cde5d92020-06-11 10:39:55 -0700318 [asyncResp, url](sdbusplus::message::message& m) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700319 std::vector<std::pair<std::string, dbus::utility::DBusPropertiesMap>>
320 interfacesProperties;
321 sdbusplus::message::object_path objPath;
322 m.read(objPath, interfacesProperties);
323 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
324 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
325 interface : interfacesProperties)
326 {
327 if (interface.first == "xyz.openbmc_project.Logging.Entry")
James Feist4cde5d92020-06-11 10:39:55 -0700328 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700329 for (const std::pair<std::string,
330 dbus::utility::DbusVariantType>& value :
331 interface.second)
Brian Mae1cc4822021-12-01 17:05:54 +0800332 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700333 if (value.first != "Message")
Brian Mae1cc4822021-12-01 17:05:54 +0800334 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700335 continue;
336 }
337 const std::string* type =
338 std::get_if<std::string>(&value.second);
339 if (type == nullptr)
340 {
341 // if this was our message, timeout will cover it
342 return;
343 }
344 fwAvailableTimer = nullptr;
345 if (*type ==
346 "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
347 {
348 redfish::messages::invalidUpload(asyncResp->res, url,
349 "Invalid archive");
350 }
351 else if (*type ==
352 "xyz.openbmc_project.Software.Image.Error."
353 "ManifestFileFailure")
354 {
355 redfish::messages::invalidUpload(asyncResp->res, url,
356 "Invalid manifest");
357 }
358 else if (
359 *type ==
360 "xyz.openbmc_project.Software.Image.Error.ImageFailure")
361 {
362 redfish::messages::invalidUpload(
363 asyncResp->res, url, "Invalid image format");
364 }
365 else if (
366 *type ==
367 "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
368 {
369 redfish::messages::invalidUpload(
370 asyncResp->res, url,
371 "Image version already exists");
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600372
Ed Tanous002d39b2022-05-31 08:59:27 -0700373 redfish::messages::resourceAlreadyExists(
374 asyncResp->res,
375 "UpdateService.v1_5_0.UpdateService", "Version",
376 "uploaded version");
377 }
378 else if (
379 *type ==
380 "xyz.openbmc_project.Software.Image.Error.BusyFailure")
381 {
382 redfish::messages::resourceExhaustion(asyncResp->res,
383 url);
384 }
385 else
386 {
387 redfish::messages::internalError(asyncResp->res);
Brian Mae1cc4822021-12-01 17:05:54 +0800388 }
389 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600390 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700391 }
James Feist4cde5d92020-06-11 10:39:55 -0700392 });
Andrew Geissler86adcd62019-04-18 10:58:05 -0500393}
Jennifer Lee729dae72018-04-24 15:59:34 -0700394
Andrew Geissler0554c982019-04-23 14:40:12 -0500395/**
396 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
397 * SimpleUpdate action.
398 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700399inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500400{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700401 BMCWEB_ROUTE(
402 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
Ed Tanoused398212021-06-09 17:05:54 -0700403 .privileges(redfish::privileges::postUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700404 .methods(boost::beast::http::verb::post)(
405 [&app](const crow::Request& req,
406 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000407 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700408 {
409 return;
410 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700411
Ed Tanous002d39b2022-05-31 08:59:27 -0700412 std::optional<std::string> transferProtocol;
413 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500414
Ed Tanous002d39b2022-05-31 08:59:27 -0700415 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
Andrew Geissler0554c982019-04-23 14:40:12 -0500416
Ed Tanous002d39b2022-05-31 08:59:27 -0700417 // User can pass in both TransferProtocol and ImageURI parameters or
418 // they can pass in just the ImageURI with the transfer protocol
419 // embedded within it.
420 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
421 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500422
Ed Tanous002d39b2022-05-31 08:59:27 -0700423 if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
424 transferProtocol, "ImageURI", imageURI))
425 {
426 BMCWEB_LOG_DEBUG
427 << "Missing TransferProtocol or ImageURI parameter";
428 return;
429 }
430 if (!transferProtocol)
431 {
432 // Must be option 2
433 // Verify ImageURI has transfer protocol in it
434 size_t separator = imageURI.find(':');
Andrew Geissler0554c982019-04-23 14:40:12 -0500435 if ((separator == std::string::npos) ||
436 ((separator + 1) > imageURI.size()))
437 {
438 messages::actionParameterValueTypeError(
439 asyncResp->res, imageURI, "ImageURI",
440 "UpdateService.SimpleUpdate");
Ed Tanous002d39b2022-05-31 08:59:27 -0700441 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
442 << imageURI;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530443 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530444 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700445 transferProtocol = imageURI.substr(0, separator);
446 // Ensure protocol is upper case for a common comparison path
447 // below
448 boost::to_upper(*transferProtocol);
449 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
450 << *transferProtocol;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530451
Ed Tanous002d39b2022-05-31 08:59:27 -0700452 // Adjust imageURI to not have the protocol on it for parsing
453 // below
454 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
455 imageURI = imageURI.substr(separator + 3);
456 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
457 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530458
Ed Tanous002d39b2022-05-31 08:59:27 -0700459 // OpenBMC currently only supports TFTP
460 if (*transferProtocol != "TFTP")
461 {
462 messages::actionParameterNotSupported(asyncResp->res,
463 "TransferProtocol",
464 "UpdateService.SimpleUpdate");
465 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
466 << *transferProtocol;
467 return;
468 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700469
Ed Tanous002d39b2022-05-31 08:59:27 -0700470 // Format should be <IP or Hostname>/<file> for imageURI
471 size_t separator = imageURI.find('/');
472 if ((separator == std::string::npos) ||
473 ((separator + 1) > imageURI.size()))
474 {
475 messages::actionParameterValueTypeError(
476 asyncResp->res, imageURI, "ImageURI",
477 "UpdateService.SimpleUpdate");
478 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
479 return;
480 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700481
Ed Tanous002d39b2022-05-31 08:59:27 -0700482 std::string tftpServer = imageURI.substr(0, separator);
483 std::string fwFile = imageURI.substr(separator + 1);
484 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700485
Ed Tanous002d39b2022-05-31 08:59:27 -0700486 // Setup callback for when new software detected
487 // Give TFTP 10 minutes to complete
488 monitorForSoftwareAvailable(
489 asyncResp, req,
490 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
491 600);
492
493 // TFTP can take up to 10 minutes depending on image size and
494 // connection speed. Return to caller as soon as the TFTP operation
495 // has been started. The callback above will ensure the activate
496 // is started once the download has completed
497 redfish::messages::success(asyncResp->res);
498
499 // Call TFTP service
500 crow::connections::systemBus->async_method_call(
501 [](const boost::system::error_code ec) {
502 if (ec)
503 {
504 // messages::internalError(asyncResp->res);
505 cleanUp();
506 BMCWEB_LOG_DEBUG << "error_code = " << ec;
507 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
508 }
509 else
510 {
511 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
512 }
513 },
514 "xyz.openbmc_project.Software.Download",
515 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
516 "DownloadViaTFTP", fwFile, tftpServer);
517
518 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700519 });
520}
521
Ed Tanousc2051d12022-05-11 12:21:55 -0700522inline void
523 handleUpdateServicePost(App& app, const crow::Request& req,
524 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
525{
Carson Labrado3ba00072022-06-06 19:40:56 +0000526 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700527 {
528 return;
529 }
530 BMCWEB_LOG_DEBUG << "doPost...";
531
532 // Setup callback for when new software detected
533 monitorForSoftwareAvailable(asyncResp, req, "/redfish/v1/UpdateService");
534
535 std::string filepath(
536 "/tmp/images/" +
537 boost::uuids::to_string(boost::uuids::random_generator()()));
538 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
539 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
540 std::ofstream::trunc);
541 out << req.body;
542 out.close();
543 BMCWEB_LOG_DEBUG << "file upload complete!!";
544}
545
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700546inline void requestRoutesUpdateService(App& app)
547{
548 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700549 .privileges(redfish::privileges::getUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700550 .methods(boost::beast::http::verb::get)(
551 [&app](const crow::Request& req,
552 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000553 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700554 {
555 return;
556 }
557 asyncResp->res.jsonValue["@odata.type"] =
558 "#UpdateService.v1_5_0.UpdateService";
559 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
560 asyncResp->res.jsonValue["Id"] = "UpdateService";
561 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
562 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700563
Ed Tanous32ca38a2022-05-11 12:36:59 -0700564#ifdef BMCWEB_ENABLE_REDFISH_UPDATESERVICE_OLD_POST_URL
Ed Tanous002d39b2022-05-31 08:59:27 -0700565 // See note about later on in this file about why this is neccesary
566 // This is "Wrong" per the standard, but is done temporarily to
567 // avoid noise in failing tests as people transition to having this
568 // option disabled
569 asyncResp->res.addHeader(boost::beast::http::field::allow,
570 "GET, PATCH, HEAD");
Ed Tanous32ca38a2022-05-11 12:36:59 -0700571#endif
572
Ed Tanous002d39b2022-05-31 08:59:27 -0700573 asyncResp->res.jsonValue["HttpPushUri"] =
574 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700575
Ed Tanous002d39b2022-05-31 08:59:27 -0700576 // UpdateService cannot be disabled
577 asyncResp->res.jsonValue["ServiceEnabled"] = true;
578 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
579 "/redfish/v1/UpdateService/FirmwareInventory";
580 // Get the MaxImageSizeBytes
581 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
582 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530583
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700584#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
Ed Tanous002d39b2022-05-31 08:59:27 -0700585 // Update Actions object.
586 nlohmann::json& updateSvcSimpleUpdate =
587 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
588 updateSvcSimpleUpdate["target"] =
589 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
590 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
591 "TFTP"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700592#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700593 // Get the current ApplyTime value
594 sdbusplus::asio::getProperty<std::string>(
595 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
596 "/xyz/openbmc_project/software/apply_time",
597 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
598 [asyncResp](const boost::system::error_code ec,
599 const std::string& applyTime) {
600 if (ec)
601 {
602 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
603 messages::internalError(asyncResp->res);
604 return;
605 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530606
Ed Tanous002d39b2022-05-31 08:59:27 -0700607 // Store the ApplyTime Value
608 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
609 "RequestedApplyTimes.Immediate")
610 {
611 asyncResp->res.jsonValue["HttpPushUriOptions"]
612 ["HttpPushUriApplyTime"]["ApplyTime"] =
613 "Immediate";
614 }
615 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
616 "RequestedApplyTimes.OnReset")
617 {
618 asyncResp->res.jsonValue["HttpPushUriOptions"]
619 ["HttpPushUriApplyTime"]["ApplyTime"] =
620 "OnReset";
621 }
622 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700623 });
624 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700625 .privileges(redfish::privileges::patchUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700626 .methods(boost::beast::http::verb::patch)(
627 [&app](const crow::Request& req,
628 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000629 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700630 {
631 return;
632 }
633 BMCWEB_LOG_DEBUG << "doPatch...";
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530634
Ed Tanous002d39b2022-05-31 08:59:27 -0700635 std::optional<nlohmann::json> pushUriOptions;
636 if (!json_util::readJsonPatch(req, asyncResp->res, "HttpPushUriOptions",
637 pushUriOptions))
638 {
639 return;
640 }
641
642 if (pushUriOptions)
643 {
644 std::optional<nlohmann::json> pushUriApplyTime;
645 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
646 "HttpPushUriApplyTime", pushUriApplyTime))
George Liu0fda0f12021-11-16 10:06:17 +0800647 {
648 return;
649 }
650
Ed Tanous002d39b2022-05-31 08:59:27 -0700651 if (pushUriApplyTime)
George Liu0fda0f12021-11-16 10:06:17 +0800652 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700653 std::optional<std::string> applyTime;
654 if (!json_util::readJson(*pushUriApplyTime, asyncResp->res,
655 "ApplyTime", applyTime))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700656 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700657 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700658 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700659
Ed Tanous002d39b2022-05-31 08:59:27 -0700660 if (applyTime)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700661 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700662 std::string applyTimeNewVal;
663 if (applyTime == "Immediate")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700664 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700665 applyTimeNewVal =
666 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
667 }
668 else if (applyTime == "OnReset")
669 {
670 applyTimeNewVal =
671 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
672 }
673 else
674 {
675 BMCWEB_LOG_INFO
676 << "ApplyTime value is not in the list of acceptable values";
677 messages::propertyValueNotInList(
678 asyncResp->res, *applyTime, "ApplyTime");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700679 return;
680 }
681
Ed Tanous002d39b2022-05-31 08:59:27 -0700682 // Set the requested image apply time value
683 crow::connections::systemBus->async_method_call(
684 [asyncResp](const boost::system::error_code ec) {
685 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700686 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700687 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
688 messages::internalError(asyncResp->res);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700689 return;
690 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700691 messages::success(asyncResp->res);
692 },
693 "xyz.openbmc_project.Settings",
694 "/xyz/openbmc_project/software/apply_time",
695 "org.freedesktop.DBus.Properties", "Set",
696 "xyz.openbmc_project.Software.ApplyTime",
697 "RequestedApplyTime",
698 dbus::utility::DbusVariantType{applyTimeNewVal});
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700699 }
George Liu0fda0f12021-11-16 10:06:17 +0800700 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700701 }
George Liu0fda0f12021-11-16 10:06:17 +0800702 });
Ed Tanousc2051d12022-05-11 12:21:55 -0700703
Ed Tanous4dc23f32022-05-11 11:32:19 -0700704// The "old" behavior of the update service URI causes redfish-service validator
705// failures when the Allow header is supported, given that in the spec,
706// UpdateService does not allow POST. in openbmc, we unfortunately reused that
707// resource as our HttpPushUri as well. A number of services, including the
708// openbmc tests, and documentation have hardcoded that erroneous API, instead
709// of relying on HttpPushUri as the spec requires. This option will exist
710// temporarily to allow the old behavior until Q4 2022, at which time it will be
711// removed.
712#ifdef BMCWEB_ENABLE_REDFISH_UPDATESERVICE_OLD_POST_URL
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700713 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700714 .privileges(redfish::privileges::postUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700715 .methods(boost::beast::http::verb::post)(
716 [&app](const crow::Request& req,
717 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
718 asyncResp->res.addHeader(
719 boost::beast::http::field::warning,
720 "299 - \"POST to /redfish/v1/UpdateService is deprecated. Use "
721 "the value contained within HttpPushUri.\"");
722 handleUpdateServicePost(app, req, asyncResp);
Ed Tanous4dc23f32022-05-11 11:32:19 -0700723 });
724#endif
725 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
726 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700727 .methods(boost::beast::http::verb::post)(
Ed Tanousc2051d12022-05-11 12:21:55 -0700728 std::bind_front(handleUpdateServicePost, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700729}
730
731inline void requestRoutesSoftwareInventoryCollection(App& app)
732{
733 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700734 .privileges(redfish::privileges::getSoftwareInventoryCollection)
Ed Tanous14766872022-03-15 10:44:42 -0700735 .methods(boost::beast::http::verb::get)(
736 [&app](const crow::Request& req,
737 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000738 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700739 {
740 return;
741 }
742 asyncResp->res.jsonValue["@odata.type"] =
743 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
744 asyncResp->res.jsonValue["@odata.id"] =
745 "/redfish/v1/UpdateService/FirmwareInventory";
746 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
747
748 crow::connections::systemBus->async_method_call(
749 [asyncResp](
750 const boost::system::error_code ec,
751 const dbus::utility::MapperGetSubTreeResponse& subtree) {
752 if (ec)
753 {
754 messages::internalError(asyncResp->res);
755 return;
756 }
757 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
758 asyncResp->res.jsonValue["Members@odata.count"] = 0;
759
760 for (const auto& obj : subtree)
761 {
762 sdbusplus::message::object_path path(obj.first);
763 std::string swId = path.filename();
764 if (swId.empty())
Ed Tanous14766872022-03-15 10:44:42 -0700765 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700766 messages::internalError(asyncResp->res);
767 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
Ed Tanous14766872022-03-15 10:44:42 -0700768 return;
769 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700770
Ed Tanous002d39b2022-05-31 08:59:27 -0700771 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
772 nlohmann::json::object_t member;
773 member["@odata.id"] =
774 "/redfish/v1/UpdateService/FirmwareInventory/" + swId;
775 members.push_back(std::move(member));
776 asyncResp->res.jsonValue["Members@odata.count"] =
777 members.size();
778 }
779 },
780 // Note that only firmware levels associated with a device
781 // are stored under /xyz/openbmc_project/software therefore
782 // to ensure only real FirmwareInventory items are returned,
783 // this full object path must be used here as input to
784 // mapper
785 "xyz.openbmc_project.ObjectMapper",
786 "/xyz/openbmc_project/object_mapper",
787 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
788 "/xyz/openbmc_project/software", static_cast<int32_t>(0),
789 std::array<const char*, 1>{"xyz.openbmc_project.Software.Version"});
790 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700791}
792/* Fill related item links (i.e. bmc, bios) in for inventory */
793inline static void
794 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
795 const std::string& purpose)
796{
797 if (purpose == fw_util::bmcPurpose)
798 {
799 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700800 nlohmann::json::object_t item;
801 item["@odata.id"] = "/redfish/v1/Managers/bmc";
802 relatedItem.push_back(std::move(item));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700803 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
804 }
805 else if (purpose == fw_util::biosPurpose)
806 {
807 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700808 nlohmann::json::object_t item;
809 item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
810 relatedItem.push_back(std::move(item));
Jiaqing Zhao1a6e51a2022-01-19 19:20:24 +0800811 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700812 }
813 else
814 {
815 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
816 }
817}
818
819inline void requestRoutesSoftwareInventory(App& app)
820{
821 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700822 .privileges(redfish::privileges::getSoftwareInventory)
Ed Tanous002d39b2022-05-31 08:59:27 -0700823 .methods(boost::beast::http::verb::get)(
824 [&app](const crow::Request& req,
825 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
826 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000827 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700828 {
829 return;
830 }
831 std::shared_ptr<std::string> swId =
832 std::make_shared<std::string>(param);
833
834 asyncResp->res.jsonValue["@odata.id"] =
835 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
836
837 crow::connections::systemBus->async_method_call(
838 [asyncResp,
839 swId](const boost::system::error_code ec,
840 const dbus::utility::MapperGetSubTreeResponse& subtree) {
841 BMCWEB_LOG_DEBUG << "doGet callback...";
842 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700843 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700844 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700845 return;
846 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700847
Ed Tanous002d39b2022-05-31 08:59:27 -0700848 // Ensure we find our input swId, otherwise return an error
849 bool found = false;
850 for (const std::pair<std::string,
851 std::vector<std::pair<
852 std::string, std::vector<std::string>>>>&
853 obj : subtree)
854 {
855 if (!boost::ends_with(obj.first, *swId))
856 {
857 continue;
858 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700859
Ed Tanous002d39b2022-05-31 08:59:27 -0700860 if (obj.second.empty())
861 {
862 continue;
863 }
864
865 found = true;
866 fw_util::getFwStatus(asyncResp, swId, obj.second[0].first);
867
868 crow::connections::systemBus->async_method_call(
869 [asyncResp, swId](const boost::system::error_code errorCode,
870 const dbus::utility::DBusPropertiesMap&
871 propertiesList) {
872 if (errorCode)
873 {
874 messages::internalError(asyncResp->res);
875 return;
876 }
877 const std::string* swInvPurpose = nullptr;
878 const std::string* version = nullptr;
879 for (const auto& property : propertiesList)
880 {
881 if (property.first == "Purpose")
882 {
883 swInvPurpose =
884 std::get_if<std::string>(&property.second);
885 }
886 if (property.first == "Version")
887 {
888 version =
889 std::get_if<std::string>(&property.second);
890 }
891 }
892
893 if (swInvPurpose == nullptr)
894 {
895 BMCWEB_LOG_DEBUG << "Can't find property \"Purpose\"!";
896 messages::internalError(asyncResp->res);
897 return;
898 }
899
900 BMCWEB_LOG_DEBUG << "swInvPurpose = " << *swInvPurpose;
901
902 if (version == nullptr)
903 {
904 BMCWEB_LOG_DEBUG << "Can't find property \"Version\"!";
905
906 messages::internalError(asyncResp->res);
907
908 return;
909 }
910 asyncResp->res.jsonValue["Version"] = *version;
911 asyncResp->res.jsonValue["Id"] = *swId;
912
913 // swInvPurpose is of format:
914 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
915 // Translate this to "ABC image"
916 size_t endDesc = swInvPurpose->rfind('.');
917 if (endDesc == std::string::npos)
918 {
919 messages::internalError(asyncResp->res);
920 return;
921 }
922 endDesc++;
923 if (endDesc >= swInvPurpose->size())
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700924 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700925 messages::internalError(asyncResp->res);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700926 return;
927 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700928
Ed Tanous002d39b2022-05-31 08:59:27 -0700929 std::string formatDesc = swInvPurpose->substr(endDesc);
930 asyncResp->res.jsonValue["Description"] =
931 formatDesc + " image";
932 getRelatedItems(asyncResp, *swInvPurpose);
933 },
934 obj.second[0].first, obj.first,
935 "org.freedesktop.DBus.Properties", "GetAll",
936 "xyz.openbmc_project.Software.Version");
937 }
938 if (!found)
939 {
940 BMCWEB_LOG_ERROR << "Input swID " << *swId << " not found!";
941 messages::resourceMissingAtURI(
942 asyncResp->res, crow::utility::urlFromPieces(
943 "redfish", "v1", "UpdateService",
944 "FirmwareInventory", *swId));
945 return;
946 }
947 asyncResp->res.jsonValue["@odata.type"] =
948 "#SoftwareInventory.v1_1_0.SoftwareInventory";
949 asyncResp->res.jsonValue["Name"] = "Software Inventory";
950 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700951
Ed Tanous002d39b2022-05-31 08:59:27 -0700952 asyncResp->res.jsonValue["Updateable"] = false;
953 fw_util::getFwUpdateableStatus(asyncResp, swId);
954 },
955 "xyz.openbmc_project.ObjectMapper",
956 "/xyz/openbmc_project/object_mapper",
957 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/",
958 static_cast<int32_t>(0),
959 std::array<const char*, 1>{"xyz.openbmc_project.Software.Version"});
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700960 });
961}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700962
963} // namespace redfish