blob: 62591149a3d92abfad4e47774ccf4216773db6be [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
John Edward Broadbent7e860f12021-04-08 15:57:16 -070058inline static 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}
John Edward Broadbent7e860f12021-04-08 15:57:16 -070064inline static void activateImage(const std::string& objPath,
65 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050066{
Ed Tanous62598e32023-07-17 17:06:25 -070067 BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
George Liu9ae226f2023-06-21 17:56:46 +080068 sdbusplus::asio::setProperty(
69 *crow::connections::systemBus, service, objPath,
70 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
71 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
Ed Tanous8b242752023-06-27 17:17:13 -070072 [](const boost::system::error_code& ec) {
73 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -070074 {
Ed Tanous62598e32023-07-17 17:06:25 -070075 BMCWEB_LOG_DEBUG("error_code = {}", ec);
76 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -070077 }
Patrick Williams5a39f772023-10-20 11:20:21 -050078 });
Andrew Geissler86adcd62019-04-18 10:58:05 -050079}
Andrew Geissler0554c982019-04-23 14:40:12 -050080
81// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
82// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080083static void
84 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -050085 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050086{
Michael Shen80f79a42023-08-24 13:41:53 +000087 dbus::utility::DBusInterfacesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -050088
89 sdbusplus::message::object_path objPath;
90
91 m.read(objPath, interfacesProperties);
92
Ed Tanous62598e32023-07-17 17:06:25 -070093 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
Ed Tanouse3eb3d62022-12-21 11:56:07 -080094 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050095 {
Ed Tanous62598e32023-07-17 17:06:25 -070096 BMCWEB_LOG_DEBUG("interface = {}", interface.first);
Andrew Geissler86adcd62019-04-18 10:58:05 -050097
98 if (interface.first == "xyz.openbmc_project.Software.Activation")
99 {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500100 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +0800101 constexpr std::array<std::string_view, 1> interfaces = {
102 "xyz.openbmc_project.Software.Activation"};
103 dbus::utility::getDbusObject(
104 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700105 [objPath, asyncResp, payload(std::move(payload))](
Ed Tanous8b242752023-06-27 17:17:13 -0700106 const boost::system::error_code& ec,
Ed Tanousa3e65892021-09-16 14:13:20 -0700107 const std::vector<
108 std::pair<std::string, std::vector<std::string>>>&
109 objInfo) mutable {
Ed Tanous8b242752023-06-27 17:17:13 -0700110 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700111 {
Ed Tanous62598e32023-07-17 17:06:25 -0700112 BMCWEB_LOG_DEBUG("error_code = {}", ec);
113 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Andrew Geissler0554c982019-04-23 14:40:12 -0500114 if (asyncResp)
115 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700116 messages::internalError(asyncResp->res);
117 }
118 cleanUp();
119 return;
120 }
121 // Ensure we only got one service back
122 if (objInfo.size() != 1)
123 {
Ed Tanous62598e32023-07-17 17:06:25 -0700124 BMCWEB_LOG_ERROR("Invalid Object Size {}", objInfo.size());
Ed Tanous002d39b2022-05-31 08:59:27 -0700125 if (asyncResp)
126 {
127 messages::internalError(asyncResp->res);
128 }
129 cleanUp();
130 return;
131 }
132 // cancel timer only when
133 // xyz.openbmc_project.Software.Activation interface
134 // is added
135 fwAvailableTimer = nullptr;
136
137 activateImage(objPath.str, objInfo[0].first);
138 if (asyncResp)
139 {
140 std::shared_ptr<task::TaskData> task =
141 task::TaskData::createTask(
Ed Tanous8b242752023-06-27 17:17:13 -0700142 [](const boost::system::error_code& ec2,
Patrick Williams59d494e2022-07-22 19:26:55 -0500143 sdbusplus::message_t& msg,
Ed Tanous002d39b2022-05-31 08:59:27 -0700144 const std::shared_ptr<task::TaskData>&
145 taskData) {
Ed Tanous8b242752023-06-27 17:17:13 -0700146 if (ec2)
Ed Tanous002d39b2022-05-31 08:59:27 -0700147 {
148 return task::completed;
149 }
150
151 std::string iface;
152 dbus::utility::DBusPropertiesMap values;
153
154 std::string index = std::to_string(taskData->index);
155 msg.read(iface, values);
156
157 if (iface == "xyz.openbmc_project.Software.Activation")
158 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000159 const std::string* state = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700160 for (const auto& property : values)
161 {
162 if (property.first == "Activation")
163 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000164 state = std::get_if<std::string>(
165 &property.second);
166 if (state == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700167 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700168 taskData->messages.emplace_back(
169 messages::internalError());
James Feist32898ce2020-03-10 16:16:52 -0700170 return task::completed;
171 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700172 }
173 }
James Feist32898ce2020-03-10 16:16:52 -0700174
Ed Tanous002d39b2022-05-31 08:59:27 -0700175 if (state == nullptr)
176 {
177 return !task::completed;
178 }
James Feist32898ce2020-03-10 16:16:52 -0700179
Ed Tanous11ba3972022-07-11 09:50:41 -0700180 if (state->ends_with("Invalid") ||
181 state->ends_with("Failed"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700182 {
183 taskData->state = "Exception";
184 taskData->status = "Warning";
185 taskData->messages.emplace_back(
186 messages::taskAborted(index));
187 return task::completed;
188 }
James Feiste5d50062020-05-11 17:29:00 -0700189
Ed Tanous11ba3972022-07-11 09:50:41 -0700190 if (state->ends_with("Staged"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700191 {
192 taskData->state = "Stopping";
193 taskData->messages.emplace_back(
194 messages::taskPaused(index));
195
196 // its staged, set a long timer to
197 // allow them time to complete the
198 // update (probably cycle the
199 // system) if this expires then
Ed Tanous8ece0e42024-01-02 13:16:50 -0800200 // task will be canceled
Ed Tanous002d39b2022-05-31 08:59:27 -0700201 taskData->extendTimer(std::chrono::hours(5));
202 return !task::completed;
203 }
204
Ed Tanous11ba3972022-07-11 09:50:41 -0700205 if (state->ends_with("Active"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700206 {
207 taskData->messages.emplace_back(
208 messages::taskCompletedOK(index));
209 taskData->state = "Completed";
210 return task::completed;
211 }
212 }
213 else if (
214 iface ==
215 "xyz.openbmc_project.Software.ActivationProgress")
216 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700217 const uint8_t* progress = nullptr;
218 for (const auto& property : values)
219 {
220 if (property.first == "Progress")
221 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000222 progress =
223 std::get_if<uint8_t>(&property.second);
224 if (progress == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700225 {
James Feist32898ce2020-03-10 16:16:52 -0700226 taskData->messages.emplace_back(
Ed Tanous002d39b2022-05-31 08:59:27 -0700227 messages::internalError());
228 return task::completed;
James Feist32898ce2020-03-10 16:16:52 -0700229 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700230 }
231 }
James Feist32898ce2020-03-10 16:16:52 -0700232
Ed Tanous002d39b2022-05-31 08:59:27 -0700233 if (progress == nullptr)
234 {
235 return !task::completed;
236 }
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000237 taskData->percentComplete = *progress;
Ed Tanous002d39b2022-05-31 08:59:27 -0700238 taskData->messages.emplace_back(
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000239 messages::taskProgressChanged(index,
240 *progress));
James Feist32898ce2020-03-10 16:16:52 -0700241
Ed Tanous002d39b2022-05-31 08:59:27 -0700242 // if we're getting status updates it's
243 // still alive, update timer
244 taskData->extendTimer(std::chrono::minutes(5));
245 }
246
247 // as firmware update often results in a
248 // reboot, the task may never "complete"
249 // unless it is an error
250
251 return !task::completed;
Patrick Williams5a39f772023-10-20 11:20:21 -0500252 },
Ed Tanous002d39b2022-05-31 08:59:27 -0700253 "type='signal',interface='org.freedesktop.DBus.Properties',"
254 "member='PropertiesChanged',path='" +
255 objPath.str + "'");
256 task->startTimer(std::chrono::minutes(5));
257 task->populateResp(asyncResp->res);
258 task->payload.emplace(std::move(payload));
259 }
260 fwUpdateInProgress = false;
Patrick Williams5a39f772023-10-20 11:20:21 -0500261 });
Patrick Williams62bafc02022-09-08 17:35:35 -0500262
263 break;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500264 }
265 }
266}
267
Myung Bae8549b952023-08-16 15:18:19 -0400268inline void afterAvailbleTimerAsyncWait(
269 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
270 const boost::system::error_code& ec)
271{
272 cleanUp();
273 if (ec == boost::asio::error::operation_aborted)
274 {
275 // expected, we were canceled before the timer completed.
276 return;
277 }
278 BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
279 BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
280 if (ec)
281 {
282 BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
283 return;
284 }
285 if (asyncResp)
286 {
287 redfish::messages::internalError(asyncResp->res);
288 }
289}
290
291inline void
292 handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
293 const std::string& url, const std::string& type)
294{
295 if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
296 {
297 redfish::messages::invalidUpload(asyncResp->res, url,
298 "Invalid archive");
299 }
300 else if (type ==
301 "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
302 {
303 redfish::messages::invalidUpload(asyncResp->res, url,
304 "Invalid manifest");
305 }
306 else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
307 {
308 redfish::messages::invalidUpload(asyncResp->res, url,
309 "Invalid image format");
310 }
311 else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
312 {
313 redfish::messages::invalidUpload(asyncResp->res, url,
314 "Image version already exists");
315
316 redfish::messages::resourceAlreadyExists(
317 asyncResp->res, "UpdateService", "Version", "uploaded version");
318 }
319 else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
320 {
321 redfish::messages::resourceExhaustion(asyncResp->res, url);
322 }
Myung Bae4034a652023-08-17 08:47:35 -0400323 else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
Myung Bae8549b952023-08-16 15:18:19 -0400324 {
Myung Bae4034a652023-08-17 08:47:35 -0400325 redfish::messages::invalidUpload(asyncResp->res, url,
326 "Incompatible image version");
327 }
328 else if (type ==
329 "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
330 {
331 redfish::messages::invalidUpload(asyncResp->res, url,
332 "Update Access Key Expired");
333 }
334 else if (type ==
335 "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
336 {
337 redfish::messages::invalidUpload(asyncResp->res, url,
338 "Invalid image signature");
339 }
340 else if (type ==
341 "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
342 type == "xyz.openbmc_project.Software.Version.Error.HostFile")
343 {
344 BMCWEB_LOG_ERROR("Software Image Error type={}", type);
Myung Bae8549b952023-08-16 15:18:19 -0400345 redfish::messages::internalError(asyncResp->res);
346 }
Myung Bae4034a652023-08-17 08:47:35 -0400347 else
348 {
349 // Unrelated error types. Ignored
350 BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
351 return;
352 }
353 // Clear the timer
354 fwAvailableTimer = nullptr;
Myung Bae8549b952023-08-16 15:18:19 -0400355}
356
357inline void
358 afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
359 const std::string& url, sdbusplus::message_t& m)
360{
Michael Shen80f79a42023-08-24 13:41:53 +0000361 dbus::utility::DBusInterfacesMap interfacesProperties;
Myung Bae8549b952023-08-16 15:18:19 -0400362 sdbusplus::message::object_path objPath;
363 m.read(objPath, interfacesProperties);
364 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
365 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
366 interface : interfacesProperties)
367 {
368 if (interface.first == "xyz.openbmc_project.Logging.Entry")
369 {
370 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
371 value : interface.second)
372 {
373 if (value.first != "Message")
374 {
375 continue;
376 }
377 const std::string* type =
378 std::get_if<std::string>(&value.second);
379 if (type == nullptr)
380 {
381 // if this was our message, timeout will cover it
382 return;
383 }
Myung Bae8549b952023-08-16 15:18:19 -0400384 handleUpdateErrorType(asyncResp, url, *type);
385 }
386 }
387 }
388}
389
Andrew Geissler0554c982019-04-23 14:40:12 -0500390// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
391// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700392static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800393 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
394 const crow::Request& req, const std::string& url,
Gunnar Mills5d138942022-09-07 10:26:21 -0500395 int timeoutTimeSeconds = 25)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500396{
397 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800398 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500399 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500400 if (asyncResp)
401 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500402 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
403 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500404 return;
405 }
406
Andrew Geissler0554c982019-04-23 14:40:12 -0500407 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700408 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500409
Ed Tanous271584a2019-07-09 16:24:22 -0700410 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500411
412 fwAvailableTimer->async_wait(
Myung Bae8549b952023-08-16 15:18:19 -0400413 std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
414
Ed Tanousa3e65892021-09-16 14:13:20 -0700415 task::Payload payload(req);
Patrick Williams59d494e2022-07-22 19:26:55 -0500416 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
Ed Tanous62598e32023-07-17 17:06:25 -0700417 BMCWEB_LOG_DEBUG("Match fired");
Ed Tanousa3e65892021-09-16 14:13:20 -0700418 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500419 };
420
421 fwUpdateInProgress = true;
422
Patrick Williams59d494e2022-07-22 19:26:55 -0500423 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Andrew Geissler86adcd62019-04-18 10:58:05 -0500424 *crow::connections::systemBus,
425 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
426 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
427 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700428
Patrick Williams59d494e2022-07-22 19:26:55 -0500429 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
James Feist4cde5d92020-06-11 10:39:55 -0700430 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800431 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
432 "member='InterfacesAdded',"
433 "path='/xyz/openbmc_project/logging'",
Myung Bae8549b952023-08-16 15:18:19 -0400434 std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500435}
Jennifer Lee729dae72018-04-24 15:59:34 -0700436
Ed Tanousf86bcc82023-08-25 09:34:07 -0700437struct TftpUrl
438{
439 std::string fwFile;
440 std::string tftpServer;
441};
442
443inline std::optional<TftpUrl>
444 parseTftpUrl(std::string imageURI,
445 std::optional<std::string> transferProtocol,
446 crow::Response& res)
447{
448 if (imageURI.find("://") == std::string::npos)
449 {
450 if (imageURI.starts_with("/"))
451 {
452 messages::actionParameterValueTypeError(
453 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
454 return std::nullopt;
455 }
456 if (!transferProtocol)
457 {
458 messages::actionParameterValueTypeError(
459 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
460 return std::nullopt;
461 }
462 // OpenBMC currently only supports TFTP
463 if (*transferProtocol != "TFTP")
464 {
465 messages::actionParameterNotSupported(res, "TransferProtocol",
466 *transferProtocol);
467 BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
468 *transferProtocol);
469 return std::nullopt;
470 }
471 imageURI = "tftp://" + imageURI;
472 }
473
474 boost::system::result<boost::urls::url> url =
475 boost::urls::parse_absolute_uri(imageURI);
476 if (!url)
477 {
478 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
479 "UpdateService.SimpleUpdate");
480
481 return std::nullopt;
482 }
483 url->normalize();
484
485 if (url->scheme() != "tftp")
486 {
487 messages::actionParameterNotSupported(res, "ImageURI", imageURI);
488 return std::nullopt;
489 }
490 std::string path(url->encoded_path());
491 if (path.size() < 2)
492 {
493 messages::actionParameterNotSupported(res, "ImageURI", imageURI);
494 return std::nullopt;
495 }
496 path.erase(0, 1);
497 std::string host(url->encoded_host_and_port());
498 return TftpUrl{path, host};
499}
500
Andrew Geissler0554c982019-04-23 14:40:12 -0500501/**
502 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
503 * SimpleUpdate action.
504 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700505inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500506{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700507 BMCWEB_ROUTE(
508 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
Ed Tanoused398212021-06-09 17:05:54 -0700509 .privileges(redfish::privileges::postUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700510 .methods(boost::beast::http::verb::post)(
511 [&app](const crow::Request& req,
512 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000513 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700514 {
515 return;
516 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700517
Ed Tanous002d39b2022-05-31 08:59:27 -0700518 std::optional<std::string> transferProtocol;
519 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500520
Ed Tanous62598e32023-07-17 17:06:25 -0700521 BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
Andrew Geissler0554c982019-04-23 14:40:12 -0500522
Ed Tanous002d39b2022-05-31 08:59:27 -0700523 // User can pass in both TransferProtocol and ImageURI parameters or
524 // they can pass in just the ImageURI with the transfer protocol
525 // embedded within it.
526 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
527 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500528
Ed Tanous002d39b2022-05-31 08:59:27 -0700529 if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
530 transferProtocol, "ImageURI", imageURI))
531 {
Ed Tanous62598e32023-07-17 17:06:25 -0700532 BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
Ed Tanous002d39b2022-05-31 08:59:27 -0700533 return;
534 }
Ed Tanousf86bcc82023-08-25 09:34:07 -0700535 std::optional<TftpUrl> ret = parseTftpUrl(imageURI, transferProtocol,
536 asyncResp->res);
537 if (!ret)
Ed Tanous002d39b2022-05-31 08:59:27 -0700538 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700539 return;
540 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700541
Ed Tanousf86bcc82023-08-25 09:34:07 -0700542 BMCWEB_LOG_DEBUG("Server: {} File: {}", ret->tftpServer, ret->fwFile);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700543
Ed Tanous002d39b2022-05-31 08:59:27 -0700544 // Setup callback for when new software detected
545 // Give TFTP 10 minutes to complete
546 monitorForSoftwareAvailable(
547 asyncResp, req,
548 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
549 600);
550
551 // TFTP can take up to 10 minutes depending on image size and
552 // connection speed. Return to caller as soon as the TFTP operation
553 // has been started. The callback above will ensure the activate
554 // is started once the download has completed
555 redfish::messages::success(asyncResp->res);
556
557 // Call TFTP service
558 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800559 [](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700560 if (ec)
561 {
562 // messages::internalError(asyncResp->res);
563 cleanUp();
Ed Tanous62598e32023-07-17 17:06:25 -0700564 BMCWEB_LOG_DEBUG("error_code = {}", ec);
565 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -0700566 }
567 else
568 {
Ed Tanous62598e32023-07-17 17:06:25 -0700569 BMCWEB_LOG_DEBUG("Call to DownloaViaTFTP Success");
Ed Tanous002d39b2022-05-31 08:59:27 -0700570 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500571 },
Ed Tanous002d39b2022-05-31 08:59:27 -0700572 "xyz.openbmc_project.Software.Download",
573 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
Ed Tanousf86bcc82023-08-25 09:34:07 -0700574 "DownloadViaTFTP", ret->fwFile, ret->tftpServer);
Ed Tanous002d39b2022-05-31 08:59:27 -0700575
Ed Tanous62598e32023-07-17 17:06:25 -0700576 BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
Patrick Williams5a39f772023-10-20 11:20:21 -0500577 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700578}
579
George Liu0ed80c82020-05-12 16:06:27 +0800580inline void uploadImageFile(crow::Response& res, std::string_view body)
581{
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700582 std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
583
Ed Tanous62598e32023-07-17 17:06:25 -0700584 BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
George Liu0ed80c82020-05-12 16:06:27 +0800585 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
586 std::ofstream::trunc);
587 // set the permission of the file to 640
Patrick Williams89492a12023-05-10 07:51:34 -0500588 std::filesystem::perms permission = std::filesystem::perms::owner_read |
589 std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800590 std::filesystem::permissions(filepath, permission);
591 out << body;
592
593 if (out.bad())
594 {
595 messages::internalError(res);
596 cleanUp();
597 }
598}
599
600inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
601 const std::string& applyTime)
602{
603 std::string applyTimeNewVal;
604 if (applyTime == "Immediate")
605 {
606 applyTimeNewVal =
607 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
608 }
609 else if (applyTime == "OnReset")
610 {
611 applyTimeNewVal =
612 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
613 }
614 else
615 {
Ed Tanous62598e32023-07-17 17:06:25 -0700616 BMCWEB_LOG_INFO(
617 "ApplyTime value is not in the list of acceptable values");
George Liu0ed80c82020-05-12 16:06:27 +0800618 messages::propertyValueNotInList(asyncResp->res, applyTime,
619 "ApplyTime");
620 return;
621 }
622
623 // Set the requested image apply time value
George Liu9ae226f2023-06-21 17:56:46 +0800624 sdbusplus::asio::setProperty(
625 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
George Liu0ed80c82020-05-12 16:06:27 +0800626 "/xyz/openbmc_project/software/apply_time",
George Liu0ed80c82020-05-12 16:06:27 +0800627 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
George Liu9ae226f2023-06-21 17:56:46 +0800628 applyTimeNewVal, [asyncResp](const boost::system::error_code& ec) {
Patrick Williams5a39f772023-10-20 11:20:21 -0500629 if (ec)
630 {
631 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec);
632 messages::internalError(asyncResp->res);
633 return;
634 }
635 messages::success(asyncResp->res);
636 });
George Liu0ed80c82020-05-12 16:06:27 +0800637}
638
639inline void
640 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
641 const MultipartParser& parser)
642{
643 const std::string* uploadData = nullptr;
644 std::optional<std::string> applyTime = "OnReset";
645 bool targetFound = false;
646 for (const FormPart& formpart : parser.mime_fields)
647 {
648 boost::beast::http::fields::const_iterator it =
649 formpart.fields.find("Content-Disposition");
650 if (it == formpart.fields.end())
651 {
Ed Tanous62598e32023-07-17 17:06:25 -0700652 BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
George Liu0ed80c82020-05-12 16:06:27 +0800653 return;
654 }
Ed Tanous62598e32023-07-17 17:06:25 -0700655 BMCWEB_LOG_INFO("Parsing value {}", it->value());
George Liu0ed80c82020-05-12 16:06:27 +0800656
657 // The construction parameters of param_list must start with `;`
658 size_t index = it->value().find(';');
659 if (index == std::string::npos)
660 {
661 continue;
662 }
663
Patrick Williams89492a12023-05-10 07:51:34 -0500664 for (const auto& param :
George Liu0ed80c82020-05-12 16:06:27 +0800665 boost::beast::http::param_list{it->value().substr(index)})
666 {
667 if (param.first != "name" || param.second.empty())
668 {
669 continue;
670 }
671
672 if (param.second == "UpdateParameters")
673 {
674 std::vector<std::string> targets;
675 nlohmann::json content =
676 nlohmann::json::parse(formpart.content);
Ed Tanous7cb59f62022-05-05 11:48:31 -0700677 nlohmann::json::object_t* obj =
678 content.get_ptr<nlohmann::json::object_t*>();
679 if (obj == nullptr)
680 {
681 messages::propertyValueFormatError(asyncResp->res, targets,
682 "UpdateParameters");
683 return;
684 }
685
686 if (!json_util::readJsonObject(
687 *obj, asyncResp->res, "Targets", targets,
688 "@Redfish.OperationApplyTime", applyTime))
George Liu0ed80c82020-05-12 16:06:27 +0800689 {
690 return;
691 }
692 if (targets.size() != 1)
693 {
Ed Tanous7cb59f62022-05-05 11:48:31 -0700694 messages::propertyValueFormatError(asyncResp->res, targets,
695 "Targets");
George Liu0ed80c82020-05-12 16:06:27 +0800696 return;
697 }
698 if (targets[0] != "/redfish/v1/Managers/bmc")
699 {
Ed Tanous7cb59f62022-05-05 11:48:31 -0700700 messages::propertyValueNotInList(asyncResp->res, targets[0],
701 "Targets/0");
George Liu0ed80c82020-05-12 16:06:27 +0800702 return;
703 }
704 targetFound = true;
705 }
706 else if (param.second == "UpdateFile")
707 {
708 uploadData = &(formpart.content);
709 }
710 }
711 }
712
713 if (uploadData == nullptr)
714 {
Ed Tanous62598e32023-07-17 17:06:25 -0700715 BMCWEB_LOG_ERROR("Upload data is NULL");
George Liu0ed80c82020-05-12 16:06:27 +0800716 messages::propertyMissing(asyncResp->res, "UpdateFile");
717 return;
718 }
719 if (!targetFound)
720 {
721 messages::propertyMissing(asyncResp->res, "targets");
722 return;
723 }
724
725 setApplyTime(asyncResp, *applyTime);
726
727 uploadImageFile(asyncResp->res, *uploadData);
728}
729
Ed Tanousc2051d12022-05-11 12:21:55 -0700730inline void
731 handleUpdateServicePost(App& app, const crow::Request& req,
732 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
733{
Carson Labrado3ba00072022-06-06 19:40:56 +0000734 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700735 {
736 return;
737 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500738 std::string_view contentType = req.getHeaderValue("Content-Type");
Ed Tanousc2051d12022-05-11 12:21:55 -0700739
Ed Tanous62598e32023-07-17 17:06:25 -0700740 BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
Ed Tanousc2051d12022-05-11 12:21:55 -0700741
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500742 // Make sure that content type is application/octet-stream or
743 // multipart/form-data
Ed Tanous18f8f602023-07-18 10:07:23 -0700744 if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
George Liu0ed80c82020-05-12 16:06:27 +0800745 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500746 // Setup callback for when new software detected
747 monitorForSoftwareAvailable(asyncResp, req,
748 "/redfish/v1/UpdateService");
749
George Liu0ed80c82020-05-12 16:06:27 +0800750 uploadImageFile(asyncResp->res, req.body());
George Liu0ed80c82020-05-12 16:06:27 +0800751 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500752 else if (contentType.starts_with("multipart/form-data"))
George Liu0ed80c82020-05-12 16:06:27 +0800753 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500754 MultipartParser parser;
755
756 // Setup callback for when new software detected
757 monitorForSoftwareAvailable(asyncResp, req,
758 "/redfish/v1/UpdateService");
759
760 ParserError ec = parser.parse(req);
761 if (ec != ParserError::PARSER_SUCCESS)
762 {
763 // handle error
Ed Tanous62598e32023-07-17 17:06:25 -0700764 BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
765 static_cast<int>(ec));
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500766 messages::internalError(asyncResp->res);
767 return;
768 }
769 updateMultipartContext(asyncResp, parser);
George Liu0ed80c82020-05-12 16:06:27 +0800770 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500771 else
772 {
Ed Tanous62598e32023-07-17 17:06:25 -0700773 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500774 asyncResp->res.result(boost::beast::http::status::bad_request);
775 }
Ed Tanousc2051d12022-05-11 12:21:55 -0700776}
777
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700778inline void requestRoutesUpdateService(App& app)
779{
780 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700781 .privileges(redfish::privileges::getUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700782 .methods(boost::beast::http::verb::get)(
783 [&app](const crow::Request& req,
784 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000785 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700786 {
787 return;
788 }
789 asyncResp->res.jsonValue["@odata.type"] =
George Liu0ed80c82020-05-12 16:06:27 +0800790 "#UpdateService.v1_11_1.UpdateService";
Ed Tanous002d39b2022-05-31 08:59:27 -0700791 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
792 asyncResp->res.jsonValue["Id"] = "UpdateService";
793 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
794 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700795
Ed Tanous002d39b2022-05-31 08:59:27 -0700796 asyncResp->res.jsonValue["HttpPushUri"] =
797 "/redfish/v1/UpdateService/update";
George Liu0ed80c82020-05-12 16:06:27 +0800798 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
799 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700800
Ed Tanous002d39b2022-05-31 08:59:27 -0700801 // UpdateService cannot be disabled
802 asyncResp->res.jsonValue["ServiceEnabled"] = true;
803 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
804 "/redfish/v1/UpdateService/FirmwareInventory";
805 // Get the MaxImageSizeBytes
806 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
807 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530808
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700809#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
Ed Tanous002d39b2022-05-31 08:59:27 -0700810 // Update Actions object.
811 nlohmann::json& updateSvcSimpleUpdate =
812 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
813 updateSvcSimpleUpdate["target"] =
814 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
815 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
816 "TFTP"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700817#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700818 // Get the current ApplyTime value
819 sdbusplus::asio::getProperty<std::string>(
820 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
821 "/xyz/openbmc_project/software/apply_time",
822 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800823 [asyncResp](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700824 const std::string& applyTime) {
825 if (ec)
826 {
Ed Tanous62598e32023-07-17 17:06:25 -0700827 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -0700828 messages::internalError(asyncResp->res);
829 return;
830 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530831
Ed Tanous002d39b2022-05-31 08:59:27 -0700832 // Store the ApplyTime Value
833 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
834 "RequestedApplyTimes.Immediate")
835 {
836 asyncResp->res.jsonValue["HttpPushUriOptions"]
837 ["HttpPushUriApplyTime"]["ApplyTime"] =
838 "Immediate";
839 }
840 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
841 "RequestedApplyTimes.OnReset")
842 {
843 asyncResp->res.jsonValue["HttpPushUriOptions"]
844 ["HttpPushUriApplyTime"]["ApplyTime"] =
845 "OnReset";
846 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700847 });
Patrick Williams5a39f772023-10-20 11:20:21 -0500848 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700849 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700850 .privileges(redfish::privileges::patchUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700851 .methods(boost::beast::http::verb::patch)(
852 [&app](const crow::Request& req,
853 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000854 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700855 {
856 return;
857 }
Ed Tanous62598e32023-07-17 17:06:25 -0700858 BMCWEB_LOG_DEBUG("doPatch...");
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530859
Ed Tanous7cb59f62022-05-05 11:48:31 -0700860 std::optional<std::string> applyTime;
861 if (!json_util::readJsonPatch(
862 req, asyncResp->res,
863 "HttpPushUriOptions/HttpPushUriApplyTime/ApplyTime", applyTime))
Ed Tanous002d39b2022-05-31 08:59:27 -0700864 {
865 return;
866 }
867
Ed Tanous7cb59f62022-05-05 11:48:31 -0700868 if (applyTime)
Ed Tanous002d39b2022-05-31 08:59:27 -0700869 {
Ed Tanous7cb59f62022-05-05 11:48:31 -0700870 setApplyTime(asyncResp, *applyTime);
Ed Tanous002d39b2022-05-31 08:59:27 -0700871 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500872 });
Ed Tanousc2051d12022-05-11 12:21:55 -0700873
Ed Tanous4dc23f32022-05-11 11:32:19 -0700874 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
875 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700876 .methods(boost::beast::http::verb::post)(
Ed Tanousc2051d12022-05-11 12:21:55 -0700877 std::bind_front(handleUpdateServicePost, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700878}
879
880inline void requestRoutesSoftwareInventoryCollection(App& app)
881{
882 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700883 .privileges(redfish::privileges::getSoftwareInventoryCollection)
Ed Tanous14766872022-03-15 10:44:42 -0700884 .methods(boost::beast::http::verb::get)(
885 [&app](const crow::Request& req,
886 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000887 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700888 {
889 return;
890 }
891 asyncResp->res.jsonValue["@odata.type"] =
892 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
893 asyncResp->res.jsonValue["@odata.id"] =
894 "/redfish/v1/UpdateService/FirmwareInventory";
895 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
John Edward Broadbent08d81ad2022-05-17 20:00:23 -0700896 const std::array<const std::string_view, 1> iface = {
George Liue99073f2022-12-09 11:06:16 +0800897 "xyz.openbmc_project.Software.Version"};
Ed Tanous002d39b2022-05-31 08:59:27 -0700898
John Edward Broadbent08d81ad2022-05-17 20:00:23 -0700899 redfish::collection_util::getCollectionMembers(
900 asyncResp,
901 boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"),
902 iface, "/xyz/openbmc_project/software");
Patrick Williams5a39f772023-10-20 11:20:21 -0500903 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700904}
905/* Fill related item links (i.e. bmc, bios) in for inventory */
906inline static void
Ed Tanousac106bf2023-06-07 09:24:59 -0700907 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700908 const std::string& purpose)
909{
Willy Tueee00132022-06-14 14:53:17 -0700910 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700911 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700912 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700913 nlohmann::json::object_t item;
914 item["@odata.id"] = "/redfish/v1/Managers/bmc";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500915 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700916 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
917 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700918 }
Willy Tueee00132022-06-14 14:53:17 -0700919 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700920 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700921 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700922 nlohmann::json::object_t item;
923 item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500924 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700925 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
926 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700927 }
928 else
929 {
Carson Labradobf2dded2023-08-10 00:37:06 +0000930 BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700931 }
932}
933
Willy Tuaf246602022-06-14 15:51:53 -0700934inline void
935 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
936 const std::string& service, const std::string& path,
937 const std::string& swId)
938{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200939 sdbusplus::asio::getAllProperties(
940 *crow::connections::systemBus, service, path,
941 "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -0700942 [asyncResp,
Ed Tanous8b242752023-06-27 17:17:13 -0700943 swId](const boost::system::error_code& ec,
Willy Tuaf246602022-06-14 15:51:53 -0700944 const dbus::utility::DBusPropertiesMap& propertiesList) {
Ed Tanous8b242752023-06-27 17:17:13 -0700945 if (ec)
Willy Tuaf246602022-06-14 15:51:53 -0700946 {
947 messages::internalError(asyncResp->res);
948 return;
949 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200950
Willy Tuaf246602022-06-14 15:51:53 -0700951 const std::string* swInvPurpose = nullptr;
952 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200953
954 const bool success = sdbusplus::unpackPropertiesNoThrow(
955 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
956 swInvPurpose, "Version", version);
957
958 if (!success)
Willy Tuaf246602022-06-14 15:51:53 -0700959 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200960 messages::internalError(asyncResp->res);
961 return;
Willy Tuaf246602022-06-14 15:51:53 -0700962 }
963
964 if (swInvPurpose == nullptr)
965 {
Ed Tanous62598e32023-07-17 17:06:25 -0700966 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
Willy Tuaf246602022-06-14 15:51:53 -0700967 messages::internalError(asyncResp->res);
968 return;
969 }
970
Ed Tanous62598e32023-07-17 17:06:25 -0700971 BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
Willy Tuaf246602022-06-14 15:51:53 -0700972
973 if (version == nullptr)
974 {
Ed Tanous62598e32023-07-17 17:06:25 -0700975 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
Willy Tuaf246602022-06-14 15:51:53 -0700976
977 messages::internalError(asyncResp->res);
978
979 return;
980 }
981 asyncResp->res.jsonValue["Version"] = *version;
982 asyncResp->res.jsonValue["Id"] = swId;
983
984 // swInvPurpose is of format:
985 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
986 // Translate this to "ABC image"
987 size_t endDesc = swInvPurpose->rfind('.');
988 if (endDesc == std::string::npos)
989 {
990 messages::internalError(asyncResp->res);
991 return;
992 }
993 endDesc++;
994 if (endDesc >= swInvPurpose->size())
995 {
996 messages::internalError(asyncResp->res);
997 return;
998 }
999
1000 std::string formatDesc = swInvPurpose->substr(endDesc);
1001 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1002 getRelatedItems(asyncResp, *swInvPurpose);
Patrick Williams5a39f772023-10-20 11:20:21 -05001003 });
Willy Tuaf246602022-06-14 15:51:53 -07001004}
1005
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001006inline void requestRoutesSoftwareInventory(App& app)
1007{
1008 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001009 .privileges(redfish::privileges::getSoftwareInventory)
Ed Tanous002d39b2022-05-31 08:59:27 -07001010 .methods(boost::beast::http::verb::get)(
1011 [&app](const crow::Request& req,
1012 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1013 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001014 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001015 {
1016 return;
1017 }
1018 std::shared_ptr<std::string> swId =
1019 std::make_shared<std::string>(param);
1020
Ed Tanousef4c65b2023-04-24 15:28:50 -07001021 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1022 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001023
George Liue99073f2022-12-09 11:06:16 +08001024 constexpr std::array<std::string_view, 1> interfaces = {
1025 "xyz.openbmc_project.Software.Version"};
1026 dbus::utility::getSubTree(
1027 "/", 0, interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -07001028 [asyncResp,
George Liue99073f2022-12-09 11:06:16 +08001029 swId](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -07001030 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Ed Tanous62598e32023-07-17 17:06:25 -07001031 BMCWEB_LOG_DEBUG("doGet callback...");
Ed Tanous002d39b2022-05-31 08:59:27 -07001032 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001033 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001034 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001035 return;
1036 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001037
Ed Tanous002d39b2022-05-31 08:59:27 -07001038 // Ensure we find our input swId, otherwise return an error
1039 bool found = false;
1040 for (const std::pair<std::string,
1041 std::vector<std::pair<
1042 std::string, std::vector<std::string>>>>&
1043 obj : subtree)
1044 {
Ed Tanous11ba3972022-07-11 09:50:41 -07001045 if (!obj.first.ends_with(*swId))
Ed Tanous002d39b2022-05-31 08:59:27 -07001046 {
1047 continue;
1048 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001049
Ed Tanous002d39b2022-05-31 08:59:27 -07001050 if (obj.second.empty())
1051 {
1052 continue;
1053 }
1054
1055 found = true;
Willy Tueee00132022-06-14 14:53:17 -07001056 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
Willy Tuaf246602022-06-14 15:51:53 -07001057 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1058 *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001059 }
1060 if (!found)
1061 {
Ed Tanous62598e32023-07-17 17:06:25 -07001062 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001063 messages::resourceMissingAtURI(
Ed Tanousef4c65b2023-04-24 15:28:50 -07001064 asyncResp->res,
1065 boost::urls::format(
1066 "/redfish/v1/UpdateService/FirmwareInventory/{}",
1067 *swId));
Ed Tanous002d39b2022-05-31 08:59:27 -07001068 return;
1069 }
1070 asyncResp->res.jsonValue["@odata.type"] =
1071 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1072 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1073 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001074
Ed Tanous002d39b2022-05-31 08:59:27 -07001075 asyncResp->res.jsonValue["Updateable"] = false;
Willy Tueee00132022-06-14 14:53:17 -07001076 sw_util::getSwUpdatableStatus(asyncResp, swId);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001077 });
Patrick Williams5a39f772023-10-20 11:20:21 -05001078 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001079}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001080
1081} // namespace redfish