blob: 74e110170a9b1978d266faaa21bca2101b700ecc [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>
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{
Ed Tanous62598e32023-07-17 17:06:25 -070065 BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
George Liu9ae226f2023-06-21 17:56:46 +080066 sdbusplus::asio::setProperty(
67 *crow::connections::systemBus, service, objPath,
68 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
69 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
Ed Tanous8b242752023-06-27 17:17:13 -070070 [](const boost::system::error_code& ec) {
71 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -070072 {
Ed Tanous62598e32023-07-17 17:06:25 -070073 BMCWEB_LOG_DEBUG("error_code = {}", ec);
74 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -070075 }
Patrick Williams5a39f772023-10-20 11:20:21 -050076 });
Andrew Geissler86adcd62019-04-18 10:58:05 -050077}
Andrew Geissler0554c982019-04-23 14:40:12 -050078
79// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
80// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080081static void
82 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -050083 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050084{
Michael Shen80f79a42023-08-24 13:41:53 +000085 dbus::utility::DBusInterfacesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -050086
87 sdbusplus::message::object_path objPath;
88
89 m.read(objPath, interfacesProperties);
90
Ed Tanous62598e32023-07-17 17:06:25 -070091 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
Ed Tanouse3eb3d62022-12-21 11:56:07 -080092 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050093 {
Ed Tanous62598e32023-07-17 17:06:25 -070094 BMCWEB_LOG_DEBUG("interface = {}", interface.first);
Andrew Geissler86adcd62019-04-18 10:58:05 -050095
96 if (interface.first == "xyz.openbmc_project.Software.Activation")
97 {
Andrew Geissler86adcd62019-04-18 10:58:05 -050098 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +080099 constexpr std::array<std::string_view, 1> interfaces = {
100 "xyz.openbmc_project.Software.Activation"};
101 dbus::utility::getDbusObject(
102 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700103 [objPath, asyncResp, payload(std::move(payload))](
Ed Tanous8b242752023-06-27 17:17:13 -0700104 const boost::system::error_code& ec,
Ed Tanousa3e65892021-09-16 14:13:20 -0700105 const std::vector<
106 std::pair<std::string, std::vector<std::string>>>&
107 objInfo) mutable {
Ed Tanous8b242752023-06-27 17:17:13 -0700108 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700109 {
Ed Tanous62598e32023-07-17 17:06:25 -0700110 BMCWEB_LOG_DEBUG("error_code = {}", ec);
111 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Andrew Geissler0554c982019-04-23 14:40:12 -0500112 if (asyncResp)
113 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700114 messages::internalError(asyncResp->res);
115 }
116 cleanUp();
117 return;
118 }
119 // Ensure we only got one service back
120 if (objInfo.size() != 1)
121 {
Ed Tanous62598e32023-07-17 17:06:25 -0700122 BMCWEB_LOG_ERROR("Invalid Object Size {}", objInfo.size());
Ed Tanous002d39b2022-05-31 08:59:27 -0700123 if (asyncResp)
124 {
125 messages::internalError(asyncResp->res);
126 }
127 cleanUp();
128 return;
129 }
130 // cancel timer only when
131 // xyz.openbmc_project.Software.Activation interface
132 // is added
133 fwAvailableTimer = nullptr;
134
135 activateImage(objPath.str, objInfo[0].first);
136 if (asyncResp)
137 {
138 std::shared_ptr<task::TaskData> task =
139 task::TaskData::createTask(
Ed Tanous8b242752023-06-27 17:17:13 -0700140 [](const boost::system::error_code& ec2,
Patrick Williams59d494e2022-07-22 19:26:55 -0500141 sdbusplus::message_t& msg,
Ed Tanous002d39b2022-05-31 08:59:27 -0700142 const std::shared_ptr<task::TaskData>&
143 taskData) {
Ed Tanous8b242752023-06-27 17:17:13 -0700144 if (ec2)
Ed Tanous002d39b2022-05-31 08:59:27 -0700145 {
146 return task::completed;
147 }
148
149 std::string iface;
150 dbus::utility::DBusPropertiesMap values;
151
152 std::string index = std::to_string(taskData->index);
153 msg.read(iface, values);
154
155 if (iface == "xyz.openbmc_project.Software.Activation")
156 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000157 const std::string* state = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700158 for (const auto& property : values)
159 {
160 if (property.first == "Activation")
161 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000162 state = std::get_if<std::string>(
163 &property.second);
164 if (state == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700165 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700166 taskData->messages.emplace_back(
167 messages::internalError());
James Feist32898ce2020-03-10 16:16:52 -0700168 return task::completed;
169 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700170 }
171 }
James Feist32898ce2020-03-10 16:16:52 -0700172
Ed Tanous002d39b2022-05-31 08:59:27 -0700173 if (state == nullptr)
174 {
175 return !task::completed;
176 }
James Feist32898ce2020-03-10 16:16:52 -0700177
Ed Tanous11ba3972022-07-11 09:50:41 -0700178 if (state->ends_with("Invalid") ||
179 state->ends_with("Failed"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700180 {
181 taskData->state = "Exception";
182 taskData->status = "Warning";
183 taskData->messages.emplace_back(
184 messages::taskAborted(index));
185 return task::completed;
186 }
James Feiste5d50062020-05-11 17:29:00 -0700187
Ed Tanous11ba3972022-07-11 09:50:41 -0700188 if (state->ends_with("Staged"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700189 {
190 taskData->state = "Stopping";
191 taskData->messages.emplace_back(
192 messages::taskPaused(index));
193
194 // its staged, set a long timer to
195 // allow them time to complete the
196 // update (probably cycle the
197 // system) if this expires then
Ed Tanous8ece0e42024-01-02 13:16:50 -0800198 // task will be canceled
Ed Tanous002d39b2022-05-31 08:59:27 -0700199 taskData->extendTimer(std::chrono::hours(5));
200 return !task::completed;
201 }
202
Ed Tanous11ba3972022-07-11 09:50:41 -0700203 if (state->ends_with("Active"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700204 {
205 taskData->messages.emplace_back(
206 messages::taskCompletedOK(index));
207 taskData->state = "Completed";
208 return task::completed;
209 }
210 }
211 else if (
212 iface ==
213 "xyz.openbmc_project.Software.ActivationProgress")
214 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700215 const uint8_t* progress = nullptr;
216 for (const auto& property : values)
217 {
218 if (property.first == "Progress")
219 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000220 progress =
221 std::get_if<uint8_t>(&property.second);
222 if (progress == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700223 {
James Feist32898ce2020-03-10 16:16:52 -0700224 taskData->messages.emplace_back(
Ed Tanous002d39b2022-05-31 08:59:27 -0700225 messages::internalError());
226 return task::completed;
James Feist32898ce2020-03-10 16:16:52 -0700227 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700228 }
229 }
James Feist32898ce2020-03-10 16:16:52 -0700230
Ed Tanous002d39b2022-05-31 08:59:27 -0700231 if (progress == nullptr)
232 {
233 return !task::completed;
234 }
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000235 taskData->percentComplete = *progress;
Ed Tanous002d39b2022-05-31 08:59:27 -0700236 taskData->messages.emplace_back(
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000237 messages::taskProgressChanged(index,
238 *progress));
James Feist32898ce2020-03-10 16:16:52 -0700239
Ed Tanous002d39b2022-05-31 08:59:27 -0700240 // if we're getting status updates it's
241 // still alive, update timer
242 taskData->extendTimer(std::chrono::minutes(5));
243 }
244
245 // as firmware update often results in a
246 // reboot, the task may never "complete"
247 // unless it is an error
248
249 return !task::completed;
Patrick Williams5a39f772023-10-20 11:20:21 -0500250 },
Ed Tanous002d39b2022-05-31 08:59:27 -0700251 "type='signal',interface='org.freedesktop.DBus.Properties',"
252 "member='PropertiesChanged',path='" +
253 objPath.str + "'");
254 task->startTimer(std::chrono::minutes(5));
255 task->populateResp(asyncResp->res);
256 task->payload.emplace(std::move(payload));
257 }
258 fwUpdateInProgress = false;
Patrick Williams5a39f772023-10-20 11:20:21 -0500259 });
Patrick Williams62bafc02022-09-08 17:35:35 -0500260
261 break;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500262 }
263 }
264}
265
Myung Bae8549b952023-08-16 15:18:19 -0400266inline void afterAvailbleTimerAsyncWait(
267 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
268 const boost::system::error_code& ec)
269{
270 cleanUp();
271 if (ec == boost::asio::error::operation_aborted)
272 {
273 // expected, we were canceled before the timer completed.
274 return;
275 }
276 BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
277 BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
278 if (ec)
279 {
280 BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
281 return;
282 }
283 if (asyncResp)
284 {
285 redfish::messages::internalError(asyncResp->res);
286 }
287}
288
289inline void
290 handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
291 const std::string& url, const std::string& type)
292{
293 if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
294 {
295 redfish::messages::invalidUpload(asyncResp->res, url,
296 "Invalid archive");
297 }
298 else if (type ==
299 "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
300 {
301 redfish::messages::invalidUpload(asyncResp->res, url,
302 "Invalid manifest");
303 }
304 else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
305 {
306 redfish::messages::invalidUpload(asyncResp->res, url,
307 "Invalid image format");
308 }
309 else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
310 {
311 redfish::messages::invalidUpload(asyncResp->res, url,
312 "Image version already exists");
313
314 redfish::messages::resourceAlreadyExists(
315 asyncResp->res, "UpdateService", "Version", "uploaded version");
316 }
317 else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
318 {
319 redfish::messages::resourceExhaustion(asyncResp->res, url);
320 }
Myung Bae4034a652023-08-17 08:47:35 -0400321 else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
Myung Bae8549b952023-08-16 15:18:19 -0400322 {
Myung Bae4034a652023-08-17 08:47:35 -0400323 redfish::messages::invalidUpload(asyncResp->res, url,
324 "Incompatible image version");
325 }
326 else if (type ==
327 "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
328 {
329 redfish::messages::invalidUpload(asyncResp->res, url,
330 "Update Access Key Expired");
331 }
332 else if (type ==
333 "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
334 {
335 redfish::messages::invalidUpload(asyncResp->res, url,
336 "Invalid image signature");
337 }
338 else if (type ==
339 "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
340 type == "xyz.openbmc_project.Software.Version.Error.HostFile")
341 {
342 BMCWEB_LOG_ERROR("Software Image Error type={}", type);
Myung Bae8549b952023-08-16 15:18:19 -0400343 redfish::messages::internalError(asyncResp->res);
344 }
Myung Bae4034a652023-08-17 08:47:35 -0400345 else
346 {
347 // Unrelated error types. Ignored
348 BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
349 return;
350 }
351 // Clear the timer
352 fwAvailableTimer = nullptr;
Myung Bae8549b952023-08-16 15:18:19 -0400353}
354
355inline void
356 afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
357 const std::string& url, sdbusplus::message_t& m)
358{
Michael Shen80f79a42023-08-24 13:41:53 +0000359 dbus::utility::DBusInterfacesMap interfacesProperties;
Myung Bae8549b952023-08-16 15:18:19 -0400360 sdbusplus::message::object_path objPath;
361 m.read(objPath, interfacesProperties);
362 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
363 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
364 interface : interfacesProperties)
365 {
366 if (interface.first == "xyz.openbmc_project.Logging.Entry")
367 {
368 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
369 value : interface.second)
370 {
371 if (value.first != "Message")
372 {
373 continue;
374 }
375 const std::string* type =
376 std::get_if<std::string>(&value.second);
377 if (type == nullptr)
378 {
379 // if this was our message, timeout will cover it
380 return;
381 }
Myung Bae8549b952023-08-16 15:18:19 -0400382 handleUpdateErrorType(asyncResp, url, *type);
383 }
384 }
385 }
386}
387
Andrew Geissler0554c982019-04-23 14:40:12 -0500388// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
389// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700390static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800391 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
392 const crow::Request& req, const std::string& url,
Gunnar Mills5d138942022-09-07 10:26:21 -0500393 int timeoutTimeSeconds = 25)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500394{
395 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800396 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500397 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500398 if (asyncResp)
399 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500400 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
401 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500402 return;
403 }
404
Andrew Geissler0554c982019-04-23 14:40:12 -0500405 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700406 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500407
Ed Tanous271584a2019-07-09 16:24:22 -0700408 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500409
410 fwAvailableTimer->async_wait(
Myung Bae8549b952023-08-16 15:18:19 -0400411 std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
412
Ed Tanousa3e65892021-09-16 14:13:20 -0700413 task::Payload payload(req);
Patrick Williams59d494e2022-07-22 19:26:55 -0500414 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
Ed Tanous62598e32023-07-17 17:06:25 -0700415 BMCWEB_LOG_DEBUG("Match fired");
Ed Tanousa3e65892021-09-16 14:13:20 -0700416 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500417 };
418
419 fwUpdateInProgress = true;
420
Patrick Williams59d494e2022-07-22 19:26:55 -0500421 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Andrew Geissler86adcd62019-04-18 10:58:05 -0500422 *crow::connections::systemBus,
423 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
424 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
425 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700426
Patrick Williams59d494e2022-07-22 19:26:55 -0500427 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
James Feist4cde5d92020-06-11 10:39:55 -0700428 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800429 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
430 "member='InterfacesAdded',"
431 "path='/xyz/openbmc_project/logging'",
Myung Bae8549b952023-08-16 15:18:19 -0400432 std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500433}
Jennifer Lee729dae72018-04-24 15:59:34 -0700434
Ed Tanousf86bcc82023-08-25 09:34:07 -0700435struct TftpUrl
436{
437 std::string fwFile;
438 std::string tftpServer;
439};
440
441inline std::optional<TftpUrl>
442 parseTftpUrl(std::string imageURI,
443 std::optional<std::string> transferProtocol,
444 crow::Response& res)
445{
446 if (imageURI.find("://") == std::string::npos)
447 {
448 if (imageURI.starts_with("/"))
449 {
450 messages::actionParameterValueTypeError(
451 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
452 return std::nullopt;
453 }
454 if (!transferProtocol)
455 {
456 messages::actionParameterValueTypeError(
457 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
458 return std::nullopt;
459 }
460 // OpenBMC currently only supports TFTP
461 if (*transferProtocol != "TFTP")
462 {
463 messages::actionParameterNotSupported(res, "TransferProtocol",
464 *transferProtocol);
465 BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
466 *transferProtocol);
467 return std::nullopt;
468 }
469 imageURI = "tftp://" + imageURI;
470 }
471
472 boost::system::result<boost::urls::url> url =
473 boost::urls::parse_absolute_uri(imageURI);
474 if (!url)
475 {
476 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
477 "UpdateService.SimpleUpdate");
478
479 return std::nullopt;
480 }
481 url->normalize();
482
483 if (url->scheme() != "tftp")
484 {
485 messages::actionParameterNotSupported(res, "ImageURI", imageURI);
486 return std::nullopt;
487 }
488 std::string path(url->encoded_path());
489 if (path.size() < 2)
490 {
491 messages::actionParameterNotSupported(res, "ImageURI", imageURI);
492 return std::nullopt;
493 }
494 path.erase(0, 1);
495 std::string host(url->encoded_host_and_port());
496 return TftpUrl{path, host};
497}
498
Andrew Geissler0554c982019-04-23 14:40:12 -0500499/**
500 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
501 * SimpleUpdate action.
502 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700503inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500504{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700505 BMCWEB_ROUTE(
506 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
Ed Tanoused398212021-06-09 17:05:54 -0700507 .privileges(redfish::privileges::postUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700508 .methods(boost::beast::http::verb::post)(
509 [&app](const crow::Request& req,
510 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000511 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700512 {
513 return;
514 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700515
Ed Tanous002d39b2022-05-31 08:59:27 -0700516 std::optional<std::string> transferProtocol;
517 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500518
Ed Tanous62598e32023-07-17 17:06:25 -0700519 BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
Andrew Geissler0554c982019-04-23 14:40:12 -0500520
Ed Tanous002d39b2022-05-31 08:59:27 -0700521 // User can pass in both TransferProtocol and ImageURI parameters or
522 // they can pass in just the ImageURI with the transfer protocol
523 // embedded within it.
524 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
525 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500526
Ed Tanous002d39b2022-05-31 08:59:27 -0700527 if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
528 transferProtocol, "ImageURI", imageURI))
529 {
Ed Tanous62598e32023-07-17 17:06:25 -0700530 BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
Ed Tanous002d39b2022-05-31 08:59:27 -0700531 return;
532 }
Ed Tanousf86bcc82023-08-25 09:34:07 -0700533 std::optional<TftpUrl> ret = parseTftpUrl(imageURI, transferProtocol,
534 asyncResp->res);
535 if (!ret)
Ed Tanous002d39b2022-05-31 08:59:27 -0700536 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700537 return;
538 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700539
Ed Tanousf86bcc82023-08-25 09:34:07 -0700540 BMCWEB_LOG_DEBUG("Server: {} File: {}", ret->tftpServer, ret->fwFile);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700541
Ed Tanous002d39b2022-05-31 08:59:27 -0700542 // Setup callback for when new software detected
543 // Give TFTP 10 minutes to complete
544 monitorForSoftwareAvailable(
545 asyncResp, req,
546 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
547 600);
548
549 // TFTP can take up to 10 minutes depending on image size and
550 // connection speed. Return to caller as soon as the TFTP operation
551 // has been started. The callback above will ensure the activate
552 // is started once the download has completed
553 redfish::messages::success(asyncResp->res);
554
555 // Call TFTP service
556 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800557 [](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700558 if (ec)
559 {
560 // messages::internalError(asyncResp->res);
561 cleanUp();
Ed Tanous62598e32023-07-17 17:06:25 -0700562 BMCWEB_LOG_DEBUG("error_code = {}", ec);
563 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -0700564 }
565 else
566 {
Ed Tanous62598e32023-07-17 17:06:25 -0700567 BMCWEB_LOG_DEBUG("Call to DownloaViaTFTP Success");
Ed Tanous002d39b2022-05-31 08:59:27 -0700568 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500569 },
Ed Tanous002d39b2022-05-31 08:59:27 -0700570 "xyz.openbmc_project.Software.Download",
571 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
Ed Tanousf86bcc82023-08-25 09:34:07 -0700572 "DownloadViaTFTP", ret->fwFile, ret->tftpServer);
Ed Tanous002d39b2022-05-31 08:59:27 -0700573
Ed Tanous62598e32023-07-17 17:06:25 -0700574 BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
Patrick Williams5a39f772023-10-20 11:20:21 -0500575 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700576}
577
George Liu0ed80c82020-05-12 16:06:27 +0800578inline void uploadImageFile(crow::Response& res, std::string_view body)
579{
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700580 std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
581
Ed Tanous62598e32023-07-17 17:06:25 -0700582 BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
George Liu0ed80c82020-05-12 16:06:27 +0800583 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
584 std::ofstream::trunc);
585 // set the permission of the file to 640
Patrick Williams89492a12023-05-10 07:51:34 -0500586 std::filesystem::perms permission = std::filesystem::perms::owner_read |
587 std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800588 std::filesystem::permissions(filepath, permission);
589 out << body;
590
591 if (out.bad())
592 {
593 messages::internalError(res);
594 cleanUp();
595 }
596}
597
598inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
599 const std::string& applyTime)
600{
601 std::string applyTimeNewVal;
602 if (applyTime == "Immediate")
603 {
604 applyTimeNewVal =
605 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
606 }
607 else if (applyTime == "OnReset")
608 {
609 applyTimeNewVal =
610 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
611 }
612 else
613 {
Ed Tanous62598e32023-07-17 17:06:25 -0700614 BMCWEB_LOG_INFO(
615 "ApplyTime value is not in the list of acceptable values");
George Liu0ed80c82020-05-12 16:06:27 +0800616 messages::propertyValueNotInList(asyncResp->res, applyTime,
617 "ApplyTime");
618 return;
619 }
620
621 // Set the requested image apply time value
George Liu9ae226f2023-06-21 17:56:46 +0800622 sdbusplus::asio::setProperty(
623 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
George Liu0ed80c82020-05-12 16:06:27 +0800624 "/xyz/openbmc_project/software/apply_time",
George Liu0ed80c82020-05-12 16:06:27 +0800625 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
George Liu9ae226f2023-06-21 17:56:46 +0800626 applyTimeNewVal, [asyncResp](const boost::system::error_code& ec) {
Patrick Williams5a39f772023-10-20 11:20:21 -0500627 if (ec)
628 {
629 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec);
630 messages::internalError(asyncResp->res);
631 return;
632 }
633 messages::success(asyncResp->res);
634 });
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);
675 if (!json_util::readJson(content, asyncResp->res, "Targets",
676 targets, "@Redfish.OperationApplyTime",
677 applyTime))
678 {
679 return;
680 }
681 if (targets.size() != 1)
682 {
683 messages::propertyValueFormatError(asyncResp->res,
684 "Targets", "");
685 return;
686 }
687 if (targets[0] != "/redfish/v1/Managers/bmc")
688 {
689 messages::propertyValueNotInList(asyncResp->res,
690 "Targets/0", targets[0]);
691 return;
692 }
693 targetFound = true;
694 }
695 else if (param.second == "UpdateFile")
696 {
697 uploadData = &(formpart.content);
698 }
699 }
700 }
701
702 if (uploadData == nullptr)
703 {
Ed Tanous62598e32023-07-17 17:06:25 -0700704 BMCWEB_LOG_ERROR("Upload data is NULL");
George Liu0ed80c82020-05-12 16:06:27 +0800705 messages::propertyMissing(asyncResp->res, "UpdateFile");
706 return;
707 }
708 if (!targetFound)
709 {
710 messages::propertyMissing(asyncResp->res, "targets");
711 return;
712 }
713
714 setApplyTime(asyncResp, *applyTime);
715
716 uploadImageFile(asyncResp->res, *uploadData);
717}
718
Ed Tanousc2051d12022-05-11 12:21:55 -0700719inline void
720 handleUpdateServicePost(App& app, const crow::Request& req,
721 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
722{
Carson Labrado3ba00072022-06-06 19:40:56 +0000723 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700724 {
725 return;
726 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500727 std::string_view contentType = req.getHeaderValue("Content-Type");
Ed Tanousc2051d12022-05-11 12:21:55 -0700728
Ed Tanous62598e32023-07-17 17:06:25 -0700729 BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
Ed Tanousc2051d12022-05-11 12:21:55 -0700730
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500731 // Make sure that content type is application/octet-stream or
732 // multipart/form-data
Ed Tanous18f8f602023-07-18 10:07:23 -0700733 if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
George Liu0ed80c82020-05-12 16:06:27 +0800734 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500735 // Setup callback for when new software detected
736 monitorForSoftwareAvailable(asyncResp, req,
737 "/redfish/v1/UpdateService");
738
George Liu0ed80c82020-05-12 16:06:27 +0800739 uploadImageFile(asyncResp->res, req.body());
George Liu0ed80c82020-05-12 16:06:27 +0800740 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500741 else if (contentType.starts_with("multipart/form-data"))
George Liu0ed80c82020-05-12 16:06:27 +0800742 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500743 MultipartParser parser;
744
745 // Setup callback for when new software detected
746 monitorForSoftwareAvailable(asyncResp, req,
747 "/redfish/v1/UpdateService");
748
749 ParserError ec = parser.parse(req);
750 if (ec != ParserError::PARSER_SUCCESS)
751 {
752 // handle error
Ed Tanous62598e32023-07-17 17:06:25 -0700753 BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
754 static_cast<int>(ec));
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500755 messages::internalError(asyncResp->res);
756 return;
757 }
758 updateMultipartContext(asyncResp, parser);
George Liu0ed80c82020-05-12 16:06:27 +0800759 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500760 else
761 {
Ed Tanous62598e32023-07-17 17:06:25 -0700762 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500763 asyncResp->res.result(boost::beast::http::status::bad_request);
764 }
Ed Tanousc2051d12022-05-11 12:21:55 -0700765}
766
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700767inline void requestRoutesUpdateService(App& app)
768{
769 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700770 .privileges(redfish::privileges::getUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700771 .methods(boost::beast::http::verb::get)(
772 [&app](const crow::Request& req,
773 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000774 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700775 {
776 return;
777 }
778 asyncResp->res.jsonValue["@odata.type"] =
George Liu0ed80c82020-05-12 16:06:27 +0800779 "#UpdateService.v1_11_1.UpdateService";
Ed Tanous002d39b2022-05-31 08:59:27 -0700780 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
781 asyncResp->res.jsonValue["Id"] = "UpdateService";
782 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
783 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700784
Ed Tanous002d39b2022-05-31 08:59:27 -0700785 asyncResp->res.jsonValue["HttpPushUri"] =
786 "/redfish/v1/UpdateService/update";
George Liu0ed80c82020-05-12 16:06:27 +0800787 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
788 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700789
Ed Tanous002d39b2022-05-31 08:59:27 -0700790 // UpdateService cannot be disabled
791 asyncResp->res.jsonValue["ServiceEnabled"] = true;
792 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
793 "/redfish/v1/UpdateService/FirmwareInventory";
794 // Get the MaxImageSizeBytes
795 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
796 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530797
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700798#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
Ed Tanous002d39b2022-05-31 08:59:27 -0700799 // Update Actions object.
800 nlohmann::json& updateSvcSimpleUpdate =
801 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
802 updateSvcSimpleUpdate["target"] =
803 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
804 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
805 "TFTP"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700806#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700807 // Get the current ApplyTime value
808 sdbusplus::asio::getProperty<std::string>(
809 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
810 "/xyz/openbmc_project/software/apply_time",
811 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800812 [asyncResp](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700813 const std::string& applyTime) {
814 if (ec)
815 {
Ed Tanous62598e32023-07-17 17:06:25 -0700816 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -0700817 messages::internalError(asyncResp->res);
818 return;
819 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530820
Ed Tanous002d39b2022-05-31 08:59:27 -0700821 // Store the ApplyTime Value
822 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
823 "RequestedApplyTimes.Immediate")
824 {
825 asyncResp->res.jsonValue["HttpPushUriOptions"]
826 ["HttpPushUriApplyTime"]["ApplyTime"] =
827 "Immediate";
828 }
829 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
830 "RequestedApplyTimes.OnReset")
831 {
832 asyncResp->res.jsonValue["HttpPushUriOptions"]
833 ["HttpPushUriApplyTime"]["ApplyTime"] =
834 "OnReset";
835 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700836 });
Patrick Williams5a39f772023-10-20 11:20:21 -0500837 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700838 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700839 .privileges(redfish::privileges::patchUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700840 .methods(boost::beast::http::verb::patch)(
841 [&app](const crow::Request& req,
842 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000843 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700844 {
845 return;
846 }
Ed Tanous62598e32023-07-17 17:06:25 -0700847 BMCWEB_LOG_DEBUG("doPatch...");
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530848
Ed Tanous002d39b2022-05-31 08:59:27 -0700849 std::optional<nlohmann::json> pushUriOptions;
850 if (!json_util::readJsonPatch(req, asyncResp->res, "HttpPushUriOptions",
851 pushUriOptions))
852 {
853 return;
854 }
855
856 if (pushUriOptions)
857 {
858 std::optional<nlohmann::json> pushUriApplyTime;
859 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
860 "HttpPushUriApplyTime", pushUriApplyTime))
George Liu0fda0f12021-11-16 10:06:17 +0800861 {
862 return;
863 }
864
Ed Tanous002d39b2022-05-31 08:59:27 -0700865 if (pushUriApplyTime)
George Liu0fda0f12021-11-16 10:06:17 +0800866 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700867 std::optional<std::string> applyTime;
868 if (!json_util::readJson(*pushUriApplyTime, asyncResp->res,
869 "ApplyTime", applyTime))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700870 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700871 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700872 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700873
Ed Tanous002d39b2022-05-31 08:59:27 -0700874 if (applyTime)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700875 {
George Liu0ed80c82020-05-12 16:06:27 +0800876 setApplyTime(asyncResp, *applyTime);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700877 }
George Liu0fda0f12021-11-16 10:06:17 +0800878 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700879 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500880 });
Ed Tanousc2051d12022-05-11 12:21:55 -0700881
Ed Tanous4dc23f32022-05-11 11:32:19 -0700882 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
883 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700884 .methods(boost::beast::http::verb::post)(
Ed Tanousc2051d12022-05-11 12:21:55 -0700885 std::bind_front(handleUpdateServicePost, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700886}
887
888inline void requestRoutesSoftwareInventoryCollection(App& app)
889{
890 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700891 .privileges(redfish::privileges::getSoftwareInventoryCollection)
Ed Tanous14766872022-03-15 10:44:42 -0700892 .methods(boost::beast::http::verb::get)(
893 [&app](const crow::Request& req,
894 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000895 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700896 {
897 return;
898 }
899 asyncResp->res.jsonValue["@odata.type"] =
900 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
901 asyncResp->res.jsonValue["@odata.id"] =
902 "/redfish/v1/UpdateService/FirmwareInventory";
903 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
John Edward Broadbent08d81ad2022-05-17 20:00:23 -0700904 const std::array<const std::string_view, 1> iface = {
George Liue99073f2022-12-09 11:06:16 +0800905 "xyz.openbmc_project.Software.Version"};
Ed Tanous002d39b2022-05-31 08:59:27 -0700906
John Edward Broadbent08d81ad2022-05-17 20:00:23 -0700907 redfish::collection_util::getCollectionMembers(
908 asyncResp,
909 boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"),
910 iface, "/xyz/openbmc_project/software");
Patrick Williams5a39f772023-10-20 11:20:21 -0500911 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700912}
913/* Fill related item links (i.e. bmc, bios) in for inventory */
914inline static void
Ed Tanousac106bf2023-06-07 09:24:59 -0700915 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700916 const std::string& purpose)
917{
Willy Tueee00132022-06-14 14:53:17 -0700918 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700919 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700920 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700921 nlohmann::json::object_t item;
922 item["@odata.id"] = "/redfish/v1/Managers/bmc";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500923 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700924 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
925 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700926 }
Willy Tueee00132022-06-14 14:53:17 -0700927 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700928 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700929 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700930 nlohmann::json::object_t item;
931 item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500932 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700933 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
934 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700935 }
936 else
937 {
Carson Labradobf2dded2023-08-10 00:37:06 +0000938 BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700939 }
940}
941
Willy Tuaf246602022-06-14 15:51:53 -0700942inline void
943 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
944 const std::string& service, const std::string& path,
945 const std::string& swId)
946{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200947 sdbusplus::asio::getAllProperties(
948 *crow::connections::systemBus, service, path,
949 "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -0700950 [asyncResp,
Ed Tanous8b242752023-06-27 17:17:13 -0700951 swId](const boost::system::error_code& ec,
Willy Tuaf246602022-06-14 15:51:53 -0700952 const dbus::utility::DBusPropertiesMap& propertiesList) {
Ed Tanous8b242752023-06-27 17:17:13 -0700953 if (ec)
Willy Tuaf246602022-06-14 15:51:53 -0700954 {
955 messages::internalError(asyncResp->res);
956 return;
957 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200958
Willy Tuaf246602022-06-14 15:51:53 -0700959 const std::string* swInvPurpose = nullptr;
960 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200961
962 const bool success = sdbusplus::unpackPropertiesNoThrow(
963 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
964 swInvPurpose, "Version", version);
965
966 if (!success)
Willy Tuaf246602022-06-14 15:51:53 -0700967 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200968 messages::internalError(asyncResp->res);
969 return;
Willy Tuaf246602022-06-14 15:51:53 -0700970 }
971
972 if (swInvPurpose == nullptr)
973 {
Ed Tanous62598e32023-07-17 17:06:25 -0700974 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
Willy Tuaf246602022-06-14 15:51:53 -0700975 messages::internalError(asyncResp->res);
976 return;
977 }
978
Ed Tanous62598e32023-07-17 17:06:25 -0700979 BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
Willy Tuaf246602022-06-14 15:51:53 -0700980
981 if (version == nullptr)
982 {
Ed Tanous62598e32023-07-17 17:06:25 -0700983 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
Willy Tuaf246602022-06-14 15:51:53 -0700984
985 messages::internalError(asyncResp->res);
986
987 return;
988 }
989 asyncResp->res.jsonValue["Version"] = *version;
990 asyncResp->res.jsonValue["Id"] = swId;
991
992 // swInvPurpose is of format:
993 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
994 // Translate this to "ABC image"
995 size_t endDesc = swInvPurpose->rfind('.');
996 if (endDesc == std::string::npos)
997 {
998 messages::internalError(asyncResp->res);
999 return;
1000 }
1001 endDesc++;
1002 if (endDesc >= swInvPurpose->size())
1003 {
1004 messages::internalError(asyncResp->res);
1005 return;
1006 }
1007
1008 std::string formatDesc = swInvPurpose->substr(endDesc);
1009 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1010 getRelatedItems(asyncResp, *swInvPurpose);
Patrick Williams5a39f772023-10-20 11:20:21 -05001011 });
Willy Tuaf246602022-06-14 15:51:53 -07001012}
1013
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001014inline void requestRoutesSoftwareInventory(App& app)
1015{
1016 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001017 .privileges(redfish::privileges::getSoftwareInventory)
Ed Tanous002d39b2022-05-31 08:59:27 -07001018 .methods(boost::beast::http::verb::get)(
1019 [&app](const crow::Request& req,
1020 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1021 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001022 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001023 {
1024 return;
1025 }
1026 std::shared_ptr<std::string> swId =
1027 std::make_shared<std::string>(param);
1028
Ed Tanousef4c65b2023-04-24 15:28:50 -07001029 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1030 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001031
George Liue99073f2022-12-09 11:06:16 +08001032 constexpr std::array<std::string_view, 1> interfaces = {
1033 "xyz.openbmc_project.Software.Version"};
1034 dbus::utility::getSubTree(
1035 "/", 0, interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -07001036 [asyncResp,
George Liue99073f2022-12-09 11:06:16 +08001037 swId](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -07001038 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Ed Tanous62598e32023-07-17 17:06:25 -07001039 BMCWEB_LOG_DEBUG("doGet callback...");
Ed Tanous002d39b2022-05-31 08:59:27 -07001040 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001041 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001042 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001043 return;
1044 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001045
Ed Tanous002d39b2022-05-31 08:59:27 -07001046 // Ensure we find our input swId, otherwise return an error
1047 bool found = false;
1048 for (const std::pair<std::string,
1049 std::vector<std::pair<
1050 std::string, std::vector<std::string>>>>&
1051 obj : subtree)
1052 {
Ed Tanous11ba3972022-07-11 09:50:41 -07001053 if (!obj.first.ends_with(*swId))
Ed Tanous002d39b2022-05-31 08:59:27 -07001054 {
1055 continue;
1056 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001057
Ed Tanous002d39b2022-05-31 08:59:27 -07001058 if (obj.second.empty())
1059 {
1060 continue;
1061 }
1062
1063 found = true;
Willy Tueee00132022-06-14 14:53:17 -07001064 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
Willy Tuaf246602022-06-14 15:51:53 -07001065 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1066 *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001067 }
1068 if (!found)
1069 {
Ed Tanous62598e32023-07-17 17:06:25 -07001070 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001071 messages::resourceMissingAtURI(
Ed Tanousef4c65b2023-04-24 15:28:50 -07001072 asyncResp->res,
1073 boost::urls::format(
1074 "/redfish/v1/UpdateService/FirmwareInventory/{}",
1075 *swId));
Ed Tanous002d39b2022-05-31 08:59:27 -07001076 return;
1077 }
1078 asyncResp->res.jsonValue["@odata.type"] =
1079 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1080 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1081 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001082
Ed Tanous002d39b2022-05-31 08:59:27 -07001083 asyncResp->res.jsonValue["Updateable"] = false;
Willy Tueee00132022-06-14 14:53:17 -07001084 sw_util::getSwUpdatableStatus(asyncResp, swId);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001085 });
Patrick Williams5a39f772023-10-20 11:20:21 -05001086 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001087}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001088
1089} // namespace redfish