blob: fe6024b6993f647d3e034490d3bb539bd4abb04b [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>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020025#include <sdbusplus/unpack_properties.hpp>
26#include <utils/dbus_utils.hpp>
Willy Tueee00132022-06-14 14:53:17 -070027#include <utils/sw_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace redfish
30{
Ed Tanous27826b52018-10-29 11:40:58 -070031
Andrew Geissler0e7de462019-03-04 19:11:54 -060032// Match signals added on software path
Ed Tanouscf9e4172022-12-21 09:30:16 -080033// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050034static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
Ed Tanouscf9e4172022-12-21 09:30:16 -080035// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050036static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060037// Only allow one update at a time
Ed Tanouscf9e4172022-12-21 09:30:16 -080038// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Andrew Geissler0e7de462019-03-04 19:11:54 -060039static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050040// Timer for software available
Ed Tanouscf9e4172022-12-21 09:30:16 -080041// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous271584a2019-07-09 16:24:22 -070042static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050043
John Edward Broadbent7e860f12021-04-08 15:57:16 -070044inline static void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050045{
46 fwUpdateInProgress = false;
47 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070048 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050049}
John Edward Broadbent7e860f12021-04-08 15:57:16 -070050inline static void activateImage(const std::string& objPath,
51 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050052{
53 BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
54 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +000055 [](const boost::system::error_code errorCode) {
Ed Tanous002d39b2022-05-31 08:59:27 -070056 if (errorCode)
57 {
58 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
59 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
60 }
Andrew Geissler86adcd62019-04-18 10:58:05 -050061 },
62 service, objPath, "org.freedesktop.DBus.Properties", "Set",
63 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
Ed Tanous168e20c2021-12-13 14:39:53 -080064 dbus::utility::DbusVariantType(
George Liu0fda0f12021-11-16 10:06:17 +080065 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active"));
Andrew Geissler86adcd62019-04-18 10:58:05 -050066}
Andrew Geissler0554c982019-04-23 14:40:12 -050067
68// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
69// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080070static void
71 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -050072 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050073{
Ed Tanousb9d36b42022-02-26 21:42:46 -080074 dbus::utility::DBusInteracesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -050075
76 sdbusplus::message::object_path objPath;
77
78 m.read(objPath, interfacesProperties);
79
80 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
Ed Tanouse3eb3d62022-12-21 11:56:07 -080081 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050082 {
83 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
84
85 if (interface.first == "xyz.openbmc_project.Software.Activation")
86 {
Andrew Geissler86adcd62019-04-18 10:58:05 -050087 // Retrieve service and activate
88 crow::connections::systemBus->async_method_call(
Ed Tanousa3e65892021-09-16 14:13:20 -070089 [objPath, asyncResp, payload(std::move(payload))](
90 const boost::system::error_code errorCode,
91 const std::vector<
92 std::pair<std::string, std::vector<std::string>>>&
93 objInfo) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -070094 if (errorCode)
95 {
96 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
97 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
Andrew Geissler0554c982019-04-23 14:40:12 -050098 if (asyncResp)
99 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700100 messages::internalError(asyncResp->res);
101 }
102 cleanUp();
103 return;
104 }
105 // Ensure we only got one service back
106 if (objInfo.size() != 1)
107 {
108 BMCWEB_LOG_ERROR << "Invalid Object Size "
109 << objInfo.size();
110 if (asyncResp)
111 {
112 messages::internalError(asyncResp->res);
113 }
114 cleanUp();
115 return;
116 }
117 // cancel timer only when
118 // xyz.openbmc_project.Software.Activation interface
119 // is added
120 fwAvailableTimer = nullptr;
121
122 activateImage(objPath.str, objInfo[0].first);
123 if (asyncResp)
124 {
125 std::shared_ptr<task::TaskData> task =
126 task::TaskData::createTask(
127 [](boost::system::error_code ec,
Patrick Williams59d494e2022-07-22 19:26:55 -0500128 sdbusplus::message_t& msg,
Ed Tanous002d39b2022-05-31 08:59:27 -0700129 const std::shared_ptr<task::TaskData>&
130 taskData) {
131 if (ec)
132 {
133 return task::completed;
134 }
135
136 std::string iface;
137 dbus::utility::DBusPropertiesMap values;
138
139 std::string index = std::to_string(taskData->index);
140 msg.read(iface, values);
141
142 if (iface == "xyz.openbmc_project.Software.Activation")
143 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000144 const std::string* state = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700145 for (const auto& property : values)
146 {
147 if (property.first == "Activation")
148 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000149 state = std::get_if<std::string>(
150 &property.second);
151 if (state == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700152 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700153 taskData->messages.emplace_back(
154 messages::internalError());
James Feist32898ce2020-03-10 16:16:52 -0700155 return task::completed;
156 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700157 }
158 }
James Feist32898ce2020-03-10 16:16:52 -0700159
Ed Tanous002d39b2022-05-31 08:59:27 -0700160 if (state == nullptr)
161 {
162 return !task::completed;
163 }
James Feist32898ce2020-03-10 16:16:52 -0700164
Ed Tanous11ba3972022-07-11 09:50:41 -0700165 if (state->ends_with("Invalid") ||
166 state->ends_with("Failed"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700167 {
168 taskData->state = "Exception";
169 taskData->status = "Warning";
170 taskData->messages.emplace_back(
171 messages::taskAborted(index));
172 return task::completed;
173 }
James Feiste5d50062020-05-11 17:29:00 -0700174
Ed Tanous11ba3972022-07-11 09:50:41 -0700175 if (state->ends_with("Staged"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700176 {
177 taskData->state = "Stopping";
178 taskData->messages.emplace_back(
179 messages::taskPaused(index));
180
181 // its staged, set a long timer to
182 // allow them time to complete the
183 // update (probably cycle the
184 // system) if this expires then
185 // task will be cancelled
186 taskData->extendTimer(std::chrono::hours(5));
187 return !task::completed;
188 }
189
Ed Tanous11ba3972022-07-11 09:50:41 -0700190 if (state->ends_with("Active"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700191 {
192 taskData->messages.emplace_back(
193 messages::taskCompletedOK(index));
194 taskData->state = "Completed";
195 return task::completed;
196 }
197 }
198 else if (
199 iface ==
200 "xyz.openbmc_project.Software.ActivationProgress")
201 {
202
203 const uint8_t* progress = nullptr;
204 for (const auto& property : values)
205 {
206 if (property.first == "Progress")
207 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000208 progress =
209 std::get_if<uint8_t>(&property.second);
210 if (progress == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700211 {
James Feist32898ce2020-03-10 16:16:52 -0700212 taskData->messages.emplace_back(
Ed Tanous002d39b2022-05-31 08:59:27 -0700213 messages::internalError());
214 return task::completed;
James Feist32898ce2020-03-10 16:16:52 -0700215 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700216 }
217 }
James Feist32898ce2020-03-10 16:16:52 -0700218
Ed Tanous002d39b2022-05-31 08:59:27 -0700219 if (progress == nullptr)
220 {
221 return !task::completed;
222 }
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000223 taskData->percentComplete = *progress;
Ed Tanous002d39b2022-05-31 08:59:27 -0700224 taskData->messages.emplace_back(
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000225 messages::taskProgressChanged(index,
226 *progress));
James Feist32898ce2020-03-10 16:16:52 -0700227
Ed Tanous002d39b2022-05-31 08:59:27 -0700228 // if we're getting status updates it's
229 // still alive, update timer
230 taskData->extendTimer(std::chrono::minutes(5));
231 }
232
233 // as firmware update often results in a
234 // reboot, the task may never "complete"
235 // unless it is an error
236
237 return !task::completed;
238 },
239 "type='signal',interface='org.freedesktop.DBus.Properties',"
240 "member='PropertiesChanged',path='" +
241 objPath.str + "'");
242 task->startTimer(std::chrono::minutes(5));
243 task->populateResp(asyncResp->res);
244 task->payload.emplace(std::move(payload));
245 }
246 fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500247 },
248 "xyz.openbmc_project.ObjectMapper",
249 "/xyz/openbmc_project/object_mapper",
250 "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500251 std::array<const char*, 1>{
Andrew Geissler86adcd62019-04-18 10:58:05 -0500252 "xyz.openbmc_project.Software.Activation"});
Patrick Williams62bafc02022-09-08 17:35:35 -0500253
254 break;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500255 }
256 }
257}
258
Andrew Geissler0554c982019-04-23 14:40:12 -0500259// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
260// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700261static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800262 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
263 const crow::Request& req, const std::string& url,
Gunnar Mills5d138942022-09-07 10:26:21 -0500264 int timeoutTimeSeconds = 25)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500265{
266 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800267 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500268 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500269 if (asyncResp)
270 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500271 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
272 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500273 return;
274 }
275
Andrew Geissler0554c982019-04-23 14:40:12 -0500276 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700277 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500278
Ed Tanous271584a2019-07-09 16:24:22 -0700279 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500280
281 fwAvailableTimer->async_wait(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500282 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700283 cleanUp();
284 if (ec == boost::asio::error::operation_aborted)
285 {
286 // expected, we were canceled before the timer completed.
287 return;
288 }
289 BMCWEB_LOG_ERROR
290 << "Timed out waiting for firmware object being created";
291 BMCWEB_LOG_ERROR << "FW image may has already been uploaded to server";
292 if (ec)
293 {
294 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
295 return;
296 }
297 if (asyncResp)
298 {
299 redfish::messages::internalError(asyncResp->res);
300 }
301 });
Ed Tanousa3e65892021-09-16 14:13:20 -0700302 task::Payload payload(req);
Patrick Williams59d494e2022-07-22 19:26:55 -0500303 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500304 BMCWEB_LOG_DEBUG << "Match fired";
Ed Tanousa3e65892021-09-16 14:13:20 -0700305 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500306 };
307
308 fwUpdateInProgress = true;
309
Patrick Williams59d494e2022-07-22 19:26:55 -0500310 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Andrew Geissler86adcd62019-04-18 10:58:05 -0500311 *crow::connections::systemBus,
312 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
313 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
314 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700315
Patrick Williams59d494e2022-07-22 19:26:55 -0500316 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
James Feist4cde5d92020-06-11 10:39:55 -0700317 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800318 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
319 "member='InterfacesAdded',"
320 "path='/xyz/openbmc_project/logging'",
Patrick Williams59d494e2022-07-22 19:26:55 -0500321 [asyncResp, url](sdbusplus::message_t& m) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700322 std::vector<std::pair<std::string, dbus::utility::DBusPropertiesMap>>
323 interfacesProperties;
324 sdbusplus::message::object_path objPath;
325 m.read(objPath, interfacesProperties);
326 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
327 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
328 interface : interfacesProperties)
329 {
330 if (interface.first == "xyz.openbmc_project.Logging.Entry")
James Feist4cde5d92020-06-11 10:39:55 -0700331 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700332 for (const std::pair<std::string,
333 dbus::utility::DbusVariantType>& value :
334 interface.second)
Brian Mae1cc4822021-12-01 17:05:54 +0800335 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700336 if (value.first != "Message")
Brian Mae1cc4822021-12-01 17:05:54 +0800337 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700338 continue;
339 }
340 const std::string* type =
341 std::get_if<std::string>(&value.second);
342 if (type == nullptr)
343 {
344 // if this was our message, timeout will cover it
345 return;
346 }
347 fwAvailableTimer = nullptr;
348 if (*type ==
349 "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
350 {
351 redfish::messages::invalidUpload(asyncResp->res, url,
352 "Invalid archive");
353 }
354 else if (*type ==
355 "xyz.openbmc_project.Software.Image.Error."
356 "ManifestFileFailure")
357 {
358 redfish::messages::invalidUpload(asyncResp->res, url,
359 "Invalid manifest");
360 }
361 else if (
362 *type ==
363 "xyz.openbmc_project.Software.Image.Error.ImageFailure")
364 {
365 redfish::messages::invalidUpload(
366 asyncResp->res, url, "Invalid image format");
367 }
368 else if (
369 *type ==
370 "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
371 {
372 redfish::messages::invalidUpload(
373 asyncResp->res, url,
374 "Image version already exists");
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600375
Ed Tanous002d39b2022-05-31 08:59:27 -0700376 redfish::messages::resourceAlreadyExists(
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800377 asyncResp->res, "UpdateService", "Version",
Ed Tanous002d39b2022-05-31 08:59:27 -0700378 "uploaded version");
379 }
380 else if (
381 *type ==
382 "xyz.openbmc_project.Software.Image.Error.BusyFailure")
383 {
384 redfish::messages::resourceExhaustion(asyncResp->res,
385 url);
386 }
387 else
388 {
389 redfish::messages::internalError(asyncResp->res);
Brian Mae1cc4822021-12-01 17:05:54 +0800390 }
391 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600392 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700393 }
James Feist4cde5d92020-06-11 10:39:55 -0700394 });
Andrew Geissler86adcd62019-04-18 10:58:05 -0500395}
Jennifer Lee729dae72018-04-24 15:59:34 -0700396
Andrew Geissler0554c982019-04-23 14:40:12 -0500397/**
398 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
399 * SimpleUpdate action.
400 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700401inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500402{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700403 BMCWEB_ROUTE(
404 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
Ed Tanoused398212021-06-09 17:05:54 -0700405 .privileges(redfish::privileges::postUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700406 .methods(boost::beast::http::verb::post)(
407 [&app](const crow::Request& req,
408 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000409 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700410 {
411 return;
412 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700413
Ed Tanous002d39b2022-05-31 08:59:27 -0700414 std::optional<std::string> transferProtocol;
415 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500416
Ed Tanous002d39b2022-05-31 08:59:27 -0700417 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
Andrew Geissler0554c982019-04-23 14:40:12 -0500418
Ed Tanous002d39b2022-05-31 08:59:27 -0700419 // User can pass in both TransferProtocol and ImageURI parameters or
420 // they can pass in just the ImageURI with the transfer protocol
421 // embedded within it.
422 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
423 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500424
Ed Tanous002d39b2022-05-31 08:59:27 -0700425 if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
426 transferProtocol, "ImageURI", imageURI))
427 {
428 BMCWEB_LOG_DEBUG
429 << "Missing TransferProtocol or ImageURI parameter";
430 return;
431 }
432 if (!transferProtocol)
433 {
434 // Must be option 2
435 // Verify ImageURI has transfer protocol in it
436 size_t separator = imageURI.find(':');
Andrew Geissler0554c982019-04-23 14:40:12 -0500437 if ((separator == std::string::npos) ||
438 ((separator + 1) > imageURI.size()))
439 {
440 messages::actionParameterValueTypeError(
441 asyncResp->res, imageURI, "ImageURI",
442 "UpdateService.SimpleUpdate");
Ed Tanous002d39b2022-05-31 08:59:27 -0700443 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
444 << imageURI;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530445 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530446 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700447 transferProtocol = imageURI.substr(0, separator);
448 // Ensure protocol is upper case for a common comparison path
449 // below
450 boost::to_upper(*transferProtocol);
451 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
452 << *transferProtocol;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530453
Ed Tanous002d39b2022-05-31 08:59:27 -0700454 // Adjust imageURI to not have the protocol on it for parsing
455 // below
456 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
457 imageURI = imageURI.substr(separator + 3);
458 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
459 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530460
Ed Tanous002d39b2022-05-31 08:59:27 -0700461 // OpenBMC currently only supports TFTP
462 if (*transferProtocol != "TFTP")
463 {
464 messages::actionParameterNotSupported(asyncResp->res,
465 "TransferProtocol",
466 "UpdateService.SimpleUpdate");
467 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
468 << *transferProtocol;
469 return;
470 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700471
Ed Tanous002d39b2022-05-31 08:59:27 -0700472 // Format should be <IP or Hostname>/<file> for imageURI
473 size_t separator = imageURI.find('/');
474 if ((separator == std::string::npos) ||
475 ((separator + 1) > imageURI.size()))
476 {
477 messages::actionParameterValueTypeError(
478 asyncResp->res, imageURI, "ImageURI",
479 "UpdateService.SimpleUpdate");
480 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
481 return;
482 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700483
Ed Tanous002d39b2022-05-31 08:59:27 -0700484 std::string tftpServer = imageURI.substr(0, separator);
485 std::string fwFile = imageURI.substr(separator + 1);
486 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700487
Ed Tanous002d39b2022-05-31 08:59:27 -0700488 // Setup callback for when new software detected
489 // Give TFTP 10 minutes to complete
490 monitorForSoftwareAvailable(
491 asyncResp, req,
492 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
493 600);
494
495 // TFTP can take up to 10 minutes depending on image size and
496 // connection speed. Return to caller as soon as the TFTP operation
497 // has been started. The callback above will ensure the activate
498 // is started once the download has completed
499 redfish::messages::success(asyncResp->res);
500
501 // Call TFTP service
502 crow::connections::systemBus->async_method_call(
503 [](const boost::system::error_code ec) {
504 if (ec)
505 {
506 // messages::internalError(asyncResp->res);
507 cleanUp();
508 BMCWEB_LOG_DEBUG << "error_code = " << ec;
509 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
510 }
511 else
512 {
513 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
514 }
515 },
516 "xyz.openbmc_project.Software.Download",
517 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
518 "DownloadViaTFTP", fwFile, tftpServer);
519
520 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700521 });
522}
523
Ed Tanousc2051d12022-05-11 12:21:55 -0700524inline void
525 handleUpdateServicePost(App& app, const crow::Request& req,
526 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
527{
Carson Labrado3ba00072022-06-06 19:40:56 +0000528 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700529 {
530 return;
531 }
532 BMCWEB_LOG_DEBUG << "doPost...";
533
534 // Setup callback for when new software detected
535 monitorForSoftwareAvailable(asyncResp, req, "/redfish/v1/UpdateService");
536
537 std::string filepath(
538 "/tmp/images/" +
539 boost::uuids::to_string(boost::uuids::random_generator()()));
540 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
541 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
542 std::ofstream::trunc);
543 out << req.body;
544 out.close();
545 BMCWEB_LOG_DEBUG << "file upload complete!!";
546}
547
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700548inline void requestRoutesUpdateService(App& app)
549{
550 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700551 .privileges(redfish::privileges::getUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700552 .methods(boost::beast::http::verb::get)(
553 [&app](const crow::Request& req,
554 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000555 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700556 {
557 return;
558 }
559 asyncResp->res.jsonValue["@odata.type"] =
560 "#UpdateService.v1_5_0.UpdateService";
561 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
562 asyncResp->res.jsonValue["Id"] = "UpdateService";
563 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
564 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700565
Ed Tanous32ca38a2022-05-11 12:36:59 -0700566#ifdef BMCWEB_ENABLE_REDFISH_UPDATESERVICE_OLD_POST_URL
Ed Tanous002d39b2022-05-31 08:59:27 -0700567 // See note about later on in this file about why this is neccesary
568 // This is "Wrong" per the standard, but is done temporarily to
569 // avoid noise in failing tests as people transition to having this
570 // option disabled
571 asyncResp->res.addHeader(boost::beast::http::field::allow,
572 "GET, PATCH, HEAD");
Ed Tanous32ca38a2022-05-11 12:36:59 -0700573#endif
574
Ed Tanous002d39b2022-05-31 08:59:27 -0700575 asyncResp->res.jsonValue["HttpPushUri"] =
576 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700577
Ed Tanous002d39b2022-05-31 08:59:27 -0700578 // UpdateService cannot be disabled
579 asyncResp->res.jsonValue["ServiceEnabled"] = true;
580 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
581 "/redfish/v1/UpdateService/FirmwareInventory";
582 // Get the MaxImageSizeBytes
583 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
584 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530585
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700586#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
Ed Tanous002d39b2022-05-31 08:59:27 -0700587 // Update Actions object.
588 nlohmann::json& updateSvcSimpleUpdate =
589 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
590 updateSvcSimpleUpdate["target"] =
591 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
592 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
593 "TFTP"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700594#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700595 // Get the current ApplyTime value
596 sdbusplus::asio::getProperty<std::string>(
597 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
598 "/xyz/openbmc_project/software/apply_time",
599 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
600 [asyncResp](const boost::system::error_code ec,
601 const std::string& applyTime) {
602 if (ec)
603 {
604 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
605 messages::internalError(asyncResp->res);
606 return;
607 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530608
Ed Tanous002d39b2022-05-31 08:59:27 -0700609 // Store the ApplyTime Value
610 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
611 "RequestedApplyTimes.Immediate")
612 {
613 asyncResp->res.jsonValue["HttpPushUriOptions"]
614 ["HttpPushUriApplyTime"]["ApplyTime"] =
615 "Immediate";
616 }
617 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
618 "RequestedApplyTimes.OnReset")
619 {
620 asyncResp->res.jsonValue["HttpPushUriOptions"]
621 ["HttpPushUriApplyTime"]["ApplyTime"] =
622 "OnReset";
623 }
624 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700625 });
626 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700627 .privileges(redfish::privileges::patchUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700628 .methods(boost::beast::http::verb::patch)(
629 [&app](const crow::Request& req,
630 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000631 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700632 {
633 return;
634 }
635 BMCWEB_LOG_DEBUG << "doPatch...";
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530636
Ed Tanous002d39b2022-05-31 08:59:27 -0700637 std::optional<nlohmann::json> pushUriOptions;
638 if (!json_util::readJsonPatch(req, asyncResp->res, "HttpPushUriOptions",
639 pushUriOptions))
640 {
641 return;
642 }
643
644 if (pushUriOptions)
645 {
646 std::optional<nlohmann::json> pushUriApplyTime;
647 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
648 "HttpPushUriApplyTime", pushUriApplyTime))
George Liu0fda0f12021-11-16 10:06:17 +0800649 {
650 return;
651 }
652
Ed Tanous002d39b2022-05-31 08:59:27 -0700653 if (pushUriApplyTime)
George Liu0fda0f12021-11-16 10:06:17 +0800654 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700655 std::optional<std::string> applyTime;
656 if (!json_util::readJson(*pushUriApplyTime, asyncResp->res,
657 "ApplyTime", applyTime))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700658 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700659 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700661
Ed Tanous002d39b2022-05-31 08:59:27 -0700662 if (applyTime)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700663 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700664 std::string applyTimeNewVal;
665 if (applyTime == "Immediate")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700666 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700667 applyTimeNewVal =
668 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
669 }
670 else if (applyTime == "OnReset")
671 {
672 applyTimeNewVal =
673 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
674 }
675 else
676 {
677 BMCWEB_LOG_INFO
678 << "ApplyTime value is not in the list of acceptable values";
679 messages::propertyValueNotInList(
680 asyncResp->res, *applyTime, "ApplyTime");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700681 return;
682 }
683
Ed Tanous002d39b2022-05-31 08:59:27 -0700684 // Set the requested image apply time value
685 crow::connections::systemBus->async_method_call(
686 [asyncResp](const boost::system::error_code ec) {
687 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700688 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700689 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
690 messages::internalError(asyncResp->res);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700691 return;
692 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700693 messages::success(asyncResp->res);
694 },
695 "xyz.openbmc_project.Settings",
696 "/xyz/openbmc_project/software/apply_time",
697 "org.freedesktop.DBus.Properties", "Set",
698 "xyz.openbmc_project.Software.ApplyTime",
699 "RequestedApplyTime",
700 dbus::utility::DbusVariantType{applyTimeNewVal});
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700701 }
George Liu0fda0f12021-11-16 10:06:17 +0800702 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700703 }
George Liu0fda0f12021-11-16 10:06:17 +0800704 });
Ed Tanousc2051d12022-05-11 12:21:55 -0700705
Ed Tanous4dc23f32022-05-11 11:32:19 -0700706// The "old" behavior of the update service URI causes redfish-service validator
707// failures when the Allow header is supported, given that in the spec,
708// UpdateService does not allow POST. in openbmc, we unfortunately reused that
709// resource as our HttpPushUri as well. A number of services, including the
710// openbmc tests, and documentation have hardcoded that erroneous API, instead
711// of relying on HttpPushUri as the spec requires. This option will exist
712// temporarily to allow the old behavior until Q4 2022, at which time it will be
713// removed.
714#ifdef BMCWEB_ENABLE_REDFISH_UPDATESERVICE_OLD_POST_URL
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700715 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700716 .privileges(redfish::privileges::postUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700717 .methods(boost::beast::http::verb::post)(
718 [&app](const crow::Request& req,
719 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
720 asyncResp->res.addHeader(
721 boost::beast::http::field::warning,
722 "299 - \"POST to /redfish/v1/UpdateService is deprecated. Use "
723 "the value contained within HttpPushUri.\"");
724 handleUpdateServicePost(app, req, asyncResp);
Ed Tanous4dc23f32022-05-11 11:32:19 -0700725 });
726#endif
727 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
728 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700729 .methods(boost::beast::http::verb::post)(
Ed Tanousc2051d12022-05-11 12:21:55 -0700730 std::bind_front(handleUpdateServicePost, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700731}
732
733inline void requestRoutesSoftwareInventoryCollection(App& app)
734{
735 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700736 .privileges(redfish::privileges::getSoftwareInventoryCollection)
Ed Tanous14766872022-03-15 10:44:42 -0700737 .methods(boost::beast::http::verb::get)(
738 [&app](const crow::Request& req,
739 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000740 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700741 {
742 return;
743 }
744 asyncResp->res.jsonValue["@odata.type"] =
745 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
746 asyncResp->res.jsonValue["@odata.id"] =
747 "/redfish/v1/UpdateService/FirmwareInventory";
748 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
749
750 crow::connections::systemBus->async_method_call(
751 [asyncResp](
752 const boost::system::error_code ec,
753 const dbus::utility::MapperGetSubTreeResponse& subtree) {
754 if (ec)
755 {
756 messages::internalError(asyncResp->res);
757 return;
758 }
759 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
760 asyncResp->res.jsonValue["Members@odata.count"] = 0;
761
762 for (const auto& obj : subtree)
763 {
764 sdbusplus::message::object_path path(obj.first);
765 std::string swId = path.filename();
766 if (swId.empty())
Ed Tanous14766872022-03-15 10:44:42 -0700767 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700768 messages::internalError(asyncResp->res);
769 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
Ed Tanous14766872022-03-15 10:44:42 -0700770 return;
771 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700772
Ed Tanous002d39b2022-05-31 08:59:27 -0700773 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
774 nlohmann::json::object_t member;
775 member["@odata.id"] =
776 "/redfish/v1/UpdateService/FirmwareInventory/" + swId;
777 members.push_back(std::move(member));
778 asyncResp->res.jsonValue["Members@odata.count"] =
779 members.size();
780 }
781 },
782 // Note that only firmware levels associated with a device
783 // are stored under /xyz/openbmc_project/software therefore
784 // to ensure only real FirmwareInventory items are returned,
785 // this full object path must be used here as input to
786 // mapper
787 "xyz.openbmc_project.ObjectMapper",
788 "/xyz/openbmc_project/object_mapper",
789 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
790 "/xyz/openbmc_project/software", static_cast<int32_t>(0),
791 std::array<const char*, 1>{"xyz.openbmc_project.Software.Version"});
792 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700793}
794/* Fill related item links (i.e. bmc, bios) in for inventory */
795inline static void
796 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
797 const std::string& purpose)
798{
Willy Tueee00132022-06-14 14:53:17 -0700799 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700800 {
801 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700802 nlohmann::json::object_t item;
803 item["@odata.id"] = "/redfish/v1/Managers/bmc";
804 relatedItem.push_back(std::move(item));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700805 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
806 }
Willy Tueee00132022-06-14 14:53:17 -0700807 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700808 {
809 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700810 nlohmann::json::object_t item;
811 item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
812 relatedItem.push_back(std::move(item));
Jiaqing Zhao1a6e51a2022-01-19 19:20:24 +0800813 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700814 }
815 else
816 {
817 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
818 }
819}
820
Willy Tuaf246602022-06-14 15:51:53 -0700821inline void
822 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
823 const std::string& service, const std::string& path,
824 const std::string& swId)
825{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200826 sdbusplus::asio::getAllProperties(
827 *crow::connections::systemBus, service, path,
828 "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -0700829 [asyncResp,
830 swId](const boost::system::error_code errorCode,
831 const dbus::utility::DBusPropertiesMap& propertiesList) {
832 if (errorCode)
833 {
834 messages::internalError(asyncResp->res);
835 return;
836 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200837
Willy Tuaf246602022-06-14 15:51:53 -0700838 const std::string* swInvPurpose = nullptr;
839 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200840
841 const bool success = sdbusplus::unpackPropertiesNoThrow(
842 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
843 swInvPurpose, "Version", version);
844
845 if (!success)
Willy Tuaf246602022-06-14 15:51:53 -0700846 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200847 messages::internalError(asyncResp->res);
848 return;
Willy Tuaf246602022-06-14 15:51:53 -0700849 }
850
851 if (swInvPurpose == nullptr)
852 {
853 BMCWEB_LOG_DEBUG << "Can't find property \"Purpose\"!";
854 messages::internalError(asyncResp->res);
855 return;
856 }
857
858 BMCWEB_LOG_DEBUG << "swInvPurpose = " << *swInvPurpose;
859
860 if (version == nullptr)
861 {
862 BMCWEB_LOG_DEBUG << "Can't find property \"Version\"!";
863
864 messages::internalError(asyncResp->res);
865
866 return;
867 }
868 asyncResp->res.jsonValue["Version"] = *version;
869 asyncResp->res.jsonValue["Id"] = swId;
870
871 // swInvPurpose is of format:
872 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
873 // Translate this to "ABC image"
874 size_t endDesc = swInvPurpose->rfind('.');
875 if (endDesc == std::string::npos)
876 {
877 messages::internalError(asyncResp->res);
878 return;
879 }
880 endDesc++;
881 if (endDesc >= swInvPurpose->size())
882 {
883 messages::internalError(asyncResp->res);
884 return;
885 }
886
887 std::string formatDesc = swInvPurpose->substr(endDesc);
888 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
889 getRelatedItems(asyncResp, *swInvPurpose);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200890 });
Willy Tuaf246602022-06-14 15:51:53 -0700891}
892
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700893inline void requestRoutesSoftwareInventory(App& app)
894{
895 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700896 .privileges(redfish::privileges::getSoftwareInventory)
Ed Tanous002d39b2022-05-31 08:59:27 -0700897 .methods(boost::beast::http::verb::get)(
898 [&app](const crow::Request& req,
899 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
900 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000901 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700902 {
903 return;
904 }
905 std::shared_ptr<std::string> swId =
906 std::make_shared<std::string>(param);
907
908 asyncResp->res.jsonValue["@odata.id"] =
909 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
910
911 crow::connections::systemBus->async_method_call(
912 [asyncResp,
913 swId](const boost::system::error_code ec,
914 const dbus::utility::MapperGetSubTreeResponse& subtree) {
915 BMCWEB_LOG_DEBUG << "doGet callback...";
916 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700917 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700918 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700919 return;
920 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700921
Ed Tanous002d39b2022-05-31 08:59:27 -0700922 // Ensure we find our input swId, otherwise return an error
923 bool found = false;
924 for (const std::pair<std::string,
925 std::vector<std::pair<
926 std::string, std::vector<std::string>>>>&
927 obj : subtree)
928 {
Ed Tanous11ba3972022-07-11 09:50:41 -0700929 if (!obj.first.ends_with(*swId))
Ed Tanous002d39b2022-05-31 08:59:27 -0700930 {
931 continue;
932 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700933
Ed Tanous002d39b2022-05-31 08:59:27 -0700934 if (obj.second.empty())
935 {
936 continue;
937 }
938
939 found = true;
Willy Tueee00132022-06-14 14:53:17 -0700940 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
Willy Tuaf246602022-06-14 15:51:53 -0700941 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
942 *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700943 }
944 if (!found)
945 {
946 BMCWEB_LOG_ERROR << "Input swID " << *swId << " not found!";
947 messages::resourceMissingAtURI(
948 asyncResp->res, crow::utility::urlFromPieces(
949 "redfish", "v1", "UpdateService",
950 "FirmwareInventory", *swId));
951 return;
952 }
953 asyncResp->res.jsonValue["@odata.type"] =
954 "#SoftwareInventory.v1_1_0.SoftwareInventory";
955 asyncResp->res.jsonValue["Name"] = "Software Inventory";
956 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700957
Ed Tanous002d39b2022-05-31 08:59:27 -0700958 asyncResp->res.jsonValue["Updateable"] = false;
Willy Tueee00132022-06-14 14:53:17 -0700959 sw_util::getSwUpdatableStatus(asyncResp, swId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700960 },
961 "xyz.openbmc_project.ObjectMapper",
962 "/xyz/openbmc_project/object_mapper",
963 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/",
964 static_cast<int32_t>(0),
965 std::array<const char*, 1>{"xyz.openbmc_project.Software.Version"});
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700966 });
967}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700968
969} // namespace redfish