blob: 795010dc71413b7ca46ba40f5db4e2e8aa4b16f5 [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"
John Edward Broadbent08d81ad2022-05-17 20:00:23 -070026#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080027#include "utils/dbus_utils.hpp"
28#include "utils/sw_utils.hpp"
29
Ed Tanousd093c992023-01-19 19:01:49 -080030#include <boost/algorithm/string/case_conv.hpp>
George Liue99073f2022-12-09 11:06:16 +080031#include <boost/system/error_code.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070032#include <boost/url/format.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070033#include <sdbusplus/asio/property.hpp>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080034#include <sdbusplus/bus/match.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020035#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050036
George Liu2b731192023-01-11 16:27:13 +080037#include <array>
George Liu0ed80c82020-05-12 16:06:27 +080038#include <filesystem>
George Liu2b731192023-01-11 16:27:13 +080039#include <string_view>
40
Ed Tanous1abe55e2018-09-05 08:30:59 -070041namespace redfish
42{
Ed Tanous27826b52018-10-29 11:40:58 -070043
Andrew Geissler0e7de462019-03-04 19:11:54 -060044// Match signals added on software path
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> fwUpdateMatcher;
Ed Tanouscf9e4172022-12-21 09:30:16 -080047// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050048static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060049// Only allow one update at a time
Ed Tanouscf9e4172022-12-21 09:30:16 -080050// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Andrew Geissler0e7de462019-03-04 19:11:54 -060051static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050052// Timer for software available
Ed Tanouscf9e4172022-12-21 09:30:16 -080053// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous271584a2019-07-09 16:24:22 -070054static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050055
John Edward Broadbent7e860f12021-04-08 15:57:16 -070056inline static void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050057{
58 fwUpdateInProgress = false;
59 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070060 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050061}
John Edward Broadbent7e860f12021-04-08 15:57:16 -070062inline static void activateImage(const std::string& objPath,
63 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050064{
65 BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
66 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -080067 [](const boost::system::error_code& errorCode) {
Ed Tanous002d39b2022-05-31 08:59:27 -070068 if (errorCode)
69 {
70 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
71 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
72 }
Andrew Geissler86adcd62019-04-18 10:58:05 -050073 },
74 service, objPath, "org.freedesktop.DBus.Properties", "Set",
75 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
Ed Tanous168e20c2021-12-13 14:39:53 -080076 dbus::utility::DbusVariantType(
George Liu0fda0f12021-11-16 10:06:17 +080077 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active"));
Andrew Geissler86adcd62019-04-18 10:58:05 -050078}
Andrew Geissler0554c982019-04-23 14:40:12 -050079
80// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
81// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080082static void
83 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -050084 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050085{
Ed Tanousb9d36b42022-02-26 21:42:46 -080086 dbus::utility::DBusInteracesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -050087
88 sdbusplus::message::object_path objPath;
89
90 m.read(objPath, interfacesProperties);
91
92 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
Ed Tanouse3eb3d62022-12-21 11:56:07 -080093 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050094 {
95 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
96
97 if (interface.first == "xyz.openbmc_project.Software.Activation")
98 {
Andrew Geissler86adcd62019-04-18 10:58:05 -050099 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +0800100 constexpr std::array<std::string_view, 1> interfaces = {
101 "xyz.openbmc_project.Software.Activation"};
102 dbus::utility::getDbusObject(
103 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700104 [objPath, asyncResp, payload(std::move(payload))](
George Liu2b731192023-01-11 16:27:13 +0800105 const boost::system::error_code& errorCode,
Ed Tanousa3e65892021-09-16 14:13:20 -0700106 const std::vector<
107 std::pair<std::string, std::vector<std::string>>>&
108 objInfo) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700109 if (errorCode)
110 {
111 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
112 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
Andrew Geissler0554c982019-04-23 14:40:12 -0500113 if (asyncResp)
114 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700115 messages::internalError(asyncResp->res);
116 }
117 cleanUp();
118 return;
119 }
120 // Ensure we only got one service back
121 if (objInfo.size() != 1)
122 {
123 BMCWEB_LOG_ERROR << "Invalid Object Size "
124 << objInfo.size();
125 if (asyncResp)
126 {
127 messages::internalError(asyncResp->res);
128 }
129 cleanUp();
130 return;
131 }
132 // cancel timer only when
133 // xyz.openbmc_project.Software.Activation interface
134 // is added
135 fwAvailableTimer = nullptr;
136
137 activateImage(objPath.str, objInfo[0].first);
138 if (asyncResp)
139 {
140 std::shared_ptr<task::TaskData> task =
141 task::TaskData::createTask(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800142 [](const boost::system::error_code& ec,
Patrick Williams59d494e2022-07-22 19:26:55 -0500143 sdbusplus::message_t& msg,
Ed Tanous002d39b2022-05-31 08:59:27 -0700144 const std::shared_ptr<task::TaskData>&
145 taskData) {
146 if (ec)
147 {
148 return task::completed;
149 }
150
151 std::string iface;
152 dbus::utility::DBusPropertiesMap values;
153
154 std::string index = std::to_string(taskData->index);
155 msg.read(iface, values);
156
157 if (iface == "xyz.openbmc_project.Software.Activation")
158 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000159 const std::string* state = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700160 for (const auto& property : values)
161 {
162 if (property.first == "Activation")
163 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000164 state = std::get_if<std::string>(
165 &property.second);
166 if (state == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700167 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700168 taskData->messages.emplace_back(
169 messages::internalError());
James Feist32898ce2020-03-10 16:16:52 -0700170 return task::completed;
171 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700172 }
173 }
James Feist32898ce2020-03-10 16:16:52 -0700174
Ed Tanous002d39b2022-05-31 08:59:27 -0700175 if (state == nullptr)
176 {
177 return !task::completed;
178 }
James Feist32898ce2020-03-10 16:16:52 -0700179
Ed Tanous11ba3972022-07-11 09:50:41 -0700180 if (state->ends_with("Invalid") ||
181 state->ends_with("Failed"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700182 {
183 taskData->state = "Exception";
184 taskData->status = "Warning";
185 taskData->messages.emplace_back(
186 messages::taskAborted(index));
187 return task::completed;
188 }
James Feiste5d50062020-05-11 17:29:00 -0700189
Ed Tanous11ba3972022-07-11 09:50:41 -0700190 if (state->ends_with("Staged"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700191 {
192 taskData->state = "Stopping";
193 taskData->messages.emplace_back(
194 messages::taskPaused(index));
195
196 // its staged, set a long timer to
197 // allow them time to complete the
198 // update (probably cycle the
199 // system) if this expires then
200 // task will be cancelled
201 taskData->extendTimer(std::chrono::hours(5));
202 return !task::completed;
203 }
204
Ed Tanous11ba3972022-07-11 09:50:41 -0700205 if (state->ends_with("Active"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700206 {
207 taskData->messages.emplace_back(
208 messages::taskCompletedOK(index));
209 taskData->state = "Completed";
210 return task::completed;
211 }
212 }
213 else if (
214 iface ==
215 "xyz.openbmc_project.Software.ActivationProgress")
216 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700217 const uint8_t* progress = nullptr;
218 for (const auto& property : values)
219 {
220 if (property.first == "Progress")
221 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000222 progress =
223 std::get_if<uint8_t>(&property.second);
224 if (progress == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700225 {
James Feist32898ce2020-03-10 16:16:52 -0700226 taskData->messages.emplace_back(
Ed Tanous002d39b2022-05-31 08:59:27 -0700227 messages::internalError());
228 return task::completed;
James Feist32898ce2020-03-10 16:16:52 -0700229 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700230 }
231 }
James Feist32898ce2020-03-10 16:16:52 -0700232
Ed Tanous002d39b2022-05-31 08:59:27 -0700233 if (progress == nullptr)
234 {
235 return !task::completed;
236 }
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000237 taskData->percentComplete = *progress;
Ed Tanous002d39b2022-05-31 08:59:27 -0700238 taskData->messages.emplace_back(
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000239 messages::taskProgressChanged(index,
240 *progress));
James Feist32898ce2020-03-10 16:16:52 -0700241
Ed Tanous002d39b2022-05-31 08:59:27 -0700242 // if we're getting status updates it's
243 // still alive, update timer
244 taskData->extendTimer(std::chrono::minutes(5));
245 }
246
247 // as firmware update often results in a
248 // reboot, the task may never "complete"
249 // unless it is an error
250
251 return !task::completed;
252 },
253 "type='signal',interface='org.freedesktop.DBus.Properties',"
254 "member='PropertiesChanged',path='" +
255 objPath.str + "'");
256 task->startTimer(std::chrono::minutes(5));
257 task->populateResp(asyncResp->res);
258 task->payload.emplace(std::move(payload));
259 }
260 fwUpdateInProgress = false;
George Liu2b731192023-01-11 16:27:13 +0800261 });
Patrick Williams62bafc02022-09-08 17:35:35 -0500262
263 break;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500264 }
265 }
266}
267
Andrew Geissler0554c982019-04-23 14:40:12 -0500268// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
269// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700270static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800271 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
272 const crow::Request& req, const std::string& url,
Gunnar Mills5d138942022-09-07 10:26:21 -0500273 int timeoutTimeSeconds = 25)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500274{
275 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800276 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500277 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500278 if (asyncResp)
279 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500280 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
281 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500282 return;
283 }
284
Andrew Geissler0554c982019-04-23 14:40:12 -0500285 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700286 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500287
Ed Tanous271584a2019-07-09 16:24:22 -0700288 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500289
290 fwAvailableTimer->async_wait(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500291 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700292 cleanUp();
293 if (ec == boost::asio::error::operation_aborted)
294 {
295 // expected, we were canceled before the timer completed.
296 return;
297 }
298 BMCWEB_LOG_ERROR
299 << "Timed out waiting for firmware object being created";
300 BMCWEB_LOG_ERROR << "FW image may has already been uploaded to server";
301 if (ec)
302 {
303 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
304 return;
305 }
306 if (asyncResp)
307 {
308 redfish::messages::internalError(asyncResp->res);
309 }
310 });
Ed Tanousa3e65892021-09-16 14:13:20 -0700311 task::Payload payload(req);
Patrick Williams59d494e2022-07-22 19:26:55 -0500312 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500313 BMCWEB_LOG_DEBUG << "Match fired";
Ed Tanousa3e65892021-09-16 14:13:20 -0700314 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500315 };
316
317 fwUpdateInProgress = true;
318
Patrick Williams59d494e2022-07-22 19:26:55 -0500319 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Andrew Geissler86adcd62019-04-18 10:58:05 -0500320 *crow::connections::systemBus,
321 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
322 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
323 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700324
Patrick Williams59d494e2022-07-22 19:26:55 -0500325 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
James Feist4cde5d92020-06-11 10:39:55 -0700326 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800327 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
328 "member='InterfacesAdded',"
329 "path='/xyz/openbmc_project/logging'",
Patrick Williams59d494e2022-07-22 19:26:55 -0500330 [asyncResp, url](sdbusplus::message_t& m) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700331 std::vector<std::pair<std::string, dbus::utility::DBusPropertiesMap>>
332 interfacesProperties;
333 sdbusplus::message::object_path objPath;
334 m.read(objPath, interfacesProperties);
335 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
336 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
337 interface : interfacesProperties)
338 {
339 if (interface.first == "xyz.openbmc_project.Logging.Entry")
James Feist4cde5d92020-06-11 10:39:55 -0700340 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700341 for (const std::pair<std::string,
342 dbus::utility::DbusVariantType>& value :
343 interface.second)
Brian Mae1cc4822021-12-01 17:05:54 +0800344 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700345 if (value.first != "Message")
Brian Mae1cc4822021-12-01 17:05:54 +0800346 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700347 continue;
348 }
349 const std::string* type =
350 std::get_if<std::string>(&value.second);
351 if (type == nullptr)
352 {
353 // if this was our message, timeout will cover it
354 return;
355 }
356 fwAvailableTimer = nullptr;
357 if (*type ==
358 "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
359 {
360 redfish::messages::invalidUpload(asyncResp->res, url,
361 "Invalid archive");
362 }
363 else if (*type ==
364 "xyz.openbmc_project.Software.Image.Error."
365 "ManifestFileFailure")
366 {
367 redfish::messages::invalidUpload(asyncResp->res, url,
368 "Invalid manifest");
369 }
370 else if (
371 *type ==
372 "xyz.openbmc_project.Software.Image.Error.ImageFailure")
373 {
374 redfish::messages::invalidUpload(
375 asyncResp->res, url, "Invalid image format");
376 }
377 else if (
378 *type ==
379 "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
380 {
381 redfish::messages::invalidUpload(
382 asyncResp->res, url,
383 "Image version already exists");
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600384
Ed Tanous002d39b2022-05-31 08:59:27 -0700385 redfish::messages::resourceAlreadyExists(
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800386 asyncResp->res, "UpdateService", "Version",
Ed Tanous002d39b2022-05-31 08:59:27 -0700387 "uploaded version");
388 }
389 else if (
390 *type ==
391 "xyz.openbmc_project.Software.Image.Error.BusyFailure")
392 {
393 redfish::messages::resourceExhaustion(asyncResp->res,
394 url);
395 }
396 else
397 {
398 redfish::messages::internalError(asyncResp->res);
Brian Mae1cc4822021-12-01 17:05:54 +0800399 }
400 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600401 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700402 }
James Feist4cde5d92020-06-11 10:39:55 -0700403 });
Andrew Geissler86adcd62019-04-18 10:58:05 -0500404}
Jennifer Lee729dae72018-04-24 15:59:34 -0700405
Andrew Geissler0554c982019-04-23 14:40:12 -0500406/**
407 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
408 * SimpleUpdate action.
409 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700410inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500411{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700412 BMCWEB_ROUTE(
413 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
Ed Tanoused398212021-06-09 17:05:54 -0700414 .privileges(redfish::privileges::postUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700415 .methods(boost::beast::http::verb::post)(
416 [&app](const crow::Request& req,
417 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000418 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700419 {
420 return;
421 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700422
Ed Tanous002d39b2022-05-31 08:59:27 -0700423 std::optional<std::string> transferProtocol;
424 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500425
Ed Tanous002d39b2022-05-31 08:59:27 -0700426 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
Andrew Geissler0554c982019-04-23 14:40:12 -0500427
Ed Tanous002d39b2022-05-31 08:59:27 -0700428 // User can pass in both TransferProtocol and ImageURI parameters or
429 // they can pass in just the ImageURI with the transfer protocol
430 // embedded within it.
431 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
432 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500433
Ed Tanous002d39b2022-05-31 08:59:27 -0700434 if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
435 transferProtocol, "ImageURI", imageURI))
436 {
437 BMCWEB_LOG_DEBUG
438 << "Missing TransferProtocol or ImageURI parameter";
439 return;
440 }
441 if (!transferProtocol)
442 {
443 // Must be option 2
444 // Verify ImageURI has transfer protocol in it
445 size_t separator = imageURI.find(':');
Andrew Geissler0554c982019-04-23 14:40:12 -0500446 if ((separator == std::string::npos) ||
447 ((separator + 1) > imageURI.size()))
448 {
449 messages::actionParameterValueTypeError(
450 asyncResp->res, imageURI, "ImageURI",
451 "UpdateService.SimpleUpdate");
Ed Tanous002d39b2022-05-31 08:59:27 -0700452 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
453 << imageURI;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530454 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530455 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700456 transferProtocol = imageURI.substr(0, separator);
457 // Ensure protocol is upper case for a common comparison path
458 // below
459 boost::to_upper(*transferProtocol);
460 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
461 << *transferProtocol;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530462
Ed Tanous002d39b2022-05-31 08:59:27 -0700463 // Adjust imageURI to not have the protocol on it for parsing
464 // below
465 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
466 imageURI = imageURI.substr(separator + 3);
467 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
468 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530469
Ed Tanous002d39b2022-05-31 08:59:27 -0700470 // OpenBMC currently only supports TFTP
471 if (*transferProtocol != "TFTP")
472 {
473 messages::actionParameterNotSupported(asyncResp->res,
474 "TransferProtocol",
475 "UpdateService.SimpleUpdate");
476 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
477 << *transferProtocol;
478 return;
479 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700480
Ed Tanous002d39b2022-05-31 08:59:27 -0700481 // Format should be <IP or Hostname>/<file> for imageURI
482 size_t separator = imageURI.find('/');
483 if ((separator == std::string::npos) ||
484 ((separator + 1) > imageURI.size()))
485 {
486 messages::actionParameterValueTypeError(
487 asyncResp->res, imageURI, "ImageURI",
488 "UpdateService.SimpleUpdate");
489 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
490 return;
491 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700492
Ed Tanous002d39b2022-05-31 08:59:27 -0700493 std::string tftpServer = imageURI.substr(0, separator);
494 std::string fwFile = imageURI.substr(separator + 1);
495 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700496
Ed Tanous002d39b2022-05-31 08:59:27 -0700497 // Setup callback for when new software detected
498 // Give TFTP 10 minutes to complete
499 monitorForSoftwareAvailable(
500 asyncResp, req,
501 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
502 600);
503
504 // TFTP can take up to 10 minutes depending on image size and
505 // connection speed. Return to caller as soon as the TFTP operation
506 // has been started. The callback above will ensure the activate
507 // is started once the download has completed
508 redfish::messages::success(asyncResp->res);
509
510 // Call TFTP service
511 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800512 [](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700513 if (ec)
514 {
515 // messages::internalError(asyncResp->res);
516 cleanUp();
517 BMCWEB_LOG_DEBUG << "error_code = " << ec;
518 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
519 }
520 else
521 {
522 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
523 }
524 },
525 "xyz.openbmc_project.Software.Download",
526 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
527 "DownloadViaTFTP", fwFile, tftpServer);
528
529 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700530 });
531}
532
George Liu0ed80c82020-05-12 16:06:27 +0800533inline void uploadImageFile(crow::Response& res, std::string_view body)
534{
535 std::filesystem::path 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 // set the permission of the file to 640
Patrick Williams89492a12023-05-10 07:51:34 -0500542 std::filesystem::perms permission = std::filesystem::perms::owner_read |
543 std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800544 std::filesystem::permissions(filepath, permission);
545 out << body;
546
547 if (out.bad())
548 {
549 messages::internalError(res);
550 cleanUp();
551 }
552}
553
554inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
555 const std::string& applyTime)
556{
557 std::string applyTimeNewVal;
558 if (applyTime == "Immediate")
559 {
560 applyTimeNewVal =
561 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
562 }
563 else if (applyTime == "OnReset")
564 {
565 applyTimeNewVal =
566 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
567 }
568 else
569 {
570 BMCWEB_LOG_INFO
571 << "ApplyTime value is not in the list of acceptable values";
572 messages::propertyValueNotInList(asyncResp->res, applyTime,
573 "ApplyTime");
574 return;
575 }
576
577 // Set the requested image apply time value
578 crow::connections::systemBus->async_method_call(
Ed Tanous81c4e332023-05-18 10:30:34 -0700579 [asyncResp](const boost::system::error_code& ec) {
George Liu0ed80c82020-05-12 16:06:27 +0800580 if (ec)
581 {
582 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
583 messages::internalError(asyncResp->res);
584 return;
585 }
586 messages::success(asyncResp->res);
587 },
588 "xyz.openbmc_project.Settings",
589 "/xyz/openbmc_project/software/apply_time",
590 "org.freedesktop.DBus.Properties", "Set",
591 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
592 dbus::utility::DbusVariantType{applyTimeNewVal});
593}
594
595inline void
596 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
597 const MultipartParser& parser)
598{
599 const std::string* uploadData = nullptr;
600 std::optional<std::string> applyTime = "OnReset";
601 bool targetFound = false;
602 for (const FormPart& formpart : parser.mime_fields)
603 {
604 boost::beast::http::fields::const_iterator it =
605 formpart.fields.find("Content-Disposition");
606 if (it == formpart.fields.end())
607 {
608 BMCWEB_LOG_ERROR << "Couldn't find Content-Disposition";
609 return;
610 }
611 BMCWEB_LOG_INFO << "Parsing value " << it->value();
612
613 // The construction parameters of param_list must start with `;`
614 size_t index = it->value().find(';');
615 if (index == std::string::npos)
616 {
617 continue;
618 }
619
Patrick Williams89492a12023-05-10 07:51:34 -0500620 for (const auto& param :
George Liu0ed80c82020-05-12 16:06:27 +0800621 boost::beast::http::param_list{it->value().substr(index)})
622 {
623 if (param.first != "name" || param.second.empty())
624 {
625 continue;
626 }
627
628 if (param.second == "UpdateParameters")
629 {
630 std::vector<std::string> targets;
631 nlohmann::json content =
632 nlohmann::json::parse(formpart.content);
633 if (!json_util::readJson(content, asyncResp->res, "Targets",
634 targets, "@Redfish.OperationApplyTime",
635 applyTime))
636 {
637 return;
638 }
639 if (targets.size() != 1)
640 {
641 messages::propertyValueFormatError(asyncResp->res,
642 "Targets", "");
643 return;
644 }
645 if (targets[0] != "/redfish/v1/Managers/bmc")
646 {
647 messages::propertyValueNotInList(asyncResp->res,
648 "Targets/0", targets[0]);
649 return;
650 }
651 targetFound = true;
652 }
653 else if (param.second == "UpdateFile")
654 {
655 uploadData = &(formpart.content);
656 }
657 }
658 }
659
660 if (uploadData == nullptr)
661 {
662 BMCWEB_LOG_ERROR << "Upload data is NULL";
663 messages::propertyMissing(asyncResp->res, "UpdateFile");
664 return;
665 }
666 if (!targetFound)
667 {
668 messages::propertyMissing(asyncResp->res, "targets");
669 return;
670 }
671
672 setApplyTime(asyncResp, *applyTime);
673
674 uploadImageFile(asyncResp->res, *uploadData);
675}
676
Ed Tanousc2051d12022-05-11 12:21:55 -0700677inline void
678 handleUpdateServicePost(App& app, const crow::Request& req,
679 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
680{
Carson Labrado3ba00072022-06-06 19:40:56 +0000681 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700682 {
683 return;
684 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500685 std::string_view contentType = req.getHeaderValue("Content-Type");
Ed Tanousc2051d12022-05-11 12:21:55 -0700686
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500687 BMCWEB_LOG_DEBUG << "doPost: contentType=" << contentType;
Ed Tanousc2051d12022-05-11 12:21:55 -0700688
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500689 // Make sure that content type is application/octet-stream or
690 // multipart/form-data
691 if (boost::iequals(contentType, "application/octet-stream"))
George Liu0ed80c82020-05-12 16:06:27 +0800692 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500693 // Setup callback for when new software detected
694 monitorForSoftwareAvailable(asyncResp, req,
695 "/redfish/v1/UpdateService");
696
George Liu0ed80c82020-05-12 16:06:27 +0800697 uploadImageFile(asyncResp->res, req.body());
George Liu0ed80c82020-05-12 16:06:27 +0800698 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500699 else if (contentType.starts_with("multipart/form-data"))
George Liu0ed80c82020-05-12 16:06:27 +0800700 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500701 MultipartParser parser;
702
703 // Setup callback for when new software detected
704 monitorForSoftwareAvailable(asyncResp, req,
705 "/redfish/v1/UpdateService");
706
707 ParserError ec = parser.parse(req);
708 if (ec != ParserError::PARSER_SUCCESS)
709 {
710 // handle error
711 BMCWEB_LOG_ERROR << "MIME parse failed, ec : "
712 << static_cast<int>(ec);
713 messages::internalError(asyncResp->res);
714 return;
715 }
716 updateMultipartContext(asyncResp, parser);
George Liu0ed80c82020-05-12 16:06:27 +0800717 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500718 else
719 {
720 BMCWEB_LOG_DEBUG << "Bad content type specified:" << contentType;
721 asyncResp->res.result(boost::beast::http::status::bad_request);
722 }
Ed Tanousc2051d12022-05-11 12:21:55 -0700723}
724
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700725inline void requestRoutesUpdateService(App& app)
726{
727 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700728 .privileges(redfish::privileges::getUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700729 .methods(boost::beast::http::verb::get)(
730 [&app](const crow::Request& req,
731 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000732 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700733 {
734 return;
735 }
736 asyncResp->res.jsonValue["@odata.type"] =
George Liu0ed80c82020-05-12 16:06:27 +0800737 "#UpdateService.v1_11_1.UpdateService";
Ed Tanous002d39b2022-05-31 08:59:27 -0700738 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
739 asyncResp->res.jsonValue["Id"] = "UpdateService";
740 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
741 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700742
Ed Tanous002d39b2022-05-31 08:59:27 -0700743 asyncResp->res.jsonValue["HttpPushUri"] =
744 "/redfish/v1/UpdateService/update";
George Liu0ed80c82020-05-12 16:06:27 +0800745 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
746 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700747
Ed Tanous002d39b2022-05-31 08:59:27 -0700748 // UpdateService cannot be disabled
749 asyncResp->res.jsonValue["ServiceEnabled"] = true;
750 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
751 "/redfish/v1/UpdateService/FirmwareInventory";
752 // Get the MaxImageSizeBytes
753 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
754 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530755
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700756#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
Ed Tanous002d39b2022-05-31 08:59:27 -0700757 // Update Actions object.
758 nlohmann::json& updateSvcSimpleUpdate =
759 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
760 updateSvcSimpleUpdate["target"] =
761 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
762 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
763 "TFTP"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700764#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700765 // Get the current ApplyTime value
766 sdbusplus::asio::getProperty<std::string>(
767 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
768 "/xyz/openbmc_project/software/apply_time",
769 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800770 [asyncResp](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700771 const std::string& applyTime) {
772 if (ec)
773 {
774 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
775 messages::internalError(asyncResp->res);
776 return;
777 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530778
Ed Tanous002d39b2022-05-31 08:59:27 -0700779 // Store the ApplyTime Value
780 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
781 "RequestedApplyTimes.Immediate")
782 {
783 asyncResp->res.jsonValue["HttpPushUriOptions"]
784 ["HttpPushUriApplyTime"]["ApplyTime"] =
785 "Immediate";
786 }
787 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
788 "RequestedApplyTimes.OnReset")
789 {
790 asyncResp->res.jsonValue["HttpPushUriOptions"]
791 ["HttpPushUriApplyTime"]["ApplyTime"] =
792 "OnReset";
793 }
794 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700795 });
796 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700797 .privileges(redfish::privileges::patchUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700798 .methods(boost::beast::http::verb::patch)(
799 [&app](const crow::Request& req,
800 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000801 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700802 {
803 return;
804 }
805 BMCWEB_LOG_DEBUG << "doPatch...";
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530806
Ed Tanous002d39b2022-05-31 08:59:27 -0700807 std::optional<nlohmann::json> pushUriOptions;
808 if (!json_util::readJsonPatch(req, asyncResp->res, "HttpPushUriOptions",
809 pushUriOptions))
810 {
811 return;
812 }
813
814 if (pushUriOptions)
815 {
816 std::optional<nlohmann::json> pushUriApplyTime;
817 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
818 "HttpPushUriApplyTime", pushUriApplyTime))
George Liu0fda0f12021-11-16 10:06:17 +0800819 {
820 return;
821 }
822
Ed Tanous002d39b2022-05-31 08:59:27 -0700823 if (pushUriApplyTime)
George Liu0fda0f12021-11-16 10:06:17 +0800824 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700825 std::optional<std::string> applyTime;
826 if (!json_util::readJson(*pushUriApplyTime, asyncResp->res,
827 "ApplyTime", applyTime))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700828 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700829 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700830 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700831
Ed Tanous002d39b2022-05-31 08:59:27 -0700832 if (applyTime)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700833 {
George Liu0ed80c82020-05-12 16:06:27 +0800834 setApplyTime(asyncResp, *applyTime);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700835 }
George Liu0fda0f12021-11-16 10:06:17 +0800836 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700837 }
George Liu0fda0f12021-11-16 10:06:17 +0800838 });
Ed Tanousc2051d12022-05-11 12:21:55 -0700839
Ed Tanous4dc23f32022-05-11 11:32:19 -0700840 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
841 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700842 .methods(boost::beast::http::verb::post)(
Ed Tanousc2051d12022-05-11 12:21:55 -0700843 std::bind_front(handleUpdateServicePost, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700844}
845
846inline void requestRoutesSoftwareInventoryCollection(App& app)
847{
848 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700849 .privileges(redfish::privileges::getSoftwareInventoryCollection)
Ed Tanous14766872022-03-15 10:44:42 -0700850 .methods(boost::beast::http::verb::get)(
851 [&app](const crow::Request& req,
852 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000853 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700854 {
855 return;
856 }
857 asyncResp->res.jsonValue["@odata.type"] =
858 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
859 asyncResp->res.jsonValue["@odata.id"] =
860 "/redfish/v1/UpdateService/FirmwareInventory";
861 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
John Edward Broadbent08d81ad2022-05-17 20:00:23 -0700862 const std::array<const std::string_view, 1> iface = {
George Liue99073f2022-12-09 11:06:16 +0800863 "xyz.openbmc_project.Software.Version"};
Ed Tanous002d39b2022-05-31 08:59:27 -0700864
John Edward Broadbent08d81ad2022-05-17 20:00:23 -0700865 redfish::collection_util::getCollectionMembers(
866 asyncResp,
867 boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"),
868 iface, "/xyz/openbmc_project/software");
Ed Tanous002d39b2022-05-31 08:59:27 -0700869 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700870}
871/* Fill related item links (i.e. bmc, bios) in for inventory */
872inline static void
Ed Tanousac106bf2023-06-07 09:24:59 -0700873 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700874 const std::string& purpose)
875{
Willy Tueee00132022-06-14 14:53:17 -0700876 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700877 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700878 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700879 nlohmann::json::object_t item;
880 item["@odata.id"] = "/redfish/v1/Managers/bmc";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500881 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700882 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
883 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700884 }
Willy Tueee00132022-06-14 14:53:17 -0700885 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700886 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700887 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700888 nlohmann::json::object_t item;
889 item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500890 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700891 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
892 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700893 }
894 else
895 {
896 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
897 }
898}
899
Willy Tuaf246602022-06-14 15:51:53 -0700900inline void
901 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
902 const std::string& service, const std::string& path,
903 const std::string& swId)
904{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200905 sdbusplus::asio::getAllProperties(
906 *crow::connections::systemBus, service, path,
907 "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -0700908 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800909 swId](const boost::system::error_code& errorCode,
Willy Tuaf246602022-06-14 15:51:53 -0700910 const dbus::utility::DBusPropertiesMap& propertiesList) {
911 if (errorCode)
912 {
913 messages::internalError(asyncResp->res);
914 return;
915 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200916
Willy Tuaf246602022-06-14 15:51:53 -0700917 const std::string* swInvPurpose = nullptr;
918 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200919
920 const bool success = sdbusplus::unpackPropertiesNoThrow(
921 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
922 swInvPurpose, "Version", version);
923
924 if (!success)
Willy Tuaf246602022-06-14 15:51:53 -0700925 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200926 messages::internalError(asyncResp->res);
927 return;
Willy Tuaf246602022-06-14 15:51:53 -0700928 }
929
930 if (swInvPurpose == nullptr)
931 {
932 BMCWEB_LOG_DEBUG << "Can't find property \"Purpose\"!";
933 messages::internalError(asyncResp->res);
934 return;
935 }
936
937 BMCWEB_LOG_DEBUG << "swInvPurpose = " << *swInvPurpose;
938
939 if (version == nullptr)
940 {
941 BMCWEB_LOG_DEBUG << "Can't find property \"Version\"!";
942
943 messages::internalError(asyncResp->res);
944
945 return;
946 }
947 asyncResp->res.jsonValue["Version"] = *version;
948 asyncResp->res.jsonValue["Id"] = swId;
949
950 // swInvPurpose is of format:
951 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
952 // Translate this to "ABC image"
953 size_t endDesc = swInvPurpose->rfind('.');
954 if (endDesc == std::string::npos)
955 {
956 messages::internalError(asyncResp->res);
957 return;
958 }
959 endDesc++;
960 if (endDesc >= swInvPurpose->size())
961 {
962 messages::internalError(asyncResp->res);
963 return;
964 }
965
966 std::string formatDesc = swInvPurpose->substr(endDesc);
967 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
968 getRelatedItems(asyncResp, *swInvPurpose);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200969 });
Willy Tuaf246602022-06-14 15:51:53 -0700970}
971
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700972inline void requestRoutesSoftwareInventory(App& app)
973{
974 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700975 .privileges(redfish::privileges::getSoftwareInventory)
Ed Tanous002d39b2022-05-31 08:59:27 -0700976 .methods(boost::beast::http::verb::get)(
977 [&app](const crow::Request& req,
978 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
979 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000980 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700981 {
982 return;
983 }
984 std::shared_ptr<std::string> swId =
985 std::make_shared<std::string>(param);
986
Ed Tanousef4c65b2023-04-24 15:28:50 -0700987 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
988 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700989
George Liue99073f2022-12-09 11:06:16 +0800990 constexpr std::array<std::string_view, 1> interfaces = {
991 "xyz.openbmc_project.Software.Version"};
992 dbus::utility::getSubTree(
993 "/", 0, interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -0700994 [asyncResp,
George Liue99073f2022-12-09 11:06:16 +0800995 swId](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700996 const dbus::utility::MapperGetSubTreeResponse& subtree) {
997 BMCWEB_LOG_DEBUG << "doGet callback...";
998 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700999 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001000 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001001 return;
1002 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001003
Ed Tanous002d39b2022-05-31 08:59:27 -07001004 // Ensure we find our input swId, otherwise return an error
1005 bool found = false;
1006 for (const std::pair<std::string,
1007 std::vector<std::pair<
1008 std::string, std::vector<std::string>>>>&
1009 obj : subtree)
1010 {
Ed Tanous11ba3972022-07-11 09:50:41 -07001011 if (!obj.first.ends_with(*swId))
Ed Tanous002d39b2022-05-31 08:59:27 -07001012 {
1013 continue;
1014 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001015
Ed Tanous002d39b2022-05-31 08:59:27 -07001016 if (obj.second.empty())
1017 {
1018 continue;
1019 }
1020
1021 found = true;
Willy Tueee00132022-06-14 14:53:17 -07001022 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
Willy Tuaf246602022-06-14 15:51:53 -07001023 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1024 *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001025 }
1026 if (!found)
1027 {
Myung Baeb90d14f2023-05-31 14:40:39 -05001028 BMCWEB_LOG_WARNING << "Input swID " << *swId << " not found!";
Ed Tanous002d39b2022-05-31 08:59:27 -07001029 messages::resourceMissingAtURI(
Ed Tanousef4c65b2023-04-24 15:28:50 -07001030 asyncResp->res,
1031 boost::urls::format(
1032 "/redfish/v1/UpdateService/FirmwareInventory/{}",
1033 *swId));
Ed Tanous002d39b2022-05-31 08:59:27 -07001034 return;
1035 }
1036 asyncResp->res.jsonValue["@odata.type"] =
1037 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1038 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1039 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001040
Ed Tanous002d39b2022-05-31 08:59:27 -07001041 asyncResp->res.jsonValue["Updateable"] = false;
Willy Tueee00132022-06-14 14:53:17 -07001042 sw_util::getSwUpdatableStatus(asyncResp, swId);
George Liue99073f2022-12-09 11:06:16 +08001043 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001044 });
1045}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001046
1047} // namespace redfish