blob: fa2dc9e101b8968057b64aeb9dd5ffc9807d1b0f [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
Ed Tanousd02aad32024-02-13 14:43:34 -0800623 setDbusProperty(asyncResp, "xyz.openbmc_project.Settings",
624 sdbusplus::message::object_path(
625 "/xyz/openbmc_project/software/apply_time"),
626 "xyz.openbmc_project.Software.ApplyTime",
627 "RequestedApplyTime", "ApplyTime", applyTimeNewVal);
George Liu0ed80c82020-05-12 16:06:27 +0800628}
629
630inline void
631 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
632 const MultipartParser& parser)
633{
634 const std::string* uploadData = nullptr;
635 std::optional<std::string> applyTime = "OnReset";
636 bool targetFound = false;
637 for (const FormPart& formpart : parser.mime_fields)
638 {
639 boost::beast::http::fields::const_iterator it =
640 formpart.fields.find("Content-Disposition");
641 if (it == formpart.fields.end())
642 {
Ed Tanous62598e32023-07-17 17:06:25 -0700643 BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
George Liu0ed80c82020-05-12 16:06:27 +0800644 return;
645 }
Ed Tanous62598e32023-07-17 17:06:25 -0700646 BMCWEB_LOG_INFO("Parsing value {}", it->value());
George Liu0ed80c82020-05-12 16:06:27 +0800647
648 // The construction parameters of param_list must start with `;`
649 size_t index = it->value().find(';');
650 if (index == std::string::npos)
651 {
652 continue;
653 }
654
Patrick Williams89492a12023-05-10 07:51:34 -0500655 for (const auto& param :
George Liu0ed80c82020-05-12 16:06:27 +0800656 boost::beast::http::param_list{it->value().substr(index)})
657 {
658 if (param.first != "name" || param.second.empty())
659 {
660 continue;
661 }
662
663 if (param.second == "UpdateParameters")
664 {
665 std::vector<std::string> targets;
666 nlohmann::json content =
667 nlohmann::json::parse(formpart.content);
Ed Tanous7cb59f62022-05-05 11:48:31 -0700668 nlohmann::json::object_t* obj =
669 content.get_ptr<nlohmann::json::object_t*>();
670 if (obj == nullptr)
671 {
672 messages::propertyValueFormatError(asyncResp->res, targets,
673 "UpdateParameters");
674 return;
675 }
676
677 if (!json_util::readJsonObject(
678 *obj, asyncResp->res, "Targets", targets,
679 "@Redfish.OperationApplyTime", applyTime))
George Liu0ed80c82020-05-12 16:06:27 +0800680 {
681 return;
682 }
683 if (targets.size() != 1)
684 {
Ed Tanous7cb59f62022-05-05 11:48:31 -0700685 messages::propertyValueFormatError(asyncResp->res, targets,
686 "Targets");
George Liu0ed80c82020-05-12 16:06:27 +0800687 return;
688 }
689 if (targets[0] != "/redfish/v1/Managers/bmc")
690 {
Ed Tanous7cb59f62022-05-05 11:48:31 -0700691 messages::propertyValueNotInList(asyncResp->res, targets[0],
692 "Targets/0");
George Liu0ed80c82020-05-12 16:06:27 +0800693 return;
694 }
695 targetFound = true;
696 }
697 else if (param.second == "UpdateFile")
698 {
699 uploadData = &(formpart.content);
700 }
701 }
702 }
703
704 if (uploadData == nullptr)
705 {
Ed Tanous62598e32023-07-17 17:06:25 -0700706 BMCWEB_LOG_ERROR("Upload data is NULL");
George Liu0ed80c82020-05-12 16:06:27 +0800707 messages::propertyMissing(asyncResp->res, "UpdateFile");
708 return;
709 }
710 if (!targetFound)
711 {
712 messages::propertyMissing(asyncResp->res, "targets");
713 return;
714 }
715
716 setApplyTime(asyncResp, *applyTime);
717
718 uploadImageFile(asyncResp->res, *uploadData);
719}
720
Ed Tanousc2051d12022-05-11 12:21:55 -0700721inline void
722 handleUpdateServicePost(App& app, const crow::Request& req,
723 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
724{
Carson Labrado3ba00072022-06-06 19:40:56 +0000725 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700726 {
727 return;
728 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500729 std::string_view contentType = req.getHeaderValue("Content-Type");
Ed Tanousc2051d12022-05-11 12:21:55 -0700730
Ed Tanous62598e32023-07-17 17:06:25 -0700731 BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
Ed Tanousc2051d12022-05-11 12:21:55 -0700732
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500733 // Make sure that content type is application/octet-stream or
734 // multipart/form-data
Ed Tanous18f8f602023-07-18 10:07:23 -0700735 if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
George Liu0ed80c82020-05-12 16:06:27 +0800736 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500737 // Setup callback for when new software detected
738 monitorForSoftwareAvailable(asyncResp, req,
739 "/redfish/v1/UpdateService");
740
George Liu0ed80c82020-05-12 16:06:27 +0800741 uploadImageFile(asyncResp->res, req.body());
George Liu0ed80c82020-05-12 16:06:27 +0800742 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500743 else if (contentType.starts_with("multipart/form-data"))
George Liu0ed80c82020-05-12 16:06:27 +0800744 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500745 MultipartParser parser;
746
747 // Setup callback for when new software detected
748 monitorForSoftwareAvailable(asyncResp, req,
749 "/redfish/v1/UpdateService");
750
751 ParserError ec = parser.parse(req);
752 if (ec != ParserError::PARSER_SUCCESS)
753 {
754 // handle error
Ed Tanous62598e32023-07-17 17:06:25 -0700755 BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
756 static_cast<int>(ec));
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500757 messages::internalError(asyncResp->res);
758 return;
759 }
760 updateMultipartContext(asyncResp, parser);
George Liu0ed80c82020-05-12 16:06:27 +0800761 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500762 else
763 {
Ed Tanous62598e32023-07-17 17:06:25 -0700764 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500765 asyncResp->res.result(boost::beast::http::status::bad_request);
766 }
Ed Tanousc2051d12022-05-11 12:21:55 -0700767}
768
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700769inline void requestRoutesUpdateService(App& app)
770{
771 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700772 .privileges(redfish::privileges::getUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700773 .methods(boost::beast::http::verb::get)(
774 [&app](const crow::Request& req,
775 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000776 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700777 {
778 return;
779 }
780 asyncResp->res.jsonValue["@odata.type"] =
George Liu0ed80c82020-05-12 16:06:27 +0800781 "#UpdateService.v1_11_1.UpdateService";
Ed Tanous002d39b2022-05-31 08:59:27 -0700782 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
783 asyncResp->res.jsonValue["Id"] = "UpdateService";
784 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
785 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700786
Ed Tanous002d39b2022-05-31 08:59:27 -0700787 asyncResp->res.jsonValue["HttpPushUri"] =
788 "/redfish/v1/UpdateService/update";
George Liu0ed80c82020-05-12 16:06:27 +0800789 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
790 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700791
Ed Tanous002d39b2022-05-31 08:59:27 -0700792 // UpdateService cannot be disabled
793 asyncResp->res.jsonValue["ServiceEnabled"] = true;
794 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
795 "/redfish/v1/UpdateService/FirmwareInventory";
796 // Get the MaxImageSizeBytes
797 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
798 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530799
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700800#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
Ed Tanous002d39b2022-05-31 08:59:27 -0700801 // Update Actions object.
802 nlohmann::json& updateSvcSimpleUpdate =
803 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
804 updateSvcSimpleUpdate["target"] =
805 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
806 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
807 "TFTP"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700808#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700809 // Get the current ApplyTime value
810 sdbusplus::asio::getProperty<std::string>(
811 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
812 "/xyz/openbmc_project/software/apply_time",
813 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800814 [asyncResp](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700815 const std::string& applyTime) {
816 if (ec)
817 {
Ed Tanous62598e32023-07-17 17:06:25 -0700818 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -0700819 messages::internalError(asyncResp->res);
820 return;
821 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530822
Ed Tanous002d39b2022-05-31 08:59:27 -0700823 // Store the ApplyTime Value
824 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
825 "RequestedApplyTimes.Immediate")
826 {
827 asyncResp->res.jsonValue["HttpPushUriOptions"]
828 ["HttpPushUriApplyTime"]["ApplyTime"] =
829 "Immediate";
830 }
831 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
832 "RequestedApplyTimes.OnReset")
833 {
834 asyncResp->res.jsonValue["HttpPushUriOptions"]
835 ["HttpPushUriApplyTime"]["ApplyTime"] =
836 "OnReset";
837 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700838 });
Patrick Williams5a39f772023-10-20 11:20:21 -0500839 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700840 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700841 .privileges(redfish::privileges::patchUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700842 .methods(boost::beast::http::verb::patch)(
843 [&app](const crow::Request& req,
844 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000845 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700846 {
847 return;
848 }
Ed Tanous62598e32023-07-17 17:06:25 -0700849 BMCWEB_LOG_DEBUG("doPatch...");
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530850
Ed Tanous7cb59f62022-05-05 11:48:31 -0700851 std::optional<std::string> applyTime;
852 if (!json_util::readJsonPatch(
853 req, asyncResp->res,
854 "HttpPushUriOptions/HttpPushUriApplyTime/ApplyTime", applyTime))
Ed Tanous002d39b2022-05-31 08:59:27 -0700855 {
856 return;
857 }
858
Ed Tanous7cb59f62022-05-05 11:48:31 -0700859 if (applyTime)
Ed Tanous002d39b2022-05-31 08:59:27 -0700860 {
Ed Tanous7cb59f62022-05-05 11:48:31 -0700861 setApplyTime(asyncResp, *applyTime);
Ed Tanous002d39b2022-05-31 08:59:27 -0700862 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500863 });
Ed Tanousc2051d12022-05-11 12:21:55 -0700864
Ed Tanous4dc23f32022-05-11 11:32:19 -0700865 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
866 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700867 .methods(boost::beast::http::verb::post)(
Ed Tanousc2051d12022-05-11 12:21:55 -0700868 std::bind_front(handleUpdateServicePost, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700869}
870
871inline void requestRoutesSoftwareInventoryCollection(App& app)
872{
873 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700874 .privileges(redfish::privileges::getSoftwareInventoryCollection)
Ed Tanous14766872022-03-15 10:44:42 -0700875 .methods(boost::beast::http::verb::get)(
876 [&app](const crow::Request& req,
877 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000878 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700879 {
880 return;
881 }
882 asyncResp->res.jsonValue["@odata.type"] =
883 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
884 asyncResp->res.jsonValue["@odata.id"] =
885 "/redfish/v1/UpdateService/FirmwareInventory";
886 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
John Edward Broadbent08d81ad2022-05-17 20:00:23 -0700887 const std::array<const std::string_view, 1> iface = {
George Liue99073f2022-12-09 11:06:16 +0800888 "xyz.openbmc_project.Software.Version"};
Ed Tanous002d39b2022-05-31 08:59:27 -0700889
John Edward Broadbent08d81ad2022-05-17 20:00:23 -0700890 redfish::collection_util::getCollectionMembers(
891 asyncResp,
892 boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"),
893 iface, "/xyz/openbmc_project/software");
Patrick Williams5a39f772023-10-20 11:20:21 -0500894 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700895}
896/* Fill related item links (i.e. bmc, bios) in for inventory */
897inline static void
Ed Tanousac106bf2023-06-07 09:24:59 -0700898 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700899 const std::string& purpose)
900{
Willy Tueee00132022-06-14 14:53:17 -0700901 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700902 {
Ed Tanousac106bf2023-06-07 09:24:59 -0700903 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -0700904 nlohmann::json::object_t item;
905 item["@odata.id"] = "/redfish/v1/Managers/bmc";
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500906 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -0700907 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
908 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700909 }
Willy Tueee00132022-06-14 14:53:17 -0700910 else if (purpose == sw_util::biosPurpose)
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/Systems/system/Bios";
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 }
919 else
920 {
Carson Labradobf2dded2023-08-10 00:37:06 +0000921 BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700922 }
923}
924
Willy Tuaf246602022-06-14 15:51:53 -0700925inline void
926 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
927 const std::string& service, const std::string& path,
928 const std::string& swId)
929{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200930 sdbusplus::asio::getAllProperties(
931 *crow::connections::systemBus, service, path,
932 "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -0700933 [asyncResp,
Ed Tanous8b242752023-06-27 17:17:13 -0700934 swId](const boost::system::error_code& ec,
Willy Tuaf246602022-06-14 15:51:53 -0700935 const dbus::utility::DBusPropertiesMap& propertiesList) {
Ed Tanous8b242752023-06-27 17:17:13 -0700936 if (ec)
Willy Tuaf246602022-06-14 15:51:53 -0700937 {
938 messages::internalError(asyncResp->res);
939 return;
940 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200941
Willy Tuaf246602022-06-14 15:51:53 -0700942 const std::string* swInvPurpose = nullptr;
943 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200944
945 const bool success = sdbusplus::unpackPropertiesNoThrow(
946 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
947 swInvPurpose, "Version", version);
948
949 if (!success)
Willy Tuaf246602022-06-14 15:51:53 -0700950 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200951 messages::internalError(asyncResp->res);
952 return;
Willy Tuaf246602022-06-14 15:51:53 -0700953 }
954
955 if (swInvPurpose == nullptr)
956 {
Ed Tanous62598e32023-07-17 17:06:25 -0700957 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
Willy Tuaf246602022-06-14 15:51:53 -0700958 messages::internalError(asyncResp->res);
959 return;
960 }
961
Ed Tanous62598e32023-07-17 17:06:25 -0700962 BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
Willy Tuaf246602022-06-14 15:51:53 -0700963
964 if (version == nullptr)
965 {
Ed Tanous62598e32023-07-17 17:06:25 -0700966 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
Willy Tuaf246602022-06-14 15:51:53 -0700967
968 messages::internalError(asyncResp->res);
969
970 return;
971 }
972 asyncResp->res.jsonValue["Version"] = *version;
973 asyncResp->res.jsonValue["Id"] = swId;
974
975 // swInvPurpose is of format:
976 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
977 // Translate this to "ABC image"
978 size_t endDesc = swInvPurpose->rfind('.');
979 if (endDesc == std::string::npos)
980 {
981 messages::internalError(asyncResp->res);
982 return;
983 }
984 endDesc++;
985 if (endDesc >= swInvPurpose->size())
986 {
987 messages::internalError(asyncResp->res);
988 return;
989 }
990
991 std::string formatDesc = swInvPurpose->substr(endDesc);
992 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
993 getRelatedItems(asyncResp, *swInvPurpose);
Patrick Williams5a39f772023-10-20 11:20:21 -0500994 });
Willy Tuaf246602022-06-14 15:51:53 -0700995}
996
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700997inline void requestRoutesSoftwareInventory(App& app)
998{
999 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001000 .privileges(redfish::privileges::getSoftwareInventory)
Ed Tanous002d39b2022-05-31 08:59:27 -07001001 .methods(boost::beast::http::verb::get)(
1002 [&app](const crow::Request& req,
1003 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1004 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001005 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001006 {
1007 return;
1008 }
1009 std::shared_ptr<std::string> swId =
1010 std::make_shared<std::string>(param);
1011
Ed Tanousef4c65b2023-04-24 15:28:50 -07001012 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1013 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001014
George Liue99073f2022-12-09 11:06:16 +08001015 constexpr std::array<std::string_view, 1> interfaces = {
1016 "xyz.openbmc_project.Software.Version"};
1017 dbus::utility::getSubTree(
1018 "/", 0, interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -07001019 [asyncResp,
George Liue99073f2022-12-09 11:06:16 +08001020 swId](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -07001021 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Ed Tanous62598e32023-07-17 17:06:25 -07001022 BMCWEB_LOG_DEBUG("doGet callback...");
Ed Tanous002d39b2022-05-31 08:59:27 -07001023 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001024 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001025 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001026 return;
1027 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001028
Ed Tanous002d39b2022-05-31 08:59:27 -07001029 // Ensure we find our input swId, otherwise return an error
1030 bool found = false;
1031 for (const std::pair<std::string,
1032 std::vector<std::pair<
1033 std::string, std::vector<std::string>>>>&
1034 obj : subtree)
1035 {
Ed Tanous11ba3972022-07-11 09:50:41 -07001036 if (!obj.first.ends_with(*swId))
Ed Tanous002d39b2022-05-31 08:59:27 -07001037 {
1038 continue;
1039 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001040
Ed Tanous002d39b2022-05-31 08:59:27 -07001041 if (obj.second.empty())
1042 {
1043 continue;
1044 }
1045
1046 found = true;
Willy Tueee00132022-06-14 14:53:17 -07001047 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
Willy Tuaf246602022-06-14 15:51:53 -07001048 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1049 *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001050 }
1051 if (!found)
1052 {
Ed Tanous62598e32023-07-17 17:06:25 -07001053 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
Ed Tanous002d39b2022-05-31 08:59:27 -07001054 messages::resourceMissingAtURI(
Ed Tanousef4c65b2023-04-24 15:28:50 -07001055 asyncResp->res,
1056 boost::urls::format(
1057 "/redfish/v1/UpdateService/FirmwareInventory/{}",
1058 *swId));
Ed Tanous002d39b2022-05-31 08:59:27 -07001059 return;
1060 }
1061 asyncResp->res.jsonValue["@odata.type"] =
1062 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1063 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1064 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001065
Ed Tanous002d39b2022-05-31 08:59:27 -07001066 asyncResp->res.jsonValue["Updateable"] = false;
Willy Tueee00132022-06-14 14:53:17 -07001067 sw_util::getSwUpdatableStatus(asyncResp, swId);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001068 });
Patrick Williams5a39f772023-10-20 11:20:21 -05001069 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001070}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001071
1072} // namespace redfish