blob: f66fe46a117437102dad97338e87d100b4f32ccd [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"
Ed Tanous5b904292024-04-16 11:10:17 -070022#include "error_messages.hpp"
Ed Tanous757178a2024-04-03 14:32:38 -070023#include "generated/enums/update_service.hpp"
George Liu0ed80c82020-05-12 16:06:27 +080024#include "multipart_parser.hpp"
Ed Tanous2c6ffdb2023-06-28 11:28:38 -070025#include "ossl_random.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080026#include "query.hpp"
27#include "registries/privilege_registry.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080028#include "task.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070029#include "task_messages.hpp"
John Edward Broadbent08d81ad2022-05-17 20:00:23 -070030#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080031#include "utils/dbus_utils.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -070032#include "utils/json_utils.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080033#include "utils/sw_utils.hpp"
34
George Liue99073f2022-12-09 11:06:16 +080035#include <boost/system/error_code.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070036#include <boost/url/format.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070037#include <sdbusplus/asio/property.hpp>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080038#include <sdbusplus/bus/match.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020039#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050040
George Liu2b731192023-01-11 16:27:13 +080041#include <array>
George Liu0ed80c82020-05-12 16:06:27 +080042#include <filesystem>
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -070043#include <memory>
Ed Tanous7cb59f62022-05-05 11:48:31 -070044#include <optional>
45#include <string>
George Liu2b731192023-01-11 16:27:13 +080046#include <string_view>
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -070047#include <vector>
George Liu2b731192023-01-11 16:27:13 +080048
Ed Tanous1abe55e2018-09-05 08:30:59 -070049namespace redfish
50{
Ed Tanous27826b52018-10-29 11:40:58 -070051
Andrew Geissler0e7de462019-03-04 19:11:54 -060052// Match signals added on software path
Ed Tanouscf9e4172022-12-21 09:30:16 -080053// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050054static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
Ed Tanouscf9e4172022-12-21 09:30:16 -080055// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050056static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060057// Only allow one update at a time
Ed Tanouscf9e4172022-12-21 09:30:16 -080058// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Andrew Geissler0e7de462019-03-04 19:11:54 -060059static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050060// Timer for software available
Ed Tanouscf9e4172022-12-21 09:30:16 -080061// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous271584a2019-07-09 16:24:22 -070062static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050063
Ed Tanousdf254f22024-04-01 13:25:46 -070064inline void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050065{
66 fwUpdateInProgress = false;
67 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070068 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050069}
Ed Tanousdf254f22024-04-01 13:25:46 -070070
71inline void activateImage(const std::string& objPath,
72 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050073{
Ed Tanous62598e32023-07-17 17:06:25 -070074 BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
George Liu9ae226f2023-06-21 17:56:46 +080075 sdbusplus::asio::setProperty(
76 *crow::connections::systemBus, service, objPath,
77 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
78 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
Ed Tanous8b242752023-06-27 17:17:13 -070079 [](const boost::system::error_code& ec) {
80 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -070081 {
Ed Tanous62598e32023-07-17 17:06:25 -070082 BMCWEB_LOG_DEBUG("error_code = {}", ec);
83 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -070084 }
Patrick Williams5a39f772023-10-20 11:20:21 -050085 });
Andrew Geissler86adcd62019-04-18 10:58:05 -050086}
Andrew Geissler0554c982019-04-23 14:40:12 -050087
88// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
89// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080090static void
91 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -050092 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050093{
Michael Shen80f79a42023-08-24 13:41:53 +000094 dbus::utility::DBusInterfacesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -050095
96 sdbusplus::message::object_path objPath;
97
98 m.read(objPath, interfacesProperties);
99
Ed Tanous62598e32023-07-17 17:06:25 -0700100 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
Ed Tanouse3eb3d62022-12-21 11:56:07 -0800101 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500102 {
Ed Tanous62598e32023-07-17 17:06:25 -0700103 BMCWEB_LOG_DEBUG("interface = {}", interface.first);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500104
105 if (interface.first == "xyz.openbmc_project.Software.Activation")
106 {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500107 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +0800108 constexpr std::array<std::string_view, 1> interfaces = {
109 "xyz.openbmc_project.Software.Activation"};
110 dbus::utility::getDbusObject(
111 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700112 [objPath, asyncResp, payload(std::move(payload))](
Ed Tanous8b242752023-06-27 17:17:13 -0700113 const boost::system::error_code& ec,
Ed Tanousa3e65892021-09-16 14:13:20 -0700114 const std::vector<
115 std::pair<std::string, std::vector<std::string>>>&
116 objInfo) mutable {
Ed Tanous8b242752023-06-27 17:17:13 -0700117 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700118 {
Ed Tanous62598e32023-07-17 17:06:25 -0700119 BMCWEB_LOG_DEBUG("error_code = {}", ec);
120 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Andrew Geissler0554c982019-04-23 14:40:12 -0500121 if (asyncResp)
122 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700123 messages::internalError(asyncResp->res);
124 }
125 cleanUp();
126 return;
127 }
128 // Ensure we only got one service back
129 if (objInfo.size() != 1)
130 {
Ed Tanous62598e32023-07-17 17:06:25 -0700131 BMCWEB_LOG_ERROR("Invalid Object Size {}", objInfo.size());
Ed Tanous002d39b2022-05-31 08:59:27 -0700132 if (asyncResp)
133 {
134 messages::internalError(asyncResp->res);
135 }
136 cleanUp();
137 return;
138 }
139 // cancel timer only when
140 // xyz.openbmc_project.Software.Activation interface
141 // is added
142 fwAvailableTimer = nullptr;
143
144 activateImage(objPath.str, objInfo[0].first);
145 if (asyncResp)
146 {
147 std::shared_ptr<task::TaskData> task =
148 task::TaskData::createTask(
Ed Tanous8b242752023-06-27 17:17:13 -0700149 [](const boost::system::error_code& ec2,
Patrick Williams59d494e2022-07-22 19:26:55 -0500150 sdbusplus::message_t& msg,
Ed Tanous002d39b2022-05-31 08:59:27 -0700151 const std::shared_ptr<task::TaskData>&
152 taskData) {
Ed Tanous8b242752023-06-27 17:17:13 -0700153 if (ec2)
Ed Tanous002d39b2022-05-31 08:59:27 -0700154 {
155 return task::completed;
156 }
157
158 std::string iface;
159 dbus::utility::DBusPropertiesMap values;
160
161 std::string index = std::to_string(taskData->index);
162 msg.read(iface, values);
163
164 if (iface == "xyz.openbmc_project.Software.Activation")
165 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000166 const std::string* state = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700167 for (const auto& property : values)
168 {
169 if (property.first == "Activation")
170 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000171 state = std::get_if<std::string>(
172 &property.second);
173 if (state == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700174 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700175 taskData->messages.emplace_back(
176 messages::internalError());
James Feist32898ce2020-03-10 16:16:52 -0700177 return task::completed;
178 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700179 }
180 }
James Feist32898ce2020-03-10 16:16:52 -0700181
Ed Tanous002d39b2022-05-31 08:59:27 -0700182 if (state == nullptr)
183 {
184 return !task::completed;
185 }
James Feist32898ce2020-03-10 16:16:52 -0700186
Ed Tanous11ba3972022-07-11 09:50:41 -0700187 if (state->ends_with("Invalid") ||
188 state->ends_with("Failed"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700189 {
190 taskData->state = "Exception";
191 taskData->status = "Warning";
192 taskData->messages.emplace_back(
193 messages::taskAborted(index));
194 return task::completed;
195 }
James Feiste5d50062020-05-11 17:29:00 -0700196
Ed Tanous11ba3972022-07-11 09:50:41 -0700197 if (state->ends_with("Staged"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700198 {
199 taskData->state = "Stopping";
200 taskData->messages.emplace_back(
201 messages::taskPaused(index));
202
203 // its staged, set a long timer to
204 // allow them time to complete the
205 // update (probably cycle the
206 // system) if this expires then
Ed Tanous8ece0e42024-01-02 13:16:50 -0800207 // task will be canceled
Ed Tanous002d39b2022-05-31 08:59:27 -0700208 taskData->extendTimer(std::chrono::hours(5));
209 return !task::completed;
210 }
211
Ed Tanous11ba3972022-07-11 09:50:41 -0700212 if (state->ends_with("Active"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700213 {
214 taskData->messages.emplace_back(
215 messages::taskCompletedOK(index));
216 taskData->state = "Completed";
217 return task::completed;
218 }
219 }
220 else if (
221 iface ==
222 "xyz.openbmc_project.Software.ActivationProgress")
223 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700224 const uint8_t* progress = nullptr;
225 for (const auto& property : values)
226 {
227 if (property.first == "Progress")
228 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000229 progress =
230 std::get_if<uint8_t>(&property.second);
231 if (progress == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700232 {
James Feist32898ce2020-03-10 16:16:52 -0700233 taskData->messages.emplace_back(
Ed Tanous002d39b2022-05-31 08:59:27 -0700234 messages::internalError());
235 return task::completed;
James Feist32898ce2020-03-10 16:16:52 -0700236 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700237 }
238 }
James Feist32898ce2020-03-10 16:16:52 -0700239
Ed Tanous002d39b2022-05-31 08:59:27 -0700240 if (progress == nullptr)
241 {
242 return !task::completed;
243 }
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000244 taskData->percentComplete = *progress;
Ed Tanous002d39b2022-05-31 08:59:27 -0700245 taskData->messages.emplace_back(
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000246 messages::taskProgressChanged(index,
247 *progress));
James Feist32898ce2020-03-10 16:16:52 -0700248
Ed Tanous002d39b2022-05-31 08:59:27 -0700249 // if we're getting status updates it's
250 // still alive, update timer
251 taskData->extendTimer(std::chrono::minutes(5));
252 }
253
254 // as firmware update often results in a
255 // reboot, the task may never "complete"
256 // unless it is an error
257
258 return !task::completed;
Patrick Williams5a39f772023-10-20 11:20:21 -0500259 },
Ed Tanous002d39b2022-05-31 08:59:27 -0700260 "type='signal',interface='org.freedesktop.DBus.Properties',"
261 "member='PropertiesChanged',path='" +
262 objPath.str + "'");
263 task->startTimer(std::chrono::minutes(5));
264 task->populateResp(asyncResp->res);
265 task->payload.emplace(std::move(payload));
266 }
267 fwUpdateInProgress = false;
Patrick Williams5a39f772023-10-20 11:20:21 -0500268 });
Patrick Williams62bafc02022-09-08 17:35:35 -0500269
270 break;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500271 }
272 }
273}
274
Myung Bae8549b952023-08-16 15:18:19 -0400275inline void afterAvailbleTimerAsyncWait(
276 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
277 const boost::system::error_code& ec)
278{
279 cleanUp();
280 if (ec == boost::asio::error::operation_aborted)
281 {
282 // expected, we were canceled before the timer completed.
283 return;
284 }
285 BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
286 BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
287 if (ec)
288 {
289 BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
290 return;
291 }
292 if (asyncResp)
293 {
294 redfish::messages::internalError(asyncResp->res);
295 }
296}
297
298inline void
299 handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
300 const std::string& url, const std::string& type)
301{
302 if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
303 {
304 redfish::messages::invalidUpload(asyncResp->res, url,
305 "Invalid archive");
306 }
307 else if (type ==
308 "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
309 {
310 redfish::messages::invalidUpload(asyncResp->res, url,
311 "Invalid manifest");
312 }
313 else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
314 {
315 redfish::messages::invalidUpload(asyncResp->res, url,
316 "Invalid image format");
317 }
318 else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
319 {
320 redfish::messages::invalidUpload(asyncResp->res, url,
321 "Image version already exists");
322
323 redfish::messages::resourceAlreadyExists(
324 asyncResp->res, "UpdateService", "Version", "uploaded version");
325 }
326 else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
327 {
328 redfish::messages::resourceExhaustion(asyncResp->res, url);
329 }
Myung Bae4034a652023-08-17 08:47:35 -0400330 else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
Myung Bae8549b952023-08-16 15:18:19 -0400331 {
Myung Bae4034a652023-08-17 08:47:35 -0400332 redfish::messages::invalidUpload(asyncResp->res, url,
333 "Incompatible image version");
334 }
335 else if (type ==
336 "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
337 {
338 redfish::messages::invalidUpload(asyncResp->res, url,
339 "Update Access Key Expired");
340 }
341 else if (type ==
342 "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
343 {
344 redfish::messages::invalidUpload(asyncResp->res, url,
345 "Invalid image signature");
346 }
347 else if (type ==
348 "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
349 type == "xyz.openbmc_project.Software.Version.Error.HostFile")
350 {
351 BMCWEB_LOG_ERROR("Software Image Error type={}", type);
Myung Bae8549b952023-08-16 15:18:19 -0400352 redfish::messages::internalError(asyncResp->res);
353 }
Myung Bae4034a652023-08-17 08:47:35 -0400354 else
355 {
356 // Unrelated error types. Ignored
357 BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
358 return;
359 }
360 // Clear the timer
361 fwAvailableTimer = nullptr;
Myung Bae8549b952023-08-16 15:18:19 -0400362}
363
364inline void
365 afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
366 const std::string& url, sdbusplus::message_t& m)
367{
Michael Shen80f79a42023-08-24 13:41:53 +0000368 dbus::utility::DBusInterfacesMap interfacesProperties;
Myung Bae8549b952023-08-16 15:18:19 -0400369 sdbusplus::message::object_path objPath;
370 m.read(objPath, interfacesProperties);
371 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
372 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
373 interface : interfacesProperties)
374 {
375 if (interface.first == "xyz.openbmc_project.Logging.Entry")
376 {
377 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
378 value : interface.second)
379 {
380 if (value.first != "Message")
381 {
382 continue;
383 }
384 const std::string* type =
385 std::get_if<std::string>(&value.second);
386 if (type == nullptr)
387 {
388 // if this was our message, timeout will cover it
389 return;
390 }
Myung Bae8549b952023-08-16 15:18:19 -0400391 handleUpdateErrorType(asyncResp, url, *type);
392 }
393 }
394 }
395}
396
Andrew Geissler0554c982019-04-23 14:40:12 -0500397// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
398// then no asyncResp updates will occur
Ed Tanousf5139332024-04-03 13:25:04 -0700399inline void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800400 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
401 const crow::Request& req, const std::string& url,
Gunnar Mills5d138942022-09-07 10:26:21 -0500402 int timeoutTimeSeconds = 25)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500403{
404 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800405 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500406 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500407 if (asyncResp)
408 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500409 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
410 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500411 return;
412 }
413
Ed Tanous8e8245d2024-04-11 22:21:38 -0700414 if (req.ioService == nullptr)
415 {
416 messages::internalError(asyncResp->res);
417 return;
418 }
419
Andrew Geissler0554c982019-04-23 14:40:12 -0500420 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700421 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500422
Ed Tanous271584a2019-07-09 16:24:22 -0700423 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500424
425 fwAvailableTimer->async_wait(
Myung Bae8549b952023-08-16 15:18:19 -0400426 std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
427
Ed Tanousa3e65892021-09-16 14:13:20 -0700428 task::Payload payload(req);
Patrick Williams59d494e2022-07-22 19:26:55 -0500429 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
Ed Tanous62598e32023-07-17 17:06:25 -0700430 BMCWEB_LOG_DEBUG("Match fired");
Ed Tanousa3e65892021-09-16 14:13:20 -0700431 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500432 };
433
434 fwUpdateInProgress = true;
435
Patrick Williams59d494e2022-07-22 19:26:55 -0500436 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Andrew Geissler86adcd62019-04-18 10:58:05 -0500437 *crow::connections::systemBus,
438 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
439 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
440 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700441
Patrick Williams59d494e2022-07-22 19:26:55 -0500442 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
James Feist4cde5d92020-06-11 10:39:55 -0700443 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800444 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
445 "member='InterfacesAdded',"
446 "path='/xyz/openbmc_project/logging'",
Myung Bae8549b952023-08-16 15:18:19 -0400447 std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500448}
Jennifer Lee729dae72018-04-24 15:59:34 -0700449
Ed Tanous757178a2024-04-03 14:32:38 -0700450inline std::optional<boost::urls::url>
451 parseSimpleUpdateUrl(std::string imageURI,
452 std::optional<std::string> transferProtocol,
453 crow::Response& res)
Ed Tanousf86bcc82023-08-25 09:34:07 -0700454{
455 if (imageURI.find("://") == std::string::npos)
456 {
457 if (imageURI.starts_with("/"))
458 {
459 messages::actionParameterValueTypeError(
460 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
461 return std::nullopt;
462 }
463 if (!transferProtocol)
464 {
465 messages::actionParameterValueTypeError(
466 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
467 return std::nullopt;
468 }
Ed Tanouse5cf7772024-04-03 13:45:31 -0700469 // OpenBMC currently only supports TFTP or HTTPS
Ed Tanous757178a2024-04-03 14:32:38 -0700470 if (*transferProtocol == "TFTP")
471 {
472 imageURI = "tftp://" + imageURI;
473 }
Ed Tanouse5cf7772024-04-03 13:45:31 -0700474 else if (*transferProtocol == "HTTPS")
475 {
476 imageURI = "https://" + imageURI;
477 }
Ed Tanous757178a2024-04-03 14:32:38 -0700478 else
Ed Tanousf86bcc82023-08-25 09:34:07 -0700479 {
480 messages::actionParameterNotSupported(res, "TransferProtocol",
481 *transferProtocol);
482 BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
483 *transferProtocol);
484 return std::nullopt;
485 }
Ed Tanousf86bcc82023-08-25 09:34:07 -0700486 }
487
488 boost::system::result<boost::urls::url> url =
489 boost::urls::parse_absolute_uri(imageURI);
490 if (!url)
491 {
492 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
493 "UpdateService.SimpleUpdate");
494
495 return std::nullopt;
496 }
497 url->normalize();
498
Ed Tanous757178a2024-04-03 14:32:38 -0700499 if (url->scheme() == "tftp")
500 {
501 if (url->encoded_path().size() < 2)
502 {
503 messages::actionParameterNotSupported(res, "ImageURI",
504 url->buffer());
505 return std::nullopt;
506 }
507 }
Ed Tanouse5cf7772024-04-03 13:45:31 -0700508 else if (url->scheme() == "https")
509 {
510 // Empty paths default to "/"
511 if (url->encoded_path().empty())
512 {
513 url->set_encoded_path("/");
514 }
515 }
Ed Tanous757178a2024-04-03 14:32:38 -0700516 else
Ed Tanousf86bcc82023-08-25 09:34:07 -0700517 {
518 messages::actionParameterNotSupported(res, "ImageURI", imageURI);
519 return std::nullopt;
520 }
Ed Tanous757178a2024-04-03 14:32:38 -0700521
522 if (url->encoded_path().empty())
Ed Tanousf86bcc82023-08-25 09:34:07 -0700523 {
Ed Tanous757178a2024-04-03 14:32:38 -0700524 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
525 "UpdateService.SimpleUpdate");
Ed Tanousf86bcc82023-08-25 09:34:07 -0700526 return std::nullopt;
527 }
Ed Tanous757178a2024-04-03 14:32:38 -0700528
529 return *url;
Ed Tanousf86bcc82023-08-25 09:34:07 -0700530}
531
Ed Tanouse5cf7772024-04-03 13:45:31 -0700532inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
533 const boost::urls::url_view_base& url)
534{
535 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
536 url.buffer());
537}
538
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700539inline void doTftpUpdate(const crow::Request& req,
540 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous757178a2024-04-03 14:32:38 -0700541 const boost::urls::url_view_base& url)
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700542{
Ed Tanousc72503f2024-05-06 14:57:13 -0700543 if (!BMCWEB_INSECURE_TFTP_UPDATE)
544 {
545 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
546 url.buffer());
547 return;
548 }
Ed Tanous757178a2024-04-03 14:32:38 -0700549
550 std::string path(url.encoded_path());
551 if (path.size() < 2)
552 {
553 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
554 url.buffer());
555 return;
556 }
557 // TFTP expects a path without a /
558 path.erase(0, 1);
559 std::string host(url.encoded_host_and_port());
560 BMCWEB_LOG_DEBUG("Server: {} File: {}", host, path);
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700561
562 // Setup callback for when new software detected
563 // Give TFTP 10 minutes to complete
564 monitorForSoftwareAvailable(
565 asyncResp, req,
566 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate", 600);
567
568 // TFTP can take up to 10 minutes depending on image size and
569 // connection speed. Return to caller as soon as the TFTP operation
570 // has been started. The callback above will ensure the activate
571 // is started once the download has completed
572 redfish::messages::success(asyncResp->res);
573
574 // Call TFTP service
575 crow::connections::systemBus->async_method_call(
576 [](const boost::system::error_code& ec) {
577 if (ec)
578 {
579 // messages::internalError(asyncResp->res);
580 cleanUp();
581 BMCWEB_LOG_DEBUG("error_code = {}", ec);
582 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
583 }
584 else
585 {
586 BMCWEB_LOG_DEBUG("Call to DownloaViaTFTP Success");
587 }
588 },
589 "xyz.openbmc_project.Software.Download",
590 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
Ed Tanous757178a2024-04-03 14:32:38 -0700591 "DownloadViaTFTP", path, host);
Ed Tanous6b0f66b2024-04-03 13:38:21 -0700592}
593
Ed Tanousf5139332024-04-03 13:25:04 -0700594inline void handleUpdateServiceSimpleUpdateAction(
595 crow::App& app, const crow::Request& req,
596 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Andrew Geissler0554c982019-04-23 14:40:12 -0500597{
Ed Tanousf5139332024-04-03 13:25:04 -0700598 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
599 {
600 return;
601 }
602
603 std::optional<std::string> transferProtocol;
604 std::string imageURI;
605
606 BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
607
608 // User can pass in both TransferProtocol and ImageURI parameters or
609 // they can pass in just the ImageURI with the transfer protocol
610 // embedded within it.
611 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
612 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
613
614 if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
615 transferProtocol, "ImageURI", imageURI))
616 {
617 BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
618 return;
619 }
620
Ed Tanous757178a2024-04-03 14:32:38 -0700621 std::optional<boost::urls::url> url =
622 parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res);
623 if (!url)
Ed Tanousf5139332024-04-03 13:25:04 -0700624 {
625 return;
626 }
Ed Tanous757178a2024-04-03 14:32:38 -0700627 if (url->scheme() == "tftp")
628 {
629 doTftpUpdate(req, asyncResp, *url);
630 }
Ed Tanouse5cf7772024-04-03 13:45:31 -0700631 else if (url->scheme() == "https")
632 {
633 doHttpsUpdate(asyncResp, *url);
634 }
Ed Tanous757178a2024-04-03 14:32:38 -0700635 else
636 {
637 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
638 url->buffer());
639 return;
640 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700641
Ed Tanousf5139332024-04-03 13:25:04 -0700642 BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700643}
644
George Liu0ed80c82020-05-12 16:06:27 +0800645inline void uploadImageFile(crow::Response& res, std::string_view body)
646{
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700647 std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
648
Ed Tanous62598e32023-07-17 17:06:25 -0700649 BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
George Liu0ed80c82020-05-12 16:06:27 +0800650 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
651 std::ofstream::trunc);
652 // set the permission of the file to 640
Patrick Williams89492a12023-05-10 07:51:34 -0500653 std::filesystem::perms permission = std::filesystem::perms::owner_read |
654 std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800655 std::filesystem::permissions(filepath, permission);
656 out << body;
657
658 if (out.bad())
659 {
660 messages::internalError(res);
661 cleanUp();
662 }
663}
664
665inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
666 const std::string& applyTime)
667{
668 std::string applyTimeNewVal;
669 if (applyTime == "Immediate")
670 {
671 applyTimeNewVal =
672 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
673 }
674 else if (applyTime == "OnReset")
675 {
676 applyTimeNewVal =
677 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
678 }
679 else
680 {
Ed Tanous62598e32023-07-17 17:06:25 -0700681 BMCWEB_LOG_INFO(
682 "ApplyTime value is not in the list of acceptable values");
George Liu0ed80c82020-05-12 16:06:27 +0800683 messages::propertyValueNotInList(asyncResp->res, applyTime,
684 "ApplyTime");
685 return;
686 }
687
Ed Tanousd02aad32024-02-13 14:43:34 -0800688 setDbusProperty(asyncResp, "xyz.openbmc_project.Settings",
689 sdbusplus::message::object_path(
690 "/xyz/openbmc_project/software/apply_time"),
691 "xyz.openbmc_project.Software.ApplyTime",
692 "RequestedApplyTime", "ApplyTime", applyTimeNewVal);
George Liu0ed80c82020-05-12 16:06:27 +0800693}
694
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700695struct MultiPartUpdateParameters
George Liu0ed80c82020-05-12 16:06:27 +0800696{
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700697 std::optional<std::string> applyTime;
698 std::string uploadData;
699 std::vector<boost::urls::url> targets;
700};
701
702inline std::optional<MultiPartUpdateParameters>
703 extractMultipartUpdateParameters(
704 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
705 MultipartParser parser)
706{
707 MultiPartUpdateParameters multiRet;
708 for (FormPart& formpart : parser.mime_fields)
George Liu0ed80c82020-05-12 16:06:27 +0800709 {
710 boost::beast::http::fields::const_iterator it =
711 formpart.fields.find("Content-Disposition");
712 if (it == formpart.fields.end())
713 {
Ed Tanous62598e32023-07-17 17:06:25 -0700714 BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700715 return std::nullopt;
George Liu0ed80c82020-05-12 16:06:27 +0800716 }
Ed Tanous62598e32023-07-17 17:06:25 -0700717 BMCWEB_LOG_INFO("Parsing value {}", it->value());
George Liu0ed80c82020-05-12 16:06:27 +0800718
719 // The construction parameters of param_list must start with `;`
720 size_t index = it->value().find(';');
721 if (index == std::string::npos)
722 {
723 continue;
724 }
725
Patrick Williams89492a12023-05-10 07:51:34 -0500726 for (const auto& param :
George Liu0ed80c82020-05-12 16:06:27 +0800727 boost::beast::http::param_list{it->value().substr(index)})
728 {
729 if (param.first != "name" || param.second.empty())
730 {
731 continue;
732 }
733
734 if (param.second == "UpdateParameters")
735 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700736 std::vector<std::string> tempTargets;
George Liu0ed80c82020-05-12 16:06:27 +0800737 nlohmann::json content =
738 nlohmann::json::parse(formpart.content);
Ed Tanous7cb59f62022-05-05 11:48:31 -0700739 nlohmann::json::object_t* obj =
740 content.get_ptr<nlohmann::json::object_t*>();
741 if (obj == nullptr)
742 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700743 messages::propertyValueTypeError(
744 asyncResp->res, formpart.content, "UpdateParameters");
745 return std::nullopt;
Ed Tanous7cb59f62022-05-05 11:48:31 -0700746 }
747
748 if (!json_util::readJsonObject(
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700749 *obj, asyncResp->res, "Targets", tempTargets,
750 "@Redfish.OperationApplyTime", multiRet.applyTime))
George Liu0ed80c82020-05-12 16:06:27 +0800751 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700752 return std::nullopt;
George Liu0ed80c82020-05-12 16:06:27 +0800753 }
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700754
755 for (size_t urlIndex = 0; urlIndex < tempTargets.size();
756 urlIndex++)
George Liu0ed80c82020-05-12 16:06:27 +0800757 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700758 const std::string& target = tempTargets[urlIndex];
759 boost::system::result<boost::urls::url_view> url =
760 boost::urls::parse_origin_form(target);
761 if (!url)
762 {
763 messages::propertyValueFormatError(
764 asyncResp->res, target,
765 std::format("Targets/{}", urlIndex));
766 return std::nullopt;
767 }
768 multiRet.targets.emplace_back(*url);
George Liu0ed80c82020-05-12 16:06:27 +0800769 }
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700770 if (multiRet.targets.size() != 1)
George Liu0ed80c82020-05-12 16:06:27 +0800771 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700772 messages::propertyValueFormatError(
773 asyncResp->res, multiRet.targets, "Targets");
774 return std::nullopt;
George Liu0ed80c82020-05-12 16:06:27 +0800775 }
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700776 if (multiRet.targets[0].path() != "/redfish/v1/Managers/bmc")
777 {
778 messages::propertyValueNotInList(
779 asyncResp->res, multiRet.targets[0], "Targets/0");
780 return std::nullopt;
781 }
George Liu0ed80c82020-05-12 16:06:27 +0800782 }
783 else if (param.second == "UpdateFile")
784 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700785 multiRet.uploadData = std::move(formpart.content);
George Liu0ed80c82020-05-12 16:06:27 +0800786 }
787 }
788 }
789
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700790 if (multiRet.uploadData.empty())
George Liu0ed80c82020-05-12 16:06:27 +0800791 {
Ed Tanous62598e32023-07-17 17:06:25 -0700792 BMCWEB_LOG_ERROR("Upload data is NULL");
George Liu0ed80c82020-05-12 16:06:27 +0800793 messages::propertyMissing(asyncResp->res, "UpdateFile");
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700794 return std::nullopt;
795 }
796 if (multiRet.targets.empty())
797 {
798 messages::propertyMissing(asyncResp->res, "Targets");
799 return std::nullopt;
800 }
801 return multiRet;
802}
803
804inline void
805 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
806 const crow::Request& req, MultipartParser&& parser)
807{
808 std::optional<MultiPartUpdateParameters> multipart =
809 extractMultipartUpdateParameters(asyncResp, std::move(parser));
810 if (!multipart)
811 {
George Liu0ed80c82020-05-12 16:06:27 +0800812 return;
813 }
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700814 if (!multipart->applyTime)
George Liu0ed80c82020-05-12 16:06:27 +0800815 {
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700816 multipart->applyTime = "OnReset";
George Liu0ed80c82020-05-12 16:06:27 +0800817 }
818
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700819 setApplyTime(asyncResp, *multipart->applyTime);
George Liu0ed80c82020-05-12 16:06:27 +0800820
Ed Tanous6b54e4e2024-04-10 08:58:48 -0700821 // Setup callback for when new software detected
822 monitorForSoftwareAvailable(asyncResp, req, "/redfish/v1/UpdateService");
823
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700824 uploadImageFile(asyncResp->res, multipart->uploadData);
George Liu0ed80c82020-05-12 16:06:27 +0800825}
826
Ed Tanousc2051d12022-05-11 12:21:55 -0700827inline void
828 handleUpdateServicePost(App& app, const crow::Request& req,
829 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
830{
Carson Labrado3ba00072022-06-06 19:40:56 +0000831 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700832 {
833 return;
834 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500835 std::string_view contentType = req.getHeaderValue("Content-Type");
Ed Tanousc2051d12022-05-11 12:21:55 -0700836
Ed Tanous62598e32023-07-17 17:06:25 -0700837 BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
Ed Tanousc2051d12022-05-11 12:21:55 -0700838
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500839 // Make sure that content type is application/octet-stream or
840 // multipart/form-data
Ed Tanous18f8f602023-07-18 10:07:23 -0700841 if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
George Liu0ed80c82020-05-12 16:06:27 +0800842 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500843 // Setup callback for when new software detected
844 monitorForSoftwareAvailable(asyncResp, req,
845 "/redfish/v1/UpdateService");
846
George Liu0ed80c82020-05-12 16:06:27 +0800847 uploadImageFile(asyncResp->res, req.body());
George Liu0ed80c82020-05-12 16:06:27 +0800848 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500849 else if (contentType.starts_with("multipart/form-data"))
George Liu0ed80c82020-05-12 16:06:27 +0800850 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500851 MultipartParser parser;
852
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500853 ParserError ec = parser.parse(req);
854 if (ec != ParserError::PARSER_SUCCESS)
855 {
856 // handle error
Ed Tanous62598e32023-07-17 17:06:25 -0700857 BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
858 static_cast<int>(ec));
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500859 messages::internalError(asyncResp->res);
860 return;
861 }
Ed Tanous6b54e4e2024-04-10 08:58:48 -0700862
Jagpal Singh Gillef93eab2024-04-17 16:06:14 -0700863 updateMultipartContext(asyncResp, req, std::move(parser));
George Liu0ed80c82020-05-12 16:06:27 +0800864 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500865 else
866 {
Ed Tanous62598e32023-07-17 17:06:25 -0700867 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500868 asyncResp->res.result(boost::beast::http::status::bad_request);
869 }
Ed Tanousc2051d12022-05-11 12:21:55 -0700870}
871
Ed Tanousf5139332024-04-03 13:25:04 -0700872inline void
873 handleUpdateServiceGet(App& app, const crow::Request& req,
874 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700875{
Ed Tanousf5139332024-04-03 13:25:04 -0700876 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
877 {
878 return;
879 }
880 asyncResp->res.jsonValue["@odata.type"] =
881 "#UpdateService.v1_11_1.UpdateService";
882 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
883 asyncResp->res.jsonValue["Id"] = "UpdateService";
884 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
885 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700886
Ed Tanousf5139332024-04-03 13:25:04 -0700887 asyncResp->res.jsonValue["HttpPushUri"] =
888 "/redfish/v1/UpdateService/update";
889 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
890 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700891
Ed Tanousf5139332024-04-03 13:25:04 -0700892 // UpdateService cannot be disabled
893 asyncResp->res.jsonValue["ServiceEnabled"] = true;
894 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
895 "/redfish/v1/UpdateService/FirmwareInventory";
896 // Get the MaxImageSizeBytes
Ed Tanous25b54db2024-04-17 15:40:31 -0700897 asyncResp->res.jsonValue["MaxImageSizeBytes"] = BMCWEB_HTTP_BODY_LIMIT *
Ed Tanousf5139332024-04-03 13:25:04 -0700898 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530899
Ed Tanousf5139332024-04-03 13:25:04 -0700900 // Update Actions object.
901 nlohmann::json& updateSvcSimpleUpdate =
902 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
903 updateSvcSimpleUpdate["target"] =
904 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
Ed Tanous757178a2024-04-03 14:32:38 -0700905
906 nlohmann::json::array_t allowed;
Ed Tanouse5cf7772024-04-03 13:45:31 -0700907 allowed.emplace_back(update_service::TransferProtocolType::HTTPS);
Ed Tanous757178a2024-04-03 14:32:38 -0700908
Ed Tanous25b54db2024-04-17 15:40:31 -0700909 if constexpr (BMCWEB_INSECURE_PUSH_STYLE_NOTIFICATION)
910 {
911 allowed.emplace_back(update_service::TransferProtocolType::TFTP);
912 }
Ed Tanous757178a2024-04-03 14:32:38 -0700913
914 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
915 std::move(allowed);
916
Ed Tanousf5139332024-04-03 13:25:04 -0700917 // Get the current ApplyTime value
918 sdbusplus::asio::getProperty<std::string>(
919 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
920 "/xyz/openbmc_project/software/apply_time",
921 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
922 [asyncResp](const boost::system::error_code& ec,
923 const std::string& applyTime) {
924 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700925 {
Ed Tanousf5139332024-04-03 13:25:04 -0700926 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
927 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -0700928 return;
929 }
930
Ed Tanousf5139332024-04-03 13:25:04 -0700931 // Store the ApplyTime Value
932 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
933 "RequestedApplyTimes.Immediate")
Ed Tanous002d39b2022-05-31 08:59:27 -0700934 {
Ed Tanousf5139332024-04-03 13:25:04 -0700935 asyncResp->res.jsonValue["HttpPushUriOptions"]
936 ["HttpPushUriApplyTime"]["ApplyTime"] =
937 "Immediate";
938 }
939 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
940 "RequestedApplyTimes.OnReset")
941 {
942 asyncResp->res.jsonValue["HttpPushUriOptions"]
943 ["HttpPushUriApplyTime"]["ApplyTime"] =
944 "OnReset";
Ed Tanous002d39b2022-05-31 08:59:27 -0700945 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500946 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700947}
948
Ed Tanousf5139332024-04-03 13:25:04 -0700949inline void handleUpdateServicePatch(
950 App& app, const crow::Request& req,
951 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700952{
Ed Tanousf5139332024-04-03 13:25:04 -0700953 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
954 {
955 return;
956 }
957 BMCWEB_LOG_DEBUG("doPatch...");
Ed Tanous002d39b2022-05-31 08:59:27 -0700958
Ed Tanousf5139332024-04-03 13:25:04 -0700959 std::optional<std::string> applyTime;
960 if (!json_util::readJsonPatch(
961 req, asyncResp->res,
962 "HttpPushUriOptions/HttpPushUriApplyTime/ApplyTime", applyTime))
963 {
964 return;
965 }
966
967 if (applyTime)
968 {
969 setApplyTime(asyncResp, *applyTime);
970 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700971}
Ed Tanousf5139332024-04-03 13:25:04 -0700972
973inline void handleUpdateServiceFirmwareInventoryCollectionGet(
974 App& app, const crow::Request& req,
975 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
976{
977 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
978 {
979 return;
980 }
981 asyncResp->res.jsonValue["@odata.type"] =
982 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
983 asyncResp->res.jsonValue["@odata.id"] =
984 "/redfish/v1/UpdateService/FirmwareInventory";
985 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
986 const std::array<const std::string_view, 1> iface = {
987 "xyz.openbmc_project.Software.Version"};
988
989 redfish::collection_util::getCollectionMembers(
990 asyncResp,
991 boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface,
992 "/xyz/openbmc_project/software");
993}
994
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700995/* Fill related item links (i.e. bmc, bios) in for inventory */
Ed Tanousf5139332024-04-03 13:25:04 -0700996inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
997 const std::string& purpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700998{
Willy Tueee00132022-06-14 14:53:17 -0700999 if (purpose == sw_util::bmcPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001000 {
Ed Tanousac106bf2023-06-07 09:24:59 -07001001 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -07001002 nlohmann::json::object_t item;
1003 item["@odata.id"] = "/redfish/v1/Managers/bmc";
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001004 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -07001005 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1006 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001007 }
Willy Tueee00132022-06-14 14:53:17 -07001008 else if (purpose == sw_util::biosPurpose)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001009 {
Ed Tanousac106bf2023-06-07 09:24:59 -07001010 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
Ed Tanous14766872022-03-15 10:44:42 -07001011 nlohmann::json::object_t item;
1012 item["@odata.id"] = "/redfish/v1/Systems/system/Bios";
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001013 relatedItem.emplace_back(std::move(item));
Ed Tanousac106bf2023-06-07 09:24:59 -07001014 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1015 relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001016 }
1017 else
1018 {
Carson Labradobf2dded2023-08-10 00:37:06 +00001019 BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001020 }
1021}
1022
Willy Tuaf246602022-06-14 15:51:53 -07001023inline void
1024 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1025 const std::string& service, const std::string& path,
1026 const std::string& swId)
1027{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001028 sdbusplus::asio::getAllProperties(
1029 *crow::connections::systemBus, service, path,
1030 "xyz.openbmc_project.Software.Version",
Willy Tuaf246602022-06-14 15:51:53 -07001031 [asyncResp,
Ed Tanous8b242752023-06-27 17:17:13 -07001032 swId](const boost::system::error_code& ec,
Willy Tuaf246602022-06-14 15:51:53 -07001033 const dbus::utility::DBusPropertiesMap& propertiesList) {
Ed Tanous8b242752023-06-27 17:17:13 -07001034 if (ec)
Willy Tuaf246602022-06-14 15:51:53 -07001035 {
1036 messages::internalError(asyncResp->res);
1037 return;
1038 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001039
Willy Tuaf246602022-06-14 15:51:53 -07001040 const std::string* swInvPurpose = nullptr;
1041 const std::string* version = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001042
1043 const bool success = sdbusplus::unpackPropertiesNoThrow(
1044 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
1045 swInvPurpose, "Version", version);
1046
1047 if (!success)
Willy Tuaf246602022-06-14 15:51:53 -07001048 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001049 messages::internalError(asyncResp->res);
1050 return;
Willy Tuaf246602022-06-14 15:51:53 -07001051 }
1052
1053 if (swInvPurpose == nullptr)
1054 {
Ed Tanous62598e32023-07-17 17:06:25 -07001055 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
Willy Tuaf246602022-06-14 15:51:53 -07001056 messages::internalError(asyncResp->res);
1057 return;
1058 }
1059
Ed Tanous62598e32023-07-17 17:06:25 -07001060 BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
Willy Tuaf246602022-06-14 15:51:53 -07001061
1062 if (version == nullptr)
1063 {
Ed Tanous62598e32023-07-17 17:06:25 -07001064 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
Willy Tuaf246602022-06-14 15:51:53 -07001065
1066 messages::internalError(asyncResp->res);
1067
1068 return;
1069 }
1070 asyncResp->res.jsonValue["Version"] = *version;
1071 asyncResp->res.jsonValue["Id"] = swId;
1072
1073 // swInvPurpose is of format:
1074 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
1075 // Translate this to "ABC image"
1076 size_t endDesc = swInvPurpose->rfind('.');
1077 if (endDesc == std::string::npos)
1078 {
1079 messages::internalError(asyncResp->res);
1080 return;
1081 }
1082 endDesc++;
1083 if (endDesc >= swInvPurpose->size())
1084 {
1085 messages::internalError(asyncResp->res);
1086 return;
1087 }
1088
1089 std::string formatDesc = swInvPurpose->substr(endDesc);
1090 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1091 getRelatedItems(asyncResp, *swInvPurpose);
Patrick Williams5a39f772023-10-20 11:20:21 -05001092 });
Willy Tuaf246602022-06-14 15:51:53 -07001093}
1094
Ed Tanousf5139332024-04-03 13:25:04 -07001095inline void handleUpdateServiceFirmwareInventoryGet(
1096 App& app, const crow::Request& req,
1097 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1098 const std::string& param)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001099{
Ed Tanousf5139332024-04-03 13:25:04 -07001100 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1101 {
1102 return;
1103 }
1104 std::shared_ptr<std::string> swId = std::make_shared<std::string>(param);
1105
1106 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1107 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
1108
1109 constexpr std::array<std::string_view, 1> interfaces = {
1110 "xyz.openbmc_project.Software.Version"};
1111 dbus::utility::getSubTree(
1112 "/", 0, interfaces,
1113 [asyncResp,
1114 swId](const boost::system::error_code& ec,
1115 const dbus::utility::MapperGetSubTreeResponse& subtree) {
1116 BMCWEB_LOG_DEBUG("doGet callback...");
1117 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001118 {
Ed Tanousf5139332024-04-03 13:25:04 -07001119 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001120 return;
1121 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001122
Ed Tanousf5139332024-04-03 13:25:04 -07001123 // Ensure we find our input swId, otherwise return an error
1124 bool found = false;
1125 for (const std::pair<
1126 std::string,
1127 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1128 obj : subtree)
1129 {
1130 if (!obj.first.ends_with(*swId))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001131 {
Ed Tanousf5139332024-04-03 13:25:04 -07001132 continue;
Ed Tanous45ca1b82022-03-25 13:07:27 -07001133 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001134
Ed Tanousf5139332024-04-03 13:25:04 -07001135 if (obj.second.empty())
Ed Tanous002d39b2022-05-31 08:59:27 -07001136 {
Ed Tanousf5139332024-04-03 13:25:04 -07001137 continue;
Ed Tanous002d39b2022-05-31 08:59:27 -07001138 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001139
Ed Tanousf5139332024-04-03 13:25:04 -07001140 found = true;
1141 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1142 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1143 *swId);
1144 }
1145 if (!found)
1146 {
1147 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
1148 messages::resourceMissingAtURI(
1149 asyncResp->res,
1150 boost::urls::format(
1151 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId));
1152 return;
1153 }
1154 asyncResp->res.jsonValue["@odata.type"] =
1155 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1156 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1157 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
1158
1159 asyncResp->res.jsonValue["Updateable"] = false;
1160 sw_util::getSwUpdatableStatus(asyncResp, swId);
Patrick Williams5a39f772023-10-20 11:20:21 -05001161 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001162}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001163
Ed Tanousf5139332024-04-03 13:25:04 -07001164inline void requestRoutesUpdateService(App& app)
1165{
1166 BMCWEB_ROUTE(
1167 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
1168 .privileges(redfish::privileges::postUpdateService)
1169 .methods(boost::beast::http::verb::post)(std::bind_front(
1170 handleUpdateServiceSimpleUpdateAction, std::ref(app)));
1171
1172 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1173 .privileges(redfish::privileges::getSoftwareInventory)
1174 .methods(boost::beast::http::verb::get)(std::bind_front(
1175 handleUpdateServiceFirmwareInventoryGet, std::ref(app)));
1176
1177 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1178 .privileges(redfish::privileges::getUpdateService)
1179 .methods(boost::beast::http::verb::get)(
1180 std::bind_front(handleUpdateServiceGet, std::ref(app)));
1181
1182 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1183 .privileges(redfish::privileges::patchUpdateService)
1184 .methods(boost::beast::http::verb::patch)(
1185 std::bind_front(handleUpdateServicePatch, std::ref(app)));
1186
1187 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
1188 .privileges(redfish::privileges::postUpdateService)
1189 .methods(boost::beast::http::verb::post)(
1190 std::bind_front(handleUpdateServicePost, std::ref(app)));
1191
1192 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
1193 .privileges(redfish::privileges::getSoftwareInventoryCollection)
1194 .methods(boost::beast::http::verb::get)(std::bind_front(
1195 handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app)));
1196}
1197
Ed Tanous1abe55e2018-09-05 08:30:59 -07001198} // namespace redfish