blob: 8617071e4591b88dcd6c40af1a9a00ed0fb6030f [file] [log] [blame]
Jennifer Lee729dae72018-04-24 15:59:34 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
Tejas Patild61e5192021-06-04 15:49:35 +053018#include "bmcweb_config.h"
19
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080020#include "app.hpp"
21#include "dbus_utility.hpp"
George Liu0ed80c82020-05-12 16:06:27 +080022#include "multipart_parser.hpp"
Ed Tanous2c6ffdb2023-06-28 11:28:38 -070023#include "ossl_random.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080024#include "query.hpp"
25#include "registries/privilege_registry.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080026#include "task.hpp"
John Edward Broadbent08d81ad2022-05-17 20:00:23 -070027#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080028#include "utils/dbus_utils.hpp"
29#include "utils/sw_utils.hpp"
30
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>
Ed Tanous7cb59f62022-05-05 11:48:31 -070039#include <optional>
40#include <string>
George Liu2b731192023-01-11 16:27:13 +080041#include <string_view>
42
Ed Tanous1abe55e2018-09-05 08:30:59 -070043namespace redfish
44{
Ed Tanous27826b52018-10-29 11:40:58 -070045
Andrew Geissler0e7de462019-03-04 19:11:54 -060046// Match signals added on software path
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> fwUpdateMatcher;
Ed Tanouscf9e4172022-12-21 09:30:16 -080049// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050050static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060051// Only allow one update at a time
Ed Tanouscf9e4172022-12-21 09:30:16 -080052// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Andrew Geissler0e7de462019-03-04 19:11:54 -060053static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050054// Timer for software available
Ed Tanouscf9e4172022-12-21 09:30:16 -080055// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous271584a2019-07-09 16:24:22 -070056static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050057
Ed Tanousdf254f22024-04-01 13:25:46 -070058inline void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050059{
60 fwUpdateInProgress = false;
61 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070062 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050063}
Ed Tanousdf254f22024-04-01 13:25:46 -070064
65inline void activateImage(const std::string& objPath,
66 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050067{
Ed Tanous62598e32023-07-17 17:06:25 -070068 BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
George Liu9ae226f2023-06-21 17:56:46 +080069 sdbusplus::asio::setProperty(
70 *crow::connections::systemBus, service, objPath,
71 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
72 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
Ed Tanous8b242752023-06-27 17:17:13 -070073 [](const boost::system::error_code& ec) {
74 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -070075 {
Ed Tanous62598e32023-07-17 17:06:25 -070076 BMCWEB_LOG_DEBUG("error_code = {}", ec);
77 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -070078 }
Patrick Williams5a39f772023-10-20 11:20:21 -050079 });
Andrew Geissler86adcd62019-04-18 10:58:05 -050080}
Andrew Geissler0554c982019-04-23 14:40:12 -050081
82// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
83// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080084static void
85 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -050086 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050087{
Michael Shen80f79a42023-08-24 13:41:53 +000088 dbus::utility::DBusInterfacesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -050089
90 sdbusplus::message::object_path objPath;
91
92 m.read(objPath, interfacesProperties);
93
Ed Tanous62598e32023-07-17 17:06:25 -070094 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
Ed Tanouse3eb3d62022-12-21 11:56:07 -080095 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050096 {
Ed Tanous62598e32023-07-17 17:06:25 -070097 BMCWEB_LOG_DEBUG("interface = {}", interface.first);
Andrew Geissler86adcd62019-04-18 10:58:05 -050098
99 if (interface.first == "xyz.openbmc_project.Software.Activation")
100 {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500101 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +0800102 constexpr std::array<std::string_view, 1> interfaces = {
103 "xyz.openbmc_project.Software.Activation"};
104 dbus::utility::getDbusObject(
105 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700106 [objPath, asyncResp, payload(std::move(payload))](
Ed Tanous8b242752023-06-27 17:17:13 -0700107 const boost::system::error_code& ec,
Ed Tanousa3e65892021-09-16 14:13:20 -0700108 const std::vector<
109 std::pair<std::string, std::vector<std::string>>>&
110 objInfo) mutable {
Ed Tanous8b242752023-06-27 17:17:13 -0700111 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700112 {
Ed Tanous62598e32023-07-17 17:06:25 -0700113 BMCWEB_LOG_DEBUG("error_code = {}", ec);
114 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Andrew Geissler0554c982019-04-23 14:40:12 -0500115 if (asyncResp)
116 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700117 messages::internalError(asyncResp->res);
118 }
119 cleanUp();
120 return;
121 }
122 // Ensure we only got one service back
123 if (objInfo.size() != 1)
124 {
Ed Tanous62598e32023-07-17 17:06:25 -0700125 BMCWEB_LOG_ERROR("Invalid Object Size {}", objInfo.size());
Ed Tanous002d39b2022-05-31 08:59:27 -0700126 if (asyncResp)
127 {
128 messages::internalError(asyncResp->res);
129 }
130 cleanUp();
131 return;
132 }
133 // cancel timer only when
134 // xyz.openbmc_project.Software.Activation interface
135 // is added
136 fwAvailableTimer = nullptr;
137
138 activateImage(objPath.str, objInfo[0].first);
139 if (asyncResp)
140 {
141 std::shared_ptr<task::TaskData> task =
142 task::TaskData::createTask(
Ed Tanous8b242752023-06-27 17:17:13 -0700143 [](const boost::system::error_code& ec2,
Patrick Williams59d494e2022-07-22 19:26:55 -0500144 sdbusplus::message_t& msg,
Ed Tanous002d39b2022-05-31 08:59:27 -0700145 const std::shared_ptr<task::TaskData>&
146 taskData) {
Ed Tanous8b242752023-06-27 17:17:13 -0700147 if (ec2)
Ed Tanous002d39b2022-05-31 08:59:27 -0700148 {
149 return task::completed;
150 }
151
152 std::string iface;
153 dbus::utility::DBusPropertiesMap values;
154
155 std::string index = std::to_string(taskData->index);
156 msg.read(iface, values);
157
158 if (iface == "xyz.openbmc_project.Software.Activation")
159 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000160 const std::string* state = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700161 for (const auto& property : values)
162 {
163 if (property.first == "Activation")
164 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000165 state = std::get_if<std::string>(
166 &property.second);
167 if (state == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700168 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700169 taskData->messages.emplace_back(
170 messages::internalError());
James Feist32898ce2020-03-10 16:16:52 -0700171 return task::completed;
172 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700173 }
174 }
James Feist32898ce2020-03-10 16:16:52 -0700175
Ed Tanous002d39b2022-05-31 08:59:27 -0700176 if (state == nullptr)
177 {
178 return !task::completed;
179 }
James Feist32898ce2020-03-10 16:16:52 -0700180
Ed Tanous11ba3972022-07-11 09:50:41 -0700181 if (state->ends_with("Invalid") ||
182 state->ends_with("Failed"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700183 {
184 taskData->state = "Exception";
185 taskData->status = "Warning";
186 taskData->messages.emplace_back(
187 messages::taskAborted(index));
188 return task::completed;
189 }
James Feiste5d50062020-05-11 17:29:00 -0700190
Ed Tanous11ba3972022-07-11 09:50:41 -0700191 if (state->ends_with("Staged"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700192 {
193 taskData->state = "Stopping";
194 taskData->messages.emplace_back(
195 messages::taskPaused(index));
196
197 // its staged, set a long timer to
198 // allow them time to complete the
199 // update (probably cycle the
200 // system) if this expires then
Ed Tanous8ece0e42024-01-02 13:16:50 -0800201 // task will be canceled
Ed Tanous002d39b2022-05-31 08:59:27 -0700202 taskData->extendTimer(std::chrono::hours(5));
203 return !task::completed;
204 }
205
Ed Tanous11ba3972022-07-11 09:50:41 -0700206 if (state->ends_with("Active"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700207 {
208 taskData->messages.emplace_back(
209 messages::taskCompletedOK(index));
210 taskData->state = "Completed";
211 return task::completed;
212 }
213 }
214 else if (
215 iface ==
216 "xyz.openbmc_project.Software.ActivationProgress")
217 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700218 const uint8_t* progress = nullptr;
219 for (const auto& property : values)
220 {
221 if (property.first == "Progress")
222 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000223 progress =
224 std::get_if<uint8_t>(&property.second);
225 if (progress == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700226 {
James Feist32898ce2020-03-10 16:16:52 -0700227 taskData->messages.emplace_back(
Ed Tanous002d39b2022-05-31 08:59:27 -0700228 messages::internalError());
229 return task::completed;
James Feist32898ce2020-03-10 16:16:52 -0700230 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700231 }
232 }
James Feist32898ce2020-03-10 16:16:52 -0700233
Ed Tanous002d39b2022-05-31 08:59:27 -0700234 if (progress == nullptr)
235 {
236 return !task::completed;
237 }
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000238 taskData->percentComplete = *progress;
Ed Tanous002d39b2022-05-31 08:59:27 -0700239 taskData->messages.emplace_back(
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000240 messages::taskProgressChanged(index,
241 *progress));
James Feist32898ce2020-03-10 16:16:52 -0700242
Ed Tanous002d39b2022-05-31 08:59:27 -0700243 // if we're getting status updates it's
244 // still alive, update timer
245 taskData->extendTimer(std::chrono::minutes(5));
246 }
247
248 // as firmware update often results in a
249 // reboot, the task may never "complete"
250 // unless it is an error
251
252 return !task::completed;
Patrick Williams5a39f772023-10-20 11:20:21 -0500253 },
Ed Tanous002d39b2022-05-31 08:59:27 -0700254 "type='signal',interface='org.freedesktop.DBus.Properties',"
255 "member='PropertiesChanged',path='" +
256 objPath.str + "'");
257 task->startTimer(std::chrono::minutes(5));
258 task->populateResp(asyncResp->res);
259 task->payload.emplace(std::move(payload));
260 }
261 fwUpdateInProgress = false;
Patrick Williams5a39f772023-10-20 11:20:21 -0500262 });
Patrick Williams62bafc02022-09-08 17:35:35 -0500263
264 break;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500265 }
266 }
267}
268
Myung Bae8549b952023-08-16 15:18:19 -0400269inline void afterAvailbleTimerAsyncWait(
270 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
271 const boost::system::error_code& ec)
272{
273 cleanUp();
274 if (ec == boost::asio::error::operation_aborted)
275 {
276 // expected, we were canceled before the timer completed.
277 return;
278 }
279 BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
280 BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
281 if (ec)
282 {
283 BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
284 return;
285 }
286 if (asyncResp)
287 {
288 redfish::messages::internalError(asyncResp->res);
289 }
290}
291
292inline void
293 handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
294 const std::string& url, const std::string& type)
295{
296 if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
297 {
298 redfish::messages::invalidUpload(asyncResp->res, url,
299 "Invalid archive");
300 }
301 else if (type ==
302 "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
303 {
304 redfish::messages::invalidUpload(asyncResp->res, url,
305 "Invalid manifest");
306 }
307 else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
308 {
309 redfish::messages::invalidUpload(asyncResp->res, url,
310 "Invalid image format");
311 }
312 else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
313 {
314 redfish::messages::invalidUpload(asyncResp->res, url,
315 "Image version already exists");
316
317 redfish::messages::resourceAlreadyExists(
318 asyncResp->res, "UpdateService", "Version", "uploaded version");
319 }
320 else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
321 {
322 redfish::messages::resourceExhaustion(asyncResp->res, url);
323 }
Myung Bae4034a652023-08-17 08:47:35 -0400324 else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
Myung Bae8549b952023-08-16 15:18:19 -0400325 {
Myung Bae4034a652023-08-17 08:47:35 -0400326 redfish::messages::invalidUpload(asyncResp->res, url,
327 "Incompatible image version");
328 }
329 else if (type ==
330 "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
331 {
332 redfish::messages::invalidUpload(asyncResp->res, url,
333 "Update Access Key Expired");
334 }
335 else if (type ==
336 "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
337 {
338 redfish::messages::invalidUpload(asyncResp->res, url,
339 "Invalid image signature");
340 }
341 else if (type ==
342 "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
343 type == "xyz.openbmc_project.Software.Version.Error.HostFile")
344 {
345 BMCWEB_LOG_ERROR("Software Image Error type={}", type);
Myung Bae8549b952023-08-16 15:18:19 -0400346 redfish::messages::internalError(asyncResp->res);
347 }
Myung Bae4034a652023-08-17 08:47:35 -0400348 else
349 {
350 // Unrelated error types. Ignored
351 BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
352 return;
353 }
354 // Clear the timer
355 fwAvailableTimer = nullptr;
Myung Bae8549b952023-08-16 15:18:19 -0400356}
357
358inline void
359 afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
360 const std::string& url, sdbusplus::message_t& m)
361{
Michael Shen80f79a42023-08-24 13:41:53 +0000362 dbus::utility::DBusInterfacesMap interfacesProperties;
Myung Bae8549b952023-08-16 15:18:19 -0400363 sdbusplus::message::object_path objPath;
364 m.read(objPath, interfacesProperties);
365 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
366 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
367 interface : interfacesProperties)
368 {
369 if (interface.first == "xyz.openbmc_project.Logging.Entry")
370 {
371 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
372 value : interface.second)
373 {
374 if (value.first != "Message")
375 {
376 continue;
377 }
378 const std::string* type =
379 std::get_if<std::string>(&value.second);
380 if (type == nullptr)
381 {
382 // if this was our message, timeout will cover it
383 return;
384 }
Myung Bae8549b952023-08-16 15:18:19 -0400385 handleUpdateErrorType(asyncResp, url, *type);
386 }
387 }
388 }
389}
390
Andrew Geissler0554c982019-04-23 14:40:12 -0500391// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
392// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700393static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800394 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
395 const crow::Request& req, const std::string& url,
Gunnar Mills5d138942022-09-07 10:26:21 -0500396 int timeoutTimeSeconds = 25)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500397{
398 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800399 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500400 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500401 if (asyncResp)
402 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500403 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
404 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500405 return;
406 }
407
Ed Tanous8e8245d2024-04-11 22:21:38 -0700408 if (req.ioService == nullptr)
409 {
410 messages::internalError(asyncResp->res);
411 return;
412 }
413
Andrew Geissler0554c982019-04-23 14:40:12 -0500414 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700415 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500416
Ed Tanous271584a2019-07-09 16:24:22 -0700417 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500418
419 fwAvailableTimer->async_wait(
Myung Bae8549b952023-08-16 15:18:19 -0400420 std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
421
Ed Tanousa3e65892021-09-16 14:13:20 -0700422 task::Payload payload(req);
Patrick Williams59d494e2022-07-22 19:26:55 -0500423 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
Ed Tanous62598e32023-07-17 17:06:25 -0700424 BMCWEB_LOG_DEBUG("Match fired");
Ed Tanousa3e65892021-09-16 14:13:20 -0700425 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500426 };
427
428 fwUpdateInProgress = true;
429
Patrick Williams59d494e2022-07-22 19:26:55 -0500430 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Andrew Geissler86adcd62019-04-18 10:58:05 -0500431 *crow::connections::systemBus,
432 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
433 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
434 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700435
Patrick Williams59d494e2022-07-22 19:26:55 -0500436 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
James Feist4cde5d92020-06-11 10:39:55 -0700437 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800438 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
439 "member='InterfacesAdded',"
440 "path='/xyz/openbmc_project/logging'",
Myung Bae8549b952023-08-16 15:18:19 -0400441 std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500442}
Jennifer Lee729dae72018-04-24 15:59:34 -0700443
Ed Tanousf86bcc82023-08-25 09:34:07 -0700444struct TftpUrl
445{
446 std::string fwFile;
447 std::string tftpServer;
448};
449
450inline std::optional<TftpUrl>
451 parseTftpUrl(std::string imageURI,
452 std::optional<std::string> transferProtocol,
453 crow::Response& res)
454{
455 if (imageURI.find("://") == std::string::npos)
456 {
457 if (imageURI.starts_with("/"))
458 {
459 messages::actionParameterValueTypeError(
460 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
461 return std::nullopt;
462 }
463 if (!transferProtocol)
464 {
465 messages::actionParameterValueTypeError(
466 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
467 return std::nullopt;
468 }
469 // OpenBMC currently only supports TFTP
470 if (*transferProtocol != "TFTP")
471 {
472 messages::actionParameterNotSupported(res, "TransferProtocol",
473 *transferProtocol);
474 BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
475 *transferProtocol);
476 return std::nullopt;
477 }
478 imageURI = "tftp://" + imageURI;
479 }
480
481 boost::system::result<boost::urls::url> url =
482 boost::urls::parse_absolute_uri(imageURI);
483 if (!url)
484 {
485 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
486 "UpdateService.SimpleUpdate");
487
488 return std::nullopt;
489 }
490 url->normalize();
491
492 if (url->scheme() != "tftp")
493 {
494 messages::actionParameterNotSupported(res, "ImageURI", imageURI);
495 return std::nullopt;
496 }
497 std::string path(url->encoded_path());
498 if (path.size() < 2)
499 {
500 messages::actionParameterNotSupported(res, "ImageURI", imageURI);
501 return std::nullopt;
502 }
503 path.erase(0, 1);
504 std::string host(url->encoded_host_and_port());
505 return TftpUrl{path, host};
506}
507
Andrew Geissler0554c982019-04-23 14:40:12 -0500508/**
509 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
510 * SimpleUpdate action.
511 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700512inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500513{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700514 BMCWEB_ROUTE(
515 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
Ed Tanoused398212021-06-09 17:05:54 -0700516 .privileges(redfish::privileges::postUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700517 .methods(boost::beast::http::verb::post)(
518 [&app](const crow::Request& req,
519 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000520 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700521 {
522 return;
523 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700524
Ed Tanous002d39b2022-05-31 08:59:27 -0700525 std::optional<std::string> transferProtocol;
526 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500527
Ed Tanous62598e32023-07-17 17:06:25 -0700528 BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
Andrew Geissler0554c982019-04-23 14:40:12 -0500529
Ed Tanous002d39b2022-05-31 08:59:27 -0700530 // User can pass in both TransferProtocol and ImageURI parameters or
531 // they can pass in just the ImageURI with the transfer protocol
532 // embedded within it.
533 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
534 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500535
Ed Tanous002d39b2022-05-31 08:59:27 -0700536 if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
537 transferProtocol, "ImageURI", imageURI))
538 {
Ed Tanous62598e32023-07-17 17:06:25 -0700539 BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
Ed Tanous002d39b2022-05-31 08:59:27 -0700540 return;
541 }
Ed Tanousf86bcc82023-08-25 09:34:07 -0700542 std::optional<TftpUrl> ret = parseTftpUrl(imageURI, transferProtocol,
543 asyncResp->res);
544 if (!ret)
Ed Tanous002d39b2022-05-31 08:59:27 -0700545 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700546 return;
547 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700548
Ed Tanousf86bcc82023-08-25 09:34:07 -0700549 BMCWEB_LOG_DEBUG("Server: {} File: {}", ret->tftpServer, ret->fwFile);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700550
Ed Tanous002d39b2022-05-31 08:59:27 -0700551 // Setup callback for when new software detected
552 // Give TFTP 10 minutes to complete
553 monitorForSoftwareAvailable(
554 asyncResp, req,
555 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
556 600);
557
558 // TFTP can take up to 10 minutes depending on image size and
559 // connection speed. Return to caller as soon as the TFTP operation
560 // has been started. The callback above will ensure the activate
561 // is started once the download has completed
562 redfish::messages::success(asyncResp->res);
563
564 // Call TFTP service
565 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800566 [](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700567 if (ec)
568 {
569 // messages::internalError(asyncResp->res);
570 cleanUp();
Ed Tanous62598e32023-07-17 17:06:25 -0700571 BMCWEB_LOG_DEBUG("error_code = {}", ec);
572 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -0700573 }
574 else
575 {
Ed Tanous62598e32023-07-17 17:06:25 -0700576 BMCWEB_LOG_DEBUG("Call to DownloaViaTFTP Success");
Ed Tanous002d39b2022-05-31 08:59:27 -0700577 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500578 },
Ed Tanous002d39b2022-05-31 08:59:27 -0700579 "xyz.openbmc_project.Software.Download",
580 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
Ed Tanousf86bcc82023-08-25 09:34:07 -0700581 "DownloadViaTFTP", ret->fwFile, ret->tftpServer);
Ed Tanous002d39b2022-05-31 08:59:27 -0700582
Ed Tanous62598e32023-07-17 17:06:25 -0700583 BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
Patrick Williams5a39f772023-10-20 11:20:21 -0500584 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700585}
586
George Liu0ed80c82020-05-12 16:06:27 +0800587inline void uploadImageFile(crow::Response& res, std::string_view body)
588{
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700589 std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
590
Ed Tanous62598e32023-07-17 17:06:25 -0700591 BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
George Liu0ed80c82020-05-12 16:06:27 +0800592 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
593 std::ofstream::trunc);
594 // set the permission of the file to 640
Patrick Williams89492a12023-05-10 07:51:34 -0500595 std::filesystem::perms permission = std::filesystem::perms::owner_read |
596 std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800597 std::filesystem::permissions(filepath, permission);
598 out << body;
599
600 if (out.bad())
601 {
602 messages::internalError(res);
603 cleanUp();
604 }
605}
606
607inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
608 const std::string& applyTime)
609{
610 std::string applyTimeNewVal;
611 if (applyTime == "Immediate")
612 {
613 applyTimeNewVal =
614 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
615 }
616 else if (applyTime == "OnReset")
617 {
618 applyTimeNewVal =
619 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
620 }
621 else
622 {
Ed Tanous62598e32023-07-17 17:06:25 -0700623 BMCWEB_LOG_INFO(
624 "ApplyTime value is not in the list of acceptable values");
George Liu0ed80c82020-05-12 16:06:27 +0800625 messages::propertyValueNotInList(asyncResp->res, applyTime,
626 "ApplyTime");
627 return;
628 }
629
Ed Tanousd02aad32024-02-13 14:43:34 -0800630 setDbusProperty(asyncResp, "xyz.openbmc_project.Settings",
631 sdbusplus::message::object_path(
632 "/xyz/openbmc_project/software/apply_time"),
633 "xyz.openbmc_project.Software.ApplyTime",
634 "RequestedApplyTime", "ApplyTime", applyTimeNewVal);
George Liu0ed80c82020-05-12 16:06:27 +0800635}
636
637inline void
638 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
639 const MultipartParser& parser)
640{
641 const std::string* uploadData = nullptr;
642 std::optional<std::string> applyTime = "OnReset";
643 bool targetFound = false;
644 for (const FormPart& formpart : parser.mime_fields)
645 {
646 boost::beast::http::fields::const_iterator it =
647 formpart.fields.find("Content-Disposition");
648 if (it == formpart.fields.end())
649 {
Ed Tanous62598e32023-07-17 17:06:25 -0700650 BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
George Liu0ed80c82020-05-12 16:06:27 +0800651 return;
652 }
Ed Tanous62598e32023-07-17 17:06:25 -0700653 BMCWEB_LOG_INFO("Parsing value {}", it->value());
George Liu0ed80c82020-05-12 16:06:27 +0800654
655 // The construction parameters of param_list must start with `;`
656 size_t index = it->value().find(';');
657 if (index == std::string::npos)
658 {
659 continue;
660 }
661
Patrick Williams89492a12023-05-10 07:51:34 -0500662 for (const auto& param :
George Liu0ed80c82020-05-12 16:06:27 +0800663 boost::beast::http::param_list{it->value().substr(index)})
664 {
665 if (param.first != "name" || param.second.empty())
666 {
667 continue;
668 }
669
670 if (param.second == "UpdateParameters")
671 {
672 std::vector<std::string> targets;
673 nlohmann::json content =
674 nlohmann::json::parse(formpart.content);
Ed Tanous7cb59f62022-05-05 11:48:31 -0700675 nlohmann::json::object_t* obj =
676 content.get_ptr<nlohmann::json::object_t*>();
677 if (obj == nullptr)
678 {
679 messages::propertyValueFormatError(asyncResp->res, targets,
680 "UpdateParameters");
681 return;
682 }
683
684 if (!json_util::readJsonObject(
685 *obj, asyncResp->res, "Targets", targets,
686 "@Redfish.OperationApplyTime", applyTime))
George Liu0ed80c82020-05-12 16:06:27 +0800687 {
688 return;
689 }
690 if (targets.size() != 1)
691 {
Ed Tanous7cb59f62022-05-05 11:48:31 -0700692 messages::propertyValueFormatError(asyncResp->res, targets,
693 "Targets");
George Liu0ed80c82020-05-12 16:06:27 +0800694 return;
695 }
696 if (targets[0] != "/redfish/v1/Managers/bmc")
697 {
Ed Tanous7cb59f62022-05-05 11:48:31 -0700698 messages::propertyValueNotInList(asyncResp->res, targets[0],
699 "Targets/0");
George Liu0ed80c82020-05-12 16:06:27 +0800700 return;
701 }
702 targetFound = true;
703 }
704 else if (param.second == "UpdateFile")
705 {
706 uploadData = &(formpart.content);
707 }
708 }
709 }
710
711 if (uploadData == nullptr)
712 {
Ed Tanous62598e32023-07-17 17:06:25 -0700713 BMCWEB_LOG_ERROR("Upload data is NULL");
George Liu0ed80c82020-05-12 16:06:27 +0800714 messages::propertyMissing(asyncResp->res, "UpdateFile");
715 return;
716 }
717 if (!targetFound)
718 {
719 messages::propertyMissing(asyncResp->res, "targets");
720 return;
721 }
722
723 setApplyTime(asyncResp, *applyTime);
724
725 uploadImageFile(asyncResp->res, *uploadData);
726}
727
Ed Tanousc2051d12022-05-11 12:21:55 -0700728inline void
729 handleUpdateServicePost(App& app, const crow::Request& req,
730 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
731{
Carson Labrado3ba00072022-06-06 19:40:56 +0000732 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700733 {
734 return;
735 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500736 std::string_view contentType = req.getHeaderValue("Content-Type");
Ed Tanousc2051d12022-05-11 12:21:55 -0700737
Ed Tanous62598e32023-07-17 17:06:25 -0700738 BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
Ed Tanousc2051d12022-05-11 12:21:55 -0700739
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500740 // Make sure that content type is application/octet-stream or
741 // multipart/form-data
Ed Tanous18f8f602023-07-18 10:07:23 -0700742 if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
George Liu0ed80c82020-05-12 16:06:27 +0800743 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500744 // Setup callback for when new software detected
745 monitorForSoftwareAvailable(asyncResp, req,
746 "/redfish/v1/UpdateService");
747
George Liu0ed80c82020-05-12 16:06:27 +0800748 uploadImageFile(asyncResp->res, req.body());
George Liu0ed80c82020-05-12 16:06:27 +0800749 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500750 else if (contentType.starts_with("multipart/form-data"))
George Liu0ed80c82020-05-12 16:06:27 +0800751 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500752 MultipartParser parser;
753
754 // Setup callback for when new software detected
755 monitorForSoftwareAvailable(asyncResp, req,
756 "/redfish/v1/UpdateService");
757
758 ParserError ec = parser.parse(req);
759 if (ec != ParserError::PARSER_SUCCESS)
760 {
761 // handle error
Ed Tanous62598e32023-07-17 17:06:25 -0700762 BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
763 static_cast<int>(ec));
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500764 messages::internalError(asyncResp->res);
765 return;
766 }
767 updateMultipartContext(asyncResp, parser);
George Liu0ed80c82020-05-12 16:06:27 +0800768 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500769 else
770 {
Ed Tanous62598e32023-07-17 17:06:25 -0700771 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500772 asyncResp->res.result(boost::beast::http::status::bad_request);
773 }
Ed Tanousc2051d12022-05-11 12:21:55 -0700774}
775
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700776inline void requestRoutesUpdateService(App& app)
777{
778 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700779 .privileges(redfish::privileges::getUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700780 .methods(boost::beast::http::verb::get)(
781 [&app](const crow::Request& req,
782 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000783 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700784 {
785 return;
786 }
787 asyncResp->res.jsonValue["@odata.type"] =
George Liu0ed80c82020-05-12 16:06:27 +0800788 "#UpdateService.v1_11_1.UpdateService";
Ed Tanous002d39b2022-05-31 08:59:27 -0700789 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
790 asyncResp->res.jsonValue["Id"] = "UpdateService";
791 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
792 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700793
Ed Tanous002d39b2022-05-31 08:59:27 -0700794 asyncResp->res.jsonValue["HttpPushUri"] =
795 "/redfish/v1/UpdateService/update";
George Liu0ed80c82020-05-12 16:06:27 +0800796 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
797 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700798
Ed Tanous002d39b2022-05-31 08:59:27 -0700799 // UpdateService cannot be disabled
800 asyncResp->res.jsonValue["ServiceEnabled"] = true;
801 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
802 "/redfish/v1/UpdateService/FirmwareInventory";
803 // Get the MaxImageSizeBytes
804 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
805 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530806
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700807#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
Ed Tanous002d39b2022-05-31 08:59:27 -0700808 // Update Actions object.
809 nlohmann::json& updateSvcSimpleUpdate =
810 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
811 updateSvcSimpleUpdate["target"] =
812 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
813 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
814 "TFTP"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700815#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700816 // Get the current ApplyTime value
817 sdbusplus::asio::getProperty<std::string>(
818 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
819 "/xyz/openbmc_project/software/apply_time",
820 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800821 [asyncResp](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700822 const std::string& applyTime) {
823 if (ec)
824 {
Ed Tanous62598e32023-07-17 17:06:25 -0700825 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -0700826 messages::internalError(asyncResp->res);
827 return;
828 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530829
Ed Tanous002d39b2022-05-31 08:59:27 -0700830 // Store the ApplyTime Value
831 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
832 "RequestedApplyTimes.Immediate")
833 {
834 asyncResp->res.jsonValue["HttpPushUriOptions"]
835 ["HttpPushUriApplyTime"]["ApplyTime"] =
836 "Immediate";
837 }
838 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
839 "RequestedApplyTimes.OnReset")
840 {
841 asyncResp->res.jsonValue["HttpPushUriOptions"]
842 ["HttpPushUriApplyTime"]["ApplyTime"] =
843 "OnReset";
844 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700845 });
Patrick Williams5a39f772023-10-20 11:20:21 -0500846 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700847 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700848 .privileges(redfish::privileges::patchUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700849 .methods(boost::beast::http::verb::patch)(
850 [&app](const crow::Request& req,
851 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000852 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700853 {
854 return;
855 }
Ed Tanous62598e32023-07-17 17:06:25 -0700856 BMCWEB_LOG_DEBUG("doPatch...");
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530857
Ed Tanous7cb59f62022-05-05 11:48:31 -0700858 std::optional<std::string> applyTime;
859 if (!json_util::readJsonPatch(
860 req, asyncResp->res,
861 "HttpPushUriOptions/HttpPushUriApplyTime/ApplyTime", applyTime))
Ed Tanous002d39b2022-05-31 08:59:27 -0700862 {
863 return;
864 }
865
Ed Tanous7cb59f62022-05-05 11:48:31 -0700866 if (applyTime)
Ed Tanous002d39b2022-05-31 08:59:27 -0700867 {
Ed Tanous7cb59f62022-05-05 11:48:31 -0700868 setApplyTime(asyncResp, *applyTime);
Ed Tanous002d39b2022-05-31 08:59:27 -0700869 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500870 });
Ed Tanousc2051d12022-05-11 12:21:55 -0700871
Ed Tanous4dc23f32022-05-11 11:32:19 -0700872 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
873 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700874 .methods(boost::beast::http::verb::post)(
Ed Tanousc2051d12022-05-11 12:21:55 -0700875 std::bind_front(handleUpdateServicePost, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700876}
877
878inline void requestRoutesSoftwareInventoryCollection(App& app)
879{
880 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700881 .privileges(redfish::privileges::getSoftwareInventoryCollection)
Ed Tanous14766872022-03-15 10:44:42 -0700882 .methods(boost::beast::http::verb::get)(
883 [&app](const crow::Request& req,
884 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000885 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700886 {
887 return;
888 }
889 asyncResp->res.jsonValue["@odata.type"] =
890 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
891 asyncResp->res.jsonValue["@odata.id"] =
892 "/redfish/v1/UpdateService/FirmwareInventory";
893 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
John Edward Broadbent08d81ad2022-05-17 20:00:23 -0700894 const std::array<const std::string_view, 1> iface = {
George Liue99073f2022-12-09 11:06:16 +0800895 "xyz.openbmc_project.Software.Version"};
Ed Tanous002d39b2022-05-31 08:59:27 -0700896
John Edward Broadbent08d81ad2022-05-17 20:00:23 -0700897 redfish::collection_util::getCollectionMembers(
898 asyncResp,
899 boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"),
900 iface, "/xyz/openbmc_project/software");
Patrick Williams5a39f772023-10-20 11:20:21 -0500901 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700902}
903/* Fill related item links (i.e. bmc, bios) in for inventory */
904inline static void
Ed Tanousac106bf2023-06-07 09:24:59 -0700905 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700906 const std::string& purpose)
907{
Willy Tueee00132022-06-14 14:53:17 -0700908 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700909 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700910 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700911 nlohmann::json::object_t item;
912 item["@odata.id"] = "/redfish/v1/Managers/bmc";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500913 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700914 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
915 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700916 }
Willy Tueee00132022-06-14 14:53:17 -0700917 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700918 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700919 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700920 nlohmann::json::object_t item;
921 item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500922 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700923 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
924 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700925 }
926 else
927 {
Carson Labradobf2dded2023-08-10 00:37:06 +0000928 BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700929 }
930}
931
Willy Tuaf246602022-06-14 15:51:53 -0700932inline void
933 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
934 const std::string& service, const std::string& path,
935 const std::string& swId)
936{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200937 sdbusplus::asio::getAllProperties(
938 *crow::connections::systemBus, service, path,
939 "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -0700940 [asyncResp,
Ed Tanous8b242752023-06-27 17:17:13 -0700941 swId](const boost::system::error_code& ec,
Willy Tuaf246602022-06-14 15:51:53 -0700942 const dbus::utility::DBusPropertiesMap& propertiesList) {
Ed Tanous8b242752023-06-27 17:17:13 -0700943 if (ec)
Willy Tuaf246602022-06-14 15:51:53 -0700944 {
945 messages::internalError(asyncResp->res);
946 return;
947 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200948
Willy Tuaf246602022-06-14 15:51:53 -0700949 const std::string* swInvPurpose = nullptr;
950 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200951
952 const bool success = sdbusplus::unpackPropertiesNoThrow(
953 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
954 swInvPurpose, "Version", version);
955
956 if (!success)
Willy Tuaf246602022-06-14 15:51:53 -0700957 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200958 messages::internalError(asyncResp->res);
959 return;
Willy Tuaf246602022-06-14 15:51:53 -0700960 }
961
962 if (swInvPurpose == nullptr)
963 {
Ed Tanous62598e32023-07-17 17:06:25 -0700964 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
Willy Tuaf246602022-06-14 15:51:53 -0700965 messages::internalError(asyncResp->res);
966 return;
967 }
968
Ed Tanous62598e32023-07-17 17:06:25 -0700969 BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
Willy Tuaf246602022-06-14 15:51:53 -0700970
971 if (version == nullptr)
972 {
Ed Tanous62598e32023-07-17 17:06:25 -0700973 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
Willy Tuaf246602022-06-14 15:51:53 -0700974
975 messages::internalError(asyncResp->res);
976
977 return;
978 }
979 asyncResp->res.jsonValue["Version"] = *version;
980 asyncResp->res.jsonValue["Id"] = swId;
981
982 // swInvPurpose is of format:
983 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
984 // Translate this to "ABC image"
985 size_t endDesc = swInvPurpose->rfind('.');
986 if (endDesc == std::string::npos)
987 {
988 messages::internalError(asyncResp->res);
989 return;
990 }
991 endDesc++;
992 if (endDesc >= swInvPurpose->size())
993 {
994 messages::internalError(asyncResp->res);
995 return;
996 }
997
998 std::string formatDesc = swInvPurpose->substr(endDesc);
999 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1000 getRelatedItems(asyncResp, *swInvPurpose);
Patrick Williams5a39f772023-10-20 11:20:21 -05001001 });
Willy Tuaf246602022-06-14 15:51:53 -07001002}
1003
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001004inline void requestRoutesSoftwareInventory(App& app)
1005{
1006 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001007 .privileges(redfish::privileges::getSoftwareInventory)
Ed Tanous002d39b2022-05-31 08:59:27 -07001008 .methods(boost::beast::http::verb::get)(
1009 [&app](const crow::Request& req,
1010 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1011 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001012 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001013 {
1014 return;
1015 }
1016 std::shared_ptr<std::string> swId =
1017 std::make_shared<std::string>(param);
1018
Ed Tanousef4c65b2023-04-24 15:28:50 -07001019 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1020 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001021
George Liue99073f2022-12-09 11:06:16 +08001022 constexpr std::array<std::string_view, 1> interfaces = {
1023 "xyz.openbmc_project.Software.Version"};
1024 dbus::utility::getSubTree(
1025 "/", 0, interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -07001026 [asyncResp,
George Liue99073f2022-12-09 11:06:16 +08001027 swId](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -07001028 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Ed Tanous62598e32023-07-17 17:06:25 -07001029 BMCWEB_LOG_DEBUG("doGet callback...");
Ed Tanous002d39b2022-05-31 08:59:27 -07001030 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001031 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001032 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001033 return;
1034 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001035
Ed Tanous002d39b2022-05-31 08:59:27 -07001036 // Ensure we find our input swId, otherwise return an error
1037 bool found = false;
1038 for (const std::pair<std::string,
1039 std::vector<std::pair<
1040 std::string, std::vector<std::string>>>>&
1041 obj : subtree)
1042 {
Ed Tanous11ba3972022-07-11 09:50:41 -07001043 if (!obj.first.ends_with(*swId))
Ed Tanous002d39b2022-05-31 08:59:27 -07001044 {
1045 continue;
1046 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001047
Ed Tanous002d39b2022-05-31 08:59:27 -07001048 if (obj.second.empty())
1049 {
1050 continue;
1051 }
1052
1053 found = true;
Willy Tueee00132022-06-14 14:53:17 -07001054 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
Willy Tuaf246602022-06-14 15:51:53 -07001055 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1056 *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001057 }
1058 if (!found)
1059 {
Ed Tanous62598e32023-07-17 17:06:25 -07001060 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001061 messages::resourceMissingAtURI(
Ed Tanousef4c65b2023-04-24 15:28:50 -07001062 asyncResp->res,
1063 boost::urls::format(
1064 "/redfish/v1/UpdateService/FirmwareInventory/{}",
1065 *swId));
Ed Tanous002d39b2022-05-31 08:59:27 -07001066 return;
1067 }
1068 asyncResp->res.jsonValue["@odata.type"] =
1069 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1070 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1071 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001072
Ed Tanous002d39b2022-05-31 08:59:27 -07001073 asyncResp->res.jsonValue["Updateable"] = false;
Willy Tueee00132022-06-14 14:53:17 -07001074 sw_util::getSwUpdatableStatus(asyncResp, swId);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001075 });
Patrick Williams5a39f772023-10-20 11:20:21 -05001076 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001077}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001078
1079} // namespace redfish